Resources

Flow

The majority of actor replication happens inside the UNetDriver::ServerReplicateActors function. This is where the server first gathers all actors it has determined to be relevant for each client, then sends any properties that have changed since the last time each connected client was updated. The UActorChannel::ReplicateActor function then handles the details of actor replication to a specific channel.

General Property Replication Process

Disclaimer

A lof of things happen in the replication process, im not talking about everything, I can sometimes skip a lof of processing or function calls.

UNetDriver::ServerReplicateActors

See NetDriver.

In UNetDriver::ServerReplicateActors we iterate “all” client connections.

About "all"

Not all connection are always iterated in one tick, see NumClientsToTick.

Next, we continue if the connection has a valid ViewTarget. All of of stuff is done afterwards, but most importantly UNetDriver::ServerReplicateActors_PrioritizeActors is called which returns 2 priority sorted list (PriorityList and PriorityActors of type FActorPriority).

Finally we actually do stuff with the actor we can replicate this tick with UNetDriver::ServerReplicateActors_ProcessPrioritizedActorsRange which calls UActorChannel::ReplicateActor. Then we call UNetDriver::ServerReplicateActors_MarkRelevantActors which are marked to be considered for next frame.

UActorChannel::ReplicateActor

See Channel, FObjectReplicator & FRepLayout & FRepState

This eventually calls FObjectReplicator::ReplicateProperties which is a wrapper of FObjectReplicator::ReplicateProperties_r. This function replicates properties to the Bunch (FOutBunch, which is a bunch used to be send).

It will call FRepLayout::ReplicateProperties which “Writes out any changed properties for an Object into the given data buffer”. Then FObjectReplicator::ReplicateCustomDeltaProperties which calls FObjectReplicator::SendCustomDeltaProperty (Does delta serialization on dynamic properties). This will eventually call FRepLayout::SendCustomDeltaProperty.

And starting here various path are followed depending on types, for example for a struct ICppStructOps::NetDeltaSerialize will be called.

Handling new replicated objects

Spawning replicated actor for client

The process seems to be the following: UNetConnection::ReceivedRawPacket UNetConnection::ReceivedPacket UNetConnection::DispatchPacket.

I don’t know how the Actor Channel exists/is created since the representing actor doesn’t exist yet. UChannel::ReceivedRawBunch (bunch is of type FInBunch) UChannel::ReceivedNextBunch UChannel::ReceivedSequencedBunch UActorChannel::ReceivedBunch UActorChannel::ProcessBunch. Then we simply check if the representing actor of the channel exists, if not we call UPackageMapClient::SerializeNewActor.

Remapping net GUIDs

When replicated objects are spawned on server, we have to spawn them on relevant clients to. Since they use unique net GUIDs, we have to tell to the locally spawned objects what are their net GUID. Thats the job of UNetDriver::UpdateUnmappedObjects, which will call FObjectReplicator::UpdateUnmappedObjects which will trigger onreps on the newly created object.

OnRep handling

The function responsible for that is FObjectReplicator::CallRepNotifies which calls FRepLayout::CallRepNotifies which uses UObject::ProcessEvent to run the OnRep function.

Common Issues

OnRep OldValue

When using OldValue in OnRep_ functions, be sure to make your parameter a reference, otherwise your value will be broken.

Example:

// broken
UFUNCTION() void OnRep_HidingActor(TWeakObjectPtr<ATFCInGameCharacter> OldVal);
// works 
UFUNCTION() void OnRep_HidingActor(TWeakObjectPtr<ATFCInGameCharacter>& OldVal);`