This page is about the things you need to consider when designing workers.
Once you’ve thought about how you will represent things in your world with entities, and what the data is that they are comprised of (components), you will want to write game logic to enact behaviours on behalf of those entities and manipulate the state of the world.
Your SpatialOS project can include multiple types of client-side unmanaged workers. Each client worker type can present different functionality to the user, receive information about a different subset of components and have different write-access configurations over components.
Most commonly a game will have just a single client, but additional client workers may be useful to create ‘game master’ tooling to manage your game world, or alternatively to provide targeted support for multiple platforms.
In all of the example projects there’s only one type of managed worker, and the main choices you need to make are how many instances of the worker are needed.
But as well as configuring the number of workers running in the cloud to provide the computation for your game, you may also decide to separate your game logic into separate worker programs, each managing a different aspect of the game world.
For example, while the majority of your game’s code is executed in one type of worker, you could delegate responsibility for a sub-system, like pathfinding or artificial intelligence, to a dedicated program.
Reasons you might want to split sub-systems into separate managed workers include:
- They have different engine, library or language requirements
- They have distinct load balancing characteristics such as sub-systems which have different scaling computational needs as the number of entities in the simulation increases
- Performance gains from isolating logic in a lightweight program
- Separation of concerns with a modular design which can allow you to easily replace parts of your game
When deciding whether a logical sub-system within your game could be split out into a separate worker, you must also consider some of the downsides.
Each instance of a managed worker may run on a separate physical machine within a data-centre used by SpatialOS. If your intended architecture would contain workers of different types which:
- frequently communicate with each other
- share frequent access to the same components
then it is worth being mindful of the inherent latency and guarantees for inter-machine communication. Nothing beats processes running on the same machine for speed, and commands and updates can fail to arrive.
Tightly coupled sub-systems make good candidates for remaining within the same worker’s code.
Protecting data with authority
It is often the case that certain game data is too sensitive to allow clients to update values themselves. For example, the health and vital statistics for characters in the world: only server-side managed workers should be allowed to modify these, to prevent a malicious user modifying their client to grant themselves advantages.
By limiting write-access permissions for a worker you can control the extent of the changes a worker can make. If a worker attempts to update components over which they do not have write-access then the update will simply fail. To design workers well it is vital to have a good understanding of access permissions.
Complex client-server authority models
Player input and client-side visualisation often need to be extremely responsive in order to provide players with an enjoyable experience. Commonly for multiplayer online games, some ‘sensitive’ behaviours and data must be processed client-side for an instantaneous outcome locally, with server-side arbitration to correct errors.
For example, player movement might fall into this category in a fast-paced game. When the
player presses a button on their keyboard to move forwards their avatar needs to move forwards
immediately, rather than after a client-server roundtrip. Granting the client write-access over
the player entity’s
Position component would allow the client responsive control over the entity’s
position, but opens a potential vulnerability by which a malicious user could modify their client
to teleport their avatar to arbitrary locations.
In scenarios such as these a more complex approach is required.
One very effective model is to create a secondary component (e.g.
VisualPosition) to represent, in the above example,
the world position the client believes the player occupies. You give the client worker write-access to this,
and your visualisation of the player’s avatar is driven by this secondary position. You give write-access for
Position component to a server-side worker which reads from
VisualPosition and updates
Position component to match it, providing it believes the
VisualPosition was reached using a legal
move. If your server-side worker believes the client must have somehow cheated to reach
then it will not update the
Position component and instead sends a command to the client
worker to reset
VisualPosition to the current value of
This pattern can be used far more widely than movement, and similar error-correction approaches are an effective means to allow logic to be executed client-side while also preventing exploitation.
Worker directory structure
Each new worker type exists as a subdirectory within the
workers directory in your SpatialOS
project root directory with specific worker configuration files.
Where multiple types of worker share resources (such as
Entity Prefabs when working with the Unity SDK)
they can share a
workers subdirectory, which would contain a worker configuration file for each of them.