Appendix B: Authoritative Server Guide
This guide will help when making the transition to an authoritative server solution. Every step can be finished and tested before going on to the next step.
Start with a uLink game built for authoritative clients. One player is the server and the rest of the players connect to this server (directly or via proxy).
The first change is to make a copy of the client scenes. Name the copies with prefix server. These scenes will become the authoritative server and when done any future changes in these scenes should only concern the server.
Step 1 - Remove server's player avatar
Remove the player avatar and all other player owned objects from the server. The server should be started as a standalone build. It is very convenient to leave a camera in the server for debugging.
The changes will probably need to be done in both scripts and in the server scene. In scripts it is easy to check if the code is executed on the server by using the uLink.Network.isServer property.
Make sure these changes works with one server and at least two players.
Step 2 - All RPCs from a client goes to the server only
In an authoritative server setup, it is not possible for a client to send RPCs to other clients. You must therefore make sure the target for all RPCs sent by clients is to the server, nothing else. Your server code is then responsible for forwarding RPCs to any other clients that should receive them.
A common flow for an RPC is this example: a player in an FPS wants to enter a vehicle. The player's client sends an RPC to the server requesting to enter the vehicle. The server performs some checks to make sure that the player can enter the vehicle, for example that the vehicle is free and that the player is close enough to it. If successful, the player enters the vehicle on the server and an RPC is sent out to all clients informing them of this fact.
To avoid massive rewriting of all RPCs we recommend making the conversion as simple as possible. Skip the server side checks, for now, but do this transformation of the code:
- RPCs that were previously sent to ALL should go to the server only. The server should then execute the RPC (if needed on the server) and then send the same RPC to Others (if needed). This is a good time to think about which RPCs need to be sent to the server only and which need to be forwarded to the players. Consider using the uLink RPC modes OthersExceptOwner and AllExceptOwner to make it easier to target the correct clients when sending from the server.
- RPCs that were previously sent to one player target directly should now go to the server only and the target player should be one of the arguments in the RPC. The server should receive the RPC, read the player ID in the argument and send a new RPC to the correct player.
Step 3 - All state synchronizations sent from clients must be removed
With an authoritative server, state sync traffic can only be sent from the server to clients. Therefore the state syncs sent from clients must be removed. The easiest way to remove the state syncs is to replace them with an RPC. If state syncs were sent unreliably 15 times per second from the clients before, change this into an unreliable RPC that is sent 15 times per second from the client to the server. The RPC payload should include all the data that was included in the state sync, or equivalent data or commands that allow the server to calculate the new state. Note that, since state syncs and RPCs are handled in a very similar way by uLink internally, this change does not incur any overhead.
The server should then receive this RPC and update the internal state of the object on the server. The object's state can then be sent from the server as state sync to the proxy objects in the clients of other players. To do this, enable state synchronization for the object on the server and set the state synchronization property to unrealible.
Step 4 - Only the server can call uLink.Network.Instantiate()
Make sure no client uses any Instantiate() method in the Network class. If this is the case, move the code to a script that is triggered on the server side. A common place is inside this callback:
uLink.Network.Instantiate(player, avatar, avatar.transform.position,
If you are using dynamic assignment of view IDs in the code, make sure that that code is only executed on the server.
Only the server is allowed to make calls to uLink.Network.AllocateViewID().
The server is also responsible for cleaning up when players disconnect. Standard server side code for this is:
void uLink_OnPlayerDisconnected(uLink.NetworkPlayer player)
Step 5 - Enable isAuthoritativeServer
Make sure the server sets:
uLink.Network.isAuthoritativeServer = true;
Make sure the client sets:
uLink.Network.isAuthoritativeServer = true;
This does not turn on any new features, but it makes uLink enforce authority by blocking and logging when clients try to break the authority. For example: Clients that are sending RPCs with RPCMode not equal to RPCMode.Server will be blocked and logged.
Step 6 - Authoritative movement and prediction
When the server is authoritative, the movement and positions for all clients are ultimately dictated by the server. When a client wants to move, it must send an RPC to the server with, for example, the current velocity of the player or the position to move to. The server then performs the move for the client and sends out information about the move to all clients.
The movement information can be sent using either RPCs or state syncs. When using state syncs, different information can be sent to the proxy objects and the player's own object by using proxy state sync and owner state sync. Owner state sync could for example be used together with client-side prediction, the next subject.
To alleviate the need for clients to wait for a response from the server before moving the local player, client-side prediction is often used. This means that a client's player object is moved locally before receiving the server response, predicting where it will move to. When the client receives the server's authoritative movement information, it compares the predicted state with the server's and corrects the local view if they differ.
The server could even keep track of where the client thinks it is located and only send corrections when needed. This is how the standard uLink script uLinkStrictPlatformer works. This script also implements a more advanced form of client-side prediction that keeps a backlog of the latest moves made by the client. When a correction is received from the server, the client rewinds to the position it had prior to the corrected move, applies the corrected move and then applies the rest of the moves stored in the list. uLinkStrictPlatformer is a good starting point for implementing authoritative movement. Consider using it and adapting it to fit your needs.
A related network technique is dead reckoning, which means extrapolating other clients' positions using information about their previous movement. For example, a client can keep a list of previous moves made by each opponent and use it to predict where the players will move next, before receiving this information from the server. By interpolating a movement curve from the list, the movement can be visualized more smoothly. As in the case with client-side prediction, corrections will have to be made in case the predictions do not match the server's view.
Step 7 - Handling actions and events authoritatively
All important game actions and events that affect the outcome of the game have to pass through the server, to prevent the possibility of cheating. Examples of such events are shooting a player or picking up an item in an FPS game. Shooting is normally predicted on the client, with gunfire and damage effects happening locally to give the player instant feedback. An RPC also needs to be sent to the server saying, for example, that the player fired a gun in a certain direction. The server then performs the shot locally and sends out information about the shot to the other clients. If a player got hit, the server can state sync the new health of the player, or send out an RPC containing this information.
The example of picking up an item can be solved without using an RPC. Let's assume that the pickup is a health pack. When the player object on the server touches the health pack, the health is added to the player on the server. The server then sends an RPC to all clients saying that the health pack was picked up by a player, letting them remove it. No client-side prediction is needed for this case, since it is usually acceptable to not get instant feedback when picking something up.
The server should also check for inconsistencies in the input from clients, such as a player moving too fast or through walls. Such discrepancies may result naturally due to the differing world views of the client and the server, but could also indicate a player trying to cheat. However, as long as the server performs its own simulation and checks the input, cheats will not succeed and the game world will stay consistent. Implementing these checks is not critical for getting the game running and may be added at a later point.
Step 8 - Hosting in batchmode
The server must run from a hosting solution you have full control over, so that it can not be tampered with by players. You can also start the server in batchmode (using Unity's -batchmode command line flag) to use less resources, unless when debugging. The server needs to fully work without any user interaction. At launch it should to initialize itself and if needed register itself to the game's master server.
Make sure the uLink policy server is running on the same machine (or at least with the same external address) as the game server if you have webplayer clients.
Start clients and make sure you can find and connect to the game server, via the master server or directly.
Step 9 - Minimizing the server's resource usage
Make sure you have set the server's frame rate, to reduce CPU load, for example:
Application.targetFrameRate = 60;
Concering memory usage, the overhead for starting an empty Unity server project with uLink imported is around 7.5 Megabytes. Please note this number is just the absolute minimum a game server will use. The real memory usage for a game server will be higher due to the game assets in the server scene and other things that are needed. To minimize server memory usage make sure unused assets, especially client-only assets, are not loaded by the server. This can be done in several ways, one of which is to have a specific Unity project for the server (you can still share assets via for example Subversion's svn:externals property). There are many more ways to minimize server resource usage, which will be detailed in another document.