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

Sending data to SpatialOS

All code examples in this section assume you have defined a MyComponents function as described in Providing components, and set up the following preamble:

#include <improbable/standard_library.h>
#include <improbable/worker.h>
#include <example.h>

using namespace improbable;

Use the worker::Connection to send data such as component updates, logs, and metrics to SpatialOS.

Component updates

Sending component updates

When a worker instance has write access authority over a component for some entity, it can send component updates for that component to SpatialOS. Do this using the worker::Connection method SendComponentUpdate<T> which takes a reference to a T::Update object. The template parameter T should be a component metaclass from the schema-generated code.

A component update can modify the value of a property or trigger an event. You can modify multiple properties and trigger multiple events in the same component update.

Component updates sent by the worker instance will appear in the operation list returned by a subsequent call to Connection::GetOpList. This means that OnComponentUpdate callbacks will also be invoked for component updates triggered by the worker itself, but not immediately.

There are a couple of (related) reasons that callbacks for sent component updates should not be invoked immediately:

  • This can lead to extremely unintuitive control flow when components are recursively updated inside a callback
  • It violates the guarantee that callbacks are only invoked as a result of a call to Process<T> method on the worker::Dispatcher.

Receiving component updates

To be notified when a worker instance receives a component update on an entity in the worker instance’s local view of the simulation, use the worker::Dispatcher method OnComponentUpdate with the same template paramater T as for sending updates.

Note that the component updates:

  • can be partial, i.e. only update some properties of the component
  • do not necessarily have to contain data that is different from the worker instance’s current view of the component
  • could have been sent by SpatialOS rather than a worker instance for synchronization purposes

Sending and receiving component events

Sending and receiving events works much in the same way as component updates. For a schema like the following:

package example;

