Rolly Docs

Save streamed levels and World Partition worlds

Streaming levels, level instances and World Partition cells keep their state as they stream out and in, their actors save under IDs that stay the same every session, runtime Data Layers keep their state, and large saves and loads can spread over frames.

Sheet
39 of 64
Rev
1.0.0
Updated
On this sheet

Keep streamed levels as the player leaves them

When a streaming level (Load Stream Level, a Level Streaming Volume), a level instance or a World Partition cell starts to unload, Rolly Save keeps what a save would hold of it: its actors’ saved state, the placed actors destroyed in play, the spawned actors that live in it and its Level Blueprint’s variables. No file is written. When it streams in again, the kept state is set before its Begin Play, with its level’s transform, so a moved crate is where the player left it. Unloading runs On Pre Save and On Rolly Save Pre Save; On Saved does not run.

Every save holds every kept level. A load replaces them with those its save holds, and each level the save holds takes those values the first time it streams in after the load. A level that only turned invisible, such as a streaming level whose Should Be Loaded stays on, keeps its actors as they are when it shows again; after a load it takes the load’s values, and the spawned actors the save lacks go, but its placed actors destroyed before it turned invisible stay gone until the engine loads the level again. The engine shows a level that streamed out as the same object until garbage collection takes it, which it forces soon after a level streams out.

While the engine makes a level visible over several frames, saves leave it out and keep the records it streams in with, so a save in those frames never writes values its actors do not have yet. A load in place during them, or while the level waits for its assets, gives the level the load’s values once it is visible, after its Begin Play.

Figure 1. Project Settings > Plugins > Rolly Save: Levels, Holding Players and Spreading Work with their defaults.
To Do
Forget each level as it unloads, as for the rooms of a roguelike Untick Project Settings > Plugins > Rolly Save > Levels > Remember Streamed Levels. A level the engine loads again then streams in as designed, its destroyed actors back and its loot gone, and a save made after holds nothing of it; one that only turned invisible, or streams in again before garbage collection takes it, comes back as it left. A load still sets each level the first time it streams in
Keep the levels travel leaves Remember Levels (see Remember levels across travel): a level that unloaded before the travel is kept with the map

Only the server or a standalone game keeps levels; clients see the result through replication.

Foliage keeps a record per cell: each cell’s foliage actor saves the changes to its own instances, and only once they differ from the level’s design. The engine names a World Partition map’s foliage actors after their grid, so keep the foliage grid size after release (see Save harvested foliage and instanced meshes).

Set values before Begin Play in streamed levels

A level that streams in after the map’s actors initialized sets each actor’s values by its Apply Timing (see Choose what saves): Before Begin Play, the default, before the level’s Begin Play; After Begin Play once the level’s Begin Play ran; When Asked once the game calls Apply Saved Values. Levels already visible when the map’s actors initialize, such as those that load with the map, take the map load’s values instead, which On Load Finished reports.

A sublevel’s Level Blueprint takes its values with its actors; when they name a spawned actor of the level, after that actor is back, after the level’s Begin Play.

In Play In Editor, a streamed level runs its construction scripts as it first becomes visible, which resets Blueprint variables that are not Instance Editable. Rolly Save sets the level’s saved values again after its Begin Play, and On Rolly Save Loaded and On Loaded run once, after that, so Begin Play there can read a default. Packaged games set the values once, before Begin Play. The log says so once per session, the first time a level has values to set again. Test Begin Play logic that reads saved variables of streamed actors in a packaged build.

The values of an actor whose Apply Timing is When Asked wait in its level until the game calls Apply Saved Values, with the assets they name kept loaded meanwhile, whatever Keep Assets of Unloaded Levels says.

Save actors inside level instances

An actor in a level instance’s level saves under the level instance actor’s save ID, so each copy of one level keeps its own state: the chest of LI_House_2 saves as 2|/Game/Maps/Tavern:LI_House_2|@Level:Chest_1. Level instances nest at most 8 deep around an actor, and its whole ID takes at most 1,024 characters; past either, the actor is not saved and a setup error says why (IdTooDeep, IdTooLong). That level’s Level Blueprint saves as <level instance actor's ID>|@LevelScript, and takes its record by that role and its Blueprint, so a spawned level instance, whose level loads under a new package name each time it spawns, keeps it.

