beasty-save-system / reference / components.md
Components
The two MonoBehaviours of the save system. BeastySaveable marks a GameObject as part of a scene save;
BeastySaveManager holds the settings and does the saving. Both live in the Beasty_SaveSystem namespace.
BeastySaveable
Add Component: Beasty > Beasty Saveable. Marked [DisallowMultipleComponent].

Marks a GameObject as part of scene saves: a stable id, plus the list of components whose state
SaveAll captures and LoadAll restores.
Inspector fields
| Field | Type | Meaning |
|---|---|---|
Save Id (id) | string | Stable identifier keying this object inside save files. Auto-generated; editable — changing it orphans data saved under the old id. |
Saved Components (components) | List<Component> | Components on this GameObject captured by SaveAll and restored by LoadAll. |
The custom inspector shows the Save Id with a New button that regenerates it, and the components as a
checklist of every component on the GameObject, each labelled with the layer that converts it (dev, a
module id, or core). Ticking a component with no converter shows a warning: SaveAll will fail with
TypeUnavailable.
Reset (the component’s default state, on Add Component) ticks the Transform.
Public members
public string Id { get; }
The current id.
public IReadOnlyList<Component> SavedComponents { get; }
The selected components, with destroyed and missing entries filtered out.
public void EnsureId()
Generates a GUID id when the id is empty. An existing id is never overwritten.
public void SetId(string value)
Pins the id. Throws ArgumentException when value is empty. This is how a runtime-spawned object is
matched to its data across sessions: give it the same id you gave it last time.
Lifecycle
- Registers on
OnEnable. Unregisters onOnDestroy— not onOnDisable. A deactivated object (a boss that has not spawned yet, a door that only exists at night) is still saved and still restored. - A prefab asset carries no id: it is cleared in the editor, because every instance would otherwise inherit the same one and all but the first would collide. Each scene instance stamps its own.
- An object you
Instantiateat runtime gets a fresh id every time, so its state cannot be found again in an older save. UseBeastySaveManager.Register(go, "a.stable.id", components)for spawned objects. - Two saveables with the same id: the second one is not registered and an error is logged. It falls out of the save.
BeastySaveManager
Add Component: Beasty > Beasty Save Manager. Marked [DisallowMultipleComponent].
The scene-level entry point of the no-code flow. It holds the settings, tracks every registered
BeastySaveable, and writes one group document per save.
The inspector
The manager’s inspector is a summary view: a status card (the active storage backend with a
Local/Cloud badge, the user provider and current user when one is registered, and — in Play Mode — the
last save and load results), then the two decisions that matter up front — Storage and Save Mode —
with everything else behind a collapsed Advanced settings foldout, and an Open Save Manager button
to the full window.

Inspector fields
| Field | Type | Meaning |
|---|---|---|
Storage (settings.StorageId) | string | Where saves are written, as a dropdown of the registered backends. Local file is the default. See Storage backends. |
Save Mode (saveMode) | BeastySaveMode | How SaveAll/LoadAll/DeleteSlot run: Synchronous (default — blocks until done) or Asynchronous (runs in the background, reports through the events). Locked to Asynchronous with a cloud backend. |
Settings (settings) | BeastySaveSettings | Location, encryption, backup, strict loading and data version used by SaveAll/LoadAll — the Advanced settings foldout. |
Logs (logs) | BeastySaveLogMode | How much the save system prints: Auto (on in the editor and development builds, off in release), On, Verbose, Off. Applied on OnEnable and OnValidate, so changing it in Play takes effect at once. See Logging. |
Every field of BeastySaveSettings is documented in Settings.
Instance and state
public static BeastySaveManager Instance { get; }
public BeastySaveSettings Settings { get; }
public BeastySaveMode SaveMode { get; set; }
public SaveResult LastSaveResult { get; }
public LoadResult LastLoadResult { get; }
Instance is set in OnEnable and cleared in OnDisable. A second manager in the scene logs a warning
and does not take over. LastSaveResult and LastLoadResult hold the outcome of the most recent
SaveAll/LoadAll, for UI that polls instead of subscribing.
public event Action<SaveResult> SaveCompleted;
public event Action<LoadResult> LoadCompleted;
Raised after every SaveAll and LoadAll with the typed outcome.
Saving and loading
public void SaveAll(string slot)
public void LoadAll(string slot)
UnityEvent-friendly. They take a single string and return void, so you can wire them straight to a uGUI
Button’s OnClick with the slot name typed into the inspector, and never write a line of C#. The outcome
arrives through SaveCompleted/LoadCompleted, LastSaveResult/LastLoadResult, and the log. See
Save without code.
Save Mode decides how they run: Synchronous blocks until done, Asynchronous runs in the background
and reports through the same events. On an asynchronous-only backend they always take the asynchronous
path, whatever the mode says — a Save Mode of Synchronous still routes asynchronously (nothing is ever
lost) and logs a warning once per session asking you to set the mode to Asynchronous to acknowledge it.
public SaveResult SaveAllNow(string slot, IDictionary<string, string> meta = null)
public LoadResult LoadAllNow(string slot)
The same operations, returning the typed result directly. meta is the plain-text dictionary a slot screen
reads back with BeastySave.ReadMeta. These ignore Save Mode — a code caller chooses explicitly — and on
an asynchronous-only backend they return BackendRequiresAsync.
public Task<SaveResult> SaveAllNowAsync(string slot, IDictionary<string, string> meta = null)
public Task<LoadResult> LoadAllNowAsync(string slot)
The async twins. Same capture, envelope, logging and event flow; LoadAllNowAsync applies the fetched
data onto the scene objects only after the await returns to Unity’s main thread. See
Async saving.
public void DeleteSlot(string slot)
Deletes the slot file and its backup. Also UnityEvent-friendly, and routed by Save Mode like the other two.
The saveable registry
public static BeastySaveable Register(GameObject target, params Component[] components)
public static BeastySaveable Register(GameObject target, string id, params Component[] components)
Adds (or reuses) a BeastySaveable on target, sets its component selection, and registers it. Passing no
components keeps the current selection. Both throw ArgumentNullException when target is null; the
second throws ArgumentException when id is empty.
Use the id overload for anything you spawn at runtime. Without a pinned id the object gets a fresh GUID
on every Instantiate and its saved state can never be found again. Give it an id that is stable across
sessions: its spawn point, its map cell, its quest key.
public static void SyncSceneSaveables()
Registers every BeastySaveable in the loaded scenes, including those on inactive GameObjects. Called
automatically before every capture and every apply. Saveables register from OnEnable, which an object
that starts disabled never runs — without this they would be silently missing from the save.
public static void Unregister(GameObject target)
public static void UnregisterAll()
Unregister drops one object. UnregisterAll empties the registry — for when you want a clean slate up
front (swapping the whole world, a test fixture).
Embedding a scene save in your own file
public static SaveResult CaptureGroupNode(out JsonNode node)
public static LoadResult ApplyGroupNode(JsonNode node, bool strict)
CaptureGroupNode builds the group document — exactly what SaveAll writes — as an in-memory JsonNode,
so a host system can nest the whole scene state inside its own save file. It returns Ok with a null node
when nothing is registered, or a typed failure naming the culprit component.
ApplyGroupNode applies such a node back onto the registered saveables. A null or JSON-null node is a
no-op success. Errors: Corrupt (the document shape is wrong), TypeUnavailable, FieldMapFailed.