diff --git a/docs/jackbox-ecast-api.md b/docs/jackbox-ecast-api.md index 24e66c7..7f25c7b 100644 --- a/docs/jackbox-ecast-api.md +++ b/docs/jackbox-ecast-api.md @@ -158,11 +158,13 @@ Room information with audience data and join role. Uses a different response for ### GET /api/v2/rooms/{code}/connections -Total allocated slot count. Includes all connection types (host, players, audience, shards). +Occupied slot count. Includes host (1) + all player slots + audience + shards. **Response:** `{"ok": true, "body": {"connections": 3}}` -> **Important:** This counts allocated **slots**, not currently-active WebSocket connections. Jackbox holds player slots open indefinitely for reconnection — there is no concept of "leaving" a game. A player who closes their browser tab still occupies a slot. The count only decreases if the room itself is destroyed. Therefore, `connections - 1` gives the number of players who have **ever joined** the room, not the number currently online. +Player count = `connections - 1` (subtract the host). Since Jackbox holds player slots for reconnection (a disconnected player's slot is reserved and unavailable to new players), this directly reflects how many of the room's `maxPlayers` slots are taken. Available slots = `maxPlayers - (connections - 1)`. + +> **Note:** If audience members are connected, they are included in the count. Use `room/get-audience` or the `/info` endpoint to get the audience count separately if needed. ### POST /api/v2/rooms @@ -657,18 +659,20 @@ In both cases, the server responds with `client/welcome` containing: - Same `id` and `secret` as the original session - Full current state of all entities (complete snapshot, not deltas) -### Disconnection (or lack thereof) +### Disconnection and Slot Persistence -**Jackbox has no concept of "leaving" or "disconnecting."** When a player's WebSocket closes (gracefully or otherwise), their player slot is held open indefinitely for reconnection. From the game's perspective, the player is still "in the game" — just temporarily unreachable. +**Jackbox has no concept of "leaving."** When a player's WebSocket closes (gracefully or otherwise), their player slot is held open for reconnection. From the game's perspective, the player is still "in the game" and can return at any time. This means a disconnected player's slot is **not** available to a new player — it remains reserved. Key behaviors: - The `connections` REST endpoint count does **not** decrease when a player's WebSocket closes. -- The `here` field in `client/welcome` continues to list disconnected players. +- The `here` field in `client/welcome` continues to list all registered players (connected or not). - No `client/disconnected` event is sent to other players. - No `textDescriptions` update is generated for disconnections. - The player's `player:{id}` entity remains intact with its full state. -The only way a player slot is freed is if the room itself is destroyed (host closes the game) or the room times out. +Player slots are freed only when the room itself is destroyed (host closes the game) or the room times out. + +This slot-persistence model makes player counting straightforward: the number of occupied slots (`connections - 1`, or count of `player` roles in `here`) directly represents how many of the room's `maxPlayers` slots are taken, regardless of whether those players currently have active WebSocket connections. > **REST/WebSocket state divergence:** A room can be reported as existing by the REST API (`GET /rooms/{code}` returns 200) while the WebSocket layer considers it ended (error code 2027). Always verify with a WebSocket connection attempt if the REST response seems stale. @@ -678,40 +682,29 @@ The only way a player slot is freed is if the room itself is destroyed (host clo Answers to common questions about managing players and rooms. -### How to detect when players join or leave +### How to detect when players join -**Join detection:** - **WebSocket (best):** Watch for `textDescriptions` entity updates with `category: "TEXT_DESCRIPTION_PLAYER_JOINED"` or `"TEXT_DESCRIPTION_PLAYER_JOINED_VIP"`. -- **WebSocket (initial):** The `here` field in `client/welcome` lists all registered players (see caveats below). -- **REST (polling):** `GET /api/v2/rooms/{code}/connections` — count increases when a new player joins. +- **WebSocket (snapshot):** The `here` field in `client/welcome` lists all registered players (includes both active and disconnected — since slots persist, this reflects the true occupied slot count). +- **REST (polling):** `GET /api/v2/rooms/{code}/connections` — count increases when a new player joins. Does not decrease when they disconnect (by design — the slot is reserved). -**Leave detection — the hard problem:** - -Jackbox does not have a concept of "leaving." Player slots are held open indefinitely for reconnection. There is: -- **No** `client/disconnected` event sent to players. -- **No** change in the `connections` REST count when a player's WebSocket closes. -- **No** `textDescriptions` update for disconnections. -- **No** change in the `here` field — disconnected players remain listed. - -In other words, **the ecast API provides no mechanism to detect that a player has disconnected.** The game treats all players as permanently in the room once joined. This is by design — the Jackbox UI has no "leave" button; a player who closes their browser can always reconnect. - -**Possible workarounds:** -- Monitor game-specific player entity state changes (e.g., a drawing game might detect timeouts for players who don't submit). -- The game host (console) may have internal logic to handle absent players, but this is not exposed through the ecast API. +There is no "leave" event. Jackbox holds player slots for reconnection, so a player who closes their browser still occupies a slot and can return. The `client/disconnected` opcode exists in the client code but is not delivered to player connections. ### How to count players -**Counting total player slots (who has ever joined):** +Since Jackbox holds player slots for reconnection, the number of occupied slots is the effective player count — a held slot is unavailable to a new player. -**Method 1 — REST `/connections`:** +**Method 1 — REST `/connections` (recommended for polling):** ``` GET /api/v2/rooms/{code}/connections → {"connections": N} ``` -`N` = host(1) + all player slots ever allocated + audience + shards. This count **does not decrease** when players disconnect. `N - 1` gives the approximate number of player slots allocated. +Player count = `N - 1` (subtract host). If audience members are present, subtract audience count too (get via `room/get-audience` opcode or `/info` endpoint's `numAudience`). -**Method 2 — WebSocket `here` field:** +Available slots = `maxPlayers - (N - 1)` + +**Method 2 — WebSocket `here` field (recommended for WebSocket clients):** The `client/welcome` message includes a `here` object mapping session IDs to roles: @@ -724,11 +717,7 @@ The `client/welcome` message includes a `here` object mapping session IDs to rol } ``` -Count entries where `roles` contains `player`. This includes **both connected and disconnected** players — `here` reflects all allocated slots, not just active WebSocket connections. The `here` field excludes the connecting client itself (you don't see yourself). - -**Counting currently-connected players:** - -There is no reliable API method to distinguish between connected and disconnected players. The `connections` count and `here` field both include disconnected players whose slots are held for reconnection. +Count entries where `roles` contains `player`. The `here` field excludes the connecting client itself (you don't see yourself in the list), so add 1 if your own connection is also a player. **Method 3 — WebSocket `room/get-audience` (audience count only):**