Documentation / Tile Tick Core RPG / Programmer

Persistence & Save Modules

How server-authoritative save capture, restore ordering, character lifecycle, and custom save modules work.

Persistence is modular. SaveCoordinator owns capture/validation/serialization/storage lifecycle, while feature-specific IPlayerSaveModule implementations own sections of player data.

Save lifecycle

Character identity assigned
PlayerSaveContext registered
Server loads snapshot
Modules restore in RestoreOrder
Gameplay mutates authoritative state
Save requested / disconnect / shutdown
Modules capture live state
Snapshot validated + checksum
Serialized payload written atomically

IPlayerSaveModule

namespace GreenCloakGames.TileTickRPGCore.SaveSystem
{
    public interface IPlayerSaveModule
    {
        string ModuleId { get; }
        int RestoreOrder { get; }
        void Capture(PlayerSaveData data, SaveContext context);
        void Restore(PlayerSaveData data, LoadContext context);
    }
}

New persistent systems should add a save DTO/section and a module rather than editing every save caller. RestoreOrder is deterministic so dependencies can be restored before systems that rely on them.

ISaveCoordinator extension surface

  • SaveAsync and LoadAsync own disk lifecycle.
  • CaptureRuntimeState builds an in-memory snapshot without disk I/O. Travel uses it to carry complete runtime state across a scene change.
  • ApplyRuntimeState restores the in-memory snapshot to the currently registered player before travel overrides arrival position.
  • Save/load events are available for diagnostics or presentation, but should not become alternate state stores.

Server authority

NetworkBootstrap installs SaveAuthority gates so normal clients cannot write authoritative saves while a network session is active. ServerCharacterPersistence owns networked character lifecycle.

Adding custom persistent data

  1. Add a stable serialized field/DTO to the save model for your feature.
  2. Implement IPlayerSaveModule.
  3. Choose a unique ModuleId.
  4. Choose a restore order based on dependencies.
  5. Capture only server-authoritative live state.
  6. Restore through the same runtime component/service that owns the feature.
  7. Register the module using the existing save-module bootstrap/registry pattern.
  8. Add migration logic if shipped data changes shape later.
Lifecycle rule: never defer capture until after player despawn/unregistration. The coordinator cannot serialize authoritative player data that no longer exists.