in our game we use the Game Features plugin. Among other things, we use game features to add World Partition content into our maps via external data layers (EDLs). These are runtime data layers which have the load filter set to None, so they should be considered both by the client and the server. One thing we noticed is that if we make a replay using the Replay System in the singleplayer (standalone) mode, the EDLs don’t load in the replay. After investigation, we concluded that the issue is caused by the array of active data layers being empty in replays.
The effective runtime state of a data layer is resolved in FWorldDataLayersEffectiveStates::GetDataLayerEffectiveRuntimeStateByName(). The logic is simple - if a data layer is contained by one of the replicated arrays, the runtime state is set to either Loaded or Activated. Updating of these arrays (RepEffectiveActiveDataLayerNames, RepEffectiveLoadedDataLayerNames, RepActiveDataLayerNames and RepLoadedDataLayerNames) is done in AWorldDataLayers::ResolveEffectiveRuntimeState() and AWorldDataLayers::SetDataLayerRuntimeState(). Upon closer inspection, we can see that an update is done only if the data layer is not local and the net mode is set to DedicatedServer or ListenServer:
thanks for your help. Unfortunately, I believe that this is not going to work for us (I’ve tried). The reason being that in our case the runtime state of data layers is only set and resolved once during map load. This condition would only be fulfilled if recording was active during that. But that’s usually not our use case. We typically start recording after the world is loaded. Therefore, the arrays don’t get updated in our case.
Can you also try these changes ? (look for >>> in the code)
static bool GAllowChangingDataLayerRuntimeStateOnClient = false;
void AWorldDataLayers::RewindForReplay()
{
Super::RewindForReplay();
// Same as PostRegisterAllComponents when rewinding we want to reset our state to the initial state and rely on the Replay/Replication.
>>> check(!GAllowChangingDataLayerRuntimeStateOnClient);
>>> TGuardValue<bool> Scope(GAllowChangingDataLayerRuntimeStateOnClient, true);
ResetDataLayerRuntimeStates();
InitializeDataLayerRuntimeStates();
}
void AWorldDataLayers::PostRegisterAllComponents()
{
Super::PostRegisterAllComponents();
// When running a Replay we want to reset our state to the initial state and rely on the Replay/Replication.
// Unfortunately this can't be tested in the PostLoad as the World doesn't have a demo driver yet.
if (GetWorld()->IsPlayingReplay())
{
>>> check(!GAllowChangingDataLayerRuntimeStateOnClient);
>>> TGuardValue<bool> Scope(GAllowChangingDataLayerRuntimeStateOnClient, true);
ResetDataLayerRuntimeStates();
InitializeDataLayerRuntimeStates();
}
}
bool AWorldDataLayers::CanChangeDataLayerRuntimeState(const UDataLayerInstance* InDataLayerInstance, AWorldDataLayers::ESetDataLayerRuntimeStateError* OutReason) const
{
if (!InDataLayerInstance->IsRuntime())
{
if (OutReason)
{
*OutReason = ESetDataLayerRuntimeStateError::NotRuntime;
}
return false;
}
const ENetMode NetMode = GetNetMode();
if (InDataLayerInstance->IsClientOnly())
{
if (NetMode != NM_Standalone && NetMode != NM_Client)
{
if (OutReason)
{
*OutReason = ESetDataLayerRuntimeStateError::ClientOnlyFromServer;
}
return false;
}
}
else if (InDataLayerInstance->IsServerOnly())
{
if (NetMode == NM_Client)
{
if (OutReason)
{
*OutReason = ESetDataLayerRuntimeStateError::ServerOnlyFromClient;
}
return false;
}
}
>>> else if (NetMode == NM_Client && !GAllowChangingDataLayerRuntimeStateOnClient)
{
if (OutReason)
{
*OutReason = ESetDataLayerRuntimeStateError::AuthoritativeFromClient;
}
return false;
}
return true;
}
Dominik, do you think you could provide a small sample that reproduces the issue?
I suspect the root cause is that AWorldDataLayers::PostRegisterAllComponents() and/or AWorldDataLayers::RewindForReplay() is causing you to loose the correct state of your EDL.
I haven’t tried reproducing this outside of our project yet. I’ll give it a try as soon as I can. Unfortunately, I’m a little preoccupied at the moment, so it might take a little bit. Thanks for everything so far.