fix(engine): preserve saved game while restore prompt is unanswered
Quat reported the restore prompt didn't appear and noticed their save file ended up with move_count 0 — diagnosed as a destructive overwrite. The flow: 1. Player exits with moves; game_state.json has move_count > 0. 2. Player relaunches. Plugin build sees moves > 0, holds the saved game in `PendingRestoredGame`, seeds `GameStateResource` with a fresh deal so the board doesn't show the half-played game until the player picks Continue. 3. The restore prompt should appear. (Why it didn't on Quat's run is still TBD — needs a fresh test.) 4. Player exits. `save_game_state_on_exit` writes `GameStateResource` (the fresh-deal placeholder) to disk, overwriting the meaningful saved game with move_count 0. Both `save_game_state_on_exit` and `auto_save_game_state` now check `PendingRestoredGame`: if it still holds an unanswered saved game, they save THAT (or skip entirely in the auto-save path). The real saved game on disk is preserved across launches no matter how many times the player exits without answering the prompt. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1173,9 +1173,17 @@ fn auto_save_game_state(
|
|||||||
path: Option<Res<GameStatePath>>,
|
path: Option<Res<GameStatePath>>,
|
||||||
mut timer: ResMut<AutoSaveTimer>,
|
mut timer: ResMut<AutoSaveTimer>,
|
||||||
paused: Option<Res<crate::pause_plugin::PausedResource>>,
|
paused: Option<Res<crate::pause_plugin::PausedResource>>,
|
||||||
|
pending: Res<PendingRestoredGame>,
|
||||||
) {
|
) {
|
||||||
// Don't save if paused, game is won, or no moves have been made yet.
|
// Don't save if paused, game is won, no moves have been made yet,
|
||||||
if paused.is_some_and(|p| p.0) || game.0.is_won || game.0.move_count == 0 {
|
// or there's a pending restore the player hasn't answered — saving
|
||||||
|
// the fresh-deal placeholder we seeded GameStateResource with at
|
||||||
|
// startup would clobber the real saved game on disk.
|
||||||
|
if paused.is_some_and(|p| p.0)
|
||||||
|
|| game.0.is_won
|
||||||
|
|| game.0.move_count == 0
|
||||||
|
|| pending.0.is_some()
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
timer.0 += time.delta_secs();
|
timer.0 += time.delta_secs();
|
||||||
@@ -1192,17 +1200,25 @@ fn auto_save_game_state(
|
|||||||
/// player can resume where they left off. Won games are not saved (the
|
/// player can resume where they left off. Won games are not saved (the
|
||||||
/// `save_game_state_to` helper skips them). Blocking on exit is acceptable
|
/// `save_game_state_to` helper skips them). Blocking on exit is acceptable
|
||||||
/// because the game loop is already shutting down.
|
/// because the game loop is already shutting down.
|
||||||
|
///
|
||||||
|
/// Special case: when `PendingRestoredGame` still holds a saved game the
|
||||||
|
/// player never answered the restore prompt for, write THAT to disk
|
||||||
|
/// instead of the live `GameStateResource`. Otherwise we'd clobber a
|
||||||
|
/// real saved game with the fresh-deal placeholder we seeded
|
||||||
|
/// `GameStateResource` with at startup.
|
||||||
fn save_game_state_on_exit(
|
fn save_game_state_on_exit(
|
||||||
mut exit_events: MessageReader<AppExit>,
|
mut exit_events: MessageReader<AppExit>,
|
||||||
game: Res<GameStateResource>,
|
game: Res<GameStateResource>,
|
||||||
path: Res<GameStatePath>,
|
path: Res<GameStatePath>,
|
||||||
|
pending: Res<PendingRestoredGame>,
|
||||||
) {
|
) {
|
||||||
if exit_events.is_empty() {
|
if exit_events.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
exit_events.clear();
|
exit_events.clear();
|
||||||
let Some(p) = path.0.as_deref() else { return };
|
let Some(p) = path.0.as_deref() else { return };
|
||||||
if let Err(e) = save_game_state_to(p, &game.0) {
|
let to_save = pending.0.as_ref().unwrap_or(&game.0);
|
||||||
|
if let Err(e) = save_game_state_to(p, to_save) {
|
||||||
warn!("game_state: failed to save on exit: {e}");
|
warn!("game_state: failed to save on exit: {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user