From 0b0b25222b3131ada79ff78c28eb8bf07d977deb Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Tue, 15 Sep 2026 15:35:52 -0400 Subject: feat: single-owner game state, transactional lifecycle, durable money on server stop --- internal/game/exit_migrate.go | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) (limited to 'internal/game/exit_migrate.go') diff --git a/internal/game/exit_migrate.go b/internal/game/exit_migrate.go index ff905f6..db70c8d 100644 --- a/internal/game/exit_migrate.go +++ b/internal/game/exit_migrate.go @@ -112,11 +112,11 @@ func rekeyIntMap[V any](m map[int]V, idMap map[int]int) bool { return true } -// RewriteRoomIDs is the shared entry point called by every room-ID change path -// (the swapid command and the admin web GUI rename) to migrate room-ID-embedded +// RewriteRoomIDs is the shared entry point called by every room-ID change +// path (the swapid command and the admin web GUI rename) to migrate room-ID-embedded // state across all characters. Online players are updated in memory (and -// re-saved); offline characters are loaded, rewritten, and saved. It follows -// the same cross-goroutine mutation precedent as cmd_undig/cmd_summon. +// re-saved); offline characters are loaded, rewritten, and saved. Callers +// must be on the owner goroutine (use MigrateRoomIDs from other goroutines). func (g *Game) RewriteRoomIDs(idMap map[int]int) { if len(idMap) == 0 { return @@ -150,3 +150,34 @@ func (g *Game) RewriteRoomIDs(idMap map[int]int) { } } } + +// MigrateRoomIDs is the complete room-ID migration entry point for other +// goroutines (the admin API and swapid command): player state, Hub room +// buckets, casino room keys, world room-keyed runtime state, and live mob +// instances all move together under one owner task. This is what makes a +// live rename leave NO gameplay or broadcast attached to the old location. +func (g *Game) MigrateRoomIDs(idMap map[int]int) { + if len(idMap) == 0 { + return + } + g.RunOnOwner(func() { + g.migrateRoomIDsLocked(idMap) + }) +} + +// migrateRoomIDsLocked performs the full runtime migration; caller must +// already be on the owner goroutine. +func (g *Game) migrateRoomIDsLocked(idMap map[int]int) { + if g.Hub != nil { + g.Hub.RekeyRooms(idMap) + } + if g.Casino != nil { + g.Casino.RekeyRooms(idMap) + } + g.World.RekeyRooms(idMap) + g.MobStore.RekeyRooms(idMap) + // The file paths changed under the world: rebuild its room index so + // lookups agree with the post-migration layout. + g.World.RebuildRoomIndex(g.DataDir) + g.RewriteRoomIDs(idMap) +} -- cgit v1.2.3