Article written by Alex K.
From the documentation for Property Replication:
“Actor property replication is reliable. This means that the property of the client version of the Actor will eventually reflect the value on the server, but the client will not necessarily receive notification of every individual change that happens to a property on the server. For example, if an integer property rapidly changes its value from 100 to 200, and then to 300, the client will eventually receive an update with the value of 300, but there is no guarantee that the client will know about the change to 200.”
It’s important to understand what is and isn’t guaranteed when it comes to property and RPC replication and what steps should be taken to account for this in your game code.
The guarantee described above states that properties on the client will eventually reflect the correct value on the server. Because actors do not necessarily replicate every frame and packets may be dropped, this does not mean that every update made on the server will be received by the client. This also means that if a property changes rapidly to another value and back, for example from 100 to 200 then back to 100, the client will only see the property as having the first value. The shadow state of the actor, used to compare if the property is different and therefore needs to be replicated, is the same as the property on the frames when the actor is replicated, so no change will be detected and replicated to the client.
If you need frequent changes to be received by the client, you may want to increase the NetUpdateFrequency of the actor to ensure it replicates each frame. However, even a high update frequency doesn’t guarantee that every change will be received, as packet loss can result in a change being dropped. How you handle this will depend on the specifics of your game, but in many cases, smoothing between received values can help mitigate issues with updates not being received.
For changes made to multiple properties across multiple frames, generally these changes should be received in the order that they were made. However, unideal network conditions can result in these changes being dropped and resent, so they may be received later. For example:
The server has three replicated booleans that are changed over three net updates respectively.
The server’s states look like this for the three properties: 0,0,0 → 1,0,0 (the first property’s change is sent) → 1,1,0 (the second property’s change is sent) → 1,1,1 (the third property’s change is sent)
After the third change is sent, the server detects the second change was lost, so it is resent.
The client receives the 1st change, but because the second change is dropped and resent, the third change is received before the second.
The client’s states look like this: 0,0,0 → 1,0,0 → 1,0,1 → 1,1,1
Again, the guarantee that the state on the client will eventually reflect the state on the server is upheld, but until then, the client’s properties can be in a state (1,0,1) that never existed on the server.
Another guarantee is that, on a frame when an actor is replicated, all of it’s changed properties will be sent at once, and the client should receive and apply these changes within a single frame (with the exception of unmapped properties). However, there is no guarantee made regarding the order in which properties will be received or the order in which OnReps will be called. Any order is considered an implementation detail that should not be relied upon. If the order of an actor’s property replication is important to your game, you may need to implement OnReps to track which properties have been updated on a frame. After the replicated values have been received and their OnReps called, you can handle the changes in the PostRepNotifies function. You may also need to save certain received values in their respective OnReps until they’re ready to be used.
It’s also important to know that the ordering of replication between actors is not guaranteed. If two actors have properties changed on the same frame, they may not be replicated or received on the same frame for a number of reasons, such as differing update frequencies, relevancy/priority differences, or packet loss. If you have one actor that is dependent on another for replication, such as an inventory actor depending on a player character, using OnReps to track state and determine when an actor is available is again a useful strategy for handling these kinds of situations. The Replication Graph also provides support for building and handling these dependencies as well.
Finally, even though unreliable bunches will be processed in order, there can be gaps in unreliable RPC calls due to packet loss, as they won’t be resent if they’re lost. Any reliable RPCs sent are guaranteed to be received and processed in the order they were sent, as they will be resent until they are acknowledged. However, regardless of reliability, order is still not guaranteed between actors, and there is no guarantee that RPCs called on the same frame will arrive on the same frame.
Again, how you handle these situations will depend on the specific needs of your game. For all games, it’s important to not rely on anything that is not guaranteed, and testing using the network emulation settings is necessary to detect any situations that can arise in unideal network conditions.