Replication issue of actors placed in "always loaded" sublevels when seamless travel is enabled

I’ve done some research into this as we were having this problem with our game:

reason this is happening is that when we are seamless traveling from one map to another, client is loading map faster than server. We had this by restarting a simple map with client already connected to it and seamless travelling to it.

Every time we replicate an actor from server to a client connection we check if UNetConnection::ClientHasInitializedLevelFor. If server thinks client doesn’t have level initialized/visible, we don’t replicate info.

RPC that updates server APlayerController::ServerUpdateLevelVisibility gets sent by client as soon as each sub level is loaded, and on server, we do UNetConnection::UpdateLevelVisibility which adds to set UNetConnection::ClientVisibleLevelNames visible map for that connection. However, since server is loading map slower than that client, this info is received whilst server is still transitioning to TransitionMap. We call UNetDriver::PreSeamlessTravelGarbageCollect() every time server seamless travel which calls UNetConnection::ResetGameWorldState() and in turn empties UNetDriver::ClientVisibleLevelNames. This clears all info that client loaded these sublevels, making levels not replicate.

simplest workaround is to either move all your replicated actors to your persistent level, or you could set your levels to load via Blueprint and try loading them in GameModeBase::PostSeamlessTravel() (not sure if that would actually work). proper solution to this problem most likely would be to queue up RPC calls from client about visible levels and only process them after server has finished travelling. But that’s for Epic people to figure out :wink:

P.S. There is also a memory leak with variable UNetConnection::ClientVisibileActorOuters that it never gets emptied. It’s just a map storing a cache of level actors to its client connection visibility. This essentially means if you seamless server travelled same client connection a lot of times or you had a lot of sub levels it would eventually break. You can probably quickly fix this by emptying it in same place UNetConnection::ClientVisibleLevelNames is done in UNetConnection::ResetGameWorldState()

1 Like