Sessions
What a player goes through from the main menu to a game and back, mode by mode: a single player's game (solo), a mission played together (co-op), and one played against each other (versus). Each diagram is the real flow of the web client's pages (apps/client/src/ui: GamePage.tsx, LobbyPage.tsx, run-view.ts, the page header's Back, nav.ts), the API's lobbies (apps/api/src/lobbies.ts, lobby-routes.ts) and runs (runs.ts), and a run's room on the game server (apps/server/src/run-host.ts).
The path is the same in every mode: the main menu, the page of games or the creation page, a game's page, then a room of one's own, a run of one, or a lobby and its runs, the results and the vote, and back. Nothing dead-ends: every state has a way out, every page a Back to its logical parent that never needs the browser's history (the header of every game page, Esc too), and every failure (the city busy, a kick, a lobby closed, a dropped seat, the API restarted, a player who never came) lands somewhere that says what happened and offers the next step.
Every state in the diagrams is a row of the table under them, with the end-to-end tests that prove it (tests/e2e/TEST-PLAN.md, whose rows name the states). A unit test (apps/client/src/ui/sessions.test.ts) parses these diagrams and fails when a state has no way out, has no row here, or names a test the plan does not have.
Solo
A single player's game (rules.mode: "solo") plays in a room of the player's own; a solo mission of any game in a run of one. Back from a run of one, the game's page shows what it paid.
Co-op
A co-op mission never starts in a level's room: its Play opens a lobby (the player its host, their party with them), Quick play and the Play together screen find one, and its runs play in rooms of their own for the lobby's roster.
Versus
The same lobby, its teams balanced by the players' hidden skill; a run ends when a team wins, the clock runs out, or every other team is left empty (a forfeit).
The states
| State | What the player sees | Its ways out | E2E |
|---|---|---|---|
Menu | The main menu over the burning car, the account in its corner | Community, Create, Play online, Quit | E2E-439, E2E-780, E2E-784 |
Browse | The page of games (?browse), its header's brand and Back to the menu | A game's card, Play together, Back | E2E-170, E2E-780 |
Create | The creation page (?create, one creation &job=) | Open in the studio, Publish, Back to the menu | E2E-714, E2E-780 |
Studio | The studio on a draft (?studio=<draft>) | Back to its creation (&job=), else to Your games; Publish | E2E-686, E2E-762, E2E-780 |
GamePage | A game's page (?game=<id>), what a run paid after one (&run=) | Play, a mission's Play, Join, Back to the page of games | E2E-172, E2E-780 |
Refused | The game's page saying why Play was refused, Rejoin for a seat still held, the error with Try again | Rejoin, Try again, Back | E2E-744, E2E-742 |
SoloRoom | A single player's game in a room of the player's own: nobody else joins | The pause menu's Quit to the main menu | E2E-742, E2E-442 |
Together | The Play together screen (?together): Quick play, the playlists, the public lobbies, the invitations | A lobby, Back to the page of games | E2E-660, E2E-683, E2E-780 |
LobbyGathering | A lobby gathering (data-state="gathering"): the mission's card, the teams' slots | Ready check, Start with AI players, Leave; told when gone | E2E-658, E2E-659, E2E-669 |
LobbyReady | The ready check (data-state="ready"): every team at its minimum, Ready and the host's Start | The countdown, back to gathering when a member leaves | E2E-658, E2E-659 |
StartWithAI | Under the minimum for 20 s: the host's Start with AI players enabled | AI stand-ins and the countdown, a player joining | E2E-740, E2E-741 |
LobbyCountdown | The countdown (#lobby-countdown), the run's room warming | The run, the city busy, back to gathering | E2E-658, E2E-649 |
CityBusy | The countdown waiting for a game server (#lobby-notice, busy) | The run once a server takes it, gathering again, Leave | E2E-786 |
RunGathering | A run's room gathering its seats (__poc().run.phase: gathering) | The countdown once every seat came, or 20 s after the first | E2E-649, E2E-787 |
RunCountdown | The run's countdown, every car held | Live | E2E-649 |
RunLive | The run under way: the mission's objectives, the teams' scores | Its result, Leave the run, a dropped connection | E2E-649, E2E-653, E2E-680 |
Dropped | A seat whose connection was lost: its minute (60 s) to come back | Back into the seat, or the seat given up (filled under always) | E2E-651, E2E-680, E2E-681 |
RunEnded | The run's results in its room (#run-results: complete, failed, a team's win) | Back to the lobby, or to the game's page for a run of one | E2E-650, E2E-652, E2E-655 |
LobbyRunning | The lobby while its run plays (data-state="running"), the way back into the seat | Rejoin, the run's result | E2E-743 |
LobbyResults | The lobby's results and the vote (data-state="results") | Replay, the next mission, Leave | E2E-661, E2E-662, E2E-663 |
LobbyGone | The lobby gone for the reader (#lobby-gone: kicked, closed, the API restarted), its header's Back | Back to its game (opened from it) or to Play together | E2E-659, E2E-785 |
The failure edges
| What happens | Where it lands | E2E |
|---|---|---|
| The city busy | The countdown says so and asks again every 5 s for two minutes, then gathers again: the lobby is never lost (CityBusy) | E2E-786 |
| Kicked | The page says the host removed them, its buttons gone, Back to the game or Play together (LobbyGone) | E2E-659 |
| The lobby closed | The same page, closed (LobbyGone) | E2E-785 |
| A dropped seat | Its player has 60 s to come back to the same seat (Dropped); past it the seat is given up, filled under always, a team left empty forfeits | E2E-651, E2E-680 |
| The API restarted | Lobbies live in the API's memory: the page finds its lobby gone (LobbyGone, closed), the session kept, and the game's page opens another | E2E-785 |
| A player who never came | The run starts 20 s after the first seat came, the no-show's seat gone for good; a room nobody came to closes 60 s after it was ready | E2E-787 |
| Seated in another run | The game's page offers Rejoin (Refused) | E2E-744 |