Questlight Studio
Docs / Save and load

22. Save and load

In short: the whole game state (party, inventory, story, quests, world, time and more) is saved in slots, from save points in the world, from a consumable save item that works almost anywhere, and by autosave at save points and zone changes. Saving is never allowed in battle, dialog or cutscenes. Every system saves itself as a named section, so adding your own state is one registration line.

22.1 Basics

22.1.1 What the player gets

  • Save points in the world: interact, the slot screen opens, pick a slot.
  • A consumable save item: use it from the party menu to save anywhere, except in battle, dialog, cutscenes and zones you mark as no-save (for example before a boss).
  • Autosave at every save point and when arriving on another map, in rotating autosave slots that manual saves can never overwrite.
  • Continue and Load in the main menu, with a slot screen that shows a thumbnail, zone, party with levels, play time, money and date.
  • Damaged saves are recovered from a backup automatically.

There is no saving or suspending during a battle: the state of a fight is never serialized.

22.1.2 Your first save point

  1. In your level, place a Save Point actor (Place Actors → search "Save Point", class ARSESavePoint).
  2. Give it a unique Save Point Id (for example Inn_Hearth). Move its Player Spawn where the player should stand when loading from it.
  3. Optionally set its Mesh and Interact Radius.
  4. Play, walk up and interact. The save screen opens.

Save screens, Continue and Load in the main menu need nothing else: they are built in.

