Simple MLAPI Authoritative Server

This is my try at a simple authoritative server: where the clients send their commands to the server, the server decides where everyone moves to on the map and sends back the location of all clients to everyone. There is no delta compression when serializing, no client-side prediction or server reconciliation, no lag compensation. In other words: very very simple.

In the picture you can see red capsules (on the Host) that show where the server says everyone is. We do our physics movement on these objects.

The blue capsules are where the server is telling clients everyone is. They have no physics associated with them, they are just moved to the locations received in a message every 1/20 seconds. Notice that the client window has no red capsules: this is because it is not a server and is not calculating where everyone is.

There are six scripts in this project.

CustomTypes
Defines the PlayerCmd type which is what the client sends to the server (mouseButton0, horizontal, vertical, jumpButton)
Defines the PlayerState type which is sent from the server to the clients (List of clientId, position, rotation)
Defines PlayerObjectDictionary which is how a server keeps track of all non-networked red capsules and how clients keep track of non-networked blue capsules
There is code in here that explains to MLAPI how to serialize PlayerCmd and PlayerState over the network

HandlePlayerCmds
The client will save inputs to PlayerCmd every 0.01 sec. After it has saved 5, it sends out an array of PlayerCmds to the server (every 1/20 sec)
Server code is in this file as well. It shows the server saving a client PlayerCmd to a dictionary (uses clientId as a key)

ServerPlayerObjects
This is where the server has a bunch of fake red gameObjects representing clients that it moves around. It is using Unity’s CharacterController to figure out where the objects move. It uses input from each client’s PlayerCmds array.
After 1/20 sec has passed, the code sends out a PlayerState to all clients which is a List of the locations of all clients.

ClientPlayerObjects
This is where the client receives the PlayerState List from the server. It will take each PlayerState and create a fake blue gameObject that represents that client. It simply Lerps each transform to the location reported by the server.

ThirdPersonCamera
This is attached to the fake blue gameObject that ClientPlayerObjects creates that shares our clientId.

NetworkGUI
Displays a menu and bytes sent/received. Note the bytes sent/recv seems to be broken in MLAPI?

The Issue of Networking

The client records user input every 0.01 sec to a PlayerState. Every 0.05 sec it sends this size 5 array to the server. Perfect world: the server takes index 0 and moves the red client object between time 0.0 and 0.01 sec. Between 0.01 and 0.02 it uses index 1, etc. The server sends a record of where every red gameObject was calculated to be to the clients every 0.05 sec. We are using MLAPI and transport Ruffles. We can turn on the Simulator and make it so PlayerState doesn’t get to the server all the time, that messages are delayed, etc. So what we do on the server is (currently) use index 0 between time 0.0 and 0.02 sec (double!), then use index 1 from 0.02 and 0.04, index 2 from 0.04 to 0.06. We still have 2 more PlayerStates we can use. Hopefully by this time a new PlayerState set has arrived from the client. If it hasn’t, we will continue to use the last index 4 to move our red client object. Networking is hard.

Editor Setup

Pretty much the same as the Simple MLAPI Test, with these differences:

Window > MLAPI > Transports > Ruffles > Install Latest
GameObject > NetworkingManager > Select transport... > RufflesTransport
NetworkingManager > Create Player Prefab > Off (We are not using any NetworkTransport)
Ruffles Transport > Log Level > Warning
Ruffles Transport > Simulator > Use Simulator (If you want)
Layer > Add Layer... > Added "Server", "Client", and "Local"
Edit > Project Settings... > Physics > Uncheck Server/Local, etc. (We don't want Host w/ client and server obj to interact)
Important: Attach a Player prefab to ClientPlayerObjects > playerPrefab
Improvements

There’s a lot wrong here. I really struggled with how Unity organizes things vs. a regular C# project with classes. The hardest part of this small project was just trying to organize each script in a logical way! I have a CustomTypes which is used by two other classes but does nothing itself, really. I have a ThirdPersonCamera that needs ClientPlayerObjects to set what it points at. I have HandlePlayerCmds that remembers (on server) what all client input was, and that’s used by ServerPlayerObjects. I have classes exposing “static public” variables for ease of use.

For the networking memory usage, we create a new List every time we send out/receive PlayerState. Create a new array of PlayerCmds when those come in. We constantly send PlayerState even if the player is standing there. This stuff really bothered me.

Source

Found at GitHub
Using MLAPI network library with Ruffles

Simple MLAPI Test

I wanted to try out MLAPI for Unity. Here’s a picture of the Scene I made running a Host (left) and Client (right).

I’ve posted the code to GitHub. Here’s how I made the Scene:

Download MLAPI
Assets > Import Package > Custom Package... > MLAPI-Installer.unitypackage > Import
Window > MLAPI > Install

GameObject > Create Empty > Rename NetworkingManager
Add Component > MLAPI > NetworkingManager > Select transport... > UnetTransport
Add Component > New script > NetworkGUI.cs

GameObject > 3D Object > Capsule > Rename Player
Add Component > Character Controller
Add Component > MLAPI > NetworkedObject
Add Component > MLAPI > NetworkedTransform
Add Component > New script > ThirdPersonController.cs
(Also add a Cube as a Visor to Capsule and set Box Collider off so it doesn't interfere with our camera)
Create > Material > Black > Add to Visor for cool factor

Create prefab of Player, drop in NetworkedPrefabs of NetworkingManager and set Default Player Prefab

GameObject > Create Empty > Rename PlayerStart

Note I couldn’t figure out how to tell how much data was being sent out on the network with MLAPI so I used their NetworkProfiler to estimate bytes per second sent/received.