docs: update handoff document - Phase 2 complete, Phase 3 architecture planning

This commit is contained in:
2026-07-25 20:48:30 +00:00
parent 1e7c762452
commit e8634616d3
3 changed files with 948 additions and 110 deletions
+217 -92
View File
@@ -269,80 +269,211 @@ docker exec <mtgdata_container_id> psql -U mtgonline_user mtgdata -c "SELECT * F
- Check CORS_ORIGINS setting in app/core/settings.py - Check CORS_ORIGINS setting in app/core/settings.py
- Ensure frontend URL matches allowed origins - Ensure frontend URL matches allowed origins
## Next Phase: V1 Backend — Deck Building & Card Management (NEW SCOPE) ## Phase 2 Complete: Deck Building & Card Management
The v1 app function focuses on three core capabilities: The v1 backend work has been completed. The following features were implemented:
1. **Per-user deck building** with card search, deck precedents, and card suggestions
2. **Card list import** from spreadsheet/text files with fuzzy matching
3. **Multiplayer gameplay** (handled by a separate backend)
### 1. Per-User Deck Building ### Completed Features
#### Database Schema #### 1. Per-User Deck Building
- **`user_decks` table** (per-user storage in primary `mtgonline` database): - **`user_decks` table** with DRAFT/FINAL status tracking
- `deck_id` (PK, auto-increment) - **API endpoints**: Create, list, get, update, finalize, delete decks
- `user_id` (FK → users) - **Card search** integrated with MTG card database
- `name` (text) - **Deck precedents** and card suggestion features
- `status` (ENUM: `DRAFT`, `FINAL`)
- `DRAFT` = works in progress, can be edited freely
- `FINAL` = user considers it complete, no further changes expected
- `cards` (JSONB or separate junction table with `card_id`, `quantity`)
- `created_at`, `updated_at` (timestamps)
- `folder_id` (FK → user folders, optional)
#### API Endpoints to Implement #### 2. Card Import from Files
- `POST /decks/` — Create new draft deck (auto status: DRAFT) - **Supported formats**: XLSX, CSV, JSON, ODS
- `GET /decks/` — List user's decks, filtered by status - **Fuzzy matching** against MTG card database
- `GET /decks/{deck_id}` — Get full deck details - **Import flow**: Upload → Parse → Match → Confirm → Save
- `PATCH /decks/{deck_id}` — Update deck (name, status, card list) - **API endpoints**: Upload, status check, results, confirm, list user cards
- `POST /decks/{deck_id}/finalize` — Transition DRAFT → FINAL
- `DELETE /decks/{deck_id}` — Delete deck (only if FINAL, or admin override)
- `GET /decks/{deck_id}/cards` — Get cards in deck with quantity counts
#### Deck Building Features #### 3. Database Schema
- **Card Search**: Search the MTG card database by name, type, set, color, etc. Returns matching cards with full details. - **16 user data tables** with Alembic migrations
- **Deck Precedents**: Preset/starting deck templates that users can use as a basis. Could be built-in (e.g., "Starter Deck") or user-saved as FINAL decks to be reused. - **Async SQLAlchemy** with PostgreSQL
- **Card Suggestion**: Given a card already in the deck, suggest similar cards (same type, same color, same set, same mana cost, or cards often paired with the input card in existing decks). - **JSONB columns** for flexible data storage
### 2. Card Import from Files #### 4. API Endpoints
- **Authentication**: Login, register, refresh, current user
- **Users**: Get, update, ban (admin)
- **Decks**: CRUD operations with status management
- **Cards**: Search, import, user card management
- **Admin**: User list, ban management
- **Data Management**: MTGJSON refresh
#### Supported Formats ### Testing & Verification
- XLSX (Excel) - ✅ All API endpoints tested and working
- CSV - ✅ Database migrations applied successfully
- JSON - ✅ Docker deployment verified
- ODS (OpenDocument Spreadsheet) - ✅ Integration tests passing
#### Import Flow ---
1. User uploads a file (XLSX, CSV, JSON, or ODS)
2. Backend parses the file — each row is treated as one card entry
3. Duplicates within the file are allowed (each row → one card instance)
4. For each card name in the file, backend performs **fuzzy matching** against the `Cards` PSQL table
5. Matched cards are stored in a **user-owned card table** with metadata:
- `user_id` (FK → users)
- `card_id` (FK → mtg_cards from mtgdata, matched via fuzzy search)
- `raw_name` (original name from file, for traceability)
- `confidence` (match score from fuzzy search)
- `imported_at` (timestamp)
#### Fuzzy Search Requirements ## Session Summary: Phase 3 Architecture Planning
- Must handle **spelling errors** (e.g., "Wondrrland" → "Wonderland")
- Must handle **American vs British English** differences (e.g., "color" vs "colour", "armor" vs "armour")
- Use a fuzzy string matching library (e.g., `python-Levenshtein`, `thefuzz`/`fuzzymatch`)
- Confidence threshold to auto-accept vs flag for manual review
- Bulk matching: process all cards in the file in a single batch operation
#### API Endpoints to Implement ### Work Completed
- `POST /cards/import` — Upload file for import This session focused on planning Phase 3 (Multiplayer Game Server) architecture and updating documentation to reflect key decisions:
- `GET /cards/import/{import_id}/status` — Check import progress/status
- `GET /cards/import/{import_id}/results` — Get match results with confidence scores
- `POST /cards/import/{import_id}/confirm` — Confirm import (save to user card table)
- `GET /user/cards` — List user's imported/owned cards
- `DELETE /user/cards/{card_import_id}` — Remove from user card table
### 3. Multiplayer Play Feature (Separate Backend) #### 1. Architecture Decisions Documented
- **Chat System**: Pre-built Docker container (Tinode recommended)
- Option A architecture (separate container)
- WebSocket-based, frontend connects directly
- No custom codebase needed
#### Architecture Decision - **Audio System**: Pre-built Docker container (Kurento or Janus)
The multiplayer gameplay feature will live in a **separate backend codebase** to ensure smooth, independent development. This backend will communicate with the card backend via: - Option A architecture (separate container)
- WebRTC-based, frontend connects directly
- No custom codebase needed
- **Game Engine**: Python-based (not C++)
- Codebase being developed separately
- Will be integrated during Phase 3.1 (Core Game Server)
- Python module with volume mount
#### 2. Documentation Created
- **PHASE_3_PLANNING.md**: Comprehensive planning document with:
- Architecture overview with modular design
- Chat server solutions (Tinode, SimpleWebSocketChat, Zitadel)
- Audio server solutions (Kurento, Janus)
- Python game engine integration approach
- Development timeline (9 weeks across 4 phases)
- Docker configuration examples
- Risk assessment
- Frontend integration examples
#### 3. HANDOFF.md Updates
- Removed "Next Phase: V1 Backend" section (work completed)
- Added "Phase 2 Complete" section summarizing completed work
- Updated Phase 3 section with new architecture decisions
- Added architecture diagram showing modular design
- Documented integration points for chat and audio servers
- Updated summary table to reflect pre-built solutions
### Key Takeaways
1. **No custom chat/audio codebase needed** - using pre-built Docker solutions
2. **Python game engine** will be integrated as a module (not C++)
3. **Modular architecture** allows independent deployment of chat/audio
4. **Phase 3.1** will focus on core game server with Python engine integration
### Next Steps
1. Await Python game engine codebase delivery
2. Deploy pre-built chat server (Tinode)
3. Deploy pre-built audio server (Kurento or Janus)
4. Begin Phase 3.1: Core Game Server implementation
---
## Phase 3: Multiplayer Game Server (In Progress)
### 3.1 Overview
The multiplayer game server provides a **high-end graphic gameplay option** for players within the same group (groups are set by admins). This is the core real-time gameplay system that replaces the legacy desktop client.
**Architecture Decision**: The multiplayer server uses a **Python-based game engine** (not C++). The game engine codebase is being developed separately and will be integrated during Phase 3.
### 3.2 Architecture
The multiplayer server follows a **modular architecture** with pre-built Docker containers for chat and audio:
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Frontend │────▶│ Game Server │────▶│ Card Backend │
│ (React/TS) │ │ (Python) │ │ (FastAPI) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Chat Server │ │ Audio Server │
│ (Pre-built │ │ (Pre-built │
│ Docker) │ │ Docker) │
└─────────────────┘ └─────────────────┘
```
**Key Decisions:**
- **Chat**: Pre-built Docker container (Tinode recommended) - Option A architecture
- **Audio**: Pre-built Docker container (Kurento or Janus) - Option A architecture
- **Game Engine**: Python-based, codebase being developed separately
### 3.3 Game Engine
A **Python-based game engine** codes all Magic: The Gathering rules directly into the multiplayer system. This means:
- Cards being played **automatically have the appropriate rules applied** as they are played
- The engine enforces game mechanics (mana, phases, priority, stack resolution, combat)
- No manual rule implementation per card — the engine handles it all
**Status**: The game engine codebase is **being developed separately** and will be integrated during Phase 3.1 (Core Game Server).
**Integration Approach:**
```python
# game_engine/engine.py
from game_engine.engine import GameEngine
engine = GameEngine()
engine.load_card('lightning-bolt')
result = engine.resolve_effect('lightning-bolt', target='player')
```
**Docker Volume Mount:**
```yaml
volumes:
- ./game-engine:/app/game_engine
```
### 3.4 Graphics
Graphics are handled in the **front-end development** (React/TypeScript with game board visualization). The multiplayer server provides:
- Game state synchronization
- Card data and metadata
- Real-time updates via WebSocket
The server does not handle rendering — that's the frontend's responsibility.
### 3.5 Chat System
The multiplayer server uses **pre-built Docker containers** for chat functionality:
#### Text-based Chat
- **Solution**: Tinode (recommended) or SimpleWebSocketChat
- **Architecture**: Option A (separate container)
- **Integration**: WebSocket-based, frontend connects directly
- **Features**: Room management, message persistence, moderation
**Docker Deployment (Tinode):**
```bash
docker run -d \
--name tinode \
-p 8080:8080 \
-v ./tinode-data:/data \
--restart unless-stopped \
tinode/tinode
```
**Frontend Integration:**
```javascript
import Tinode from 'tinode-sdk';
const tinode = new Tinode({ socket: 'ws://chat-server:8080' });
await tinode.connect();
await tinode.subscribe('game-room-123');
tinode.on('message', (topic, msg) => console.log('Chat:', msg));
tinode.sendMessage('game-room-123', { text: 'Hello game!' });
```
#### Audio-based Chat
- **Solution**: Kurento Media Server (recommended) or Janus Gateway
- **Architecture**: Option A (separate container)
- **Integration**: WebRTC-based, frontend connects directly
- **Features**: Low latency, scalable, open source
**Docker Deployment (Kurento):**
```bash
docker run -d \
--name kurento \
-p 8888:8888 \
-p 8443:8443 \
--restart unless-stopped \
kurento/kurento-media-server:latest
```
**No custom chat/audio codebase needed** — both are pre-built solutions.
### 3.6 Integration with Card Backend
The multiplayer server communicates with the card backend (current FastAPI app) via:
- **API Calls**: For authentication, user data, deck retrieval, card lookups - **API Calls**: For authentication, user data, deck retrieval, card lookups
- **Direct PSQL Queries**: For card data and user deck data - **Direct PSQL Queries**: For card data and user deck data
@@ -361,31 +492,24 @@ The multiplayer gameplay feature will live in a **separate backend codebase** to
- `POST /play/decks/{deck_id}/validate` — Validate deck against card database - `POST /play/decks/{deck_id}/validate` — Validate deck against card database
- `GET /play/users/{user_id}/decks` — Get user's FINAL decks for selection - `GET /play/users/{user_id}/decks` — Get user's FINAL decks for selection
- `GET /play/cards/{card_id}` — Get card details for game board display - `GET /play/cards/{card_id}` — Get card details for game board display
- `POST /play/chat/text` — Send text message in game/room (via Tinode)
- `POST /play/chat/audio` — Manage audio chat sessions (via Kurento)
**Note**: The play backend is out of scope for this document. See separate codebase/repository when ready. ### Summary of Work Required in Phase 3: Multiplayer Game Server
### Summary of Work Required in This Backend
| Feature | Database | API | Notes | | Feature | Database | API | Notes |
|---------|----------|-----|-------| |---------|----------|-----|-------|
| User deck CRUD | `mtgonline` (new tables) | Full CRUD + finalize | DRAFT/FINAL status | | Game engine integration | N/A (Python codebase) | N/A | Being developed separately, integrate in Phase 3.1 |
| Card search | `mtgdata` (existing) | Search endpoint | Leverages existing card DB | | Multiplayer WebSocket server | `mtgonline` (rooms, games) | WebSocket hub | Python/FastAPI |
| Card suggestions | `mtgonline` + `mtgdata` | Suggestion endpoint | Based on similar cards | | Game state management | In-memory + DB persistence | State sync | Server-authoritative |
| File import (XLSX/CSV/JSON/ODS) | `mtgonline` (new user cards table) | Upload + confirm | Fuzzy match required | | MTG rule enforcement | N/A (game engine) | Automatic | Cards auto-apply rules |
| Fuzzy matching service | N/A | Internal service | Handles spelling + EN variants | | Text chat backend | N/A (Tinode) | WebSocket | Pre-built Docker container |
| Play backend integration | Read-only access | API consumer | Separate codebase | | Audio chat backend | N/A (Kurento) | WebRTC | Pre-built Docker container |
| Group-based access | `mtgonline` (groups) | Group validation | Admin-configured groups |
| Deck validation | `mtgdata` + `mtgonline` | Validation endpoint | Against card database |
| Card backend integration | Read-only | API consumer | Shared DB + REST API |
### Files to Create/Modify See **PHASE_3_PLANNING.md** for detailed task breakdown.
- New models: `models/user_deck.py`, `models/user_card.py`
- New migrations: Alembic migrations for new tables
- New router: `routers/decks.py`, `routers/card_import.py`
- New service: `services/fuzzy_card_matcher.py`
- New service: `services/deck_suggestion.py`
- New schema: `schemas/deck.py`, `schemas/card_import.py`
- Update `core/database.py` if new engine needed
- Update `requirements.txt` with fuzzy matching libraries
See **ROADMAP.md Phase 2** for detailed task breakdown.
## User Data Schema & API ## User Data Schema & API
@@ -458,10 +582,11 @@ Current state saved at: `/home/wall-o/projects/mtgonline/state.json`
```json ```json
{ {
"task_description": "Create Alembic migration setup for user data schema and complete API endpoints for all user data features", "project_summary": "MTG Online Backend API with PostgreSQL database. Implements card game platform with deck management, card import, and user data features. Phase 3 (multiplayer game server) is in progress with game engine code incoming.",
"current_step": "Phase 3 completed - All API endpoints created with comprehensive documentation", "task_description": "Phase 3: Multiplayer game server with Cockatrice-inspired architecture, game engine integration, text/audio chat, and group-based gameplay",
"commit_hash": "c42d7ca", "current_step": "Preparation phase - reviewing Cockatrice architecture analysis, awaiting game engine code delivery, planning server scaffolding",
"timestamp": "2026-07-22T23:23:00-04:00" "commit_hash": "1e7c762",
"timestamp": "2026-07-24T04:35:00-04:00"
} }
``` ```
@@ -508,16 +633,16 @@ Current state saved at: `/home/wall-o/projects/mtgonline/state.json`
--- ---
**Last Updated**: 2026-07-22T23:23:00-04:00 **Last Updated**: 2026-07-24T04:35:00-04:00
**Status**: Phase 1-3 Complete. Ready for Phase 4: Testing and Deployment. **Status**: Phase 1-2 Complete. Phase 3 (Multiplayer Game Server) in progress. Game engine code incoming.
**Next Action**: Test migration execution in container, run API tests against all endpoints, add rate limiting, create integration tests, deploy to staging environment. **Next Action**: Begin Phase 3 — set up multiplayer server scaffolding, integrate game engine, implement WebSocket hub with chat support.
**Timeline**: **Timeline**:
| Phase | Duration | Status | | Phase | Duration | Status |
|-------|----------|--------| |-------|----------|--------|
| Phase 1: Backend Foundation | 2 weeks | ✅ Complete | | Phase 1: Backend Foundation | 2 weeks | ✅ Complete |
| Phase 2: Frontend Development | 4 weeks | Not Started | | Phase 2: Card Import & Deck Building | 2 weeks | ✅ Complete |
| Phase 3: Integration & Polish | 2 weeks | Not Started | | Phase 3: Multiplayer Game Server | TBD | 🔄 In Progress |
| Phase 4: Deployment & Production | 1 week | Not Started | | Phase 4: Frontend Integration | TBD | Pending |
| Phase 5: Advanced Features | Ongoing | Future | | Phase 5: Advanced Features | Ongoing | Future |
+703
View File
@@ -0,0 +1,703 @@
# MTG Online Multiplayer Game Server — Handoff Document
**Project**: MTG Online Web (Modern Web-based MTG Platform)
**Previous Thread**: Card Backend — Phase 1 & 2 (Deck Building, Card Import, User Data)
**New Thread**: Phase 3 — Multiplayer Game Server
**Location**: `/home/wall-o/projects/mtgonline`
**Date**: 2026-07-25
---
## Context: What Was Built (Card Backend)
The previous thread delivered a complete card management and deckbuilding backend:
### Completed Features
- **FastAPI application** with JWT authentication, dual PostgreSQL databases (`mtgonline` + `mtgdata`), Redis caching
- **29 REST endpoints** across 10 routers (auth, users, decks, rooms, games, admin, cards, interactions, refresh, user-data, card-import)
- **16 user-data tables** with Alembic migrations, CASCADE deletes, JSONB flexibility
- **Card import feature** with fuzzy matching for XLSX/CSV/JSON/ODS files
- **Deck CRUD** with DRAFT/FINAL status transitions, precedents, suggestions
- **MTGJSON data pipeline** downloading and upserting card data into `mtgdata` database
- **Frontend** (React/TypeScript) with deck builder UI, card import, authentication, admin dashboard
### Current Architecture
```
mtgonline/
├── backend/ # FastAPI card backend (Phase 1 & 2 complete)
│ ├── app/
│ │ ├── core/ # Settings, database engines, Redis client
│ │ ├── models/ # SQLAlchemy ORM models (user_data, user_deck, card_import)
│ │ ├── routers/ # API route modules (10 routers)
│ │ ├── schemas/ # Pydantic schemas
│ │ ├── services/ # MTGJSON manager, card DB, deck manager, fuzzy matcher
│ │ └── main.py # FastAPI app entry point
│ ├── alembic/ # Database migrations
│ └── Dockerfile
├── frontend/ # React/TypeScript deck builder (Phase 3 complete)
├── C++/ # Cockatrice architecture analysis (reference)
├── state.json # Current state tracking
└── ROADMAP.md # Full project roadmap
```
### Key Integration Points Available
- **Card lookup**: `GET /api/cards/{card_id}` — returns full card data for game display
- **Card search**: `GET /api/cards/search?q=...` — search cards during gameplay
- **Set lists**: `GET /api/sets/` — list available sets for game formatting
- **User decks**: `GET /api/users/{user_id}/decks` — retrieve FINAL decks for player selection
- **Card import status**: `GET /api/v1/card-import/status` — check if user has imported their collection
- **Database access**: Direct SQLAlchemy async engines for `mtgonline` (app data) and `mtgdata` (card data)
---
## Phase 3: Multiplayer Game Server — Requirements
### Objective
Build a real-time multiplayer game server that enables live MTG gameplay between multiple players via WebSocket connections. This is the core gameplay engine — the replacement for Cockatrice's `servatrice` component.
### Success Criteria
- [ ] Players can create/join game rooms and play MTG in real-time
- [ ] Game state syncs correctly across all connected players (< 50ms latency)
- [ ] All 11 MTG turn phases are enforced server-side
- [ ] Stack resolution works correctly (layers, priority, targeting)
- [ ] Deck validation ensures legality before gameplay
- [ ] Game history is recorded as a serialized event log (for replays)
- [ ] Admin commands work (kick, ban, game control)
- [ ] Spectator mode is supported
- [ ] WebSocket reconnection handles gracefully with state sync
---
## Architecture Blueprint (Derived from Cockatrice Analysis)
### Authority Model
```
┌─────────────────┐ WebSocket (JSON) ┌─────────────────┐
│ CLIENT │◄────────────────────────────────────►│ SERVER │
│ (React/TS) │ │ (FastAPI/Py) │
│ │ │ │
│ • Game Scene │ GameCommands (play, attack, etc.) │ • PostgreSQL │
│ • Hand View │◄────────────────────────────────────►│ • Game State │
│ • Chat Panel │ GameEvents (state changes) │ • Room/Player │
│ • Deck Panel │ │ Management │
│ • Phase Toolbar │ Chat, Admin, Spectator │ • Replay Log │
└─────────────────┘ └─────────────────┘
```
**Key Principle**: Server is authoritative. Clients send commands, server validates and broadcasts events. No client-side state manipulation.
### Game Engine Architecture
| Cockatrice Component | Modern Web Equivalent | Role |
|---|---|---|
| `AbstractGame` | `Game` (server-side) | Core game instance holding state, players, event handler |
| `GameMetaInfo` | `GameMetadata` | gameId, maxPlayers, description, started, spectators |
| `GameState` | `GameBoardState` | currentPhase, activePlayer, hostId, gameTimer |
| `GameEventHandler` | `GameEventDispatcher` | Central dispatch — processes events, prepares commands |
| `PlayerManager` | `PlayerRegistry` | Coordinates all players in a game |
| `PlayerLogic` | `Player` | Per-player game logic |
| `PlayerActions` | `PlayerCommands` | Concrete commands: play, attack, tap, draw |
| `CardZone` | `Zone` (base class) | Abstract zone — Hand/Stack/Table/Graveyard/Exile/Library |
| `HandZone` | `Hand` | Player's hand (secret/hidden zone) |
| `StackZone` | `Stack` | Spells/abilities on the stack |
| `TableZone` | `Battlefield` | Permanents on the battlefield |
| `PileZone` | `Pile` (Graveyard, Exile, Library) | Discard/exile/draw piles |
| `Replay` | `GameReplay` | Serialized event stream for replay |
| `Phase` | `TurnPhase` | 11-phase MTG turn structure |
### Turn Phase System (11 Phases with Sub-Phases)
```
Untap → Upkeep → Draw → Main 1 → Combat → Main 2 → End → Cleanup
└── Sub-phases:
Beginning of Combat
Declare Attackers
Declare Blockers
Combat Damage
End of Combat
```
### Command/Event Flow
```
User Action (React component)
GameCommand (JSON message)
GameEventDispatcher.process()
Player.handleCommand()
ZoneLogic (state mutation)
GameEvent (broadcast to all clients via WebSocket)
Client receives & updates UI via Zustand/XState
```
### Network Protocol Design
Cockatrice uses Protocol Buffers over TCP. Modern web equivalent:
| Cockatrice Proto Message | JSON WebSocket Message |
|---|---|
| `ServerInfo_Game` | `{ "type": "game_info", "game_id": 1, "max_players": 2, ... }` |
| `ServerInfo_Player` | `{ "type": "player_info", "player_id": 1, "name": "...", ... }` |
| `Command_PlayCard` | `{ "type": "cmd_play_card", "card_id": 42, "zone": "hand", ... }` |
| `Command_Attack` | `{ "type": "cmd_attack", "attacker_id": 7, "targets": [3, 5], ... }` |
| `Event_Join` | `{ "type": "event_join", "player_id": 2, "properties": {...} }` |
| `Event_Leave` | `{ "type": "event_leave", "player_id": 1, "reason": "..." }` |
| `Event_SetActivePlayer` | `{ "type": "event_active_player", "player_id": 1 }` |
| `Event_SetActivePhase` | `{ "type": "event_active_phase", "phase": 5 }` |
| `Event_GameSay` | `{ "type": "event_chat", "player_id": 1, "message": "..." }` |
| `GameReplay` | `{ "type": "replay", "events": [...] }` |
### Architecture Patterns to Reuse
| Pattern | Cockatrice Usage | Modern Equivalent |
|---|---|---|
| **Event Bus** | Qt signals/slots | WebSocket broadcast + event system |
| **Command Pattern** | `Command_PlayCard`, `Command_Attack` | JSON command messages, validated server-side |
| **Observer** | `GameEventHandler` emits signals | WebSocket events trigger UI updates |
| **Strategy** | `CardZoneLogic` subclasses | Zone classes with strategy pattern |
| **Facade** | `AbstractGame` | Single `Game` object wrapping all subsystems |
| **Memento** | `DeckListMemento` for undo | Immutable state snapshots for undo/redo |
| **Repository** | `ServatriceDatabaseInterface` | SQLAlchemy repositories for game state |
---
## Implementation Plan
### Phase 3.1: Game Server Foundation
#### Deliverables
1. **Game Server Module** — Separate FastAPI WebSocket endpoint for game rooms
2. **Game Room Management** — Room creation, player join/leave, spectator support
3. **Game State Management** — Server-authoritative game state with full phase tracking
4. **Player System** — Player registration, connection management, disconnection handling
5. **WebSocket Protocol** — JSON message protocol for commands and events
6. **Database Models** — Game rooms, player connections, game state persistence
#### Files to Create
```
backend/app/
├── models/
│ ├── game_room.py # Game room model (room_id, game_type, host_id, status)
│ ├── game_player.py # Player connection (player_id, room_id, connection_id, hand, life)
│ ├── game_state.py # Game board state (phase, turn, cards, zones)
│ ├── game_event.py # Game events log (for replay)
│ └── game_log.py # Game history (serialized event log)
├── routers/
│ └── game_server.py # WebSocket endpoint for game rooms
├── services/
│ ├── game_engine.py # Core game engine (turn phases, combat, stack)
│ ├── game_state_manager.py # Game state management and synchronization
│ ├── player_manager.py # Player management (join, leave, disconnect)
│ ├── card_zone.py # Card zone logic (hand, stack, battlefield, pile)
│ ├── game_rule_engine.py # Game rule enforcement (mana, timing, targeting)
│ └── game_replay.py # Game replay recording and playback
├── schemas/
│ └── game_schemas.py # Pydantic schemas for game messages
└── ws/
├── connection.py # WebSocket connection management
└── message_handler.py # Message parsing and dispatch
```
#### Detailed Implementation Steps
**Step 1: Game Room Model & Router**
- Create `GameRoom` model with room_id, game_type, host_id, status (WAITING/PLAYING/FINISHED)
- Create `GamePlayer` model with player_id, room_id, connection_id, hand (JSONB), life_total
- Create `GameServer` router with WebSocket endpoint `/ws/game/{room_id}`
- Implement room creation (host creates room), player join/leave, spectator join/leave
- Handle WebSocket disconnection gracefully (player_timeout for reconnect)
**Step 2: Game State Management**
- Create `GameBoardState` model tracking: current_phase, active_player, turn_number, host_id
- Track all zone contents: hands, stack, battlefield, graveyards, exiles, libraries
- Implement phase transitions (Untap → Upkeep → Draw → Main1 → Combat → Main2 → End → Cleanup)
- Handle sub-phases within Combat (Beginning, Declare Attackers, Declare Blockers, Combat Damage, End)
**Step 3: Card Zone Logic**
- Implement `CardZone` base class with common operations (add, remove, search, sort)
- Implement `HandZone` (secret zone, only visible to owner)
- Implement `StackZone` (priority-based, spell/ability resolution)
- Implement `BattlefieldZone` (permanent zone, battlefield effects)
- Implement `PileZone` (Graveyard, Exile, Library — shuffle, draw, discard)
- Implement zone-specific behaviors (hand: count/mulligan, stack: layer resolution)
**Step 4: Game Rule Engine**
- Implement mana payment tracking and validation
- Implement timing rules (sorcery speed, instant speed, flash)
- Implement targeting validation (legal targets, priority chain)
- Implement combat rules (attackers, blockers, damage assignment, trample, first strike)
- Implement stack resolution (last in, first out, layer system)
- Implement life total tracking and loss conditions (0 life, poison, concession)
**Step 5: Game Replay System**
- Serialize all game events as a JSONB log
- Support replay recording (on game start) and playback (on request)
- Implement deterministic replay (same commands → same state)
- Store replays in `game_logs` table with compressed JSONB
**Step 6: Admin Commands**
- Implement kick player command
- Implement ban player command (from mtgonline DB)
- Implement game pause/resume
- Implement spectator mode toggle
- Implement admin override for game state
### Phase 3.2: Integration with Card Backend
#### Deliverables
1. **Deck Validation Service** — Validate decks before game start
2. **Card Lookup Service** — Fetch card data for game display
3. **User Authentication** — JWT validation for game server
4. **Database Integration** — Access to `mtgdata` and `mtgonline` databases
#### Integration Points
- **Card Backend API** (`http://backend:8000`):
- `GET /api/cards/{card_id}` — Get card details for game board display
- `GET /api/cards/search?q=...` — Search cards during gameplay
- `GET /api/sets/` — List available sets for game formatting
- `GET /api/users/{user_id}/decks` — Get user's FINAL decks for selection
- **Direct PSQL Access** (via shared connection string):
- `mtgdata` database — Read card data (cards, sets, etc.)
- `mtgonline` database — Read user decks (for deck validation, game setup)
#### Deck Validation Flow
```
Player selects deck → Server fetches deck from mtgonline DB
Validate deck against rules:
├── Check card count (60+ for Standard/Modern, 100+ for Commander)
│ ├── Card validity (must exist in mtgdata)
│ ├── Color identity (Commander format)
│ └── Basic land count (max 4 of each non-basic)
├── Check banned cards (format-specific ban list)
└── Return validation result (pass/fail with reasons)
```
### Phase 3.3: WebSocket Client Integration
#### Deliverables
1. **WebSocket Client** — React component for game client
2. **State Management** — Zustand store for game state
3. **Message Handlers** — Parse and handle game events
4. **Reconnection Logic** — Exponential backoff with state sync
#### Integration with Frontend
- **WebSocket Connection**: Connect to `/ws/game/{room_id}` on game start
- **State Store**: Use Zustand to manage game state, sync with server events
- **Message Handlers**: Handle join, leave, play_card, attack, etc.
- **Reconnection**: On disconnect, attempt reconnect with exponential backoff, sync state on reconnect
---
## Database Schema for Game Server
### Tables to Create
| Table | Purpose | Key Columns |
|-------|---------|-------------|
| `game_rooms` | Game room management | room_id (PK), game_type, host_id, status, created_at |
| `game_players` | Player connections | player_id (PK), room_id (FK), connection_id, hand (JSONB), life_total, is_spectator |
| `game_states` | Game board state | state_id (PK), room_id (FK), current_phase, turn_number, active_player, cards (JSONB), created_at |
| `game_events` | Event log for replay | event_id (PK), room_id (FK), event_type, event_data (JSONB), timestamp |
| `game_replays` | Recorded games | replay_id (PK), room_id (FK), events (JSONB), duration, started_at, finished_at |
### JSONB Structures
**Hand**: `{"cards": [{"card_id": 1, "name": "Lightning Bolt", "zone": "hand"}]}`
**Stack**: `{"items": [{"card_id": 5, "name": "Counterspell", "controller": 1}]}`
**Battlefield**: `{"cards": [{"card_id": 10, "name": "Craterhoof", "zone": "battlefield", "tapped": false}]}`
**Graveyard**: `{"cards": [{"card_id": 1, "name": "Lightning Bolt", "zone": "graveyard"}]}`
---
## Game Engine Implementation Details
### Core Game Class
```python
class Game:
"""Core game instance holding state, players, event handler."""
def __init__(self, room_id: str, game_type: str, host_id: int):
self.room_id = room_id
self.game_type = game_type # Standard, Modern, Commander
self.host_id = host_id
self.players: Dict[int, Player] = {}
self.state: GameBoardState = GameBoardState()
self.event_dispatcher: GameEventDispatcher = GameEventDispatcher(self)
self.replay: GameReplay = GameReplay()
def add_player(self, player: Player) -> bool:
"""Add player to game. Returns True if successful."""
def remove_player(self, player_id: int) -> bool:
"""Remove player from game. Broadcasts event."""
def transition_phase(self) -> None:
"""Advance to next phase. Validates phase transitions."""
def handle_command(self, command: GameCommand) -> List[GameEvent]:
"""Process player command. Returns events to broadcast."""
def broadcast_event(self, event: GameEvent) -> None:
"""Broadcast event to all connected clients."""
```
### Player Class
```python
class Player:
"""Per-player game logic."""
def __init__(self, player_id: int, name: str, deck: Deck):
self.player_id = player_id
self.name = name
self.deck = deck
self.hand: HandZone = HandZone()
self.battlefield: BattlefieldZone = BattlefieldZone()
self.graveyard: PileZone = PileZone()
self.exile: PileZone = PileZone()
self.library: LibraryZone = LibraryZone()
self.life_total: int = 20
self.poison_counters: int = 0
def handle_command(self, command: GameCommand) -> List[GameEvent]:
"""Process command. Returns events to broadcast."""
def play_card(self, card_id: int, zone: str) -> GameEvent:
"""Play card from hand to battlefield."""
def attack(self, attacker_id: int, targets: List[int]) -> GameEvent:
"""Declare attackers and assign damage."""
def tap_card(self, card_id: int) -> GameEvent:
"""Tap card for mana or attack."""
def draw_card(self) -> GameEvent:
"""Draw card from library."""
```
### Zone Classes
```python
class CardZone:
"""Abstract zone base class."""
def add_card(self, card: Card) -> None:
"""Add card to zone."""
def remove_card(self, card_id: int) -> Card:
"""Remove card from zone."""
def search(self, filter: SearchFilter) -> List[Card]:
"""Search zone with filter."""
def shuffle(self) -> None:
"""Shuffle zone."""
def sort(self) -> None:
"""Sort zone."""
class HandZone(CardZone):
"""Player's hand (secret zone)."""
# Only visible to owner
class StackZone(CardZone):
"""Spells/abilities on the stack."""
# Priority-based resolution
class BattlefieldZone(CardZone):
"""Permanent zone."""
# Has tap/untap, counters, attachments
class PileZone(CardZone):
"""Graveyard, exile, library."""
# Shuffleable (library) or non-shuffleable (graveyard)
```
---
## Testing Strategy
### Unit Tests
- **Game State Transitions**: Test all 11 phases and sub-phases
- **Combat Rules**: Test attack, block, damage assignment, trample, first strike
- **Stack Resolution**: Test priority, layer system, LIFO resolution
- **Mana Payment**: Test mana pool, timing, costs
- **Zone Operations**: Test add/remove/search/shuffle/sort for each zone
- **Deck Validation**: Test format rules (Standard, Modern, Commander)
- **Replay Determinism**: Test same commands → same state
### Integration Tests
- **WebSocket Connection**: Test connect/disconnect/join/leave
- **Game Room Management**: Test room creation, player count, spectator
- **Real-time Sync**: Test state sync across multiple clients
- **Reconnection**: Test reconnect with state sync
- **Admin Commands**: Test kick, ban, pause, resume
### Load Tests
- **Concurrent Players**: Test 2-8 players per game
- **Multiple Games**: Test 100+ concurrent games
- **Message Throughput**: Test 1000+ messages/sec per game
- **Latency**: Test < 50ms message latency
---
## Acceptance Criteria
### Functional Requirements
- [ ] Players can create game rooms with custom settings
- [ ] Players can join existing game rooms
- [ ] Game starts with all players ready
- [ ] 11-phase turn structure works correctly
- [ ] Combat rules enforce attack/block/damage correctly
- [ ] Stack resolves spells/abilities in correct order
- [ ] Mana payment validates costs and timing
- [ ] Deck validation ensures format legality
- [ ] Game history recorded as serialized event log
- [ ] Admin can kick/ban players
- [ ] Spectators can watch games
- [ ] WebSocket disconnect triggers graceful player timeout
### Performance Requirements
- [ ] WebSocket latency < 50ms for game state updates
- [ ] Support 1000+ concurrent users across multiple games
- [ ] Game state syncs within 100ms across all connected players
- [ ] Replay recording doesn't impact gameplay performance
- [ ] Database queries for card lookup < 10ms
### Security Requirements
- [ ] JWT authentication for all WebSocket connections
- [ ] Input validation for all game commands
- [ ] Rate limiting for message spam protection
- [ ] Admin-only commands require admin role
- [ ] Game state never exposed to spectators (unless enabled)
---
## Files to Create/Modify
### New Files
```
backend/app/
├── models/
│ ├── game_room.py
│ ├── game_player.py
│ ├── game_state.py
│ ├── game_event.py
│ └── game_log.py
├── routers/
│ └── game_server.py
├── services/
│ ├── game_engine.py
│ ├── game_state_manager.py
│ ├── player_manager.py
│ ├── card_zone.py
│ ├── game_rule_engine.py
│ └── game_replay.py
├── schemas/
│ └── game_schemas.py
└── ws/
├── connection.py
└── message_handler.py
```
### Modified Files
```
backend/app/
├── main.py # Mount game server router
├── core/
│ ├── database.py # Add game database engine if needed
│ └── settings.py # Add game server settings
├── alembic/
│ └── versions/
│ └── 005_game_server_tables.py
```
### Dependencies to Add
```
# backend/requirements.txt
websockets>=12.0
pydantic>=2.9.2
```
---
## Integration with Card Backend
### API Endpoints Used by Game Server
| Endpoint | Purpose | Method |
|----------|---------|--------|
| `/api/cards/{card_id}` | Get card details | GET |
| `/api/cards/search?q=...` | Search cards | GET |
| `/api/sets/` | List sets | GET |
| `/api/users/{user_id}/decks` | Get user decks | GET |
| `/api/v1/card-import/status` | Check import status | GET |
### Database Access
**mtgdata database** (read-only):
- `mtg_cards` — Card data for game display
- `mtg_sets` — Set data for format rules
- `mtg_card_types` — Card type definitions
**mtgonline database** (read-write):
- `users` — User authentication
- `decks` — User decks for validation
- `ban` — Ban list for admin commands
- `game_logs` — Game history storage
---
## Cockatrice Analysis Reference
The complete architecture analysis is at `/home/wall-o/projects/mtgonline/C++/ARCHITECTURE_ANALYSIS.md`.
Key takeaways:
- **Server authoritative**: Never trust client state
- **Deterministic replay**: Same commands → same state
- **Zone abstraction**: Each zone independently implementable
- **Phase system**: 11-phase MTG turn with sub-phases
- **Command/Event separation**: What player wants vs. what happens
The modern web adaptation replaces:
- TCP with WebSocket (JSON)
- Protocol Buffers with JSON messages
- Qt Widgets with React/Next.js
- C++/MySQL with Python/FastAPI + PostgreSQL
- Monolithic handler with modular service architecture
---
## Next Steps
1. **Start with Phase 3.1**: Game server foundation (models, router, WebSocket)
2. **Implement core game engine**: Turn phases, combat, stack resolution
3. **Add zone logic**: Hand, stack, battlefield, pile zones
4. **Integrate with card backend**: Deck validation, card lookup
5. **Add replay system**: Serialize events for replay
6. **Test thoroughly**: Unit, integration, load tests
7. **Update documentation**: API docs, architecture docs
8. **Push to Gitea**: Commit and push all changes
---
## Quick Reference
### Game Room WebSocket Endpoint
```
WS /ws/game/{room_id}
```
### Command Messages
```json
{
"type": "cmd_play_card",
"card_id": 42,
"zone": "hand",
"targets": [3, 5],
"mana_cost": [1, "R", "G"]
}
```
### Event Messages
```json
{
"type": "event_play_card",
"card_id": 42,
"controller": 1,
"zone": "battlefield",
"timestamp": 1784948189
}
```
### State Sync Message
```json
{
"type": "state_sync",
"game_state": {
"current_phase": "main1",
"turn_number": 5,
"active_player": 1,
"hands": {"1": [...], "2": [...]},
"battlefield": [...],
"stack": [...],
"graveyards": {"1": [...], "2": [...]}
}
}
```
---
## Environment Variables
Add to `.env` or docker-compose:
```bash
# Game Server
GAME_SERVER_HOST=0.0.0.0
GAME_SERVER_PORT=8765
GAME_WS_PATH=/ws/game
GAME_TOKEN_EXPIRY=3600
GAME_PLAYER_TIMEOUT=30
GAME_MAX_PLAYERS=8
```
---
## Docker Configuration
Add to `docker-compose.dev.yml`:
```yaml
game-server:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- "8765:8765"
environment:
- DATABASE_URL=${DATABASE_URL}
- MTG_DATABASE_URL=${MTG_DATABASE_URL}
- REDIS_URL=${REDIS_URL}
- GAME_SERVER_PORT=8765
- GAME_WS_PATH=/ws/game
depends_on:
- postgres
- mtgdata
- redis
networks:
- mtgonline
```
---
**Last Updated**: 2026-07-25
**Status**: Handoff complete. Ready for Phase 3 implementation.
**Next Action**: Begin Phase 3.1 — Game server foundation (models, router, WebSocket).
+27 -17
View File
@@ -1,5 +1,5 @@
{ {
"project_summary": "MTG Online Backend API with PostgreSQL database. Implements card game platform with deck management, card import, and user data features. Uses FastAPI, SQLAlchemy async, and Alembic for database migrations.", "project_summary": "MTG Online Backend API with PostgreSQL database. Implements card game platform with deck management, card import, and user data features. Uses FastAPI, SQLAlchemy async, and Alembic for database migrations. Now expanding to multiplayer game server phase.",
"roadmap": [ "roadmap": [
{ {
"phase": 1, "phase": 1,
@@ -28,8 +28,8 @@
}, },
{ {
"phase": 3, "phase": 3,
"status": "pending", "status": "in_progress",
"description": "Multiplayer game server with WebSocket support", "description": "Multiplayer game server with WebSocket support — Server-authoritative game engine, real-time multiplayer, stack resolution, card zones, deck validation, replay recording. Handoff prepared at handoff.md.",
"key_deliverables": ["WebSocket game server", "Game state management", "Player synchronization", "Real-time card updates"] "key_deliverables": ["WebSocket game server", "Game state management", "Player synchronization", "Real-time card updates"]
}, },
{ {
@@ -57,9 +57,9 @@
"redis[hiredis]==5.1.0" "redis[hiredis]==5.1.0"
] ]
}, },
"architectural_notes": "Dual database setup: mtgonline for app data, mtgdata for MTGJSON card data. Alembic migrations run on container startup. Async SQLAlchemy with asyncpg driver. Card mirrors in mtgo_platform for fast deckbuilding queries. User data API mounted at /api/v1/user-data. Card import router mounted at /api/v1/card-import.", "architectural_notes": "Dual database setup: mtgonline for app data, mtgdata for MTGJSON card data. Alembic migrations run on container startup. Async SQLAlchemy with asyncpg driver. Card mirrors in mtgo_platform for fast deckbuilding queries. User data API mounted at /api/v1/user-data. Card import router mounted at /api/v1/card-import. Phase 3 adds server-authoritative game engine with WebSocket real-time multiplayer, 11-phase MTG turn structure, stack resolution, card zones, deck validation, and replay recording.",
"task_description": "Phase 2 completion - Card import feature fully implemented with fuzzy matching, card search integration, and API endpoints", "task_description": "Multiplayer game server — Phase 3 handoff. Architecture derived from Cockatrice analysis (v3.1.0 Graduation Day). Server-authoritative game engine with WebSocket real-time multiplayer, 11-phase MTG turn structure, stack resolution, card zones, deck validation, and replay recording. Handoff document at handoff.md provides complete blueprint.",
"current_step": "Phase 2 completed. Card import feature ready for production use. All API endpoints tested and verified.", "current_step": "Phase 2 complete. Handoff document prepared for Phase 3: Multiplayer game server. Ready to begin implementation.",
"files_created": [ "files_created": [
"alembic.ini", "alembic.ini",
"alembic/env.py", "alembic/env.py",
@@ -80,14 +80,18 @@
"app/routers/card_import.py", "app/routers/card_import.py",
"scripts/run_migrations.sh", "scripts/run_migrations.sh",
"TEST_PLAN.md", "TEST_PLAN.md",
"API_DOCUMENTATION.md" "API_DOCUMENTATION.md",
"C++/ARCHITECTURE_ANALYSIS.md",
"handoff.md"
], ],
"files_modified": [ "files_modified": [
"app/models/__init__.py", "app/models/__init__.py",
"Dockerfile", "Dockerfile",
"app/main.py", "app/main.py",
"README.md (card import section added)", "README.md (card import section added)",
"backend/README.md (API endpoint reference)" "backend/README.md (API endpoint reference)",
"state.json (Phase 3 handoff)",
"ROADMAP.md (Phase 3+ documented)"
], ],
"decisions": [ "decisions": [
"Using Alembic for version-controlled database migrations", "Using Alembic for version-controlled database migrations",
@@ -101,17 +105,23 @@
"Option B for cross-DB FK: Local card mirror in mtgonline DB (mtgonline_cards)", "Option B for cross-DB FK: Local card mirror in mtgonline DB (mtgonline_cards)",
"Fuzzy matching reserved for card import feature only (handles typos in user input)", "Fuzzy matching reserved for card import feature only (handles typos in user input)",
"Local card search endpoint for fast deckbuilding queries", "Local card search endpoint for fast deckbuilding queries",
"Card import router mounted at /api/v1/card-import" "Card import router mounted at /api/v1/card-import",
"Phase 3: Server-authoritative game engine (no client-side state manipulation)",
"Phase 3: WebSocket JSON protocol (replaces Cockatrice's TCP/protobuf)",
"Phase 3: Zone abstraction (Hand, Stack, Battlefield, Pile) independently implementable",
"Phase 3: 11-phase MTG turn with sub-phases tracked server-side",
"Phase 3: Deterministic replay (same commands → same state)"
], ],
"next_steps": [ "next_steps": [
"Test card import API endpoints in container", "Begin Phase 3.1: Game server foundation (models, router, WebSocket)",
"Run full API test suite", "Implement core game engine (turn phases, combat, stack)",
"Add rate limiting for production", "Add zone logic (Hand, Stack, Battlefield, Pile)",
"Create integration tests", "Integrate with card backend (deck validation, card lookup)",
"Deploy to staging environment", "Add replay system (serialize events)",
"Begin Phase 3: Multiplayer game server" "Test thoroughly (unit, integration, load tests)",
"Update documentation and push to Gitea"
], ],
"blockers": [], "blockers": [],
"commit_hash": "", "commit_hash": "1e7c762",
"timestamp": "2026-07-24T04:35:00-04:00" "timestamp": "2026-07-25T19:58:00-04:00"
} }