Add full multi-step decision workflow for SINGLE_UNCAPPED and DOUBLE_UNCAPPED outcomes, replacing the previous stub that fell through to basic single/double advancement. The decision tree follows the same interactive pattern as X-Check resolution with 5 phases: lead runner advance, defensive throw, trail runner advance, throw target selection, and safe/out speed check. - game_models.py: PendingUncappedHit model, 5 new decision phases - game_engine.py: initiate_uncapped_hit(), 5 submit methods, 3 result builders - handlers.py: 5 new WebSocket event handlers - ai_opponent.py: 5 AI decision stubs (conservative defaults) - play_resolver.py: Updated TODO comments for fallback paths - 80 new backend tests (2481 total): workflow (49), handlers (23), truth tables (8) - Fix GameplayPanel.spec.ts: add missing Pinia setup, fix component references Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3.2 KiB
3.2 KiB
WebSocket Module - Real-Time Game Communication
Purpose
Real-time bidirectional communication using Socket.io. Primary interface between players and game engine - all game actions flow through WebSocket events.
Architecture
Client (Browser)
↓ Socket.io
ConnectionManager
↓
Event Handlers
↓
Game Engine → StateManager → Database
↓
Broadcast to All Players
Structure
app/websocket/
├── connection_manager.py # Connection lifecycle & broadcasting
└── handlers.py # Event handler registration (20 handlers)
ConnectionManager
Manages connections, rooms, and broadcasting.
State:
user_sessions: Dict[str, str]- sid → user_idgame_rooms: Dict[str, Set[str]]- game_id → sids
Key Methods:
await manager.connect(sid, user_id)
await manager.disconnect(sid)
await manager.join_game(sid, game_id, role)
await manager.broadcast_to_game(game_id, event, data)
await manager.emit_to_user(sid, event, data)
Event Handlers (20 Total)
Connection Events
connect- JWT authenticationdisconnect- Cleanup sessions
Game Flow
join_game- Join game roomstart_game- Initialize game stateget_game_state- Request current stateget_box_score- Get statistics
Decision Submission
submit_defensive_decision- Defense strategysubmit_offensive_decision- Offense strategy
Manual Outcome Flow
roll_dice- Roll dice for playsubmit_manual_outcome- Submit card result
Substitutions
submit_pinch_hitter- Batting substitutionsubmit_pitching_change- Pitcher substitutionsubmit_defensive_replacement- Field substitution
Uncapped Hit Decisions
submit_uncapped_lead_advance- Lead runner advance choice (offensive)submit_uncapped_defensive_throw- Throw to base choice (defensive)submit_uncapped_trail_advance- Trail runner advance choice (offensive)submit_uncapped_throw_target- Throw at lead or trail (defensive)submit_uncapped_safe_out- Declare safe or out from card (offensive)
Lineup
get_lineup- Get team lineup
Event Pattern
@sio.event
async def event_name(sid, data):
try:
# 1. Validate input
# 2. Get game state
# 3. Process action
# 4. Broadcast result
except Exception as e:
await emit_error(sid, str(e))
Key Events Emitted
| Event | Recipient | Purpose |
|---|---|---|
game_state_update |
Room | State changed |
play_resolved |
Room | Play completed |
decision_required |
User | Need input |
error |
User | Error occurred |
dice_rolled |
Room | Dice result |
Common Tasks
Broadcasting State Update
state_dict = state.model_dump()
await manager.broadcast_to_game(game_id, "game_state_update", state_dict)
Error Handling
await manager.emit_to_user(sid, "error", {"message": str(e)})
References
- Protocol Spec:
../../.claude/implementation/websocket-protocol.md - Game Engine: See
../core/CLAUDE.md - Frontend Integration: See
frontend-sba/composables/CLAUDE.md
Handlers: 20/20 implemented | Updated: 2026-02-11