Custom state and dependencies

The versioned Writer/Reader API, the IWSRFResettable callbacks, supported value types, and storing inter-Actor relationships as Stable IDs.

Automatic capture handles the things every Actor has: transform, visibility, collision, meshes. It deliberately does not try to serialise your gameplay. Custom State is the explicit, versioned channel for that.

Explicit, not reflection

Custom State is not reflection-based object serialisation. You name each value and write it with a typed node. That is what makes restore ordering deterministic and snapshots free of UObject references.

The interface

Implement WSRF Resettable on the Blueprint or C++ class, then set the restore strategy to AutomaticThenInterface (default) or InterfaceOnly.

EventWhenWhat to do
WSRF Capture Custom StateWhen the initial state is capturedSet a version, write your values
WSRF Prepare For ResetBefore the Actor is restoredStop timers, abort montages, disable transient gameplay
WSRF Restore Custom StateAfter automatic state and attachmentsRead the values back and apply them
WSRF Reset CompletedAt the end of the Actor’s resetResume gameplay, notify other systems

Writing values

UWSRFStateWriter is handed to you in the capture callback:

WSRF Capture Custom State (Writer)
  -> Writer -> Set Version (2)
  -> Writer -> Write Bool     ("bArmed", bArmed)
  -> Writer -> Write Integer  ("ChargesLeft", ChargesLeft)
  -> Writer -> Write Name     ("Phase", CurrentPhase)
  -> Writer -> Write Transform("DoorOpenTransform", DoorTransform)

Supported types:

Writer nodeReader nodeValue
WriteBoolReadBoolbool
WriteIntegerReadIntegerint64
WriteFloatReadFloatfloat
WriteDoubleReadDoubledouble
WriteEnumReadEnumenum value + enum type name
WriteEnumByteReadEnumBytebyte enum value + type name
WriteNameReadNameFName
WriteStringReadStringFString
WriteVectorReadVectorFVector
WriteRotatorReadRotatorFRotator
WriteTransformReadTransformFTransform
WriteGameplayTagReadGameplayTagFGameplayTag
WriteGameplayTagsReadGameplayTagsFGameplayTagContainer
WriteStableIdReadStableIdFWSRFActorId

Rules the API enforces: empty keys are rejected, writing the same key again replaces its previous typed value, and a read fails cleanly when the key is missing or its type does not match.

Reading values back

UWSRFStateReader is handed to you in the restore callback. Check the version first, then read:

WSRF Restore Custom State (Reader)
  -> Reader -> Get Version -> Switch on Int
       2: Read Bool("bArmed") -> Read Integer("ChargesLeft") -> Read Name("Phase")
       1: Read Bool("bArmed")  // older payload, defaults for the rest
  -> apply the values to the Actor

Every read returns a success boolean alongside the value, so a missing key is a branch rather than a crash.

Restoration should assign state directly. Do not call gameplay events such as TakeDamage or GrantLoot merely to reconstruct a value — those may trigger irreversible side effects.

Versioning your payload

SetVersion stamps the payload with your own schema version. Nothing forces you to bump it — but when you do, old payloads keep their old number and your restore code can branch on it.

HasKey covers the simple case: a value added in a later version is absent from older payloads, and you can default it without touching the version at all.

Your version is not WSRF's version

The version you pass to SetVersion belongs to your Actor’s contract and is preserved verbatim through storage and migration. It never selects or triggers a WSRF snapshot migration — WSRF’s own storage format is versioned separately (snapshot schema 3 stores Custom State as a compact stream of key, value type and active value). Bumping your version changes only what your own restore branch sees.

Writer and Reader are callback-scoped

Both objects are temporary helpers owned by the framework for the duration of the callback. Do not store them in a variable and use them later.

Dependencies between Actors

The rule: never store an object reference to another managed Actor. It goes stale the moment that Actor is destroyed and respawned. Store its Stable ID instead.

Capture:
  Linked Lever -> Get Component (WSRF State Component) -> Get Stable Id
  -> Writer -> Write Stable Id ("LinkedLever", Id)

Restore:
  Reader -> Read Stable Id ("LinkedLever") -> Id
  -> WSRF World State Subsystem -> Find Loaded Actor (Id)
  -> Cast To BP_Lever -> store the reference for this session

This works even when both Actors were destroyed before the reset: respawn happens earlier in the pipeline, so by the time WSRF Restore Custom State runs, the target exists again.

Still handle a null result from Find Loaded Actor and resolve again later if the target’s level or container is not loaded. A completion callback does not guarantee that every possible related source level has loaded or that every respawn succeeded.

Inside a Level Instance, use Find Loaded Actor In Context with a context Actor from the same container, so two copies of the same instanced content resolve their own lever rather than each other’s.

Restore ordering

The pipeline orders Actors before restoring anything: a parent before its attached children, then ascending ResetPriority, then Stable ID as a deterministic tie-break. Two runs over the same world produce the same order.

GetPredictedRestoreOrder exposes that ordering for a set of IDs, which is what the Inspector uses to show the documented pipeline for a selected Actor.

Deliberate exclusions

There are no Writer nodes for UObject references, arbitrary object graphs, generic arrays, maps, sets, or user-defined binary blobs.

Scope limitation

Custom State is a typed key/value payload, not a general-purpose serializer. Values and Stable IDs only. That constraint is what guarantees a snapshot can be stored in a SaveGame and applied in a later session without retaining engine objects.

Demo

The demo map’s orange fixtures are exactly this feature: a Lever and a Door that hold a Stable-ID relationship and their own custom values, and reconstruct it after both original Actors have been destroyed.

Last updated: 2026-09-04

Type to search titles, sections and keywords.