Refactor deploy/push scripts and enhance game docs generator

- Add comprehensive headers to deploy_gsp.sh and push_to_github.sh
- Make scripts configurable via environment variables
- Update push_to_github.sh to support GITHUB_TOKEN env var
- Enhance generate_game_docs.py with:
  * Command-line argument support (--games, --force, --todo-only)
  * Extraction of detailed startup parameters from XML
  * Prevention of overwriting existing files unless --force is used
  * Comprehensive parameter documentation with options and defaults

Co-authored-by: iaretechnician <2749183+iaretechnician@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-11-22 22:35:31 +00:00
parent d3c83c33b1
commit f8ae24e87b
4 changed files with 498 additions and 108 deletions

View file

@ -1,4 +1,40 @@
#!/usr/bin/env bash #!/usr/bin/env bash
#
# GSP Deployment Script
# =====================
# This script deploys the Game Server Panel (GSP) from GitHub to a web server.
#
# HOW IT WORKS:
# 1. Clones/updates the GSP repository to a staging directory
# 2. Syncs files to the web root using rsync (preserving configs)
# 3. Sets proper permissions for OGP panel operation
#
# CONFIGURATION:
# All settings can be configured via environment variables or by editing
# the defaults in the "Config" section below.
#
# ENVIRONMENT VARIABLES:
# - REPO_URL: Git repository URL (default: https://github.com/GameServerPanel/GSP.git)
# - STAGE_DIR: Staging directory for git clone (default: $HOME/gsp_stage)
# - WEB_ROOT: Live web server directory (default: /var/www/html/panel)
# - OWNER: File owner user (default: www-data)
# - GROUP: File owner group (default: www-data)
# - SUDO: Command prefix for privilege escalation (default: sudo, set empty to skip)
# - DRY_RUN: Set to 1 to test without making changes (default: 0)
#
# EXAMPLE USAGE:
# # Use defaults:
# ./deploy_gsp.sh
#
# # Custom web root:
# WEB_ROOT=/home/panel/public_html ./deploy_gsp.sh
#
# # Dry run to test:
# DRY_RUN=1 ./deploy_gsp.sh
#
# # Different user/group:
# OWNER=apache GROUP=apache ./deploy_gsp.sh
#
set -Eeuo pipefail set -Eeuo pipefail
umask 022 umask 022

View file

