From 8fe9e3c3fef0049df82281a62afe35dda516dfe2 Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Fri, 18 Sep 2026 17:48:26 -0400 Subject: fix: close combat ghost, admin traversal, login throttle, journal double-pay, and quality/economy gaps --- internal/casino/baccarat.go | 48 +++++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 19 deletions(-) (limited to 'internal/casino/baccarat.go') diff --git a/internal/casino/baccarat.go b/internal/casino/baccarat.go index c9a50a1..164d0ff 100644 --- a/internal/casino/baccarat.go +++ b/internal/casino/baccarat.go @@ -200,24 +200,28 @@ func (t *baccaratTable) deal() []Event { if !p.HasBet { continue } - result, payout := settleBaccaratBet(p.lastSpot, p.Wager.Total, playerTotal, bankerTotal, t.Rules) - payoutFailed := false - if payout > 0 && p.Wallet != nil { + result, payout, settleErr := settleBaccaratBet(p.lastSpot, p.Wager.Total, playerTotal, bankerTotal, t.Rules) + // A payout-overflow error means the win could not be computed; treat + // it like an unpersisted payout so the win is never announced as paid + // (blackjack handles the same case identically). + payoutFailed := settleErr != nil + if !payoutFailed && payout > 0 && p.Wallet != nil { if err := p.Wallet.PayStakeCover(payout, []string{p.stakeID}); err != nil { // The payout could not be made durable; recovery will // re-apply the owed amount from the open journal record. // Do not announce the win as paid and leave the stakes // open for recovery to settle. payoutFailed = true - publicResult, _ := baccaratResultMessages(p.Name, result, payout) - settlement := t.playerAction(p.Name, publicResult, payoutFailedMessage()) - if result == "win" { - settlement.Color = "casino_big_win" - } - events = append(events, settlement) } } - if !payoutFailed { + if payoutFailed { + publicResult, _ := baccaratResultMessages(p.Name, result, payout) + settlement := t.playerAction(p.Name, publicResult, payoutFailedMessage()) + if result == "win" { + settlement.Color = "casino_big_win" + } + events = append(events, settlement) + } else { publicResult, privateResult := baccaratResultMessages(p.Name, result, payout) settlement := t.playerAction(p.Name, publicResult, privateResult) if result == "win" { @@ -360,15 +364,18 @@ func bankerDraws(bankerTotal, playerThird int) bool { // winnings) for one wager. Player and banker bets push on a tie. Winnings // round down to whole credits, so the banker commission effectively rounds // up: never bet less than one commission unit. -func settleBaccaratBet(spot string, bet int, playerTotal, bankerTotal int, rules BaccaratConfig) (string, int) { +func settleBaccaratBet(spot string, bet int, playerTotal, bankerTotal int, rules BaccaratConfig) (string, int, error) { switch { case playerTotal > bankerTotal: if spot == baccaratSpotPlayer { - return "win", bet * 2 + return "win", bet * 2, nil } case bankerTotal > playerTotal: if spot == baccaratSpotBanker { - bankerWin, _ := MulPayout(bet, rules.BankerPayout) + bankerWin, err := MulPayout(bet, rules.BankerPayout) + if err != nil { + return "win", bet + bankerWin, err + } // The banker commission rounds UP, not down: when the wager is // too small for a whole credit of commission, a winning banker // bet returns only the stake. MulPayout's positive-stake floor @@ -377,18 +384,21 @@ func settleBaccaratBet(spot string, bet int, playerTotal, bankerTotal int, rules // push), which the "never bet less than one commission unit" // rule exists to prevent. if float64(bet)*rules.BankerPayout < 1 { - return "push", bet + return "push", bet, nil } - return "win", bet + bankerWin + return "win", bet + bankerWin, nil } default: if spot == baccaratSpotTie { - tieWin, _ := MulPayout(bet, rules.TiePayout) - return "win", bet + tieWin + tieWin, err := MulPayout(bet, rules.TiePayout) + if err != nil { + return "win", bet + tieWin, err + } + return "win", bet + tieWin, nil } - return "push", bet + return "push", bet, nil } - return "loss", 0 + return "loss", 0, nil } func baccaratOutcomeMessage(playerTotal, bankerTotal int) string { -- cgit v1.2.3