Phase 2 (local LAN) — server runs on tsx (0.0.0.0:8000), two clients
in the same room sync state via SocketIO. Tested end-to-end with 2
browser tabs.
server:
- src/index.ts: read ALLOWED_ORIGINS env, register all 3 games
(War, Mill, DragTest), add /healthz endpoint, SIGTERM handler
- package.json: 'start' uses 'node --import tsx' for prod-like run
- Dockerfile: multi-stage build (pnpm install → tsc → pnpm deploy prod
deps → alpine runtime, non-root, healthcheck)
- docker-compose.yml at repo root for local stack
- Wars.ts: add turn: { activePlayers: ActivePlayers.ALL } so P1 can
call collect (SocketIO transport rejects moves from non-currentPlayer)
web/App.tsx:
- Add mode toggle: 本地 / 联机
- OnlineConfigBar: server URL (default http://localhost:8000),
matchID, playerID, credentials; persisted to localStorage
- CardOnlineView / MillOnlineView: SocketIO transport with config
server, no debug panel
- OnlineClient rendered with matchID + playerID + credentials
Verified with 2 Playwright tabs:
- Tab 0 (player 0, secret p0) clicks 翻牌: 26→25 decks, 1 card each pile
- Tab 1 (player 1, secret p1) sees identical state
- Tab 1 clicks 收牌: 7♣ wins, p0-deck 25→27, piles empty
- Tab 0 sees the exact same final state
148 B
148 B