How-To: Custom Fragments & Versioning
Goal: persist data that a SaveGame property can't hold - UObject references, GAS state, computed values - with a custom fragment, and evolve its schema safely as your game changes.
ICrimsonSaveableSystem and is registered (see Quick Start: Saveable Systems). You only need this when UPROPERTY(SaveGame) auto-serialization isn't enough.1. Subclass the fragment
Subclass UCrimsonSaveGameFragmentBase and add the fields you want to control by hand. A Blueprint subclass is a normal asset; a C++ subclass lives in your Public/ header.
Content Browser -> right-click -> Blueprint Class -> search Crimson Save Game Fragment Base -> create, and name it something like BP_MyInventoryFragment. In My Blueprint -> Variables, press + once per field you want to control by hand - for this example an Item Ids array of Name, and an Item Counts map of Name to Integer.
Leave the SaveGame flag alone on these. A fragment's own variables are written because they are on the fragment; the flag is what pulls fields off the system the fragment belongs to.
UPROPERTY(SaveGame) fields regardless - a custom fragment is additive. GatherSaveData may return nullptr to rely entirely on auto-serialization, or return a fragment for the data auto-serialization can't reach. Both paths run together.2. Return it from GatherSaveData, read it in RestoreFromSaveData
On the saveable object - not the fragment - open My Blueprint -> Interfaces and double-click Gather Save Data. In its graph: Construct Object from Class (Class = your fragment Blueprint, Outer = Self) -> Set each of its fields from your live data -> Return Node (Return Value = the constructed object). Returning nothing is valid too; it just means you are relying on SaveGame auto-serialization alone.
Then double-click Restore From Save Data, which appears as an event with a Fragment input. Cast To your fragment Blueprint on Fragment, and apply the fields back on the success path. Fragment is never null, but the cast can still fail if an older save carried a different fragment class, so use the cast's failure pin rather than assuming it succeeded.
GetFragmentName grows to include the custom data.3. Version the schema
Override GetCurrentVersion to declare your fragment's schema version, and increment it whenever you add, remove, or rename fields. Override UpgradeFromVersion to migrate data from an older save before RestoreFromSaveData runs.
Open the fragment Blueprint itself. In My Blueprint -> Functions, use the Override dropdown to pick Get Current Version, and in the graph it opens set the Return Node's Return Value to your schema number (2 here). Increment it every time you add, remove, or rename a field.
Override Upgrade From Version from the same dropdown - it drops an event node carrying an Old Version integer. Branch on an Integer < Integer node comparing Old Version to 2, and on the true path backfill whatever version 2 added: here, a For Each Loop over Item Ids calling Add on Item Counts with a count of 1. It runs before Restore From Save Data, so the data your restore reads is already migrated.
UPROPERTY(SaveGame) field without an UpgradeFromVersion migration silently drops the old value on load. Increment the version and handle it explicitly.4. (Optional) Custom header fields
To show extra metadata in your save menu (chapter name, character portrait, difficulty), subclass UCrimsonSaveGameHeader, override OnHeaderUpdate to populate your fields, and set the subclass in Developer Settings under SaveGameHeaderClass.
Content Browser -> right-click -> Blueprint Class -> search Crimson Save Game Header -> create. Add your metadata as variables: Current Chapter Name (Text) and Completion Percent (Float). Then My Blueprint -> Functions -> Override -> On Header Update, which drops an event node with a World Context Object input; from that pin, Get Game Instance -> Cast To your Game Instance class -> Set the two variables from whatever your game tracks.
Finally point the manager at the subclass: Project Settings -> Crimson -> Crimson Save Game Manager -> set `Save Game Header Class` to your Blueprint. Every header written from then on is an instance of it, so Get All Save Slot Headers hands your menu rows the extra fields.
See also
- Concept: The Fragment Model - how auto-serialization and custom fragments run together
- How-To: Save Player Progression - the player-state fragment applies this pattern
- API Reference -> Data types -
UCrimsonSaveGameFragmentBase/UCrimsonSaveGameHeaderfields