diff --git a/modules/config_games/config_servers.php b/modules/config_games/config_servers.php index 0e4d15ce..7717365e 100644 --- a/modules/config_games/config_servers.php +++ b/modules/config_games/config_servers.php @@ -23,6 +23,7 @@ */ require_once("server_config_parser.php"); +require_once(__DIR__ . "/xml_tag_descriptions.php"); /** * Safely convert any config value (string, NULL, or array from SimpleXML) to a @@ -264,6 +265,14 @@ function config_games_print_editor_css() .xml-raw-section textarea{width:100%;min-height:300px;font-family:monospace;font-size:0.85rem;background:#0c0c0c;color:#eee;border:1px solid #3a3a3a;border-radius:4px;padding:8px} .xml-raw-warning{background:#2d2200;border:1px solid #7a5a00;border-radius:4px;padding:8px 12px;color:#f0c050;font-size:0.85rem;margin-bottom:6px} .xml-section-header{margin:20px 0 4px;font-size:0.8rem;color:#888;text-transform:uppercase;letter-spacing:0.1em;border-bottom:1px solid #2a2a2a;padding-bottom:4px} +.xml-node__desc{font-size:0.82rem;color:#aaa;background:#0e0e0e;border-left:3px solid #2a4a7a;padding:6px 10px;margin:6px 0 8px;border-radius:0 4px 4px 0} +.xml-node__options{margin:4px 0 4px 12px;padding:0;list-style:disc inside} +.xml-node__options li{margin-bottom:2px} +.xml-node__options code{color:#7eb3f0;background:rgba(30,100,200,0.12);padding:1px 4px;border-radius:3px} +.xml-node__example{display:block;margin-top:4px;color:#888} +.xml-node__example code{color:#a0d0a0;background:rgba(30,150,50,0.1);padding:1px 4px;border-radius:3px} +.xml-jump-link{display:inline-block;margin-bottom:12px;padding:6px 14px;background:#1c6dd0;color:#fff;border-radius:4px;text-decoration:none;font-size:0.9rem} +.xml-jump-link:hover{background:#1f7aec;text-decoration:none} CSS; } @@ -296,12 +305,34 @@ function config_games_render_node(SimpleXMLElement $node, array $ancestors, arra ? "required" : "optional"; + // Look up per-tag description from the descriptions helper. + $tagDescriptions = config_games_tag_descriptions(); + $tagDesc = $tagDescriptions[$name] ?? null; + $html = "
"; $actionId = 'node_action_' . substr(md5($safePath . $index), 0, 8); $html .= "
{$safeLabel}{$badge}
{$displayPath}
"; $html .= "
"; $html .= ""; $html .= "
"; + if ($tagDesc !== null) { + $safeDesc = htmlspecialchars($tagDesc['desc'], ENT_QUOTES, 'UTF-8'); + $html .= "
{$safeDesc}"; + if (!empty($tagDesc['options'])) { + $html .= ""; + } + if (!empty($tagDesc['example'])) { + $safeExample = htmlspecialchars($tagDesc['example'], ENT_QUOTES, 'UTF-8'); + $html .= "Example: {$safeExample}"; + } + $html .= "
"; + } $html .= "
"; $html .= ""; $html .= ""; @@ -667,7 +698,8 @@ function exec_ogp_module() { \n"; if ( isset($_GET['home_cfg_id']) ) - { + { + echo "

↓ Jump to XML Editor

"; $home_cfg_id = trim($_GET['home_cfg_id']); $cfg_info = $db->getGameCfg($home_cfg_id); @@ -721,6 +753,7 @@ function exec_ogp_module() { print_failure(get_lang_f("error_when_handling_file",$config_file)); } else { $raw_xml_content = htmlspecialchars(file_get_contents($config_file), ENT_QUOTES, 'UTF-8'); + echo "
"; echo "
"; echo ""; echo ""; @@ -738,6 +771,7 @@ function exec_ogp_module() { echo "
"; echo "
"; echo ""; + echo "
"; // #xml-editor-section } } } diff --git a/modules/config_games/xml_tag_descriptions.php b/modules/config_games/xml_tag_descriptions.php new file mode 100644 index 00000000..944c9dba --- /dev/null +++ b/modules/config_games/xml_tag_descriptions.php @@ -0,0 +1,155 @@ +, example?: string}> + */ +function config_games_tag_descriptions(): array +{ + return [ + 'game_key' => [ + 'desc' => 'Unique lowercase identifier for this game configuration. Used internally to link homes, mods, and Workshop profiles.', + 'example' => 'arma3_linux64', + ], + 'protocol' => [ + 'desc' => 'Query protocol used to monitor server status.', + 'options' => [ + 'lgsl' => 'LGSL – Lightweight Game Server Library', + 'gameq' => 'GameQ – PHP-based multi-protocol server query', + '' => 'None (server is not queryable)', + ], + ], + 'lgsl_query_name' => [ + 'desc' => 'LGSL type string identifying the game in the LGSL library (only used when protocol is "lgsl").', + 'example' => 'arma3', + ], + 'gameq_query_name' => [ + 'desc' => 'GameQ protocol name identifying the game (only used when protocol is "gameq").', + 'example' => 'arma3', + ], + 'installer' => [ + 'desc' => 'Installer/updater helper used to download and update game server files.', + 'options' => [ + 'steam' => 'SteamCMD / HLDSUpdateTool', + 'steamcmd' => 'SteamCMD (explicit)', + '' => 'None (manual installation)', + ], + ], + 'game_name' => [ + 'desc' => 'Display name shown in the panel UI for this game.', + 'example' => 'Arma 3', + ], + 'server_exec_name' => [ + 'desc' => 'Filename of the server executable (without path). The panel uses this to detect whether the server process is running.', + 'example' => 'arma3server_x64', + ], + 'query_port' => [ + 'desc' => 'Port offset added to the main port to obtain the query port used for status monitoring.', + 'example' => '1', + ], + 'cli_template' => [ + 'desc' => 'Command-line template used to launch the server. Supports placeholder tokens such as %ip%, %port%, %slots%.', + 'example' => '-ip=%ip% -port=%port% -maxPlayers=%slots%', + ], + 'cli_params' => [ + 'desc' => 'Container for individual child elements that define configurable launch parameters shown in the panel UI.', + ], + 'reserve_ports' => [ + 'desc' => 'Additional sequential ports (beyond the main port) that must be reserved for this server instance.', + 'example' => '3', + ], + 'cli_allow_chars' => [ + 'desc' => 'Extra characters that are safe to include in command-line parameter values (extends the default whitelist).', + 'example' => '@_-.', + ], + 'maps_location' => [ + 'desc' => 'Path inside the server directory where map files are stored. The panel uses this to populate map-selection dropdowns.', + 'example' => 'Maps', + ], + 'map_list' => [ + 'desc' => 'Hardcoded comma-separated list of map names when maps cannot be read from disk.', + ], + 'console_log' => [ + 'desc' => 'Relative path to the server log file that the panel displays in the console viewer.', + 'example' => 'logs/console.log', + ], + 'exe_location' => [ + 'desc' => 'Subdirectory within the server installation where the executable resides. Leave empty if the executable is at the root.', + 'example' => 'Binaries/Win64', + ], + 'max_user_amount' => [ + 'desc' => 'Maximum player slots the panel will allow for this game type. Enforced in the panel UI when creating or editing a server.', + 'example' => '64', + ], + 'control_protocol' => [ + 'desc' => 'Protocol used to send RCON/admin commands to the running server.', + 'options' => [ + 'rcon' => 'RCON (remote console)', + 'rconhl' => 'Half-Life RCON', + '' => 'None', + ], + ], + 'control_protocol_type' => [ + 'desc' => 'Sub-type or variant that further qualifies the control protocol.', + 'example' => 'rcon_password', + ], + 'mods' => [ + 'desc' => 'Container element for mod definitions. Child elements describe each variant of the server configuration.', + ], + 'replace_texts' => [ + 'desc' => 'Container for text-replacement rules applied to config files on the server.', + ], + 'server_params' => [ + 'desc' => 'Additional fixed parameters appended verbatim to the server launch command.', + ], + 'custom_fields' => [ + 'desc' => 'Container for admin-defined extra fields displayed in the server control panel.', + ], + 'list_players_command' => [ + 'desc' => 'RCON command sent to the server to retrieve the current player list.', + 'example' => 'players', + ], + 'player_info_regex' => [ + 'desc' => 'Regular expression used to parse each line of the player-list response into name and other fields.', + ], + 'player_info' => [ + 'desc' => 'Defines which capture groups from player_info_regex map to player attributes (name, score, ping, etc.).', + ], + 'player_commands' => [ + 'desc' => 'Container for RCON commands that can be executed on individual players (kick, ban, etc.).', + ], + 'pre_install' => [ + 'desc' => 'Shell/batch script executed on the agent BEFORE the game server files are installed or updated.', + ], + 'post_install' => [ + 'desc' => 'Shell/batch script executed on the agent AFTER the game server files are installed or updated.', + ], + 'pre_start' => [ + 'desc' => 'Shell/batch script executed on the agent BEFORE the game server process is started.', + ], + 'post_start' => [ + 'desc' => 'Shell/batch script executed on the agent AFTER the game server process has started.', + ], + 'environment_variables' => [ + 'desc' => 'Container for environment variables that are set in the server process environment at startup.', + ], + 'lock_files' => [ + 'desc' => 'Files that should not be overwritten when updating the server. Paths are relative to the server installation directory.', + ], + 'configuration_files' => [ + 'desc' => 'Container listing server configuration files that the panel can display and edit via the config-file editor.', + ], + ]; +} diff --git a/modules/steam_workshop/lang/en_US.php b/modules/steam_workshop/lang/en_US.php index 3e5e28e5..718ff282 100644 --- a/modules/steam_workshop/lang/en_US.php +++ b/modules/steam_workshop/lang/en_US.php @@ -7,7 +7,7 @@ return [ 'button_save' => 'Save settings', 'button_cancel' => 'Back to list', 'label_feature_flag' => 'Enable scheduled Workshop updates for this server', - 'label_adapter' => 'Game adapter', + 'label_adapter' => 'Game type', 'label_interval' => 'Update interval (minutes)', 'label_interval_hint' => 'Runs on the agent scheduler. Allowed range: 15–360 minutes.', 'label_staging_dir' => 'Staging directory (optional)', @@ -17,24 +17,24 @@ return [ 'label_mod_import' => 'Workshop IDs list (one "id,@ModName" per line)', 'hint_mod_import' => 'Paste from Modlist.txt or import from a collection. IDs are sanitized automatically.', 'hint_admin_only' => 'Managed by your administrator.', - 'adapter_locked_note' => 'This adapter is enforced for the current game type by your administrator.', - 'admin_heading_game_mapping' => 'Adapter mapping by game type', - 'admin_subheading_game_mapping' => 'Pick which adapter becomes the default whenever a server of that game opens the Workshop UI.', + 'adapter_locked_note' => 'The game type for this server is managed by your administrator.', + 'admin_heading_game_mapping' => 'Game type mapping', + 'admin_subheading_game_mapping' => 'Pick which game configuration becomes the default whenever a server of that game opens the Workshop UI.', 'admin_col_game_key' => 'Game key', - 'admin_col_adapter' => 'Adapter', + 'admin_col_adapter' => 'Game configuration', 'admin_no_game_keys' => 'No server configuration XML files were detected.', - 'admin_heading_adapters' => 'Available adapters', + 'admin_heading_adapters' => 'Available game configurations', 'admin_col_key' => 'Key', 'admin_col_mods_dir' => 'Mods directory', 'admin_col_notes' => 'Notes', - 'admin_heading_per_game' => 'Per-game adapters', - 'admin_subheading_per_game' => 'Each game should have its own adapter XML for Steam Workshop automation.', + 'admin_heading_per_game' => 'Per-game Workshop configurations', + 'admin_subheading_per_game' => 'Each game should have its own Workshop configuration to control how mods are installed.', 'admin_col_status' => 'Status', 'admin_col_updated' => 'Last updated', 'admin_col_actions' => 'Actions', - 'admin_heading_edit_adapter' => 'Editing adapter for %s', - 'admin_hint_select_game' => 'Select a game in the table above to edit or create its adapter.', - 'status_no_adapter' => 'No adapter defined', + 'admin_heading_edit_adapter' => 'Editing Workshop configuration for %s', + 'admin_hint_select_game' => 'Select a game in the table above to edit or create its Workshop configuration.', + 'status_no_adapter' => 'Not configured', 'status_enabled' => 'Enabled', 'status_disabled' => 'Disabled', 'status_hot_reload' => 'Hot reload ready', @@ -55,15 +55,15 @@ return [ 'install_staging' => 'Download to staging only (manual apply)', 'action_queue_for_restart' => 'Queue for restart', 'action_hot_reload_if_supported' => 'Hot reload if the adapter allows it', - 'summary_adapter' => 'Adapter', + 'summary_adapter' => 'Game', 'summary_interval' => 'Interval', 'summary_mods' => 'Mods', 'summary_last_saved' => 'Last saved', 'summary_hot_reload' => 'Hot reload', 'raw_definition_label' => 'Raw Workshop list', - 'message_mappings_saved' => 'Adapter mappings saved.', - 'message_adapter_saved' => 'Adapter saved.', - 'message_adapter_deleted' => 'Adapter deleted.', + 'message_mappings_saved' => 'Game type mappings saved.', + 'message_adapter_saved' => 'Game configuration saved.', + 'message_adapter_deleted' => 'Game configuration deleted.', 'error_admin_only' => 'Administrator access required.', 'mod_picker_heading' => 'Workshop library', 'mod_picker_hint' => 'Search Steam Workshop and add mods to keep them synced automatically.', @@ -87,15 +87,15 @@ return [ 'mod_picker_request_label' => 'Submitting request', 'mod_picker_request_hint' => 'Exact Steam URL preview. The input shows the text that will be submitted.', 'mod_picker_request_input_label' => 'Workshop query preview', - 'error_game_key_required' => 'Select a valid game key before editing the adapter.', - 'error_adapter_delete_failed' => 'Adapter could not be deleted.', + 'error_game_key_required' => 'Select a valid game key before editing the Workshop configuration.', + 'error_adapter_delete_failed' => 'Game configuration could not be deleted.', 'button_edit_adapter' => 'Edit', 'button_create_adapter' => 'Create', 'button_delete_adapter' => 'Delete', - 'button_save_adapter' => 'Save adapter', - 'confirm_delete_adapter' => 'Delete this adapter? Servers mapped to it will fall back to defaults.', + 'button_save_adapter' => 'Save game configuration', + 'confirm_delete_adapter' => 'Delete this game configuration? Servers mapped to it will fall back to defaults.', 'label_game_key' => 'Game key', - 'label_adapter_name' => 'Adapter display name', + 'label_adapter_name' => 'Game display name', 'label_adapter_app_id' => 'Steam App ID', 'label_adapter_mods_dir' => 'Mods directory', 'label_adapter_keys_dir' => 'Keys directory (optional)', diff --git a/modules/steam_workshop/views/admin/index.php b/modules/steam_workshop/views/admin/index.php index 40238269..c1e94a83 100644 --- a/modules/steam_workshop/views/admin/index.php +++ b/modules/steam_workshop/views/admin/index.php @@ -52,7 +52,7 @@ declare(strict_types=1); App ID - +
@@ -61,7 +61,7 @@ declare(strict_types=1);
- + - @@ -111,7 +111,7 @@ declare(strict_types=1);
- +
@@ -159,12 +159,12 @@ declare(strict_types=1); -

+

- + diff --git a/modules/steam_workshop/views/partials/form_fields.php b/modules/steam_workshop/views/partials/form_fields.php index 82f397e0..9698b9e3 100644 --- a/modules/steam_workshop/views/partials/form_fields.php +++ b/modules/steam_workshop/views/partials/form_fields.php @@ -21,10 +21,10 @@ $currentAdapterName = $adapterOptions[$formConfig['adapter_key']] ?? strtoupper(
Steam App ID