diff --git a/Panel/lang/English/modules/steam_workshop.php b/Panel/lang/English/modules/steam_workshop.php index 43b99344..335a7f09 100644 --- a/Panel/lang/English/modules/steam_workshop.php +++ b/Panel/lang/English/modules/steam_workshop.php @@ -42,6 +42,16 @@ define('LANG_open_workshop_page', "Open Workshop page"); define('LANG_unable_to_contact_steam_workshop', "Unable to contact the Steam Workshop."); define('LANG_unable_to_parse_steam_workshop_response', "Unable to parse the Steam Workshop response."); define('LANG_failed_to_fetch_workshop_item_details', "Failed to fetch Workshop item details for %s."); +define('LANG_copy_from_existing_workshop_config', "Copy From Existing Workshop Config"); +define('LANG_copy_from_existing_workshop_config_info', "Copy an existing Workshop XML into the currently selected game and OS configuration."); +define('LANG_select_config_to_copy', "Select config to copy"); +define('LANG_copy_selected_config', "Copy Selected Config"); +define('LANG_confirm_copy_workshop_config', "Confirm Workshop config copy"); +define('LANG_workshop_config_copy_confirm_info', "Copy %s into %s and overwrite the current configuration?"); +define('LANG_invalid_workshop_config_copy_source', "Invalid Workshop config source."); +define('LANG_cannot_copy_workshop_config_to_itself', "Cannot copy a Workshop config onto itself."); +define('LANG_failed_to_copy_workshop_config', "Failed to copy the selected Workshop config."); +define('LANG_workshop_config_copied', "Copied Workshop config %s to %s."); define('LANG_select_game', "Select Game"); define('LANG_save_config', "Save Config"); define('LANG_mod_key_not_found_from_xml', "Mod key %s not found from xml."); diff --git a/Panel/modules/steam_workshop/functions.php b/Panel/modules/steam_workshop/functions.php index 3e7623e0..0fdd9afc 100644 --- a/Panel/modules/steam_workshop/functions.php +++ b/Panel/modules/steam_workshop/functions.php @@ -56,6 +56,92 @@ function steam_workshop_h($value) return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8'); } +function steam_workshop_has_config_file_path($filepath) +{ + return trim((string)$filepath) !== ''; +} + +function steam_workshop_resolve_config_filepath($home_path, $filepath) +{ + $filepath = trim((string)$filepath); + if($filepath === '') + return ''; + + return clean_path(rtrim($home_path, '/').'/'.$filepath); +} + +function steam_workshop_get_configs_dir() +{ + return dirname(__FILE__).'/game_configs'; +} + +function steam_workshop_list_existing_configs() +{ + $configs = array(); + $config_dir = steam_workshop_get_configs_dir(); + if(!is_dir($config_dir)) + return $configs; + + $files = glob($config_dir.'/*.xml'); + if($files === false) + return $configs; + + sort($files, SORT_NATURAL | SORT_FLAG_CASE); + foreach($files as $file) + { + $basename = basename($file); + $xml = @simplexml_load_file($file); + $configs[$basename] = array( + 'filename' => $basename, + 'workshop_id' => $xml !== false && isset($xml->workshop_id) ? (string)$xml->workshop_id : '', + 'download_method' => $xml !== false && isset($xml->download_method) ? (string)$xml->download_method : '', + 'mods_path' => $xml !== false && isset($xml->mods_path) ? (string)$xml->mods_path : '', + ); + } + + return $configs; +} + +function steam_workshop_get_existing_config_options($exclude_filename = '') +{ + $options = array('' => get_lang('select_config_to_copy')); + foreach(steam_workshop_list_existing_configs() as $basename => $config) + { + if($basename === $exclude_filename) + continue; + $label = $basename; + $meta = array(); + if($config['workshop_id'] !== '') + $meta[] = 'Workshop '.$config['workshop_id']; + if($config['download_method'] !== '') + $meta[] = $config['download_method']; + if($config['mods_path'] !== '') + $meta[] = 'mods_path='.$config['mods_path']; + if(!empty($meta)) + $label .= ' ['.implode(' | ', $meta).']'; + $options[$basename] = $label; + } + return $options; +} + +function steam_workshop_get_safe_config_copy_paths($selected_source, $destination_file) +{ + $selected_source = basename((string)$selected_source); + $destination_file = basename((string)$destination_file); + $config_dir = realpath(steam_workshop_get_configs_dir()); + if($config_dir === false || $selected_source === '' || $destination_file === '') + return array(false, false); + + $source_path = realpath($config_dir.'/'.$selected_source); + $destination_path = $config_dir.'/'.$destination_file; + if($source_path === false || strpos($source_path, $config_dir.DIRECTORY_SEPARATOR) !== 0) + return array(false, false); + if(pathinfo($source_path, PATHINFO_EXTENSION) !== 'xml' || pathinfo($destination_path, PATHINFO_EXTENSION) !== 'xml') + return array(false, false); + + return array($source_path, $destination_path); +} + function steam_workshop_request($url, $context, &$error = "") { $response = @file_get_contents($url, false, $context); @@ -422,15 +508,18 @@ function steam_workshop_search_items($workshop_id, $query, &$error = "") function get_installed_mods($home_cfg, $remote, $xml) { - $workshop_id = $xml->workshop_id; $config = $xml->config; $regex = $config->regex; $mods_backreference_index = (int)$config->mods_backreference_index; $string_separator = stripcslashes($config->string_separator); $filepath = $config->filepath; - $mods = $xml->mods->mod; - - $full_filepath = clean_path($home_cfg['home_path']."/$filepath"); + $full_filepath = steam_workshop_resolve_config_filepath($home_cfg['home_path'], $filepath); + + if(!steam_workshop_has_config_file_path($filepath)) + { + $retval = $remote->get_workshop_mods_info($mod_info_array); + return $retval == "1" && !empty($mod_info_array) ? $mod_info_array : False; + } if($remote->rfile_exists($full_filepath) === 0) return False; @@ -476,40 +565,44 @@ function remove_mod($home_cfg, $remote, $xml, $mod_string) $string_separator = stripcslashes($config->string_separator); $filepath = $config->filepath; - $full_filepath = $home_cfg['home_path']."/$filepath"; + $full_filepath = steam_workshop_resolve_config_filepath($home_cfg['home_path'], $filepath); $mods_full_path = clean_path($home_cfg['home_path'].'/'.$xml->mods_path); - - if($remote->rfile_exists($full_filepath) === 0) - return False; - - $remote->remote_readfile($full_filepath, $file_content); - - if(preg_match("/$regex/m", $file_content, $matches)) + + if(steam_workshop_has_config_file_path($filepath)) { - $full_regex_string = trim($matches[0]); - $current_mods_string = trim($matches[$mods_backreference_index]); - if($current_mods_string != '') + if($remote->rfile_exists($full_filepath) === 0) + return False; + + $remote->remote_readfile($full_filepath, $file_content); + + if(preg_match("/$regex/m", $file_content, $matches)) { - $current = explode($string_separator, $current_mods_string); - - foreach($current as $index => $c) + $full_regex_string = trim($matches[0]); + $current_mods_string = trim($matches[$mods_backreference_index]); + if($current_mods_string != '') { - if(trim($c) == $mod_string) - unset($current[$index]); + $current = explode($string_separator, $current_mods_string); + + foreach($current as $index => $c) + { + if(trim($c) == $mod_string) + unset($current[$index]); + } + $current = array_filter($current); + $new_mods_string = implode($string_separator, $current); + + $replacement = $variable.$new_mods_string; + $file_content = str_replace($full_regex_string, $replacement, $file_content); } - $current = array_filter($current); - $new_mods_string = implode($string_separator, $current); - - $replacement = $variable.$new_mods_string; - $file_content = str_replace($full_regex_string, $replacement, $file_content); + else + return False; } else return False; + + $remote->remote_writefile($full_filepath, $file_content); } - else - return False; - - $remote->remote_writefile($full_filepath, $file_content); + $uninstall_filepath = clean_path($mods_full_path.'/postuninstall.sh'); $uninstallcmd = str_replace('%mods_full_path%', $mods_full_path, $xml->uninstall); $uninstallcmd = str_replace('%mod_string%', $mod_string, $uninstallcmd); diff --git a/Panel/modules/steam_workshop/main.php b/Panel/modules/steam_workshop/main.php index 9968986a..b717452a 100644 --- a/Panel/modules/steam_workshop/main.php +++ b/Panel/modules/steam_workshop/main.php @@ -205,7 +205,7 @@ function exec_ogp_module() $place_after = $config->place_after; $mod_string = $config->mod_string; $string_separator = $config->string_separator; - $config_file_path = clean_path($home_cfg['home_path']."/".$config->filepath); + $config_file_path = steam_workshop_resolve_config_filepath($home_cfg['home_path'], $config->filepath); $post_install = $xml->post_install; $mod_names_list = get_mod_names_list($mods_list, $xml->mods->mod); $mods_full_path = clean_path($home_cfg['home_path'].'/'.$xml->mods_path); diff --git a/Panel/modules/steam_workshop/workshop_admin.php b/Panel/modules/steam_workshop/workshop_admin.php index 1d4d8fab..54bb47a0 100644 --- a/Panel/modules/steam_workshop/workshop_admin.php +++ b/Panel/modules/steam_workshop/workshop_admin.php @@ -31,6 +31,68 @@ require_once(dirname(__FILE__) . '/simple_admin_helper.php'); */ require_once('includes/form_table_class.php'); require_once("modules/steam_workshop/functions.php"); + +function steam_workshop_admin_save_xml($xml_file, $post, $removed_ids = array()) +{ + $xml = new SimpleXMLElement(''); + $xml->addChild('workshop_id', trim((string)$post['workshop_id'])); + $xml->addChild('download_method', trim((string)$post['download_method'])); + $xml->addChild('anonymous_login', trim((string)$post['anonymous_login'])); + $xml->addChild('mods_path', trim((string)$post['mods_path'])); + $mods = $xml->addChild('mods'); + if(file_exists($xml_file)) + { + $file_xml = simplexml_load_file($xml_file); + if($file_xml !== false && isset($file_xml->mods)) + { + foreach($file_xml->mods->mod as $xml_mod) + { + if(in_array((string)$xml_mod['id'], $removed_ids, true)) + continue; + $mod = $mods->addChild('mod'); + $mod->addAttribute('id', (string)$xml_mod['id']); + $mod->addChild('name', (string)$xml_mod->name); + $mod->addChild('description', (string)$xml_mod->description); + $mod->addChild('image_url', (string)$xml_mod->image_url); + $mod->addChild('download_url', (string)$xml_mod->download_url); + $mod->addChild('filename', (string)$xml_mod->filename); + $mod->addChild('file_size', (string)$xml_mod->file_size); + } + } + } + + $config = $xml->addChild('config'); + $config->addChild('regex', trim((string)$post['regex'])); + $config->addChild('mods_backreference_index', trim((string)$post['mods_backreference_index'])); + $config->addChild('variable', (string)$post['variable']); + $config->addChild('place_after', (string)$post['place_after']); + $config->addChild('mod_string', (string)$post['mod_string']); + $config->addChild('string_separator', (string)$post['string_separator']); + $config->addChild('filepath', trim((string)$post['filepath'])); + $xml->addChild('post_install', str_replace('&','&', (string)$post['post_install'])); + $xml->addChild('uninstall', str_replace('&','&', (string)$post['uninstall'])); + + $dom = dom_import_simplexml($xml)->ownerDocument; + $dom->formatOutput = true; + file_put_contents($xml_file, $dom->saveXML()); +} + +function steam_workshop_admin_hidden_state($values) +{ + $fields = array( + 'workshop_id','download_method','anonymous_login','mods_path','regex', + 'mods_backreference_index','variable','place_after','mod_string', + 'string_separator','filepath','post_install','uninstall' + ); + $html = ''; + foreach($fields as $field) + { + $value = isset($values[$field]) ? (string)$values[$field] : ''; + $html .= ''."\n"; + } + return $html; +} + function exec_ogp_module() { @@ -62,86 +124,63 @@ function exec_ogp_module() return; } $xml_file = CONFIGS.$mod_xml->installer_name."_".$os.".xml"; + $workshop_id = ''; + $download_method = 'steamcmd'; + $anonymous_login = 1; + $mods_path = ''; + $regex = ''; + $mods_backreference_index = ''; + $variable = ''; + $place_after = ''; + $mod_string = ''; + $string_separator = ''; + $filepath = ''; + $post_install = ''; + $uninstall = ''; if(isset($_POST['save_config'])) { - $xml = new SimpleXMLElement(''); - $xml->addChild('workshop_id', $_POST['workshop_id']); - $xml->addChild('download_method', $_POST['download_method']); - $xml->addChild('anonymous_login', $_POST['anonymous_login']); - $xml->addChild('mods_path', $_POST['mods_path']); - $mods = $xml->addChild('mods'); - if(file_exists($xml_file)) - { - $file_xml = simplexml_load_file($xml_file); - foreach($file_xml->mods->mod as $xml_mod) - { - $mod = $mods->addChild('mod'); - $mod->addAttribute('id', $xml_mod['id']); - $mod->addChild('name', $xml_mod->name); - $mod->addChild('description', $xml_mod->description); - $mod->addChild('image_url', $xml_mod->image_url); - $mod->addChild('download_url', $xml_mod->download_url); - $mod->addChild('filename', $xml_mod->filename); - $mod->addChild('file_size', $xml_mod->file_size); - } - } - - $config = $xml->addChild('config'); - $config->addChild('regex', $_POST['regex']); - $config->addChild('mods_backreference_index', $_POST['mods_backreference_index']); - $config->addChild('variable', $_POST['variable']); - $config->addChild('place_after', $_POST['place_after']); - $config->addChild('mod_string', $_POST['mod_string']); - $config->addChild('string_separator', $_POST['string_separator']); - $config->addChild('filepath', $_POST['filepath']); - $xml->addChild('post_install', str_replace('&','&',$_POST['post_install'])); - $xml->addChild('uninstall', str_replace('&','&',$_POST['uninstall'])); - - $dom = dom_import_simplexml($xml)->ownerDocument; - $dom->formatOutput = true; - file_put_contents($xml_file, $dom->saveXML()); + steam_workshop_admin_save_xml($xml_file, $_POST); } if(isset($_POST['remove_mods'])) { - $xml = new SimpleXMLElement(''); - $xml->addChild('workshop_id', $_POST['workshop_id']); - $xml->addChild('download_method', $_POST['download_method']); - $xml->addChild('anonymous_login', $_POST['anonymous_login']); - $xml->addChild('mods_path', $_POST['mods_path']); - $mods = $xml->addChild('mods'); - if(file_exists($xml_file)) + $removed_ids = isset($_POST['workshop_mod_id']) && is_array($_POST['workshop_mod_id']) ? array_map('strval', $_POST['workshop_mod_id']) : array(); + steam_workshop_admin_save_xml($xml_file, $_POST, $removed_ids); + } + + if(isset($_POST['copy_selected_config']) || isset($_POST['confirm_copy_config'])) + { + list($source_path, $destination_path) = steam_workshop_get_safe_config_copy_paths( + isset($_POST['copy_source_config']) ? $_POST['copy_source_config'] : '', + basename($xml_file) + ); + if($source_path === false || $destination_path === false) { - $file_xml = simplexml_load_file($xml_file); - foreach($file_xml->mods->mod as $xml_mod) - { - if(in_array($xml_mod['id'],$_POST['workshop_mod_id'])) - continue; - $mod = $mods->addChild('mod'); - $mod->addAttribute('id', $xml_mod['id']); - $mod->addChild('name', $xml_mod->name); - $mod->addChild('description', $xml_mod->description); - $mod->addChild('image_url', $xml_mod->image_url); - $mod->addChild('download_url', $xml_mod->download_url); - $mod->addChild('filename', $xml_mod->filename); - $mod->addChild('file_size', $xml_mod->file_size); - } + print_failure(get_lang('invalid_workshop_config_copy_source')); + } + elseif(realpath($source_path) === realpath($destination_path)) + { + print_failure(get_lang('cannot_copy_workshop_config_to_itself')); + } + elseif(file_exists($destination_path) && !isset($_POST['confirm_copy_config'])) + { + echo "
".get_lang('confirm_copy_workshop_config')."
". + get_lang_f('workshop_config_copy_confirm_info', basename($source_path), basename($destination_path)). + "
". + "". + "". + "". + "
"; + } + elseif(@copy($source_path, $destination_path)) + { + print_success(get_lang_f('workshop_config_copied', basename($source_path), basename($destination_path))); + } + else + { + print_failure(get_lang('failed_to_copy_workshop_config')); } - $config = $xml->addChild('config'); - $config->addChild('regex', $_POST['regex']); - $config->addChild('mods_backreference_index', $_POST['mods_backreference_index']); - $config->addChild('variable', $_POST['variable']); - $config->addChild('place_after', $_POST['place_after']); - $config->addChild('mod_string', $_POST['mod_string']); - $config->addChild('string_separator', $_POST['string_separator']); - $config->addChild('filepath', $_POST['filepath']); - $xml->addChild('post_install', $_POST['post_install']); - $xml->addChild('uninstall', $_POST['uninstall']); - - $dom = dom_import_simplexml($xml)->ownerDocument; - $dom->formatOutput = true; - file_put_contents($xml_file, $dom->saveXML()); } if(file_exists($xml_file)) @@ -162,9 +201,24 @@ function exec_ogp_module() $post_install = $xml->post_install; $uninstall = $xml->uninstall; } - - + } + + $current_values = array( + 'workshop_id' => isset($workshop_id) ? $workshop_id : '', + 'download_method' => isset($download_method) ? $download_method : '', + 'anonymous_login' => isset($anonymous_login) ? $anonymous_login : '', + 'mods_path' => isset($mods_path) ? $mods_path : '', + 'regex' => isset($regex) ? $regex : '', + 'mods_backreference_index' => isset($mods_backreference_index) ? $mods_backreference_index : '', + 'variable' => isset($variable) ? $variable : '', + 'place_after' => isset($place_after) ? $place_after : '', + 'mod_string' => isset($mod_string) ? $mod_string : '', + 'string_separator' => isset($string_separator) ? $string_separator : '', + 'filepath' => isset($filepath) ? $filepath : '', + 'post_install' => isset($post_install) ? $post_install : '', + 'uninstall' => isset($uninstall) ? $uninstall : '', + ); $game_cfgs = $db->getGameCfgs(); $games[0] = get_lang('select_game'); @@ -206,6 +260,15 @@ function exec_ogp_module() $ft->add_custom_field('game', create_drop_box_from_array_onchange($games, "home_cfg_id-mod_cfg_id-os", @$_REQUEST['home_cfg_id-mod_cfg_id-os'])); if(isset($home_cfg_id) and isset($mod_cfg_id)) { + $copy_options = steam_workshop_get_existing_config_options(isset($xml_file) ? basename($xml_file) : ''); + echo "
".get_lang('copy_from_existing_workshop_config')."
". + get_lang('copy_from_existing_workshop_config_info'). + "
". + "". + create_drop_box_from_array($copy_options, "copy_source_config", ''). + " ". + "
"; + $ft->add_field('string','workshop_id',@$workshop_id); $ft->add_custom_field('download_method',create_drop_box_from_array($download_methods, "download_method", @$download_method)); $ft->add_field('on_off','anonymous_login',@$anonymous_login); @@ -237,19 +300,7 @@ function exec_ogp_module() $ft->start_form("?m=steam_workshop&p=workshop_admin&home_cfg_id-mod_cfg_id-os=".$_REQUEST['home_cfg_id-mod_cfg_id-os'], "post", "autocomplete=\"off\""); $ft->start_table(); echo ""; - $ft->add_field_hidden('workshop_id',$workshop_id); - $ft->add_field_hidden('download_method',$download_method); - $ft->add_field_hidden('anonymous_login',$anonymous_login); - $ft->add_field_hidden('mods_path',$mods_path); - $ft->add_field_hidden('regex',$regex); - $ft->add_field_hidden('mods_backreference_index',$mods_backreference_index); - $ft->add_field_hidden('variable',$variable); - $ft->add_field_hidden('place_after',$place_after); - $ft->add_field_hidden('mod_string',$mod_string); - $ft->add_field_hidden('string_separator',$string_separator); - $ft->add_field_hidden('filepath',$filepath); - $ft->add_field_hidden('post_install',$post_install); - $ft->add_field_hidden('uninstall',$uninstall); + echo steam_workshop_admin_hidden_state($current_values); echo "". '
'; foreach($xml->mods->mod as $mod) @@ -262,4 +313,4 @@ function exec_ogp_module() } if (function_exists('gsp_steam_workshop_simple_admin_script')) { gsp_steam_workshop_simple_admin_script(); } -?> \ No newline at end of file +?> diff --git a/docs/agents/LINUX_AGENT.md b/docs/agents/LINUX_AGENT.md index 95b54b69..bec8c693 100644 --- a/docs/agents/LINUX_AGENT.md +++ b/docs/agents/LINUX_AGENT.md @@ -83,7 +83,7 @@ The agent reads screen logs and may also copy a local log file into the game hom ## Workshop / Server Content -The primary Workshop workflow is owned by the Panel `addonsmanager`, not the legacy `steam_workshop` RPC. For Linux servers the Panel: +The current customer-facing Workshop workflow is the dedicated Panel `steam_workshop` module. The older `steam_workshop` XML-RPC method is still used by that module for compatibility. For Linux servers the Panel: 1. writes `gsp_server_content/workshop_manifest.json` under the server home 2. writes a generated per-job shell script under `gsp_server_content/jobs/workshop/` @@ -92,7 +92,11 @@ The primary Workshop workflow is owned by the Panel `addonsmanager`, not the leg The generated job uses Python and SteamCMD, validates numeric Workshop IDs, keeps writes under the server home, logs to `gsp_server_content/workshop_install.log`, and supports DayZ/Arma-style `@mod` folders plus `.bikey` copying. The Linux agent does not need a permanent `generic_steam_workshop_linux.sh` file on disk. -The older `steam_workshop` XML-RPC method remains for legacy compatibility only and should not be treated as the primary customer workflow. +For legacy `steam_workshop` RPC installs: + +- blank `config_file_path` means no config-file editing +- the generated post-install script still runs +- `WorkshopModsInfo` is still written so uninstall can work without parsing a game config file ## Scheduler diff --git a/docs/agents/WINDOWS_AGENT.md b/docs/agents/WINDOWS_AGENT.md index a05dfbc8..b671abeb 100644 --- a/docs/agents/WINDOWS_AGENT.md +++ b/docs/agents/WINDOWS_AGENT.md @@ -163,7 +163,7 @@ Windows/Cygwin logs come from screen logs and/or local copies. Log retrieval sho ## Workshop / Server Content -The primary Workshop workflow is owned by the Panel `addonsmanager`, not the legacy `steam_workshop` RPC. For Windows/Cygwin servers the Panel: +The current customer-facing Workshop workflow is the dedicated Panel `steam_workshop` module. The older `steam_workshop` XML-RPC method is still used by that module for compatibility. For Windows/Cygwin servers the Panel: 1. writes `gsp_server_content/workshop_manifest.json` under the server home 2. writes a generated per-job shell script under `gsp_server_content/jobs/workshop/` @@ -172,7 +172,11 @@ The primary Workshop workflow is owned by the Panel `addonsmanager`, not the leg The generated job uses Python and SteamCMD, validates numeric Workshop IDs, keeps writes under the server home, logs to `gsp_server_content/workshop_install_windows.log`, and supports DayZ/Arma-style `@mod` folders plus `.bikey` copying. The Windows agent does not need a permanent `generic_steam_workshop_windows_cygwin.sh` file on disk. -The older `steam_workshop` XML-RPC method remains for legacy compatibility only and should not be treated as the primary customer workflow. +For legacy `steam_workshop` RPC installs: + +- blank `config_file_path` means no config-file editing +- the generated post-install script still runs +- `WorkshopModsInfo` is still written so uninstall can work without parsing a game config file ## Scheduler diff --git a/docs/features/WORKSHOP_SYSTEM.md b/docs/features/WORKSHOP_SYSTEM.md index dae5e19e..8980934b 100644 --- a/docs/features/WORKSHOP_SYSTEM.md +++ b/docs/features/WORKSHOP_SYSTEM.md @@ -34,6 +34,22 @@ The dedicated module still provides: - monitor-button access from Game Monitor - per-game Workshop configuration files - uninstall and admin support pages +- optional regex/config-file editing for games that need automatic mod-list updates +- post-install-only workflows for games that only need a move/copy script + +## Legacy RPC Install Semantics + +The dedicated `steam_workshop` module still uses the legacy `steam_workshop` agent RPC for installs. + +Current rule: + +- if `config/filepath` is blank, the Panel passes a blank `config_file_path` +- agents must skip the generated config-file editing block entirely +- post-install scripts still run +- `WorkshopModsInfo` still records installed items +- uninstall can rely on `WorkshopModsInfo` when no config file is managed + +This keeps advanced regex editing available without forcing it for simple Arma/DayZ-style `@mod` installs. ## Main Limitations diff --git a/docs/modules/steam_workshop.md b/docs/modules/steam_workshop.md index 835d206b..1b2e6462 100644 --- a/docs/modules/steam_workshop.md +++ b/docs/modules/steam_workshop.md @@ -39,6 +39,9 @@ Dedicated Steam Workshop support for game servers. - configure Workshop game XML files under `Panel/modules/steam_workshop/game_configs/` - use `workshop_admin.php` for module administration +- `File Path` and the regex/mod-string fields are optional +- a blank `File Path` means post-install and uninstall scripts run without any automatic config-file editing +- the admin page can copy one existing XML config into the currently selected game/OS config ## Search Backend @@ -53,6 +56,14 @@ Dedicated Steam Workshop support for game servers. - the main Workshop page `Back` link is rendered as a real panel button - uninstall remains in the dedicated `steam_workshop` module +## Legacy RPC Behavior + +- the dedicated module still calls the legacy agent `steam_workshop` XML-RPC method +- when `config/filepath` is blank, the Panel now passes an empty `config_file_path` +- both agents skip the generated `cat` / regex / config-write block when `config_file_path` is blank +- both agents still run custom post-install scripts and still write `WorkshopModsInfo` +- uninstall falls back to `WorkshopModsInfo` when no config file is managed + ## Security Concerns - should not be duplicated under `addonsmanager`