Add real-time radar feature for nearby objects

Browser can open a radar window per character that streams nearby
monsters, players, NPCs, portals, and other objects in real-time.
On-demand activation via start_radar/stop_radar commands through
the existing WebSocket command channel.

- Backend: nearby_objects event handler with in-memory cache and broadcast
- Frontend: canvas mini-map + entity list table in draggable window
- Radar button added to player list alongside Chat/Stats/Inventory/Char

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-04-07 23:00:53 +02:00
parent c0da36c280
commit 3852cf205e
3 changed files with 439 additions and 1 deletions

View file

@ -981,6 +981,7 @@ live_snapshots: Dict[str, dict] = {}
live_vitals: Dict[str, dict] = {}
live_character_stats: Dict[str, dict] = {}
live_equipment_cantrip_states: Dict[str, dict] = {}
live_nearby_objects: Dict[str, dict] = {}
# Shared secret used to authenticate plugin WebSocket connections (override for production)
SHARED_SECRET = "your_shared_secret"
@ -2677,6 +2678,13 @@ async def ws_receive_snapshots(
f"Invalid portal message format from {websocket.client}: missing required fields"
)
continue
if msg_type == "nearby_objects":
character_name = data.get("character_name")
if character_name:
live_nearby_objects[character_name] = data
await _broadcast_to_browser_clients(data)
continue
# Unknown message types are ignored
if msg_type:
logger.warning(
@ -2691,6 +2699,7 @@ async def ws_receive_snapshots(
for name in disconnected_names:
plugin_conns.pop(name, None)
live_equipment_cantrip_states.pop(name, None)
live_nearby_objects.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]