Concept: The Fragment Model
A save slot is not one file - it is a directory of fragments. Each registered system owns one fragment; the world subsystem owns per-level slabs; a header holds the slot's metadata. Nothing reads another system's fragment, which is what keeps plugins decoupled.
Glossary
| Concept | Description |
|---|---|
| Fragment | One .sav file per system per save slot. Written and read independently from all other systems. |
| Auto-serialization | Any UPROPERTY(SaveGame) field on the registered object is captured automatically via the engine's proxy archive - no custom fragment subclass needed. |
| Custom fragment | A UCrimsonSaveGameFragmentBase subclass for data auto-serialization can't hold (UObject references, GAS state, computed values). Runs alongside auto-serialization. |
| Global vs player-scoped | World fragments are shared across the session (world state, game progress). Player fragments exist once per player per slot, addressed by that player's profile GUID. Profile fragments live outside any slot and belong to the local machine, so a character can travel between servers. |
| Save slot | A directory under Saved/SaveGames/<Slot>/ containing all fragment files, world slabs, and a header. One slot = one playthrough. |
| Header | A metadata .sav per slot for the save-menu UI - display name, date, play time, slot type, optional thumbnail. |
How a save runs
- Register - each system calls
RegisterSaveableSystemon startup and returns a uniqueGetFragmentName. - Gather - on save the manager captures every
SaveGameproperty into a blob and callsGatherSaveDatafor an optional custom fragment. Both run for every system. - Write - the manager writes the header, one
.savper fragment, and (withbIncludeWorldStateInMainSave) the world slabs. - Match - on load each fragment file is matched to its registered system by name.
- Restore - the manager deserializes the
SaveGameblob, then callsRestoreFromSaveDatafor anything the custom fragment holds.
GatherSaveData returning nullptr just means "nothing beyond my SaveGame fields." Start with SaveGame properties; add a custom fragment only for what they can't hold. See How-To: Custom Fragments & Versioning.Saves and loads are asynchronous
Loads complete through delegates, not synchronously. Gate "loaded" UI and screen transitions on OnLoadComplete, and rebuild slot lists on OnSaveSlotListChanged. The one synchronous entry point is SaveGameToActiveSlotSynchronous, used for save-on-exit. See How-To: Handle Save/Load Errors.
On-disk layout
A slot is a set of independently stored blobs: one header, one blob per global fragment, one per player-scoped fragment, and one per level touched. Where those blobs live is decided by the storage provider, not by the save manager - see Concept: Where Saves Are Stored.
| Slot kind | Index range | Notes |
|---|---|---|
| Manual | 0 and up | Named playthroughs; the only slots GetAllSaveSlotHeaders and GetAllSaveSlotIndices return. |
| Auto-save | 10000-10009 | Rotating; oldest overwritten past MaxAutoSaves. List with GetRotatingSaveSlotIndices(AutoSave). |
| Quick-save | 10100-10109 | Rotating; oldest overwritten past MaxQuickSaves. List with GetRotatingSaveSlotIndices(QuickSave). |
bIncludeWorldStateInMainSave (default), a single RequestSaveProgress persists both registered systems (fragments) and world actors (slabs), and loading a slot restores both. See Concept: World State & Identity.