arena: fix UDP snap drops + add cmd/snap rate telemetry
Client side (lib/udp.mjs):
Geckos on the wire serializes via {"<event>": payload}. When the server
emits an object (as arena-manager does with snaps), the client's `on`
handler receives the payload already as a parsed object — not a string.
respond() was calling JSON.parse(content) unconditionally, which throws
on objects ("[object Object]" is invalid JSON) and silently dropped every
arena:snap. That's why remote figures appeared at their first WS-delivered
position and then went stale once UDP bound and the server stopped
falling back to WS. Guard the JSON.parse behind a typeof check.
Server side (arena-manager.mjs):
Add minimal telemetry to diagnose the matching cmd-direction issue:
log the first cmd received per handle, count cmd rx + snap tx per player,
and dump a 5-second rate summary. Cheap to leave in — ~1 line every 5s
when the arena is occupied.
Also bump sw cache to v7 so clients pick up the fresh udp.mjs on reload
instead of riding out another stale-while-revalidate cycle.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>