The bulk of actor replication happens inside UNetDriver::ServerReplicateActors
. This is where the server will gather all of the actors that it has determined to be relevant for each client, and send any properties that have changed since the last time each connected client was updated.
There is a defined flow in how actors are updated, certain framework callbacks that are invoked, and certain properties used during this process. The important ones are:
AActor::NetUpdateFrequency
– Used to determine how often an actor replicatesAActor::PreReplication
– Called before any replication occursAActor::bOnlyRelevantToOwner
– True if this actor only replicates to ownerAActor::IsRelevancyOwnerFor
– Called to determine relevancy when bOnlyRelevantToOwner is trueAActor::IsNetRelevantFor
– Called to determine relevancy when bOnlyRelevantToOwner is false
The high level flow looks like this:
- Loop over each actor that is actively replicating (
AActor::SetReplicates( true )
)- Determine if this actor is initially dormant (
DORM_Initial
), and if so, skip immediately. - Determine if the actor needs to update by checking the NetUpdateFrequency value, if not skip
- If
AActor::bOnlyRelevantToOwner
is true, check the owning connection of this actor for relevancy by callingAActor::IsRelevancyOwnerFor
on the viewer of the owning connection. If relevant, add to owned relevant list on the connection.- In this case, this actor will only send to a single connection.
- For any actor that passes these initial checks,
AActor::PreReplication
is called.- PreReplication is a place where you can decide if you want properties to replicate for connections. Use the
DOREPLIFETIME_ACTIVE_OVERRIDE
for this.
- PreReplication is a place where you can decide if you want properties to replicate for connections. Use the
- If we pass the above, add to the considered list
- Determine if this actor is initially dormant (
- For each connection:
- For each considered actor from above
- Determine if dormant
- If there is no channel yet
- Determine if client has loaded the level the actor is in
- If not loaded, skip
- Determine if the actor is relevant by calling
AActor::IsNetRelevantFor
for the connection- If not relevant, skip
- Determine if client has loaded the level the actor is in
- Add any actors on the connections owned relevant list from above
- At this point, we have a list of actors that are relevant for this connection
- Sort actors by priority
- For each sorted actor:
- If the connection hasn’t loaded the level this actor is in, close the channel (if any), and continue
- Every 1 second, determine if actor is relevant to connection by calling AActor::IsNetRelevantFor
- If not relevant for 5 seconds, close channel
- If relevant and no channel is open, open one now
- If at any point this connection is saturated
- For remaining actors
- If relevant for less than 1 second, force an update next tick
- If relevant for more than 1 second, call
AActor::IsNetRelevantFor
to determine if we should update next tick
- For remaining actors
- For any actor that passes all of the above, the actor is replicated to the connection by calling
UChannel::ReplicateActor
- For each considered actor from above
Replicating an Actor to a Connection
UChannel::ReplicateActor
is the workhorse for replicating an actor and all of its components to a connection. The flow looks something like this:
- Determine if this is the first update since this actor channel was opened
- If so, serialize specific information that is needed (initial location, rotation, etc)
- Determine if this connection owns this actor
- If not owned, and this actor’s role is
ROLE_AutonomousProxy
, then downgrade toROLE_SimulatedProxy
- If not owned, and this actor’s role is
- Replicate this actors changed properties
- Replicate each component’s changed properties
- For any deleted components, send special delete command