Implement workshop install normalization
Agent-Logs-Url: https://github.com/GameServerPanel/GSP/sessions/b08aa54e-755c-4869-ba97-0f817a05b3c6 Co-authored-by: iaretechnician <2749183+iaretechnician@users.noreply.github.com>
This commit is contained in:
parent
68d06253fe
commit
c471c4b3f8
11 changed files with 827 additions and 236 deletions
|
|
@ -1,7 +1,15 @@
|
|||
$(function() {
|
||||
function replaceTemplate(template, values) {
|
||||
var output = String(template || '');
|
||||
$.each(values, function(key, value) {
|
||||
output = output.split(key).join(value);
|
||||
});
|
||||
return output;
|
||||
}
|
||||
|
||||
var methodToRows = {
|
||||
download_zip: ['#scm-row-url', '#scm-row-path'],
|
||||
steam_workshop: ['#scm-row-workshop-id', '#scm-row-workshop-app-id', '#scm-row-target-path-template', '#scm-row-optional-folder-name'],
|
||||
steam_workshop: ['#scm-row-workshop-id', '#scm-row-workshop-app-id', '#scm-row-target-path-template', '#scm-row-optional-folder-name', '#scm-row-post-script', '#scm-row-launch-param-additions'],
|
||||
post_script: ['#scm-row-post-script'],
|
||||
config_edit: ['#scm-row-path', '#scm-row-config-edit-rule']
|
||||
};
|
||||
|
|
@ -37,4 +45,66 @@ $(function() {
|
|||
|
||||
$method.on('change', applyContentTypeUi);
|
||||
applyContentTypeUi();
|
||||
|
||||
var $userSelect = $('#scm-user-addon-select');
|
||||
var $userWorkshopRows = $('.scm-user-workshop-row');
|
||||
var $userWorkshopId = $('#scm-user-workshop-id');
|
||||
var $userWorkshopAppId = $('#scm-user-workshop-app-id');
|
||||
var $userTargetTemplate = $('#scm-user-target-path-template');
|
||||
var $userOptionalFolderName = $('#scm-user-optional-folder-name');
|
||||
var $userPreview = $('#scm-user-target-path-preview');
|
||||
|
||||
function updateUserWorkshopUi() {
|
||||
if ($userSelect.length === 0) return;
|
||||
var $selected = $userSelect.find('option:selected');
|
||||
var installMethod = String($selected.data('installMethod') || '');
|
||||
var isWorkshop = installMethod === 'steam_workshop';
|
||||
$userWorkshopRows.toggle(isWorkshop);
|
||||
if (!isWorkshop) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$userWorkshopId.val()) {
|
||||
$userWorkshopId.val(String($selected.data('workshopItemId') || ''));
|
||||
}
|
||||
if (!$userWorkshopAppId.val()) {
|
||||
$userWorkshopAppId.val(String($selected.data('workshopAppId') || $userWorkshopAppId.data('defaultAppId') || ''));
|
||||
}
|
||||
if (!$userTargetTemplate.val()) {
|
||||
$userTargetTemplate.val(String($selected.data('targetPathTemplate') || $userTargetTemplate.data('defaultTemplate') || ''));
|
||||
}
|
||||
if (!$userOptionalFolderName.val()) {
|
||||
$userOptionalFolderName.val(String($selected.data('optionalFolderName') || ''));
|
||||
}
|
||||
|
||||
var workshopId = $.trim($userWorkshopId.val());
|
||||
var workshopAppId = $.trim($userWorkshopAppId.val()) || String($selected.data('workshopAppId') || $userWorkshopAppId.data('defaultAppId') || '');
|
||||
var folderName = $.trim($userOptionalFolderName.val()) || (workshopId ? '@' + workshopId : '@{WORKSHOP_ID}');
|
||||
var targetTemplate = $.trim($userTargetTemplate.val()) || String($selected.data('targetPathTemplate') || $userTargetTemplate.data('defaultTemplate') || '');
|
||||
var previewValues = {
|
||||
'{SERVER_ROOT}': String($userPreview.data('serverRoot') || ''),
|
||||
'{GAME_ROOT}': String($userPreview.data('gameRoot') || ''),
|
||||
'{WORKSHOP_ID}': workshopId || '{WORKSHOP_ID}',
|
||||
'{WORKSHOP_APP_ID}': workshopAppId || '{WORKSHOP_APP_ID}',
|
||||
'{STEAM_APP_ID}': String($userPreview.data('steamAppId') || '{STEAM_APP_ID}'),
|
||||
'{FOLDER_NAME}': folderName,
|
||||
'{MOD_FOLDER}': folderName
|
||||
};
|
||||
$userPreview.text(replaceTemplate(targetTemplate, previewValues));
|
||||
}
|
||||
|
||||
if ($userSelect.length) {
|
||||
$userSelect.on('change', function() {
|
||||
$userWorkshopId.val(String($(this).find('option:selected').data('workshopItemId') || ''));
|
||||
$userWorkshopAppId.val(String($(this).find('option:selected').data('workshopAppId') || ''));
|
||||
$userTargetTemplate.val(String($(this).find('option:selected').data('targetPathTemplate') || ''));
|
||||
$userOptionalFolderName.val(String($(this).find('option:selected').data('optionalFolderName') || ''));
|
||||
updateUserWorkshopUi();
|
||||
});
|
||||
$userWorkshopId.on('input', updateUserWorkshopUi);
|
||||
$userWorkshopAppId.on('input', updateUserWorkshopUi);
|
||||
$userTargetTemplate.on('input', updateUserWorkshopUi);
|
||||
$userOptionalFolderName.on('input', updateUserWorkshopUi);
|
||||
updateUserWorkshopUi();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -42,9 +42,9 @@ define('LANG_wait_while_decompressing', "Wait while the file %s is decompressed.
|
|||
define('LANG_addon_name', "Content Item Name");
|
||||
define('LANG_url', "URL");
|
||||
define('LANG_select_game_type', "Select Game Type");
|
||||
define('LANG_plugin', "Plugins / Mods");
|
||||
define('LANG_mappack', "Map Packs");
|
||||
define('LANG_config', "Config Packs");
|
||||
define('LANG_plugin', "Downloadable Mod");
|
||||
define('LANG_mappack', "Steam Workshop Item");
|
||||
define('LANG_config', "Configuration Package");
|
||||
if (!defined('LANG_version')) {
|
||||
define('LANG_version', "Version");
|
||||
}
|
||||
|
|
@ -93,8 +93,8 @@ define('LANG_target_path_template', "Target Path");
|
|||
define('LANG_optional_folder_name', "Optional Folder Name");
|
||||
define('LANG_config_edit_rule', "Config Edit Rule");
|
||||
define('LANG_launch_param_additions', "Launch Parameter Additions");
|
||||
define('LANG_content_type_help_download_zip', "Downloads an archive/file from URL; extract path is optional.");
|
||||
define('LANG_content_type_help_steam_workshop', "Installs/updates a Workshop item with Workshop ID (no URL required).");
|
||||
define('LANG_content_type_help_post_script', "Runs a scripted installer action (no URL required).");
|
||||
define('LANG_content_type_help_config_edit', "Edits config at target path using provided action/rules (no URL required).");
|
||||
define('LANG_content_type_help_download_zip', "Download and extract a ZIP, RAR, or archive file.");
|
||||
define('LANG_content_type_help_steam_workshop', "Install a Steam Workshop mod using Workshop ID.");
|
||||
define('LANG_content_type_help_post_script', "Run a custom scripted installation process.");
|
||||
define('LANG_content_type_help_config_edit', "Install configuration files, profiles, or templates.");
|
||||
?>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
<script type="text/javascript" src="js/modules/addonsmanager.js"></script>
|
||||
<?php
|
||||
/*
|
||||
*
|
||||
|
|
@ -182,17 +183,13 @@ function exec_ogp_module() {
|
|||
$install_method = scm_get_install_method_default(isset($addon_info['install_method']) ? $addon_info['install_method'] : 'download_zip');
|
||||
$content_version = isset($addon_info['content_version']) ? $addon_info['content_version'] : '';
|
||||
$requires_stop = !empty($addon_info['requires_stop']) ? 1 : 0;
|
||||
$user_override_keys = ($install_method === 'steam_workshop')
|
||||
? array('workshop_item_id', 'workshop_app_id', 'target_path_template', 'optional_folder_name')
|
||||
: array();
|
||||
$install_payload = scm_collect_install_payload($addon_info, $_REQUEST, $user_override_keys);
|
||||
$post_script = '';
|
||||
$validation_payload = array(
|
||||
'url' => isset($addon_info['url']) ? $addon_info['url'] : '',
|
||||
'path' => isset($addon_info['path']) ? $addon_info['path'] : '',
|
||||
'workshop_item_id' => isset($addon_info['workshop_item_id']) ? $addon_info['workshop_item_id'] : '',
|
||||
'target_path_template' => isset($addon_info['target_path_template']) ? $addon_info['target_path_template'] : '',
|
||||
'post_script' => isset($addon_info['post_script']) ? $addon_info['post_script'] : '',
|
||||
'config_edit_rule' => isset($addon_info['config_edit_rule']) ? $addon_info['config_edit_rule'] : '',
|
||||
);
|
||||
$validation_message = '';
|
||||
if ($state == "start" && !scm_validate_install_method_payload($install_method, $validation_payload, $validation_message)) {
|
||||
if ($state == "start" && !scm_validate_install_method_payload($install_method, $install_payload, $validation_message)) {
|
||||
print_failure($validation_message);
|
||||
return;
|
||||
}
|
||||
|
|
@ -209,7 +206,7 @@ function exec_ogp_module() {
|
|||
return;
|
||||
}
|
||||
}
|
||||
$url = $addon_info['url'];
|
||||
$url = $install_payload['url'];
|
||||
$filename = basename($url);
|
||||
#### Replace template variables in the post-install script with
|
||||
#### live server data before sending to the agent.
|
||||
|
|
@ -285,52 +282,38 @@ function exec_ogp_module() {
|
|||
'content_type' => $install_method,
|
||||
'home_id' => (int)$home_id,
|
||||
'home_cfg_id' => (int)$home_info['home_cfg_id'],
|
||||
'workshop_id' => isset($addon_info['workshop_item_id']) ? (string)$addon_info['workshop_item_id'] : '',
|
||||
'target_path' => isset($addon_info['target_path_template']) ? (string)$addon_info['target_path_template'] : (string)$addon_info['path'],
|
||||
'workshop_id' => isset($install_payload['workshop_item_id']) ? (string)$install_payload['workshop_item_id'] : '',
|
||||
'target_path' => ($install_method === 'steam_workshop')
|
||||
? (string)$install_payload['target_path_template']
|
||||
: (string)$install_payload['path'],
|
||||
'action' => 'started',
|
||||
));
|
||||
if ($install_method === 'steam_workshop') {
|
||||
scm_ensure_workshop_schema($db);
|
||||
$workshop_item_id = trim((string)$addon_info['workshop_item_id']);
|
||||
$target_path_template = trim((string)$addon_info['target_path_template']);
|
||||
$resolved = function_exists('steam_workshop_install_item_to_home')
|
||||
? steam_workshop_install_item_to_home($db, $home_info, $workshop_item_id, $target_path_template, array(
|
||||
'optional_folder_name' => trim((string)$addon_info['optional_folder_name']),
|
||||
'workshop_app_id' => trim((string)$addon_info['workshop_app_id']),
|
||||
))
|
||||
: array('ok' => false);
|
||||
if (empty($resolved['ok'])) {
|
||||
$fallback_profile = function_exists('sw_get_profile_for_home') ? sw_get_profile_for_home($db, (int)$home_id) : false;
|
||||
$fallback_workshop_app_id = trim((string)$addon_info['workshop_app_id']);
|
||||
if ($fallback_workshop_app_id === '' && is_array($fallback_profile) && !empty($fallback_profile['workshop_app_id'])) {
|
||||
$fallback_workshop_app_id = (string)$fallback_profile['workshop_app_id'];
|
||||
}
|
||||
if ($fallback_workshop_app_id === '') {
|
||||
$fallback_workshop_app_id = scm_extract_workshop_app_id($server_xml);
|
||||
}
|
||||
$placeholder_map = scm_build_placeholder_map($home_info, array('exe_location' => (string)$server_xml->exe_location), array(
|
||||
'WORKSHOP_ID' => $workshop_item_id,
|
||||
'WORKSHOP_APP_ID' => $fallback_workshop_app_id,
|
||||
'STEAM_APP_ID' => (is_array($fallback_profile) && !empty($fallback_profile['steam_app_id'])) ? (string)$fallback_profile['steam_app_id'] : '',
|
||||
));
|
||||
$resolved = array(
|
||||
'workshop_app_id' => $fallback_workshop_app_id,
|
||||
'steam_app_id' => (is_array($fallback_profile) && !empty($fallback_profile['steam_app_id'])) ? (string)$fallback_profile['steam_app_id'] : '',
|
||||
'target_path_resolved' => scm_apply_placeholders($target_path_template, $placeholder_map),
|
||||
);
|
||||
$workshop_runtime = scm_build_workshop_runtime_context($db, $home_info, $server_xml, $install_payload, $validation_message);
|
||||
if ($workshop_runtime === false) {
|
||||
print_failure($validation_message);
|
||||
echo "<p><a href=\"?m=addonsmanager&p=user_addons&home_id=$home_id&mod_id=$mod_id&ip=$ip&port=$port\">".get_lang('back')."</a></p>";
|
||||
return;
|
||||
}
|
||||
$workshop_app_id = isset($resolved['workshop_app_id']) ? (string)$resolved['workshop_app_id'] : '';
|
||||
$steam_app_id = isset($resolved['steam_app_id']) ? (string)$resolved['steam_app_id'] : '';
|
||||
$target_path_resolved = isset($resolved['target_path_resolved']) ? (string)$resolved['target_path_resolved'] : '';
|
||||
$workshop_item_id = (string)$workshop_runtime['workshop_item_id'];
|
||||
$target_path_template = (string)$workshop_runtime['target_path_template'];
|
||||
$workshop_app_id = (string)$workshop_runtime['workshop_app_id'];
|
||||
$steam_app_id = (string)$workshop_runtime['steam_app_id'];
|
||||
$target_path_resolved = (string)$workshop_runtime['target_path_resolved'];
|
||||
$extra_manifest = array(
|
||||
'addon_id' => (int)$addon_id,
|
||||
'target_path_template' => $target_path_template,
|
||||
'target_path_resolved' => $target_path_resolved,
|
||||
'optional_folder_name' => trim((string)$addon_info['optional_folder_name']),
|
||||
'optional_folder_name' => trim((string)$install_payload['optional_folder_name']),
|
||||
'config_edit_rule' => trim((string)$addon_info['config_edit_rule']),
|
||||
'launch_param_additions' => trim((string)$addon_info['launch_param_additions']),
|
||||
'workshop_app_id' => $workshop_app_id,
|
||||
'steam_app_id' => $steam_app_id,
|
||||
'steamcmd_path' => isset($workshop_runtime['steamcmd_path']) ? (string)$workshop_runtime['steamcmd_path'] : '',
|
||||
'workshop_download_dir' => isset($workshop_runtime['workshop_download_dir']) ? (string)$workshop_runtime['workshop_download_dir'] : '',
|
||||
'server_root' => isset($workshop_runtime['server_root']) ? (string)$workshop_runtime['server_root'] : rtrim((string)$home_info['home_path'], '/'),
|
||||
'post_install_script' => trim((string)$post_script),
|
||||
);
|
||||
$workshop_error = '';
|
||||
$workshop_ok = scm_workshop_write_manifest_and_run($db, $home_info, $server_xml, 'install', array($workshop_item_id), $workshop_error, $extra_manifest);
|
||||
|
|
@ -350,7 +333,10 @@ function exec_ogp_module() {
|
|||
'home_id' => (int)$home_id,
|
||||
'home_cfg_id' => (int)$home_info['home_cfg_id'],
|
||||
'workshop_id' => $workshop_item_id,
|
||||
'steam_app_id' => $steam_app_id,
|
||||
'workshop_app_id' => $workshop_app_id,
|
||||
'target_path' => $target_path_resolved,
|
||||
'final_folder_path' => $target_path_resolved,
|
||||
'action' => 'succeeded',
|
||||
));
|
||||
print_success(get_lang('addon_installed_successfully'));
|
||||
|
|
@ -363,6 +349,8 @@ function exec_ogp_module() {
|
|||
'home_id' => (int)$home_id,
|
||||
'home_cfg_id' => (int)$home_info['home_cfg_id'],
|
||||
'workshop_id' => $workshop_item_id,
|
||||
'steam_app_id' => $steam_app_id,
|
||||
'workshop_app_id' => $workshop_app_id,
|
||||
'target_path' => $target_path_resolved,
|
||||
'action' => 'failed',
|
||||
'error' => $workshop_error,
|
||||
|
|
@ -558,10 +546,28 @@ function exec_ogp_module() {
|
|||
}
|
||||
?>
|
||||
<?php
|
||||
$addon_type_lang_key = "server_content_".$addon_type;
|
||||
$addon_type_lang = get_lang($addon_type_lang_key);
|
||||
if($addon_type_lang === "_".$addon_type_lang_key."_")
|
||||
$addon_type_lang = get_lang($addon_type);
|
||||
$category_labels = get_server_content_categories();
|
||||
$addon_type_lang = isset($category_labels[$addon_type]) ? $category_labels[$addon_type] : ucfirst(str_replace('_', ' ', $addon_type));
|
||||
$addons = $db->resultQuery(
|
||||
"SELECT addon_id, name, install_method, workshop_item_id, workshop_app_id, target_path_template, optional_folder_name
|
||||
FROM OGP_DB_PREFIXaddons
|
||||
WHERE addon_type='".$addon_type."' AND home_cfg_id=" . $home_cfg_id . $query_groups . "
|
||||
ORDER BY name ASC"
|
||||
);
|
||||
if (!is_array($addons)) {
|
||||
$addons = [];
|
||||
}
|
||||
$selected_addon = isset($addons[0]) ? $addons[0] : array();
|
||||
$default_install_method = isset($selected_addon['install_method']) ? scm_get_install_method_default($selected_addon['install_method']) : '';
|
||||
$is_workshop_default = ($default_install_method === 'steam_workshop');
|
||||
$workshop_profile = function_exists('sw_get_profile_for_home') ? sw_get_profile_for_home($db, (int)$home_id) : false;
|
||||
$default_workshop_app_id = !empty($selected_addon['workshop_app_id'])
|
||||
? trim((string)$selected_addon['workshop_app_id'])
|
||||
: ((is_array($workshop_profile) && !empty($workshop_profile['workshop_app_id'])) ? (string)$workshop_profile['workshop_app_id'] : scm_extract_workshop_app_id($server_xml));
|
||||
$default_target_template = !empty($selected_addon['target_path_template'])
|
||||
? trim((string)$selected_addon['target_path_template'])
|
||||
: ((is_array($workshop_profile) && !empty($workshop_profile['install_path_template'])) ? (string)$workshop_profile['install_path_template'] : '{SERVER_ROOT}/{MOD_FOLDER}');
|
||||
$default_optional_folder_name = !empty($selected_addon['optional_folder_name']) ? trim((string)$selected_addon['optional_folder_name']) : '';
|
||||
?>
|
||||
<h2><?php echo htmlentities($home_info['home_name'])." ".$addon_type_lang ;?></h2>
|
||||
<table class='center'>
|
||||
|
|
@ -579,21 +585,40 @@ function exec_ogp_module() {
|
|||
<td align='left'><?php echo "$home_info[remote_server_name] ($home_info[agent_ip]:$home_info[agent_port])"; ?></td></tr>
|
||||
<tr><td align='right'><?php print_lang('select_addon'); ?>: </td>
|
||||
<td align='left'>
|
||||
<select name="addon_id">
|
||||
<?php
|
||||
$addons = $db->resultQuery("SELECT addon_id, name FROM OGP_DB_PREFIXaddons WHERE addon_type='".$addon_type."' AND home_cfg_id=" . $home_cfg_id . $query_groups . " ORDER BY name ASC");
|
||||
if (!is_array($addons)) {
|
||||
$addons = [];
|
||||
}
|
||||
foreach ((array)$addons as $addon)
|
||||
{
|
||||
?>
|
||||
<option value="<?php echo $addon['addon_id']; ?>"><?php echo $addon['name']; ?></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<select name="addon_id" id="scm-user-addon-select">
|
||||
<?php foreach ((array)$addons as $addon) { ?>
|
||||
<option
|
||||
value="<?php echo (int)$addon['addon_id']; ?>"
|
||||
data-install-method="<?php echo htmlspecialchars(scm_get_install_method_default(isset($addon['install_method']) ? $addon['install_method'] : ''), ENT_QUOTES, 'UTF-8'); ?>"
|
||||
data-workshop-item-id="<?php echo htmlspecialchars(isset($addon['workshop_item_id']) ? $addon['workshop_item_id'] : '', ENT_QUOTES, 'UTF-8'); ?>"
|
||||
data-workshop-app-id="<?php echo htmlspecialchars(isset($addon['workshop_app_id']) ? $addon['workshop_app_id'] : '', ENT_QUOTES, 'UTF-8'); ?>"
|
||||
data-target-path-template="<?php echo htmlspecialchars(isset($addon['target_path_template']) ? $addon['target_path_template'] : '', ENT_QUOTES, 'UTF-8'); ?>"
|
||||
data-optional-folder-name="<?php echo htmlspecialchars(isset($addon['optional_folder_name']) ? $addon['optional_folder_name'] : '', ENT_QUOTES, 'UTF-8'); ?>"
|
||||
><?php echo htmlspecialchars($addon['name']); ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</td></tr>
|
||||
<tr class="scm-user-workshop-row" <?php echo $is_workshop_default ? '' : 'style="display:none;"'; ?>><td align='right'><strong><?php print_lang('workshop_id'); ?></strong></td><td align='left'>
|
||||
<input type="text" id="scm-user-workshop-id" name="workshop_item_id" size="50" value="<?php echo htmlspecialchars(isset($selected_addon['workshop_item_id']) ? $selected_addon['workshop_item_id'] : '', ENT_QUOTES, 'UTF-8'); ?>" placeholder="Example Arma 3 Workshop ID: 450814997" />
|
||||
<div class="info" style="margin-top:4px;">Install a Steam Workshop mod using Workshop ID. URL is not required.</div>
|
||||
</td></tr>
|
||||
<tr class="scm-user-workshop-row" <?php echo $is_workshop_default ? '' : 'style="display:none;"'; ?>><td align='right'><strong>Workshop App ID Override</strong></td><td align='left'>
|
||||
<input type="text" id="scm-user-workshop-app-id" name="workshop_app_id" size="50" value="<?php echo htmlspecialchars($default_workshop_app_id, ENT_QUOTES, 'UTF-8'); ?>" data-default-app-id="<?php echo htmlspecialchars($default_workshop_app_id, ENT_QUOTES, 'UTF-8'); ?>" placeholder="Optional App ID override" />
|
||||
</td></tr>
|
||||
<tr class="scm-user-workshop-row" <?php echo $is_workshop_default ? '' : 'style="display:none;"'; ?>><td align='right'><strong><?php print_lang('optional_folder_name'); ?></strong></td><td align='left'>
|
||||
<input type="text" id="scm-user-optional-folder-name" name="optional_folder_name" size="50" value="<?php echo htmlspecialchars($default_optional_folder_name, ENT_QUOTES, 'UTF-8'); ?>" placeholder="@myWorkshopMod (optional)" />
|
||||
</td></tr>
|
||||
<tr class="scm-user-workshop-row" <?php echo $is_workshop_default ? '' : 'style="display:none;"'; ?>><td align='right'><strong><?php print_lang('target_path_template'); ?></strong></td><td align='left'>
|
||||
<input type="text" id="scm-user-target-path-template" name="target_path_template" size="85" value="<?php echo htmlspecialchars($default_target_template, ENT_QUOTES, 'UTF-8'); ?>" data-default-template="<?php echo htmlspecialchars($default_target_template, ENT_QUOTES, 'UTF-8'); ?>" placeholder="{SERVER_ROOT}/{MOD_FOLDER}" />
|
||||
<div class="info" style="margin-top:4px;">Supported placeholders: {SERVER_ROOT}, {GAME_ROOT}, {WORKSHOP_ID}, {WORKSHOP_APP_ID}, {STEAM_APP_ID}, {FOLDER_NAME}, {MOD_FOLDER}</div>
|
||||
</td></tr>
|
||||
<tr class="scm-user-workshop-row" <?php echo $is_workshop_default ? '' : 'style="display:none;"'; ?>><td align='right'><strong>Target Path Preview</strong></td><td align='left'>
|
||||
<code id="scm-user-target-path-preview"
|
||||
data-server-root="<?php echo htmlspecialchars(rtrim((string)$home_info['home_path'], '/'), ENT_QUOTES, 'UTF-8'); ?>"
|
||||
data-game-root="<?php echo htmlspecialchars(rtrim((string)$home_info['home_path'], '/'), ENT_QUOTES, 'UTF-8'); ?>"
|
||||
data-steam-app-id="<?php echo htmlspecialchars((is_array($workshop_profile) && !empty($workshop_profile['steam_app_id'])) ? (string)$workshop_profile['steam_app_id'] : '', ENT_QUOTES, 'UTF-8'); ?>"
|
||||
><?php echo htmlspecialchars($default_target_template, ENT_QUOTES, 'UTF-8'); ?></code>
|
||||
</td></tr>
|
||||
<tr><td colspan='2' class='info'> </td></tr>
|
||||
<td align='left'>
|
||||
|
||||
|
|
|
|||
|
|
@ -202,6 +202,7 @@ function exec_ogp_module() {
|
|||
</td>
|
||||
<td align="left">
|
||||
<input type="text" value="<?php echo htmlspecialchars($workshop_item_id, ENT_QUOTES, 'UTF-8'); ?>" name="workshop_item_id" size="85" placeholder="e.g. 450814997" />
|
||||
<small style="color:#666;">Example Arma 3 Workshop ID: 450814997</small>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="scm-row-workshop-app-id">
|
||||
|
|
@ -217,8 +218,8 @@ function exec_ogp_module() {
|
|||
<b><?php print_lang('target_path_template'); ?></b>
|
||||
</td>
|
||||
<td align="left">
|
||||
<input type="text" value="<?php echo htmlspecialchars($target_path_template, ENT_QUOTES, 'UTF-8'); ?>" name="target_path_template" size="85" placeholder="{SERVER_ROOT}/mods/{WORKSHOP_ID}" />
|
||||
<small style="color:#666;">Supported placeholders: {HOME_ID}, {SERVER_ROOT}, {GAME_ROOT}, {WORKSHOP_ID}, {WORKSHOP_APP_ID}, {STEAM_APP_ID}</small>
|
||||
<input type="text" value="<?php echo htmlspecialchars($target_path_template, ENT_QUOTES, 'UTF-8'); ?>" name="target_path_template" size="85" placeholder="{SERVER_ROOT}/{MOD_FOLDER}" />
|
||||
<small style="color:#666;">Supported placeholders: {HOME_ID}, {SERVER_ROOT}, {GAME_ROOT}, {WORKSHOP_ID}, {WORKSHOP_APP_ID}, {STEAM_APP_ID}, {FOLDER_NAME}, {MOD_FOLDER}</small>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="scm-row-optional-folder-name">
|
||||
|
|
|
|||
233
Panel/modules/addonsmanager/scripts/workshop/generic_steam_workshop_linux.sh
Normal file → Executable file
233
Panel/modules/addonsmanager/scripts/workshop/generic_steam_workshop_linux.sh
Normal file → Executable file
|
|
@ -7,57 +7,190 @@ if [[ -z "$MANIFEST_PATH" ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$MANIFEST_PATH" ]]; then
|
||||
echo "Manifest not found: $MANIFEST_PATH"
|
||||
exit 1
|
||||
fi
|
||||
python3 - "$MANIFEST_PATH" <<'PY'
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
MANIFEST_DIR="$(dirname "$MANIFEST_PATH")"
|
||||
WORKSHOP_DIR="${MANIFEST_DIR}/workshop"
|
||||
REMOVED_DIR="${WORKSHOP_DIR}/removed"
|
||||
LOG_FILE="${MANIFEST_DIR}/workshop_phase1.log"
|
||||
manifest_path = os.path.abspath(sys.argv[1])
|
||||
if not os.path.isfile(manifest_path):
|
||||
print(f"Manifest not found: {manifest_path}")
|
||||
sys.exit(1)
|
||||
|
||||
mkdir -p "$WORKSHOP_DIR" "$REMOVED_DIR"
|
||||
manifest_dir = os.path.dirname(manifest_path)
|
||||
home_root = os.path.dirname(manifest_dir)
|
||||
log_file = os.path.join(manifest_dir, 'workshop_install.log')
|
||||
removed_dir = os.path.join(manifest_dir, 'workshop', 'removed')
|
||||
os.makedirs(removed_dir, exist_ok=True)
|
||||
|
||||
ACTION="$(python3 - <<'PY' "$MANIFEST_PATH"
|
||||
import json,sys
|
||||
with open(sys.argv[1], "r", encoding="utf-8") as f:
|
||||
data=json.load(f)
|
||||
print(data.get("action",""))
|
||||
|
||||
def log(message, status=None):
|
||||
line = f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}]"
|
||||
if status:
|
||||
line += f" [{status}]"
|
||||
line += f" {message}"
|
||||
print(line)
|
||||
with open(log_file, 'a', encoding='utf-8') as handle:
|
||||
handle.write(line + "\n")
|
||||
|
||||
|
||||
def fail(message):
|
||||
log(message, 'Failed')
|
||||
raise RuntimeError(message)
|
||||
|
||||
|
||||
def uniq_numeric_items(raw_items):
|
||||
seen = []
|
||||
for value in raw_items:
|
||||
text = str(value).strip()
|
||||
if text.isdigit() and text not in seen:
|
||||
seen.append(text)
|
||||
return seen
|
||||
|
||||
|
||||
def render_template(template, values):
|
||||
rendered = str(template or '')
|
||||
for key, value in values.items():
|
||||
rendered = rendered.replace('{' + key + '}', str(value))
|
||||
return rendered
|
||||
|
||||
|
||||
def ensure_under_home(path_value):
|
||||
target = os.path.abspath(path_value)
|
||||
try:
|
||||
common = os.path.commonpath([home_root, target])
|
||||
except ValueError:
|
||||
common = ''
|
||||
if common != os.path.abspath(home_root):
|
||||
fail(f"Refusing to write outside server home: {target}")
|
||||
return target
|
||||
|
||||
|
||||
def resolve_steamcmd(explicit_path=''):
|
||||
candidates = []
|
||||
explicit_path = str(explicit_path or '').strip()
|
||||
if explicit_path:
|
||||
candidates.append(explicit_path)
|
||||
env_value = os.environ.get('STEAMCMD_PATH', '').strip()
|
||||
if env_value:
|
||||
candidates.append(env_value)
|
||||
for path_value in (
|
||||
'/home/gameserver/steamcmd/steamcmd.sh',
|
||||
shutil.which('steamcmd'),
|
||||
shutil.which('steamcmd.exe'),
|
||||
):
|
||||
if path_value:
|
||||
candidates.append(path_value)
|
||||
for candidate in candidates:
|
||||
if candidate and os.path.isfile(candidate):
|
||||
return candidate
|
||||
fail('SteamCMD is missing on the agent host.')
|
||||
|
||||
|
||||
def sync_copy(src, dst):
|
||||
if not os.path.isdir(src):
|
||||
fail(f"Workshop download source was not found: {src}")
|
||||
os.makedirs(dst, exist_ok=True)
|
||||
for entry in os.listdir(src):
|
||||
source_entry = os.path.join(src, entry)
|
||||
target_entry = os.path.join(dst, entry)
|
||||
if os.path.isdir(source_entry):
|
||||
sync_copy(source_entry, target_entry)
|
||||
else:
|
||||
os.makedirs(os.path.dirname(target_entry), exist_ok=True)
|
||||
shutil.copy2(source_entry, target_entry)
|
||||
|
||||
|
||||
try:
|
||||
with open(manifest_path, 'r', encoding='utf-8') as handle:
|
||||
manifest = json.load(handle)
|
||||
|
||||
extra = manifest.get('extra') or {}
|
||||
action = str(manifest.get('action', '')).strip()
|
||||
raw_items = manifest.get('items', [])
|
||||
if isinstance(raw_items, dict):
|
||||
raw_items = raw_items.get('workshop_item_ids', [])
|
||||
items = uniq_numeric_items(raw_items)
|
||||
if not items:
|
||||
fail('No Workshop IDs were found in the manifest.')
|
||||
|
||||
workshop_app_id = str(extra.get('workshop_app_id') or manifest.get('workshop_app_id') or '').strip()
|
||||
steam_app_id = str(extra.get('steam_app_id') or manifest.get('steam_app_id') or '').strip()
|
||||
server_root = ensure_under_home(extra.get('server_root') or home_root)
|
||||
steamcmd_path = resolve_steamcmd(extra.get('steamcmd_path') or '')
|
||||
post_install_script = str(extra.get('post_install_script') or '').strip()
|
||||
default_download_dir = extra.get('workshop_download_dir') or os.path.join(server_root, 'steamapps', 'workshop', 'content', workshop_app_id or steam_app_id)
|
||||
|
||||
action_label = 'Queued' if action in ('install', 'update', 'check_updates') else action
|
||||
log(f"action={action} manifest={manifest_path} steam_app_id={steam_app_id or 'n/a'} workshop_app_id={workshop_app_id or 'n/a'}", action_label)
|
||||
|
||||
for workshop_id in items:
|
||||
folder_name = str(extra.get('optional_folder_name') or '').strip() or ('@' + workshop_id)
|
||||
template_values = {
|
||||
'HOME_ID': manifest.get('home_id', ''),
|
||||
'SERVER_ROOT': server_root,
|
||||
'GAME_ROOT': server_root,
|
||||
'WORKSHOP_ID': workshop_id,
|
||||
'WORKSHOP_APP_ID': workshop_app_id,
|
||||
'STEAM_APP_ID': steam_app_id,
|
||||
'FOLDER_NAME': folder_name,
|
||||
'MOD_FOLDER': folder_name,
|
||||
}
|
||||
target_template = str(extra.get('target_path_template') or '{SERVER_ROOT}/{MOD_FOLDER}')
|
||||
target_path = str(extra.get('target_path_resolved') or '').strip()
|
||||
if len(items) != 1 or not target_path:
|
||||
target_path = render_template(target_template, template_values)
|
||||
target_path = ensure_under_home(target_path)
|
||||
|
||||
download_dir = ensure_under_home(render_template(default_download_dir, template_values))
|
||||
source_path = os.path.join(download_dir, workshop_id)
|
||||
|
||||
if action in ('install', 'update', 'check_updates'):
|
||||
if not workshop_app_id:
|
||||
fail(f"Workshop App ID is missing for Workshop item {workshop_id}.")
|
||||
command = [
|
||||
steamcmd_path,
|
||||
'+force_install_dir', server_root,
|
||||
'+login', 'anonymous',
|
||||
'+workshop_download_item', workshop_app_id, workshop_id, 'validate',
|
||||
'+quit',
|
||||
]
|
||||
log(f"workshop_id={workshop_id} steamcmd={' '.join(command)}", 'Downloading Workshop Item')
|
||||
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, cwd=server_root)
|
||||
if result.stdout:
|
||||
for line in result.stdout.splitlines():
|
||||
log(f"steamcmd[{workshop_id}] {line}")
|
||||
if result.returncode != 0:
|
||||
fail(f"SteamCMD failed for Workshop item {workshop_id} with exit code {result.returncode}.")
|
||||
if not os.path.isdir(source_path):
|
||||
fail(f"SteamCMD did not create the expected Workshop cache path: {source_path}")
|
||||
|
||||
if action != 'check_updates':
|
||||
log(f"workshop_id={workshop_id} install_path={target_path}", 'Extracting/Copying')
|
||||
sync_copy(source_path, target_path)
|
||||
log(f"workshop_id={workshop_id} final_folder_path={target_path}", 'Applying Folder Name')
|
||||
if post_install_script:
|
||||
log(f"workshop_id={workshop_id} cwd={server_root}", 'Running Post-install Script')
|
||||
post_result = subprocess.run(['bash', '-lc', post_install_script], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, cwd=server_root)
|
||||
if post_result.stdout:
|
||||
for line in post_result.stdout.splitlines():
|
||||
log(f"post_install[{workshop_id}] {line}")
|
||||
if post_result.returncode != 0:
|
||||
fail(f"Post-install script failed for Workshop item {workshop_id} with exit code {post_result.returncode}.")
|
||||
log(f"workshop_id={workshop_id} install_path={target_path}", 'Completed')
|
||||
elif action == 'remove':
|
||||
if os.path.exists(target_path):
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
removed_path = os.path.join(removed_dir, f"{workshop_id}_{timestamp}")
|
||||
shutil.move(target_path, removed_path)
|
||||
log(f"workshop_id={workshop_id} removed_path={removed_path}", 'Completed')
|
||||
else:
|
||||
log(f"workshop_id={workshop_id} target_path_missing={target_path}", 'Completed')
|
||||
else:
|
||||
fail(f"Unknown workshop action: {action}")
|
||||
except RuntimeError:
|
||||
sys.exit(1)
|
||||
PY
|
||||
)"
|
||||
|
||||
ITEMS="$(python3 - <<'PY' "$MANIFEST_PATH"
|
||||
import json,sys
|
||||
with open(sys.argv[1], "r", encoding="utf-8") as f:
|
||||
data=json.load(f)
|
||||
items=data.get("items",[])
|
||||
print(",".join(str(x) for x in items if str(x).isdigit()))
|
||||
PY
|
||||
)"
|
||||
|
||||
{
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] workshop_phase1 action=${ACTION} manifest=${MANIFEST_PATH}"
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] workshop_phase1 items=${ITEMS}"
|
||||
} >> "$LOG_FILE"
|
||||
|
||||
case "$ACTION" in
|
||||
install|update)
|
||||
# TODO: Replace with game-specific SteamCMD workshop install/update logic.
|
||||
# Example flow:
|
||||
# 1) Use workshop_app_id + item IDs from the manifest.
|
||||
# 2) Download/refresh content into a controlled staging folder.
|
||||
# 3) Copy/sync approved files into the game server content path.
|
||||
;;
|
||||
remove)
|
||||
# Phase 1 safety behavior: avoid destructive delete.
|
||||
# TODO: move/disable per-item content using game-specific mapping rules.
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] remove requested; preserving files (non-destructive phase 1)." >> "$LOG_FILE"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown workshop action: ${ACTION}" >> "$LOG_FILE"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
|
|
|||
229
Panel/modules/addonsmanager/scripts/workshop/generic_steam_workshop_windows_cygwin.sh
Normal file → Executable file
229
Panel/modules/addonsmanager/scripts/workshop/generic_steam_workshop_windows_cygwin.sh
Normal file → Executable file
|
|
@ -7,52 +7,191 @@ if [[ -z "$MANIFEST_PATH" ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$MANIFEST_PATH" ]]; then
|
||||
echo "Manifest not found: $MANIFEST_PATH"
|
||||
exit 1
|
||||
fi
|
||||
python3 - "$MANIFEST_PATH" <<'PY'
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
MANIFEST_DIR="$(dirname "$MANIFEST_PATH")"
|
||||
WORKSHOP_DIR="${MANIFEST_DIR}/workshop"
|
||||
REMOVED_DIR="${WORKSHOP_DIR}/removed"
|
||||
LOG_FILE="${MANIFEST_DIR}/workshop_phase1_windows.log"
|
||||
manifest_path = os.path.abspath(sys.argv[1])
|
||||
if not os.path.isfile(manifest_path):
|
||||
print(f"Manifest not found: {manifest_path}")
|
||||
sys.exit(1)
|
||||
|
||||
mkdir -p "$WORKSHOP_DIR" "$REMOVED_DIR"
|
||||
manifest_dir = os.path.dirname(manifest_path)
|
||||
home_root = os.path.dirname(manifest_dir)
|
||||
log_file = os.path.join(manifest_dir, 'workshop_install_windows.log')
|
||||
removed_dir = os.path.join(manifest_dir, 'workshop', 'removed')
|
||||
os.makedirs(removed_dir, exist_ok=True)
|
||||
|
||||
ACTION="$(python3 - <<'PY' "$MANIFEST_PATH"
|
||||
import json,sys
|
||||
with open(sys.argv[1], "r", encoding="utf-8") as f:
|
||||
data=json.load(f)
|
||||
print(data.get("action",""))
|
||||
|
||||
def log(message, status=None):
|
||||
line = f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}]"
|
||||
if status:
|
||||
line += f" [{status}]"
|
||||
line += f" {message}"
|
||||
print(line)
|
||||
with open(log_file, 'a', encoding='utf-8') as handle:
|
||||
handle.write(line + "\n")
|
||||
|
||||
|
||||
def fail(message):
|
||||
log(message, 'Failed')
|
||||
raise RuntimeError(message)
|
||||
|
||||
|
||||
def uniq_numeric_items(raw_items):
|
||||
seen = []
|
||||
for value in raw_items:
|
||||
text = str(value).strip()
|
||||
if text.isdigit() and text not in seen:
|
||||
seen.append(text)
|
||||
return seen
|
||||
|
||||
|
||||
def render_template(template, values):
|
||||
rendered = str(template or '')
|
||||
for key, value in values.items():
|
||||
rendered = rendered.replace('{' + key + '}', str(value))
|
||||
return rendered
|
||||
|
||||
|
||||
def ensure_under_home(path_value):
|
||||
target = os.path.abspath(path_value)
|
||||
try:
|
||||
common = os.path.commonpath([home_root, target])
|
||||
except ValueError:
|
||||
common = ''
|
||||
if common != os.path.abspath(home_root):
|
||||
fail(f"Refusing to write outside server home: {target}")
|
||||
return target
|
||||
|
||||
|
||||
def resolve_steamcmd(explicit_path=''):
|
||||
candidates = []
|
||||
explicit_path = str(explicit_path or '').strip()
|
||||
if explicit_path:
|
||||
candidates.append(explicit_path)
|
||||
env_value = os.environ.get('STEAMCMD_PATH', '').strip()
|
||||
if env_value:
|
||||
candidates.append(env_value)
|
||||
for path_value in (
|
||||
'/home/gameserver/steamcmd/steamcmd.sh',
|
||||
shutil.which('steamcmd'),
|
||||
shutil.which('steamcmd.exe'),
|
||||
shutil.which('steamcmd.sh'),
|
||||
):
|
||||
if path_value:
|
||||
candidates.append(path_value)
|
||||
for candidate in candidates:
|
||||
if candidate and os.path.isfile(candidate):
|
||||
return candidate
|
||||
fail('SteamCMD is missing on the agent host.')
|
||||
|
||||
|
||||
def sync_copy(src, dst):
|
||||
if not os.path.isdir(src):
|
||||
fail(f"Workshop download source was not found: {src}")
|
||||
os.makedirs(dst, exist_ok=True)
|
||||
for entry in os.listdir(src):
|
||||
source_entry = os.path.join(src, entry)
|
||||
target_entry = os.path.join(dst, entry)
|
||||
if os.path.isdir(source_entry):
|
||||
sync_copy(source_entry, target_entry)
|
||||
else:
|
||||
os.makedirs(os.path.dirname(target_entry), exist_ok=True)
|
||||
shutil.copy2(source_entry, target_entry)
|
||||
|
||||
|
||||
try:
|
||||
with open(manifest_path, 'r', encoding='utf-8') as handle:
|
||||
manifest = json.load(handle)
|
||||
|
||||
extra = manifest.get('extra') or {}
|
||||
action = str(manifest.get('action', '')).strip()
|
||||
raw_items = manifest.get('items', [])
|
||||
if isinstance(raw_items, dict):
|
||||
raw_items = raw_items.get('workshop_item_ids', [])
|
||||
items = uniq_numeric_items(raw_items)
|
||||
if not items:
|
||||
fail('No Workshop IDs were found in the manifest.')
|
||||
|
||||
workshop_app_id = str(extra.get('workshop_app_id') or manifest.get('workshop_app_id') or '').strip()
|
||||
steam_app_id = str(extra.get('steam_app_id') or manifest.get('steam_app_id') or '').strip()
|
||||
server_root = ensure_under_home(extra.get('server_root') or home_root)
|
||||
steamcmd_path = resolve_steamcmd(extra.get('steamcmd_path') or '')
|
||||
post_install_script = str(extra.get('post_install_script') or '').strip()
|
||||
default_download_dir = extra.get('workshop_download_dir') or os.path.join(server_root, 'steamapps', 'workshop', 'content', workshop_app_id or steam_app_id)
|
||||
|
||||
action_label = 'Queued' if action in ('install', 'update', 'check_updates') else action
|
||||
log(f"action={action} manifest={manifest_path} steam_app_id={steam_app_id or 'n/a'} workshop_app_id={workshop_app_id or 'n/a'}", action_label)
|
||||
|
||||
for workshop_id in items:
|
||||
folder_name = str(extra.get('optional_folder_name') or '').strip() or ('@' + workshop_id)
|
||||
template_values = {
|
||||
'HOME_ID': manifest.get('home_id', ''),
|
||||
'SERVER_ROOT': server_root,
|
||||
'GAME_ROOT': server_root,
|
||||
'WORKSHOP_ID': workshop_id,
|
||||
'WORKSHOP_APP_ID': workshop_app_id,
|
||||
'STEAM_APP_ID': steam_app_id,
|
||||
'FOLDER_NAME': folder_name,
|
||||
'MOD_FOLDER': folder_name,
|
||||
}
|
||||
target_template = str(extra.get('target_path_template') or '{SERVER_ROOT}/{MOD_FOLDER}')
|
||||
target_path = str(extra.get('target_path_resolved') or '').strip()
|
||||
if len(items) != 1 or not target_path:
|
||||
target_path = render_template(target_template, template_values)
|
||||
target_path = ensure_under_home(target_path)
|
||||
|
||||
download_dir = ensure_under_home(render_template(default_download_dir, template_values))
|
||||
source_path = os.path.join(download_dir, workshop_id)
|
||||
|
||||
if action in ('install', 'update', 'check_updates'):
|
||||
if not workshop_app_id:
|
||||
fail(f"Workshop App ID is missing for Workshop item {workshop_id}.")
|
||||
command = [
|
||||
steamcmd_path,
|
||||
'+force_install_dir', server_root,
|
||||
'+login', 'anonymous',
|
||||
'+workshop_download_item', workshop_app_id, workshop_id, 'validate',
|
||||
'+quit',
|
||||
]
|
||||
log(f"workshop_id={workshop_id} steamcmd={' '.join(command)}", 'Downloading Workshop Item')
|
||||
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, cwd=server_root)
|
||||
if result.stdout:
|
||||
for line in result.stdout.splitlines():
|
||||
log(f"steamcmd[{workshop_id}] {line}")
|
||||
if result.returncode != 0:
|
||||
fail(f"SteamCMD failed for Workshop item {workshop_id} with exit code {result.returncode}.")
|
||||
if not os.path.isdir(source_path):
|
||||
fail(f"SteamCMD did not create the expected Workshop cache path: {source_path}")
|
||||
|
||||
if action != 'check_updates':
|
||||
log(f"workshop_id={workshop_id} install_path={target_path}", 'Extracting/Copying')
|
||||
sync_copy(source_path, target_path)
|
||||
log(f"workshop_id={workshop_id} final_folder_path={target_path}", 'Applying Folder Name')
|
||||
if post_install_script:
|
||||
log(f"workshop_id={workshop_id} cwd={server_root}", 'Running Post-install Script')
|
||||
post_result = subprocess.run(['bash', '-lc', post_install_script], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, cwd=server_root)
|
||||
if post_result.stdout:
|
||||
for line in post_result.stdout.splitlines():
|
||||
log(f"post_install[{workshop_id}] {line}")
|
||||
if post_result.returncode != 0:
|
||||
fail(f"Post-install script failed for Workshop item {workshop_id} with exit code {post_result.returncode}.")
|
||||
log(f"workshop_id={workshop_id} install_path={target_path}", 'Completed')
|
||||
elif action == 'remove':
|
||||
if os.path.exists(target_path):
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
removed_path = os.path.join(removed_dir, f"{workshop_id}_{timestamp}")
|
||||
shutil.move(target_path, removed_path)
|
||||
log(f"workshop_id={workshop_id} removed_path={removed_path}", 'Completed')
|
||||
else:
|
||||
log(f"workshop_id={workshop_id} target_path_missing={target_path}", 'Completed')
|
||||
else:
|
||||
fail(f"Unknown workshop action: {action}")
|
||||
except RuntimeError:
|
||||
sys.exit(1)
|
||||
PY
|
||||
)"
|
||||
|
||||
ITEMS="$(python3 - <<'PY' "$MANIFEST_PATH"
|
||||
import json,sys
|
||||
with open(sys.argv[1], "r", encoding="utf-8") as f:
|
||||
data=json.load(f)
|
||||
items=data.get("items",[])
|
||||
print(",".join(str(x) for x in items if str(x).isdigit()))
|
||||
PY
|
||||
)"
|
||||
|
||||
{
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] workshop_phase1_windows action=${ACTION} manifest=${MANIFEST_PATH}"
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] workshop_phase1_windows items=${ITEMS}"
|
||||
} >> "$LOG_FILE"
|
||||
|
||||
case "$ACTION" in
|
||||
install|update)
|
||||
# TODO: Replace with game-specific SteamCMD workshop install/update logic for Cygwin environments.
|
||||
;;
|
||||
remove)
|
||||
# Phase 1 safety behavior: avoid destructive delete.
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] remove requested; preserving files (non-destructive phase 1)." >> "$LOG_FILE"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown workshop action: ${ACTION}" >> "$LOG_FILE"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
|
|
|||
|
|
@ -176,7 +176,15 @@ function server_content_execute_manifest($home_id, $manifest_path, $script_key,
|
|||
if ($remote->status_chk() !== 1) {
|
||||
return server_content_result('failed', 'Agent is offline.', array('remote_server_id' => (int)$home_info['remote_server_id']));
|
||||
}
|
||||
if ((int)$remote->rfile_exists($script_path) !== 1) {
|
||||
if ($script_key === 'workshop') {
|
||||
$prepare_error = '';
|
||||
$prepared_path = scm_prepare_workshop_script_for_agent($remote, $home_info, $server_xml, $prepare_error);
|
||||
if ($prepared_path === false) {
|
||||
return server_content_result('failed', $prepare_error, array('script_key' => (string)$script_key));
|
||||
}
|
||||
$script_path = $prepared_path;
|
||||
}
|
||||
elseif ((int)$remote->rfile_exists($script_path) !== 1) {
|
||||
return server_content_result('failed', 'Server content script was not found on agent host.', array('script_path' => $script_path));
|
||||
}
|
||||
$command = "bash " . escapeshellarg($script_path) . " " . escapeshellarg((string)$manifest_path) . " ; echo __GSP_SERVER_CONTENT_EXIT:$?";
|
||||
|
|
@ -469,4 +477,3 @@ function server_content_run_scheduled_action($home_id, $action, $options = array
|
|||
server_content_log_action($home_id, $action, $result['status'], $result['message'], $result['details']);
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,9 +40,9 @@
|
|||
function get_server_content_categories()
|
||||
{
|
||||
return array(
|
||||
'file_download' => 'File Download / Archive',
|
||||
'file_download' => 'Downloadable Mod',
|
||||
'workshop_item' => 'Steam Workshop Item',
|
||||
'config_edit' => 'Config Edit',
|
||||
'config_edit' => 'Configuration Package',
|
||||
'scripted_installer' => 'Scripted Installer',
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@
|
|||
*/
|
||||
|
||||
if (!defined('SCM_WORKSHOP_SCRIPT_LINUX_DEFAULT')) {
|
||||
define('SCM_WORKSHOP_SCRIPT_LINUX_DEFAULT', '/var/www/html/GSP/Panel/modules/addonsmanager/scripts/workshop/generic_steam_workshop_linux.sh');
|
||||
define('SCM_WORKSHOP_SCRIPT_LINUX_DEFAULT', 'generic_steam_workshop_linux.sh');
|
||||
}
|
||||
if (!defined('SCM_WORKSHOP_SCRIPT_WINDOWS_DEFAULT')) {
|
||||
define('SCM_WORKSHOP_SCRIPT_WINDOWS_DEFAULT', '/var/www/html/GSP/Panel/modules/addonsmanager/scripts/workshop/generic_steam_workshop_windows_cygwin.sh');
|
||||
define('SCM_WORKSHOP_SCRIPT_WINDOWS_DEFAULT', 'generic_steam_workshop_windows_cygwin.sh');
|
||||
}
|
||||
|
||||
function scm_ensure_workshop_schema($db)
|
||||
|
|
@ -182,6 +182,73 @@ function scm_get_workshop_script_path(array $home_info, $server_xml)
|
|||
return scm_is_windows_home($home_info) ? SCM_WORKSHOP_SCRIPT_WINDOWS_DEFAULT : SCM_WORKSHOP_SCRIPT_LINUX_DEFAULT;
|
||||
}
|
||||
|
||||
function scm_get_bundled_workshop_script_source(array $home_info)
|
||||
{
|
||||
$filename = scm_is_windows_home($home_info) ? SCM_WORKSHOP_SCRIPT_WINDOWS_DEFAULT : SCM_WORKSHOP_SCRIPT_LINUX_DEFAULT;
|
||||
return dirname(__FILE__) . '/scripts/workshop/' . $filename;
|
||||
}
|
||||
|
||||
function scm_is_legacy_panel_workshop_script_path($script_path)
|
||||
{
|
||||
$script_path = trim((string)$script_path);
|
||||
if ($script_path === '') {
|
||||
return false;
|
||||
}
|
||||
return (strpos($script_path, '/var/www/html/') === 0 && strpos($script_path, '/Panel/modules/addonsmanager/scripts/workshop/') !== false)
|
||||
|| (strpos($script_path, '/OGP_User_Files/modules/addonsmanager/scripts/') !== false);
|
||||
}
|
||||
|
||||
function scm_get_agent_managed_workshop_script_path(array $home_info)
|
||||
{
|
||||
$home_path = rtrim(clean_path((string)$home_info['home_path']), '/');
|
||||
$filename = scm_is_windows_home($home_info) ? SCM_WORKSHOP_SCRIPT_WINDOWS_DEFAULT : SCM_WORKSHOP_SCRIPT_LINUX_DEFAULT;
|
||||
$remote_path = clean_path($home_path . '/gsp_server_content/scripts/workshop/' . $filename);
|
||||
if (!scm_path_is_under_home($home_path, $remote_path)) {
|
||||
return false;
|
||||
}
|
||||
return $remote_path;
|
||||
}
|
||||
|
||||
function scm_prepare_workshop_script_for_agent($remote, array $home_info, $server_xml, &$error = '')
|
||||
{
|
||||
$error = '';
|
||||
$configured_path = trim((string)scm_get_workshop_script_path($home_info, $server_xml));
|
||||
if ($configured_path !== '' && !scm_is_legacy_panel_workshop_script_path($configured_path) && preg_match('/^[^\\r\\n\\0]+$/', $configured_path)) {
|
||||
if ((int)$remote->rfile_exists($configured_path) === 1) {
|
||||
return $configured_path;
|
||||
}
|
||||
$error = 'Configured workshop script not found on agent host: ' . $configured_path;
|
||||
return false;
|
||||
}
|
||||
|
||||
$source_path = scm_get_bundled_workshop_script_source($home_info);
|
||||
if (!is_file($source_path)) {
|
||||
$error = 'Bundled workshop script is missing from the panel: ' . $source_path;
|
||||
return false;
|
||||
}
|
||||
|
||||
$remote_path = scm_get_agent_managed_workshop_script_path($home_info);
|
||||
if ($remote_path === false) {
|
||||
$error = 'Unable to resolve an agent-managed workshop script path for this server.';
|
||||
return false;
|
||||
}
|
||||
|
||||
$script_body = @file_get_contents($source_path);
|
||||
if ($script_body === false || $script_body === '') {
|
||||
$error = 'Failed to read bundled workshop script: ' . $source_path;
|
||||
return false;
|
||||
}
|
||||
|
||||
$remote_dir = dirname($remote_path);
|
||||
$remote->exec("mkdir -p " . escapeshellarg($remote_dir));
|
||||
if ((int)$remote->remote_writefile($remote_path, $script_body) !== 1) {
|
||||
$error = 'Failed to sync workshop script to agent host.';
|
||||
return false;
|
||||
}
|
||||
$remote->exec("chmod 755 " . escapeshellarg($remote_path) . " >/dev/null 2>&1 || true");
|
||||
return $remote_path;
|
||||
}
|
||||
|
||||
function scm_get_csrf_token()
|
||||
{
|
||||
if (empty($_SESSION['addonsmanager_workshop_csrf'])) {
|
||||
|
|
@ -255,9 +322,9 @@ function scm_get_cache_mode($db)
|
|||
function scm_get_install_methods()
|
||||
{
|
||||
return array(
|
||||
'download_zip' => 'File Download / Archive',
|
||||
'download_zip' => 'Downloadable Mod',
|
||||
'steam_workshop' => 'Steam Workshop Item',
|
||||
'config_edit' => 'Config Edit',
|
||||
'config_edit' => 'Configuration Package',
|
||||
'post_script' => 'Scripted Installer',
|
||||
);
|
||||
}
|
||||
|
|
@ -265,10 +332,10 @@ function scm_get_install_methods()
|
|||
function scm_get_install_method_help_text()
|
||||
{
|
||||
return array(
|
||||
'download_zip' => 'Downloads an archive or file from URL; extract path is optional.',
|
||||
'steam_workshop' => 'Installs a Steam Workshop item by Workshop ID without requiring URL.',
|
||||
'config_edit' => 'Applies config edits to the target file/path without requiring URL.',
|
||||
'post_script' => 'Runs an installer script/action body without requiring URL.',
|
||||
'download_zip' => 'Download and extract a ZIP, RAR, or archive file.',
|
||||
'steam_workshop' => 'Install a Steam Workshop mod using Workshop ID.',
|
||||
'config_edit' => 'Install configuration files, profiles, or templates.',
|
||||
'post_script' => 'Run a custom scripted installation process.',
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -305,52 +372,201 @@ function scm_get_install_method_default($value = '')
|
|||
return isset($methods[$value]) ? $value : 'download_zip';
|
||||
}
|
||||
|
||||
function scm_validate_install_method_payload($install_method, array $payload, &$message = '')
|
||||
function scm_get_install_payload_keys()
|
||||
{
|
||||
$install_method = scm_get_install_method_default($install_method);
|
||||
$required = scm_get_install_method_required_fields();
|
||||
$errors = scm_get_install_method_validation_errors();
|
||||
if (!isset($required[$install_method])) {
|
||||
$message = 'Invalid install/content type selected.';
|
||||
return array(
|
||||
'url',
|
||||
'path',
|
||||
'workshop_item_id',
|
||||
'workshop_app_id',
|
||||
'target_path_template',
|
||||
'optional_folder_name',
|
||||
'post_script',
|
||||
'config_edit_rule',
|
||||
'launch_param_additions',
|
||||
);
|
||||
}
|
||||
|
||||
function scm_collect_install_payload(array $defaults = array(), array $request = array(), array $override_keys = array())
|
||||
{
|
||||
$payload = array();
|
||||
foreach (scm_get_install_payload_keys() as $key) {
|
||||
$payload[$key] = isset($defaults[$key]) ? trim((string)$defaults[$key]) : '';
|
||||
}
|
||||
foreach ($override_keys as $key) {
|
||||
if (array_key_exists($key, $request)) {
|
||||
$payload[$key] = trim((string)$request[$key]);
|
||||
}
|
||||
}
|
||||
return $payload;
|
||||
}
|
||||
|
||||
function scm_validate_numeric_content_value($value, $error_message, &$message, $allow_blank = false)
|
||||
{
|
||||
$value = trim((string)$value);
|
||||
if ($value === '') {
|
||||
if ($allow_blank) {
|
||||
$message = '';
|
||||
return true;
|
||||
}
|
||||
$message = $error_message;
|
||||
return false;
|
||||
}
|
||||
if ($install_method === 'config_edit') {
|
||||
$path = isset($payload['path']) ? trim((string)$payload['path']) : '';
|
||||
$rule = isset($payload['config_edit_rule']) ? trim((string)$payload['config_edit_rule']) : '';
|
||||
if ($path === '' || $rule === '') {
|
||||
$message = $errors['config_edit'];
|
||||
return false;
|
||||
}
|
||||
$message = '';
|
||||
return true;
|
||||
}
|
||||
if ($install_method === 'post_script') {
|
||||
$script = isset($payload['post_script']) ? trim((string)$payload['post_script']) : '';
|
||||
if ($script === '') {
|
||||
$message = $errors['post_script'];
|
||||
return false;
|
||||
}
|
||||
$message = '';
|
||||
return true;
|
||||
}
|
||||
foreach ($required[$install_method] as $field) {
|
||||
$value = isset($payload[$field]) ? trim((string)$payload[$field]) : '';
|
||||
if ($value === '') {
|
||||
$message = isset($errors[$install_method]) ? $errors[$install_method] : 'Missing required field.';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ($install_method === 'steam_workshop') {
|
||||
$wid = isset($payload['workshop_item_id']) ? trim((string)$payload['workshop_item_id']) : '';
|
||||
if ($wid === '' || !preg_match('/^[0-9]+$/', $wid)) {
|
||||
$message = 'Please enter a Workshop ID.';
|
||||
return false;
|
||||
}
|
||||
if (!preg_match('/^[0-9]+$/', $value)) {
|
||||
$message = $error_message;
|
||||
return false;
|
||||
}
|
||||
$message = '';
|
||||
return true;
|
||||
}
|
||||
|
||||
function scm_validate_download_content(array $payload, &$message = '')
|
||||
{
|
||||
$url = isset($payload['url']) ? trim((string)$payload['url']) : '';
|
||||
if ($url === '') {
|
||||
$message = 'Please enter a download URL.';
|
||||
return false;
|
||||
}
|
||||
$message = '';
|
||||
return true;
|
||||
}
|
||||
|
||||
function scm_validate_workshop_content(array $payload, &$message = '')
|
||||
{
|
||||
if (!scm_validate_numeric_content_value(isset($payload['workshop_item_id']) ? $payload['workshop_item_id'] : '', 'Please enter a Workshop ID.', $message, false)) {
|
||||
return false;
|
||||
}
|
||||
if (!scm_validate_numeric_content_value(isset($payload['workshop_app_id']) ? $payload['workshop_app_id'] : '', 'Workshop App ID must be numeric.', $message, true)) {
|
||||
return false;
|
||||
}
|
||||
$folder_name = isset($payload['optional_folder_name']) ? trim((string)$payload['optional_folder_name']) : '';
|
||||
if ($folder_name !== '' && (strpos($folder_name, '..') !== false || preg_match('/[\\\\\\/]/', $folder_name))) {
|
||||
$message = 'Optional folder name must be a single folder name.';
|
||||
return false;
|
||||
}
|
||||
$message = '';
|
||||
return true;
|
||||
}
|
||||
|
||||
function scm_validate_scripted_installer(array $payload, &$message = '')
|
||||
{
|
||||
$script = isset($payload['post_script']) ? trim((string)$payload['post_script']) : '';
|
||||
if ($script === '') {
|
||||
$message = 'Please enter the installer script/action.';
|
||||
return false;
|
||||
}
|
||||
$message = '';
|
||||
return true;
|
||||
}
|
||||
|
||||
function scm_validate_configuration_package(array $payload, &$message = '')
|
||||
{
|
||||
$path = isset($payload['path']) ? trim((string)$payload['path']) : '';
|
||||
$rule = isset($payload['config_edit_rule']) ? trim((string)$payload['config_edit_rule']) : '';
|
||||
if ($path === '' || $rule === '') {
|
||||
$message = 'Please enter the config target and edit action.';
|
||||
return false;
|
||||
}
|
||||
$message = '';
|
||||
return true;
|
||||
}
|
||||
|
||||
function scm_validate_install_method_payload($install_method, array $payload, &$message = '')
|
||||
{
|
||||
$install_method = scm_get_install_method_default($install_method);
|
||||
if (!isset(scm_get_install_method_required_fields()[$install_method])) {
|
||||
$message = 'Invalid install/content type selected.';
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($install_method === 'download_zip') {
|
||||
return scm_validate_download_content($payload, $message);
|
||||
}
|
||||
if ($install_method === 'steam_workshop') {
|
||||
return scm_validate_workshop_content($payload, $message);
|
||||
}
|
||||
if ($install_method === 'post_script') {
|
||||
return scm_validate_scripted_installer($payload, $message);
|
||||
}
|
||||
if ($install_method === 'config_edit') {
|
||||
return scm_validate_configuration_package($payload, $message);
|
||||
}
|
||||
$message = '';
|
||||
return true;
|
||||
}
|
||||
|
||||
function scm_build_workshop_runtime_context($db, array $home_info, $server_xml, array $payload, &$message = '')
|
||||
{
|
||||
if (!scm_validate_workshop_content($payload, $message)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$workshop_item_id = trim((string)$payload['workshop_item_id']);
|
||||
$target_path_template = trim((string)$payload['target_path_template']);
|
||||
$optional_folder_name = trim((string)$payload['optional_folder_name']);
|
||||
$workshop_app_id_override = trim((string)$payload['workshop_app_id']);
|
||||
$fallback_profile = function_exists('sw_get_profile_for_home') ? sw_get_profile_for_home($db, (int)$home_info['home_id']) : false;
|
||||
$resolved = function_exists('steam_workshop_install_item_to_home')
|
||||
? steam_workshop_install_item_to_home($db, $home_info, $workshop_item_id, $target_path_template, array(
|
||||
'optional_folder_name' => $optional_folder_name,
|
||||
'workshop_app_id' => $workshop_app_id_override,
|
||||
))
|
||||
: array('ok' => false);
|
||||
|
||||
if (!empty($resolved['ok'])) {
|
||||
$profile = (isset($resolved['profile']) && is_array($resolved['profile'])) ? $resolved['profile'] : array();
|
||||
$message = '';
|
||||
return array(
|
||||
'workshop_item_id' => $workshop_item_id,
|
||||
'workshop_app_id' => isset($resolved['workshop_app_id']) ? (string)$resolved['workshop_app_id'] : '',
|
||||
'steam_app_id' => isset($resolved['steam_app_id']) ? (string)$resolved['steam_app_id'] : '',
|
||||
'folder_name' => isset($resolved['folder_name']) ? (string)$resolved['folder_name'] : ($optional_folder_name !== '' ? $optional_folder_name : '@' . $workshop_item_id),
|
||||
'target_path_template' => isset($resolved['target_path_template']) ? (string)$resolved['target_path_template'] : $target_path_template,
|
||||
'target_path_resolved' => isset($resolved['target_path_resolved']) ? (string)$resolved['target_path_resolved'] : '',
|
||||
'server_root' => rtrim((string)$home_info['home_path'], '/'),
|
||||
'steamcmd_path' => isset($profile['steamcmd_path']) ? trim((string)$profile['steamcmd_path']) : '',
|
||||
'workshop_download_dir' => (isset($profile['workshop_download_dir_template']) && trim((string)$profile['workshop_download_dir_template']) !== '')
|
||||
? sw_apply_template((string)$profile['workshop_download_dir_template'], (array)$resolved['vars'])
|
||||
: '',
|
||||
);
|
||||
}
|
||||
|
||||
$fallback_workshop_app_id = $workshop_app_id_override;
|
||||
if ($fallback_workshop_app_id === '' && is_array($fallback_profile) && !empty($fallback_profile['workshop_app_id'])) {
|
||||
$fallback_workshop_app_id = (string)$fallback_profile['workshop_app_id'];
|
||||
}
|
||||
if ($fallback_workshop_app_id === '') {
|
||||
$fallback_workshop_app_id = scm_extract_workshop_app_id($server_xml);
|
||||
}
|
||||
$steam_app_id = (is_array($fallback_profile) && !empty($fallback_profile['steam_app_id'])) ? (string)$fallback_profile['steam_app_id'] : '';
|
||||
$folder_name = ($optional_folder_name !== '') ? $optional_folder_name : '@' . $workshop_item_id;
|
||||
$effective_template = $target_path_template;
|
||||
if ($effective_template === '') {
|
||||
$effective_template = (is_array($fallback_profile) && !empty($fallback_profile['install_path_template']))
|
||||
? (string)$fallback_profile['install_path_template']
|
||||
: '{SERVER_ROOT}/{MOD_FOLDER}';
|
||||
}
|
||||
$placeholder_map = scm_build_placeholder_map($home_info, array('exe_location' => isset($server_xml->exe_location) ? (string)$server_xml->exe_location : ''), array(
|
||||
'WORKSHOP_ID' => $workshop_item_id,
|
||||
'WORKSHOP_APP_ID' => $fallback_workshop_app_id,
|
||||
'STEAM_APP_ID' => $steam_app_id,
|
||||
'FOLDER_NAME' => $folder_name,
|
||||
'MOD_FOLDER' => $folder_name,
|
||||
));
|
||||
$message = '';
|
||||
return array(
|
||||
'workshop_item_id' => $workshop_item_id,
|
||||
'workshop_app_id' => $fallback_workshop_app_id,
|
||||
'steam_app_id' => $steam_app_id,
|
||||
'folder_name' => $folder_name,
|
||||
'target_path_template' => $effective_template,
|
||||
'target_path_resolved' => scm_apply_placeholders($effective_template, $placeholder_map),
|
||||
'server_root' => rtrim((string)$home_info['home_path'], '/'),
|
||||
'steamcmd_path' => (is_array($fallback_profile) && !empty($fallback_profile['steamcmd_path'])) ? (string)$fallback_profile['steamcmd_path'] : '',
|
||||
'workshop_download_dir' => '',
|
||||
);
|
||||
}
|
||||
|
||||
function scm_build_placeholder_map(array $home_info, array $server_context = array(), array $overrides = array())
|
||||
{
|
||||
$home_id = (int)(isset($home_info['home_id']) ? $home_info['home_id'] : 0);
|
||||
|
|
@ -370,6 +586,8 @@ function scm_build_placeholder_map(array $home_info, array $server_context = arr
|
|||
'{WORKSHOP_ID}' => '',
|
||||
'{WORKSHOP_APP_ID}' => '',
|
||||
'{STEAM_APP_ID}' => '',
|
||||
'{FOLDER_NAME}' => '',
|
||||
'{MOD_FOLDER}' => '',
|
||||
);
|
||||
foreach ($overrides as $key => $value) {
|
||||
$token = '{' . strtoupper(trim((string)$key, '{}')) . '}';
|
||||
|
|
|
|||
|
|
@ -81,13 +81,6 @@ function scm_workshop_write_manifest_and_run($db, array $home_info, $server_xml,
|
|||
return false;
|
||||
}
|
||||
|
||||
$script_path = scm_get_workshop_script_path($home_info, $server_xml);
|
||||
$script_path = trim((string)$script_path);
|
||||
if ($script_path === '' || !preg_match('/^[^\\r\\n\\0]+$/', $script_path)) {
|
||||
$error = 'Workshop script path is invalid.';
|
||||
return false;
|
||||
}
|
||||
|
||||
$home_path = rtrim(clean_path((string)$home_info['home_path']), '/');
|
||||
if (!scm_path_is_under_home($home_path, $manifest_path)) {
|
||||
$error = 'Manifest path is outside of the server home.';
|
||||
|
|
@ -99,8 +92,10 @@ function scm_workshop_write_manifest_and_run($db, array $home_info, $server_xml,
|
|||
'action' => (string)$action,
|
||||
'home_id' => (int)$home_info['home_id'],
|
||||
'home_cfg_id' => (int)$home_info['home_cfg_id'],
|
||||
'workshop_app_id' => scm_extract_workshop_app_id($server_xml),
|
||||
'workshop_app_id' => (!empty($extra_manifest['workshop_app_id']) ? (string)$extra_manifest['workshop_app_id'] : scm_extract_workshop_app_id($server_xml)),
|
||||
'steam_app_id' => !empty($extra_manifest['steam_app_id']) ? (string)$extra_manifest['steam_app_id'] : '',
|
||||
'items' => array_values($item_ids),
|
||||
'generated_at' => date('Y-m-d H:i:s'),
|
||||
);
|
||||
if (!empty($extra_manifest)) {
|
||||
$manifest['extra'] = $extra_manifest;
|
||||
|
|
@ -123,8 +118,8 @@ function scm_workshop_write_manifest_and_run($db, array $home_info, $server_xml,
|
|||
$error = 'Failed to write workshop manifest to remote server.';
|
||||
return false;
|
||||
}
|
||||
if ((int)$remote->rfile_exists($script_path) !== 1) {
|
||||
$error = 'Configured workshop script not found on agent host: ' . $script_path;
|
||||
$script_path = scm_prepare_workshop_script_for_agent($remote, $home_info, $server_xml, $error);
|
||||
if ($script_path === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1432,14 +1432,7 @@ function api_addonsmanager()
|
|||
$addon_info = $addons_rows[0];
|
||||
|
||||
$install_method = scm_get_install_method_default(isset($addon_info['install_method']) ? $addon_info['install_method'] : 'download_zip');
|
||||
$validation_payload = array(
|
||||
'url' => isset($addon_info['url']) ? $addon_info['url'] : '',
|
||||
'path' => isset($addon_info['path']) ? $addon_info['path'] : '',
|
||||
'workshop_item_id' => isset($addon_info['workshop_item_id']) ? $addon_info['workshop_item_id'] : '',
|
||||
'target_path_template' => isset($addon_info['target_path_template']) ? $addon_info['target_path_template'] : '',
|
||||
'post_script' => isset($addon_info['post_script']) ? $addon_info['post_script'] : '',
|
||||
'config_edit_rule' => isset($addon_info['config_edit_rule']) ? $addon_info['config_edit_rule'] : '',
|
||||
);
|
||||
$validation_payload = scm_collect_install_payload($addon_info);
|
||||
$validation_message = '';
|
||||
if (!scm_validate_install_method_payload($install_method, $validation_payload, $validation_message))
|
||||
return array("status" => '422', "message" => $validation_message);
|
||||
|
|
@ -1480,14 +1473,24 @@ function api_addonsmanager()
|
|||
|
||||
if ($install_method === 'steam_workshop') {
|
||||
scm_ensure_workshop_schema($db);
|
||||
$workshop_item_id = trim((string)$addon_info['workshop_item_id']);
|
||||
$workshop_runtime = scm_build_workshop_runtime_context($db, $home_info, $server_xml, $validation_payload, $validation_message);
|
||||
if ($workshop_runtime === false)
|
||||
return array("status" => '422', "message" => $validation_message);
|
||||
$workshop_item_id = (string)$workshop_runtime['workshop_item_id'];
|
||||
$workshop_error = '';
|
||||
$extra_manifest = array(
|
||||
'addon_id' => (int)$addon_id,
|
||||
'target_path_template' => isset($addon_info['target_path_template']) ? (string)$addon_info['target_path_template'] : '',
|
||||
'optional_folder_name' => isset($addon_info['optional_folder_name']) ? (string)$addon_info['optional_folder_name'] : '',
|
||||
'target_path_template' => (string)$workshop_runtime['target_path_template'],
|
||||
'target_path_resolved' => (string)$workshop_runtime['target_path_resolved'],
|
||||
'optional_folder_name' => isset($validation_payload['optional_folder_name']) ? (string)$validation_payload['optional_folder_name'] : '',
|
||||
'config_edit_rule' => isset($addon_info['config_edit_rule']) ? (string)$addon_info['config_edit_rule'] : '',
|
||||
'launch_param_additions' => isset($addon_info['launch_param_additions']) ? (string)$addon_info['launch_param_additions'] : '',
|
||||
'workshop_app_id' => (string)$workshop_runtime['workshop_app_id'],
|
||||
'steam_app_id' => (string)$workshop_runtime['steam_app_id'],
|
||||
'steamcmd_path' => isset($workshop_runtime['steamcmd_path']) ? (string)$workshop_runtime['steamcmd_path'] : '',
|
||||
'workshop_download_dir' => isset($workshop_runtime['workshop_download_dir']) ? (string)$workshop_runtime['workshop_download_dir'] : '',
|
||||
'server_root' => isset($workshop_runtime['server_root']) ? (string)$workshop_runtime['server_root'] : rtrim((string)$home_info['home_path'], '/'),
|
||||
'post_install_script' => trim((string)$post_script),
|
||||
);
|
||||
$ok = scm_workshop_write_manifest_and_run($db, $home_info, $server_xml, 'install', array($workshop_item_id), $workshop_error, $extra_manifest);
|
||||
if ($ok)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue