SCUM 停止/重启/更新以前只有“结束进程”这一条路,插件没有声明任何优雅关闭方式,
平台也没有把停止后重新启动串起来。现在插件声明自己的关闭脚本,run 先执行它,
平台在停止或更新成功后再自动拉起服务。
run:
- lifecycle stop 支持插件声明的 gracefulStop(可执行文件、参数、环境、超时、
fallback=report|terminate);关闭命令超时且声明 report 时任务失败,不再默默杀进程。
- 新增 steam.update 依赖探针:调用 steamcmd +app_info_print 获取公开分支 buildid,
与本地 steamapps/appmanifest_<appid>.acf 的 buildid 比较,输出
installed/latest/update=yes|no|unknown。
platform:
- 新增 POST /api/v1/server-instances/{id}/restart 与 /update。
- restart 派发插件 stop 动作(走优雅关闭),终态成功后入队 start 作业。
- update 派发插件 install 动作;插件在更新前必须先优雅关闭 SCUM,关闭失败直接拒绝
SteamCMD 更新,成功后平台再拉起服务。
- 依赖检查输入带上插件声明的服务器安装根目录,供 steam.update 读取 appmanifest。
plugin (SCUM server plugin 0.1.16):
- bin/scum-stop.cmd:解析已声明的可执行文件路径,定位同路径正在运行的 SCUMServer.exe,
通过本地 RCON 公告并发送关闭命令,等待进程自行退出;不再使用 taskkill。
- bin/scum-rcon.ps1:插件自有的 Source RCON 客户端,从 UE4SS mod config.ini 读取
密码/端口,密钥不离开本机。
- actions/stop.json 声明 gracefulStop;actions/install.json 更新前先执行同一关闭脚本。
platform_web:
- 服务器详情新增“重启”按钮和“SCUM 版本更新”面板;点“检查更新”查询公开分支版本,
只有检测到更新时“更新版本”按钮才会置为可用并高亮,点击后先确认再派发更新任务。
99 lines
9.2 KiB
Markdown
99 lines
9.2 KiB
Markdown
# Server Plugin and Instance Lifecycle Contract
|
|
|
|
## Installed Server Plugin
|
|
|
|
An installed game management plugin is reusable. It defines a server type, but it is not a server instance.
|
|
|
|
The plugin marketplace API is a platform-facing projection over this installed registry metadata. It is not a package store, billing system, provider marketplace, or cloud host sales surface.
|
|
|
|
### States
|
|
|
|
- `installed`: plugin manifest and schemas are valid.
|
|
- `disabled`: plugin cannot create new servers but existing instances remain inspectable.
|
|
- `invalid`: plugin failed validation and cannot create or manage instances.
|
|
- `updating`: plugin version is being changed.
|
|
|
|
## Server Instance
|
|
|
|
A server instance is created from one installed game management plugin and is later bound to the generated Run endpoint when that Run registers. Platform stores the instance and projections; Run owns observed lifecycle execution on the machine.
|
|
|
|
### States
|
|
|
|
- `draft`: instance record exists and is awaiting Run-owned lifecycle bootstrap or reports.
|
|
- `installing`: Run reports that plugin-owned install/bootstrap work is active.
|
|
- `ready`: install/bootstrap succeeded without starting a supervised process, and the server can start.
|
|
- `running`: server process is running.
|
|
- `stopped`: server process is stopped.
|
|
- `failed`: last lifecycle operation failed.
|
|
- `deleted`: instance is no longer active.
|
|
|
|
## Invariants
|
|
|
|
- One `GamePlugin` installation may own many `ServerInstance` records.
|
|
- A `ServerInstance` must keep its own config version, artifacts, jobs, log streams, and permissions.
|
|
- Updating a plugin must not silently mutate existing server instances without a recorded reconcile job.
|
|
- Deleting a plugin must be blocked or explicitly require handling existing server instances first.
|
|
|
|
## Lifecycle Actions
|
|
|
|
- `create`: validate plugin and create the instance record without requiring a run endpoint, deployment target, or runtime profile.
|
|
- `start`: record/authorize operator intent and route bounded control to the bound Run when applicable; generated Run startup is driven by its package-local autonomous lifecycle plan.
|
|
- `stop`: record/authorize operator intent and route bounded control to the bound Run when applicable. Run runs the plugin-declared graceful stop step first and escalates to bounded termination only when that action declares `fallback: "terminate"`.
|
|
- `restart`: queues the plugin-declared stop action for a `running` or `stopped` instance and queues the plugin-declared start action after the terminal stop job succeeds. A failed or cancelled stop leaves the instance failed and never starts the server.
|
|
- `update`: queues the plugin-declared install/update action for a `running`, `stopped`, `ready`, or `failed` instance and queues the plugin-declared start action after the update job succeeds. The plugin owns the graceful close inside its install action, so an update never replaces server files under a live process.
|
|
- `delete`: stop server when needed, preserve or remove artifacts according to policy, mark deleted.
|
|
|
|
## Implemented Workflow Routes
|
|
|
|
- `GET /api/v1/plugin-marketplace/plugins` lists plugin marketplace summaries from registry metadata with status, server type, capability, and keyword filters.
|
|
- `GET /api/v1/plugin-marketplace/plugins/{id}` returns one registry-backed marketplace detail.
|
|
- `POST /api/v1/plugin-marketplace/plugins/{id}/state` applies metadata-only `install`, `enable`, or `disable` state changes.
|
|
- `POST /api/v1/server-instances/workflows/create` validates an installed plugin, server name, idempotency key, and plugin-declared create inputs when provided. It creates the instance without requiring a deployment target, run endpoint, or runtime profile. Generated Run packages carry the autonomous lifecycle plan that Run consumes on startup; registration confirms binding/auth and does not enqueue bootstrap lifecycle jobs.
|
|
- `POST /api/v1/server-instances/{id}/start` validates the instance is `ready` or `stopped`, checks the expected config version, verifies the plugin start action and run endpoint `process.start` capability, and queues a start job.
|
|
- `POST /api/v1/server-instances/{id}/stop` validates the instance is `running`, checks the expected config version, verifies the plugin stop action and run endpoint `process.stop` capability, and queues a stop job.
|
|
- `POST /api/v1/server-instances/{id}/restart` validates the instance is `running` or `stopped`, checks the expected config version, verifies the plugin stop action and run endpoint `process.stop` capability, and queues the plugin-declared stop job recorded as a `restart` lifecycle operation.
|
|
- `POST /api/v1/server-instances/{id}/update` validates the instance is `running`, `stopped`, `ready`, or `failed`, checks the expected config version, verifies the plugin install action and run endpoint `process.install` capability, and queues the plugin-declared install/update job recorded as an `update` lifecycle operation.
|
|
- Server-scoped raw config read/diff/approve routes are not product APIs. AI-assisted configuration uses reviewable AI config-diff approvals and typed dispatch metadata without returning raw config text to plugin pages.
|
|
- `POST /api/v1/file-operations/dispatch` queues scoped `files.read` or `files.write` jobs for logical server/plugin file keys after role and permission checks.
|
|
- `GET /api/v1/metrics/server-instances` returns bounded per-server metrics for instances visible to the authenticated user.
|
|
|
|
Workflow route responses include the accepted action, bounded server instance metadata, and bounded job metadata. They do not expose run session tokens, host paths, raw credentials, direct sockets, AI provider keys, or plugin action file contents.
|
|
|
|
Server metrics responses are bounded and platform-mediated. They do not expose host filesystem paths, run sockets, raw credentials, direct storage backends, or AI provider keys.
|
|
|
|
Config write approval and file dispatch are platform-mediated. They carry logical keys such as `server.properties` or `logs/latest.log`, scoped refs such as `input://...` or `artifact://...`, and bounded job metadata only. They do not mutate local files in the platform process and do not expose raw host paths, run credentials, direct sockets, AI provider keys, or inline large payloads.
|
|
|
|
Marketplace state actions update only registry install state. They do not download packages, dispatch run jobs, execute plugin bridge code, write server files, expose package bytes, or contact external services. Package acquisition and runtime execution remain deferred to explicit future changes.
|
|
|
|
## Lifecycle Projection
|
|
|
|
Platform-visible lifecycle state is a projection from Run-reported facts. Terminal run job results update the associated server instance when the job capability is a lifecycle capability for explicit Platform-authorized operations:
|
|
|
|
- `process.install` + `succeeded` marks the instance `ready`.
|
|
- `process.start` + `succeeded` marks the instance `running`.
|
|
- `process.stop` + `succeeded` marks the instance `stopped`.
|
|
- `process.install`, `process.start`, or `process.stop` + `failed` or `cancelled` marks the instance `failed`.
|
|
|
|
Active start and stop jobs do not introduce separate `starting` or `stopping` states in this change. Operators can inspect pending job state through the job list while the instance remains in its last terminal server state.
|
|
|
|
A terminal succeeded `restart` or `update` job queues the plugin-declared start action for the same instance and config version with idempotency key `<restart|update key>:start`. The follow-up start runs through the same start validation as an explicit operator start. Active start, stop, restart, and update jobs do not introduce separate `starting`, `stopping`, `restarting`, or `updating` states; operators can inspect pending job state through the job list while the instance remains in its last terminal server state.
|
|
|
|
## Plugin-Declared Graceful Stop
|
|
|
|
Run performs no game-specific shutdown logic. A plugin stop action may declare a `gracefulStop` step in `actions/stop.json`:
|
|
|
|
```json
|
|
"gracefulStop": {
|
|
"executableKey": "bin/scum-stop.cmd",
|
|
"environment": { "SERVER_STOP_SHUTDOWN_COMMAND": "Quit" },
|
|
"timeoutMs": 300000,
|
|
"fallback": "terminate"
|
|
}
|
|
```
|
|
|
|
Run runs that declared script first, then reads the supervised process state. If the process has exited, the stop result reports a graceful stop. If it is still running, Run applies the declared fallback: `report` fails the job so the operator can intervene, and `terminate` escalates to the existing bounded termination path. The shutdown command, player notice, wait budget, and Steam app id stay in plugin-owned files and manifest declarations; Run and Platform only implement the generic mechanism.
|
|
|
|
## Plugin-Declared Build Comparison
|
|
|
|
A plugin may declare a `steam.update` dependency probe with `targetKey` (the SteamCMD executable binding), `steamAppId`, and an optional platform list. Run queries the published build for the app's public branch with SteamCMD and compares it with the installed build recorded in `steamapps/appmanifest_<appId>.acf` under the instance's declared server install root, falling back to the SteamCMD directory. The probe reports bounded evidence of the form `installed=<build> latest=<build> update=yes|no` (or `update=unknown`) so the management console can light up an update action without embedding game policy in the platform.
|