feat: stream live equipment cantrip states

This commit is contained in:
Erik 2026-03-13 08:59:43 +01:00
parent 0cb8e2f75a
commit a7e2d4d404
2 changed files with 90 additions and 103 deletions

46
main.py
View file

@ -979,6 +979,7 @@ app = FastAPI()
live_snapshots: Dict[str, dict] = {}
live_vitals: Dict[str, dict] = {}
live_character_stats: Dict[str, dict] = {}
live_equipment_cantrip_states: Dict[str, dict] = {}
# Shared secret used to authenticate plugin WebSocket connections (override for production)
SHARED_SECRET = "your_shared_secret"
@ -2131,6 +2132,7 @@ async def ws_receive_snapshots(
name = data.get("character_name") or data.get("player_name")
if isinstance(name, str):
plugin_conns[name] = websocket
live_equipment_cantrip_states.pop(name, None)
logger.info(f"📋 PLUGIN_REGISTERED: {name} from {websocket.client}")
continue
# --- Spawn event: persist to spawn_events table ---
@ -2526,6 +2528,22 @@ async def ws_receive_snapshots(
exc_info=True,
)
continue
# --- Equipment cantrip state: live-only overlay for mana panel ---
if msg_type == "equipment_cantrip_state":
try:
character_name = data.get("character_name")
if character_name:
live_equipment_cantrip_states[character_name] = data
await _broadcast_to_browser_clients(data)
logger.debug(
f"Updated equipment cantrip state for {character_name}"
)
except Exception as e:
logger.error(
f"Failed to process equipment_cantrip_state for {data.get('character_name', 'unknown')}: {e}",
exc_info=True,
)
continue
# --- Quest message: update cache and broadcast (no database storage) ---
if msg_type == "quest":
character_name = data.get("character_name")
@ -2639,6 +2657,12 @@ async def ws_receive_snapshots(
finally:
# Track plugin disconnection
_plugin_connections = max(0, _plugin_connections - 1)
disconnected_names = [
name for name, ws in plugin_conns.items() if ws is websocket
]
for name in disconnected_names:
plugin_conns.pop(name, None)
live_equipment_cantrip_states.pop(name, None)
# Clean up any plugin registrations for this socket
to_remove = [n for n, ws in plugin_conns.items() if ws is websocket]
@ -3035,6 +3059,28 @@ async def get_character_stats(name: str):
raise HTTPException(status_code=500, detail="Internal server error")
@app.get("/equipment-cantrip-state/{name}")
async def get_equipment_cantrip_state(name: str):
"""Return latest live equipment cantrip state overlay for a character."""
try:
data = live_equipment_cantrip_states.get(name)
if data:
return JSONResponse(content=jsonable_encoder(data))
return JSONResponse(
content={
"type": "equipment_cantrip_state",
"character_name": name,
"items": [],
}
)
except Exception as e:
logger.error(
f"Failed to get equipment cantrip state for {name}: {e}", exc_info=True
)
raise HTTPException(status_code=500, detail="Internal server error")
# -------------------- static frontend ---------------------------
# Custom icon handler that prioritizes clean icons over originals
from fastapi.responses import FileResponse