UDN
Search public documentation:

GameStateReplication
日本語訳
中国翻译
한국어

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

UE3 Home > Networking & Replication > Game State Replication

Game State Replication


Overview


For each actor that is relevant to a particular client, the server executes a series of replication checks on that actor, for that client. These checks determine which variables should be replicated to the client, and determine if a given function should be sent to the client for execution instead of executing on the local machine. Likewise, the client is able to replicate functions (but not variables) back to the server. This sending of data about an actor is called replication. Sending all of the data for an actor would overflow connections, as would sending changed data. Instead, the approach Unreal takes is to replicate only the data that is needed on that particular client.

Replication checks


When you think about the number of clients in a server, and the number of relevant actors for each client, and the number of replication checks on variables for each actor, there's a large number of these checks that are performed to determine what data to send over the network. Special care should be taken when implementing replication statements so as to not waste too much of the CPU calculating what to send. Don't call functions from within replication statements, as function calls are much too slow for replication statements. If necessary, calculate the result of a function in tick, and use the value of the variable in your replication statement. Along the same lines, you should try to keep your checks as simple as possible, only evaluating a condition if its necessary. Due to the time needed to evaluate the replication conditions for the variables in Actor, Inventory, and other popular classes in Unrealscript; these checks were moved to native code. That's why you will see the nativereplication on a few of the Engine classes. Any variables being evaluated for replication will be covered by the native replication code. The Unrealscript replication statements are still important however, and are used for situations like cheat protection and documentation for mod authors who do not have the source. Any variables that are not defined in a nativereplication class, (defined in a non-nativereplication subclass or superclass) will be replicated via the unrealscript definitions.

Reliable versus unreliable


Another optimization used in networking is the difference between reliable and unreliable data. In the case of variables, all data is reliable. If a variable does not make it through to the client, it is guaranteed to be re-sent to the client and eventually make it. To allow for out-of-order packets, the variable data from the highest-numbered packet is used, ensuring freshness. Functions however, exist in both reliable and unreliable forms. A reliable function, is guaranteed to reach the client in the same order it was sent, relative to other reliable functions. An unreliable function however, is sent to the client, but it is not guaranteed to be received (if it does not make it to the client, then the server does not attempt to resend the function again). Generally, a reliable function should be used when the function is important to game play, so that clients maintain a relatively close simulation to the server. An unreliable function should be viewed as a nice to have, but not necessary; such as special effects.

Replication data approximation


Another optimization is that of how Unreal sends vectors, rotators, and arrays across the internet. These rules apply to both function arguments and variables.

  • Vectors - Each component rounded to the nearest integer.
  • Planes - Each component rounded to the nearest integer.
  • Rotators - Can have 255 distinct values for each component. The values are then scaled back up from 0-255 to 0-65536 on the other end of the connection.
  • Structs - All or nothing. If one element changes, the entire struct is replicated.
  • Static Arrays - Dynamic arrays can not be replicated.
  • Strings, Structs - Must be less than 448 bytes or the variable will starve the rest of that particular actor's variables
  • CompressedPosition - The above transformations for vectors and rotators (except for Rotator.Roll, this is always zero) are applied. And the velocity component gets the same transformation as a vector.

These rules apply to variable replication only.

  • Static Arrays - Only the changed elements in the array will be sent.

See Variable replication notes for more information. If you require precise vectors, planes and rotators then it is best to create a new struct as these a struct specific optimizations. It is always best to send as little data as needed, this simulating as much as you can on the client is highly recommended.

Replication statements rules


There are a few useful things to know about when writing your own replication statements. First, you can only define a replication statement in the class in which the variable is defined. This means you can never override replication statements, and you must define the replication statement in the correct class that defines the data. If you find yourself needing to override a variable's replication conditions, chances are that you're using the wrong role for your actor, and you need to rethink what kind of role it should have, that allows you to replicate this data. This way, if the Actor class contains a variable DrawType, then you know where to look for its replication condition: it can only reside there in the Actor class. If you still need to replicate data, you're usually better off making a new variable that acts as the carrier message, and is copied over to the true variable on the other side.

