Choose when loads happen

A manager loads on its own when its asset comes into scope, and again after a slot change. A load that failed is retried every Auto Load Retry Delay seconds, until it lands.

Your own loads run on top of those:

PersistenceManager pm = playerData.PersistenceManager;

pm.Load();                                               // fire and forget
pm.Load(result => StartGame());                          // callback when done
LoadResult syncResult = pm.LoadSync();                   // forced synchronous, result inline
LoadResult asyncResult = await pm.LoadAsync();           // async
PersistenceOperation<LoadResult> op = pm.LoadRoutine();  // coroutine: yield return it,
yield return op;                                         //   then read op.Result

Persistence.LoadAll();                                   // every object in scope at once

With Auto Load off, the manager reads nothing until you call Load(), and refuses every save until it has.

To veto a load, subscribe to the manager's BlockLoad hook.

Choose when saves happen

A manager saves on its own at four moments: when a persistent asset leaves scope, when the game loses focus or pauses, before a slot change, and every Regular Save Delay seconds.

Your own saves run on top of those. Every way of calling Load works for Save too:

pm.Save();                                  // fire and forget
SaveResult result = await pm.SaveAsync();   // async
// ...

With Auto Save off, a crash or a task switch loses everything since your last save.

To veto a save from outside a persistent asset, subscribe to the manager's BlockSave hook:

// Blocks every save, the quit-time one included
manager.BlockSave += source => inCutscene;

// Blocks only the saves your code asked for
manager.BlockSave += source => (source == PersistenceSource.Manual);

React to load and save

To react from outside a persistent asset, subscribe to the manager's events. OnBeforeLoad and OnAfterLoad bracket a load, and save and clear have the same pairs.

To react from inside a persistent asset, implement one of two interfaces:

Save on quit

A shutdown drain holds the quit open while saves are still in flight, so a game that quits mid-save finishes writing it. The manager's Save Timeout bounds how long that lasts, as do Max Drain Time In Editor and Max Drain Time In Build, in Project Settings > Persistent Asset.

The static Persistence class reports and controls the drain.

Reset an asset or restore a snapshot

ResetToDefaults() puts the asset back to its authored values in memory, and Clear() does that and deletes the stored save as well.

SaveData.Snapshot(manager) hands you the asset's values as a string, and SaveData.Restore(manager, snapshot) puts them back, which covers an undo step or a checkpoint your game holds itself.

To reset single fields rather than the whole asset, implement IFieldResettable, whose DefaultValuesRequested() hands back the authored values as a SaveNode.

Optional hooks

Each of the following is optional, and the package picks it up on its own when your class carries it.

HookDescription
IDirtyTrackedExpose bool IsDirty { get; set; } and set it when you change a field: every save is skipped while it is false, and a save that lands clears it.
IDynamicInitializeRuns the first time an asset loads with no save.
IPersistencePolicyFixes Auto Load, Auto Save, Use Slot and Always Global for every asset of the type, over the manager's own settings.
IUpgradableReads a save written by an older build field by field, so a change to your class doesn't strand it. For more information, refer to Version your data.
IMergeableFolds another device's save into this one instead of discarding either. For more information, refer to Merge two saves.
[RequiredSerializer]Locks the serializer when the data only works with one.