fix(matchmaking): re-enqueue opens a new game, not the caller's own
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 16s
CI / ui (pull_request) Has been skipped
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m8s
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 16s
CI / ui (pull_request) Has been skipped
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m8s
A second "random opponent" enqueue with the same variant and per-turn rule, while the caller's first game was still open (awaiting an opponent), returned that same open game, so a player could never start a fresh random game while one was still searching. Drop the own-open short-circuit (step 1) in store.OpenOrJoin: a re-enqueue now joins another player's open game or opens a fresh one. Accumulation stays bounded by MaxActiveQuickGames, which counts open games. Update the matchmaker/service/store doc comments and ARCHITECTURE.md, and flip the pinning test to assert the new behavior.
This commit is contained in:
@@ -466,8 +466,10 @@ disguised robot stays indistinguishable from a person.
|
||||
caller with an **empty opponent seat** (status `open`, §9), or — when another player
|
||||
is already waiting for the same `variant` and per-turn rule — seats the caller into
|
||||
that open game and starts it; which seat the caller takes is randomised for
|
||||
first-move fairness, and a re-enqueue returns the caller's own still-open game
|
||||
(idempotent). Matchmaking state is therefore the **open games in the database** (not
|
||||
first-move fairness, and a re-enqueue while already waiting opens **another** game
|
||||
(or joins a different player's) rather than returning the caller's own, so choosing
|
||||
"random opponent" again always starts a new search (bounded by the simultaneous
|
||||
quick-game cap, §9). Matchmaking state is therefore the **open games in the database** (not
|
||||
an in-memory pool), so it survives a restart and stays anonymous (no block check);
|
||||
concurrent enqueues for one bucket are serialised by a transaction-scoped advisory
|
||||
lock so two callers pair rather than each opening a game. A background **reaper**
|
||||
|
||||
Reference in New Issue
Block a user