System entities
Introduction
System entities are entities that represent data concerning the current state of a running game - for example, entities that represent connected client-worker instances.
All system entities have the improbable.restricted.System
component. You can’t create, update, or delete system entities, and worker instances do not gain authority over any components on them. You can only interact with system entities via commands defined in the improbable.restricted
package.
System entities can contain sensitive data, such as the unique identifier that confirms that a specific player is who they claim to be. It is important that this data does not end up on external player clients that could be compromised (for example, by malicious parties trying to break a game or impersonate other players).
For these reasons:
- system entities are not saved to snapshots.
- system entities are not visible in entity interest or streaming queries, to make sure you don’t accidentally receive updates about entities that you don’t expect.
- system entities are not visible to worker instances without the
system_entity_command
permission
Accessing data about system entities
You can access data about system entities:
- in the Inspector
- using query-based interest. Make sure that you trust all worker instances that have the permission to gain authority over the
improbable.Interest
component. - using queries. Queries have an existing permission system to determine which queries are permitted by each worker type, so you can make sure that your player clients can’t use queries to access system entity data.
Types of system entity
There is currently only one type of system entity: a worker entity.
Worker entities
Worker entities are entities that represent a worker instance that is interacting with a deployment.
The structure of a worker entity looks like this (populated with dummy data):
[Entity]
[improbable.restricted.System]
[improbable.restricted.Worker]
worker_id = “MyGameClient-1234”
worker_type = “GameClient”
connection
connection_status = CONNECTED
data_latency_ms = 25
connected_since_utc = 1553868663
[improbable.Position]
position = (1.0, 2.0, 3.0)
[improbable.EntityAcl]
read = {{“ServerWorker”}, {“PlayerManagementWorker”}}
write = {{{}}}
If the worker instance corresponds to a player, its corresponding worker entity has an additional component:
[improbable.restricted.PlayerClient]
player_identity = “Player12345”
provider = “myauthenticationprovider.com”
metadata = 0x6f70617175652064617461
For information about the components that make up a system entity, see the schema library documentation on system (restricted) components.
Worker entity fields
Field | Description |
---|---|
worker_id |
The worker ID that this worker instance connected with. |
worker_type |
The worker type of this worker instance. |
connection_status |
This represents whether the Runtime-side of the connection is AWAITING_WORKER_CONNECTION , CONNECTED , or DISCONNECTED .Currently the DISCONNECTED state isn’t used, because when a worker instance disconnects, the Runtime deletes that instance. |
data_latency_ms |
Data latency is a measure of the total time taken, in milliseconds, for:
For more information, see Data latency. |
connected_since_utc |
This represents the time at which the Runtime received a connection from the worker instance. The time is epoch time in milliseconds. If the worker instance is not connected, the value is 0. |
position |
This is a rough approximation of the center of the worker instance’s area of authority in the world. You shouldn’t depend on this value for gameplay, but it’s helpful to visualize in the Inspector. If the worker instance is not authoritative over any components on any entities (and therefore has no area of interest), then the position is not meaningful, and defaults to the center of the world. |
read |
The read field of the EntityAcl component.This is configured such that all worker instances with the permission to make commands to system entities have read access to this entity. |
write |
The write field of the EntityAcl component.This will never contain any entries. |
player_identity |
The contents of this component correspond exactly to the fields set in the player login request. |
Worker entity lifecycle
- The Runtime creates a worker entity when it receives and validates a login request. The worker entity’s connection is in the
AWAITING_WORKER_CONNECTION
state. - When the worker instance connects, the worker connection state becomes
CONNECTED
. - When the worker instance disconnects, for example because it crashes or the connection closes, the Runtime deletes the worker entity.
Interacting with client-worker entities
You can send a command to disconnect a client-worker entity’s underlying worker instance. This is useful if, for example, you need to remove cheating players from your game, or you need to disconnect all players so you can carry out maintenance.
You do this by issuing the disconnect
entity command against the worker entity that corresponds to the relevant worker instance.
For example:
public static void DisconnectPlayer(string playerWorkerId, Connection connection, Dispatcher dispatcher)
{
// Find the system entities for all the worker instances in the game world.
var query = new Improbable.Worker.Query.EntityQuery{
Constraint = new Improbable.Worker.Query.ComponentConstraint(Improbable.Restricted.Worker.ComponentId),
ResultType = new Improbable.Worker.Query.SnapshotResultType()
};
// Register a callback to handle the entity query response.
dispatcher.OnEntityQueryResponse(op =>
{
if (op.StatusCode == StatusCode.Success)
{
// Search through the results of the query for the entity which has a Worker component with a workerId equal to playerWorkerId.
foreach(var result in op.Result)
{
var entityId = result.Key;
var entity = result.Value;
var workerComponentData = (Improbable.Restricted.Worker.Data) entity.Get<Improbable.Restricted.Worker>().Value;
if (workerComponentData.Value.workerId == playerWorkerId)
{
// Issue a command to disconnect the player.
connection.SendCommandRequest(entityId, new Improbable.Restricted.Worker.Commands.Disconnect.Request(), 1000 /* timeout */);
}
}
}
});
// Register a callback to handle the disconnect command response.
dispatcher.OnCommandResponse<Improbable.Restricted.Worker.Commands.Disconnect>(op =>
{
// Logic to be executed on command completion.
});
// Send the entity query.
connection.SendEntityQueryRequest(query, 1000 /* timeout */);
}