Defining replicated functions is now only done using the function modifiers server, client, reliable and unrealiable. The meaning of these modifiers are straight forward. A function declared server will be executed on the server and is usually called by a client. A function declared client will be executed on the actor's owning client. Client and server are mutually exclusive, just like reliable and unreliable; that is to say a function cannot be both server and client. A function declared reliable will always be executed in order and guaranteed to arrive. A function declared unreliable might not make it to the other side; meaning, if the network is saturated this function call will be discarded.

Replication statement guidelines


Keep your replication statements simple. If you have many relevant instances of this class, then these replication conditions are likely to be run very frequently, and any optimization you can perform will go a long way. We already discussed the importance of not doing anything overly complicated, and that rule should be adhered to. Second, use the bNet... variables if possible. These variables, like bNetInitial, bNetOwner, bNetDirty are quite useful and requires no additional CPU requirement to use these variables, as they are set for you automatically. Checks can also be performed to do various things depending upon if the Role/RemoteRole is a Role_SimulatedProxy or Role_AutonomousProxy. The best place to find examples of these more intricate checks is in the Engine classes, particularly Actor, Pawn and PlayerController. It should also go without saying that you should not change anything in a replication statement. Doing i++ in a replication statement causes i to change unpredictably as it is being replicated, which is not something you really want to do. And finally, as the last word advice, the server checks all incoming data to be sure it is legitimate. If the client replicates something to the server, the server will exchange the Role and RemoteRole variables temporarily, and evaluate the replication condition to see if the other end had a legitimate reason for sending that data. If the check fails, then the data is discarded. That means that when you are writing replication statements, make sure that the server also has all the data needed to perform the replication check. Otherwise, it will never be processed or accepted by the server.

Writing replication statements


In UnrealScript, every class can have one replication block which contains many replication statements. Each replication statement consists of a replication condition (a statement that evaluates to true or false), and a list of one or more variables to which the condition applies.

It's perfectly okay for a class to not contain a replication statement, it just means that the class will not replicate any of it's defined variables to the other side. In fact, most classes do not need replication statements because most of the "interesting" variables that affect display are defined in the Actor class, and are only modified by code in subclasses.

If you define a new variable in a class, but you don't list it in a replication definition, that means that your variable is absolutely never replicated. This is the norm; most variables don't need to be replicated.

Here is an example of the UnrealScript syntax for the replication statement. This is taken from the Pawn class:

Pawn.uc
replication
{
  // Variables the server should send ALL clients.
  if (bNetDirty && Role == ROLE_Authority)
  FlashLocation, bSimulateGravity, bIsWalking, PlayerReplicationInfo, HitDamageType, TakeHitLocation, DrivenVehicle, Health;

  // variables sent to owning client
  if (bNetDirty && bNetOwner && Role == ROLE_Authority)
    InvManager, Controller, GroundSpeed, WaterSpeed, AirSpeed, AccelRate, JumpZ, AirControl;

  // sent to non owning clients
  if (bNetDirty && !bNetOwner && Role==Role_Authority)
    bIsCrouched, FlashCount, FiringMode;

  // variable sent to all clients when Pawn has been torn off. (bTearOff)
  if (bTearOff && bNetDirty && Role == ROLE_Authority)
    TearOffMomentum;

  // variables sent to all but the owning client
  if (!bNetOwner && Role==ROLE_Authority)
    RemoteViewPitch;
}

Here is an example of some replicated functions, taken from the Controller class:

Controller.uc
reliable server function ServerRestartPlayer()
{
  if (WorldInfo.NetMode != NM_Client && Pawn != None)
  {
    ServerGivePawn();
  }
}

reliable client function ClientSetWeapon( class<Weapon> WeaponClass )
{
  local Inventory Inv;

  if (Pawn == None)
  {
    return;
  }

  Inv = Pawn.FindInventoryType( WeaponClass );
  if (Weapon(Inv) != None)
  {
    Pawn.SetActiveWeapon(Weapon(Inv));
  }
}