Skip to content

Race Condition With Streamed Out Level Auto Saves #80

Description

@dyanikoglu

Hi. This is kind of complex issue I faced with auto saves for streamed out levels.

While testing the feature, I noticed dynamically spawned actors in sublevels were failing to save their data into SPUD while their level is streaming out. After extensive debugging, I noticed it's because plugin tries to save data of those actors "after" level is streamed out, so actors have a null UWorld reference during the operation.

The root cause was, all of ISpudObject and ISpudObjectCallback interface functions were failing to execute for those actors during the stream out save process. I noticed AActor::ProcessEvent function is responsible with initiating interface calls, and it has a check for validity of actor's world:

UWorld* MyWorld = GetWorld();
if( ((MyWorld && (MyWorld->AreActorsInitialized() || bAllowScriptExecution)) || HasAnyFlags(RF_ClassDefaultObject)) && !IsGarbageCollecting() )
{
#if !UE_BUILD_SHIPPING
		if (!ProcessEventDelegate.IsBound() || !ProcessEventDelegate.Execute(this, Function, Parameters))
		{
			Super::ProcessEvent(Function, Parameters);
		}
#else
		Super::ProcessEvent(Function, Parameters);
#endif
}

So, all interface calls were just being ignored for Spud because of that.

As solution, I honestly didn't like what was done in #45 to monitor load/unload state of streamed levels in tick function of spud subsystem. LevelStreamingDelegates.cpp from engine source already has two good callbacks for SpudSubsystem to bind itself:

TMulticastDelegate<void(UWorld*, const ULevelStreaming*, ULevel* LoadedLevel)> FLevelStreamingDelegates::OnLevelBeginMakingVisible;
TMulticastDelegate<void(UWorld*, const ULevelStreaming*, ULevel* LoadedLevel)> FLevelStreamingDelegates::OnLevelBeginMakingInvisible;

After cleaning out MonitoredStreamingLevels stuff from subsystem and binding into those callbacks fixed the issue for me. Now, spud saves state of actors first, then the level streams out.

I might open a pull request, but I'm not able to test this change for WorldPartition at the moment, and not sure if this change breaks the compatibility.

Anyways, wanted to create this issue to give that problem some visibility.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions