Sites

Menu
These are the docs for 14.1, an old version of SpatialOS. The docs for this version are frozen: we do not correct, update or republish them. 14.3 is the newest →

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:

You need to use version 13.7.0 or later of the SpatialOS SDK to interact with or read data from system entities. If you’re on an earlier SDK version, you can view them in the Inspector, but not interact with or read data from them from worker instances.

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

Client-worker entities won’t have the improbable.restricted.PlayerClient component if you’re using the deprecated authentication flow.

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:
  • the Runtime sending an op to a worker instance
  • the worker instance responding to that op
  • the Runtime processing the response from the worker instance
If the worker instance is not connected, the value is 0.

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

  1. 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.
  2. When the worker instance connects, the worker connection state becomes CONNECTED.
  3. 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 */);
}

This command can only succeed if the worker instance that issues it is of a worker type that has permission to send the command. This permission is included in the all permission, or you can configure it separately using the system_entity_command permission.

Search results

Was this page helpful?

Thanks for letting us know!

Thanks for your feedback

Need more help? Ask on the forums