aboutsummaryrefslogtreecommitdiff
path: root/internal/game/casino.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-09-16 21:05:39 -0400
committerhistoria <[not public]>2026-09-16 21:05:39 -0400
commit35ddd0ad2215760bb1d3dc7724480f179ea33fa6 (patch)
tree3e9acf5e8a690544ecee1ec03af888ee28dd05b8 /internal/game/casino.go
parent6ef40a5739f1723ec26cc32dfc48fe764f776dca (diff)
downloadthehouseoficarus-35ddd0ad2215760bb1d3dc7724480f179ea33fa6.tar.gz
fix: login account race, casino chip double-refund, room-create rollback, rename collision, fatal transport errors
Diffstat (limited to 'internal/game/casino.go')
-rw-r--r--internal/game/casino.go37
1 files changed, 33 insertions, 4 deletions
diff --git a/internal/game/casino.go b/internal/game/casino.go
index f9eb771..79d324a 100644
--- a/internal/game/casino.go
+++ b/internal/game/casino.go
@@ -103,16 +103,28 @@ func (w casinoWallet) payCreditsFlagged(amount int, refund bool) error {
// resort: pay through the character file only. The save below is
// the commit point — the money is durable even without a journal
// record, so the settlement may be announced as paid. The stake
- // resolution evidence (ResolveStakes) keeps recovery from treating
- // the round as unresolved; without a payout record, an open wager
- // can no longer be distinguished from a lost one, so the balance
- // itself is the only record of what happened.
+ // resolution evidence (recordStakeEvidence) recorded in that same
+ // file keeps recovery from treating the round as unresolved;
+ // without a payout record, an open wager can no longer be
+ // distinguished from a lost one, so the balance itself is the only
+ // record of what happened.
if capErr != nil {
return capErr
}
w.p.Credits = creditsAfter
+ // The character file is the only durable record, so it must also
+ // carry the resolution evidence for this player's open stakes: a
+ // crash before the games' own ResolveStakes call would otherwise
+ // leave the wager records open with checkpoint balances that no
+ // longer match, and recovery would refund chip stakes the paid
+ // settlement had already consumed. The single save below is the
+ // commit point for the payout AND the resolution.
+ resolved := w.recordStakeEvidence()
if serr := w.g.AccountStore.SaveCharacter(w.p); serr != nil {
w.p.Credits = creditsBefore
+ if resolved > 0 {
+ w.p.ResolvedStakes = w.p.ResolvedStakes[:len(w.p.ResolvedStakes)-resolved]
+ }
return casino.ErrWalletPersistence
}
kind := "settlement"
@@ -174,6 +186,23 @@ func (w casinoWallet) ResolveStakes() {
}
}
+// recordStakeEvidence adds every currently open stake of the player to the
+// file-side resolution evidence list and returns how many IDs were newly
+// recorded. Used when the economy journal is unavailable: the open wager
+// record IDs in the character file prove the round resolved even though no
+// settled marker (or payout record) could ever be journaled. The caller
+// must persist the character file for the evidence to count.
+func (w casinoWallet) recordStakeEvidence() int {
+ added := 0
+ for _, rec := range w.g.Economy.openStakes(economyKey(w.p)) {
+ if !resolvedStakeRecorded(w.p, rec.ID) {
+ w.p.ResolvedStakes = append(w.p.ResolvedStakes, rec.ID)
+ added++
+ }
+ }
+ return added
+}
+
// RefundOutstanding returns every outstanding stake to the player: chips
// come back as chips, credits through the durable payout path. The stakes
// are resolved only once the return is durable; on failure the chip return