1. Race condition: session_end now waits for an in-flight
session_start promise before sending, so quick restarts
don't drop the end call.
2. beforeunload: replaced sendBeacon (which can't set headers)
with fetch(..., keepalive: true) so the X-API-Key header
is included and the backend doesn't 401.
3. Stats double-counting: removed stats_record_damage_dealt
and stats_record_kill from damage_entity (which was called
for all damage including player deaths). Now only recorded
at player-sourced call sites (projectile hits, stomps).
4. Removed const-cast: analytics_session_end now takes
GameStats* (non-const) since stats_update_score mutates it.
5. beforeunload now uses stashed stats from the last C-side
session_end call instead of hardcoded zeroes. Session ID is
cleared synchronously before async fetch to prevent races.
6. Removed unused stdint.h include from stats.h.
Implements session-based analytics tracking that sends gameplay
stats to the Horchposten API. Adds stats.{c,h} for accumulating
per-session metrics (kills, deaths, shots, dashes, jumps, pickups,
damage, time) and analytics.{c,h} with EM_JS bridge for fetch()
calls to the backend. Client ID is persisted in localStorage.
Session start/end hooks are wired into all game lifecycle events
(level transitions, restart, quit, tab close via sendBeacon).
Analytics URL/key are configured via data attributes on the canvas
container. Non-WASM builds compile with no-op stubs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>