Level instance Its actors’ Custom Save IDs
Standalone (Level Behavior) Unique within each level instance: every copy of the level may hold an actor with the same ID, and two actors of one copy with one ID are both saved under their names, with a setup error that names the level instance
Embedded Its actors join the map’s World Partition and save under the map, as any actor of the map. A Custom Save ID inside a level the map embeds more than once repeats in each copy; Check Open Map reports it

A Level Instance actor spawned during play saves under its GUID, and the actors of its level save under it.

Renaming, replacing or copying a level instance actor gives its level’s actors new IDs, so their saved state is lost. Before release, give each placed level instance actor a Rolly Save component with a Custom Save ID: its contents then keep their state through renames.

Keep level instances unloaded

Save Level Instance State (What Saves, on) saves whether a level instance’s level is loaded and which level it shows. A level instance your game unloaded stays unloaded after every load, travel and stream back, and none of its level’s actors begins play. In the map’s own level it is unloaded before Begin Play, so a Begin Play that loads it wins; in a streamed level as the level is added, after its Begin Play, so the saved state wins. One your game gave another level with Set World Asset gets that level back.

Level instance Saves
Standalone, placed or spawned Its state when its level is unloaded, shows another level than when Rolly Save first met it (at its level’s start or its spawn; for one met without a level, the level the first save finds), or holds actors that save
Embedded in a World Partition map Nothing: its actors play without it
Packed Level Actor Nothing: it loads no level while playing
  • The saved state of the actors inside follows their level: when an instance shows another level than the one they were saved in, as after an update that gave it another level, their records leave the loaded game as that level streams in, the log names it (LevelInstanceChanged), and they start as designed. This runs whatever Save Level Instance State is.
  • Set World Asset exists only in editor builds for the engine’s Level Instance actor, so a packaged game cannot give one back the level it showed: it keeps the level it has, with setup warning WorldAssetNotSet, and the saved state of its actors is not set. Swap levels with a level instance class whose C++ code sets its level, or with Load Level Instance under a name.
  • A load with Keep Current Map loads the levels of the instances the save holds loaded, or does not name.
  • A Reset Area resets the instances it holds, which load as designed, and the actors inside their levels by where those actors stand, as each level streams in again.
  • Only the server or a standalone game sets it; clients keep their instances loaded in this version.
Figure 2. LI_House_2's record in the Save Inspector: its Actor group with Level Instance Unloaded, showing LI_House_Night, under it the Asset LI_House_Night and the Design LI_House it was built with.

Load levels at run time under a stable name

Load Level Instance without an Optional Level Name Override loads the level as <level>_LevelInstance_<n>, with a counter that differs each session. Rolly Save does not save the actors of such a level, since their IDs would differ next session, and a setup warning says so (LevelNameNotStable). Pass an override that is the same each session, such as Cellar_1, or place the level as a Level Instance actor.

Save Run-Time Levels (Project Settings > Plugins > Rolly Save > Levels, on) saves the levels your game loaded with Load Level Instance under a name, with their level, place, streaming level class, temporary package flag and visibility, and loads them again as a load sets the map’s values and as the player returns to the map; their actors take their saved state as they stream in. Only levels that are to stay loaded save, a hidden one hidden.

  • A load with Keep Current Map keeps a level of the saved name with the same level, class and place, loads one that differs again where it was, and removes the levels the save lacks, named or not.
  • A level that cannot be loaded again, such as a renamed one without a Core Redirect, gives setup warning DynamicLevelNotLoaded; it stays in the next saves, with the saved state of its actors, until a load loads it.
  • After a load, Hold Players holds a player standing in such a level until the level has loaded under it, as for any ground (see Hold players until the ground has loaded).
  • A map saves at most 1,024 such levels, the last by name left out with setup warning TooManyDynamicLevels.
  • A Begin Play that loads a level under a name the load loaded already gets none back, and the engine logs LoadLevelInstance called with a name that already exists: find the level with Get Streaming Level, or turn the setting off and load your levels yourself after On Load Finished.
  • Only the server or a standalone game loads them.

