diff --git a/AGENTS.md b/AGENTS.md index 59a1d92..9e6717b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,7 +21,8 @@ Online Mancala (the Turkish *Mangala* rules) with a web app, an Android/iOS app | `docs/specs/` | Specs: `requirements.md`, `design.md`, `tasks.md` | | `TODO.md` | Open work, known bugs and deployment steps; read it before starting | -Specs that exist: `keycloak-auth` (login, guests, tokens) and `game-history` (saved games, live games, replay). +Specs that exist: `keycloak-auth` (login, guests, tokens), `game-history` (saved games, live games, replay) and +`game-clock` (5 minutes per player, ending games on time). ## Running locally @@ -66,6 +67,8 @@ There is no automated end-to-end suite. Changes to the game flow were checked by **Packages and builds** - The apps use `packages/core` and `packages/mancala.js` through `link:` dependencies, i.e. their built `dist`. After changing a package, run `yarn build` in it (or `yarn dev` to rebuild on change), or the apps use stale code. + Running dev servers do not notice a rebuilt package: restart the backend, and restart Parcel after deleting + `apps/frontend/.parcel-cache`. - Backend and frontend Docker images are built from the repo root: `docker build -f apps/backend/Dockerfile .`. - The mobile app uses `RTMTWS` from core (its own `src/rtmt` copy is unused), so changing the core WebSocket client changes mobile too. Metro needs `watchFolders` and `nodeModulesPaths` (already in `metro.config.js`). @@ -76,6 +79,8 @@ There is no automated end-to-end suite. Changes to the game flow were checked by - Live games: memory is the source of truth, Valkey is a write-through copy restored on startup; the backend assumes a single instance. Finished games are written to Postgres once, with retry when it is down. - Postgres or Valkey being down must not stop games; reads fall back, writes are retried. +- The game clock is stored as each player's time at the start of the turn plus `turnStartedAt`; compute the time + left with `getClockTimes`, never store a ticking value. `GAME_CLOCK_MS=40000` makes games short for manual tests. - `RTMT.listenOnClientConnectionChange` adds a listener (several are allowed). - Jest 27 lacks Node's web globals that testcontainers needs; `tests/support/NodeEnvironment.js` copies them in. - A Valkey client pointed at a dead port keeps Jest alive for `disconnectTimeout`; tests pass a short one. diff --git a/TODO.md b/TODO.md index 2688f21..f93e679 100644 --- a/TODO.md +++ b/TODO.md @@ -44,6 +44,14 @@ The web app has game history (see `docs/specs/game-history`); the mobile app nee before running more than one - [ ] If Valkey is unreachable when the backend starts, live games are not restored until the next restart +## Game clock + +See `docs/specs/game-clock`. + +- [ ] Other time controls or time added per move; a long move animates on the next player's time +- [ ] Clocks in the home page's live game previews and in the ongoing-game banner +- [ ] Try the mobile clocks on a device; only the type check and the bundle were run + ## Deployment - [ ] Build the backend and frontend images from the repo root in CI: @@ -52,6 +60,8 @@ The web app has game history (see `docs/specs/game-history`); the mobile app nee - [ ] Deploy Keycloak (`apps/.env` with `KEYCLOAK_DB_PASSWORD`, `KEYCLOAK_ADMIN_PASSWORD`, `KEYCLOAK_HOSTNAME=https://auth.playmancala.app`) and reload nginx with the new `auth.playmancala.app` block - [ ] Deploy the backend before the web app; the new web app needs token auth on the server +- [ ] Release the mobile update with the game clock before or with the backend: older app versions do not show the + clock and do not handle `on_game_end`, so a game they lose on time does not end for them - [ ] Back up the Postgres volume (Keycloak and saved games), e.g. a daily `pg_dumpall` - [ ] Set `MANCALA_DB_PASSWORD` and `POSTGRES_PASSWORD` in `apps/.env` on the server; the `postgres` volume replaces `keycloak-db`, so Keycloak starts with a fresh database there diff --git a/apps/backend/README.md b/apps/backend/README.md index 970153a..9950c19 100644 --- a/apps/backend/README.md +++ b/apps/backend/README.md @@ -61,6 +61,17 @@ backend starts, so a restart or deploy does not end them. The design is in `docs The backend keeps running when either is down: games are still played, finished games are saved once Postgres is back, and lists are served without the missing data. +## Game clock + +Each player has a clock for the whole game; only the clock of the player whose turn it is runs, also while they are +offline. A move that arrives after the mover's time has run out is refused, and `GameClockScheduler` ends games whose +running clock reaches zero once a second, so the opponent wins on time even if nobody is connected. Time while the +backend is down is not charged. The design is in `docs/specs/game-clock`. + +| Variable | Default | Purpose | +|---|---|---| +| `GAME_CLOCK_MS` | `300000` | Time each player has for a game; a short value such as `40000` helps when testing by hand | + | Route | Auth | Response | |---|---|---| | `GET /game/:id` | – | A live game, or `{ status: "finished", record }` | diff --git a/docs/specs/game-clock/requirements.md b/docs/specs/game-clock/requirements.md index 2937bd2..8829ad3 100644 --- a/docs/specs/game-clock/requirements.md +++ b/docs/specs/game-clock/requirements.md @@ -1,6 +1,6 @@ # Game clock: requirements -Status: proposed. +Status: implemented on branch `feature/game-clock` (based on `feature/game-history`). Scope: backend, web app and mobile app. ## Goals diff --git a/docs/specs/game-clock/tasks.md b/docs/specs/game-clock/tasks.md index 793252e..a388a6a 100644 --- a/docs/specs/game-clock/tasks.md +++ b/docs/specs/game-clock/tasks.md @@ -17,5 +17,5 @@ Work through the tasks in order; each ends with its tests passing and a commit. `UserConnectionInfo.offlineSince` and `OpponentOfflineCountdown` from core (the web app uses them until then). - [x] 6. **Web app, replay**: clocks in `ReplayView` from `getClockAtMove`. - [x] 7. **Mobile app**: clocks in `GameScreen`; type check and bundle. -- [ ] 8. **End to end and docs**: Chrome tests from `design.md`; README, `AGENTS.md` and `TODO.md` (release the mobile +- [x] 8. **End to end and docs**: Chrome tests from `design.md`; README, `AGENTS.md` and `TODO.md` (release the mobile update before or with the backend; the out of scope items); mark `game-history` R3.3 and R3.4 as replaced. diff --git a/docs/specs/game-history/requirements.md b/docs/specs/game-history/requirements.md index f6f4462..a05f3d8 100644 --- a/docs/specs/game-history/requirements.md +++ b/docs/specs/game-history/requirements.md @@ -38,7 +38,10 @@ from the home page, and step through the moves of any game, finished or in progr - R3.2 **Left**: a player who leaves the game loses; the other player wins. - R3.3 **Timeout**: if the player whose turn it is has been offline for 5 minutes while the opponent is online, the opponent wins. While the opponent is offline on their turn, the waiting player sees a countdown. + *Replaced by the game clock (`docs/specs/game-clock`): "timeout" now means the player to move ran out of time.* - R3.4 **Inactive**: if both players have been offline for 30 minutes, the game ends without a winner. + *Replaced by the game clock, which keeps running while players are offline; saved games may still have this + reason.* ### R4. Home page: live and popular games - R4.1 The home page shows up to 5 live games, most spectated first, each as a preview of the real board with the