Hello!
We’ve been experiencing an issue during where an assertion is triggered in UNetConnection::UpdateLevelVisibilityInternal for the branch to try and make a streaming level visible. The repro rate is quite low.
Few bits of key information upfront:
- We’re on UE5.7.4.
- We’re running a vanilla version of UNetConnection.
- The issue’s been observed both in LAN MP and also with Steam’s P2P Net Driver present.
- We’ve got some CVars turned on for World Partition & Level Streaming.
- We’ve got Seamless Travel turned on.
The CVar changes we’ve got enabled in our project’s DefaultEngine.ini:
[SystemSettings]
wp.runtime.UpdateStreaming.EnableAsyncUpdate=True
wp.runtime.EnableServerStreaming=1
wp.Runtime.EnableServerStreamingOut=True
LevelStreaming.DefaultAllowClientUseMakingVisibleTransactionRequests=True
LevelStreaming.DefaultAllowClientUseMakingInvisibleTransactionRequests=True
LevelStreaming.ShouldServerUseVisibleTransactionRequest=True
wp.Runtime.UseMakingVisibleTransactionRequests=True
wp.Runtime.UseMakingInvisibleTransactionRequests=True
LevelStreaming.AllowIncrementalPreRegisterComponents=True
LevelStreaming.AllowIncrementalPreUnregisterComponents=True
LevelStreaming.AsyncRegisterLevelContext.Enabled=True
The assertion is the check in this snippet:
else if (LevelVisibility.bTryMakeVisible)
{
if (FLevelUtils::SupportsMakingVisibleTransactionRequests(GetWorld()))
{
const FNetLevelVisibilityTransactionId VisibilityRequestId = LevelVisibility.VisibilityRequestId;
check(VisibilityRequestId.IsValid() && VisibilityRequestId.IsClientTransaction());
// Only consider visible levels with their streaming level in the LoadedVisible state and returning ShouldBeVisible()
ULevelStreaming* ServerVisibleStreamingLevel = FLevelUtils::GetServerVisibleStreamingLevel(GetWorld(), LevelVisibility.PackageName);
if (ServerVisibleStreamingLevel && ServerVisibleStreamingLevel->ShouldBeVisible() && (ServerVisibleStreamingLevel->GetLevelStreamingState() == ELevelStreamingState::LoadedVisible))
{
ClientMakingVisibleLevelNames.Add(LevelVisibility.PackageName);
check(!ClientVisibleLevelNames.Contains(LevelVisibility.PackageName));
}
}
}
Specifically, it’s this assertion:
ClientMakingVisibleLevelNames.Add(LevelVisibility.PackageName);
check(!ClientVisibleLevelNames.Contains(LevelVisibility.PackageName));
It’s worth noting that whilst looking through the source code in NetConnection.cpp, I stumbled upon a comment for when level visibility requests are scheduled to be deferred during Seamless Travel -- that they need to be deferred until after level loading has completed on the server. Looking at the code, there’s no skipping of the call to UpdateLevelVisibilityInternal when this happens.
void UNetConnection::UpdateLevelVisibility(const FUpdateLevelVisibilityLevelInfo& LevelVisibility)
{
if (Driver && Driver->GetWorld())
{
// If we are doing seamless travel we need to defer visibility updates until after the server has completed loading the level
// otherwise we might end up in a situation where visibility is not correctly updated
if (Driver->GetWorld()->IsInSeamlessTravel())
{
PendingUpdateLevelVisibility.FindOrAdd(LevelVisibility.PackageName) = LevelVisibility;
}
}
UpdateLevelVisibilityInternal(LevelVisibility);
NotifyConnectionUpdated();
}
Later in UNetConnection::ResetGameWorldState, any pending requests are processed with the same internal function after clearing various containers.
Few questions:
- Is it intended for the request to be processed immediately after it’s queued it to be pending during Seamless Travel or is this a mistake and the code should be exiting early instead?
- Are any of the CVars we’re using outdated / do they happen to be used with succession on any of Epic Games’ active projects?
- Have there been any reports on this assertion being triggered for others?
Any insight would be much appreciated, thank you!
[Attachment Removed]