Try SpatialOS

Sites

Menu
These are the docs for 12.2, an old version of SpatialOS. 13.2 is the newest →

Sending data to SpatialOS

All code examples in this section assume using Improbable.Worker; and using Example; (the generated code namespace).

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

Component updates

Sending component updates

When the worker has authority over a component on an entity, it can send component updates for that component to SpatialOS. Do this using the method Connection.SendComponentUpdate<C>, which takes an IComponentUpdate<C> object. The generic type parameter should be a subclass of IComponentMetaclass defined in 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 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 the Process method on the Dispatcher.

Sending and receiving component events

Sending and receiving events works much in the same way as property 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:

private static void TriggerEvent(Connection connection, Improbable.EntityId entityId)
{
  var update = new Example.Switch.Update();
  update.AddToggled(new Example.SwitchToggled(1));
  connection.SendComponentUpdate(entityId, update);
}

If you are not authoritative on 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:

dispatcher.OnComponentUpdate<Switch>(op =>
{
  var update = op.Update.Get();
  foreach (var toggleEvent in update.toggled)
  {
    System.Console.WriteLine("Switch has been toggled at {0}.", toggleEvent.time);
  }
})

Receiving component updates

To be notified when a worker receives a component update on an entity in the worker’s local view of the simulation, use the Dispatcher method OnComponentUpdate with the same IComponentMetaclass generic parameter C 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’s current view of the component
  • could have been sent by SpatialOS rather than a worker for synchronization purposes

Updating component interests

For each entity in its view, a worker receives updates for (“has interest in”):

  • the set of components the worker has authority over
  • the set of components it is explicitly interested in

A worker always has interest in the components of the entities it is authoritative on, in addition to any others specified in the bridge settings or using the method described below.

You can configure the set of explicitly interested components in the bridge settings. This is the default set of explicit interest for every entity.

At runtime, the worker can override this set on a per-entity, per-component basis, using the Connection.SendComponentInterest method.

When the set of interested components for an entity changes, the worker receives OnAddComponent and OnRemoveComponent callbacks to reflect this change.

For example, you might be interested in one specific switch, but not every switch in the world:

private static void SendComponentInterest(Connection connection, Improbable.EntityId entityId)
{
  var interestOverrides = new System.Collections.Generic.Dictionary<uint, InterestOverride>
  {
    { Switch.ComponentId, new InterestOverride { IsInterested = true } }
  };
  connection.SendComponentInterest(entityId, interestOverrides);
}

Component commands

Sending component commands

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

SendCommandRequest<C> takes:

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

Command failures

Commands can fail, so you should check the StatusCode field in the CommandResponseOp, and retry the command as necessary. The caller will always get a response callback, but it can be one of several failure cases, including:

  • ApplicationError (rejected by the target worker or by SpatialOS)
  • AuthorityLost (target worker lost authority, or no worker had authority)
  • NotFound (target entity, or target component on the entity, didn’t exist)
  • PermissionDenied (sending worker didn’t have permission to send request)
  • Timeout
  • InternalError (most likely indicates a bug in SpatialOS, should be reported)

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

Receiving component commands

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

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

Entity queries

In order to send an entity query, a worker must have 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’s checkout radius, search internally on the worker instead of using a query

A worker can run remote entity queries against the world by using the Connection method SendEntityQueryRequest. This takes an Improbable.Worker.Query.EntityQuery object and an optional timeout.

Like other request methods, SendEntityQueryRequest returns an Improbable.Worker.RequestId request ID, which can be used to match a request with its response.

The query object

The query object is made of:

  • An Improbable.Worker.Query.IConstraint, which determines which entities are matched by the query.

    Available constraints:

  • An Improbable.Worker.Query.iResultType, 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 new SnapshotResultType().

      To select every component whose ID is contained in the given set, use new 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 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 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.

There are two typical use cases for sending metrics:

  1. Reporting your own custom metrics. Both time series and histogram metrics are supported.
  2. Updating a worker’s load.

    The load of a worker is a floating-point value, with 0 indicating an unloaded worker and values above 1 corresponding to an overloaded worker. The reported values direct SpatialOS’s load balancing strategy.

Example

The following example demonstrates both use cases:

// A queue of tasks the worker has to complete.
private static readonly System.Collections.Queue TaskQueue = new System.Collections.Queue();
private const double MaximumQueueSize = 200; // An arbitrary maximum value.

private static void SendMetrics(Connection connection)
{
  var load = System.Convert.ToDouble(TaskQueue.Count) / MaximumQueueSize;
  var metrics = new Metrics { Load = load };
  metrics.GaugeMetrics.Add("MyCustomMetric", 1.0);
  var histogram = new HistogramMetric();
  histogram.RecordObservation(123);
  metrics.HistogramMetrics.Add("MyCustomHistogram", histogram);
  connection.SendMetrics(metrics);
}

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

Search results

Was this page helpful?

Thanks for letting us know!

Thanks for your feedback

Need more help? Ask on the forums