Concept: Where Saves Are Stored

The save manager decides what to save and serializes it down to bytes. A storage provider decides where those bytes go. Nothing in the save manager knows about files, paths, or platforms - so retargeting storage never means touching save logic, and a cloud backend is a small class rather than a fork of the save system.

You probably do not need to change this
The default provider routes through the engine's ISaveGameSystem, which is the platform's own save system. It is already correct on PC and on console, including per-user save containers and platform-managed cloud sync. Read this page when you want saves somewhere else - not to get them working.

The two halves

LayerOwnsExamples
UCrimsonSaveGameManagerSubsystemWhat to save: slots, fragments, world slabs, versioning, player scoping, serialization to bytes.RequestSaveProgress, RegisterSaveableSystem, fragment upgrades.
UCrimsonSaveStorageProviderWhere the bytes live: write, read, delete, exists, enumerate.The platform save system, a directory of files, an HTTP API.

Choose the provider in Project Settings -> Crimson -> Crimson Save Game Manager -> Storage Provider Class. The manager instantiates it once at startup and calls InitializeStorage before any save access. If the configured class is unset or fails to initialize, the manager logs an error and falls back to the platform provider rather than silently discarding saves.

Shipped providers

ProviderBacking storeUse it for
UCrimsonPlatformSaveStorageProvider (default)Engine ISaveGameSystemEverything, unless you have a specific reason not to. On PC this resolves to the engine's generic file system (Saved/SaveGames/*.sav); on console it resolves to that platform's save system module and writes into the platform save container, honoring its user binding and native save UI.
UCrimsonFileSaveStorageProviderPlain files under a directory you configurePutting saves somewhere specific on PC - a Documents subfolder, a Steam Auto-Cloud path, or separate directories per build. Bypasses ISaveGameSystem, so it is a PC-oriented choice.

The blob model

A provider never sees a USaveGame, a UObject, or a path. It is a keyed byte store. The key is FCrimsonSaveBlobId, which carries everything needed to name, find, or delete one piece of save data:

FieldTypeMeaning
SlotFCrimsonSaveSlotIdSlot kind (Manual / AutoSave / QuickSave / Global) plus a number. This is the decoded form of the public int32 slot index, so providers never deal with the index-offset encoding.
BlobKindECrimsonSaveBlobKindHeader, Fragment, WorldSlab, or UserSettings.
NameFStringFragment name for Fragment blobs, level name for WorldSlab blobs. Empty otherwise.
PlayerKeyFStringSet only for player-scoped fragments. Empty means the blob is shared by all players.
Why enumeration matters
The manager discovers what a slot contains by calling EnumerateBlobs - it never scans a directory. That is what lets a backend with no filesystem at all (a console container, an HTTP API) work unchanged. A provider's EnumerateBlobs must return ids whose Name and PlayerKey round-trip exactly.

Storage naming

Both shipped providers store blobs flat - one blob per name, no subdirectories - with the identity encoded in the name:

text
Slot0_H header for manual slot 0
Auto1_H header for auto-save slot 1
Slot0_F_Inventory global fragment "Inventory" in manual slot 0
Slot0_F_Inventory__Key the same fragment scoped to player "Key"
Slot0_W_MainLevel world state for level "MainLevel" in manual slot 0
Global_S user plugin settings

Flat is deliberate, and it is the reason the layout looks the way it does. Console save systems treat the name as an opaque container key and reject path separators outright. On top of that, the engine's own ISaveGameSystem::GetSaveGameNames does not recurse into subdirectories - so a nested layout could not be enumerated even on PC.

Reserved sequence: double underscore
A fragment name, level name, or player key must not contain a double underscore (__) - it delimits the player key in the stored name. The manager validates this and logs an error rather than writing a blob it cannot read back. Single underscores are fine (My_Fragment, PIE_Server).

Cloud saves

"Cloud save" means two very different things depending on the platform, and only one of them needs code from you.

PlatformHow it worksNeeds a custom provider?
PlayStation / Switch / XboxThe platform syncs its own save container. The default provider writes into that container, so sync is automatic.No
Steam (Auto-Cloud)Steam syncs files matching a pattern you register in the Steamworks partner site. Point it at the save directory; the plugin is not involved.No
Steam Cloud APIReads and writes through ISteamRemoteStorage rather than the filesystem.Yes
Epic Online ServicesReads and writes through EOS Player Data Storage.Yes
Custom serverYour own HTTP API.Yes

The pattern: if the platform syncs a container or directory for you, the default provider is enough. If the backend is a service API, it needs a provider - see How-To: Change Where Saves Are Stored.

Per-user storage

On console, save data is bound to a signed-in platform user. UCrimsonPlatformSaveStorageProvider::SetUserIndex selects which local user's storage to use; it defaults to 0, the primary user. This is distinct from the player save key, which scopes fragments within a slot for multiplayer - see Concept: Multiplayer & Player Scoping. One is which storage container to open; the other is which fragment inside it belongs to whom.

See also

  • How-To: Change Where Saves Are Stored
  • Concept: The Fragment Model
  • Concept: Multiplayer & Player Scoping
  • API Reference -> Storage providers