magicciv/.project/objectives/p0-12-save-load-autosave.md
Natalie a505267839 feat(@projects): update progress report UI and economy bridge logic
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-04-17 17:18:43 -07:00

4.6 KiB

id title priority status scope owner updated_at evidence
p0-12 Save / load + autosave on quit p0 done game1 shipwright 2026-04-17
src/game/engine/src/core/save_manager.gd
src/game/engine/src/entities/player.gd
src/game/engine/scenes/main/main.gd
src/game/engine/scenes/ui/ingame_menu.gd
src/game/engine/scenes/menus/load_game.gd
src/game/engine/tests/unit/test_save_manager.gd
src/game/engine/tests/integration/test_save_load_round_trip.gd
src/game/engine/scenes/tests/auto_play.gd
scripts/autoplay/test_save_resume.sh
src/simulator/crates/mc-turn/tests/serde_roundtrip.rs
src/simulator/crates/mc-core/src/wonder.rs
src/simulator/crates/mc-turn/src/game_state.rs

Summary

Save/load UI, autosave-on-quit hook, multi-slot naming, schema version with rejection of mismatches, and GDScript + Rust round-trip tests all shipped. The three serde regressions that reopened this objective on 2026-04-17 are closed:

  1. WonderId newtype serialized as an array, not a string — fixed by Testwright via #[serde(transparent)] at mc-core/src/wonder.rs:5.
  2. strategic_axes + TechState.progress non-deterministic key order — fixed by changing both fields from HashMap<String, _>BTreeMap<String, _> in mc-turn/src/game_state.rs. BTreeMap iteration is sorted, so JSON output is byte-stable across processes. The three #[ignore] attributes on game_state_json_roundtrip_is_stable, player_state_json_roundtrip_is_stable, and tech_state_json_roundtrip_is_stable have been removed; all pass.
  3. PlayerState.relations tuple-keyed map fails serde_json — fixed by a relations_as_pairs serde adapter module in mc-turn/src/game_state.rs that round-trips the map as Vec<((u8, u8), RelationState)>. The T2 fixture now populates a (0, 1) → Peace relation and asserts relations.is_empty() == false plus field preservation after round-trip.

Full mc-turn test suite green: 89 passed, 0 failed, 1 ignored (unrelated). All 5 tests in mc-turn/tests/serde_roundtrip.rs pass.

Acceptance

  • ✓ Save/load from main menu + in-game pause — scenes/menus/load_game.gd calls list_saves() + load_named_slot(); scenes/ui/ingame_menu.gd calls save_to_named_slot().
  • ✓ Autosave on quit — scenes/main/main.gd handles NOTIFICATION_WM_CLOSE_REQUESTSaveManagerScript.autosave() before quit().
  • ✓ Byte-identical T100 turn_stats after save-at-T50 + load-back — AUTO_PLAY_SAVE_AT=N writes mid_run.save + quits; AUTO_PLAY_LOAD_AUTOSAVE=<path> resumes from that save after world loads. Integration test harness: scripts/autoplay/test_save_resume.sh. SaveManager.load_from_path() added for absolute-path loads.
  • ✓ GUT round-trip test covers new fields — tests/integration/test_save_load_round_trip.gd (9 tests incl. traded_luxuries, clan_id, GameState.diplomacy, wonders_built).
  • SCHEMA_VERSION rejection — save_manager.gd:SCHEMA_VERSION = 2; _read_slot rejects mismatches via ERR_FILE_UNRECOGNIZED. Test: test_wrong_schema_version_rejected.
  • NEW 2026-04-17: Rust serde round-trip of PlayerState.wonders_builtWonderId(pub String) was missing #[serde(transparent)]; fixed at mc-core/src/wonder.rs:5. Full workspace cargo test green on apricot after fix (11 crates, ~700 tests). Surfaced + fixed by Testwright.
  • 2026-04-17: HashMap → BTreeMap for deterministic save orderingPlayerState.strategic_axes: BTreeMap<String, u8> at mc-turn/src/game_state.rs:89 and TechState.progress: BTreeMap<String, u32> at mc-turn/src/game_state.rs:202. Call sites in processor.rs, snapshot.rs, victory.rs, processor_invariants.rs, gpu/mod.rs, bridge_contract_tests.rs, api-gdext/src/{lib,ai}.rs, mc-sim/src/{lib,bin/*}.rs, and the integration tests updated to match. Previously-#[ignore]'d tests now pass: game_state_json_roundtrip_is_stable, player_state_json_roundtrip_is_stable, tech_state_json_roundtrip_is_stable at mc-turn/tests/serde_roundtrip.rs.
  • 2026-04-17: PlayerState.relations diplomacy round-triprelations_as_pairs serde adapter at mc-turn/src/game_state.rs:19-35 serializes BTreeMap<(u8, u8), RelationState> as Vec<((u8, u8), RelationState)>. Annotated via #[serde(default, with = "relations_as_pairs")] at mc-turn/src/game_state.rs:149. Fixture in mc-turn/tests/serde_roundtrip.rs::populated_player now inserts a (0, 1) → Peace relation with populated peaceful_turns/trade_turns; test asserts !restored.relations.is_empty() and that the round-tripped values match.

Non-goals

  • Cloud saves (Game 2).
  • Save-file encryption or DRM.