API Reference
Every public method in the AvanatroTools.SaveStack namespace, grouped by surface. Side-effects, events, and exceptions are noted with badges — no surprises after install.
How to read this page
Each method shows its full signature, a one-sentence description, and badges for what to expect at runtime:
- async returns a
Task/Task<T>, acceptsCancellationToken - events raises
OnSaved/OnLoaded/OnSaveFailed/OnLoadFailed - side-effect writes to storage on a Read-path (only versioned-slot pre-migration backups)
- throws can throw under normal use; check description for which exception
- editor Editor-only API (inside
UNITY_EDITOR)
SaveSystem — main slot facade
Static entry point for all gameplay save operations. Slot-based (default slot = 0). All public methods serialize through the per-slot SemaphoreSlim; async paths hold the lock across await.
Configuration
Set or swap providers. All four are independently optional — leave a parameter null to keep the current provider.
static void Configure(ISerializer serializer = null, IStorageProvider storage = null, IEncryptionProvider encryption = null, IPathProvider pathProvider = null, MigrationHandler migrations = null)
Replaces one or more providers. Resets the slot cache so the next access lazily rebuilds against the new providers.
static void DisableVersioning()
Drops the MigrationHandler from the current configuration. New saves are written as raw JSON without an envelope.
static SaveSlot GetSlot(int index = 0)
Returns the cached SaveSlot instance for the given index. Slots are created lazily on first access.
Save & Load (sync)
Default path for gameplay code. Throws on serialization / storage failure — wrap with TryLoad if you need branch-on-error.
static void Save<T>(string key, T data, int slot = 0)
Serializes, encrypts, and writes data under key. Then writes slot meta (__meta) atomically under the same lock.
static T Load<T>(string key, int slot = 0)
Reads, decrypts, deserializes the value stored under key. Returns default(T) when the key is missing.
static bool TryLoad<T>(string key, out T value, out string error, int slot = 0)
Non-throwing load. Returns true + populated value on success; false + descriptive error when the key is missing or any error occurs.
static bool HasKey(string key, int slot = 0)
Returns true when a value is stored under the given key in the given slot. Does not touch the storage backend beyond a single Exists check.
static bool HasValidSlot(int slot)
Returns true when the slot's __meta record can be successfully read and deserialized. Shallow check — individual user keys are not validated.
Save & Load (async)
Identical semantics to the sync API. I/O is dispatched via IAsyncStorageProvider when the storage supports it (e.g. FileStorage), otherwise offloads to Task.Run.
static async Task SaveAsync<T>(string key, T data, int slot = 0, CancellationToken ct = default)
Async Save. Serialization + encryption run inline on the caller thread; the write itself is awaited.
static async Task<T> LoadAsync<T>(string key, int slot = 0, CancellationToken ct = default)
Async Load. Throws KeyNotFoundException when the key is absent — use TryLoadAsync if you need the non-throwing form.
static async Task<(bool success, T value)> TryLoadAsync<T>(string key, int slot = 0, CancellationToken ct = default)
Non-throwing async load. Returns a tuple; OnLoadFailed still fires on errors but is not re-thrown.
Slot inventory & metadata
Use these for "Load Game" menus, save-thumbnail listings, and admin tooling.
static IReadOnlyList<SlotInfo> GetAllSlots()
Enumerates every slot directory currently under the configured root path. Returns one SlotInfo per slot — index, meta record, key list (excluding reserved keys).
static SlotInfo GetSlotInfo(int slot)
Inventory of a single slot. Reads the __meta record and lists user keys (no full decrypt of each value).
static SlotMeta GetSlotMeta(int slot = 0)
Returns the slot's meta record (SlotIndex / LastScene / SavedAt / Description / CustomFields / TotalPlaytimeTicks) or null if no meta exists.
static IEnumerable<int> GetUsedSlots()
Returns the sorted set of slot indices currently in use. Skips empty slots without a __meta record.
static void SetSlotDescription(int slot, string description)
Sets the user-facing slot description (shown in load menus). Atomic Read-Modify-Write on __meta — does not clobber other meta fields.
static string GetSlotDescription(int slot)
Returns the previously-set slot description, or null when none is set.
static void SetSlotMetaField(int slot, string key, string value)
Sets a custom meta field (key/value). Useful for save-screen extras: playtime label, character class, location name, etc.
static string GetSlotMetaField(int slot, string key)
Reads a previously-set custom meta field. Returns null on miss.
static void RemoveSlotMetaField(int slot, string key)
Removes a previously-set custom meta field. No-op when the key is absent.
static long GetTotalPlaytime(int slot = 0)
Returns the slot's accumulated playtime in DateTime.Ticks. Increment with PlaytimeTracker for monotonic-clock-safe tracking.
Slot lifecycle
Destructive operations — guard with confirmation in the UI.
static void DeleteSlot(int slot)
Deletes every key in the slot, including reserved keys (__meta, __quests, __flags, scene buckets, backups).
static void DeleteAll()
Wipes the entire storage root — every slot, plus the settings folder (when sharing a root). Recoverable only from filesystem backups.
Pre-migration backups
Recovery API for schema migrations gone wrong. Backups are written transparently by Load when a slot's stored version is below the current — this surface lets you list and roll back.
static bool RestoreBackup(int slot, string key, int fromVersion)
Restores the slot/key from its .premigration_v{fromVersion}.bak. Returns false when no such backup exists. Does not raise save/load events — restore is a maintenance operation.
static IReadOnlyList<int> ListBackupVersions(int slot, string key)
Returns the sorted list of pre-migration backup versions that exist for the given slot/key. Empty list when no backups exist.
Flag store
First-class boolean flags per slot — for "met NPC", "tutorial seen", "world state unlocked". Persisted under the reserved key __flags; reads use a TryLoad-fallback so a corrupt flag store does not throw.
static void SetFlag(string flag, bool value, int slot = 0)
Sets the boolean flag for the given slot. Read-Modify-Write under the slot lock — multiple parallel SetFlag calls are safe.
static bool GetFlag(string flag, int slot = 0)
Returns the flag value, or false when not set. Never throws.
static bool HasFlag(string flag, int slot = 0)
Distinguishes "flag is set to false" from "flag was never set". Useful for tutorial gates.
static void ClearFlag(string flag, int slot = 0)
Removes the flag entirely. Subsequent HasFlag returns false.
static IReadOnlyDictionary<string, bool> GetAllFlags(int slot = 0)
Returns the full flag map for the slot. Snapshot — safe to enumerate concurrently with SetFlag.
Scene-state bucket
Storage layer for SaveSystemSceneController and the saver components. Usually not called directly — use SlotSave / SlotLoad or the SaveSystemSceneController instead.
static IReadOnlyDictionary<string, string> GetSceneState(string sceneName, int slot = 0)
Returns the captured state of every Saver in the named scene, keyed by Saver.GetUniqueKey().
static void SetSceneState(string sceneName, IReadOnlyDictionary<string, string> state, int slot = 0)
Writes a scene-state bucket back to the slot. The scene's SaveSystemSceneController calls this on save.
SlotSave / SlotLoad — high-level scene capture
The "press the save button" / "press the load button" API. Iterates every SaveSystemSceneController currently in the loaded scene(s) and triggers their scene-state capture / restore.
static int SlotSave(int slot)
Captures every SaveSystemSceneController's scene state into the given slot. Returns the number of controllers processed.
static int SlotLoad(int slot)
Restores every SaveSystemSceneController's scene state from the given slot. Returns the number of controllers processed.
Path-traversal validation
Used internally by Save / Load. Exposed publicly so callers can validate user-supplied keys before forwarding.
static void SaveSlot.ValidateUserKey(string key)
Throws ArgumentException when key contains .., /, \, null bytes, or a Windows drive letter. Safe pre-flight for user-input-driven save keys.
Events
Subscribe to observe save/load lifecycle. Subscriber exceptions are swallowed — your subscribers cannot break the save pipeline.
static event Action<int, string> OnSaved
Fired after a successful Save / SaveAsync. Arguments: (slot, key).
static event Action<int, string> OnLoaded
Fired after a successful Load / LoadAsync / TryLoad. Arguments: (slot, key).
static event Action<int, string, Exception> OnSaveFailed
Fired on save failure. Arguments: (slot, key, exception). The exception is re-thrown after — subscribers run synchronously before the throw.
static event Action<int, string, Exception> OnLoadFailed
Fired on load failure or key-not-found (for TryLoad). Arguments: (slot, key, exception).
SettingsSystem — non-slot global settings
Parallel facade for game settings (audio volume, quality, key bindings, language). No slots, no versioning, no events, no migration. Get is exception-free. Storage root defaults to <persistentDataPath>/settings/, kept separate from slot data. All operations are serialized through a static lock — safe to call from multiple threads.
Read & Write
static void Set<T>(string key, T value)
Stores value under key. Throws on serialization / storage failure — settings writes are considered fatal.
static T Get<T>(string key, T defaultValue = default)
Reads + decrypts + deserializes. Returns defaultValue when the key is missing OR any read / decrypt / deserialize step fails. Never throws.
static bool HasKey(string key)
Returns true when a value is stored under key.
static void Delete(string key)
Removes the value stored under key. No-op when missing.
static void Clear()
Removes every setting. Equivalent to the "Reset to Defaults" button at the top of a settings screen.
static IEnumerable<string> Keys
Materialized snapshot of every settings key. Stable across Set / Delete / Clear calls that happen during iteration.
static void Configure(ISerializer serializer = null, IStorageProvider storage = null, IEncryptionProvider encryption = null, IPathProvider pathProvider = null)
Swap providers. Like SaveSystem.Configure but the storage defaults to <root>/settings/ when only a path provider is given.
QuestStore — RPG quest tracking
Optional sub-module under AvanatroTools.SaveStack.Quests (separate asmdef). Persists under the reserved slot key __quests. Active/completed/failed states with stage progression, counters, and arbitrary key/value variables.
Construction
new QuestStore(int slot = 0)
Creates a quest store bound to slot. State is loaded lazily on first access. Multiple stores against the same slot share state via __quests.
Lifecycle
void Start(string questId)
Marks the quest as Active and timestamps it. No-op if already Active.
void Advance(string questId, int newStage)
Sets the quest stage. Active-only — throws if the quest is not currently Active.
void Complete(string questId)
Marks Complete + timestamps. Idempotent on already-completed quests.
void Fail(string questId)
Marks Failed + timestamps. Idempotent on already-failed quests.
Counters & variables (per quest)
void IncrementCounter(string questId, string counterKey, int by = 1)
"Kill 10 boars": store.IncrementCounter("boar_quest", "boars_killed"). Active-only.
int GetCounter(string questId, string counterKey)
Returns the counter value, or 0 when missing.
void SetVariable(string questId, string key, string value)
Arbitrary per-quest key/value: "chosen_dialogue_branch" = "trader_kind". Active-only.
string GetVariable(string questId, string key)
Returns the variable value, or null when missing.
Queries & persistence
QuestState Get(string questId)
Returns the quest's full state record (status / stage / counters / variables / timestamps), or null when the quest has never been started.
IEnumerable<QuestState> GetActive() / GetCompleted()
Enumerates all quests currently Active / Completed. Use for quest-log UIs.
bool IsActive(string questId) / IsComplete(string questId)
Short-form status checks. Useful in dialog tree conditionals.
void Persist() / Reload()
Persist flushes pending changes to __quests; Reload re-reads from disk and discards in-memory state. Use Persist from your save trigger, Reload from your load trigger.
Saver components — scene-state drop-ins
Add to GameObjects in the Inspector. The scene's SaveSystemSceneController captures and restores them on SaveScene / LoadScene (or via SaveSystem.SlotSave / SlotLoad).
Saver — base class
Inherit if you need a custom saver. Override RecordState + ApplyState; the rest comes for free.
virtual string GetUniqueKey()
Returns OverrideKey when set, otherwise {sceneName}/{hierarchyPath}/{typeName}. The basis for cross-scene-stable persistence.
abstract string RecordState()
Capture the current GameObject state as a serialized string. Called at save time.
abstract void ApplyState(string serialized)
Restore from a previously-recorded string. Called at load time, before the next frame.
public string OverrideKey
Optional explicit key. Set this when you need cross-scene persistence on a GameObject that gets re-instantiated.
Built-in saver components
Drop-in components for the common cases. Each is one file, one purpose, no hidden state.
PositionSaver / RotationSaver
Captures + restores transform.position / transform.rotation. World-space.
ActiveSaver / EnabledSaver
ActiveSaver: GameObject's activeSelf. EnabledSaver: target Component's enabled field.
AnimatorParametersSaver
Captures all Animator parameters (Float / Int / Bool / Trigger) and current state hash per layer.
RigidbodySaver
Captures linearVelocity + angularVelocity + position/rotation (Unity 6 API; falls back via RigidbodyState container).
SaveSystemSceneController
One per scene. Maintains a static registry of all Saver instances; orchestrates capture / restore.
public int TargetSlot
The slot index this controller saves into / loads from. Inspector-exposed.
public bool AutoRestoreOnLoad
When true, restores scene state automatically on SceneManager.sceneLoaded. Default true.
public void SaveScene() / LoadScene()
Manual triggers — hook up to UI buttons or save-checkpoint events.
static void Register(Saver saver) / Unregister(Saver saver)
Called automatically by Saver.Awake / OnDestroy. Rarely called by user code — useful for runtime-instantiated savers that bypass Awake.
AutoSaver — periodic + lifecycle saves
Optional MonoBehaviour for "auto-save every N seconds" and "save on quit". One per scene.
public int TargetSlot
Slot index to save into. Default 0.
public float IntervalSeconds
Auto-save interval. Set to 0 to disable periodic saves (keep lifecycle hooks active).
public bool SaveOnApplicationQuit
Trigger a final save on OnApplicationQuit. Default true.
public bool SaveOnApplicationPause
Trigger save on OnApplicationPause(true) — mobile-recommended. Default true.
public bool SaveOnDisable
Trigger save on OnDisable. Useful for sub-scenes / mini-games.
public UnityEvent OnAutoSave
Inspector-wireable event raised before each auto-save. Hook UI "saving…" indicators here.
public void TriggerSave()
Programmatic trigger — bypass the timer + call out to SaveSystem.SlotSave(TargetSlot) immediately.
PlaytimeTracker
Monotonic-clock playtime accumulation. Robust against system-clock jumps + sleep/wake. Caps absurd intervals (>24h between flushes) defensively.
public int SlotIndex
The slot index this tracker accumulates into. Read-only after construction.
public void SetSlot(int slot)
Switches the target slot. Flushes pending ticks to the old slot first.
public void Flush()
Forces an accumulation pass — reads elapsed ticks via Stopwatch.GetTimestamp, calls SaveSystem.GetSlot(slot).AccumulatePlaytime, resets the timer.
Provider interfaces — swap the engine room
Four single-responsibility interfaces. Implement and pass to SaveSystem.Configure when the defaults don't fit. None of the four depend on the others.
ISerializer
Object ↔ JSON. Default: JsonNetSerializer (Newtonsoft, TypeNameHandling.Auto, Unity converters for Vector3/Color/Quaternion/Rect).
string Serialize<T>(T value)
Converts a value to its JSON string representation.
T Deserialize<T>(string json)
Parses JSON back to a typed instance.
IStorageProvider
byte[] ↔ filesystem-or-equivalent. Defaults: FileStorage (atomic), PlayerPrefsStorage (WebGL/mobile fallback). Optionally implement IAsyncStorageProvider for native async I/O.
void Write(string key, byte[] data) / byte[] Read(string key)
Core byte-level write/read. Read returns null when the key is absent.
bool Exists(string key) / Delete(string key) / Clear()
Existence check, single-key delete, whole-storage wipe.
IEnumerable<string> List(string prefix = null)
Enumerates all keys, optionally filtered by prefix. Used by slot listings + scene controllers.
IEncryptionProvider
byte[] ↔ byte[]. Defaults: NoEncryption, AesEncryption (AES-256-CBC, prod default), AesGcmEncryption (AES-GCM, IL2CPP only in v1.0).
byte[] Encrypt(byte[] plaintext)
Encrypts and returns the cipher bytes. Implementations are expected to handle their own IV / nonce generation.
byte[] Decrypt(byte[] cipher)
Decrypts back to plaintext. Implementations should accept their own past output without breaking Backwards-Compat across versions.
IPathProvider
Returns the root directory under which slot folders are created. Default: UnityPathProvider → Application.persistentDataPath/saves.
string GetRootDirectory()
Returns the absolute path to the save-root. Slots are folders below, e.g. <root>/slot_0/.
v1.1 New APIs
All additions are fully backwards-compatible. v1.0 code compiles and runs unchanged.
Streaming Save/Load (P.1)
Chunked async I/O for payloads over 50 MB. Peak RAM stays under 2× chunk size regardless of total payload.
static async Task SaveStreamAsync<T>(int slot, string key, T data, StreamSaveOptions options = null, IProgress<StreamProgress> progress = null, CancellationToken ct = default)
Serializes data in chunks and streams to storage. Use when a single-shot SaveAsync would spike RAM on large world states.
static async Task<T> LoadStreamAsync<T>(int slot, string key, IProgress<StreamProgress> progress = null, CancellationToken ct = default)
Reads and deserializes in chunks. Fires progress with phase label, bytes read, and percent complete.
class StreamSaveOptions { int ChunkSizeBytes = 2097152; }
Configuration for streaming writes. Default chunk is 2 MB. Larger chunks reduce I/O round-trips at the cost of higher peak memory.
class StreamProgress { string Phase; long BytesProcessed; double? PercentComplete; }
Progress token passed to IProgress<StreamProgress>. PercentComplete is null when total size is unknown.
Save-Batching / Transactions (P.2)
Write multiple keys atomically via 2-phase commit. Either all writes succeed or none are visible.
static BatchResult SaveBatch(int slot, IReadOnlyList<BatchEntry> entries)
Synchronous batch write. All entries commit together; on any failure the slot state is rolled back to its pre-batch state.
static bool TrySaveBatch(int slot, IReadOnlyList<BatchEntry> entries, out BatchResult result)
Non-throwing variant. Returns false on failure; result.Error carries the exception.
static async Task<BatchResult> SaveBatchAsync(int slot, IReadOnlyList<BatchEntry> entries, CancellationToken ct = default)
Async batch write. I/O is awaited per chunk; rollback is synchronous on failure.
class BatchEntry { string Key; object Data; Type DataType; }
One entry in a batch. Use BatchEntry.For<T>(key, value) factory for typed construction.
class BatchResult { bool Success; int KeysWritten; Exception Error; }
Result of a batch operation. KeysWritten is 0 on failure (all-or-nothing semantics).
Security Stack (Z.1, Z.2)
HMAC-SHA256 tamper detection and HKDF-derived machine-bound encryption keys. Both are optional — pass to SaveSystem.Configure.
class HmacSha256Provider : IHmacProvider { public HmacSha256Provider(byte[] hmacKey); }
Appends a 32-byte HMAC-SHA256 tag to every save. Throws HmacVerificationFailedException on load when the tag does not match (format byte 0x07 on disk).
interface IHmacProvider { byte[] Sign(byte[] data); bool Verify(byte[] data, byte[] tag); }
Implement to supply a custom HMAC algorithm. SaveSystem.Configure(hmac: provider) accepts it.
static class MachineBoundKey { static byte[] DeriveForLocalMachine(byte[] masterKey, string info = "savestack-v1"); }
Derives a 32-byte AES key via HKDF-SHA256 using masterKey + the device’s IMachineIdentityProvider output as salt. Save files copied to another machine fail to decrypt.
interface IMachineIdentityProvider { byte[] GetMachineId(); }
Default implementation reads SystemInfo.deviceUniqueIdentifier. Swap with a custom provider for dedicated hardware tokens or server-issued IDs.
static class Hkdf { static byte[] DeriveKey(byte[] ikm, byte[] salt, byte[] info, int length = 32); }
Public HKDF-SHA256 primitive exposed for callers who need custom key derivation outside of MachineBoundKey.
Window → SaveStack → Repair Save File
Save-File-Repair Tool (Z.3) — EditorWindow that runs 5 diagnostic checks (header, HMAC, encryption, migration version, slot meta) and offers single-click repair or export. Also available headless via CLI: dotnet run --project SaveStackRepair.csproj -- --slot 0.
Test Framework (M.1, Q.1, Q.2)
Fluent migration TDD, stress benchmarking, and fuzz mutation — all runnable from NUnit or Unity Test Runner.
class MigrationTestBed
Fluent Given/When/Then builder for migration handler tests. MigrationTestBed.Given(version, json).When(handler).Then(expectedVersion).AssertJsonPath(path, value).Run(). Supports handler chains for multi-step migrations.
class StressTestRunner { static StressResult Run(StressOptions options); }
Executes a configurable number of concurrent save/load cycles and collects P50/P95/P99/StdDev latency statistics. Reports the first failure with its iteration index and exception.
class FuzzerEngine { static FuzzResult RunAll(FuzzOptions options); }
Applies 5 byte-level mutators (BitFlip, ByteDeletion, RandomInjection, HeaderCorruption, Truncation) against every save format and asserts graceful exception behavior. Suitable for CI gating.
class StressOptions { int Iterations = 10000; int Concurrency = 4; int PayloadSizeBytes = 1024; }
Configure iteration count, concurrency level, and payload size for stress runs. Results include per-iteration latency histogram.
class FuzzOptions { int MutationsPerFormat = 100; bool IncludeHeaderMutations = true; }
Configure fuzz depth and which mutation categories to apply. IncludeHeaderMutations = false skips format-byte corruption for faster smoke tests.
Editor tools
In UNITY_EDITOR only — separate asmdef so they're not shipped to runtime.
SaveSystemWindow
Inspect / delete / export slots. Window → SaveStack → Inspect Saves. Pure-C# SaveSystemWindowViewModel for testability — covered by 13+ unit tests.
Window → SaveStack → Inspect Saves
Slot list, key detail, JSON-decoded preview (when not encrypted), Delete + Export-JSON actions.
SaveWizard
Bulk-add saver components to a Selection. Window → SaveStack → Save Wizard.
Window → SaveStack → Save Wizard
Toggle Position / Rotation / Active / Enabled / Animator / Rigidbody, optional overrideKey prefix, "Add to N selected GameObjects" button. Snapshot-diff prevents overwriting existing overrideKeys.