We have a crash that’s been happening for a while, and is triggered when these conditions are met:
The cvar s.UseUnifiedTimeBudgetForStreaming must be true
The level must contain a landscape
World partition must be enabled
Some tiles of the landscape must be modified and left unsaved before starting a PIE session
When the camera comes near the unsaved tiles there is a chance that a crash occur in RemapGuid() when the tiles are streamed in.
In UWorld::UpdateLevelStreaming(), if several streaming levels become visible or invisible in the same frame the global PIEInstanceID will be set to -1 in the process (through a call to ULevelStreaming::BroadcastLevelVisibleStatus() and then SetPlayInEditorWorld()) and won’t be restored to the previous value afterward.
It’s not a problem in most cases, except when the loaded cell contains an unsaved landscape proxy actor that need a valid PIEInstanceID during the serialization.
I fixed the issue by restoring the correct PIE instance ID before each streaming level is processed:
Thanks for sharing the project. It made our life super simple. I was able to reproduce the problem in version 5.6 but it’s not happening in Main. We are investigating to understand what is happening as lots of assets should be getting remapped IDs.
As for your proposed fix, it seems more appropriate to use a FScopedConditionalWorldSwitcher in UEngine::HandleUnifiedStreaming. There are some BP related events that might be using that same guard. Those null the world and reset the PIE ID on destruction which can conflict with FTemporaryPlayInEditorIDOverride. The conditional switcher can properly deal when multiple embedded scopes are creatd.
// If the process async loading did not use the full time budget it will be granted to UpdateLevelStreaming for all ticking worlds
// If process async loading exceeded the budget - UpdateLevelStreaming will amortize the hitch
for (int32 WorldIdx = 0; WorldIdx < WorldList.Num(); ++WorldIdx)
{
FWorldContext& Context = WorldList[WorldIdx];
if (Context.World() != nullptr && Context.World()->ShouldTick())
{
---> FScopedConditionalWorldSwitcher WorldSwitcher(Context.World());
Context.World()->UpdateLevelStreaming(StreamingTimeout);