A streaming level’s actors save under its package name, such as /Game/Maps/Tavern_Cellar:Crate_3: renaming or moving the level loses their state, as renaming an actor does (see Rename saved data without losing saves).

Bring loot back into its level or cell

A spawned actor that saves comes back into the level it lived in:

It lived in It comes back
A streaming level or a level instance’s level Into that level, once it streams in
A World Partition cell Into the same cell in the session; after a load, into the cell at its saved place once that cell is visible, so it never falls through a floor that has not loaded. In a map that keeps the Spatial Hash, the cell whose square holds its place, at any height
The persistent level With the map, wherever the player is

A spawned actor with an owner, such as a lantern a placed guard holds, waits for its owner and comes back with it. Spawn actors that must exist everywhere into the persistent level, such as with the Game State as owner.

Run code when a streamed level is ready

On Streamed Level Ready on the Rolly Save Subsystem fires once per streamed level that became visible after the map’s actors initialized, after its Begin Play, its After Begin Play values, its spawned actors’ return and its Reset Areas. Restored is true when Rolly Save holds saved state for the level, set now or kept since it unloaded; false when nothing is saved for it. Levels visible when the map’s actors initialize are reported by On Load Finished instead. It fires on the server or in a standalone game only.

Blueprint

In the Game Mode’s Begin Play, get the Rolly Save Subsystem (Get Game Instance Subsystem), drag from it to Assign On Streamed Level Ready, and compare Streaming Level with the one Get Streaming Level gives for your level.

C++

#include "Game/RollySaveSubsystem.h"

URollySaveSubsystem::Get(this)->OnStreamedLevelReady.AddDynamic(this, &AMyGameMode::HandleLevelReady);

// A UFUNCTION of AMyGameMode.
void AMyGameMode::HandleLevelReady(ULevelStreaming* StreamingLevel, bool bRestored)
{
	if (!bRestored)
	{
		SpawnFirstVisitGuards(StreamingLevel);
	}
}
C++

Keep memory in check

Setting (Project Settings > Plugins > Rolly Save > Levels) Does
Keep Assets of Unloaded Levels (on) The meshes, materials and other assets the kept state of unloaded levels names stay loaded, up to 16,384 packages and 1 GiB, so their values are set before Begin Play when they stream in
Off Their memory is freed; such a level’s values are set after its Begin Play, once its assets have loaded in the background. A setup warning says so once per level (AppliedAfterPreload)
Remember Streamed Levels off Nothing of an unloaded level is kept, in memory or in saves

Each kept level stays in memory and in every save, as remembered maps do. Save an index into a list of meshes rather than the mesh itself in values of streamed actors when many levels change many assets.

A server loads every World Partition cell and unloads none unless World Settings > World Partition Setup > Server Streaming Mode and Server Streaming Out Mode are on (or wp.Runtime.EnableServerStreaming and wp.Runtime.EnableServerStreamingOut), so on a server cells rarely stream out and in.

Save runtime Data Layers

In a World Partition map, Save Data Layers (Project Settings > Plugins > Rolly Save > Levels, on) saves the state of the map’s runtime Data Layers (Unloaded, Loaded or Activated) with the map and sets it again with the map’s values before Begin Play, so their cells start loading before Begin Play. Only the server or a standalone game sets layers; clients follow through replication.

Setting Does
Data Layer States: Changed Layers (default) Saves only layers whose state differs from the one they start in, so an update that changes a layer’s start state reaches old saves
All Layers Saves every runtime layer
Data Layers Not Saved Layers your game sets itself, such as a time of day layer: saves leave them out and loads leave them as they are

Layers whose Load Filter is Client Only are never saved. A load sets every other runtime layer its save does not mention back to its start state, so a Keep Current Map load also undoes layers changed since the save. A load of a save that holds no Data Layers record, one made with Save Data Layers off or before this version, leaves every layer as it is.

  • A layer the map lacks, such as one of a Game Feature that is off, keeps its saved state for the next save, until Data Layers Not Saved lists it; the load’s Issues list DataLayerNotFound. A state this version does not know is kept and not set (DataLayerStateUnknown).
  • A map saves at most 1,024 layers, with setup warning TooMany past that; a load reads at most 1,024 entries (DataLayerEntriesDropped).
  • A layer the engine refuses to set, such as one that is no runtime layer any more, gives setup warning StateRefused with its state now.

