feat(gameserver): /save admin command to force a world snapshot (#41) #42

Merged
marco merged 1 commit from feat/save-command into docs/persistence-study 2026-07-18 08:32:46 +02:00
Owner

Closes #41. Stacked on #40 — review/merge that first; this PR targets the persistence branch so its diff is only the /save work.

Adds an admin /save chat command to force an immediate world save instead of waiting for the 15s periodic snapshot.

  • New shared WorldPersister: gathers a snapshot of every online player and flushes it to world.sav. Both the periodic PersistenceService and /save go through the same single instance, and a lock-free gate (Interlocked) skips a save requested while another is in flight — so a manual save and the periodic one never write the file at once.
  • PersistenceService now delegates to WorldPersister (timer + shutdown).
  • SaveCommand : GmCommand (/save), authorized server-side by the signed admin claim via CanExecute. It returns a SaveWorld result the session handler fulfils (persistence is off the World facade, like targeting), replying with a SystemMessage: saved N players / nothing to save / already in progress / failed.
  • docs/gm-commands.md updated with the /save row.

Tests: WorldPersisterTests (writes file + reload / empty world / sequential saves both persist) and CommandTests (/save as admin returns SaveWorld; non-admin refused). Whole suite green (GameServer 110), 0 warnings, csharpier clean.

Definition of Done

  • /save as admin writes world.sav immediately and reports how many players were saved.
  • Non-admin /save is refused and writes no file.
  • /save with no players online reports nothing to save and writes no file.
  • A concurrent save is skipped with an already-in-progress reply (single shared WorldPersister).
  • Periodic save and /save share one code path (WorldPersister).
  • docs/gm-commands.md lists /save.
Closes #41. **Stacked on #40** — review/merge that first; this PR targets the persistence branch so its diff is only the `/save` work. Adds an admin `/save` chat command to force an immediate world save instead of waiting for the 15s periodic snapshot. - New shared `WorldPersister`: gathers a snapshot of every online player and flushes it to `world.sav`. Both the periodic `PersistenceService` and `/save` go through the **same single instance**, and a lock-free gate (`Interlocked`) skips a save requested while another is in flight — so a manual save and the periodic one never write the file at once. - `PersistenceService` now delegates to `WorldPersister` (timer + shutdown). - `SaveCommand : GmCommand` (`/save`), authorized server-side by the signed `admin` claim via `CanExecute`. It returns a `SaveWorld` result the session handler fulfils (persistence is off the `World` facade, like targeting), replying with a `SystemMessage`: saved N players / nothing to save / already in progress / failed. - `docs/gm-commands.md` updated with the `/save` row. Tests: `WorldPersisterTests` (writes file + reload / empty world / sequential saves both persist) and `CommandTests` (`/save` as admin returns `SaveWorld`; non-admin refused). Whole suite green (GameServer 110), 0 warnings, csharpier clean. ## Definition of Done - [x] `/save` as admin writes `world.sav` immediately and reports how many players were saved. - [x] Non-admin `/save` is refused and writes no file. - [x] `/save` with no players online reports nothing to save and writes no file. - [x] A concurrent save is skipped with an already-in-progress reply (single shared `WorldPersister`). - [x] Periodic save and `/save` share one code path (`WorldPersister`). - [x] `docs/gm-commands.md` lists `/save`.
feat(gameserver): /save admin command to force a world snapshot (#41)
All checks were successful
ci / Lint & Test (pull_request) Successful in 1m23s
98cff033c4
marco merged commit aad24a22b4 into docs/persistence-study 2026-07-18 08:32:46 +02:00
marco deleted branch feat/save-command 2026-07-18 08:32:46 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
marco/IsoMmo!42
No description provided.