Networked entities can be simulated either on a Game Client ("Client authority") or a Simulator ("Server-side authority"). Authority defines which Client or Simulator is allowed to make changes to the synced properties of an entity, and in general defines who "runs the gameplay code" for that entity.
When an entity is created, the creator is assigned authority over the entity. Authority can be then transferred at any time between Clients – or even between Clients and Simulators, or between Simulators.
In any case, only one Client or Simulator can be the authority over the entity at any given time.
To learn more about authority, check out this short video:
You can see the basic Authority principles in practice in our First Steps interactive demo. You can read the explanation as well.
When architecting a multiplayer game, it is important to choose which authority model the game relies on. coherence supports a variety of models.
Client authority is the easiest to set up initially, but it has some drawbacks:
Higher latency. Because both Clients have a non-zero ping to the Replication Server, the minimum latency for data replication and commands is the combined ping (Client 1 to Replication Server and Replication Server to Client 2).
Higher exposure to cheating. Because we trust Game Clients to simulate their own Entities, there is a risk that one such Client is tampered with and sends out unrealistic data.
In many cases, especially when not working on a competitive PvP game, these are not really issues and are a perfectly fine choice for the game developer.
Client authority does have a few advantages:
Easier to set up. No Client vs. Server logic separation in the code, no building and uploading of Simulation Servers, everything just works out of the box.
Cheaper. Depending on how optimized the Simulator code is, running a Simulator in the cloud will in most cases incur more costs than just running a Replication Server (which is comparatively very lean).
Having one or several Simulators taking care of important world simulation tasks (like AI, player character state, score, health, etc.) is always a good idea for competitive PvP games. In this scenario, the Simulator has authority over key game elements, like a "game manager", a score-keeping object, and so on.
Running a Simulator in the cloud next to the Replication Server (with the ping between them being negligible) will also result in lower latency.
A typical choice for competitive games, sometimes called "Server-authoritative". The entity is simulated on the Server, and the Client only sends inputs. To achieve smoother gameplay, the Client can predict the entity's state locally and then reconciliate once the Simulator has come back with a new state.
You can read more about how to achieve this in the section about Server-authoritative setup, or below in the Input authority section.
Mixing authority models
A cool possibility that coherence enables is to mix these modes, since authority is not tied to the match but rather a property of each CoherenceSync
.
So for instance, you can have a game where some critical entities are server-side with client input for cheat prevention, while others are distributed among Clients. It's up to you!
While we generally speak of "authority" in abstract, in the coherence model we break authority in two, in order to support the variety of scenarios needed in multiplayer games. We call these State authority and Input authority.
A Client or Simulator can only have State authority over an entity, only Input authority, or both (in this case we say it has "full authority"). In fact, if you use coherence on a basic level, most of the time you will be dealing with full authority without realising it.
When a Client has State authority over an entity it means that they are authorized to change its state, that is, the values of the entity's networked properties.
For instance, if the entity's Transform.position
and Transform.rotation
properties are set to sync, the Client who has authority can change these and move the entity around.
A Client who tries to change properties with no State authority will see those properties be reset immediately by coherence.
Hint: If you see an entity jittering around, it might be the signal that the current Client has no authority over an entity, but it's trying to change its values. Time to do some debugging!
When a Client or Simulator has State authority over an entity, it means that they are authorized to send inputs to the State authority.
Whoever has State authority then is in charge of processing that input, and producing a new state for the entity, which is then sent to all observing Clients.
Splitting Input and State authority is a common pattern when creating a server-authoritative setup.
Entities that no-one has authority over (neither State nor Input) are called "orphans". Orphaned entities are not simulated, so the values of their synced properties don't change. In a way you could think of them as sleeping.
Authority over an entity can be given up using CoherenceSync.AbandonAuthority()
. Using this API will make an entity orphan until someone else adopts it. An entity can also become an orphan when a Client or Simulator that had State authority disconnects.
To change the state of an orphan entity, someone has to take State authority over them. This is done either automatically when an orphan is seen for the first time (only if the entity is set to be on Auto-Adopt Orphan), or intentionally, using the API CoherenceSync.Adopt()
.
For an entity to become an orphan, they need to be set as Persistent. A non-persistent entity that is abandoned will be immediately deleted by the Replication Server.
When a Client has no authority whatsoever over an entity, we often refer to that entity as "remote". It's important to understand that a remote entity is only remote to some of the Clients, so "remote" is not a authority state in itself, but just a way to refer to an entity from the point of view of a certain Client.
For instance, an entity seen as remote by Client A might be:
Authoritative on some other Client B or C, or on Simulator A, etc.
If no one has authority over it, it is an orphan.
Even if an entity is not currently being simulated locally (the Client does not have authority), we can still affect its state by sending a network command or even requesting a transfer of authority.
Authority in practice
To recap all possibilities with an example, consider the following case. We're creating a competitive 1v1 robot fighting game in a big arena.
Client A has Input authority over their mech robot.
Client B also has Input authority over their robot.
The Simulator Server in charge of the match has State authority over both mechs, so they can't cheat.
Client A sees the robot belonging to Client B as a remote entity.
The same happens to Client B: they see Client A's robot as remote.
Authority transfer has been disabled for the robot mechs, so even if cheating, Clients couldn't be stealing authority from each other.
Client A also has State authority over some cosmetic items they are wearing.
They can turn them on/off at any time by enabling/disabling the MeshRenderer component, or literally remove them and leave them on the ground.
If Client A drops an item to the ground, the entity gets abandoned by them. It is now an orphan, and won't move for the duration of the match.
If Client B finds the cosmetic item and picks it up, they will adopt it and can now wear it on themselves.
We hope that using this example you can see all the possibilities that a flexible authority system can provide.
Authority over an Entity is transferrable, so it is possible to move the authority between different Clients or even to a Simulator. This is useful for things such as balancing the simulation load, or for exchanging items. It is possible for an Entity to have no Client or Simulator as the authority - these Entities are considered orphaned and are not simulated.
In the design phase, CoherenceSync objects can be configured to handle authority transfer in different ways:
Request. Authority transfer may be requested, but it may be rejected by the current authority.
Steal. Authority will always be given to the requesting party on a FCFS ("first come first serve") basis.
Disabled. Authority cannot be transferred.
When using Request, an optional callback OnAuthorityRequested
can be set on the CoherenceSync behaviour. If the callback is set, then the results of the callback will override the Approve Requests setting in the behaviour.
The request can be approved or rejected in the callback.
Support for requests based on CoherenceClientConnection.ClientID
is coming soon.
When Lifetime is set to Persistent, you will see an extra checkbox called Auto-adopt Orphan.
Enabling this option makes it so that if the entity is abandoned by its owner, as soon as possible the Replication Server will assign it to a Client again. This can be useful for instance in a big game world, where entities often go out of LiveQueries. When they are first seen again by a Client, the Auto-adopt Orphan option ensures that the Client takes over that entity (i.e. its State authority) without you having to write code for it.
Note: If you abandon an entity but it's still in your LiveQuery, on the next frame the Replication Server might assign it to you again. If you want more control over that, then perhaps you should turn Auto-adopt Orphan off, and implement callbacks to the authority events for that entity.
Requesting authority is very straight-forward.
RequestAuthority
returns false
if the request was not sent. This can be because of the following reasons:
The sync is not ready yet.
The entity is not allowed to be transferred becauseauthorityTransferType
is set to NonTransferable
.
There is already a request underway.
The entity is orphaned, in which case you must call Adopt
instead to request authority.
The request itself might fail depending on the response of the current authority.
As the transfer is asynchronous, we have to subscribe to one or more Unity Events in CoherenceSync to learn the result.
Also because of their asynchronous nature, clients can receive commands for entities that they have already transferred. Such commands are dropped.
These events are also exposed in the Custom Events section of the CoherenceSync inspector.
Sometimes, distributing authority is not the way to go. Certain types of games require a model where the server (or, we should say, the Simulator) is in control of the simulation of the whole game, and the players only send inputs to it. The Simulator elaborates these inputs, and in response updates the Client about the new game state.
These are usually referred to as server-authoritative games.
In competitive games. Many game genres use client inputs and centralized simulation to guarantee the fairness of actions or the stability of physics simulations.
In situations where Clients have low processing power. If the Clients don't have sufficient processing power to simulate the World it makes sense to send inputs and just display the replicated results on the Clients.
In situations where determinism is important. RTS and fighting games can use CoherenceInput
and rollback to process input events in a shared (not centralized) and deterministic way so that all Clients simulate the same conditions and produce the same results.
coherence currently only supports using CoherenceInput
in a centralized way, where a single Simulator is setup to process all inputs and replicate the results to all Clients.
Setting up an object for server-side simulation using CoherenceInput and CoherenceSync is done in three steps:
The simulation type of the CoherenceSync component is set to Server Side With Client Input
Setting the simulation type to this mode instructs the Client to automatically transfer State Authority for this object to the Simulator that is in charge of simulating inputs on all objects.
Each simulated CoherenceSync component is able to define its own, unique set of inputs for simulating that object. An input can be one of:
Button. A button input is tracked with just a binary on/off state.
Button Range. A button range input is tracked with a float value from 0 to 1.
Axis. An axis input is tracked as two floats from -1 to 1 in both the X and Y axis.
String. A string value representing custom input state. (max length of 63 characters)
To declare the inputs used by the CoherenceSync component, the CoherenceInput component is added to the object. The input is named and the fields are defined.
In this example, the input block is named Player Movement and the inputs are WASD and mouse for the XY mouse position.
In order for the inputs to be simulated on CoherenceSync objects, they must be optimized through baking.
If the CoherenceInput fields or name is changed, then the CoherenceSync object must be re-baked to reflect the new fields/values.
When a Simulator is running it will find objects that are set up using CoherenceInput components and will automatically assume authority and perform simulations. Both the Client and Simulator need to access the inputs of the CoherenceInput of the replicated object. The Client uses the Set* methods and the Simulator uses the Get* methods to access the state of the inputs of the object. In all of these methods, the name parameter is the same as the Name field in the CoherenceInput component.
Check the CoherenceInput API for a complete list of the available methods.
For example, the mouse click position can be passed from the Client to the Simulator via the "mouse
" field in the setup example.
The Simulator can access the state of the input to perform simulations on the object which are then reflected back to the Client just as any replicated object is.
Each object only accepts inputs from one specific Client, called the object's Input Authority.
When a Client spawns an object it automatically becomes the Input Authority for that object. The object's creator will retain control over the object even after state authority has been transferred to the Simulator.
If an object is spawned directly by the Simulator, you will need to assign the Input Authority manually. Use the TransferAuthority method on the CoherenceSync component to assign or re-assign a Client that will take control of the object:
The ClientId used to specify Input Authority can currently only be accessed from the ClientConnection class. For detailed information about setting up the ClientConnection Prefab, see the Client connections page.
Use the OnInputAuthority and OnInputRemote events on the CoherenceSync component to be notified whenever an object changes input authority.
Only the object's current State Authority is allowed to transfer Input Authority.
In order to get notified when the Simulator (or host) takes state authority of the input you can use the OnInputSimulatorConnected event from the CoherenceSync component.
The OnInputSimulatorConnected event can also be raised on the Simulator or host if they have both input and state authority over an entity. This allows the session host to use inputs just like any other client but might be undesirable if input entities are created on the host and then have their input authority transferred to the clients.
To solve this you can check the CoherenceSync.IsSimulatorOrHost flag in the callback:
The CoherenceLiveQuery component can be used to limit the visible portion of the Game World that a player is allowed to see. The Replication Server filters out networked objects that are outside the range of the LiveQuery so that players can't cheat by inspecting the incoming network traffic.
When a query component is placed on a Game Object that is set to Server Side With Client Inputs the query visibility will be applied to the Game Object's Input Authority (i.e., the player) while the component remains in control of the State Authority (i.e. the Simulator). This prevents players from viewing other parts of the map by simply manipulating the radius or position of the query component.
See Area of interest for more information on how to use queries.
Using Server-side simulation takes a significantly longer period of time from the Client providing input until the game state is updated, compared to just using Client-side simulation. That's because of the time required for the input to be sent to the Simulator, processed, and then the updates to the object returned across the network. This round-trip time results in an input lag that can make controls feel awkward and slow to respond.
If you want to use a Server-authoritative setup without sacrificing input responsiveness, you need to use Client-side prediction. With Client-side prediction enabled, incoming network data is ignored for one or more bindings, allowing the Client to predict those values locally. Usually, position and rotation are predicted for the local player, but you can toggle Client-side prediction for any binding in the Configuration window.
By processing inputs both on the Client and on the Server, the Client can make a prediction of where the player is heading without having to wait for the authoritative Server response. This provides immediate input feedback and a more responsive playing experience.
Note that inputs should not be processed for Clients that neither have State Authority nor Input Authority. That's because we can only predict the local player; remote players and other networked objects are synced just as normal.
With Client-side prediction enabled, the predicted Client state will sometimes diverge from the Server state. This is called misprediction. When misprediction occurs, you will need to adjust the Client state to match the Server state in one way or another. This is called Server Reconciliation.
There are many possible approaches to Server Reconciliation and coherence doesn't favor one over another. The simplest method is to snap the Client state to the Server state once a misprediction is detected. Another method is to continuously blend from Client state to Server state.
Misprediction detection and reconciliation can be implemented in a binding's OnNetworkSampleReceived
event callback. This event is called every time new network data arrives, so we can test the incoming data to see if it matches with our local Client state.
The misprediction threshold is a measure of how far the prediction is allowed to drift from the Server state. Its value will depend on how fast your player is moving and how much divergence is acceptable in your particular game.
Remember that incoming sample data is delayed by the round-trip time to the Server, so it will trail the currently predicted state by at least a few frames, depending on network latency. The simulationFrame
parameter tells you the exact frame at which the sample was produced on the authoritative Server.
For better accuracy, incoming network samples should be compared to the predicted state at the corresponding simulation frame. This requires keeping a history buffer of predicted states in memory.
This feature is in the experimental phase.
A client-hosted session is an alternative way to use CoherenceInput in Server Side With Client Input mode that doesn't require a Simulator.
A Client that created a Room can join as a Host of this Room. Just like a Simulator, the Host will take over the State Authority of the CoherenceInput objects while leaving the Input Authority in the hands of the Client that created those objects.
The difference between a Host and a Simulator is that the Host is still a standard client connection, which means it counts towards the Room's client limit and will show up as a client connection in the connection list.
To connect as a Host all we have to do is call CoherenceBridge.ConnectAsHost: