Extending capture
Register a C++ component capturer so World State Reset Framework restores a component type it does not cover natively, with payload versioning and a stable API contract.
WSRF handles its documented native component types automatically. A project can register its own capturer in C++ for a component type WSRF does not cover — a spline whose control points are edited at runtime is a good example.
This is not the extension point for gameplay state
Health, AI state, quest progress, inventory, consumed decisions and Blueprint variables belong in Custom State. The Actor owns the meaning and the migration of that data. A component capturer is only appropriate for stable, reusable component configuration.
Runtime contract
- A capturer receives only
AActor&,UActorComponent&, and a capture or restore context. It cannot reach WSRF registration records, the subsystem, or another Actor’s state. - The capturer owns its payload schema. WSRF stores the payload as opaque bytes alongside the capturer ID, capturer version and stable component name.
- Provider modules register through
IModularFeaturesduringStartupModule. Registration after a world registry has been frozen is rejected. - Provider IDs sort lexically; capturers then sort by
SortOrderand capturer ID. Execution order is deterministic. - Sort orders
0–999are reserved by WSRF. Third-party capturers start atFWSRFComponentCapturerRegistry::FirstExternalSortOrder. - Capturer IDs must be globally unique. A collision with a native or already-discovered ID is rejected and reported.
- WSRF never creates a missing component during restore.
The capturer
class FMySplineCapturer final : public IWSRFComponentStateCapturer
{
public:
virtual FName GetCapturerId() const override
{
return TEXT("MyGame.Spline");
}
virtual int32 GetCapturerVersion() const override
{
return 1;
}
virtual UClass* GetSupportedComponentClass() const override
{
return USplineComponent::StaticClass();
}
virtual int32 GetSortOrder() const override
{
return FWSRFComponentCapturerRegistry::FirstExternalSortOrder;
}
virtual void CaptureComponent(
AActor& Actor,
UActorComponent& Component,
FWSRFComponentCaptureContext& Context) const override;
virtual bool RestoreComponent(
AActor& Actor,
UActorComponent& Component,
const FWSRFComponentRestoreContext& Context,
FString& OutError) const override;
};
The provider module
class FMyWSRFCapturerModule final
: public IModuleInterface
, public IWSRFComponentCapturerProvider
{
public:
virtual void StartupModule() override
{
IModularFeatures::Get().RegisterModularFeature(
GetModularFeatureName(), this);
}
virtual void ShutdownModule() override
{
if (IModularFeatures::Get().IsModularFeatureAvailable(GetModularFeatureName()))
{
IModularFeatures::Get().UnregisterModularFeature(
GetModularFeatureName(), this);
}
}
virtual FName GetProviderId() const override
{
return TEXT("MyGame.WSRFCapturers");
}
virtual void GetComponentCapturers(
TArray<TSharedRef<IWSRFComponentStateCapturer>>& OutCapturers) const override
{
OutCapturers.Add(MakeShared<FMySplineCapturer>());
}
};
IMPLEMENT_MODULE(FMyWSRFCapturerModule, MyWSRFCapturer)
Declare the module as "Type": "Runtime" with "LoadingPhase": "Default". It must stay loaded
while worlds using its capturer exist, because a frozen world registry retains shared capturer
instances.
The complete example ships with the plugin
Docs/ExtendingCapture.md contains the full four-file example — payload struct, capturer,
provider module and Build.cs — in a form you can copy directly. It is the same design compiled
and exercised by the plugin’s own automation suite.
Payload versioning
The capturer ID identifies the meaning of the bytes. Never reuse an ID for a different component or schema.
When the payload structure changes:
- increment
GetCapturerVersion(); - override
CanRestoreVersion()if the new capturer can still read older payloads; - deserialise the correct legacy struct for each accepted version, then convert it;
- leave unsupported future versions rejected — WSRF reports and ignores them rather than guessing.
The Actor-state schema does not change when you change an opaque payload. That version belongs to
the capturer. FWSRFActorState records whether the external section was captured, so an older v2
save that predates this API leaves third-party components at their construction or Blueprint
values.
Removing an extension
If a save references a capturer that is no longer registered, WSRF reports the capturer ID and the component name, ignores that payload only, and continues restoring everything else it can read. It never reinterprets the bytes and never creates a replacement component. Reinstalling the provider makes the retained payload readable again.
API stability
For the WSRF 1.x line, these contracts are stable public API:
IWSRFComponentStateCapturermethod meanings;IWSRFComponentCapturerProviderdiscovery through its modular-feature name;- the capture and restore contexts’ opaque payload methods;
- capturer ID plus positive capturer version routing;
- deterministic order by provider ID, sort order and capturer ID;
- the reservation of third-party sort orders beginning at
FirstExternalSortOrder; - missing, unknown and unsupported payloads being isolated rather than failing the complete restore.
Additive context helpers, diagnostics or restore phases may appear in a compatible minor release. Private registry storage, native payload structures, cache implementation and subsystem record types are not extension points and may change. A future major version may revise the contract with migration notes; WSRF will not silently reinterpret an existing third-party payload ID.
Niagara adapter without the Demo
The core Runtime never loads Niagara. The included Demo loads WorldStateResetFrameworkNiagara
because it uses Niagara content. If a project removes the Demo but wants Niagara component capture,
keep the adapter module, enable Unreal’s Niagara plugin, and load WorldStateResetFrameworkNiagara
before the first WSRF game-world registry is frozen. If the adapter is absent, Niagara payloads are
reported and skipped while other state continues to restore.
Related pages
Last updated: 2026-09-04