strat-gameplay-webapp/backend/app/main.py
Cal Corum 9627a79dce CLAUDE: Add decision_required WebSocket event and quick-create testing endpoint
Backend enhancements for real-time decision workflow:

**New Features**:
- decision_required event emission when game starts and after each decision
- Quick-create endpoint (/games/quick-create) for rapid testing with pre-configured lineups
- WebSocket connection manager integration in GameEngine

**Changes**:
- game_engine.py: Added _emit_decision_required() method and set_connection_manager()
- game_engine.py: Emit decision_required on game start with 5-minute timeout
- games.py: New /quick-create endpoint with Team 35 vs Team 38 lineups
- main.py: Wire connection manager to game_engine singleton
- state_manager.py: Enhanced state management for decision phases
- play_resolver.py: Improved play resolution logic
- handlers.py: Updated WebSocket handlers for new workflow
- backend/CLAUDE.md: Added WebSocket protocol spec reference

**Why**:
Eliminates polling - frontend now gets real-time notification when decisions are needed.
Quick-create saves 2 minutes of lineup setup during each test iteration.

**Testing**:
- Manual testing with terminal client
- WebSocket event flow verified with live frontend

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 15:40:27 -06:00

108 lines
2.8 KiB
Python

import logging
from contextlib import asynccontextmanager
import socketio
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.api.routes import auth, games, health, teams
from app.config import get_settings
from app.database.session import init_db
from app.services import redis_client
from app.utils.logging import setup_logging
from app.websocket.connection_manager import ConnectionManager
from app.websocket.handlers import register_handlers
logger = logging.getLogger(f"{__name__}.main")
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Startup and shutdown events"""
settings = get_settings()
# Startup
logger.info("Starting Paper Dynasty Game Backend")
setup_logging()
# Initialize database
await init_db()
logger.info("Database initialized")
# Initialize Redis
try:
redis_url = settings.redis_url
await redis_client.connect(redis_url)
logger.info(f"Redis initialized: {redis_url}")
except Exception as e:
logger.warning(
f"Redis connection failed: {e}. Position rating caching will be unavailable."
)
yield
# Shutdown
logger.info("Shutting down Paper Dynasty Game Backend")
# Disconnect Redis
if redis_client.is_connected:
await redis_client.disconnect()
logger.info("Redis disconnected")
# Initialize FastAPI app
app = FastAPI(
title="Paper Dynasty Game Backend",
description="Real-time baseball game engine for Paper Dynasty leagues",
version="1.0.0",
lifespan=lifespan,
)
# CORS middleware
settings = get_settings()
app.add_middleware(
CORSMiddleware,
allow_origins=settings.cors_origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Initialize Socket.io
sio = socketio.AsyncServer(
async_mode="asgi",
cors_allowed_origins=settings.cors_origins,
logger=True,
engineio_logger=False,
)
# Create Socket.io ASGI app
socket_app = socketio.ASGIApp(sio, app)
# Initialize connection manager and register handlers
connection_manager = ConnectionManager(sio)
register_handlers(sio, connection_manager)
# Configure game engine with connection manager for real-time event emission
from app.core.game_engine import game_engine
game_engine.set_connection_manager(connection_manager)
# Include API routes
app.include_router(health.router, prefix="/api", tags=["health"])
app.include_router(auth.router, prefix="/api/auth", tags=["auth"])
app.include_router(games.router, prefix="/api/games", tags=["games"])
app.include_router(teams.router, prefix="/api/teams", tags=["teams"])
@app.get("/")
async def root():
return {"message": "Paper Dynasty Game Backend", "version": "1.0.0"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"app.main:socket_app", host="0.0.0.0", port=8000, reload=True, log_level="info"
)