Fix review issues in analytics integration

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.
This commit is contained in:
2026-03-08 19:29:11 +00:00
parent a23ecaf4c1
commit 322dd184ab
5 changed files with 94 additions and 51 deletions

View File

@@ -286,23 +286,33 @@
}
/* ── Analytics: end session on tab close ────────────── */
/* Fallback for when the WASM shutdown path didn't get a chance to
* run (e.g. user closes tab mid-game). Uses fetch with keepalive
* so the browser can send the request after the page is gone, and
* includes the X-API-Key header that sendBeacon can't carry. */
window.addEventListener('beforeunload', function() {
if (typeof Module !== 'undefined' && Module._analyticsSessionId &&
Module._analyticsUrl) {
/* Use sendBeacon for reliability during page unload */
var sid = Module._analyticsSessionId;
var body = JSON.stringify({
Module._analyticsSessionId = null;
/* Use stashed stats from the last C-side update if available,
* otherwise send minimal data so the session isn't left open. */
var body = Module._analyticsLastStats || JSON.stringify({
score: 0,
level_reached: 1,
lives_used: 0,
duration_seconds: 0,
end_reason: 'quit'
});
var blob = new Blob([body], { type: 'application/json' });
navigator.sendBeacon(
Module._analyticsUrl + '/api/analytics/session/' + sid + '/end/',
blob
);
fetch(Module._analyticsUrl + '/api/analytics/session/' + sid + '/end/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': Module._analyticsKey
},
body: body,
keepalive: true
});
}
});
</script>