Compare commits
8 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7be7152cc | ||
|
|
fe0315938a | ||
|
|
fbc611c49f | ||
|
|
fef7c82a3e | ||
|
|
2cb16ed5f0 | ||
|
|
1b02f6ce8e | ||
|
|
8d35876838 | ||
|
|
69d4cfde97 |
8 changed files with 372 additions and 156 deletions
|
|
@ -8,9 +8,6 @@ jobs:
|
|||
build-and-deploy:
|
||||
runs-on: docker
|
||||
steps:
|
||||
- name: Install tools
|
||||
run: apt-get update && apt-get install -y docker.io openssh-client
|
||||
|
||||
- name: Checkout code
|
||||
uses: https://data.forgejo.org/actions/checkout@v4
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "tsx watch src/server.ts",
|
||||
"dev": "pnpm --filter shared build && pnpm --filter db build && tsx watch src/server.ts",
|
||||
"build": "tsc",
|
||||
"start": "node dist/src/server.js",
|
||||
"test": "vitest"
|
||||
|
|
|
|||
|
|
@ -1,91 +0,0 @@
|
|||
services:
|
||||
caddy:
|
||||
container_name: lila-caddy
|
||||
image: caddy:2-alpine
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile
|
||||
- caddy_data:/data
|
||||
- caddy_config:/config
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
api:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- lila-network
|
||||
|
||||
api:
|
||||
container_name: lila-api
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./apps/api/Dockerfile
|
||||
target: runner
|
||||
env_file:
|
||||
- .env
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test:
|
||||
["CMD-SHELL", "wget -qO- http://localhost:3000/api/health || exit 1"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
depends_on:
|
||||
database:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- lila-network
|
||||
|
||||
web:
|
||||
container_name: lila-web
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./apps/web/Dockerfile
|
||||
target: production
|
||||
args:
|
||||
VITE_API_URL: https://api.lilastudy.com
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- lila-network
|
||||
|
||||
database:
|
||||
container_name: lila-database
|
||||
image: postgres:18.3-alpine3.23
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- PGDATA=/var/lib/postgresql/data
|
||||
volumes:
|
||||
- lila-db:/var/lib/postgresql/data
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
networks:
|
||||
- lila-network
|
||||
|
||||
forgejo:
|
||||
container_name: lila-forgejo
|
||||
image: codeberg.org/forgejo/forgejo:11
|
||||
volumes:
|
||||
- forgejo-data:/data
|
||||
environment:
|
||||
- USER_UID=1000
|
||||
- USER_GID=1000
|
||||
ports:
|
||||
- "2222:22"
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- lila-network
|
||||
|
||||
networks:
|
||||
lila-network:
|
||||
|
||||
volumes:
|
||||
lila-db:
|
||||
caddy_data:
|
||||
caddy_config:
|
||||
forgejo-data:
|
||||
|
|
@ -42,11 +42,12 @@ services:
|
|||
volumes:
|
||||
- ./apps/api:/app/apps/api # Hot reload API code
|
||||
- ./packages/shared:/app/packages/shared # Hot reload shared
|
||||
- ./packages/db:/app/packages/db
|
||||
- /app/node_modules
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test:
|
||||
["CMD-SHELL", "wget -qO- http://localhost:3000/api/health || exit 1"]
|
||||
["CMD-SHELL", "wget -qO- http://localhost:3000/api/v1/health || exit 1"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
|
|
@ -66,6 +67,7 @@ services:
|
|||
- "5173:5173"
|
||||
volumes:
|
||||
- ./apps/web:/app/apps/web # Hot reload: local edits reflect immediately
|
||||
- ./packages/shared:/app/packages/shared
|
||||
- /app/node_modules # Protect container's node_modules from being overwritten
|
||||
environment:
|
||||
- VITE_API_URL=http://localhost:3000
|
||||
|
|
|
|||
|
|
@ -225,59 +225,9 @@ Host git.lilastudy.com
|
|||
|
||||
This allows standard git commands without specifying the port.
|
||||
|
||||
## CI/CD Pipeline
|
||||
|
||||
Automated build and deploy via Forgejo Actions. On every push to `main`, the pipeline builds ARM64 images natively on the VPS, pushes them to the Forgejo registry, and restarts the app containers.
|
||||
|
||||
### Components
|
||||
|
||||
- **Forgejo Actions** — enabled by default, workflow files in `.forgejo/workflows/`
|
||||
- **Forgejo Runner** — runs as a container (`lila-ci-runner`) on the VPS, uses the host's Docker socket to build images natively on ARM64
|
||||
- **Workflow file** — `.forgejo/workflows/deploy.yml`
|
||||
|
||||
### Pipeline Steps
|
||||
|
||||
1. Install Docker CLI and SSH client in the job container
|
||||
2. Checkout the repository
|
||||
3. Login to the Forgejo container registry
|
||||
4. Build API image (target: `runner`)
|
||||
5. Build Web image (target: `production`, with `VITE_API_URL` baked in)
|
||||
6. Push both images to `git.lilastudy.com`
|
||||
7. SSH into the VPS, pull new images, restart `api` and `web` containers, prune old images
|
||||
|
||||
### Secrets (stored in Forgejo repo settings → Actions → Secrets)
|
||||
|
||||
| Secret | Value |
|
||||
|---|---|
|
||||
| REGISTRY_USER | Forgejo username |
|
||||
| REGISTRY_PASSWORD | Forgejo password |
|
||||
| SSH_PRIVATE_KEY | Contents of `~/.ssh/ci-runner` on the VPS |
|
||||
| SSH_HOST | VPS IP address |
|
||||
| SSH_USER | `lila` |
|
||||
|
||||
### Runner Configuration
|
||||
|
||||
The runner config is at `/data/config.yml` inside the `lila-ci-runner` container. Key settings:
|
||||
|
||||
- `docker_host: "automount"` — mounts the host Docker socket into job containers
|
||||
- `valid_volumes: ["/var/run/docker.sock"]` — allows the socket mount
|
||||
- `privileged: true` — required for Docker access from job containers
|
||||
- `options: "--group-add 989"` — adds the host's docker group (GID 989) to job containers
|
||||
|
||||
The runner command must explicitly reference the config file:
|
||||
|
||||
```yaml
|
||||
command: '/bin/sh -c "sleep 5; forgejo-runner -c /data/config.yml daemon"'
|
||||
```
|
||||
|
||||
### Deploy Cycle
|
||||
|
||||
Push to main → pipeline runs automatically (~2-5 min) → app is updated. No manual steps required.
|
||||
|
||||
To manually trigger a re-run: go to the repo's Actions tab, click on the latest run, and use the re-run button.
|
||||
|
||||
## Known Issues and Future Work
|
||||
|
||||
- **CI/CD**: Currently manual build-push-pull cycle. Plan: Forgejo Actions with a runner on the VPS building ARM images natively (eliminates QEMU cross-compilation)
|
||||
- **Backups**: Offsite backup storage (Hetzner Object Storage or similar) should be added
|
||||
- **Valkey**: Not in the production stack yet. Will be added when multiplayer requires session/room state
|
||||
- **Monitoring/logging**: No centralized logging or uptime monitoring configured
|
||||
|
|
|
|||
83
documentation/game_modes.md
Normal file
83
documentation/game_modes.md
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
# Game Modes
|
||||
|
||||
This document describes the planned game modes for lila. Each mode uses the same lobby system and vocabulary data but differs in how answers are submitted, scored, and how a winner is determined.
|
||||
|
||||
The first multiplayer mode to implement is TBD. The lobby infrastructure (create, join, WebSocket connection) is mode-agnostic — adding a new mode means adding new game logic, not changing the lobby.
|
||||
|
||||
---
|
||||
|
||||
## TV Quiz Show
|
||||
|
||||
**Type:** Multiplayer
|
||||
**Answer model:** Buzzer — first to press gets to answer
|
||||
**Rounds:** Fixed (e.g. 10)
|
||||
|
||||
A question appears for all players. The first player to buzz in gets to answer. If correct, they score a point. If wrong, other players may get a chance to answer (TBD: whether the question passes to the next buzzer or the round ends). The host or a timer controls the pace.
|
||||
|
||||
Key difference from other modes: only one player answers per question. Speed of reaction matters as much as knowledge.
|
||||
|
||||
---
|
||||
|
||||
## Race to the Top
|
||||
|
||||
**Type:** Multiplayer
|
||||
**Answer model:** Simultaneous — all players answer independently
|
||||
**Rounds:** None — play until target score reached
|
||||
|
||||
All players see the same question and answer independently. No fixed round count. The first player to reach a target number of correct answers wins (e.g. 20). Fast-paced and competitive.
|
||||
|
||||
Open questions: what happens if two players hit the target on the same question? Tiebreaker by speed? Shared win?
|
||||
|
||||
---
|
||||
|
||||
## Chain Link
|
||||
|
||||
**Type:** Multiplayer
|
||||
**Answer model:** Turn-based — one player at a time, in rotation
|
||||
**Rounds:** None — play until a player fails
|
||||
|
||||
Players answer in a fixed rotation: Player 1, Player 2, Player 3, then back to Player 1. Each player gets one question per turn. The game continues until a player answers incorrectly — that player is out (or the game ends). Last correct answerer wins, or the game simply ends on the first wrong answer.
|
||||
|
||||
Key difference from other modes: turn-based, not simultaneous. Pressure builds as you wait for your turn.
|
||||
|
||||
Open questions: does the player who answers wrong lose, or does the game just end? If the game continues, does it become elimination?
|
||||
|
||||
---
|
||||
|
||||
## Elimination Round
|
||||
|
||||
**Type:** Multiplayer
|
||||
**Answer model:** Simultaneous — all players answer independently
|
||||
**Rounds:** Continue until one player remains
|
||||
|
||||
All players see the same question and answer simultaneously. Players who answer incorrectly are eliminated. Rounds continue until only one player is left standing.
|
||||
|
||||
Open questions: what if everyone gets it wrong in the same round? Reset that round? Eliminate nobody? What if it comes down to two players and both get it wrong repeatedly?
|
||||
|
||||
---
|
||||
|
||||
## Cooperative Challenge
|
||||
|
||||
**Type:** Multiplayer
|
||||
**Answer model:** TBD
|
||||
**Rounds:** TBD
|
||||
|
||||
Players work together rather than competing. Concept not yet defined. Possible ideas: shared team score with a target, each player contributes answers to a collective pool, or players take turns and the team survives as long as the chain doesn't break.
|
||||
|
||||
---
|
||||
|
||||
## Single Player Extended
|
||||
|
||||
**Type:** Singleplayer
|
||||
**Answer model:** TBD
|
||||
**Rounds:** TBD
|
||||
|
||||
An expanded version of the current singleplayer quiz. Concept not yet defined. Possible ideas: longer sessions with increasing difficulty, mixed POS/language rounds, streak bonuses, progress tracking across sessions, or timed challenge mode.
|
||||
|
||||
---
|
||||
|
||||
## Schema Impact
|
||||
|
||||
The `lobbies` table includes a `game_mode` column (varchar) with values like `tv_quiz`, `race_to_top`, `chain_link`, `elimination`. Mode-specific settings (e.g. target score for Race to the Top) can be stored in a `settings` jsonb column if needed.
|
||||
|
||||
The singleplayer modes (Single Player Extended) don't require a lobby — they extend the existing singleplayer flow.
|
||||
|
|
@ -21,18 +21,13 @@ WARNING! Your credentials are stored unencrypted in '/home/languagedev/.docker/c
|
|||
Configure a credential helper to remove this warning. See
|
||||
https://docs.docker.com/go/credential-store/
|
||||
|
||||
### docker containers on startup?
|
||||
|
||||
laptop: verify if docker containers run on startup (they shouldnt)
|
||||
|
||||
### vps setup
|
||||
|
||||
- monitoring and logging (eg via chrootkit or rkhunter, logwatch/monit => mails daily with summary)
|
||||
- ~~keep the vps clean (e.g. old docker images/containers)~~ ✅ CI/CD pipeline runs `docker image prune -f` after deploy
|
||||
|
||||
### ~~cd/ci pipeline~~ ✅ RESOLVED
|
||||
|
||||
Forgejo Actions with runner on VPS, Forgejo built-in container registry. See `deployment.md`.
|
||||
|
||||
### ~~postgres backups~~ ✅ RESOLVED
|
||||
|
||||
Daily pg_dump cron job, 7-day retention, dev laptop auto-sync via rsync. See `deployment.md`.
|
||||
|
||||
### try now option
|
||||
|
||||
|
|
|
|||
280
scripts/create-issues.sh
Normal file
280
scripts/create-issues.sh
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Forgejo batch issue creator for lila
|
||||
# Usage: FORGEJO_TOKEN=your_token ./create-issues.sh
|
||||
|
||||
FORGEJO_URL="https://git.lilastudy.com"
|
||||
OWNER="forgejo-lila"
|
||||
REPO="lila"
|
||||
TOKEN="${FORGEJO_TOKEN:?Set FORGEJO_TOKEN environment variable}"
|
||||
|
||||
API="${FORGEJO_URL}/api/v1/repos/${OWNER}/${REPO}"
|
||||
|
||||
# Helper: create a label (ignores if already exists)
|
||||
create_label() {
|
||||
local name="$1" color="$2" description="$3"
|
||||
curl -s -X POST "${API}/labels" \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"name\":\"${name}\",\"color\":\"${color}\",\"description\":\"${description}\"}" > /dev/null
|
||||
echo "Label: ${name}"
|
||||
}
|
||||
|
||||
# Helper: create an issue with labels
|
||||
create_issue() {
|
||||
local title="$1" body="$2"
|
||||
shift 2
|
||||
local labels="$*"
|
||||
|
||||
# Build labels JSON array
|
||||
local label_ids=""
|
||||
for label in $labels; do
|
||||
local id
|
||||
id=$(curl -s "${API}/labels" \
|
||||
-H "Authorization: token ${TOKEN}" | \
|
||||
python3 -c "import sys,json; [print(l['id']) for l in json.load(sys.stdin) if l['name']=='${label}']")
|
||||
if [ -n "$label_ids" ]; then
|
||||
label_ids="${label_ids},${id}"
|
||||
else
|
||||
label_ids="${id}"
|
||||
fi
|
||||
done
|
||||
|
||||
curl -s -X POST "${API}/issues" \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"title\":$(echo "$title" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read().strip()))'),\"body\":$(echo "$body" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read().strip()))'),\"labels\":[${label_ids}]}" > /dev/null
|
||||
|
||||
echo "Issue: ${title}"
|
||||
}
|
||||
|
||||
echo "=== Creating labels ==="
|
||||
create_label "feature" "#0075ca" "New user-facing functionality"
|
||||
create_label "infra" "#e4e669" "Infrastructure, deployment, DevOps"
|
||||
create_label "debt" "#d876e3" "Technical cleanup, refactoring"
|
||||
create_label "security" "#b60205" "Security improvements"
|
||||
create_label "ux" "#1d76db" "User experience, accessibility, polish"
|
||||
create_label "multiplayer" "#0e8a16" "Multiplayer lobby and game features"
|
||||
|
||||
echo ""
|
||||
echo "=== Creating issues ==="
|
||||
|
||||
# ── feature ──
|
||||
|
||||
create_issue \
|
||||
"Add guest/try-now option — play without account" \
|
||||
"Allow users to play a quiz without signing in so they can see what the app offers before creating an account. Make auth middleware optional on game routes, add a 'Try without account' button on the login/landing page." \
|
||||
feature
|
||||
|
||||
create_issue \
|
||||
"Add Apple login provider" \
|
||||
"Add Apple as a social login option via Better Auth. Requires Apple Developer account and Sign in with Apple configuration." \
|
||||
feature
|
||||
|
||||
create_issue \
|
||||
"Add email+password login" \
|
||||
"Add traditional email and password authentication as an alternative to social login. Configure via Better Auth." \
|
||||
feature
|
||||
|
||||
create_issue \
|
||||
"User stats endpoint + profile page" \
|
||||
"Add GET /users/me/stats endpoint returning games played, score history, etc. Build a frontend profile page displaying the stats." \
|
||||
feature
|
||||
|
||||
# ── infra ──
|
||||
|
||||
create_issue \
|
||||
"Google OAuth app verification and publishing" \
|
||||
"Currently only test users can log in via Google. Publish the OAuth consent screen so any Google user can sign in. Requires branding verification through Google Cloud Console." \
|
||||
infra
|
||||
|
||||
create_issue \
|
||||
"Set up Docker credential helper on dev laptop" \
|
||||
"Docker credentials are stored unencrypted in ~/.docker/config.json. Set up a credential helper to store them securely. See https://docs.docker.com/go/credential-store/" \
|
||||
infra
|
||||
|
||||
create_issue \
|
||||
"VPS monitoring and logging" \
|
||||
"Set up monitoring and centralized logging on the VPS. Options: chkrootkit/rkhunter for security, logwatch/monit for daily summaries, uptime monitoring for service health." \
|
||||
infra
|
||||
|
||||
create_issue \
|
||||
"Move to offsite backup storage" \
|
||||
"Currently database backups live on the same VPS. Add offsite copies to Hetzner Object Storage or similar S3-compatible service to protect against VPS failure." \
|
||||
infra
|
||||
|
||||
create_issue \
|
||||
"Replace in-memory game session store with Valkey" \
|
||||
"Add Valkey container to the production Docker stack. Implement ValkeyGameSessionStore using the existing GameSessionStore interface. Required before multiplayer." \
|
||||
infra
|
||||
|
||||
create_issue \
|
||||
"Modern env management approach" \
|
||||
"Evaluate replacing .env files with a more robust approach (e.g. dotenvx, infisical, or similar). Current setup works but .env files are error-prone and not versioned." \
|
||||
infra
|
||||
|
||||
create_issue \
|
||||
"Pin dependencies in package.json files" \
|
||||
"Pin all dependency versions in package.json files to exact versions to prevent unexpected updates from breaking builds." \
|
||||
infra
|
||||
|
||||
# ── debt ──
|
||||
|
||||
create_issue \
|
||||
"Rethink organization of datafiles and wordlists" \
|
||||
"The current layout of data-sources/, scripts/datafiles/, scripts/data-sources/, and packages/db/src/data/ is confusing with overlapping content. Consolidate into a clear structure." \
|
||||
debt
|
||||
|
||||
create_issue \
|
||||
"Resolve eslint peer dependency warning" \
|
||||
"eslint-plugin-react-hooks 7.0.1 expects eslint ^3.0.0-^9.0.0 but found 10.0.3. Resolve the peer dependency mismatch." \
|
||||
debt
|
||||
|
||||
# ── security ──
|
||||
|
||||
create_issue \
|
||||
"Rate limiting on API endpoints" \
|
||||
"Add rate limiting to prevent abuse. At minimum: auth endpoints (brute force prevention), game endpoints (spam prevention). Consider express-rate-limit or similar." \
|
||||
security
|
||||
|
||||
# ── ux ──
|
||||
|
||||
create_issue \
|
||||
"404/redirect handling for unknown routes and subdomains" \
|
||||
"Unknown routes return raw errors. Add a catch-all route on the frontend for client-side 404s. Consider Caddy fallback for unrecognized subdomains." \
|
||||
ux
|
||||
|
||||
create_issue \
|
||||
"React error boundaries" \
|
||||
"Add error boundaries to catch and display runtime errors gracefully instead of crashing the entire app." \
|
||||
ux
|
||||
|
||||
create_issue \
|
||||
"Accessibility pass" \
|
||||
"Keyboard navigation for quiz buttons, ARIA labels on interactive elements, focus management during quiz flow." \
|
||||
ux
|
||||
|
||||
create_issue \
|
||||
"Favicon, page titles, Open Graph meta" \
|
||||
"Add favicon, set proper page titles per route, add Open Graph meta tags for link previews when sharing." \
|
||||
ux
|
||||
|
||||
# ── multiplayer ──
|
||||
|
||||
create_issue \
|
||||
"Drizzle schema: lobbies, lobby_players + migration" \
|
||||
"Create lobbies table (id, code, host_user_id, status, is_private, game_mode, settings, created_at) and lobby_players table (lobby_id, user_id, score, joined_at). Run migration. See game-modes.md for game_mode values." \
|
||||
multiplayer
|
||||
|
||||
create_issue \
|
||||
"REST endpoints: POST /lobbies, POST /lobbies/:code/join" \
|
||||
"Create lobby (generates short code, sets host) and join lobby (validates code, adds player, enforces max limit)." \
|
||||
multiplayer
|
||||
|
||||
create_issue \
|
||||
"LobbyService: create lobby, join lobby, enforce player limit" \
|
||||
"Service layer for lobby management. Generate human-readable codes, validate join requests, track lobby state. Public lobbies are browsable, private lobbies require code." \
|
||||
multiplayer
|
||||
|
||||
create_issue \
|
||||
"WebSocket server: attach ws upgrade to Express" \
|
||||
"Attach ws library upgrade handler to the existing Express HTTP server. Handle connection lifecycle." \
|
||||
multiplayer
|
||||
|
||||
create_issue \
|
||||
"WS auth middleware: validate session on upgrade" \
|
||||
"Validate Better Auth session on WebSocket upgrade request. Reject unauthenticated connections." \
|
||||
multiplayer
|
||||
|
||||
create_issue \
|
||||
"WS message router: dispatch by type" \
|
||||
"Route incoming WebSocket messages by their type field to the appropriate handler. Use Zod discriminated union for type safety." \
|
||||
multiplayer
|
||||
|
||||
create_issue \
|
||||
"Lobby join/leave handlers + broadcast lobby state" \
|
||||
"Handle lobby:join and lobby:leave WebSocket events. Broadcast updated player list to all connected players in the lobby." \
|
||||
multiplayer
|
||||
|
||||
create_issue \
|
||||
"Lobby state in Valkey (ephemeral) + PostgreSQL (durable)" \
|
||||
"Store live lobby state (connected players, current question, timer) in Valkey. Store durable records (who played, final scores) in PostgreSQL." \
|
||||
multiplayer
|
||||
|
||||
create_issue \
|
||||
"WS event Zod schemas in packages/shared" \
|
||||
"Define all WebSocket message types as Zod discriminated unions in packages/shared. Covers lobby events (join, leave, start) and game events (question, answer, result, finished)." \
|
||||
multiplayer
|
||||
|
||||
create_issue \
|
||||
"Frontend: lobby browser + create/join lobby" \
|
||||
"Lobby list showing public open lobbies. Create lobby form (game mode, public/private). Join-by-code input for private lobbies." \
|
||||
multiplayer
|
||||
|
||||
create_issue \
|
||||
"Frontend: lobby view (player list, code, start game)" \
|
||||
"Show lobby code, connected players, game mode. Host sees Start Game button. Players see waiting state. Real-time updates via WebSocket." \
|
||||
multiplayer
|
||||
|
||||
create_issue \
|
||||
"Frontend: WS client singleton with reconnect" \
|
||||
"WebSocket client that maintains a single connection, handles reconnection on disconnect, and dispatches incoming messages to the appropriate state handlers." \
|
||||
multiplayer
|
||||
|
||||
create_issue \
|
||||
"GameService: question sequence + server timer" \
|
||||
"Generate question sequence for a lobby game. Enforce per-question timer (e.g. 15s). Timer logic varies by game mode — see game-modes.md." \
|
||||
multiplayer
|
||||
|
||||
create_issue \
|
||||
"lobby:start WS handler — broadcast first question" \
|
||||
"When host starts the game, generate questions, change lobby status to in_progress, broadcast first question to all players." \
|
||||
multiplayer
|
||||
|
||||
create_issue \
|
||||
"game:answer WS handler — collect answers" \
|
||||
"Receive player answers via WebSocket. Track who has answered. Behavior varies by game mode (simultaneous vs turn-based vs buzzer)." \
|
||||
multiplayer
|
||||
|
||||
create_issue \
|
||||
"Answer evaluation + broadcast results" \
|
||||
"On all-answered or timeout: evaluate answers, calculate scores, broadcast game:answer_result to all players. Then send next question or end game." \
|
||||
multiplayer
|
||||
|
||||
create_issue \
|
||||
"Game finished: broadcast results, update DB" \
|
||||
"After final round: broadcast game:finished with final scores and winner. Write game results to PostgreSQL (transactional). Change lobby status to finished." \
|
||||
multiplayer
|
||||
|
||||
create_issue \
|
||||
"Frontend: multiplayer game route" \
|
||||
"Route for active multiplayer games. Receives questions and results via WebSocket. Reuses QuestionCard and OptionButton components." \
|
||||
multiplayer
|
||||
|
||||
create_issue \
|
||||
"Frontend: countdown timer component" \
|
||||
"Visual countdown timer synchronized with server timer. Shows remaining seconds per question." \
|
||||
multiplayer
|
||||
|
||||
create_issue \
|
||||
"Frontend: ScoreBoard component (live per-player scores)" \
|
||||
"Displays live scores for all players during a multiplayer game. Updates in real-time via WebSocket." \
|
||||
multiplayer
|
||||
|
||||
create_issue \
|
||||
"Frontend: GameFinished screen" \
|
||||
"Winner highlight, final scores, play again option. Returns to lobby on play again." \
|
||||
multiplayer
|
||||
|
||||
create_issue \
|
||||
"Multiplayer GameService unit tests" \
|
||||
"Unit tests for round evaluation, scoring, tie-breaking, timeout handling across different game modes." \
|
||||
multiplayer
|
||||
|
||||
create_issue \
|
||||
"Graceful WS reconnect with exponential back-off" \
|
||||
"Handle WebSocket disconnections gracefully. Reconnect with exponential back-off. Restore game state on reconnection if game is still in progress." \
|
||||
multiplayer
|
||||
|
||||
echo ""
|
||||
echo "=== Done ==="
|
||||
Loading…
Add table
Add a link
Reference in a new issue