I'm looking for PostNetInit event on client for objects NOT owned by PlayerController

I’m working on a networked game. We have it running and as long as nobody disconnects the game is playable. Now we’re working on players dropping and reconnecting. So I’m recovering the visual state of replicated objects from BeginPlay(), but only the ones that have PlayerController as owner. The objects spawned by the world (and thus not the Player or PlayerController) do NOT get server values in BeginPlay() because their PostNetInit() doesn’t get called. The goal here is to override local values on the client with server values but only a single time when connection is established, actor replication kicks in, and (most importantly) the values on the server have already been replicated to client.

Is there a function or delegate:

  • for objects NOT owned by PlayerController
  • that gets called before or after BeginPlay()
  • that gets called once per game connecting to server (like BeginPlay() or PostInitializeComponents()
  • that has the server data already replicated to the client

I have tried these:

virtual void OnActorChannelOpen(class FInBunch& InBunch, class UNetConnection Connection) {};

  • this one didn’t seem to have server data, only client data was there

virtual void PostNetReceive() override;

  • this one happens every time replicated data is synced, not just once

virtual void PostNetInit();

  • this one doesn’t happen on actors not owned by PlayerController

virtual void OnReplicationPausedChanged(bool bIsReplicationPaused);

  • this one didn’t fire for me on objects not owned by PlayerController

Answer from Unreal’s dev:

You could probably make a pretty simple engine modification to call PostNetInit for placed actors as well - at the end of UActorChannel::ProcessBunch, where it calls PostNetInit if bSpawnedNewActor is true, you could also call it if bNetStartup is true and the bunch is an open bunch. But this will have some caveats - if you use dormancy, it will be called every time the actor wakes from dormancy, for example. You could maybe deal with this by adding another flag, similar to ActorHasBegunPlay.

However, having said that, I would actually recommend an alternate approach. The best way to handle join-in-progress is usually to have any state that needs to be restored be based on replicated properties - and if you need to do extra set-up, to use a RepNotify/OnRep function for the relevant property. This keeps the logic scoped to the property and should work in all cases of needing to re-initialize it in a network environment - which could be as you mentioned, for join-in-progress players, but also for cases of an actor going outside its relevancy range (NetCullDistance). Or, if you use UE4’s replay system, scrubbing to a new time in a replay also re-initializes replicated actors (it’s treated like a join-in-progress).