feat: add database-driven Steam Workshop system

- Create 3 new DB tables: workshop_game_profiles, workshop_cache, server_workshop_mods
- Add WorkshopRepository (DB access layer for all 3 tables)
- Add WorkshopInstaller (rsync/robocopy/custom_script copy logic, SteamCMD download via agent exec)
- Add WorkshopUpdater (scheduled cache update functions grouped by agent)
- Add WorkshopPreStart (pre-start mod sync helper)
- Add WorkshopProfileController (admin CRUD for profiles)
- Add WorkshopModController (user install/remove/toggle/load_order/sync)
- Add admin views: profiles list + profile_form
- Add user views: user_workshop_index + user_workshop_mods
- Add cron_update.php CLI entry point (--all/--agent-id/--home-id/--profile-id/--workshop-id)
- Add prestart_sync.php CLI helper for XML pre_start hook
- Update workshop_admin.php to route to profile management
- Update main.php to route to new mod management (legacy fallback preserved)
- Update module.php with DB migration SQL and version bump to 2.1
- Update lang/en_US.php with all new strings

Agent-Logs-Url: https://github.com/GameServerPanel/GSP/sessions/dbeebd0e-e7a5-469d-8a8c-e63193d1ebb0

Co-authored-by: iaretechnician <2749183+iaretechnician@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-04-30 18:01:33 +00:00 committed by GitHub
parent 4ad46c4332
commit 8eff063a93
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 3007 additions and 8 deletions

View file

@ -0,0 +1,91 @@
#!/usr/bin/env php
<?php
declare(strict_types=1);
/*
* OGP / GSP Steam Workshop pre-start sync helper
*
* Called from the game XML <pre_start> tag or a server pre-start hook:
* php modules/steam_workshop/prestart_sync.php --home-id=<ID>
*
* This script:
* 1. Finds all enabled Workshop mods for the given home.
* 2. Checks each mod's local cache on the agent.
* 3. If the cache differs from the server install path, syncs it.
* 4. Continues normal server start (exits 0 on success).
* 5. Exits non-zero ONLY if a critical error prevents completion.
*
* Design note: sync failures are logged but do NOT abort the server start,
* because a stale mod is better than no start.
*/
$panelRoot = defined('PANEL_ROOT') ? PANEL_ROOT : realpath(__DIR__ . '/../../..');
if ($panelRoot === false) {
$panelRoot = __DIR__ . '/../../..';
}
chdir($panelRoot);
if (!is_file('includes/config.inc.php')) {
fwrite(STDERR, "[ERROR] Cannot locate includes/config.inc.php.\n");
exit(0); // don't block server start
}
require_once 'includes/config.inc.php';
require_once 'includes/database.php';
require_once 'includes/database_mysqli.php';
require_once 'includes/lib_remote.php';
if (!isset($db_host, $db_user, $db_pass, $db_name)) {
fwrite(STDERR, "[ERROR] Database configuration not set.\n");
exit(0);
}
$db = new OGPDatabaseMySQL();
$connResult = $db->connect($db_host, $db_user, $db_pass, $db_name, $table_prefix ?? 'gsp_', $db_port ?? null);
if ($connResult !== true) {
fwrite(STDERR, "[ERROR] DB connect failed: {$connResult}\n");
exit(0);
}
require_once __DIR__ . '/lib/WorkshopRepository.php';
require_once __DIR__ . '/lib/WorkshopInstaller.php';
require_once __DIR__ . '/lib/WorkshopPreStart.php';
$opts = getopt('', ['home-id:', 'help']);
if (isset($opts['help']) || !isset($opts['home-id'])) {
echo "Usage: php prestart_sync.php --home-id=<ID>\n";
exit(0);
}
$homeId = (int)$opts['home-id'];
if ($homeId <= 0) {
fwrite(STDERR, "[ERROR] --home-id must be a positive integer.\n");
exit(0);
}
$home = $db->getGameHome($homeId);
if (!is_array($home)) {
fwrite(STDERR, "[WARN] Home {$homeId} not found skipping pre-start sync.\n");
exit(0);
}
$repo = new WorkshopRepository($db);
$installer = new WorkshopInstaller($repo);
$preStart = new WorkshopPreStart($repo, $installer);
$result = $preStart->syncModsForHome($home);
echo sprintf(
"[PRE-START] home=%d synced=%d skipped=%d failed=%d\n",
$homeId,
$result['synced'],
$result['skipped'],
$result['failed']
);
foreach ((array)($result['log'] ?? []) as $line) {
echo " {$line}\n";
}
// Always exit 0 don't block server start due to Workshop sync issues
exit(0);