Status: root cause confirmed
Affects: MultiplayerPvP Closed Beta v3 (mqtt branch)
Symptom
Tester reports: "the winning sound plays like 3x" / win screen re-triggers at match end.
Root cause
The Lua referee port dropped the legacy TS server's game-over guard. try_resolve_round (pvp_api/referee.lua:154-191) runs on every play_hand the host receives — both players, including the host's own loopback, plus stray sends near match end (play_hand(chips,0) at ui/game/game_state.lua:304, play_hand(0,0) on deck-out at game_state.lua:454). Once a player's lives hit 0, lives_blocker stops further life loss, but the broadcast("pvp_win", ...) at referee.lua:183 re-fires unconditionally on every subsequent resolution attempt — and the same missing guard applies to the other pvp_win broadcast sites (referee.lua:241, 268-274, 287, 299).
Each duplicate pvp_win loops back through action_win_game/action_lose_game (networking/action_handlers.lua:465-476), which have no idempotency check, replaying the jingle and screen each time.
Related (unconfirmed, likely same class): the forfeit path pvp_player_won (pvp_api/gamemodes.lua) and practice-mode win paths (lib/ghost_replay.lua) are structurally similar and may exhibit the same symptom.
Status: root cause confirmed
Affects: MultiplayerPvP Closed Beta v3 (mqtt branch)
Symptom
Tester reports: "the winning sound plays like 3x" / win screen re-triggers at match end.
Root cause
The Lua referee port dropped the legacy TS server's game-over guard.
try_resolve_round(pvp_api/referee.lua:154-191) runs on everyplay_handthe host receives — both players, including the host's own loopback, plus stray sends near match end (play_hand(chips,0)atui/game/game_state.lua:304,play_hand(0,0)on deck-out atgame_state.lua:454). Once a player's lives hit 0,lives_blockerstops further life loss, but thebroadcast("pvp_win", ...)atreferee.lua:183re-fires unconditionally on every subsequent resolution attempt — and the same missing guard applies to the otherpvp_winbroadcast sites (referee.lua:241, 268-274, 287, 299).Each duplicate
pvp_winloops back throughaction_win_game/action_lose_game(networking/action_handlers.lua:465-476), which have no idempotency check, replaying the jingle and screen each time.Related (unconfirmed, likely same class): the forfeit path
pvp_player_won(pvp_api/gamemodes.lua) and practice-mode win paths (lib/ghost_replay.lua) are structurally similar and may exhibit the same symptom.