MLAPI Network Variables and object lifetime

Paul Marsh
3 min readJun 25, 2021

--

The core design decision when using MLAPI is who has the responsibility for updating the data, is it the Server or the Client? For example, in my Xevious game the server is always responsible for creating/spawning an enemy, whereas the client is responsible for creating bullets (although they are started by a server call).

Created by Client or Server

There are a number of MLAPI features that help you achieve your design. Once such feature is called the Network Variable. However, in order to use it you may have to consider the lifetime of your components.

Player Lives as a Network Variable

I decided to store the current lives of the player as a Network Variable for a couple of reasons; a) the server needs to know if the game is over, and that is when all players have died. Therefore the server needs to be aware of the life state of the players b) if you notice the Network Variable Settings have a write permission. In this example only the server can alter the number of lives remaining, i.e. it is harder for a client to cheat.

The sum of the parts is more complex than the whole??

In order to keep a nice separation of concerns I have a number of components responsible for various features, e.g. UI Manager, Player Manager, and the player’s ship. Current I have ended up with a starting sequence that looks like this.

Object Lifetime’s not in sync

In the above example we have MLAPI starting the network objects in different sequences. The ship is network-aware before the Player Manager. In the above example the Game Manager waits for an On Ship Created event to occur before the game can start. However, in order for all the MLAPI Network goodness to occur correctly it has to wait until the Player Manager has been made network-aware. In this case I decided to use a Coroutine and poll per frame — it usually takes between 1–3 frames.

Normally we design a system with the Gestalt, “the sum of parts is greater than the whole”, but with networking the more parts we have the more complexity of synchronisation is introduced.

Display lives left message

The complexity can be seen again when you consider the trivial example of the UI Manager displayed a, “you have {lives} left” message when a player starts a new life. The Player manager is responsible for knowing the maximum number of lives a player can have, NOT the Player. The Player only knows how many lives it currently has, via the Server-Only writable Network Variable. The problem is that from the perspective of the Game Manager’s Activation life time of this call, then it is simply telling the Player Manager to set the lives, and then asking the UI Manager to display a message. BUT, because of the work MLAPI is doing in asynchronously managing the Network Variable the current lives is not immediately changed. Therefore, by default the UI Manager will ask the ship for its current lives and will be told “0” rather than “3”. Therefore, again, we have to wait for the ship’s lives to be set.

In this example, this ship is only considered ‘Ready’ when something has requested its lives have changed from 0 to something else.

Summary

I am sure there is a better way of doing this, please let me know, as I am still finding my way through MLAPI. But what is certain is that when you introduce a networking layer then you can no longer assume that the state of your items is in a nice easy to follow sequence. It is much better to consider everything is a multi-threaded scenario, i.e. assume everything is out of sequence, use some form of synchronisation signalling and code defensively.

--

--