Panel/modules/steam_workshop/lib/WorkshopPreStart.php
copilot-swe-agent[bot] 8eff063a93
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>
2026-04-30 18:01:33 +00:00

118 lines
4.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
/*
* OGP / GSP Steam Workshop
* WorkshopPreStart: syncs updated cached mods into the game server folder
* before the server is launched.
*
* Intended to be called from the game XML <pre_start> tag or from a
* pre-start hook in the panel.
*
* Design rules:
* - Does NOT restart running servers.
* - Only syncs if the cache differs from the installed path.
* - Logs every check and sync attempt.
*/
require_once __DIR__ . '/WorkshopRepository.php';
require_once __DIR__ . '/WorkshopInstaller.php';
class WorkshopPreStart
{
private WorkshopRepository $repo;
private WorkshopInstaller $installer;
private string $logFile;
public function __construct(WorkshopRepository $repo, WorkshopInstaller $installer)
{
$this->repo = $repo;
$this->installer = $installer;
$logDir = __DIR__ . '/../logs';
if (!is_dir($logDir)) {
mkdir($logDir, 0775, true);
}
$this->logFile = $logDir . '/workshop_prestart.log';
}
// ------------------------------------------------------------------
// Public API
// ------------------------------------------------------------------
/**
* Sync all enabled mods for the given home_id before server start.
*
* @param array $home Full game home row (from getGameHome / getUserGameHome)
* @return array{synced:int, skipped:int, failed:int, log:list<string>}
*/
public function syncModsForHome(array $home): array
{
$homeId = (int)($home['home_id'] ?? 0);
$log = [];
$synced = 0;
$skipped = 0;
$failed = 0;
$this->log("PRE-START home={$homeId}");
$mods = $this->repo->listEnabledModsForHome($homeId);
if (empty($mods)) {
$log[] = 'No enabled Workshop mods for this server.';
$this->log("PRE-START home={$homeId}: no mods");
return ['synced' => 0, 'skipped' => 0, 'failed' => 0, 'log' => $log];
}
foreach ((array)$mods as $modRow) {
$workshopId = (string)($modRow['workshop_id'] ?? '');
$profileId = (int)($modRow['profile_id'] ?? 0);
$log[] = "Checking mod {$workshopId}";
$profile = $profileId > 0 ? $this->repo->getProfileById($profileId) : null;
if ($profile === null) {
$log[] = " Profile not found (profile_id={$profileId}) skipped.";
$this->log("PRE-START home={$homeId} mod={$workshopId}: profile missing");
$skipped++;
continue;
}
$result = $this->installer->syncMod($home, $modRow, $profile);
if ($result['success'] && $result['changed']) {
$log[] = " Synced: " . ($result['message'] ?? '');
$this->log("PRE-START home={$homeId} mod={$workshopId}: synced");
$synced++;
} elseif ($result['success'] && !$result['changed']) {
$log[] = ' Already up to date no sync needed.';
$skipped++;
} else {
$log[] = " Sync failed: " . ($result['message'] ?? 'unknown error');
$this->log("PRE-START home={$homeId} mod={$workshopId}: FAILED");
$failed++;
}
// Append sub-log
foreach ((array)($result['log'] ?? []) as $line) {
$log[] = ' ' . $line;
}
}
$this->log("PRE-START home={$homeId} done: synced={$synced} skipped={$skipped} failed={$failed}");
return [
'synced' => $synced,
'skipped' => $skipped,
'failed' => $failed,
'log' => $log,
];
}
// ------------------------------------------------------------------
// Private helpers
// ------------------------------------------------------------------
private function log(string $message): void
{
$line = '[' . date('Y-m-d H:i:s') . '] ' . $message . "\n";
@file_put_contents($this->logFile, $line, FILE_APPEND | LOCK_EX);
}
}