05 — Save¶
Goal¶
Learn where inventory stops being special and becomes one section of a save file: capture every character in the scene through InventorySaveParticipant, hand it to RevSaveCoordinator alongside your own game state, and restore it.
What This Scene Demonstrates¶
The participant flow:
Scene characters → StableId lookup → per-character snapshot → one labelled section → coordinator → save payload
Two characters carry a CharacterInventory and a StableId. The participant finds both on its own — nothing in this scene points it at either of them.
A second participant defined inside the panel's own file writes ordinary quest flags into the same payload, so you can read both sections side by side and see that the coordinator does not treat them differently.
How This Differs From 04 — Snapshots¶
They are adjacent layers, not alternatives.
04 is the layer below: it captures one inventory you bind in the Inspector, and writes the JSON itself.
05 never names an inventory. It covers the whole scene, and the JSON it produces is a save file that also holds everything else your game persists.
If you only need to save one container to disk, 04 is the smaller answer and is enough.
What To Look For¶
- Who Gets Saved lists every character with both a
CharacterInventoryand aStableId. A character without an id is invisible to the participant, because a saved inventory has no way to find its way back - Capture produces the payload. Read it: one section is keyed
revframework.inventory, onedemo.questflags, and neither outranks the other - Inventory's JSON is nested inside the section as a string rather than expanded. Inventory owns its own format and its own schema version; re-modelling it at this layer would create a second copy of a format this layer does not own
- Change It, Then Restore shows that restore replaces the character's state with what the payload held. It does not undo your changes
- When The Id Does Not Match scrambles the live ids and restores. The section fails with a message naming the mismatch instead of quietly doing nothing, and the quest flags still load
- The Database Is Not Optional builds a participant with no
ItemDatabaseand shows the refusal - A Restore Can Succeed And Still Be Short toggles
MissingItemPolicy, which decides whether an item the database can no longer resolve is dropped or replaced with a placeholder.SubstitutePlaceholdersubstitutes only when theItemDatabasehas a Missing Placeholder assigned, and with none it behaves exactly likeSkip— the sample database carries one, so the two settings differ here, and a database of your own will not until you assign one
Scene Setup¶
- Two characters, each with
CharacterInventoryandStableId. Two rather than one because "the participant covers every character" is not visible with a single one - A
SceneInventoryServicewith anItemDatabaseassigned. The participant resolves saved item GUIDs through it and refuses to be constructed without one InventorySavePanelon its own object, with Seed Item assigned
If you duplicate a character to make the second one, add the StableId after duplicating. Copying a GameObject copies the id verbatim, and two objects sharing an id is the collision Tools ▸ RevGaming ▸ RevFramework ▸ Validate ▸ Duplicate Stable Ids exists to report.
Sample Scope¶
This scene focuses only on:
- Capturing every scene inventory through one participant
- Composing that with a non-inventory participant into a single payload
- Restoring, and reading the report the coordinator returns
- The StableId contract that makes a restore find its target
- Missing-item policy at the save layer
This scene does NOT cover:
- Writing the payload to disk — nothing here does any file I/O, on purpose
- Save slots, autosave, cloud saves or save-slot UI
- Save versioning beyond the participant's own version guard
- Spawned-object identity strategies
- Networking or replication
Where the bytes go is the game's decision. See RevSaveManager and FileSaveStore for the storage side.
Authority Note¶
This scene may include a permissive sample authority setup. If no authority is resolved, mutations are allowed for demonstration purposes.
Networking Reminder¶
No networking is included. Multiplayer authority and synchronization are the developer's responsibility.
How To Use¶
- Press Give Seed Items to put something in both characters worth losing
- Press Capture, and read the payload
- Give more items, then press Restore. The extra items go, because the payload did not have them
- Press Advance Quest Flags, capture again, and watch both sections move together
- Press Scramble Ids, then Restore. The inventory section fails and names the mismatch; the quest flags still load
- Press Put Ids Back, then Restore again. The section applies
- Press Build A Participant With No Database to see the constructor refuse
- Toggle Policy and restore, to choose what happens to items the database cannot resolve.
SubstitutePlaceholdersubstitutes because the sampleItemDatabasehas a Missing Placeholder assigned; clear that field and the policy silently falls back toSkip, which is what a database of your own does until you assign one - Read Last Result after each action
Failure Behaviour¶
Failures are reported through RevSaveReport rather than thrown. A load that throws is how a game ends up stuck on its main menu.
Common patterns:
-
No eligible characters → nothing in the scene has both
CharacterInventoryandStableId→ Add aStableId; the capture would otherwise write no inventory section at all -
The section fails naming every character → the ids in the save are not the ids in the scene → Usually the loaded scene is not the one the save was taken in. An object spawned at runtime gets a fresh id every launch unless
StableId.AssignIdis called with one of your own -
The participant refuses to be constructed → no
ItemDatabase→ Assign one onSceneInventoryService. Refusing here rather than at load time is deliberate: a null database would make every restore return false, which reads as a corrupt save rather than a missing reference -
The captured payload holds only
demo.questflags→ theSceneInventoryServicehas noItemDatabase, so the panel could not build the inventory participant at all → The panel warns and carries on with the quest flags alone rather than blocking. Capture is not exempt from the database requirement here — the requirement is on constructing the participant, and with no participant there is no inventory section to write. Assign a database to put inventory back in the payload -
A restore reports Ok and the character is short of items →
MissingItemPolicy.Skipdropped items the database no longer resolves → This is documented behaviour, not a failure, and the console warning is the only place the loss is visible — in the editor and in development builds. It is aDevDiagnosticswarning, so it compiles out of a release player: in the build your players run, this loss has no signal at all -
A restore reports a partial → some characters restored and others were refused → That section is no longer a description of anything. Do not carry it into a later save
Behind The Scenes¶
This panel uses public save and inventory APIs only:
InventorySaveParticipantRevSaveCoordinator.Capture(...)/RevSaveCoordinator.Restore(...)RevSaveReportIRevSaveParticipantStableId/StableIdOwners.Find<T>(...)SceneInventoryService.GiveExact(...)/Get(...)InventorySnapshotOptionsMissingItemPolicyCharacterInventoryItemStackContainerId
The participant is a translator, not a store. Inventory is unchanged by it, which is why it lives in a define-gated integration assembly rather than in the Inventory package.
Key Takeaway¶
A save participant is something your game assembles and hands to the coordinator, and the coordinator never parses what one writes — it labels the string and hands it back.
Your quest flags join the same file the framework's systems use, through the same interface, with no privilege difference between them.
The one thing that must be true for any of it to work is identity: a saved inventory finds its way home by StableId, and nothing else.