Entities, Components on the Network¶
Unlike in single player, the client and the server reside on different instances in multiplayer. Therefore, components and entities have to be correctly configured to ensure that they work as intended in a multiplayer setting. Often, this means annotating the relevant fields with @Replicate
.
Replicate annotation¶
@Replicate
is an annotation used to mark types or fields to indicate that these types or fields should be replicated between the server and the client. In other words, when changes are made to these types or fields on the server, these changes will be reflected in the clients as well.
Note
This page will focus on component fields and how they can be replicated by the network. However, take note that @Replicate
can be applied on component classes
as well. This is especially important for ensuring that empty components are correctly replicated.
To illustrate how @Replicate
can be used, let’s take a look at a field in ItemComponent
:
/**
* How many of said item are there in this stack
*/
@Replicate(FieldReplicateType.SERVER_TO_CLIENT)
public byte stackCount = 1;
The stackCount
field in ItemComponent
specifies the amount of items in the current stack.
FieldReplicateType¶
As you can see, under the @Replicate
annotation, the FieldReplicateType
element is set to SERVER_TO_CLIENT
. This annotation ensures that this when the value of the stackCount
field of ItemComponent
is updated on the server (i.e. when the stack size of the item changes), its value on all connected clients will be updated as well. Obviously very important as the number of items in a stack should be always be updated on all clients!
Apart from SERVER_TO_CLIENT
, there are also a few other values for FieldReplicateType
that determine the circumstances under which the field will be replicated.
FieldReplicateType | Description |
---|---|
SERVER_TO_CLIENT (default) | The field will be replicated by the server to all clients connected to the server. |
SERVER_TO_OWNER | The field will be replicated by the server to the client only if the client is the owner (i.e. the client belongs to the entity containing the component). |
OWNER_TO_SERVER | Functionally the same as OWNER_TO_SERVER_TO_CLIENT |
OWNER_TO_SERVER_TO_CLIENT | The field will be replicated from the owner to the server. It will be then be replicated by the server to all connected clients that are not the owner. |
initialOnly¶
You can also specify the value of the initialOnly
element, which is false by default. When set to true, the field will only be replicated once when the entity containing the component first becomes relevant to the client.
For instance in ItemComponent
, it is used in maxStackSize
:
@Replicate(value = FieldReplicateType.SERVER_TO_CLIENT, initialOnly = true)
public byte maxStackSize = 99;
Unlike stackSize
, which might change over the course of a game as the player receives or uses the item, the maxStackSize
of an item does not change. Therefore, the initialOnly
element is set to true as the value of maxStackSize
only needs to be replicated by the server to the client once when the ItemComponent
first becomes relevant.
To summarise, the server will send replicated fields only when:
- It is the initial send of the component field
- The field is replicated from Server to Client
- The field is replicated from Server to Owner and the client owns the entity
- The field is replicated from owner and the client doesn’t own the entity
The exception to this is when initialOnly
is set to true and it isn’t the inital send of the component field.
Note
There is also the @NoReplicate
annotation, which is the opposite of @Replicate
annotation. It specifies that a component field should not be replicated. By default, all fields except Event fields are not replicated.
Note
Don’t forget to use entityRef.saveComponent(component)
to save change of value in the component, or the change will not replicate.
Network Component¶
However, for updates to component fields
of an entity to be replicated in a server, the entity needs to be registered on the network, which is where NetworkComponent
comes into the picture.
When NetworkSystem
is first initialised, all entities containing a NetworkComponent
are registered on the network as network entities and given a network ID. While entities might have different IDs each time, network entities are linked to their respective entities through the network IDs, allowing these entities to survive dropping in and out of relevance.
Similar to FieldReplicateType
, the ReplicateMode
enum determines which clients the entity should be replicated to (i.e. which clients the entity is registered on).
ReplicateMode | Description |
---|---|
ALWAYS | The entity will always replicated to all clients connected to the server. |
RELEVANT (default) | The entity will only be replicated to clients where it was relevant (within a certain distance). |
OWNER | The entity will always be replicated to its owner. |
An example whereby both the @Replicate
annotation and NetworkComponent
are used is in the chest.
Chests store their items in InventoryComponent
, in the following List:
@Replicate
@Owns
public List<EntityRef> itemSlots = Lists.newArrayList();
Again, the @Replicate
annotation ensures that whenever the value of the component field is updated on the server, this change will be reflected in all clients as well (recall that the default value of FieldReplicateType
is SERVER_TO_CLIENT). In other words, whenever a player modifies the items in the chest, others in the same server will be able to see this change.
However, if the chest entity is not registered on the network, not all clients connected to the server might recognise the chest entity, preventing them from interacting with it. This is why NetworkComponent
is specified in chest.prefab
as well:
...
"Network": {
}
...
Recall that the default ReplicateMode
is RELEVANT. This NetworkComponent
thus ensures that the chest entity will always be replicated by the server to a client whenever it is relevant to the client, ensuring that all interactions with the chest work as intended.