create a save system #225

Closed
opened 2026-09-09 09:09:38 +00:00 by metaheavens · 1 comment
Owner

save system should propose to load existing save. a system to upgrade saves from different version should also be available to take into account new versions. ensure to clarify in this system that it's sometimes fine to not be compatible with previous versions if the transitions are too complex, and just make previous versions incompatible.

save system should propose to load existing save. a system to upgrade saves from different version should also be available to take into account new versions. ensure to clarify in this system that it's sometimes fine to not be compatible with previous versions if the transitions are too complex, and just make previous versions incompatible.
Author
Owner

Done in fb35871.

Format. JSON at user://savegame.json rather than a binary resource: a save that can be opened in a text editor is diagnosable from a bug report, and ResourceSaver would tie the file's shape to GDScript class layout — the thing that keeps changing.

The actual work was that the run is not plain data. A tagged codec carries Vector2, Vector2i, PackedStringArray and the Inventory objects nested inside buildings. explored_chunks is keyed by Vector2i and JSON has no such key, so every dictionary key carries its own type prefix (v2i:3,-4) — without that a planet id could come back as a chunk coordinate.

What is saved is an explicit list of 30 fields, not a dump of GameState's 58 vars. A dump would carry transient junk (which pointer last touched the screen, whether a dialog holds the camera) and would break silently on every rename. The cost is that new progression must be added to the list; the benefit is that forgetting is a missing feature, not a corrupt save.

Versioning, and the incompatibility policy you asked for. SAVE_VERSION bumps on shape changes. MIGRATIONS walks a save up one version at a time. MIN_SUPPORTED_VERSION is how a break too messy to migrate gets declared rather than bodged — a gap in the migration chain is not an oversight, it is the statement that older saves are incompatible.

A save that cannot be walked forward is refused by name, with a player-readable reason. That is the reading I took of "sometimes fine to not be compatible": a save that loads subtly wrong is far worse than one that refuses and says why. Adding a brand-new field needs no bump at all — it arrives missing from old saves and keeps its new-game value, which case F tests.

The prompt. The menu offers Continue only when there is something to offer, so a first-time player never meets a button that does nothing. A save this build cannot open is still shown, disabled, with the reason underneath — silently hiding someone's run reads as lost rather than incompatible.

Saving is automatic: every 30s, plus on close and on Android's app-pause, since the phone kills backgrounded apps with no warning. Skipped during the prologue. No save-on-demand button, by decision — automatic only.

Tests. test_save_game.gd (round-trip field-for-field, Vector2i keys, inventory contents and capacity, newer-save refusal, damaged-file refusal, missing-field tolerance) and test_main_menu_continue.gd (offered only when a save exists, resumes rather than resets, unopenable save shown disabled with a reason). Verified by breaking the codec: neutering the key decoder fails with chunk keys came back as [v2i:-11,0, v2i:3,-4].

One thing worth recording: the autosave writes to the same user:// path every test that loads Main.tscn shares, so it could make Continue appear and shift the menu stack under test_main_menu depending on what ran first. That test clears the save before building the menu.

Not included: no route back to the menu from in-game, so a resume currently needs a relaunch.

175/175.

Done in fb35871. **Format.** JSON at `user://savegame.json` rather than a binary resource: a save that can be opened in a text editor is diagnosable from a bug report, and `ResourceSaver` would tie the file's shape to GDScript class layout — the thing that keeps changing. **The actual work** was that the run is not plain data. A tagged codec carries `Vector2`, `Vector2i`, `PackedStringArray` and the `Inventory` objects nested inside `buildings`. `explored_chunks` is keyed by `Vector2i` and JSON has no such key, so every dictionary key carries its own type prefix (`v2i:3,-4`) — without that a planet id could come back as a chunk coordinate. **What is saved** is an explicit list of 30 fields, not a dump of GameState's 58 vars. A dump would carry transient junk (which pointer last touched the screen, whether a dialog holds the camera) and would break silently on every rename. The cost is that new progression must be added to the list; the benefit is that forgetting is a missing feature, not a corrupt save. **Versioning, and the incompatibility policy you asked for.** `SAVE_VERSION` bumps on shape changes. `MIGRATIONS` walks a save up one version at a time. `MIN_SUPPORTED_VERSION` is how a break too messy to migrate gets *declared* rather than bodged — a gap in the migration chain is not an oversight, it is the statement that older saves are incompatible. A save that cannot be walked forward is refused **by name, with a player-readable reason**. That is the reading I took of "sometimes fine to not be compatible": a save that loads subtly *wrong* is far worse than one that refuses and says why. Adding a brand-new field needs no bump at all — it arrives missing from old saves and keeps its new-game value, which case F tests. **The prompt.** The menu offers Continue only when there is something to offer, so a first-time player never meets a button that does nothing. A save this build *cannot* open is still shown, disabled, with the reason underneath — silently hiding someone's run reads as lost rather than incompatible. **Saving** is automatic: every 30s, plus on close and on Android's app-pause, since the phone kills backgrounded apps with no warning. Skipped during the prologue. No save-on-demand button, by decision — automatic only. **Tests.** `test_save_game.gd` (round-trip field-for-field, Vector2i keys, inventory contents *and* capacity, newer-save refusal, damaged-file refusal, missing-field tolerance) and `test_main_menu_continue.gd` (offered only when a save exists, resumes rather than resets, unopenable save shown disabled with a reason). Verified by breaking the codec: neutering the key decoder fails with `chunk keys came back as [v2i:-11,0, v2i:3,-4]`. One thing worth recording: the autosave writes to the same `user://` path every test that loads `Main.tscn` shares, so it could make Continue appear and shift the menu stack under `test_main_menu` depending on what ran first. That test clears the save before building the menu. **Not included:** no route back to the menu from in-game, so a resume currently needs a relaunch. 175/175.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
metaheavens/spaceship#225
No description provided.