Stats double-counted on game forfeit — record_abandoned() fires twice #21

Closed
opened 2026-05-19 18:43:05 +00:00 by funman300 · 0 comments
Owner

File

solitaire_engine/src/stats_plugin.rs lines 596–613 (update_stats_on_new_game), 620–645 (handle_forfeit)

Description

handle_forfeit calls stats.0.record_abandoned() then writes a NewGameRequestEvent. Both handle_forfeit and update_stats_on_new_game are registered .before(GameMutation) with no ordering between them. When Bevy schedules handle_forfeit first, update_stats_on_new_game reads the NewGameRequestEvent in the same frame, checks game.0.move_count > 0 && !game.0.is_won (still true — GameMutation hasn't run yet), and calls record_abandoned() a second time. Every forfeit doubles games_played and games_lost.

Fix

Add .before(update_stats_on_new_game) to handle_forfeit's system registration so update_stats_on_new_game cannot observe the forfeit's NewGameRequestEvent in the same frame.

## File `solitaire_engine/src/stats_plugin.rs` lines 596–613 (`update_stats_on_new_game`), 620–645 (`handle_forfeit`) ## Description `handle_forfeit` calls `stats.0.record_abandoned()` then writes a `NewGameRequestEvent`. Both `handle_forfeit` and `update_stats_on_new_game` are registered `.before(GameMutation)` with no ordering between them. When Bevy schedules `handle_forfeit` first, `update_stats_on_new_game` reads the `NewGameRequestEvent` in the same frame, checks `game.0.move_count > 0 && !game.0.is_won` (still true — `GameMutation` hasn't run yet), and calls `record_abandoned()` a second time. Every forfeit doubles `games_played` and `games_lost`. ## Fix Add `.before(update_stats_on_new_game)` to `handle_forfeit`'s system registration so `update_stats_on_new_game` cannot observe the forfeit's `NewGameRequestEvent` in the same frame.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: funman300/Ferrous-Solitaire#21