We recently had some issues with assets and due to those issues, we hit two different level streaming path that are causing check to trigger (and otherwise would crash or get in an unsupported state).
When looking at ULevelStreaming::AsyncLevelLoadComplete, SetCurrentState is called at the start to set ELevelStreamingState::LoadedNotVisible, but not all error state seems to be catched and some of those error state continues to have ELevelStreamingState::LoadedNotVisible instead of ELevelStreamingState::FailedToLoad (or another more appropriate state).
In particular, the first call to PrepareLoadedLevel can fail in case where the OwningWorld would not be set for the level (that’s a separate issue we need to investigate in our code). Normally, it would trigger an ensure, but otherwise would continue without setting LoadedLevel to a good value.
Then the UE_LOG below that call is also not setting the appropriate error state. The easiest solution seems to just add an extra condition on LoadedLevel == nullptr that would SetCurrentState and SetShouldBeLoaded appropriately.
By having LoadedLevel == nullptr and CurrentState == ELevelStreamingState::LoadedNotVisible, we later hit a check in ULevelStreaming::GetLevelStreamingStatus() in the else block.
Sorry about the delay, and thank you for reaching out. I can see what you described in the code, but it would be very helpful if we could have some repro steps to get the engine (vanilla from the launcher) into that situation. If the bad behavior is dependent on specific assets that cannot be easily recreated externally, would you be able to provide a repro project, or perhaps an example (generic, non-confidential) asset that causes this issue? That would help us better understand the situation and the best steps to deal with it.
I completely agree that it would be fantastic if I had better repro, but sadly at current time I don’t have anything that could serve as a repro.
From what I’ve heard internally, it has been a recurring problem when some levels are reloaded too fast, but I have not been able to pinpoint the exact conditions that triggers the issue (except for the fact that a world has been “resurrected” without its PersistentLevel being set), and it also started happening close to the moment when we enabled the incremental GC internally although I can’t verify if it is the culprit at current time.
Sorry for the lack of concrete evidence, a lot of big systems have been turned on and off in the last few weeks.
Hopefully, next time those errors happen, we’ll be able to see them in the log rather than have a crash.
Have a nice day, and thank you for looking into this!
Sorry about the extended delay, but after several attempts, I was not successful in reproducing the crash. Actually, I was not able to save a level asset where PersistentLevel or PersistentLevel->OwningWorld were null pointers, because forcefully attempting to do so crashes inside UEditorEngine::OnPreSaveWorld().
If you want, I can file a bug report for the engine devs based solely on “code inspection” pointing out that those situations are handled to some degree in the current ULevelStreaming::AsyncLevelLoadComplete() code, but that SetCurrentState() and SetShouldBeLoaded() are not being called appropriately. But without a repro for the devs to gauge the priority of the task and test possible solutions, this can easily be flagged as “won’t fix” or similar.
If there is any chance you can provide a repro project or at least a repro level asset, it could help the bug report be prioritized. Otherwise, I’ll just move forward with what we’ve got.