@ -26,7 +26,7 @@
<h2 id="quick-info">Quick Info</h2> <h2 id="quick-info">Quick Info</h2>
<div style="background: #1e3a5f; padding: 20px; border-left: 4px solid #3b82f6; margin: 20px 0; border-radius: 4px;"> <div style="background: #1e3a5f; padding: 20px; border-left: 4px solid #3b82f6; margin: 20px 0; border-radius: 4px;">
<ul style="color: #e5e7eb; line-height: 1.8; margin: 0;"> <ul style="color: #e5e7eb; line-height: 1.8; margin: 0;">
<li><strong style="color: #ffffff;">Default Port:</strong> <code style="background: #0f172a; padding: 2px 6px; border-radius: 3px; color: #a5b4fc;">28015</code></li> <li><strong style="color: #ffffff;">Default Port:</strong> <code style="background: #0f172a; padding: 2px 6px; border-radius: 3px; color: #a5b4fc;">Varies (see configuration)</code></li>
<li><strong style="color: #ffffff;">Protocol:</strong> TCP/UDP</li> <li><strong style="color: #ffffff;">Protocol:</strong> TCP/UDP</li>
<li><strong style="color: #ffffff;">Minimum RAM:</strong> 1GB</li> <li><strong style="color: #ffffff;">Minimum RAM:</strong> 1GB</li>
<li><strong style="color: #ffffff;">Engine:</strong> Various</li> <li><strong style="color: #ffffff;">Engine:</strong> Various</li>
@ -38,33 +38,7 @@
<h2 id="ports">🔌 Network Ports</h2> <h2 id="ports">🔌 Network Ports</h2>
<div style="background: #1e3a5f; padding: 20px; border-left: 4px solid #3b82f6; margin: 20px 0; border-radius: 4px;"> <div style="background: #1e3a5f; padding: 20px; border-left: 4px solid #3b82f6; margin: 20px 0; border-radius: 4px;">
<h3 style="color: #ffffff; margin-top: 0;">Required Ports</h3> <h3 style="color: #ffffff; margin-top: 0;">Required Ports</h3>
<p style="color: #e5e7eb;">The following ports are used by this game server:</p> <p style="color: #e5e7eb;">The Rust server typically uses a configurable port. Check your server configuration files for the specific port settings.</p>
<table style="width: 100%; color: #e5e7eb; border-collapse: collapse;">
<thead>
<tr style="background: #0f172a;">
<th style="padding: 10px; text-align: left; color: #ffffff;">Port</th>
<th style="padding: 10px; text-align: left; color: #ffffff;">Protocol</th>
<th style="padding: 10px; text-align: left; color: #ffffff;">Purpose</th>
</tr>
</thead>
<tbody>
<tr style="border-bottom: 1px solid #374151;">
<td style="padding: 10px;"><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px;">28015</code></td>
<td style="padding: 10px;">UDP</td>
<td style="padding: 10px;">Game port</td>
</tr>
<tr style="border-bottom: 1px solid #374151;">
<td style="padding: 10px;"><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px;">28016</code></td>
<td style="padding: 10px;">TCP</td>
<td style="padding: 10px;">RCON</td>
</tr>
<tr style="border-bottom: 1px solid #374151;">
<td style="padding: 10px;"><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px;">28082</code></td>
<td style="padding: 10px;">TCP</td>
<td style="padding: 10px;">Web panel (if enabled)</td>
</tr>
</tbody>
</table>
<h3 style="color: #ffffff; margin-top: 20px;">Firewall Configuration</h3> <h3 style="color: #ffffff; margin-top: 20px;">Firewall Configuration</h3>
<p style="color: #e5e7eb;">Allow server ports through your firewall:</p> <p style="color: #e5e7eb;">Allow server ports through your firewall:</p>
@ -194,68 +168,116 @@ setadminpassword [password]
<h2 id="parameters">⚙️ Startup Parameters</h2> <h2 id="parameters">⚙️ Startup Parameters</h2>
<h3>Basic Startup</h3> <h3>Command Line Template</h3>
<pre><code># Generic startup command structure <p>The server uses the following command line template:</p>
./server_executable [parameters] <pre><code>-batchmode -nographics +server.ip %IP% %PORT% %QUERY_PORT% %PLAYERS% %HOSTNAME% %IDENTITY% %DESCRIPTION% %WORLDSIZE% %SEED% %SALT% %TICKRATE% %MAP% %BCK% %SAVEINTERNAL% %SECURE% %RCONWEB% %CONTROL_PASSWORD% -logfile output.txt</code></pre>
</code></pre>
<h3>Common Parameters</h3> <h3>Available Startup Parameters</h3>
<ul> <p>The following parameters can be configured when starting the server:</p>
<li><code>-port [number]</code> - Set the server port</li>
<li><code>-maxplayers [number]</code> - Maximum player slots</li>
<li><code>-map [name]</code> - Starting map/level</li>
<li><code>-console</code> - Enable console output</li>
<li><code>-nographics</code> - Run without graphics (headless mode)</li>
</ul>
<h3>Creating a Start Script</h3> <div style="background: #1e3a5f; padding: 20px; border-left: 4px solid #3b82f6; margin: 20px 0; border-radius: 4px;">
<p><strong>Linux (start.sh):</strong></p> <div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #374151;">
<pre><code>#!/bin/bash <h4 style="color: #ffffff; margin-top: 0;">
cd /path/to/server <code style="background: #0f172a; padding: 4px 8px; border-radius: 3px; color: #a5b4fc;">+server.identity</code>
./server_executable [parameters] 2>&1 | tee server.log <span style="color: #e5e7eb; font-weight: normal; font-size: 0.9em;"> - +server.identity</span>
</code></pre> </h4>
<pre><code>chmod +x start.sh <p style="color: #e5e7eb; margin: 10px 0;">Changes path to your server data (e.g. rust/server/my_server_identity).</p>
./start.sh <p style="color: #fbbf24;"><strong>Default:</strong> <code style="background: #0f172a; padding: 2px 6px; border-radius: 3px;">server_identity</code></p>
</code></pre> </div>
<p><strong>Windows (start.bat):</strong></p> <div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #374151;">
<pre><code>@echo off <h4 style="color: #ffffff; margin-top: 0;">
cd /d "%~dp0" <code style="background: #0f172a; padding: 4px 8px; border-radius: 3px; color: #a5b4fc;">+server.description</code>
server_executable.exe [parameters] <span style="color: #e5e7eb; font-weight: normal; font-size: 0.9em;"> - +server.description</span>
pause </h4>
</code></pre> <p style="color: #e5e7eb; margin: 10px 0;">Server Description shown on server browser</p>
<p style="color: #fbbf24;"><strong>Default:</strong> <code style="background: #0f172a; padding: 2px 6px; border-radius: 3px;">Plain old Rust Server</code></p>
</div>
<h3>Running as a Service</h3> <div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #374151;">
<h4 style="color: #ffffff; margin-top: 0;">
<code style="background: #0f172a; padding: 4px 8px; border-radius: 3px; color: #a5b4fc;">+server.worldsize</code>
<span style="color: #e5e7eb; font-weight: normal; font-size: 0.9em;"> - +server.worldsize</span>
</h4>
<p style="color: #e5e7eb; margin: 10px 0;">Defines the size of the map generated (min 1000, max 8000).</p>
<p style="color: #fbbf24;"><strong>Default:</strong> <code style="background: #0f172a; padding: 2px 6px; border-radius: 3px;">1000</code></p>
</div>
<p><strong>Linux (systemd):</strong></p> <div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #374151;">
<pre><code># Create service file: /etc/systemd/system/gameserver.service <h4 style="color: #ffffff; margin-top: 0;">
[Unit] <code style="background: #0f172a; padding: 4px 8px; border-radius: 3px; color: #a5b4fc;">+server.seed</code>
Description=Rust Server <span style="color: #e5e7eb; font-weight: normal; font-size: 0.9em;"> - +server.seed</span>
After=network.target </h4>
<p style="color: #e5e7eb; margin: 10px 0;">Defines the map generation seed.</p>
<p style="color: #fbbf24;"><strong>Default:</strong> <code style="background: #0f172a; padding: 2px 6px; border-radius: 3px;">0</code></p>
</div>
[Service] <div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #374151;">
Type=simple <h4 style="color: #ffffff; margin-top: 0;">
User=gameserver <code style="background: #0f172a; padding: 4px 8px; border-radius: 3px; color: #a5b4fc;">+server.salt</code>
WorkingDirectory=/home/gameserver/server <span style="color: #e5e7eb; font-weight: normal; font-size: 0.9em;"> - +server.salt</span>
ExecStart=/home/gameserver/server/start.sh </h4>
Restart=on-failure <p style="color: #e5e7eb; margin: 10px 0;">Defines the randomization to mining resources.</p>
RestartSec=10 <p style="color: #fbbf24;"><strong>Default:</strong> <code style="background: #0f172a; padding: 2px 6px; border-radius: 3px;">0</code></p>
</div>
[Install] <div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #374151;">
WantedBy=multi-user.target <h4 style="color: #ffffff; margin-top: 0;">
</code></pre> <code style="background: #0f172a; padding: 4px 8px; border-radius: 3px; color: #a5b4fc;">+server.tickrate</code>
<span style="color: #e5e7eb; font-weight: normal; font-size: 0.9em;"> - +server.tickrate</span>
</h4>
<p style="color: #e5e7eb; margin: 10px 0;">Defines the server tickrate (going higher than 30 is not recommended).</p>
<p style="color: #fbbf24;"><strong>Default:</strong> <code style="background: #0f172a; padding: 2px 6px; border-radius: 3px;">30</code></p>
</div>
<pre><code># Enable and start service <div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #374151;">
sudo systemctl daemon-reload <h4 style="color: #ffffff; margin-top: 0;">
sudo systemctl enable gameserver <code style="background: #0f172a; padding: 4px 8px; border-radius: 3px; color: #a5b4fc;">+server.level</code>
sudo systemctl start gameserver <span style="color: #e5e7eb; font-weight: normal; font-size: 0.9em;"> - +server.level</span>
sudo systemctl status gameserver </h4>
</code></pre> <p style="color: #e5e7eb; margin: 10px 0;">Defines the map of the server.</p>
<p style="color: #e5e7eb;"><strong>Options:</strong></p>
<ul style="color: #e5e7eb; margin-left: 20px;">
<li><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px; color: #a5b4fc;">Barren</code> - Barren</li>
<li><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px; color: #a5b4fc;">CraggyIsland</code> - Craggy Island</li>
<li><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px; color: #a5b4fc;">HapisIsland</code> - Hapis Island</li>
<li><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px; color: #a5b4fc;">Procedural Map</code> - Procedural Map</li>
<li><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px; color: #a5b4fc;">SavasIsland</code> - Savas Island</li>
<li><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px; color: #a5b4fc;">SavasIsland_koth</code> - Savas Island KoTH</li>
</ul>
</div>
<h2 id="troubleshooting">🔧 Troubleshooting</h2> <div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #374151;">
<h4 style="color: #ffffff; margin-top: 0;">
<code style="background: #0f172a; padding: 4px 8px; border-radius: 3px; color: #a5b4fc;">+backup</code>
<span style="color: #e5e7eb; font-weight: normal; font-size: 0.9em;"> - +backup</span>
</h4>
<p style="color: #e5e7eb; margin: 10px 0;">Enable automatic backups.</p>
</div>
<h3>Server Won't Start</h3> <div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #374151;">
<h4 style="color: #ffffff; margin-top: 0;">
<code style="background: #0f172a; padding: 4px 8px; border-radius: 3px; color: #a5b4fc;">+server.saveinterval</code>
<span style="color: #e5e7eb; font-weight: normal; font-size: 0.9em;"> - +server.saveinterval</span>
</h4>
<p style="color: #e5e7eb; margin: 10px 0;">Interval between the server saves the map.</p>
<p style="color: #fbbf24;"><strong>Default:</strong> <code style="background: #0f172a; padding: 2px 6px; border-radius: 3px;">600</code></p>
</div>
<div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #374151;">
<h4 style="color: #ffffff; margin-top: 0;">
<code style="background: #0f172a; padding: 4px 8px; border-radius: 3px; color: #a5b4fc;">+rcon.web</code>
<span style="color: #e5e7eb; font-weight: normal; font-size: 0.9em;"> - +rcon.web</span>
</h4>
<p style="color: #e5e7eb; margin: 10px 0;">If set to enabled, use websocket RCON. If set to disabled, use legacy source engine RCON.</p>
<p style="color: #e5e7eb;"><strong>Options:</strong></p>
<ul style="color: #e5e7eb; margin-left: 20px;">
<li><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px; color: #a5b4fc;">0</code> - Disabled</li>
<li><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px; color: #a5b4fc;">1</code> - Enabled</li>
</ul>
</div>
</div>
<h4>Check Server Logs</h4> <h4>Check Server Logs</h4>
<pre><code># View recent log entries <pre><code># View recent log entries