type SwitchToggled {
  int64 time = 1;

component Switch {
  id = 1234;
  bool is_enabled = 1;
  event SwitchToggled toggled;

To trigger an event:

void TriggerEvent(worker::Connection& connection, worker::EntityId entity_id) {
  example::Switch::Update update;
  example::SwitchToggled event{1};
  connection.SendComponentUpdate<example::Switch>(entity_id, update);

If you do not have write access authority over the component, your event will be silently ignored.

Receiving an event works just like receiving a component update, by registering a callback on the dispatcher:

    [&](const worker::ComponentUpdateOp<example::Switch>& op) {
      // `op.Update.toggled()` contains a list of SwitchToggled events.
      for (auto it : op.Update.toggled()) {
        std::cout << "Switch toggled at " << it.time() << std::endl;

Configuring interest in components

A worker instance gets updates from the entity database in the SpatialOS Runtime about any entity components that it has active read access to. There are three prerequisites for active read access:

When the set of entity components that a worker instance has interest in changes, the worker instance receives an OnAddComponent and an OnRemoveComponent callback to reflect this change.

For example, you might want to receive updates about one specific switch, but not every switch in the world:

void ComponentInterest(worker::Connection& connection, worker::EntityId entity_id) {
      {{example::Switch::ComponentId, worker::InterestOverride{/* IsInterested */ true}}});

Component commands

Sending component commands

To send a command request, use the worker::Connection method SendCommandRequest<T>. The command is executed by the worker instance that currently has write access authority over the component containing the command on the entity specified in the command request.

SendCommandRequest<T> takes:

  • an entity ID
  • an optional timeout
  • a T::Request object
  • an optional CommandParameters object

    This contains a field called AllowShortCircuit, which, if set to true, will try to “short-circuit” the command and avoid a round trip to SpatialOS in some cases. See documentation on commands for more information.

  • The template parameter T should be a command metaclass defined in the schema-generated code.

Before sending the command, register a callback to handle the response with the worker::Dispatcher with OnCommandResponse<T>. You can match up the request ID returned by SendCommandRequest (of type worker::RequestId<worker::OutgoingCommandRequest<T>>) with the one in the CommandResponseOp to identify the request that is being responded to.

Receiving component commands

To handle commands issued by another worker instance, use the opposite flow:

  1. Register a callback with the worker::Dispatcher with OnCommandRequest<T>.
  2. When the callback is executed:

Note that a RequestId only uniquely identifies a single command invocation from the perspective of a single worker instance; that is, the RequestId used by the calling worker instance and the RequestId used by the caller worker instance for handling that same command are unrelated.

Command failures

Commands can fail. When a command fails, check the the StatusCode field in the CommandResponseOp, and then retry the command as necessary.

The caller always gets a response callback, which can be one of the following failure cases:

  • ApplicationError: The command is rejected by the target worker instance or by SpatialOS.
  • AuthorityLost: The target worker instance lost write access authority, or no worker instance had write access authority.
  • NotFound: The target entity, or target component on the entity, didn’t exist.
  • PermissionDenied: The sending worker instance didn’t have permission to send request.
  • Timeout
  • InternalError: A bug might exist in SpatialOS. Raise a support request or ask on our forums.

For more detail on these status codes, see the documentation for StatusCode.

Entity queries

In order to send an entity query, a worker instance must be of a type that has permission to do so. For more information, see the Worker permissions page.

You should keep entity queries as limited as possible. All queries hit the network and cause a runtime lookup, which is expensive even in the best cases. This means you should:

  • always limit queries to a specific sphere of the world
  • only return the information you need from queries (eg the specific components you care about)
  • if you’re looking for entities that are within your worker instance’s interest radius, search internally on the worker instance instead of using a query

A worker instance can run remote entity queries against the simulation by using the Connection method SendEntityQueryRequest. This takes a worker::query::EntityQuery object and an optional timeout.

Like other request methods, this returns a worker::RequestId, which can be used to match a request with its response.

The query object

The query object is made of

  • a worker::query::Constraint, which determines which entities are matched by the query.

    Available constraints:

    • EntityIdConstraint: Matches a specific entity ID.
    • ComponentConstraint: Matches entities with a particular component.
    • SphereConstraint: Matches entities contained in the given sphere.
    • AndConstraint: Matches entities that match all of the given subconstraints.
    • OrConstraint: Matches entities that match any of the given subconstraints.
    • NotConstraint](/reference/13.8/cppsdk/api-reference#worker-query-not_constraint): Matches entities that do not match the given subconstraint.

Note: An AndConstraint with no subconstraints is equivalent to a true query and will match all entities. Conversely, an OrConstraint with no subconstraints is equivalent to a false query and will match no entities.

  • a worker::query::ResultType, which determines what data is returned for matched entities.

    Available result types:

    • CountResultType: Returns the number of entities that matched the query.
    • SnapshotResultType: Returns a snapshot of component data for entities that matched the query.

      To select all components, use worker::query::SnapshotResultType snapshotResultType{{}};.

      To select every component whose ID is contained in the given set, use worker::query::SnapshotResultType snapshotResultType{{componentIdSet}}; (thus, pass an empty set to get no components but entity IDs only).

The query response

The response is received via a callback registered with the worker::Dispatcher using the OnEntityQueryResponse method.

The EntityQueryResponseOp contains:

Success or failure of the request is indicated by the StatusCode field of the response object, but in the failure case the result may still contain some data: the count or snapshot map might still contain the data for some entities that matched the query, but won’t necessarily contain all matching entities. This is because the worker instance might still be able to do something useful with a partial result.

Sending and receiving metrics

You can optionally send metrics by calling Connection::SendMetrics. Metrics can be viewed on deployment dashboards and in the Inspector.

Typical use cases for sending metrics are as follows:

  • Reporting your own metrics. There are APIs for both time series and histogram metrics. However, only time series metrics are currently exposed via the Inspector. Unlike the metrics that are listed on Metrics reference, your own metrics are not persistent.
  • Updating a worker instance’s load. The load of a worker instance is a floating-point value. The reported values direct SpatialOS’s load balancing strategy:

    • A value of 0 indicates an unloaded worker instance.
    • Values above 1 correspond to an overloaded worker instance.


The following example demonstrates both use cases:

// A queue of tasks the worker has to complete.
std::queue<std::function<void()>> taskQueue;
constexpr double kMaximumQueueSize = 200;  // An arbitrary maximum value.

void SendUserMetrics(worker::Connection& connection) {
  worker::Metrics metrics;
  // Update the current load of the worker.
  double load = static_cast<double>(taskQueue.size()) / kMaximumQueueSize;
  metrics.Load = load;

  // Update the custom metrics.
  metrics.GaugeMetrics["MyCustomMetric"] = 1.0;


Worker instances automatically send several built-in, internal metrics at a period defined by the BuiltInMetricsReportPeriodMillis field of worker::ConnectionParameters. You can register a callback to receive these metrics inside a worker::MetricsOp using Dispatcher::OnMetrics:

void RegisterMetricsCallback(worker::Dispatcher& dispatcher) {
  dispatcher.OnMetrics([&](const worker::MetricsOp& op) {
    auto short_circuit_rate =
    // Do something with the metric, or store it...
    std::cout << "Command requests short-circuited per second: " << short_circuit_rate->second
              << std::endl;

The full list of built-in gauge metrics is as follows. All rate metrics are per-second.

Metric name (std::string) Metric value (double)
connection_send_queue_size The current size of the send queue (the messages waiting to be sent to SpatialOS).
connection_send_queue_fill_rate The rate at which messages are being added to the send queue.
connection_receive_queue_size The current size of the send queue.
connection_receive_queue_fill_rate The rate at which messages are being received from SpatialOS and added to the receive queue.
connection_oplist_queue_size The current size of the op list.
connection_oplist_queue_fill_rate The rate at which ops are being added to the internal OpList (the queue of processed messages that workers operate on).
connection_log_message_send_rate The rate at which log messages are being sent.
connection_component_update_send_rate The rate at which component updates are being sent.
connection_add_component_send_rate The rate at which add components are being added sent.
connection_remove_component_send_rate The rate at which remove components are being sent.
connection_command_request_send_rate The rate at which command requests are being sent.
connection_command_response_send_rate The rate at which successful command responses are being sent.
connection_command_failure_send_rate The rate at which command failure responses are being sent.
connection_local_command_timeouts The total local commands that timed out when waiting for a response.
connection_local_command_timeouts_rate The rate at which local commands time out when waiting for a response.
connection_unexpected_command_response_receives The total unexpected command responses recieved.
connection_unexpected_command_response_receives_rate The rate at which unexpected command responses are recieved.
connection_reserve_entity_id_request_send_rate The rate at which requests to reserve an entity ID are being sent.
connection_reserve_entity_ids_request_send_rate The rate at which requests to reserve multiple entity IDs are being sent.
connection_create_entity_request_send_rate The rate at which entity creation requests are being sent.
connection_delete_entity_request_send_rate The rate at which entity deletion requests are being sent.
connection_entity_query_request_send_rate The rate at which entity query requests are being sent.
connection_component_interest_send_rate The rate at which component interest updates are being sent.
connection_authority_loss_imminent_acknowledgement_send_rate The rate at which imminent authority loss acknowledgements are being sent.
connection_command_request_short_circuit_rate The rate at which command requests are being short-circuited.
connection_command_response_short_circuit_rate The rate at which successful command responses are being short-circuited.
connection_command_failure_short_circuit_rate The rate at which command failure responses are being sent.
connection_flag_update_op_receive_rate The rate at which FlagUpdate Ops are being received.
connection_critical_section_op_receive_rate The rate at which CriticalSection Ops are being received.
connection_add_entity_op_receive_rate The rate at which AddEntity Ops are being received.
connection_remove_entity_op_receive_rate The rate at which RemoveEntity Ops are being received.
connection_reserve_entity_id_response_op_receive_rate The rate at which ReserveEntityIdResponse Ops are being received.
connection_reserve_entity_ids_response_op_receive_rate The rate at which ReserveEntityIdsResponse Ops are being received.
connection_create_entity_response_op_receive_rate The rate at which CreateEntityResponse Ops are being received.
connection_delete_entity_response_op_receive_rate The rate at which DeleteEntityResponse Ops are being received.
connection_entity_query_response_op_receive_rate The rate at which EntityQueryResponse Ops are being received.
connection_add_component_op_receive_rate The rate at which AddComponent Ops are being received.
connection_remove_component_op_receive_rate The rate at which RemoveComponent Ops are being received.
connection_authority_change_op_receive_rate The rate at which AuthorityChange Ops are being received.
connection_component_update_op_receive_rate The rate at which ComponentUpdate Ops are being received.
connection_command_request_op_receive_rate The rate at which CommandRequest Ops are being received.
connection_command_response_op_receive_rate The rate at which CommandResponse Ops are being received.
connection_egress_bytes The number of bytes that have been sent. This refers to bytes encoded by the application layer - the actual number of bytes transmitted on the transport may be slightly higher.
connection_egress_bytes_rate The rate at which data is being sent, in bytes per second.
connection_ingress_bytes The number of bytes that have been received. This refers to bytes decoded by the application layer - the actual number of bytes received on the transport may be slightly higher.
connection_ingress_bytes_rate The rate at which data is being received, in bytes per second.
connection_delta_compression_egress_bandwidth_saved_bytes The number of network egress bytes saved through delta compressing component updates.
connection_delta_compression_egress_bandwidth_saved_bytes_rate The rate at which network egress bandwidth is saved through delta compressing component updates, in bytes per second.
connection_delta_compression_egress_total_diffs_sent The number of delta compressed component updates sent.
connection_delta_compression_egress_total_diffs_sent_rate The rate at which delta compressed component updates are sent, in updates per second.
connection_delta_compression_egress_diffs_abandoned The number of delta compressed component updates abandoned (due to taking too long to compute or being too large).
connection_delta_compression_egress_diffs_abandoned_rate The rate at which delta compressed component updates are abandoned, in updates per second.
connection_delta_compression_ingress_bandwidth_saved_bytes The number of network ingress bytes saved through delta compressing component updates.
connection_delta_compression_ingress_bandwidth_saved_bytes_rate The rate at which network ingress bandwidth is saved through delta compressing component updates, in bytes per second.
raknet_receive_buffer_size The current size of the RakNet receive buffer.
raknet_send_buffer_size The current size of the RakNet read buffer.
raknet_send_buffer_size_bytes The number of bytes in the RakNet send buffer.
raknet_resend_buffer_size The number of messages waiting in the RakNet resend buffer.
raknet_resend_buffer_size_bytes The number of bytes in the RakNet resend buffer.
raknet_packet_loss_last_second The packet loss over the last second. This number will range from 0.0 to 1.0.
raknet_packet_loss_lifetime The packet loss average over the lifetime of the connection. This number will range from 0.0 to 1.0.
raknet_last_ping_seconds The response time of the last ping emitted by the RakNet client.
kcp_send_queue_size_packets The number of KCP packets currently in the send queue, waiting to be sent.
kcp_recv_queue_size_packets The number of KCP packets currently in the receive queue, waiting to be processed.
kcp_smoothed_round_trip_time_seconds A time-weighted moving average of the round-trip time between the worker and the Runtime.
kcp_round_trip_time_variation_seconds A time-weighted moving average of the difference between each round-trip time sample and the smoothed round-trip time.
erasure_coding_completed_batches The number of erasure codec batches whose packets were all successfully delivered.
erasure_coding_completed_batches_rate The number of erasure codec batches whose packets were all successfully delivered, per second.
erasure_coding_recovered_batches The number of erasure codec batches for which the original data was recovered even though not all packets were delivered.
erasure_coding_recovered_batches_rate The number of erasure codec batches for which the original data was recovered even though not all packets were delivered, per second.
erasure_coding_unrecoverable_batches The number of erasure codec batches for which the original data could not be recovered because too many packets were missing.
erasure_coding_unrecoverable_batches_rate The number of erasure codec batches for which the original data could not be recovered because too many packets were missing, per second.

The full list of built-in histogram metrics is as follows.

Metric name (std::string) Metric values (double)
kcp_packet_send_count The number of times each KCP packet had to be sent before its delivery was acknowledged.

You need to register an OnMetrics callback with the dispatcher to receive histogram metrics as they aren’t currently exposed in the Inspector.

Sending logs

You can send log messages by calling Connection::SendLogMessage. Log messages can be viewed on deployment dashboards.

Search results

Was this page helpful?

Thanks for letting us know!

Thanks for your feedback

Need more help? Ask on the forums