Simulators | Flexible authority
Even when creating a game that is mainly client-driven, we can still run some of the code on a Simulator. This is very useful to create, for instance, an NPC that operates even when all Clients (players) are disconnected, to give a semblance of a living world.
In this project we used this pattern for the little yellow robot that sits beside the camp. If players move one of the camp's key objects out of place, the robot will tidy up after them. It can even recreate burned objects out of thin air!
Because this behavior is run by a Simulator, even if no-one is connected, given enough time all objects will be back in their place.
To setup the robot Prefab to be run by a Simulator couldn't be simpler. The only thing we need to do is to set the Simulate In property of the CoherenceSync
to Server Side.
By just doing so, when you start the game as a Client, the robot GameObject will be deactivated. But if starting as a Simulator (instructions are here), it will run.
We also set both the KeeperRobot
script and the NavMeshAgent
to disable on remote instances from the coherence Configuration panel, so they automatically turns themselves off on Client machines.
Besides the simple state machine code that runs it, only one thing is worth noting here.
The exact moment when the robot starts acting is not in Start
like usual. We imagined this behavior for an always-on world, so that it could start acting even long after other Clients disconnected. To ensure this, we hook into the onLiveQuerySynced
event of the CoherenceBridge
:
This way, the Simulator has the time to sync up with whatever happened to the campfire objects on the Replication Server, before even beginning to act.
This means that while gameplay can benefit from the presence of this NPC, it's not dependent on it. The Simulator can be always online, or connect and disconnect at times, or to be online only at certain times of the day, and so on.
Coding behaviors like this can open up many creative possibilities in the game's design.
One typical pattern here is to wrap any server-side logic in the conditional compilation directive #if COHERENCE_SIMULATOR
. This is a great idea especially if the code needs to be obfuscated to normal Client builds, because by doing so, it won't be compiled in the Client at all.
We did it, but we were careful to leave some things out:
As you can see, we left out the 4 Network Commands used to play sounds, and the properties they need to do it. The idea here is that the authoritative instance of the robot, which is running the logic on the Simulator, instructs the non-authoritative instances to play sounds when needed.
Remember that disabling a script only prevents Unity functions to be called (Awake
, Start
, Update
...), but it doesn't prevent invoking its methods.
Besides the above, wrapping synced variables or Network Commands inside a pre-compiler directive would hide them from coherence schema baking, effectively creating a different schema for the Simulator, which would then not be able to connect to the RS.
Make sure you keep all data of this type out of the #if
, so that both Client and Simulator bake the same schema.
Finally, you might have noticed how we not only compile this code for Simulator builds, but also when in the Unity editor:
This allows us to quickly test the behavior of this robot without adding and removing compilation directives. By simply changing the Simulate In property of the CoherenceSync
to Client Side, we can hit the Play button and see the robot move, as if a Simulator was connected.
This is a great way to speed up development and one of the advantages of coherence's flexible authority model: you don't need to code a behavior in a special way to change it from Client to Server side and vice versa.
It is good practice though to switch the robot to Server Side again at regular intervals, and test the game by making an actual Simulator build, in order to create the whole network scenario with all its actors.
This will help locate bugs that have to do with timing, connection speed, authority transfers, etc.
The code for the robot is all contained in the KeeperRobot.cs
class, in Scripts/Robot
.