feat: refuse stale character saves with a save generation

Every character gets a save generation in charxml. A world bumps it when it loads the character for
play, and every save from that world only goes through while the stored generation is still the
one it loaded or last saved (and moves it on). Dashboard edits, restores and maintenance writes bump
it too. A world that lost the character to another world (a disconnect noticed late, a zone
transfer, an instance migration) or to a dashboard edit can no longer overwrite the newer data: the
save is refused, logged and audited as stale_save_refused, the world stops saving that character
and a player still connected to it is disconnected with the save failure reason so they reload.

Fixes #639

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-26 19:10:16 -05:00
parent 17689cd663
commit 341ce89a6a
14 changed files with 280 additions and 6 deletions

View File

@@ -0,0 +1,2 @@
/* Stale save guard: see the MySQL migration. */
ALTER TABLE charxml ADD COLUMN save_generation INTEGER NOT NULL DEFAULT 0;