From 92d3d40bc7ee4c9482e96f139426ed030c5f5dde Mon Sep 17 00:00:00 2001 From: admin Date: Mon, 19 Oct 2026 12:05:13 +0000 Subject: [PATCH] Add WebSocket game server architecture and handoff documentation --- backend/HANDOFF_WEBSOCKET.md | 144 ++++++++ backend/docs/WEB_SOCKET_ARCHITECTURE.md | 463 ++++++++++++++++++++++++ backend/state.json | 28 +- 3 files changed, 623 insertions(+), 12 deletions(-) create mode 100644 backend/HANDOFF_WEBSOCKET.md create mode 100644 backend/docs/WEB_SOCKET_ARCHITECTURE.md diff --git a/backend/HANDOFF_WEBSOCKET.md b/backend/HANDOFF_WEBSOCKET.md new file mode 100644 index 0000000..da7fc8f --- /dev/null +++ b/backend/HANDOFF_WEBSOCKET.md @@ -0,0 +1,144 @@ +# MTG Online WebSocket Game Server - Handoff Document + +## Project Overview + +MTG Online is a multiplayer Magic: The Gathering platform with a FastAPI backend. Phase 5 (Game Management) is complete. Phase 6 (WebSocket Game Server) is ready for implementation. + +## Current Status + +- **Phase 1-4:** Complete (authentication, users, decks, cards, user data) +- **Phase 5:** Complete (game management, rooms, MTGJSON integration) +- **Phase 6:** In Progress (WebSocket game server architecture designed) + +## Codebase Structure + +``` +/home/wall-o/projects/mtgonline/backend/ +├── app/ +│ ├── core/ # Configuration, database, security, logging +│ ├── models/ # SQLAlchemy ORM models +│ ├── routers/ # API endpoint definitions +│ ├── services/ # Business logic layer +│ ├── schemas/ # Pydantic request/response schemas +│ └── main.py # Application entry point +├── tests/ # Test suite (28 tests, all passing) +├── docs/ # Documentation +└── state.json # Project state +``` + +## Recent Changes + +1. **Fixed critical bug** in `games_router.py`: `player_result` → `player_stmt` in `create_game` endpoint +2. **Fixed deprecation warning** in `main.py`: Updated lifespan from generator to `@asynccontextmanager` +3. **Designed WebSocket architecture** documented in `docs/WEB_SOCKET_ARCHITECTURE.md` + +## WebSocket Game Server Architecture + +The architecture is fully designed and documented. Key components: + +### Protocol +- JSON message format with type, sequence number, timestamp, and payload +- Message types: connection management, game lifecycle, player actions, turn management, chat +- JWT authentication via `hello` message + +### Components +1. **WebSocket Endpoint** (`/ws/game/{game_id}`) - connection management +2. **Game Server Manager** - coordinates active game instances +3. **Game State Manager** - authoritative state, validates actions +4. **Persistence Layer** - PostgreSQL for state saving and recovery + +### Concurrency +- asyncio-based architecture +- Game-level locks for state mutations +- Action queue for ordering within turns + +### Key Features +- Full state snapshots on connection +- Incremental state updates for changes +- Priority system for turn management +- Reconnection and recovery support +- Error handling with specific error codes + +## Implementation Plan + +### Phase 1: Basic Infrastructure +- WebSocket endpoint setup +- Connection management +- Authentication +- Basic message routing + +### Phase 2: Game State Management +- Game state model +- State serialization +- Full state snapshots +- Incremental updates + +### Phase 3: Turn and Phase Management +- Turn structure implementation +- Phase transitions +- Priority system +- Action validation + +### Phase 4: Player Actions +- Card play actions +- Combat actions +- Spell casting +- Ability activation + +### Phase 5: Persistence and Recovery +- Game state persistence +- Reconnection handling +- Game recovery +- Chat history + +### Phase 6: Testing and Optimization +- Unit tests +- Integration tests +- Load testing +- Performance optimization + +## Technical Stack + +- **Language:** Python 3.12 +- **Framework:** FastAPI +- **ORM:** SQLAlchemy 2.0 +- **Database:** SQLite (development), PostgreSQL (production) +- **Auth:** JWT (python-jose) +- **WebSocket:** FastAPI built-in WebSocket support + +## Key Files + +- `app/main.py` - Application entry point with WebSocket endpoint registration +- `app/routers/games_router.py` - Game management endpoints (includes basic WebSocket endpoint) +- `app/services/game_server.py` - Game server service (basic implementation exists) +- `docs/WEB_SOCKET_ARCHITECTURE.md` - Complete architecture document +- `tests/` - Test suite + +## Testing + +- 28 tests currently passing +- Test framework: pytest with pytest-asyncio +- Database: In-memory SQLite for tests +- Run tests: `pytest --tb=short` + +## Environment + +- Host venv: `/home/wall-o/workspace/venv` +- Run as user: `wall-o` (UID 1001) +- Gitea credentials: `/home/wall-o/projects/gitea_credentials.txt` +- Repository: `https://git.optimex.systems/admin/mtgonline.git` + +## Next Steps + +1. Implement WebSocket game server following the architecture document +2. Add test coverage for games, cards, and user_data routers +3. Configure linting (Ruff) +4. Add pre-commit hooks +5. Set up CI/CD pipeline + +## Notes + +- The codebase is well-structured with clean separation of concerns +- Comprehensive docstrings and type hints throughout +- Service layer abstraction is well-implemented +- No need for code simplification - architecture is already clean diff --git a/backend/docs/WEB_SOCKET_ARCHITECTURE.md b/backend/docs/WEB_SOCKET_ARCHITECTURE.md new file mode 100644 index 0000000..5b7646c --- /dev/null +++ b/backend/docs/WEB_SOCKET_ARCHITECTURE.md @@ -0,0 +1,463 @@ +# WebSocket Game Server Architecture + +## Overview + +The MTG Online WebSocket game server provides real-time game state synchronization and player action handling for multiplayer Magic: The Gathering sessions. This document defines the architecture, protocol, and message formats. + +## Architecture + +``` +┌─────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ Player 1 │────▶│ WebSocket │────▶│ Game Server │ +│ Browser │◀────│ Connection │◀────│ (asyncio) │ +└─────────────┘ └─────────────────┘ └────────┬────────┘ + │ +┌─────────────┐ ┌─────────────────┐ ┌────────┴────────┐ +│ Player 2 │────▶│ WebSocket │────▶│ Game State │ +│ Browser │◀────│ Connection │◀────│ Manager │ +└─────────────┘ └─────────────────┘ └────────┬────────┘ + │ +┌─────────────┐ ┌─────────────────┐ ┌────────┴────────┐ +│ Player 3 │────▶│ WebSocket │────▶│ PostgreSQL │ +│ Browser │◀────│ Connection │◀────│ (Persistence) │ +└─────────────┘ └─────────────────┘ └─────────────────┘ +``` + +### Components + +1. **WebSocket Endpoint** (`/ws/game/{game_id}`) + - Accepts connections + - Authenticates players + - Routes messages to/from game instances + +2. **Game Server Manager** + - Manages active game instances + - Handles game lifecycle (create, join, start, end) + - Coordinates between players + +3. **Game State Manager** + - Maintains authoritative game state + - Validates player actions + - Emits state changes to all players + +4. **Persistence Layer** + - Saves game state to PostgreSQL + - Enables game replay and recovery + +## Protocol Design + +### Message Format + +All messages use JSON with the following envelope: + +```json +{ + "type": "message_type", + "seq": 123, + "ts": 1697750000.123, + "data": { + // Message-specific payload + } +} +``` + +- `type`: Message type identifier +- `seq`: Sequence number for ordering and gap detection +- `ts`: Unix timestamp (milliseconds) +- `data`: Message-specific payload + +### Message Types + +#### Connection Management + +| Type | Direction | Description | +|------|-----------|-------------| +| `hello` | Client → Server | Initial connection with auth token | +| `welcome` | Server → Client | Connection established, game info | +| `bye` | Client → Server | Player disconnecting | +| `ping` | Either | Keepalive | +| `pong` | Either | Keepalive response | + +#### Game Lifecycle + +| Type | Direction | Description | +|------|-----------|-------------| +| `game_start` | Server → Client | Game starting, initial state | +| `game_state` | Server → Client | Full game state snapshot | +| `state_update` | Server → Client | Incremental state change | +| `game_over` | Server → Client | Game ended with results | + +#### Player Actions + +| Type | Direction | Description | +|------|-----------|-------------| +| `action` | Client → Server | Player takes an action | +| `action_ack` | Server → Client | Action acknowledged | +| `action_error` | Server → Client | Action failed validation | + +#### Turn Management + +| Type | Direction | Description | +|------|-----------|-------------| +| `turn_start` | Server → Client | New turn beginning | +| `phase_change` | Server → Client | Phase transition | +| `priority` | Server → Client | Player has priority | +| `stack_empty` | Server → Client | Stack is empty | + +#### Chat + +| Type | Direction | Description | +|------|-----------|-------------| +| `chat` | Either | Chat message | +| `chat_history` | Server → Client | Chat history on join | + +### Authentication + +Players authenticate via JWT token in the `hello` message: + +```json +{ + "type": "hello", + "data": { + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "game_id": 42, + "seat": 1 + } +} +``` + +## Game State Model + +### Full State Snapshot + +```json +{ + "game_id": 42, + "room_id": 5, + "game_type": "Commander", + "state": "IN_PROGRESS", + "turn": 3, + "phase": "main1", + "players": [ + { + "player_id": 1, + "user_id": 101, + "seat": 1, + "life_total": 18, + "hand_size": 5, + "library_size": 95, + "graveyard_size": 3, + "is_active": true, + "has_priority": true + }, + { + "player_id": 2, + "user_id": 102, + "seat": 2, + "life_total": 20, + "hand_size": 6, + "library_size": 94, + "graveyard_size": 2, + "is_active": false, + "has_priority": false + } + ], + "battlefield": { + "player_1": [ + { + "card_id": "card_123", + "name": "Lightning Bolt", + "type": "Creature", + "power": 2, + "toughness": 2, + "is_tapped": false, + "is_attacking": false, + "damage": 0 + } + ], + "player_2": [] + }, + "stack": [], + "graveyard": { + "player_1": [], + "player_2": [] + } +} +``` + +### Incremental State Update + +```json +{ + "type": "state_update", + "seq": 124, + "data": { + "changes": [ + { + "player_id": 1, + "field": "life_total", + "old_value": 20, + "new_value": 18 + }, + { + "player_id": 1, + "field": "hand_size", + "old_value": 6, + "new_value": 5 + } + ], + "reason": "played_lightning_bolt" + } +} +``` + +## Player Action Protocol + +### Action Submission + +```json +{ + "type": "action", + "seq": 125, + "data": { + "action_type": "play_card", + "card_id": "card_456", + "target": null, + "mode": "attack" + } +} +``` + +### Action Types + +| Action Type | Description | Required Fields | +|-------------|-------------|-----------------| +| `play_card` | Play a card from hand | `card_id`, `target` (optional) | +| `tap_card` | Tap a card | `card_id` | +| `untap_card` | Untap a card | `card_id` | +| `attack` | Declare attacker | `card_id`, `target_player` | +| `block` | Declare blocker | `card_id`, `target_card_id` | +| `assign_damage` | Assign combat damage | `card_id`, `target_card_id`, `damage` | +| `pass_priority` | Pass priority | - | +| `cast_spell` | Cast a spell | `card_id`, `target` (optional) | +| `activate_ability` | Activate ability | `card_id`, `ability_id`, `target` (optional) | +| `end_turn` | End current turn | - | +| `concede` | Concede the game | - | + +### Action Validation + +The server validates all actions against the current game state: + +1. **Turn/Phase Check**: Is this action legal in the current phase? +2. **Priority Check**: Does the player have priority? +3. **Card State Check**: Is the card in the correct zone? +4. **Target Check**: Is the target valid? +5. **Cost Check**: Can the player pay the cost? + +## Turn and Phase Management + +### Turn Structure + +``` +Turn Start + └─ Untap Phase + └─ Untap all permanents + └─ Upkeep Phase + └─ Upkeep costs + └─ Draw Phase + └─ Draw a card + └─ Main Phase 1 + └─ Play lands, creatures, instants + └─ Combat Phase + └─ Beginning of Combat + └─ Declare Attackers + └─ Declare Blockers + └─ Combat Damage + └─ End of Combat + └─ Main Phase 2 + └─ Play lands, creatures, instants + └─ End Phase + └─ Cleanup +Turn End +``` + +### Priority System + +The server tracks priority and only accepts actions from the player with priority: + +```json +{ + "type": "priority", + "data": { + "player_id": 1, + "phase": "main1", + "stack_size": 0 + } +} +``` + +## Error Handling + +### Action Errors + +```json +{ + "type": "action_error", + "seq": 126, + "data": { + "action_seq": 125, + "error_code": "INVALID_TARGET", + "error_message": "Target player is not in the game", + "action_type": "play_card" + } +} +``` + +### Error Codes + +| Code | Description | +|------|-------------| +| `INVALID_ACTION` | Action not legal in current phase | +| `NO_PRIORITY` | Player does not have priority | +| `INVALID_TARGET` | Target is not valid | +| `CARD_NOT_IN_HAND` | Card not in player's hand | +| `CARD_NOT_ON_BATTLEFIELD` | Card not on battlefield | +| `CANT_PAY_COST` | Player cannot pay the cost | +| `STACK_NOT_EMPTY` | Stack is not empty | +| `GAME_OVER` | Game is already over | + +## Reconnection and Recovery + +### Reconnection Protocol + +When a player reconnects: + +1. Client sends `hello` with same game_id and seat +2. Server validates player is still in the game +3. Server sends `welcome` with current game state +4. Server sends `chat_history` for the session +5. Player resumes play + +### State Recovery + +If the server crashes: + +1. Game state is loaded from PostgreSQL +2. Players are notified to reconnect +3. Game resumes from last saved state + +## Concurrency Model + +### asyncio-based Architecture + +```python +class GameServer: + def __init__(self): + self.games: Dict[int, GameInstance] = {} + self.player_connections: Dict[int, WebSocket] = {} + + async def handle_connection(self, websocket, game_id): + """Handle a single WebSocket connection.""" + pass + + async def game_loop(self, game_id): + """Main game loop for a game instance.""" + pass + +class GameInstance: + def __init__(self, game_id, players): + self.game_id = game_id + self.players = players + self.state = GameState() + self.lock = asyncio.Lock() + + async def process_action(self, player_id, action): + """Process a player action with locking.""" + async with self.lock: + return self.state.apply_action(player_id, action) +``` + +### Locking Strategy + +- **Game-level lock**: Protects game state mutations +- **Action queue**: Orders actions within a turn +- **Phase transitions**: Synchronized across all players + +## Performance Considerations + +### Message Size + +- Full state snapshots: ~5-10 KB +- Incremental updates: ~100-500 bytes +- Chat messages: ~50-200 bytes + +### Latency Targets + +- Action acknowledgment: < 100ms +- State update delivery: < 200ms +- Turn transition: < 500ms + +### Scaling + +- Single server: ~50 concurrent games +- Multiple servers: Use Redis pub/sub for cross-server communication +- Database: PostgreSQL for persistence, Redis for caching + +## Security + +### Message Validation + +- All messages validated against schema +- Sequence numbers prevent replay attacks +- JWT tokens expire after 1 hour +- Rate limiting on actions per second + +### Data Integrity + +- Server is authoritative +- Client cannot modify game state directly +- All actions validated before application + +## Implementation Plan + +### Phase 1: Basic Infrastructure +- [ ] WebSocket endpoint setup +- [ ] Connection management +- [ ] Authentication +- [ ] Basic message routing + +### Phase 2: Game State Management +- [ ] Game state model +- [ ] State serialization +- [ ] Full state snapshots +- [ ] Incremental updates + +### Phase 3: Turn and Phase Management +- [ ] Turn structure implementation +- [ ] Phase transitions +- [ ] Priority system +- [ ] Action validation + +### Phase 4: Player Actions +- [ ] Card play actions +- [ ] Combat actions +- [ ] Spell casting +- [ ] Ability activation + +### Phase 5: Persistence and Recovery +- [ ] Game state persistence +- [ ] Reconnection handling +- [ ] Game recovery +- [ ] Chat history + +### Phase 6: Testing and Optimization +- [ ] Unit tests +- [ ] Integration tests +- [ ] Load testing +- [ ] Performance optimization + +## References + +- [FastAPI WebSocket Documentation](https://fastapi.tiangolo.com/advanced/websockets/) +- [Magic: The Gathering Comprehensive Rules](https://magic.wizards.com/en/rules) +- [WebSocket Protocol RFC 6455](https://tools.ietf.org/html/rfc6455) diff --git a/backend/state.json b/backend/state.json index 29bee5b..0659308 100644 --- a/backend/state.json +++ b/backend/state.json @@ -1,9 +1,9 @@ { - "project_summary": "MTG Online backend API with FastAPI, SQLAlchemy, and SQLite. Phase 5 development complete with all tests passing.", + "project_summary": "MTG Online backend API with FastAPI, SQLAlchemy, and SQLite. Phase 5 development complete with all tests passing. Critical bug and deprecation warnings fixed. WebSocket game server architecture designed.", "roadmap": [ {"phase": "Phase 1-4", "status": "completed"}, {"phase": "Phase 5 - Game Management", "status": "completed"}, - {"phase": "Phase 6 - WebSocket Game Server", "status": "planned"} + {"phase": "Phase 6 - WebSocket Game Server", "status": "in_progress"} ], "tech_stack": { "language": "Python 3.12", @@ -12,21 +12,25 @@ "database": "SQLite (aiosqlite)", "auth": "JWT (python-jose)" }, - "architectural_notes": "Single-process FastAPI app with async SQLAlchemy. SQLite for development/testing. JWT auth with user roles.", - "task_description": "Fix remaining game model test failures", - "current_step": "All tests passing", - "files_created": [], + "architectural_notes": "Single-process FastAPI app with async SQLAlchemy. SQLite for development/testing. JWT auth with user roles. WebSocket game server architecture designed.", + "task_description": "WebSocket game server architecture design and preparation", + "current_step": "Architecture designed, ready for implementation", + "files_created": [ + "docs/WEB_SOCKET_ARCHITECTURE.md" + ], "files_modified": [ - "app/models/models.py" + "app/routers/games_router.py", + "app/main.py" ], "decisions": [ - "Added host_id field to GameHistory model", - "Changed players relationship to lazy='selectin' for async compatibility" + "Fixed player_result -> player_stmt bug in create_game endpoint", + "Updated lifespan from generator to @asynccontextmanager for FastAPI compatibility", + "Designed WebSocket game server architecture with JSON message protocol" ], "next_steps": [ - "Phase 6: WebSocket game server development" + "Phase 6: Implement WebSocket game server following architecture document" ], "blockers": [], - "commit_hash": "3926b57", - "timestamp": "2026-10-18T13:08:00Z" + "commit_hash": "1331885", + "timestamp": "2026-10-19T06:35:00Z" } \ No newline at end of file