Full Lifecycle at a Glance
Phase 1 — Matchmaking
Players enter a queue viaIMatchmakingService. The four queue types differ in how the server pairs players:
Online matches support 2–4 players. Pass & Play supports 2–8 players on a single device, but that limit does not apply to online modes. Tournament mode allows up to 8 players in server-scheduled brackets only.
While queuing, the client shows the Matchmaking Queue Modal: a spinner, an estimated wait time, and a Cancel button that calls
IMatchmakingService.CancelQueue(). The modal must be dismissible at any point before the match is created — if the server creates the match while the player is cancelling, the client must leave the match gracefully.
Phase 2 — Match Setup
Once the server has paired enough players, it:- Creates an authoritative match instance in the Nakama match handler.
- Generates and shuffles the 81-card deck using server-side RNG (not a seed sent to the client).
- Deals the initial 12-card board.
- Assigns each player a session ID (used as a tiebreaker for simultaneous claims).
- Broadcasts the full initial
MatchState— including all 12 card IDs and their positions — to every client over the WebSocket.
NakamaMultiplayerService receives this snapshot and emits it on IMultiplayerService.Messages. The OnlineMatchController subscribes to Messages, translates the snapshot into an ApplyServerStateCommand, and GameSession applies it, transitioning into mirror mode. Input is disabled until the initial state is fully applied and the deal animation completes. This prevents any player from tapping cards before the board is guaranteed to be in sync.
Phase 3 — In-Match Play Loop
The server runs at 20 Hz (one tick every 50 ms). This rate was chosen to balance claim-resolution responsiveness against server CPU cost for a turn-like real-time card game — 50 ms latency is imperceptible to players. Each tick is the atomic unit of match progression.Client-Side Input Path
Server-Side Tick
Each 20 Hz tick on the Nakama match handler:- Collect all client messages received since the last tick.
- Sort by server-received timestamp (earliest first).
- Validate and resolve claims in order. If a claim references cards that were removed by a prior claim in the same tick, it is automatically invalid.
- Run NoSetCheck if the board was modified. If no Set exists and the deck has cards, deal three more.
- Broadcast a complete
MatchStatesnapshot to all connected clients.
Network Message Format
Every message between client and server uses a{type, seq, data} JSON envelope:
seq field allows the client to detect and discard out-of-order messages. The client always applies the latest seq it receives; it never applies an older snapshot on top of a newer one.
Phase 4 — Simultaneous Claim Resolution
Simultaneous claims are the most nuanced part of the lifecycle. Here is the exact resolution algorithm: Exact-millisecond ties (two messages with identical server-received timestamps) are broken deterministically by lower session ID. This is documented and tested — it is not arbitrary behaviour. The client must not assume its claim succeeded based on its own validation. After callingSendClaim, the OnlineMatchController puts the UI into a “claiming…” visual state and waits for the server’s match_state response. If the cards are gone by the time the server processes the claim, the response will carry no score change and potentially a penalty — the UI must handle this gracefully.
Phase 5 — Match End
The match ends when the deck is empty and there is no valid Set remaining on the board. The server detects this after each NoSetCheck during tick processing.- Server broadcasts a
MatchEndedEventto all clients. - Server calculates the winner: most Sets claimed. Tiebreaker: fewest invalid claim attempts.
- For Ranked matches, the server updates each player’s MMR. The delta is included in the post-match payload.
- All clients transition to the Post-Match Results screen, showing final scores, MMR delta (Ranked), and the share button.
Forfeit and Disconnect
If a player disconnects, the server starts a 30-second reconnect window (see Sync & Reconnect for the full reconnect flow). If the window expires without the player rejoining, they forfeit the match. Their Sets are retained in the final score, but they cannot win.Latency Thresholds
These thresholds are Hard Boundaries from the project specification. They must not be raised without a formal change request.
Implementation Checklist
Use this checklist when implementing each phase:- Matchmaking flow triggers before the GameBoard scene loads — the scene must not appear until
ConnectAsyncsucceeds and the initialMatchStateis received - Initial board state is received from the server before player input is enabled
-
OnlineMatchControllersubscribes toIMultiplayerService.Messagesand translates eachServerMessageinto anIGameCommandforGameSession -
SendClaimimmediately puts the UI into a “claiming…” visual state — do not play a success animation before receiving the server’s confirmation -
GameSession.ApplyServerState()updates all state atomically — no partial updates mid-frame - The “cards already gone” case (second simultaneous claimant) is handled: the UI shows the claim was invalid, not stuck in “claiming…”
-
seqfield is tracked and out-of-order messages are discarded - Post-match screen shows MMR delta for Ranked matches
- Disconnect detection triggers the 30-second countdown overlay, not a silent spinner
Common Mistakes
Related Pages
Authority Model
Why the server validates every Set, and the interface contracts that enforce it.
Sync & Reconnect
The 20 Hz tick model, full-state broadcasts, and the 30-second reconnect window.
Anti-Cheat
Rate limiting, server-side data ownership, and why the client cannot influence outcomes.
Session Lifecycle
How GameSession manages state transitions across all three game modes.