hoangsonww / hoangsonww/Tic-Tac-Toe-Fullstack-Game

Feature: Live Spectator Mode + Match Replays + Rage-Quit Handling (ELO-safe) + Shareable Highlights

Open
#2 0 comments 0 reactions 1 assignee Claimed by @hoangsonww View on GitHub
bug documentation enhancement good first issue help wanted question
Dominant language
TypeScript
Stars
16
Forks
11
PR merge metrics
No merged PRs in 30d

Description

**Summary**
Introduce a real-time **Spectator Mode** (with anti-ghosting delay), persistent **Match Replays** with timeline scrubber, **rage-quit/AFK handling** that protects ELO, and one-click **shareable highlights** (GIF/WebM export from the replay). This deepens engagement, reduces frustration from quits, and creates reusable content for social sharing.

---

## Why

* **Engagement:** Let users watch top games and learn openings/endgames.
* **Fairness:** Quits/timeouts shouldn’t unfairly punish the active player or inflate ELO.
* **Retention:** Replay library + highlights encourage return visits and sharing.
* **Esports-like feel:** Spectating and history are table-stakes for competitive titles.

---

## Scope (MVP)

### 1) Spectator Mode (with 3–5s anti-ghosting delay)

* Live match directory: “Now Playing” list with player names/ELO and elapsed time.
* Join as **read-only spectator**; cannot send moves/chat.
* Server enforces **delayed broadcast** of board state (configurable, default 3s).
* Private matches: creator can set `visibility = private` to hide from directory.
* Show live spectator count on the game UI.

### 2) Match Replays (persisted)

* Save for all **ranked** matches: ordered move list, timestamps, final state, winner, ELO delta, board size/mode.
* Replay viewer UI: play/pause, next/prev move, scrubber, speed (0.5×/1×/2×), jump to result.
* Metadata: players, ELOs pre/post, duration, createdAt, “favorite” toggle.

### 3) Rage-Quit / AFK Handling (ELO-safe)

* **Grace window** (e.g., 30s) on disconnect; auto-reconnect preserves session.
* If timer expires: adjudicate as a **loss** for quitter in ranked, **no-ELO** in unranked.
* AFK timer per turn (e.g., 45s) with per-match max AFK violations → forfeit.
* Prevent abuse: if both disconnect → result “unresolved” (no ELO change).

### 4) Shareable Highlights (client-side export; MVP)

* From any replay, export a short **GIF/WebM**: final N moves (default 5) + result banner.
* Client-side Canvas capture (no server video pipeline in MVP).
* Provide share URL to hosted replay page.

---

## Data Model

**`Match` (existing) – add fields**

* `visibility: 'public' | 'private'` (default `public`)
* `spectatorsCount: number` (derived/cached)
* `afkViolations: {playerX: number, playerO: number}`
* `disconnectAt?: Date`, `resolvedBy?: 'quit' | 'afk' | 'normal'`

**`Replay` (new collection)**

* `_id`, `matchId`, `boardSize`, `mode` (ranked/unranked/ai/online),
* `players: { x: { id, username, eloBefore, eloAfter }, o: {...} }`,
* `moves: Array<{ index: number; player: 'X' | 'O'; t: number }>` // `index` 0..(boardSize^2 - 1)
* `result: 'X' | 'O' | 'draw'`, `durationMs`, `createdAt`
* `hash?: string` // short content hash for integrity & share URLs

---

## API & Sockets

### REST (Express)

* `GET /api/matches/live` → paginated list of live public matches (players, elo, board, startedAt, spectatorsCount).
* `GET /api/replays/:id` → replay payload (auth required for private if owner).
* `POST /api/replays/:id/highlight` (optional later) → create server-hosted highlight asset (out of MVP).
* Extend existing finish endpoints to set `resolvedBy`, write replay, compute ELO safely when `quit/afk`.

### Socket.io (new events)

* **Spectating**

* `spectate:join` { matchId } → ok or error (private/finished).
* Server emits `spectate:state` (delayed) with board, nextTurn, moveNumber.
* `spectate:leave` auto on disconnect.
* **Presence/Counts**

* Server emits `spectate:count` { matchId, spectatorsCount } on joins/leaves.
* **AFK/Disconnect**

* Server emits `match:warning` { player, reason: 'afk'|'disconnect', timeLeftSec }.
* On forfeit → `match:forfeit` { winner, reason } to players + spectators.

**Anti-ghosting:** server buffers moves and emits to spectators after `SPECTATOR_DELAY_MS` (env).

---

## Frontend (React + MUI)

* **Live tab:** “Watch Live” page, filters (ranked/unranked, board size), search by username.
* **Game page:** spectator mode banner; live count; disable inputs.
* **Replay viewer:** board with move timeline, keyboard shortcuts (←/→), speed control, share button.
* **Export highlight:** client-side Canvas rendering of last N moves → download GIF/WebM (use `MediaRecorder` + canvas or `gif.js`).

---

## ELO & Rules

* Ranked normal finish → compute ELO as today.
* **Quit after grace** → winner gets ELO as if checkmate; quitter loses ELO.
* **AFK forfeit** → same as above.
* Unranked/AI → never change global ELO.
* Store `resolvedBy` to support fair analytics.

---

## Security/Privacy

* Private matches: not listed; spectate denied unless you’re a participant.
* Rate-limit `spectate:join`; cap spectators per match (e.g., 200).
* Replay share URL exposes only match content, not emails/IPs.

---

## Config / Env

```
SPECTATOR_DELAY_MS=3000
DISCONNECT_GRACE_MS=30000
TURN_AFK_MS=45000
MAX_AFK_VIOLATIONS=2
MAX_SPECTATORS_PER_MATCH=200
```

---

## Acceptance Criteria

* Users can browse and spectate **public** live matches with a visible delay.
* Replays are saved for all ranked matches and playable in a dedicated viewer.
* Exporting a short highlight from a replay works offline (client-side).
* Disconnects/AFK are adjudicated per rules; ELO updates only when appropriate.
* Private matches are hidden and not spectatable by others.
* Spectator count updates in real time; no control or info leakage to spectators.

---

## Testing

* **Unit:** ELO calc for normal/quit/afk; AFK/disconnect timers; spectator delay buffer.
* **Integration:** Start match → spectator joins → sees delayed updates; disconnect triggers grace → adjudication.
* **Frontend:** Replay scrubber controls; highlight export; live list pagination.
* **Load:** Simulate 1k spectators across matches; ensure CPU/memory OK and rates limited.

---

## Tasks

* [ ] DB: add `Replay` collection; extend `Match` fields; indexes on `createdAt`, `mode`, `visibility`.
* [ ] Backend: live list endpoint, replay read endpoint, adjudication logic, ELO branching.
* [ ] Socket: implement spectate join/leave/state with delay; presence counts.
* [ ] Frontend: Live directory page; spectator UI; replay viewer; highlight export.
* [ ] AFK/Disconnect timers & server enforcement; configurable via env.
* [ ] Docs: README + Swagger updates + screenshots/gifs.
* [ ] Tests: unit/integration + CI wiring.

---

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.