22.1.3 A no-save zone

  1. Place a No Save Volume actor (class ARSENoSaveVolume) around the area, for example the corridor before a boss.
  2. While the player is inside, the consumable item is blocked and the save screen says why. Save points still work (the block's scope is Consumable Item Only).

For an area name shown on the slot screen, place an Area Volume actor (class ARSEAreaVolume; the higher priority wins where volumes overlap).

22.1.4 A save item

Create the item as a Save Item data asset (URSESaveItemData, a consumable without a use ability that is never usable in battle). Give it to the player like any item. Using it opens the slot screen; the item leaves the inventory only after a successful save.

22.2 How it works

22.2.1 The principle

The save system knows no gameplay system. Each system that keeps state registers a participant and writes its own section: an id, a version and bytes. The save file is a header (Info: slot, timestamp, play time, request source, location, party summary, money, a JPEG thumbnail) plus a list of opaque sections. Only fields marked UPROPERTY(SaveGame) are saved, with tagged properties, so a field added after a save loads with its default and a removed field is ignored. References to content are written as text and stay stable between versions.

The sections the plugin saves include:

SectionWhat it holds
Partymembers (recruitment, departures, guests), inventory with key items and documents, equipment, vitals (HP, MP and any persistent resource, KO), progression, formation
Narrativestory flags and variables, solved puzzles, unlocked zones, seen events and scenes, one-shot interactions, discoveries
Rulesthe active rules profile id
Quests, Cards, Dialog, Difficultyquest status and steps, card collection, read lines and seen dialogs, adaptive intelligence per mode and recent battles
Worldencounter triggers, random areas, visible enemy spawners, locks, chests, traps, interactables, data layers, interior visits (keyed by stable State Keys)
Audio, Tutorials, Bestiary, Time, NPCs, Seeds, PartyControl, Mapstory music override, seen tutorials, met enemies, clock and weather, story changes of NPC days, random seeds, who controls whom in the field, fog of war and pins

Intentionally not saved: a battle in progress, the dialog runner, a scene in progress, save blocks, travel in progress, zone music, the current zone banner, character looks and animation state (rebuilt from data), and the player's options (volume, language, text speed, UI style, controls), which belong to the user in GameUserSettings.ini. Session-only sections (such as session rules and stealth state) replicate in co-op but never go in files.

22.2.2 Save rules

Blocking is a registry of reasons, counted so overlapping zones stack. The built-in reasons are Battle, Dialog, Cutscene (all scope) and NoSaveZone (consumable only). Check with Can Save Now, which also returns the reasons. A save is refused with Blocked when a reason applies.

Autosave: Autosave Slot Count slots rotate (Autosave, Autosave1...): the first empty one, else the oldest (a damaged slot is replaced first). Autosave runs at every save point and on arrival at a map through travel, after Zone Autosave Delay, never in no-save zones (unless Autosave In No Save Zones) and not within Min Seconds Between Autosaves. A load never autosaves.

22.2.3 Files and recovery

With the default File backend a slot is Saved/SaveGames/<Slot>.sav: a small container (magic, version, size, CRC32) around the save bytes. Writes are atomic: the new file is written to .tmp, the current good file becomes .bak, then the temp file is renamed over the slot. A damaged file never replaces a good backup. When loading, a damaged or missing slot falls back to the complete temp file, then to the backup; the slot screen marks a slot that came from backup. A save written by a newer game version is refused before anything is applied, so a half-loaded state never exists.

22.2.4 Save backends

Project Settings → Save → Backend: File (default, with free-space check), Save Game System (the platform's ISaveGameSystem, the storage consoles implement, with our container around the bytes) or Custom (a C++ class implementing IRSESaveBackend). Saves are per platform user (FRSESaveUser). Failures report Corrupt, NoSpace, UserSignedOut or Failed through On Save Failed.

22.2.5 Loading

Load Game applies every section to its system immediately (in the game instance, so state survives the map change), then opens the saved map and places the party at the saved save point, marker or transform. Each section is read into a fresh value and assigned only if it succeeds, so a corrupt section cannot spoil the current state. New game resets every saved system (Start New Game).

22.2.6 Versions and migration

The file has a version and each participant has its own. ReadSaveSection receives the saved version so a participant can convert old data. A missing section (a system added after the save) calls OnSaveSectionMissing (default: that system's new-game state). A section with no participant (a removed system) is ignored with a warning. A section newer than its participant is refused.

References to deleted content are dropped on load with a warning per reference, instead of failing the load: the stack of a deleted item, a deleted member, a deleted equipment slot entry.

22.3 Key classes and assets

Class / assetModuleRole
URSESaveSubsystemRSECoreGame instance subsystem: slots, save, load, blocks, autosave, participants
IRSESaveParticipant, TRSEStructSaveParticipant<T>RSECoreA system's section writer and reader
URSESaveGame, FRSESaveSlotInfo, FRSESaveLocation, FRSESaveRequestRSECoreThe file, its header, where the party was, why it was saved
URSESaveSettingsRSECoreProject Settings → Save
URSESaveInteropLibraryRSECoreExport and import sections to your own save system
IRSESaveBackendRSECoreCustom storage
URSESaveSessionPolicyRSECoreCo-op rules for who may save and load
URSESaveFlowSubsystemRSEOpens the save screen, opens the saved map and places the party, consumes the save item
ARSESavePoint, ARSENoSaveVolume, ARSEAreaVolumeRSESave point, no-save zone, zone name
URSESaveItemDataRSEThe consumable save item
URSESaveUISettingsRSEProject Settings → Save UI
URSEBlueprintLibrary_SaveRSEThe Blueprint nodes for everything above

22.4 Settings

Project Settings → RPG Saga Engine → Save. Full list: Appendix B.

SettingDefaultMeaning
Slot Count3Manual slots
Slot Name PrefixSlotSlot0, Slot1...
Autosave Slot NameAutosaveEmpty means no autosave
Autosave Slot Count1Rotating autosave slots
Autosave On Zone TransitiononAutosave when arriving on a map
Autosave In No Save ZonesoffAllow it inside no-save zones
Zone Autosave Delay0.5 sWait after arrival
Min Seconds Between Autosaves30 sRate limit
BackendFileFile, Save Game System or Custom
Suspend Flush Timeout2 sHow long a pause waits for pending writes
Keep BackuponKeep the previous good save
Min Free Disk Space MB1Refuse to save below this

Save UI (Project Settings → Save UI) holds the slot screen: autosave at save points, thumbnail size and capture, lock after save, and the texts for corrupt, backup and unavailable slots. Game Flow holds Show Continue and the menu keys.

22.5 Blueprint usage

Category RPG Saga | Save (the RPG Saga Blueprint Library Save nodes):

  • Save Game (Slot Name, Save Point Id), Load Game (Slot Name; also opens the saved map), Open Save Screen (Save Point Id), Can Save Now (Source, returns the reasons), Has Any Save, Get Continue Slot, Get Save Slots, Delete Save Slot, Start New Game, Block Saving (Reason, Scope) and Allow Saving (Reason), Get Play Time Seconds, Get Slot Name (index).
  • On Get Save Subsystem: Request Save, Autosave, Save To Slot Async, Load From Slot Async, Is Busy, Get Last Save Result, events On Saved, On Loaded, On Load Failed, On Save Requested, On Save Failed.

Example, a minigame that blocks saving:

  1. Event: minigame starts → Block Saving (Reason = Minigame, Scope = All).
  2. Event: minigame ends (and in Event EndPlay) → Allow Saving (Reason = Minigame).

Example, a custom "Continue" button:

  1. Button On Clicked → Get Continue Slot → Branch on the return value.
  2. True → Load Game (Slot Name = the slot).
  3. False → show a "no saves" message (or use Has Any Save to disable the button).

Keeping our state inside your own SaveGame: the RPG Saga | Save | Interop nodes read and write live state of any section without using our slots: Get Save Section Ids, Export Section To Bytes, Import Section From Bytes, Export Sections To Bytes / Import Sections From Bytes (an empty list means every section), and the JSON forms (Export Sections To JSON, Import Sections From JSON, Export Section To JSON, Import Section From JSON). In your save function call Export Sections To Bytes with an empty list and keep the array in a Byte Array variable of your SaveGame; after you load your map, call Import Sections From Bytes. Import replaces the state of those sections and does not open the map: load it yourself. A section the project does not have is skipped, not an error.

22.6 C++ usage

Register a section for your own system, in the Initialize of a subsystem whose state is a struct of SaveGame fields:

USTRUCT()
struct FMyQuestBoardState
{
    GENERATED_BODY()
    UPROPERTY(SaveGame) TArray<FName> CompletedBoards;
    UPROPERTY(SaveGame) int32 ReputationPoints = 0;
};

void UMyBoardSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
    Super::Initialize(Collection);
    Collection.InitializeDependency<URSESaveSubsystem>()
        ->RegisterStruct<FMyQuestBoardState>(TEXT("MyBoard"), /*Version=*/1, this, [this] { return &State; });
}

Rules: the section id never changes after saves exist; the participant lives as long as its owner; use the returned participant's PostLoad(SavedVersion) to convert old data, OnMissing for a system added after the save, and OnNewGame for new-game state. Load order is registration order (the party is first). Do not save things that can be rebuilt (positions of schedule-driven NPCs, animation state).

Block saving from your own code: Save->PushSaveBlock(TEXT("MyCinematic")) and Save->PopSaveBlock(TEXT("MyCinematic")) (reasons are counted; always pop in EndPlay).

State held by actors (triggers, locks, interactables) lives in the exploration state subsystem under a State Key (the Save Key property, assigned by the editor); the actor reads it in BeginPlay and writes on change. See 16-world-building.md.

22.7 Advanced

  • Custom backend. Implement IRSESaveBackend and set Backend to Custom with your class: write the bytes to your cloud or platform service. Metadata (location, party, time, thumbnail as bytes) is in the save, so it works on any backend.
  • Async save and load. SaveToSlotAsync captures the state immediately and writes on a worker thread; LoadFromSlotAsync reads on a worker and applies on the game thread. Check Is Busy and listen to On Saved and On Loaded.
  • State scope. A participant's GetStateScope is Shared, PerPlayer or HostOnly, and IsSavedToFile false makes it session-only. In co-op, saves belong to the host: a guest's save and load calls return HostOnly, with the message of URSESaveSessionPolicy. See 22-networking.md.
  • Thumbnails. The slot screen captures the viewport without UI. Headless runs and servers skip it. Turn it off in Save UI settings.
  • Missing content. Participants may opt out of dropping missing references with bRemoveMissingContent.
  • Testing. RSE.Save.FullRoundTrip compares the registered participants with the audit list, fills every section, writes it to a temp slot, resets, loads and checks byte for byte. A new system with a section fails it until you add it to the list.
  • Console commands (development). RSE.Save.Slots, RSE.Save.To <slot index | slot name>, RSE.Save.Load [slot index | auto | slot name], RSE.Save.UseItem [item path] [give], and RSE.Save.SimulateFault NoSpace|Corrupt|SignedOut|Failed. See Appendix C.

22.8 Troubleshooting

SymptomCauseFix
The save item says it cannot be usedA block applies: battle, dialog, cutscene or no-save zoneCheck Can Save Now for the reasons; a block you pushed yourself needs its pop
Saving is blocked forever after a minigameA Push without a PopPop in every exit path and in EndPlay; the log warns on a pop without a push
A manual save fails with Protected SlotIt targeted an autosave slotUse Get Slot Name for manual slots
Autosave does not run on arrivalRate limit, no-save zone or the setting is offCheck Autosave On Zone Transition, Min Seconds Between Autosaves, Autosave In No Save Zones
After loading, a chest is closed againThe actor's state key changed (renamed actor, copied instance)Run Fix State Keys and keep the Save Key values
Load says IncompatibleThe save was written by a newer versionUpdate the game; nothing was applied
My system's data is gone after loadingNo participant, or no SaveGame flag on the fieldsRegister the section and mark fields UPROPERTY(SaveGame)
A load puts the party at the wrong placeThe save point id is not uniqueGive each save point its own Save Point Id
A guest cannot saveCo-op: only the host savesExpected; see the session policy
Slot shows as damagedA corrupt file with no readable backupDelete the slot, or load another; saves rewrite damaged slots

22.9 See also