Fix SCUM SteamCMD lifecycle install

This commit is contained in:
npc0-hue
2026-08-01 14:47:24 +08:00
parent 0e4dbf26aa
commit e37f00c48a
8 changed files with 56 additions and 14 deletions
+3 -2
View File
@@ -26,8 +26,9 @@ from command text. Empty `shell` means argv-oriented execution.
Game plugins own concrete game policy: install/update commands, app ids,
executable refs, default launch flags, stop-before-update behavior, and startup
argument construction. For SCUM, the plugin action assets own the SteamCMD flow:
stop `SCUMServer.exe` when updating, run
`steamcmd.exe +force_install_dir <serverRoot> +login anonymous +app_update 3792580 validate +quit`,
stop `SCUMServer.exe` when updating, keep SteamCMD outside the server install
root, run
`steamcmd.exe +force_install_dir <serverRoot> +login anonymous +app_update 3792580 +quit`,
and start `<serverRoot>\\SCUM\\Binaries\\Win64\\SCUMServer.exe -port=<gamePort> -MaxPlayers=<maxPlayers> -log`.
Generated Run distributions carry validated plugin lifecycle assets into the
@@ -9,8 +9,8 @@
"SERVER_TEMPLATE": "scum-server",
"SERVER_STEAM_APP_ID": "3792580",
"SERVER_STEAMCMD_INSTALL_DIR_ARG": "+force_install_dir",
"SERVER_STEAMCMD_UPDATE_ARGS": "+login anonymous +app_update 3792580 validate +quit",
"SERVER_STEAMCMD_UPDATE_ARGS": "+login anonymous +app_update 3792580 +quit",
"SERVER_EXECUTABLE_REF": "SCUM/Binaries/Win64/SCUMServer.exe"
},
"timeoutMs": 300000
"timeoutMs": 7200000
}
@@ -5,21 +5,24 @@ if "%SERVER_ROOT%"=="" set "SERVER_ROOT=C:\scumserver"
set "SERVER_ROOT_WINDOWS=%SERVER_ROOT:/=\%"
if "%SERVER_STEAM_APP_ID%"=="" set "SERVER_STEAM_APP_ID=3792580"
if "%SERVER_STEAMCMD_INSTALL_DIR_ARG%"=="" set "SERVER_STEAMCMD_INSTALL_DIR_ARG=+force_install_dir"
if "%SERVER_STEAMCMD_UPDATE_ARGS%"=="" set "SERVER_STEAMCMD_UPDATE_ARGS=+login anonymous +app_update 3792580 validate +quit"
if "%SERVER_STEAMCMD_UPDATE_ARGS%"=="" set "SERVER_STEAMCMD_UPDATE_ARGS=+login anonymous +app_update 3792580 +quit"
if "%SERVER_EXECUTABLE_REF%"=="" set "SERVER_EXECUTABLE_REF=SCUM\Binaries\Win64\SCUMServer.exe"
if "%SERVER_INSTALL_DIR%"=="" set "SERVER_INSTALL_DIR=%SERVER_ROOT_WINDOWS%"
set "SERVER_INSTALL_DIR_WINDOWS=%SERVER_INSTALL_DIR:/=\%"
set "SERVER_INSTALL_DIR_STEAM=%SERVER_INSTALL_DIR_WINDOWS:\=/%"
if "%SERVER_STEAMCMD_DIR%"=="" call :default_steamcmd_dir
set "SERVER_STEAMCMD_DIR_WINDOWS=%SERVER_STEAMCMD_DIR:/=\%"
if "%SERVER_STEAMCMD_MAX_ATTEMPTS%"=="" set "SERVER_STEAMCMD_MAX_ATTEMPTS=3"
if "%SERVER_STEAMCMD_RETRY_SECONDS%"=="" set "SERVER_STEAMCMD_RETRY_SECONDS=30"
set "STEAMCMD_DIR=%SERVER_ROOT_WINDOWS%\steamcmd"
set "STEAMCMD_DIR=%SERVER_STEAMCMD_DIR_WINDOWS%"
set "STEAMCMD_EXE=%STEAMCMD_DIR%\steamcmd.exe"
set "SCUM_EXE_MARKER=%SERVER_ROOT_WINDOWS%\.scum-exe-path"
if not exist "%SERVER_ROOT_WINDOWS%" mkdir "%SERVER_ROOT_WINDOWS%"
if not exist "%STEAMCMD_DIR%" mkdir "%STEAMCMD_DIR%"
if not exist "%SERVER_INSTALL_DIR_WINDOWS%" mkdir "%SERVER_INSTALL_DIR_WINDOWS%"
if /I "%STEAMCMD_DIR%"=="%SERVER_INSTALL_DIR_WINDOWS%" exit /b 3
if not exist "%STEAMCMD_EXE%" (
powershell -NoProfile -ExecutionPolicy Bypass -Command "[Net.ServicePointManager]::SecurityProtocol=[Net.SecurityProtocolType]::Tls12; $zip=Join-Path $env:STEAMCMD_DIR 'steamcmd.zip'; Invoke-WebRequest -Uri 'https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip' -OutFile $zip; Expand-Archive -Path $zip -DestinationPath $env:STEAMCMD_DIR -Force; Remove-Item $zip -Force"
@@ -54,3 +57,12 @@ set "SCUM_EXE=%SERVER_ROOT_WINDOWS%\steamcmd\steamapps\common\SCUM Server\%SERVE
if exist "%SCUM_EXE%" exit /b 0
set "SCUM_EXE=%SERVER_ROOT_WINDOWS%\SCUM Server\%SERVER_EXECUTABLE_REF:/=\%"
exit /b 0
:default_steamcmd_dir
for %%I in ("%SERVER_ROOT_WINDOWS%") do set "SERVER_ROOT_DRIVE=%%~dI"
if "%SERVER_ROOT_DRIVE%"=="" (
set "SERVER_STEAMCMD_DIR=%SERVER_ROOT_WINDOWS%-steamcmd"
) else (
set "SERVER_STEAMCMD_DIR=%SERVER_ROOT_DRIVE%\steamcmd"
)
exit /b 0
@@ -3,7 +3,7 @@
"id": "game.scum",
"name": "SCUM Server",
"description": "First-party SCUM game server operations plugin with platform-mediated lifecycle and companion bridge support.",
"version": "0.1.2",
"version": "0.1.4",
"kind": "game-plugin",
"tags": [
"scum",
@@ -21,7 +21,7 @@
"additionalProperties": { "type": "string", "maxLength": 512 },
"maxProperties": 32
},
"timeoutMs": { "type": "integer", "minimum": 1, "maximum": 300000 },
"timeoutMs": { "type": "integer", "minimum": 1, "maximum": 7200000 },
"stopTimeoutMs": { "type": "integer", "minimum": 1, "maximum": 60000 }
}
}
+4 -1
View File
@@ -199,11 +199,14 @@ describe("plugin manifest validation", () => {
const startScript = fs.readFileSync(path.join(pluginDir, startAction.executableKey), "utf8");
expect(manifest.runtimeProfiles.serverDeployments).toBeUndefined();
expect(assetPaths).toEqual(expect.arrayContaining(["actions/install.json", "actions/start.json", "bin/scum-install-update.cmd", "bin/scum-start.cmd"]));
expect(installAction).toMatchObject({ executableKey: "bin/scum-install-update.cmd", environment: { SERVER_STEAM_APP_ID: "3792580", SERVER_STEAMCMD_UPDATE_ARGS: "+login anonymous +app_update 3792580 validate +quit" } });
expect(installAction).toMatchObject({ executableKey: "bin/scum-install-update.cmd", environment: { SERVER_STEAM_APP_ID: "3792580", SERVER_STEAMCMD_UPDATE_ARGS: "+login anonymous +app_update 3792580 +quit" } });
expect(installAction.timeoutMs).toBe(7200000);
expect(startAction).toMatchObject({ executableKey: "bin/scum-start.cmd", environment: { SERVER_LOG_FLAG: "-log" } });
expect(installScript).toContain("taskkill /IM SCUMServer.exe /F");
expect(installScript).toContain("SERVER_ROOT_WINDOWS=%SERVER_ROOT:/=\\%");
expect(installScript).toContain("SERVER_INSTALL_DIR=%SERVER_ROOT_WINDOWS%");
expect(installScript).toContain("SERVER_STEAMCMD_DIR=%SERVER_ROOT_DRIVE%\\steamcmd");
expect(installScript).toContain("STEAMCMD_DIR=%SERVER_STEAMCMD_DIR_WINDOWS%");
expect(installScript).toContain("SERVER_INSTALL_DIR_STEAM=%SERVER_INSTALL_DIR_WINDOWS:\\=/%");
expect(installScript).toContain("%SERVER_STEAMCMD_INSTALL_DIR_ARG% \"%SERVER_INSTALL_DIR_STEAM%\" %SERVER_STEAMCMD_UPDATE_ARGS%");
expect(installScript).toContain("SERVER_STEAMCMD_MAX_ATTEMPTS=3");
+23 -3
View File
@@ -92,9 +92,29 @@ local_debug_seed_lifecycle_scope() {
cp "$source_dir/actions/$action.json" "$scope/actions/$action.json"
fi
done
cp "$true_binary" "$scope/bin/install-server"
cp "$true_binary" "$scope/bin/game-server"
chmod +x "$scope/bin/install-server" "$scope/bin/game-server"
local executable_key
while IFS= read -r executable_key; do
[[ -n "$executable_key" ]] || continue
local executable_target="$scope/$executable_key"
mkdir -p "$(dirname "$executable_target")"
cp "$true_binary" "$executable_target"
chmod +x "$executable_target"
done < <(node - "$scope/actions" <<'NODE'
const fs = require("fs");
const path = require("path");
const actionsDir = process.argv[2];
for (const entry of fs.readdirSync(actionsDir)) {
if (!entry.endsWith(".json")) continue;
const action = JSON.parse(fs.readFileSync(path.join(actionsDir, entry), "utf8"));
const key = action.executableKey;
if (typeof key !== "string" || key.length === 0) continue;
if (path.posix.isAbsolute(key) || key.includes("\\") || key.split("/").includes("..")) {
throw new Error(`unsafe local-debug executableKey: ${key}`);
}
console.log(key);
}
NODE
)
}
local_debug_seed_plugin_lifecycle_template() {
+8 -2
View File
@@ -296,14 +296,19 @@ json_id() {
require_scum_manifest_contract() {
local file="$1"
node - "$file" <<'NODE'
local expected_version="$2"
node - "$file" "$expected_version" <<'NODE'
const fs = require("fs");
const file = process.argv[2];
const expectedVersion = process.argv[3];
const plugin = JSON.parse(fs.readFileSync(file, "utf8"));
const missing = [];
const declaredPermissions = Array.isArray(plugin.declaredPermissions) ? plugin.declaredPermissions : [];
const bridgeActions = Array.isArray(plugin.bridgeActions) ? plugin.bridgeActions : [];
const lifecycleProfiles = plugin.runtimeProfiles?.lifecycleProfiles ?? [];
if (plugin.version !== expectedVersion) {
missing.push(`version ${expectedVersion}`);
}
if (!declaredPermissions.includes("server.run.distribution")) {
missing.push("server.run.distribution permission");
}
@@ -781,7 +786,8 @@ NODE
printf 'registering SCUM plugin through platform API\n'
register_plugin_manifest "SCUM plugin" "game.scum" "$WORK_DIR/register-scum-plugin.request.json" "$WORK_DIR/register-scum-plugin.response.json"
reject_forbidden_fragments "$WORK_DIR/register-scum-plugin.response.json"
require_scum_manifest_contract "$WORK_DIR/register-scum-plugin.response.json"
SCUM_PLUGIN_VERSION="$(node -e 'const fs=require("fs"); process.stdout.write(JSON.parse(fs.readFileSync(process.argv[1],"utf8")).version);' "$ROOT_DIR/plugins/examples/scum-server-plugin/manifest.json")"
require_scum_manifest_contract "$WORK_DIR/register-scum-plugin.response.json" "$SCUM_PLUGIN_VERSION"
printf 'waiting for run endpoint heartbeat\n'
for _ in $(seq 1 20); do