From f8ae24e87b3367da2a2258a4eef7dfbe0d6c864e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Nov 2025 22:35:31 +0000 Subject: [PATCH] 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> --- deploy_gsp.sh | 36 ++++ modules/billing/docs/rust/index.php | 180 +++++++++------- push_to_github.sh | 76 ++++++- tools/generate_game_docs.py | 314 ++++++++++++++++++++++++++-- 4 files changed, 498 insertions(+), 108 deletions(-) diff --git a/deploy_gsp.sh b/deploy_gsp.sh index c4606de6..fafc0a7e 100644 --- a/deploy_gsp.sh +++ b/deploy_gsp.sh @@ -1,4 +1,40 @@ #!/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 umask 022 diff --git a/modules/billing/docs/rust/index.php b/modules/billing/docs/rust/index.php index e6cbd525..26816d53 100644 --- a/modules/billing/docs/rust/index.php +++ b/modules/billing/docs/rust/index.php @@ -26,7 +26,7 @@
28015Varies (see configuration)The following ports are used by this game server:
-| Port | -Protocol | -Purpose | -
|---|---|---|
28015 |
- UDP | -Game port | -
28016 |
- TCP | -RCON | -
28082 |
- TCP | -Web panel (if enabled) | -
The Rust server typically uses a configurable port. Check your server configuration files for the specific port settings.
Allow server ports through your firewall:
@@ -194,68 +168,116 @@ setadminpassword [password]# Generic startup command structure
-./server_executable [parameters]
-
+The server uses the following command line template:
+-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
--port [number] - Set the server port-maxplayers [number] - Maximum player slots-map [name] - Starting map/level-console - Enable console output-nographics - Run without graphics (headless mode)The following parameters can be configured when starting the server:
-Linux (start.sh):
-#!/bin/bash
-cd /path/to/server
-./server_executable [parameters] 2>&1 | tee server.log
-
-chmod +x start.sh
-./start.sh
-
+ +server.identity
+ - +server.identity
+ Changes path to your server data (e.g. rust/server/my_server_identity).
+Default: server_identity
Windows (start.bat):
-@echo off
-cd /d "%~dp0"
-server_executable.exe [parameters]
-pause
-
+ +server.description
+ - +server.description
+ Server Description shown on server browser
+Default: Plain old Rust Server
+server.worldsize
+ - +server.worldsize
+ Defines the size of the map generated (min 1000, max 8000).
+Default: 1000
Linux (systemd):
-# Create service file: /etc/systemd/system/gameserver.service
-[Unit]
-Description=Rust Server
-After=network.target
+
+
+ +server.seed
+ - +server.seed
+
+ Defines the map generation seed.
+ Default: 0
+
-[Service]
-Type=simple
-User=gameserver
-WorkingDirectory=/home/gameserver/server
-ExecStart=/home/gameserver/server/start.sh
-Restart=on-failure
-RestartSec=10
+
+
+ +server.salt
+ - +server.salt
+
+ Defines the randomization to mining resources.
+ Default: 0
+
-[Install]
-WantedBy=multi-user.target
-
+ +server.tickrate
+ - +server.tickrate
+ Defines the server tickrate (going higher than 30 is not recommended).
+Default: 30
# Enable and start service
-sudo systemctl daemon-reload
-sudo systemctl enable gameserver
-sudo systemctl start gameserver
-sudo systemctl status gameserver
-
+ +server.level
+ - +server.level
+ Defines the map of the server.
+Options:
+Barren - BarrenCraggyIsland - Craggy IslandHapisIsland - Hapis IslandProcedural Map - Procedural MapSavasIsland - Savas IslandSavasIsland_koth - Savas Island KoTH+backup
+ - +backup
+ Enable automatic backups.
++server.saveinterval
+ - +server.saveinterval
+ Interval between the server saves the map.
+Default: 600
+rcon.web
+ - +rcon.web
+ If set to enabled, use websocket RCON. If set to disabled, use legacy source engine RCON.
+Options:
+0 - Disabled1 - Enabled# View recent log entries
diff --git a/push_to_github.sh b/push_to_github.sh
index d454165b..fc8e8bc0 100644
--- a/push_to_github.sh
+++ b/push_to_github.sh
@@ -1,26 +1,82 @@
#!/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
umask 022
-# ---------- HARD-CODED PROJECT SETTINGS ----------
-PANEL_DIR="/var/www/html/panel"
-TOKEN_FILE="/home/gameserver/git.token"
+# ---------- CONFIGURABLE SETTINGS ----------
+PANEL_DIR="${PANEL_DIR:-/var/www/html/panel}"
+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"
-BR_PREFIX="panel-sync" # branch prefix for PRs
-GIT_USER_NAME="Server Sync Bot"
-GIT_USER_EMAIL="server-sync@local"
+BR_PREFIX="${BR_PREFIX:-panel-sync}" # branch prefix for PRs
+GIT_USER_NAME="${GIT_USER_NAME:-Server Sync Bot}"
+GIT_USER_EMAIL="${GIT_USER_EMAIL:-server-sync@local}"
log(){ printf '[%s] %s\n' "$(date +'%F %T')" "$*"; }
die(){ echo "ERROR: $*" >&2; exit 1; }
# ---------- PRECHECKS ----------
[[ -d "$PANEL_DIR" ]] || die "Panel dir not found: $PANEL_DIR"
-[[ -s "$TOKEN_FILE" ]] || die "Token file missing/empty: $TOKEN_FILE"
-TOKEN="$(<"$TOKEN_FILE")"
-[[ ${#TOKEN} -ge 10 ]] || die "Token in $TOKEN_FILE looks invalid"
+
+# Try to get token from file or environment
+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"
if [[ ! -d ".git" ]]; then
diff --git a/tools/generate_game_docs.py b/tools/generate_game_docs.py
index 8a0aa4b1..67513991 100755
--- a/tools/generate_game_docs.py
+++ b/tools/generate_game_docs.py
@@ -1,8 +1,42 @@
#!/usr/bin/env python3
"""
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
@@ -10,6 +44,7 @@ import sys
import json
import yaml
import re
+import argparse
from pathlib import Path
from datetime import datetime
import xml.etree.ElementTree as ET
@@ -115,6 +150,68 @@ class GameDocGenerator:
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):
"""Extract configuration file paths from XML"""
config_files = []
@@ -139,15 +236,17 @@ class GameDocGenerator:
kb_info = self.get_game_info_from_knowledgepack(game_name)
xml_config = self.get_xml_config(folder_name)
- # Extract ports and configs
+ # Extract ports, configs, and startup parameters
ports_info = []
config_files = []
+ startup_params_data = {}
if xml_config is not None:
ports_info = self.extract_ports_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
- 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
@@ -180,7 +279,7 @@ class GameDocGenerator:
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"""
# Extract data from various sources
@@ -495,21 +594,81 @@ setadminpassword [password]
The server uses the following command line template:
+{cli_template}
+'''
+
+ php_doc += '''
+The following parameters can be configured when starting the server:
+ +{param_key}
+ - {caption}
+ {description_clean}
+''' + + if param_type == 'select' and options: + php_doc += '''Options:
+{opt['value']} - {opt['text']}Default: {default}
{startup_cmd}
+ if startup_cmd:
+ php_doc += f'''{startup_cmd}
'''
- else:
- php_doc += '''# Generic startup command structure
+ else:
+ php_doc += '''# Generic startup command structure
./server_executable [parameters]
'''
- php_doc += '''
+ php_doc += '''
Common Parameters
-port [number] - Set the server port
@@ -892,8 +1051,103 @@ sudo ufw enable
errors.append(error_msg)
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():
+ # 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"
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"
@@ -908,15 +1162,37 @@ def main():
generator.load_knowledgepack()
generator.load_xml_configs()
- print("\n" + "="*70)
- print("Processing INCOMPLETE game documentation...")
- print("="*70)
- processed, skipped, errors = generator.process_incomplete_games()
+ 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("Processing INCOMPLETE game documentation...")
+ print("="*70)
+ processed, skipped, errors = generator.process_incomplete_games()
print(f"\n{'='*70}")
print(f"Documentation generation complete!")
print(f" ✓ Processed: {processed}")
- print(f" → Skipped (already complete): {skipped}")
+ if skipped > 0:
+ print(f" → Skipped: {skipped}")
print(f" ✗ Errors: {len(errors)}")
if errors:
@@ -926,12 +1202,12 @@ def main():
if len(errors) > 10:
print(f" ... and {len(errors) - 10} more")
- print(f"\nAll documentation has been enhanced with:")
- print(f" • Actual port information (not 'Check server configuration')")
+ print(f"\nGenerated documentation includes:")
+ print(f" • Actual port information from XML configs")
print(f" • Steam App IDs and exact SteamCMD commands")
print(f" • Configuration file details from XML configs")
- print(f" • Startup parameters extracted from XML")
- print(f" • Troubleshooting info from knowledgepack")
+ print(f" • DETAILED startup parameters extracted from XML")
+ print(f" • Comprehensive troubleshooting sections")
print("="*70)
return 0 if not errors else 1