91 lines
3.1 KiB
Python
91 lines
3.1 KiB
Python
|
|
from fastapi import WebSocket
|
||
|
|
from typing import Dict, Set
|
||
|
|
import uuid
|
||
|
|
|
||
|
|
class WebSocketManager:
|
||
|
|
"""Manages WebSocket connections for multiplayer"""
|
||
|
|
|
||
|
|
def __init__(self):
|
||
|
|
self.active_connections: Dict[str, WebSocket] = {}
|
||
|
|
self.player_nicknames: Dict[str, str] = {}
|
||
|
|
self.nickname_to_id: Dict[str, str] = {}
|
||
|
|
|
||
|
|
async def connect(self, websocket: WebSocket, nickname: str):
|
||
|
|
"""Connect a new player"""
|
||
|
|
await websocket.accept()
|
||
|
|
|
||
|
|
# Generate or reuse player ID
|
||
|
|
if nickname in self.nickname_to_id:
|
||
|
|
player_id = self.nickname_to_id[nickname]
|
||
|
|
else:
|
||
|
|
player_id = str(uuid.uuid4())
|
||
|
|
self.nickname_to_id[nickname] = player_id
|
||
|
|
|
||
|
|
self.active_connections[player_id] = websocket
|
||
|
|
self.player_nicknames[player_id] = nickname
|
||
|
|
|
||
|
|
# Broadcast player joined
|
||
|
|
await self.broadcast({
|
||
|
|
"type": "player_joined",
|
||
|
|
"player_id": player_id,
|
||
|
|
"nickname": nickname
|
||
|
|
})
|
||
|
|
|
||
|
|
async def disconnect(self, websocket: WebSocket):
|
||
|
|
"""Disconnect a player"""
|
||
|
|
player_id = None
|
||
|
|
for pid, ws in self.active_connections.items():
|
||
|
|
if ws == websocket:
|
||
|
|
player_id = pid
|
||
|
|
break
|
||
|
|
|
||
|
|
if player_id:
|
||
|
|
del self.active_connections[player_id]
|
||
|
|
nickname = self.player_nicknames.pop(player_id, None)
|
||
|
|
|
||
|
|
# Broadcast player left
|
||
|
|
await self.broadcast({
|
||
|
|
"type": "player_left",
|
||
|
|
"player_id": player_id,
|
||
|
|
"nickname": nickname
|
||
|
|
})
|
||
|
|
|
||
|
|
async def broadcast(self, message: dict, exclude: WebSocket = None):
|
||
|
|
"""Broadcast message to all connected players"""
|
||
|
|
disconnected = []
|
||
|
|
|
||
|
|
for player_id, websocket in self.active_connections.items():
|
||
|
|
if websocket == exclude:
|
||
|
|
continue
|
||
|
|
|
||
|
|
try:
|
||
|
|
await websocket.send_json(message)
|
||
|
|
except Exception:
|
||
|
|
disconnected.append(player_id)
|
||
|
|
|
||
|
|
# Clean up disconnected websockets
|
||
|
|
for player_id in disconnected:
|
||
|
|
if player_id in self.active_connections:
|
||
|
|
del self.active_connections[player_id]
|
||
|
|
if player_id in self.player_nicknames:
|
||
|
|
del self.player_nicknames[player_id]
|
||
|
|
|
||
|
|
async def broadcast_game_state(self, state: dict):
|
||
|
|
"""Broadcast full game state"""
|
||
|
|
await self.broadcast({
|
||
|
|
"type": "game_state_update",
|
||
|
|
"state": state
|
||
|
|
})
|
||
|
|
|
||
|
|
async def get_player_id(self, websocket: WebSocket) -> str:
|
||
|
|
"""Get player ID from websocket"""
|
||
|
|
for player_id, ws in self.active_connections.items():
|
||
|
|
if ws == websocket:
|
||
|
|
return player_id
|
||
|
|
return None
|
||
|
|
|
||
|
|
async def get_nickname(self, websocket: WebSocket) -> str:
|
||
|
|
"""Get nickname from websocket"""
|
||
|
|
player_id = await self.get_player_id(websocket)
|
||
|
|
return self.player_nicknames.get(player_id, "Unknown")
|