diff --git a/src/game/analytics.c b/src/game/analytics.c index 7b40b33..30c1ebe 100644 --- a/src/game/analytics.c +++ b/src/game/analytics.c @@ -167,6 +167,20 @@ EM_JS(void, js_analytics_send_end, (int score, int level_reached, doEnd(sid, endReason, score, level_reached, lives_used, duration_secs); }); +/* Stash current stats into Module._analyticsLastStats so the + * beforeunload fallback sends real data if the tab is closed. */ +EM_JS(void, js_analytics_stash_stats, (int score, int level_reached, + int lives_used, int duration_secs), { + if (!Module._analyticsUrl || !Module._analyticsSessionId) return; + Module._analyticsLastStats = JSON.stringify({ + score: score, + level_reached: level_reached > 0 ? level_reached : 1, + lives_used: lives_used, + duration_seconds: duration_secs, + end_reason: 'quit' + }); +}); + /* ── C wrappers ─────────────────────────────────────────────────── */ void analytics_init(void) { @@ -188,6 +202,16 @@ void analytics_session_end(GameStats *stats, const char *end_reason) { ); } +void analytics_stash_stats(GameStats *stats) { + stats_update_score(stats); + js_analytics_stash_stats( + stats->score, + stats->levels_completed > 0 ? stats->levels_completed : 1, + stats->deaths, + (int)stats->time_elapsed + ); +} + #else /* ── Non-WASM stubs ─────────────────────────────────────────────── */ @@ -202,4 +226,8 @@ void analytics_session_end(GameStats *stats, const char *end_reason) { (void)end_reason; } +void analytics_stash_stats(GameStats *stats) { + (void)stats; +} + #endif /* __EMSCRIPTEN__ */ diff --git a/src/game/analytics.h b/src/game/analytics.h index cc55822..8bf99b8 100644 --- a/src/game/analytics.h +++ b/src/game/analytics.h @@ -15,4 +15,9 @@ void analytics_session_start(void); * end_reason: "death", "quit", "timeout", or "completed". */ void analytics_session_end(GameStats *stats, const char *end_reason); +/* Stash current stats for the beforeunload fallback so that + * closing the browser tab sends real data instead of zeros. + * Call periodically from the game loop. No-op on non-WASM. */ +void analytics_stash_stats(GameStats *stats); + #endif /* JNR_ANALYTICS_H */ diff --git a/src/main.c b/src/main.c index a718758..e686464 100644 --- a/src/main.c +++ b/src/main.c @@ -55,6 +55,8 @@ static int s_mars_depth = 0; /* ── Analytics / stats tracking ── */ static GameStats s_stats; static bool s_session_active = false; +static int s_stash_tick = 0; /* frames since last analytics stash */ +#define ANALYTICS_STASH_INTERVAL 60 /* stash stats every ~1 second */ /* ── Pause menu state ── */ #define PAUSE_ITEM_COUNT 3 @@ -262,6 +264,7 @@ static void begin_session(void) { stats_set_active(&s_stats); analytics_session_start(); s_session_active = true; + s_stash_tick = 0; } static void end_session(const char *reason) { @@ -581,6 +584,12 @@ static void game_update(float dt) { /* Accumulate play time */ if (s_session_active) { s_stats.time_elapsed += dt; + + /* Periodically stash stats for the beforeunload fallback. */ + if (++s_stash_tick >= ANALYTICS_STASH_INTERVAL) { + s_stash_tick = 0; + analytics_stash_stats(&s_stats); + } } /* Check for level exit transition */