DTWorldz

Saving a Systemic Homestead

How DTWorldz approaches Unity saves with stable IDs, ordered restoration, schema migration, runtime entities and atomic backup recovery.

Jun 6, 20267 min readBy Deniz TrakaUpdated Aug 31, 2026

SystemsArchitectureUnityAshen Hearth

A Unity save system becomes difficult when it has to reconstruct a connected world rather than deserialize one player object. Ashen Hearth’s design needs time, weather, inventories, world objects and runtime-spawned entities to agree after loading.

The implementation reference in this article is DTWorldz’s related reusable Unity game/template foundation. It treats a save file as a versioned contract: handlers own separate sections, persistent entities have explicit identity, restoration follows a declared order, and the file store validates both the primary save and its backup before accepting either.

Stable Identity Comes Before Saved Values

The first persistence question is not “which fields should be serialized?” It is “which entity will receive those fields when the world is rebuilt?”

Scene-authored objects, runtime-spawned objects and mobile entities do not all return through the same path. A transform position is not an identity, and a Unity instance ID is not a durable save key. If two objects can exchange positions or be destroyed independently, location alone cannot tell them apart.

The inspected DTWorldz save architecture uses explicit IDs for registered save sections and persistent world entities. Registration rejects an empty ID and also rejects a duplicate ID instead of silently allowing the later handler to overwrite the first.

This catches one of the most damaging save bugs early. Without that guard, two systems can appear to save correctly while only one survives in the resulting dictionary.

Runtime identity introduces another distinction:

  • a definition ID says what kind of thing this is;
  • an instance ID says which particular thing this is;
  • saved state says what happened to that instance.

All three may be required to recreate a spawned entity. A prefab catalog or factory can resolve the definition, while the persistent instance ID lets later state and references reconnect to the correct object.

Save Handlers Own Small Contracts

The save manager does not need to know the private fields of every system. It registers handlers through a shared interface, asks each handler to capture state, and stores the result under that handler’s SaveID.

Current sections cover concerns such as game time, player state, hotbar state, world maps, world objects and mobile entities, with additional world and location data integrated through their own handlers.

This structure has two benefits:

  1. A system can validate its own semantic state before the file is written.
  2. The manager can detect a missing required section before presenting the save as playable.

Serialization success alone is not enough. Valid JSON can still describe an impossible world. The current validation path checks required sections and delegates deeper checks to world, object, mobile, player, dungeon and location codecs where those sections are present.

Load Order Is A Reconstruction Plan

Loading is not the inverse of writing in one automatic step. Some systems have to exist before others can interpret their state.

The current manager orders registered handlers by an explicit LoadOrder. That allows foundational state to be restored before dependent state without encoding the sequence in scene hierarchy or registration timing.

A useful reconstruction model is:

  1. Read and validate the file envelope.
  2. Migrate older data in memory when a known migration path exists.
  3. Restore foundational clocks, world structure and registries.
  4. Materialize or match persistent entities.
  5. Apply component and inventory state.
  6. Resolve cross-references and dependent presentation.
  7. Resume normal simulation only after the world is coherent.

The exact handlers can evolve, but the principle stays the same: a crop should not calculate elapsed growth against an uninitialized clock, and a player location should not reference a dungeon instance that failed validation.

Schema Versions Make Change Explicit

The reference JSON root is wrapped in an envelope containing a schemaVersion and a data dictionary. At the time of this revision, that migration pipeline’s current schema version is 2.

The pipeline recognizes older roots, applies registered migrations one version at a time and validates the resulting current envelope. It also distinguishes an invalid file from a file created by a newer game version.

That last case is important. Attempting to guess how to open future data can corrupt a valid save. The safer response is to stop and tell the player that the current game version cannot open it safely.

Migrations are applied in memory before restoration. The older file is not considered current merely because it could be parsed. A missing migration step produces an explicit failure instead of skipping versions and hoping field names still line up.

Atomic Writes And Backup Recovery

A correct in-memory snapshot can still be lost during disk writing. The current file store writes to a same-directory temporary file, flushes it, then atomically replaces the primary file. The previous successful primary is retained with a .bak suffix.

On read, the store validates the primary first. If that file is damaged and the backup is valid, it can open the backup and attempt to repair the primary. A damaged primary is retained with a .corrupt suffix during successful recovery instead of being silently discarded.

This does not make saving impossible to lose—storage failure, permissions and platform behavior still exist—but it removes the risky “truncate the only good file and write directly into it” path.

The same principle applies to deletion: primary, temporary, backup and corrupt recovery files belong to one save lifecycle and need deliberate cleanup.

Runtime-Spawns And Deletions Are Both State

Saving only objects that currently exist misses half of persistence. If the player destroys or collects a scene-authored object, loading the untouched scene can bring it back unless deletion is recorded.

Conversely, a runtime-spawned entity needs enough data to be materialized again before its component state is applied.

For each persistent entity category, the save design therefore needs an explicit answer to four cases:

  • authored and unchanged;
  • authored and modified;
  • authored and removed;
  • created at runtime.

This is where stable identity and catalogs meet. A world-object record can describe an instance, while the registered definition tells the factory what to create. Unknown definitions should fail visibly or follow a documented compatibility rule; silently substituting a different prefab creates harder corruption later.

Restoration Must Not Replay Gameplay

Loading state is not the same operation as performing the original action again.

Restoring fuel should not consume another inventory item. Restoring a planted crop should not emit a new planting reward. Restoring a construction site should not deliver its materials twice. Presentation may need a refresh, but normal interaction side effects must remain suppressed during reconstruction.

This is why dedicated capture/restore contracts are safer than trying to drive every system through its public gameplay buttons during load.

What Still Needs Ongoing Testing

Persistence is never “finished” after one successful round trip. Every new saved system needs at least these cases:

  • new game without a file;
  • save, change the world, then load;
  • quit and load in a fresh process;
  • interrupted or malformed primary file with a valid backup;
  • unsupported future schema;
  • missing required section;
  • duplicate or unknown entity identity;
  • older schema migration;
  • deletion and runtime-spawn restoration;
  • load after the project’s content definitions have changed.

The player-facing success condition is intentionally boring: after loading, the homestead should be coherent enough that attention returns to food, fire, weather and exploration.

For how time-based crop state uses that foundation, read Farming Before Empire. For a smaller persistent object with strong visual feedback, read Keeping the Hearth Alive. The standalone Save System by DTWorldz is a separate Unity utility product; this article documents a related DTWorldz game/template architecture rather than claiming the product, Ashen Hearth and the reference implementation are identical.

Written by Deniz Traka, founder of DTWorldz. These notes document systems, tools, and production decisions used in DTWorldz projects.