Add WebSocket game server architecture and handoff documentation
This commit is contained in:
@@ -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
|
||||||
@@ -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)
|
||||||
+16
-12
@@ -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": [
|
"roadmap": [
|
||||||
{"phase": "Phase 1-4", "status": "completed"},
|
{"phase": "Phase 1-4", "status": "completed"},
|
||||||
{"phase": "Phase 5 - Game Management", "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": {
|
"tech_stack": {
|
||||||
"language": "Python 3.12",
|
"language": "Python 3.12",
|
||||||
@@ -12,21 +12,25 @@
|
|||||||
"database": "SQLite (aiosqlite)",
|
"database": "SQLite (aiosqlite)",
|
||||||
"auth": "JWT (python-jose)"
|
"auth": "JWT (python-jose)"
|
||||||
},
|
},
|
||||||
"architectural_notes": "Single-process FastAPI app with async SQLAlchemy. SQLite for development/testing. JWT auth with user roles.",
|
"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": "Fix remaining game model test failures",
|
"task_description": "WebSocket game server architecture design and preparation",
|
||||||
"current_step": "All tests passing",
|
"current_step": "Architecture designed, ready for implementation",
|
||||||
"files_created": [],
|
"files_created": [
|
||||||
|
"docs/WEB_SOCKET_ARCHITECTURE.md"
|
||||||
|
],
|
||||||
"files_modified": [
|
"files_modified": [
|
||||||
"app/models/models.py"
|
"app/routers/games_router.py",
|
||||||
|
"app/main.py"
|
||||||
],
|
],
|
||||||
"decisions": [
|
"decisions": [
|
||||||
"Added host_id field to GameHistory model",
|
"Fixed player_result -> player_stmt bug in create_game endpoint",
|
||||||
"Changed players relationship to lazy='selectin' for async compatibility"
|
"Updated lifespan from generator to @asynccontextmanager for FastAPI compatibility",
|
||||||
|
"Designed WebSocket game server architecture with JSON message protocol"
|
||||||
],
|
],
|
||||||
"next_steps": [
|
"next_steps": [
|
||||||
"Phase 6: WebSocket game server development"
|
"Phase 6: Implement WebSocket game server following architecture document"
|
||||||
],
|
],
|
||||||
"blockers": [],
|
"blockers": [],
|
||||||
"commit_hash": "3926b57",
|
"commit_hash": "1331885",
|
||||||
"timestamp": "2026-10-18T13:08:00Z"
|
"timestamp": "2026-10-19T06:35:00Z"
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user