feat: cancel ranked 2v2 matches that don't fill or fully spawn#4655
Conversation
A ranked 2v2 that starts short a player, or where a player idles through the spawn phase, plays out as a lopsided 2v1 and records a ranked result. Instead, void the match: WinCheckExecution now runs a one-time check on the first tick after the spawn phase and ends the game with no winner when fewer than 4 players spawned (a player who never joined isn't in the start info at all, so the same check covers both cases). setWinner accepts null and emits a Win update with winner undefined — the wire schema already allowed an absent winner. WinModal's empty undefined-winner branch now shows "Match cancelled", and still votes the winnerless result to the server so the record archives promptly. The API treats winnerless ranked records as void. Server-side, the only change is keying winner votes with JSON.stringify(winner ?? null), since JSON.stringify(undefined) is not a string. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (7)
WalkthroughRanked 2v2 matches now cancel without a winner when not all expected human players spawn. The result is stored and emitted consistently, server vote keys normalize missing winners, and the client shows a localized replay modal. ChangesRanked 2v2 cancellation
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant WinCheckExecution
participant GameImpl
participant WinModal
participant GameServer
WinCheckExecution->>GameImpl: setWinner(null, allPlayersStats)
GameImpl-->>WinModal: emit Win update without winner
WinModal->>GameServer: send undefined winner
GameServer->>GameServer: normalize vote key to null
WinModal->>WinModal: show cancellation replay modal
Possibly related PRs
Suggested labels: Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Problem
A ranked 2v2 that starts with a missing player, or where a matched player idles through the spawn phase, plays out as a lopsided 2v1 — and records a ranked result (ELO) for a match that was never fair.
Solution
Void the match from inside the sim:
WinCheckExecutionruns a one-time check on the first check tick after the spawn phase ends. ForrankedType === "2v2", if fewer thanmaxPlayershumans have spawned, the game ends immediately with no winner. A player who never joined isn't in the game start info at all, so the single spawn-count check covers both the missing-at-start and never-spawned cases.setWinnernow acceptsnulland emits aWinUpdatewithwinner: undefined— the wire schema (WinnerSchema) already allowed an absent winner.WinModal's previously-emptywinner === undefinedbranch shows "Match cancelled — not all players spawned" and still votes the winnerless result to the server, so the winner-vote consensus is reached and the record archives promptly.winner; the API voids winnerless ranked records (same shape any ranked game already produces when the winner vote never reaches consensus).JSON.stringify(winner ?? null), sinceJSON.stringify(undefined)is not a string and would break theVoteRoundkey contract.The check counts via
allPlayers()rather thanplayers(), since the latter filters out tile-less players — which is exactly what a never-spawned player is.Ranked 1v1 is deliberately untouched (the existing last-human-connected walkover still applies).
Tests
tests/Ranked2v2Cancel.test.ts:WinUpdateFull suite (2094 tests), tsc, ESLint, and Prettier all pass.
🤖 Generated with Claude Code