Exactly what Sam said. You typically won’t see a crash because this
is null, you would see the crash happen when calling a method on a class pointer that is null (or the instance was destroyed and the pointer now points to garbage). Since you are already in SetServerList
, this
instance of UMainMenu is still valid, otherwise the game would have crashed on your call to SetServerList (probably MainMenu->SetServerList(ServerList);
in OnFindSessionsComplete
.
The actual line that is reporting the crash is misleading - probably because you are running in DevelopmentEditor, which I believe is the UE4 equivalent of running in ‘Release’ mode. This means that you aren’t going to get accurate debug info because of compiler optimizations. However, running the project in debug reveals the real culprit:
The problem is that World
is nullptr
, and you are hitting the ensure macro. Instead of using the macro, you can simply do if (!World) return;
and the method will exit cleanly (I tested this myself and no crash).
As to why World
is null here? Well, my assumption is because we’re doing a ServerTravel
when we host a game. I think this might actually de-register the UMainMenu instance with the current world or a new World is created after the ServerTravel call returns and our UMainMenu instance hasn’t registered itself yet. I’m going to do some further debugging on this and will update this post if I can pinpoint the exact reason.
Update
After running the debugger a couple times, it looks like AActor::GetWorld() might be getting called - note the comment:
UWorld* AActor::GetWorld() const
{
// CDO objects do not belong to a world
// If the actors outer is destroyed or unreachable we are shutting down and the world should be nullptr
if (!HasAnyFlags(RF_ClassDefaultObject) && !GetOuter()->HasAnyFlags(RF_BeginDestroyed) && !GetOuter()->IsUnreachable())
{
if (ULevel* Level = GetLevel())
{
return Level->OwningWorld;
}
}
return nullptr;
}
So this makes sense as to why World could be nullptr. What doesn’t make sense is why AActor’s GetWorld()
is being called because our UMainMenu is actually a UUserWidget which also implements GetWorld(). It’s hard to say exactly what’s happening here but as I mentioned I did have to run the debugger several times before I was even able to step into the GetWorld()
function. Usually random behavior indicates some sort of race condition, perhaps UMainMenu is in an undefined state when SetServerList()
is called because we’ve transitioned to new map.
Granted, I had to click Join, then quickly go back to the main menu and click Host (and Host again from the set server name menu) to get into a map before the server list returned. This is probably not the typical use-case, and there are likely other ways we could break the game by performing an unintentional sequence of actions. I highly doubt Sam had an entire team of QA performing exhaustive stability tests on these demo apps. That being said, one thing we could do to improve the game’s stability is disable the Host button while a search is pending.