From be42868c1c02ecd55dacc7fa8c9b7d2093b5a629 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 18 May 2026 22:20:27 +0000 Subject: [PATCH] Add protocol and image review docs with query wrapper scaffold Agent-Logs-Url: https://github.com/GameServerPanel/GSP/sessions/c4b059df-09a3-4d61-b0f6-de738ea00a58 Co-authored-by: iaretechnician <2749183+iaretechnician@users.noreply.github.com> --- Panel/CHANGELOG.md | 1 + Panel/docs/COPILOT_TODO.md | 1 + Panel/modules/SERVER_IMAGE_MODULE_REVIEW.md | 80 ++++++++++ Panel/protocol/PROTOCOL_UPGRADE_REVIEW.md | 168 ++++++++++++++++++++ Panel/protocol/gsp_query.php | 157 ++++++++++++++++++ 5 files changed, 407 insertions(+) create mode 100644 Panel/modules/SERVER_IMAGE_MODULE_REVIEW.md create mode 100644 Panel/protocol/PROTOCOL_UPGRADE_REVIEW.md create mode 100644 Panel/protocol/gsp_query.php diff --git a/Panel/CHANGELOG.md b/Panel/CHANGELOG.md index 5a3c80b0..5b0e7a9f 100644 --- a/Panel/CHANGELOG.md +++ b/Panel/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## 2026-05-18 +- **Protocol/image upgrade Phase 1 scaffolding (non-breaking):** Added `protocol/gsp_query.php` normalized query wrapper (LGSL default provider with future-provider placeholders), documented current protocol integration and migration plan in `protocol/PROTOCOL_UPGRADE_REVIEW.md`, and documented image module comparison/unification direction in `modules/SERVER_IMAGE_MODULE_REVIEW.md` without removing LGSL, dsi, or `lgsl_with_img_mod`. - **Cron ↔ Server Content action hook integration:** Added scheduler-callable Server Content hooks in `modules/addonsmanager/server_content_actions.php`, exposed API route `server_content/run_scheduled_action`, and wired cron/user-cron action builders/parsing to support server content scheduled actions (check/install/queue/restart/validate/backup flows) without embedding game-specific install logic in the scheduler. - **Server Content Workshop Phase 1 in addonsmanager:** Added a new `Workshop Content` flow under Server Content with per-home Workshop ID storage, ID validation/deduplication, install/update/remove/update-all actions, manifest-based script handoff (`gsp_server_content/workshop_manifest.json`), safe placeholder workshop scripts for Linux/Cygwin, and schema support via `server_content_workshop` plus `addons.addon_type VARCHAR(32)`. - **Updater layout hardening + pre-update patch framework:** Reworked `modules/administration/panel_update.php` to resolve explicit GSP root/Panel/Website paths, run mandatory preflight checks, self-update updater files before main sync when drift is detected, and apply ordered required patches from `modules/update/patches/` with DB/local state tracking. Backup/rollback now includes both Panel + Website archives and root `version.json`, logs moved to root `logs/update_trace.log`, and the admin Update UI now exposes preflight, patch apply, Apache path scan/fix, backup, update, and rollback actions. diff --git a/Panel/docs/COPILOT_TODO.md b/Panel/docs/COPILOT_TODO.md index fdb3355a..439e581a 100644 --- a/Panel/docs/COPILOT_TODO.md +++ b/Panel/docs/COPILOT_TODO.md @@ -19,3 +19,4 @@ - Add an admin preview/diff panel for Apache path repairs so staff can review exact vhost line changes before confirming `Fix Apache Paths`. - Add Phase 2 Workshop Content UX in `addonsmanager`: browse/search/select Workshop items with metadata while reusing the Phase 1 per-home saved-ID action pipeline. - Add localized language strings/tooltips for the new cron scheduler `server_content_*` action labels across all supported panel locales. +- Add a Game Manager "Live Server Status" panel that consumes `Panel/protocol/gsp_query.php` and shows banner preview plus copyable embed code. diff --git a/Panel/modules/SERVER_IMAGE_MODULE_REVIEW.md b/Panel/modules/SERVER_IMAGE_MODULE_REVIEW.md new file mode 100644 index 00000000..d0d8f942 --- /dev/null +++ b/Panel/modules/SERVER_IMAGE_MODULE_REVIEW.md @@ -0,0 +1,80 @@ +# Server Image Module Review (Phase 1) + +## Scope reviewed +- `Panel/modules/lgsl_with_img_mod/` +- `Panel/modules/dsi/` +- Integration touchpoints in `Panel/modules/gamemanager/` + +## Entry points + +### lgsl_with_img_mod +- Module metadata: `Panel/modules/lgsl_with_img_mod/module.php` +- User/admin pages: + - `lgsl.php` + - `lgsl_admin.php` +- Image endpoint: + - `image.php` (reads by `s` argument, supports `img_type`) +- Core query/cache/image logic: + - `lgsl_files/lgsl_class.php` + +### dsi +- Module metadata: `Panel/modules/dsi/module.php` +- User/admin/list pages: + - `dsi_user.php` + - `dsi_admin.php` + - `dsi_list.php` +- Image endpoint: + - `image.php` (`modules/dsi/s-IP_PORT-type.png` style) +- Helpers: + - `includes/functions.php` + - `includes/functions_ui.php` + +## Comparison + +### Which module generates images? +- **Both** generate PNG status banners. +- `lgsl_with_img_mod` is LGSL-centric and built around the `OGP_DB_PREFIXlgsl` cache model. +- `dsi` supports LGSL/GameQ/TS3 monitor includes and can render banner/code snippets directly for panel users. + +### Which has better cache support? +- **lgsl_with_img_mod** has deeper cache integration: + - DB-backed query cache (`OGP_DB_PREFIXlgsl.cache`, `cache_time`) + - image file cache handling + - pending/retry semantics in `lgsl_query_cached(...)` +- `dsi` has simple file cache (60s TTL) per generated image and relies on protocol monitor include side effects for query state. + +### Which integrates with server monitor better? +- **dsi** is currently more user-facing for “banner + embed code” workflows: + - `dsi_render_table(...)` outputs HTML/BBCode snippets. + - Integrates query handlers by protocol in `dsi/image.php`. +- `lgsl_with_img_mod` is more standalone/legacy LGSL module flow. + +### Which supports player info better? +- Both are focused on banner status fields (name/map/players/status). +- Neither is currently a full player-list UI provider for Game Manager. +- Query-level player data comes from monitor protocol paths, not these image modules as a first-class shared API. + +### Which is easier to modernize? +- **dsi** is the better base for a future unified GSP banner module: + - simpler structure + - clear image endpoint + code generation UI + - already aware of LGSL/GameQ/TS3 protocol branching +- `lgsl_with_img_mod` contains useful mature cache ideas and map/image utilities worth reusing. + +## Recommended future GSP banner direction +Future module target: `Panel/modules/server_status_banner/` + +### Plan +1. Keep both current modules in place during migration. +2. Use `dsi` UX flow and embed-code patterns as baseline. +3. Reuse selective `lgsl_with_img_mod` cache + map/image helper ideas. +4. Drive data from normalized query cache (planned `server_query_cache`) and wrapper output. +5. Generate GSP-owned PNG banners (small / wide / large styles). +6. Avoid external GameTracker asset dependency. +7. Provide HTML / BBCode / direct image URL output. +8. Surface banner preview and code tool in Game Manager. + +## No-removal statement (Phase 1 safety) +- `lgsl_with_img_mod` was **not removed**. +- `dsi` was **not removed**. +- This phase is documentation and direction-setting only. diff --git a/Panel/protocol/PROTOCOL_UPGRADE_REVIEW.md b/Panel/protocol/PROTOCOL_UPGRADE_REVIEW.md new file mode 100644 index 00000000..fea9eafc --- /dev/null +++ b/Panel/protocol/PROTOCOL_UPGRADE_REVIEW.md @@ -0,0 +1,168 @@ +# Protocol Upgrade Review (Phase 1, safe path) + +## Scope reviewed +- `Panel/protocol/lgsl/` +- `Panel/protocol/GameQ/` +- `Panel/modules/gamemanager/server_monitor.php` +- `Panel/modules/gamemanager/ref_servermonitor.php` +- `Panel/modules/gamemanager/start_server.php` +- `Panel/modules/gamemanager/restart_server.php` +- `Panel/modules/gamemanager/mini_start.php` +- `Panel/modules/dashboard/query_ref.php` +- `Panel/modules/config_games/server_configs/*.xml` + +## Current protocol folders +- `Panel/protocol/lgsl/` + - Legacy LGSL implementation and protocol map (`lgsl_protocol.php`). + - Monitor helper (`LGSLMonitor.php`). + - Player list rendering helper (`functions.php`). +- `Panel/protocol/GameQ/` + - Modern namespaced GameQ implementation with PSR-4 autoloader. + - Monitor helper (`GameQMonitor.php`) + player list rendering helper (`functions.php`). + - Very large protocol coverage in `Protocols/`. + - Also contains legacy-looking `gameq/` subtree alongside modern files (technical debt signal). + +## How LGSL is currently called +- Monitor refresh path: + - `gamemanager/ref_servermonitor.php` loads `protocol/lgsl/LGSLMonitor.php` when XML protocol is `lgsl`. + - `LGSLMonitor.php` calls `lgsl_query_live(...)`, handles panel query cache (`getServerStatusCache`/`saveServerStatusCache`), and sets `$status/$map/$players/$player_list`. +- Start/restart detection path: + - `gamemanager/start_server.php` and `restart_server.php` call `lgsl_query_live(..., "sa")` after process launch to decide if server is considered running. +- Quick checks: + - `gamemanager/mini_start.php` and other helpers call `lgsl_port_conversion(...)` and `lgsl_query_live(...)`. +- Connection links: + - `server_monitor.php` and dsi/lgsl image flows use `lgsl_port_conversion(...)` and `lgsl_software_link(...)`. + +## GameQ status (usable vs incomplete) +- Present and actively used in monitor/start/restart flows (`GameQMonitor.php`, `start_server.php`, `restart_server.php`, `dashboard/query_ref.php`). +- Usable for configured XML entries (`protocol=gameq` + `gameq_query_name`). +- Inconsistencies/risks: + - Mixed API usage exists in module code (`process()` and `requestData()` patterns). + - Legacy and modern GameQ structures coexist under `Panel/protocol/GameQ/`. + - GameQ is integrated, but implementation consistency is incomplete and should be normalized before broad protocol migration. + +## Files that map game configs to protocol names +- Primary source of protocol selection: + - `Panel/modules/config_games/server_configs/*.xml` fields: + - `` + - `` + - `` +- Supporting editor/schema surfaces: + - `Panel/modules/config_games/config_servers.php` (schema order includes protocol tags). + - `Panel/modules/config_games/xml_tag_descriptions.php` (documents protocol fields). + - `Panel/modules/config_games/xml_config_creator.php` (protocol selector and query-name population). + +## Where player list parsing happens +- LGSL: + - Query payload from `lgsl_query_live(...)` in `lgsl_protocol.php`. + - Player table rendering in `protocol/lgsl/functions.php::print_player_list(...)`. +- GameQ: + - Query payload from `GameQMonitor.php`. + - Player table rendering and field normalization in `protocol/GameQ/functions.php::print_player_list_gameq(...)`. + +## Where map/player/server status data is returned +- LGSL live payload shape: `b/s/e/p/t` arrays from `lgsl_query_live(...)`: + - status: `b.status` + - map/name/player counts/password: `s.*` + - player list: `p` + - extras (including some bot fields): `e` +- GameQ normalized payload (after `normalise` filter): + - status: `server.gq_online` + - map: `server.gq_mapname` + - player counts: `server.gq_numplayers`, `server.gq_maxplayers` + - player list: `server.players` + +## Known problems (Phase 1 findings) +- Query invocation is duplicated in multiple modules (monitor/start/restart/dashboard/image modules), increasing drift risk. +- Start detection currently combines process + query checks but does not explicitly represent a `starting` state in monitor output. +- LGSL uses hard exits for invalid parameters (`lgsl_query_live`), which is risky for direct callers without guard logic. +- GameQ integration style is not fully standardized across all call sites. +- Existing cache model is spread across current status cache and module-specific image caches, without one normalized query cache contract. + +## New wrapper prepared in this phase +- Added `Panel/protocol/gsp_query.php`. +- Introduces `gsp_query_server($server_info, $options = [])` normalized result contract. +- Keeps default provider on LGSL (`lgsl_legacy`) for safety. +- Adds provider concept placeholders (no broad provider switch in this phase): + - `lgsl_legacy` + - `gameq` + - `xpaw_source_query` + - `minecraft_query` + - `custom_script` + +## Proposed query cache table (planning only, no migration applied) +```sql +CREATE TABLE IF NOT EXISTS OGP_DB_PREFIXserver_query_cache ( + id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + home_id INT NOT NULL, + ip VARCHAR(64) NOT NULL, + port INT NOT NULL, + query_port INT NULL, + protocol VARCHAR(64) NULL, + provider VARCHAR(64) NULL, + online TINYINT(1) NOT NULL DEFAULT 0, + server_name VARCHAR(255) NULL, + map_name VARCHAR(128) NULL, + players INT NULL, + max_players INT NULL, + bots INT NULL, + passworded TINYINT(1) NULL, + latency_ms INT NULL, + player_list_json MEDIUMTEXT NULL, + raw_json MEDIUMTEXT NULL, + last_query_at DATETIME NULL, + last_success_at DATETIME NULL, + last_error TEXT NULL, + UNIQUE KEY uniq_home_query (home_id), + KEY idx_last_query_at (last_query_at), + KEY idx_online (online) +); +``` + +Cache TTL target: **60 seconds** default. + +## Server start detection improvement plan (no behavior change yet) +- After start command, mark status as **starting**. +- Poll on a short interval until timeout: + 1. Check agent process state. + 2. Check network port open. + 3. Check query response (if protocol supported). +- Final states: + - Process + query OK: **Online** + - Process OK but query unavailable: **Running, query unavailable** + - Process missing at timeout: **Failed to start** + +## Game Manager integration plan (`server_monitor.php`) +Future `Live Server Status` panel should include: +- State: Online / Offline / Starting / Running query unavailable +- Server name +- Current map +- Players / max players +- Player list +- Query latency +- Last query time +- Banner preview +- “Get banner code” action + +## Admin query debug/test page plan +Future page: `Panel/protocol/query_test.php` (admin-only) +- Inputs: IP, port, query port, protocol, provider, timeout +- Outputs: normalized result, raw result, errors +- Security: + - admin-only access gate + - CSRF for submit actions + - request limits / timeout caps + - no anonymous/public proxy behavior + +## Recommended next phase +1. Switch one low-risk monitor path to read `gsp_query_server()` output in parallel with existing behavior (feature-flag style). +2. Standardize GameQ call style (single API usage pattern) and document supported protocol mappings. +3. Add normalized cache write/read adapter (without removing existing caches yet). +4. Add explicit start-state model and timeout policy constants. +5. Begin unified banner module implementation against normalized cache payloads. + +## Safety statement +- No protocol engines were removed. +- LGSL remains in place. +- GameQ remains in place. +- Existing server monitor behavior remains intact in this phase. diff --git a/Panel/protocol/gsp_query.php b/Panel/protocol/gsp_query.php new file mode 100644 index 00000000..e9b71711 --- /dev/null +++ b/Panel/protocol/gsp_query.php @@ -0,0 +1,157 @@ + false, + 'online' => false, + 'provider' => 'lgsl_legacy', + 'protocol' => '', + 'game' => '', + 'server_name' => '', + 'map' => '', + 'players' => 0, + 'max_players' => 0, + 'bots' => 0, + 'passworded' => false, + 'latency_ms' => null, + 'address' => '', + 'port' => 0, + 'query_port' => 0, + 'player_list' => array(), + 'raw' => array(), + 'error' => '', + ); + } +} + +if (!function_exists('gsp_query_normalize_player_list')) { + function gsp_query_normalize_player_list($players) + { + $normalized = array(); + foreach ((array)$players as $player) { + $normalized[] = array( + 'name' => isset($player['name']) ? (string)$player['name'] : '', + 'score' => isset($player['score']) ? (int)$player['score'] : 0, + 'time' => isset($player['time']) ? $player['time'] : '', + 'ping' => isset($player['ping']) ? (int)$player['ping'] : 0, + 'raw' => (array)$player, + ); + } + return $normalized; + } +} + +if (!function_exists('gsp_query_server')) { + function gsp_query_server($server_info, $options = array()) + { + $result = gsp_query_default_result(); + $server = (array)$server_info; + $options = (array)$options; + + $provider = isset($options['provider']) ? (string)$options['provider'] : (isset($server['query_provider']) ? (string)$server['query_provider'] : 'lgsl_legacy'); + $result['provider'] = $provider; + + $ip = isset($server['ip']) ? trim((string)$server['ip']) : ''; + $port = isset($server['port']) ? (int)$server['port'] : 0; + $query_ip = $ip; + if (!empty($server['use_nat']) && !empty($server['agent_ip'])) { + $query_ip = trim((string)$server['agent_ip']); + } + + $result['address'] = ($ip !== '' && $port > 0) ? $ip . ':' . $port : ''; + $result['port'] = $port; + + if ($provider !== 'lgsl_legacy') { + $result['error'] = "Query provider not implemented yet: {$provider}"; + return $result; + } + + $query_name = ''; + if (isset($server['lgsl_query_name'])) { + $query_name = (string)$server['lgsl_query_name']; + } elseif (isset($server['query_name'])) { + $query_name = (string)$server['query_name']; + } + $query_name = trim($query_name); + $result['protocol'] = $query_name; + + if ($query_name === '') { + $result['error'] = 'Missing LGSL query name.'; + return $result; + } + + if ($query_ip === '' || preg_match("/[^0-9a-z\\.\\-\\[\\]\\:]/i", $query_ip)) { + $result['error'] = 'Invalid query IP/hostname.'; + return $result; + } + + if ($port <= 0) { + $result['error'] = 'Invalid server port.'; + return $result; + } + + require_once __DIR__ . '/lgsl/lgsl_protocol.php'; + $protocols = lgsl_protocol_list(); + if (!isset($protocols[$query_name])) { + $result['error'] = "Unsupported LGSL protocol type: {$query_name}"; + return $result; + } + + list($c_port, $default_q_port, $s_port) = lgsl_port_conversion($query_name, $port, "", ""); + $q_port = isset($server['query_port']) && (int)$server['query_port'] > 0 ? (int)$server['query_port'] : (int)$default_q_port; + $result['query_port'] = $q_port; + + if ($q_port <= 0) { + $result['error'] = 'Invalid query port for LGSL query.'; + return $result; + } + + $raw = lgsl_query_live($query_name, $query_ip, $c_port, $q_port, $s_port, "sep"); + if (!is_array($raw) || !isset($raw['b']) || !isset($raw['b']['status'])) { + $result['error'] = 'LGSL query returned an invalid payload.'; + return $result; + } + + $result['raw'] = $raw; + $result['success'] = true; + $result['online'] = ((string)$raw['b']['status'] === '1' || (int)$raw['b']['status'] === 1); + $result['game'] = isset($raw['s']['game']) ? (string)$raw['s']['game'] : ''; + $result['server_name'] = isset($raw['s']['name']) ? (string)$raw['s']['name'] : ''; + $result['map'] = isset($raw['s']['map']) ? (string)$raw['s']['map'] : ''; + $result['players'] = isset($raw['s']['players']) ? (int)$raw['s']['players'] : 0; + $result['max_players'] = isset($raw['s']['playersmax']) ? (int)$raw['s']['playersmax'] : 0; + $result['bots'] = isset($raw['e']['bots']) ? (int)$raw['e']['bots'] : 0; + $result['passworded'] = !empty($raw['s']['password']); + $result['latency_ms'] = isset($raw['t']['ping']) ? (int)$raw['t']['ping'] : null; + $result['player_list'] = isset($raw['p']) ? gsp_query_normalize_player_list($raw['p']) : array(); + + return $result; + } +} +?>