menu_bookDocumentation

Sandbox: SaveSystem — scene diff save/load with versioning and ISaveEvents

calendar_today May 4, 2026 schedule ~1 min read person patrickjr verified 50

Sandbox SaveSystem — Scene Diff Save/Load

The SaveSystem captures the difference between the current scene state and the original scene file. This means saves are compact — only changed/added/removed objects are stored.

Save format

JSON
{
  "Version": 2,
  "Metadata": { "key": "value" },
  "Packages": ["org.package1", "org.package2"],
  "SceneProperties": { ... },
  "Objects": [ ... diff object definitions ... ]
}

Key API

CSHARP
public sealed class SaveSystem : GameObjectSystem<SaveSystem>, ISceneLoadingEvents
{
    public static int SaveVersion => 2;
    public string LoadedSavePath { get; private set; }
    public bool HasLoadedSave => LoadedSavePath is not null;

    // Save current scene state to a file
    public async Task<bool> Save( string path ) { ... }

    // Load a save file — host only
    public async Task<bool> Load( string path ) { ... }

    // Metadata API
    public void SetMetadata( string key, string value ) { ... }
    public string GetMetadata( string key, string defaultValue = null ) { ... }
    public IReadOnlyDictionary<string, string> GetAllMetadata() { ... }

    // Read metadata without loading the full save
    public static IReadOnlyDictionary<string, string> GetFileMetadata( string path ) { ... }

    // Read save version without loading
    public static int GetFileSaveVersion( string path ) { ... }
}

Load flow

  1. Read the save JSON and extract the diff
  2. Mount any required packages listed in the save
  3. Broadcast a loading screen to all clients
  4. Set _suppressSystemScene = true to prevent duplicate system scenes
  5. Call CleanupSystem.PreserveBaselineForSaveLoad()
  6. Build a patched scene file from the diff and call Game.ChangeScene()

ISaveEvents

CSHARP
public interface ISaveEvents
{
    void BeforeSave( string filename ) { }
    void AfterSave( string filename ) { }
    void BeforeLoad( string filename ) { }
    void AfterLoad( string filename ) { }
}

Implement Global.ISaveEvents on a GameObjectSystem or Component to hook into save/load lifecycle. The GameManager uses AfterLoad to re-spawn any players not included in the save.

Gotchas

Was this helpful?