Description

Context

The LevelStreamingPersistence plugin can be used to remember and restore properties on actors, components and other objects that are part of a streamed level. Persisted properties are restored when a level is made visible, during AddToWorld.

A map placed actor’s blueprint may be modified since the instance was last saved. Components may have been added to the blueprint (i.e. SimpleConstructionScript components). In the editor, UE ensures that map placed actors have their latest component structure by running RerunConstructionScripts. This happens during AddToWorld, after restoring persistence.

Problem

Persistence of SCS component properties does not work if the component was added to actor blueprint after the map actor was last saved. During PIE, those values silently fail to persist when a streamed level was reloaded from disk. This can lead to confusion. Users likely won’t realize it fails, or realize why it’s failing.

When the map actor is resaved, the component will be part of the package and persistence works for them. This isn’t a problem for packaged builds, since actor instances are reconstructed and resaved during cook.

Suggested Fixes

The main problem is the silent failure: devs may not realize a property isn’t persisting properly in PIE. We can detect after RerunConstructionScript, if any components have been spawned with persisted properties, that no record was created for yet. That means the component was created late. We can then:

  • Either warn the user via UE_LOG or FMessageDialog, that the actor instance should be resaved to persist its properties correctly in PIE. In my experience, UE_LOG is easy to miss for devs so FMessageDialog to popup after PIE would be my preference.
  • And/or perform the persistence on the new SCS component after detecting that it was spawned late. It may still satisfy the user’s needs, though it can also be extra confusing since that persistence would apply after BeginPlay. There is nu AddToWorld delegate currently between an actor’s RerunConstructionScript and BeginPlay. Adding that just for this purpose would be overkill.
Steps to Reproduce

In the attached repro project:

  • Open MAP_NewSCSComp
  • Follow repro steps below.
  • The repro blueprints are called BP_ActorWithSCSComponent and BP_TimerActorComponent.

Blank project setup steps:

  • Enable LevelStreamingPersistence plugin
  • Create an actor component blueprint BP_MyComponent
    • Add a float variable TimeAlive
    • Increase TimeAlive on tick. Also print it on tick.
  • In DefaultEngine.ini, configure the property to persist
[/Script/LevelStreamingPersistence.LevelStreamingPersistenceSettings]
+Properties=(Path="/Game/Blueprints/BP_MyComponent.BP_MyComponent_C:TimeAlive", bIsPublic=true)
  • Create an actor blueprint BP_MyActor
    • Add a cube mesh just to see the actor in the level.
    • Don’t add the actor component yet.
  • In a world partitioned map:
    • In World Settings, lower Cell Size and Loading Range to easily trigger the actor being streamed out and in.
    • Add BP_MyActor. Save the map.
  • Add BP_MyComponent to BP_MyActor.
    • Save BP_MyActor.
    • Don’t resave the map.

Repro steps:

  • Start PIE
  • Move camera so that level with BP_MyActor gets streamed out
  • Run ‘obj gc’ so garbage gets collected. Level will reload from disk.
  • Move camera back so that level with BP_MyActor gets streamed in
  • Observe: TimeAlive is restored
  • Expected: TimeAlive value is restored
Callstack

Non-fatal callstack. This is the callstack where a SCS component that wasn’t saved on the actor is first created. This during AddToWorld, but after persisted properties have been restored:

>	UnrealEditor-Engine.dll!UActorComponent::UActorComponent(const FObjectInitializer & ObjectInitializer) Line 560	C++
 	UnrealEditor-CoreUObject.dll!StaticConstructObject_Internal(const FStaticConstructObjectParameters & Params) Line 4969	C++
 	UnrealEditor-CoreUObject.dll!StaticDuplicateObjectEx(FObjectDuplicationParameters & Parameters) Line 3186	C++
 	UnrealEditor-Engine.dll!AActor::CreateComponentFromTemplate(UActorComponent * Template, const FName InName) Line 1127	C++
 	UnrealEditor-Engine.dll!USCS_Node::ExecuteNodeOnActor(AActor * Actor, USceneComponent * ParentComponent, const UE::Math::TTransform<double> * RootTransform, const FRotationConversionCache * RootRelativeRotationCache, bool bIsDefaultTransform, ESpawnActorScaleMethod TransformScaleMethod) Line 104	C++
 	UnrealEditor-Engine.dll!USimpleConstructionScript::ExecuteScriptOnActor(AActor * Actor, const TInlineComponentArray<USceneComponent *,24> & NativeSceneComponents, const UE::Math::TTransform<double> & RootTransform, const FRotationConversionCache * RootRelativeRotationCache, bool bIsDefaultTransform, ESpawnActorScaleMethod TransformScaleMethod) Line 640	C++
 	UnrealEditor-Engine.dll!AActor::ExecuteConstruction(const UE::Math::TTransform<double> & Transform, const FRotationConversionCache * TransformRotationCache, const FComponentInstanceDataCache * InstanceDataCache, bool bIsDefaultTransform, ESpawnActorScaleMethod TransformScaleMethod) Line 902	C++
 	UnrealEditor-Engine.dll!AActor::RerunConstructionScripts() Line 612	C++
 	UnrealEditor-Engine.dll!ULevel::IncrementalRunConstructionScripts(bool bProcessAllActors) Line 2078	C++
 	UnrealEditor-Engine.dll!ULevel::IncrementalUpdateComponents(int NumComponentsToUpdate, bool bRerunConstructionScripts, FRegisterComponentContext * InContext) Line 1891	C++
 	UnrealEditor-Engine.dll!UWorld::AddToWorld(ULevel * Level, const UE::Math::TTransform<double> & LevelTransform, bool bConsiderTimeLimit, const TOptional<UE::FTimeout const> & ExternalTimeout, FNetLevelVisibilityTransactionId TransactionId, ULevelStreaming * InOwningLevelStreaming) Line 3792	C++
 	UnrealEditor-Engine.dll!ULevelStreaming::UpdateStreamingState(bool & bOutUpdateAgain, bool & bOutRedetermineTarget, const TOptional<UE::FTimeout const> & InExternalTimeout) Line 1059	C++
 	[Inline Frame] UnrealEditor-Engine.dll!FStreamingLevelPrivateAccessor::UpdateStreamingState(ULevelStreaming *) Line 808	C++
 	UnrealEditor-Engine.dll!UWorld::UpdateLevelStreaming(const TOptional<UE::FTimeout const> & ExternalTimeout) Line 5045	C++
 	UnrealEditor-Engine.dll!UGameViewportClient::Draw(FViewport * InViewport, FCanvas * SceneCanvas) Line 1848	C++
 	UnrealEditor-Engine.dll!FViewport::Draw(bool bShouldPresent) Line 1798	C++
 	UnrealEditor-UnrealEd.dll!UEditorEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 2440	C++

Have Comments or More Details?

There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-368857 in the post.

0
Login to Vote

Unresolved
CreatedMar 6, 2026
UpdatedMar 9, 2026
View Jira Issue