View file

@ -1,26 +1,82 @@
#!/usr/bin/env bash #!/usr/bin/env bash
#
# GSP GitHub Push Script
# ======================
# This script commits local changes in your GSP panel directory and pushes
# them to GitHub via a Pull Request.
#
# HOW IT WORKS:
# 1. Initializes git in the panel directory (if needed)
# 2. Commits all changes (excluding sensitive files like config.inc.php)
# 3. Creates a timestamped branch
# 4. Attempts to push to upstream repo (requires write access)
# 5. If no write access, creates/uses a fork and submits PR from there
#
# CONFIGURATION:
# Configure settings via environment variables or by editing the defaults below.
#
# ENVIRONMENT VARIABLES:
# - PANEL_DIR: Path to your GSP panel installation (default: /var/www/html/panel)
# - TOKEN_FILE: File containing GitHub Personal Access Token (default: ~/.github-token)
# - GITHUB_TOKEN: GitHub token directly (used if TOKEN_FILE doesn't exist)
# - UPSTREAM_REPO: Target repository in "owner/repo" format (default: GameServerPanel/GSP)
# - BR_PREFIX: Branch name prefix for PRs (default: panel-sync)
# - GIT_USER_NAME: Git commit author name (default: Server Sync Bot)
# - GIT_USER_EMAIL: Git commit author email (default: server-sync@local)
#
# GITHUB TOKEN SETUP:
# You need a GitHub Personal Access Token with 'repo' scope.
# Create one at: https://github.com/settings/tokens
#
# Option 1: Store in file (recommended):
# echo "your_token_here" > ~/.github-token
# chmod 600 ~/.github-token
#
# Option 2: Use environment variable:
# export GITHUB_TOKEN="your_token_here"
#
# EXAMPLE USAGE:
# # Use defaults:
# ./push_to_github.sh
#
# # Custom panel directory:
# PANEL_DIR=/home/panel/public_html ./push_to_github.sh
#
# # Using environment token:
# GITHUB_TOKEN="ghp_xxxx" ./push_to_github.sh
#
set -Eeuo pipefail set -Eeuo pipefail
umask 022 umask 022
# ---------- HARD-CODED PROJECT SETTINGS ---------- # ---------- CONFIGURABLE SETTINGS ----------
PANEL_DIR="/var/www/html/panel" PANEL_DIR="${PANEL_DIR:-/var/www/html/panel}"
TOKEN_FILE="/home/gameserver/git.token" TOKEN_FILE="${TOKEN_FILE:-$HOME/.github-token}"
UPSTREAM_REPO="GameServerPanel/GSP" # owner/repo UPSTREAM_REPO="${UPSTREAM_REPO:-GameServerPanel/GSP}" # owner/repo
UPSTREAM_URL="https://github.com/${UPSTREAM_REPO}.git" UPSTREAM_URL="https://github.com/${UPSTREAM_REPO}.git"
BR_PREFIX="panel-sync" # branch prefix for PRs BR_PREFIX="${BR_PREFIX:-panel-sync}" # branch prefix for PRs
GIT_USER_NAME="Server Sync Bot" GIT_USER_NAME="${GIT_USER_NAME:-Server Sync Bot}"
GIT_USER_EMAIL="server-sync@local" GIT_USER_EMAIL="${GIT_USER_EMAIL:-server-sync@local}"
log(){ printf '[%s] %s\n' "$(date +'%F %T')" "$*"; } log(){ printf '[%s] %s\n' "$(date +'%F %T')" "$*"; }
die(){ echo "ERROR: $*" >&2; exit 1; } die(){ echo "ERROR: $*" >&2; exit 1; }
# ---------- PRECHECKS ---------- # ---------- PRECHECKS ----------
[[ -d "$PANEL_DIR" ]] || die "Panel dir not found: $PANEL_DIR" [[ -d "$PANEL_DIR" ]] || die "Panel dir not found: $PANEL_DIR"
[[ -s "$TOKEN_FILE" ]] || die "Token file missing/empty: $TOKEN_FILE"
TOKEN="$(<"$TOKEN_FILE")" # Try to get token from file or environment
[[ ${#TOKEN} -ge 10 ]] || die "Token in $TOKEN_FILE looks invalid" TOKEN=""
if [[ -s "$TOKEN_FILE" ]]; then
TOKEN="$(<"$TOKEN_FILE")"
elif [[ -n "${GITHUB_TOKEN:-}" ]]; then
TOKEN="$GITHUB_TOKEN"
else
die "GitHub token not found. Set GITHUB_TOKEN env var or create $TOKEN_FILE with your token.
Get a token at: https://github.com/settings/tokens (requires 'repo' scope)"
fi
[[ ${#TOKEN} -ge 10 ]] || die "Token looks invalid (too short)"
cd "$PANEL_DIR" cd "$PANEL_DIR"
if [[ ! -d ".git" ]]; then if [[ ! -d ".git" ]]; then

View file

@ -1,8 +1,42 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Comprehensive Game Server Documentation Generator for GSP Comprehensive Game Server Documentation Generator for GSP
Generates PHP documentation files for all games in the "todo" category
Based on the Minecraft template structure This script generates PHP documentation files for game servers in the GSP billing module.
USAGE:
# Generate docs for all incomplete games:
python3 generate_game_docs.py
# Generate docs for specific game(s):
python3 generate_game_docs.py --games minecraft csgo rust
# Regenerate docs (overwrite existing):
python3 generate_game_docs.py --games minecraft --force
# Process all "todo" category games:
python3 generate_game_docs.py --todo-only
OPTIONS:
--games GAME1 GAME2 ... Generate docs for specific game folder names
--force Overwrite existing documentation files
--todo-only Only process games with category="todo"
--help Show this help message
The generator extracts information from:
- XML configurations (modules/config_games/server_configs/*.xml)
- YAML knowledgepack (modules/billing/docs/gameserver_knowledgepack_v2.yaml)
- Existing metadata.json files
Generated documentation includes:
- Quick info (ports, RAM, Steam App ID)
- Network ports and firewall configuration
- Installation steps with SteamCMD commands
- Server configuration files and settings
- Detailed startup parameters from XML
- Comprehensive troubleshooting guide
- Performance optimization tips
- Security best practices
""" """
import os import os
@ -10,6 +44,7 @@ import sys
import json import json
import yaml import yaml
import re import re
import argparse
from pathlib import Path from pathlib import Path
from datetime import datetime from datetime import datetime
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
@ -115,6 +150,68 @@ class GameDocGenerator:
return ports return ports
def extract_startup_parameters_from_xml(self, xml_root):
"""Extract detailed startup parameters from XML server_params section"""
startup_params = []
# Get CLI template and params
cli_template = xml_root.find('cli_template')
cli_template_text = cli_template.text if cli_template is not None else ""
cli_params_elem = xml_root.find('cli_params')
cli_params_info = {}
if cli_params_elem is not None:
for param in cli_params_elem.findall('cli_param'):
param_id = param.get('id', '')
cli_string = param.get('cli_string', '')
options = param.get('options', '')
if param_id:
cli_params_info[param_id] = {
'cli_string': cli_string,
'options': options
}
# Extract server_params - these are the configurable startup parameters
server_params = xml_root.find('server_params')
if server_params is not None:
for param in server_params.findall('param'):
param_key = param.get('key', '')
param_id = param.get('id', '')
param_type = param.get('type', 'text')
# Get caption and description
caption_elem = param.find('caption')
desc_elem = param.find('desc')
default_elem = param.find('default')
caption = caption_elem.text if caption_elem is not None else param_key
description = desc_elem.text if desc_elem is not None else "No description available"
default_value = default_elem.text if default_elem is not None else None
# For select type, get options
options = []
if param_type == 'select':
for option in param.findall('option'):
opt_value = option.get('value', '')
opt_text = option.text if option.text else opt_value
options.append({'value': opt_value, 'text': opt_text})
startup_params.append({
'key': param_key,
'id': param_id,
'type': param_type,
'caption': caption,
'description': description,
'default': default_value,
'options': options
})
return {
'cli_template': cli_template_text,
'cli_params': cli_params_info,
'server_params': startup_params
}
def extract_config_files_from_xml(self, xml_root): def extract_config_files_from_xml(self, xml_root):
"""Extract configuration file paths from XML""" """Extract configuration file paths from XML"""
config_files = [] config_files = []
@ -139,15 +236,17 @@ class GameDocGenerator:
kb_info = self.get_game_info_from_knowledgepack(game_name) kb_info = self.get_game_info_from_knowledgepack(game_name)
xml_config = self.get_xml_config(folder_name) xml_config = self.get_xml_config(folder_name)
# Extract ports and configs # Extract ports, configs, and startup parameters
ports_info = [] ports_info = []
config_files = [] config_files = []
startup_params_data = {}
if xml_config is not None: if xml_config is not None:
ports_info = self.extract_ports_from_xml(xml_config) ports_info = self.extract_ports_from_xml(xml_config)
config_files = self.extract_config_files_from_xml(xml_config) config_files = self.extract_config_files_from_xml(xml_config)
startup_params_data = self.extract_startup_parameters_from_xml(xml_config)
# Build the PHP document # Build the PHP document
php_content = self.build_php_content(game_name, folder_name, kb_info, xml_config, ports_info, config_files) php_content = self.build_php_content(game_name, folder_name, kb_info, xml_config, ports_info, config_files, startup_params_data)
return php_content return php_content
@ -180,7 +279,7 @@ class GameDocGenerator:
return 'N/A' return 'N/A'
def build_php_content(self, game_name, folder_name, kb_info, xml_config, ports_info, config_files): def build_php_content(self, game_name, folder_name, kb_info, xml_config, ports_info, config_files, startup_params_data):
"""Build the complete PHP documentation content""" """Build the complete PHP documentation content"""
# Extract data from various sources # Extract data from various sources
@ -495,7 +594,67 @@ setadminpassword [password]
</code></pre> </code></pre>
<h2 id="parameters"> Startup Parameters</h2> <h2 id="parameters"> Startup Parameters</h2>
'''
# Add detailed startup parameters from XML if available
if startup_params_data and startup_params_data.get('server_params'):
server_params = startup_params_data['server_params']
cli_template = startup_params_data.get('cli_template', '')
if cli_template:
php_doc += f'''
<h3>Command Line Template</h3>
<p>The server uses the following command line template:</p>
<pre><code>{cli_template}</code></pre>
'''
php_doc += '''
<h3>Available Startup Parameters</h3>
<p>The following parameters can be configured when starting the server:</p>
<div style="background: #1e3a5f; padding: 20px; border-left: 4px solid #3b82f6; margin: 20px 0; border-radius: 4px;">
'''
for param in server_params:
param_key = param['key']
caption = param['caption']
description = param['description']
param_type = param['type']
default = param.get('default')
options = param.get('options', [])
# Clean HTML from description
description_clean = re.sub(r'<[^>]+>', '', description) if description else "No description available"
php_doc += f'''
<div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #374151;">
<h4 style="color: #ffffff; margin-top: 0;">
<code style="background: #0f172a; padding: 4px 8px; border-radius: 3px; color: #a5b4fc;">{param_key}</code>
<span style="color: #e5e7eb; font-weight: normal; font-size: 0.9em;"> - {caption}</span>
</h4>
<p style="color: #e5e7eb; margin: 10px 0;">{description_clean}</p>
'''
if param_type == 'select' and options:
php_doc += ''' <p style="color: #e5e7eb;"><strong>Options:</strong></p>
<ul style="color: #e5e7eb; margin-left: 20px;">
'''
for opt in options:
php_doc += f''' <li><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px; color: #a5b4fc;">{opt['value']}</code> - {opt['text']}</li>\n'''
php_doc += ''' </ul>
'''
if default:
php_doc += f''' <p style="color: #fbbf24;"><strong>Default:</strong> <code style="background: #0f172a; padding: 2px 6px; border-radius: 3px;">{default}</code></p>
'''
php_doc += ''' </div>
'''
php_doc += '''</div>
'''
else:
# Fallback to generic parameters if no XML data
php_doc += '''
<h3>Basic Startup</h3> <h3>Basic Startup</h3>
''' '''
@ -893,7 +1052,102 @@ sudo ufw enable
return processed, errors return processed, errors
def process_specific_games(self, game_names, force_overwrite=False):
"""Process specific game folders by name"""
processed = 0
skipped = 0
errors = []
for game_name in game_names:
folder = self.docs_dir / game_name
if not folder.is_dir():
error_msg = f"Game folder not found: {game_name}"
print(f"{error_msg}")
errors.append(error_msg)
continue
metadata_file = folder / 'metadata.json'
index_file = folder / 'index.php'
if not metadata_file.exists():
error_msg = f"metadata.json not found for {game_name}"
print(f"{error_msg}")
errors.append(error_msg)
continue
try:
# Read metadata
with open(metadata_file, 'r', encoding='utf-8') as f:
content = f.read()
content = content.lstrip('\ufeff')
metadata = json.loads(content)
# Check if we should skip
if index_file.exists() and not force_overwrite:
print(f"Skipping {game_name} (file exists, use --force to overwrite)")
skipped += 1
continue
print(f"Processing: {game_name}")
# Generate new documentation
php_content = self.generate_php_doc(folder.name, metadata)
# Write the new index.php
with open(index_file, 'w', encoding='utf-8') as f:
f.write(php_content)
# Update metadata if it was todo
if metadata.get('category', '').lower() == 'todo':
metadata['category'] = 'game'
# Mark as complete
metadata['complete'] = True
with open(metadata_file, 'w', encoding='utf-8') as f:
json.dump(metadata, f, indent=4, ensure_ascii=False)
processed += 1
print(f" ✓ Generated documentation for {game_name}")
except Exception as e:
error_msg = f"Error processing {game_name}: {e}"
print(f"{error_msg}")
errors.append(error_msg)
return processed, skipped, errors
def main(): def main():
# Parse command line arguments
parser = argparse.ArgumentParser(
description='Generate comprehensive game server documentation for GSP',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Generate docs for all incomplete games:
python3 generate_game_docs.py
# Generate docs for specific game(s):
python3 generate_game_docs.py --games minecraft csgo rust
# Regenerate docs (overwrite existing):
python3 generate_game_docs.py --games minecraft --force
# Process all "todo" category games:
python3 generate_game_docs.py --todo-only
"""
)
parser.add_argument('--games', nargs='+', metavar='GAME',
help='Generate docs for specific game folder names')
parser.add_argument('--force', action='store_true',
help='Overwrite existing documentation files')
parser.add_argument('--todo-only', action='store_true',
help='Only process games with category="todo"')
args = parser.parse_args()
docs_dir = "/home/runner/work/GSP/GSP/modules/billing/docs" docs_dir = "/home/runner/work/GSP/GSP/modules/billing/docs"
config_dir = "/home/runner/work/GSP/GSP/modules/config_games/server_configs" config_dir = "/home/runner/work/GSP/GSP/modules/config_games/server_configs"
knowledgepack = "/home/runner/work/GSP/GSP/modules/billing/docs/gameserver_knowledgepack_v2.yaml" knowledgepack = "/home/runner/work/GSP/GSP/modules/billing/docs/gameserver_knowledgepack_v2.yaml"
@ -908,6 +1162,27 @@ def main():
generator.load_knowledgepack() generator.load_knowledgepack()
generator.load_xml_configs() generator.load_xml_configs()
processed = 0
skipped = 0
errors = []
# Determine which mode to run
if args.games:
# Process specific games
print("\n" + "="*70)
print(f"Processing {len(args.games)} specified game(s)...")
if args.force:
print("FORCE mode: Will overwrite existing files")
print("="*70)
processed, skipped, errors = generator.process_specific_games(args.games, args.force)
elif args.todo_only:
# Process only todo category games
print("\n" + "="*70)
print("Processing TODO category games...")
print("="*70)
processed, errors = generator.process_todo_folders()
else:
# Process incomplete games (default)
print("\n" + "="*70) print("\n" + "="*70)
print("Processing INCOMPLETE game documentation...") print("Processing INCOMPLETE game documentation...")
print("="*70) print("="*70)
@ -916,7 +1191,8 @@ def main():
print(f"\n{'='*70}") print(f"\n{'='*70}")
print(f"Documentation generation complete!") print(f"Documentation generation complete!")
print(f" ✓ Processed: {processed}") print(f" ✓ Processed: {processed}")
print(f" → Skipped (already complete): {skipped}") if skipped > 0:
print(f" → Skipped: {skipped}")
print(f" ✗ Errors: {len(errors)}") print(f" ✗ Errors: {len(errors)}")
if errors: if errors:
@ -926,12 +1202,12 @@ def main():
if len(errors) > 10: if len(errors) > 10:
print(f" ... and {len(errors) - 10} more") print(f" ... and {len(errors) - 10} more")
print(f"\nAll documentation has been enhanced with:") print(f"\nGenerated documentation includes:")
print(f" • Actual port information (not 'Check server configuration')") print(f" • Actual port information from XML configs")
print(f" • Steam App IDs and exact SteamCMD commands") print(f" • Steam App IDs and exact SteamCMD commands")
print(f" • Configuration file details from XML configs") print(f" • Configuration file details from XML configs")
print(f"Startup parameters extracted from XML") print(f"DETAILED startup parameters extracted from XML")
print(f"Troubleshooting info from knowledgepack") print(f"Comprehensive troubleshooting sections")
print("="*70) print("="*70)
return 0 if not errors else 1 return 0 if not errors else 1