From 194164a77eb4f2479b94c19e6f0e1278ab608781 Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Fri, 18 Sep 2026 16:13:49 -0400 Subject: fix: more casino, economy, gameplay, security, and world data hardening gaps --- internal/game/sys_triggers.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'internal/game/sys_triggers.go') diff --git a/internal/game/sys_triggers.go b/internal/game/sys_triggers.go index 312fe50..8bba630 100644 --- a/internal/game/sys_triggers.go +++ b/internal/game/sys_triggers.go @@ -151,6 +151,7 @@ func (g *Game) advanceSequence(seq *sequence) { if len(seq.steps) == 0 { g.completePlayerSequence(seq, p) } + g.syncPendingSequence(seq, p) return } @@ -172,6 +173,7 @@ func (g *Game) advanceSequence(seq *sequence) { if len(seq.steps) == 0 { g.completePlayerSequence(seq, p) } + g.syncPendingSequence(seq, p) return } @@ -220,6 +222,22 @@ func (g *Game) completePlayerSequence(seq *sequence, p *player.Player) { } } +// syncPendingSequence keeps a locked sequence's persisted snapshot in step +// with the live sequence as it advances, so a crash before the next clean +// disconnect/shutdown cannot replay already-fired steps (and re-grant their +// rewards) on reconnect. It is a no-op for unlocked/completed sequences. +func (g *Game) syncPendingSequence(seq *sequence, p *player.Player) { + if p == nil || !seq.locked || p.PendingSequence == nil || p.PendingSequence.Key != seq.key { + return + } + if len(seq.steps) == 0 { + return + } + p.PendingSequence.Steps = append([]behavior.Step(nil), seq.steps...) + p.PendingSequence.Wait = seq.wait + g.saveCharacter(p) +} + func (g *Game) completeGlobalSequence(seq *sequence) { g.removeSequence(seq) } -- cgit v1.2.3