feat(lobby): enter the game immediately and wait for the opponent inside it
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 8s
CI / integration (pull_request) Successful in 14s
CI / ui (pull_request) Successful in 45s
CI / gate (pull_request) Successful in 1s
CI / deploy (pull_request) Successful in 1m4s
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 8s
CI / integration (pull_request) Successful in 14s
CI / ui (pull_request) Successful in 45s
CI / gate (pull_request) Successful in 1s
CI / deploy (pull_request) Successful in 1m4s
Quick auto-match no longer waits on a separate screen: Enqueue opens a real game seating the caller with an empty opponent seat (new game status 'open') and the player enters it at once. A second human searching the same variant+rule joins that open game; otherwise a background reaper seats a robot after a 90s + random 0-90s wait, pushing a new in-app opponent_joined event that fills the opponent card and re-enables resign and chat in place. Matchmaking state is now the open games in the database (the in-memory pool, lobby.poll and lobby.cancel are gone), serialised by a per-bucket advisory lock. While a game is open the starter may move on their turn, but resign, chat and nudge are refused; the lobby and opponent card show "searching for opponent". Schema edited in the baseline (no prod data): 'open' status, nullable game_players.account_id for the empty seat, and a games.open_deadline_at stamp; jet code regenerated.
This commit is contained in:
+11
-9
@@ -28,9 +28,10 @@ export, per-account statistics on finish, and a background turn-timeout sweeper
|
||||
that auto-resigns overdue turns (honouring each player's daily away window). Like
|
||||
the engine it is a service/store layer; the HTTP surface lives in the `gateway`.
|
||||
|
||||
The lobby and social fabric. `internal/lobby` holds an in-memory
|
||||
matchmaking pool (FIFO per variant and per-turn word rule, pairs two humans into an
|
||||
auto-match) and
|
||||
The lobby and social fabric. `internal/lobby` runs **auto-match** — `Enqueue` opens a
|
||||
real game seating the caller with an **empty opponent seat** (status `open`) or, when
|
||||
another player already waits for the same variant and per-turn word rule, seats the
|
||||
caller into that open game and starts it — and
|
||||
friend-game invitations (invite → accept, starting a 2–4 player game once every
|
||||
invitee accepts). `internal/social` owns the friend graph (request/accept),
|
||||
per-user blocks, and per-game chat with nudges folded in as a message kind; chat
|
||||
@@ -52,16 +53,17 @@ robot's moves through the public game API as an ordinary seated player (so only
|
||||
win (≈ 40%), targets a small score margin, and times its moves with a move-number-aware
|
||||
right-skewed delay (quick openings, long endgames), a night-sleep window anchored to the opponent's timezone, and nudge
|
||||
behaviour — all derived deterministically from the game seed, so it keeps no extra
|
||||
state. The matchmaker now substitutes a pooled robot (matching the game's language) after a 10-second wait and
|
||||
exposes `Poll` so a waiting player can collect the started game — it is the **stream-down
|
||||
fallback**: while the client is streaming, the live match-found push (enriched with the recipient's
|
||||
initial game state) drives it instead.
|
||||
state. A background **reaper** seats a pooled robot (matching the game's language) in any open
|
||||
game whose wait window — a fixed **90 s** plus a random **0–90 s** (so **90–180 s**) — has
|
||||
elapsed, and the waiting starter is told an opponent took the seat by an in-app
|
||||
**opponent_joined** push (carrying their refreshed game state) that fills the opponent card and
|
||||
re-enables resign and chat in place.
|
||||
|
||||
The backend opens to the edge. The route groups gain their first
|
||||
handlers (`internal/server/handlers_*.go`): gateway-only session endpoints under
|
||||
`/api/v1/internal` (Telegram/guest/email login → mint, resolve, revoke) and a
|
||||
slice of authenticated `/api/v1/user` operations (profile, submit play, game
|
||||
state, lobby enqueue/poll, chat). The social/account/history operations under
|
||||
state, lobby enqueue, chat). The social/account/history operations under
|
||||
`/api/v1/user`: `friends/*` (request/respond/cancel/unfriend,
|
||||
list/incoming, the one-time `code` issue/redeem), `blocks/*`, `invitations/*`
|
||||
(create/accept/decline/cancel/list), `PUT profile`, `email/{request,confirm}`,
|
||||
@@ -126,7 +128,7 @@ internal/server/ # gin engine, route groups, X-User-ID middleware, probes
|
||||
internal/engine/ # in-process scrabble-solver bridge: registry, bag, Game, replay
|
||||
internal/game/ # game domain: lifecycle, journal+cache, hint, word-check, GCG, sweeper
|
||||
internal/social/ # friend graph, per-user blocks, per-game chat + nudge, content filter
|
||||
internal/lobby/ # in-memory matchmaking pool (+ robot substitution) + friend-game invitations
|
||||
internal/lobby/ # auto-match (DB-backed open games + robot substitution) + friend-game invitations
|
||||
internal/robot/ # human-like robot opponent: account pool, seed-derived strategy, move driver
|
||||
internal/adminconsole/ # server-rendered admin console (Go templates + embedded CSS, view models), served at /_gm
|
||||
internal/connector/ # backend gRPC client to the Telegram connector (operator broadcasts)
|
||||
|
||||
Reference in New Issue
Block a user