Spread large saves and loads over frames

Project Settings > Plugins > Rolly Save > Spreading Work:

Setting Does
Save Kinds (empty) Saves of these kinds capture actors over several frames instead of in one. Players, global objects, Save Handlers and the slot’s place and play time are taken in the first frame
Save Time per Frame (2 ms) How long such a save captures each frame, 0.5 to 33 ms
Max Save Frames (30) Once a save has taken this many frames, the first included, it captures what is left in that frame and logs MaxFrames once. 2 to 600
Spread After Begin Play Values (off) After a load that opens its map, the values of actors whose Apply Timing is After Begin Play are set over several frames
Apply Time per Frame (4 ms) How long those values are set each frame, 0.5 to 33 ms

While a save captures over frames:

  • A Save Block pauses it, and paused frames do not count toward Max Save Frames. A value moved between two actors during those frames can save twice or not at all: add a Save Block around such moves (see Block saving during a fight).
  • Each frame captures whole actors: an actor, its components and its child actors come from the same frame.
  • A load, a travel or the game’s end captures what is left at once, so the save holds the world before it changes. A load that an actor’s pre-save function starts does so once that function returns.
  • A second save waits for it, then captures in its turn.
  • A placed actor destroyed after its capture saves as destroyed, once; a level that unloads gives the save the records its unload captured.
  • The slot’s Capture Frames (FRollySaveSlotInfo::CaptureFrames) tells the frames it took: 1 for a save in one frame, and for saves made before this version.

Spread After Begin Play values arrive after On Load Finished, which fires before them, as do After Begin Play Save Handlers and each actor’s OnRollySaveLoaded for actors not set yet: start gameplay that reads those values at On World Ready, which fires once they are set (see Hold players until the ground has loaded). Until then:

  • A save, or a load, asked for sets the rest in that frame first, then runs, so a save holds the loaded game.
  • Autosaves wait and run once the values are set.
  • A travel sets the rest before the map is remembered. A level that streams out is remembered with the loaded values of its actors not set yet, which it gets when it streams in again.
  • Their Issues lines join the load’s result as Get Last Load Result gives it.

See streamed levels while you test

Figure 3. The Save Inspector's running game: Streamed Levels with Tavern_Cellar unloaded with 3 records, a cell whose values wait for 2 assets and Cellar_LevelInstance_7 not saved, and loot waiting for its level and for its cell.

The Save Inspector’s running game (see Inspect, compare and edit saves) lists the current map’s Streamed Levels after Remembered Levels while one has something to show:

Row Means
{Level} · {Kind} · {N} records · {Size} · unloaded {Time} Unloaded, with the state its unload kept; open the row for its records
{Level} · {Kind} · values wait for {N} assets Visible, its values waiting for the assets they name, with Keep Assets of Unloaded Levels off or past its limits
{Level} · not saved: its name changes each session Loaded under a name the engine numbers anew each session (LevelNameNotStable)

{Kind} is sublevel, level instance, cell, or cell of {Level Instance}; a cell is named by its debug name. Waiting Records names loot that waits “for {Level} to stream in”, “for the cell at its saved place to stream in” or “for its owner {Owner} to stream in”, and records that wait “for the assets of {Level} to load”; loot destroyed in play waits for nothing and is not listed. The record line selects the actor of a record in a sublevel or a level instance’s level while that level is shown, and in the editor Load and Select loads a sublevel the editor has not loaded, or shows a hidden one, first.

Figure 4. The RollySave category for BP_Chest_3 of the sublevel Tavern_Cellar, and for the host's pawn held until the ground below it has loaded.

The Gameplay Debugger’s RollySave category (see See what an actor saves while you play) shows an actor’s level and its kind, where a spawned actor respawns and a held pawn’s hold; without an actor, whether the world is ready, each held player and what it waits for, and how many streamed levels are visible, unloaded with saved state, waiting for assets and not saved.