Get SpatialOS

Sites

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

Bridge configuration

The bridge field of the worker configuration file specifies the worker’s runtime settings. It has the following overall structure:

"bridge": {
    "worker_attribute_set": {},
    "entity_interest": {},
    "streaming_query": [],
    "component_delivery": {},
    "component_settings": {}
}

Worker attribute sets

Required field.

A worker attribute set describes a worker’s capabilities, used to work out which components the worker can have access to, as a list of free-form strings. See the worker attributes page for details.

If you’re using the new load balancer, use the worker_attribute_set field to specify the layer that this worker simulates. This must be a single string.

For example, a UnityWorker would have the following attribute set:

"worker_attribute_set": {
    "attributes": ["physics"]
}

In addition to the attribute set defined here, each worker automatically has the attribute "workerId:<worker ID>". For example, the worker MyCSharpWorker12 would have the attribute "workerId:MyCSharpWorker12" added to it.

Entity interest

Required field.

The entity interest field is about defining which entities a worker gets component updates for.

If a worker is authoritative over an entity, it gets an update whenever that entity’s components change. But it also needs to know about the entities nearby.

By default, workers get component updates for all entities they’re authoritative over, as well as for all other entities in the same chunk (the grid squares of a SpatialOS world). But chunks are square, and so this doesn’t quite work.

You can use the entity interest field to set a radius (in world units, see note below). This is a radius around entities that a worker is authoritative over. If there’s another chunk with any point inside this radius, the worker will get component updates for entities inside that chunk.

For example, to make sure a worker gets updates from entities within 10 world units of any entity the worker has write access to, use the following entity_interest setting:

"entity_interest": {
    "range_entity_interest": {
        "radius": 10
    }
}

Positions and distances are expressed in an arbitrary unit called “world unit”. It is up to the workers that run the simulation to interpret them consistently.

Streaming queries

Optional field.

What streaming queries are

SpatialOS is based on the idea that, usually, workers only need to know about in the entities they’re authoritative over, plus entities that are nearby (ie within the entity interest radius). For example, a player might need to see entities up to 50m away from them, but not any further away.

But sometimes, workers need information about other entities. For example, entities that are far away but should be visible (such as mountains), or entities for global communication that aren’t physically located anywhere (for example, a global weather system). To allow a worker to receive updates to these entities, you can use streaming queries.

Entity queries compared to streaming queries

If you want information about an entity, but you don’t need regular updates about it, use an entity query instead.

If you need regular updates about an entity, it’s better to use streaming queries: they return information periodically, and they only return the new information about a component, rather than all the information about it.

How streaming queries work

Using streaming queries is similar to checking out. If you set up a streaming query for a component, for entities with that component, the worker gets a local representation of an entity, and receives updates about it. Effectively, to the worker, it looks like the entity is within its checkout area.

The worker gets updates to this entity’s components at specific time intervals. You can configure this (see below).

Limitations

Streaming queries have some limitations:

  • The worker doesn’t receive all of the updates for each component of an entity:
    • It only gets updates at the time interval you set (see below).
    • It doesn’t receive any event updates.
  • You can only set the time interval globally for all streaming queries within a deployment.
  • You can’t change streaming queries at runtime.
  • You have to specify components to query, rather than entities.

Setting up a streaming query

First, you need to pick a component. By default, for every entity with that component, a streaming query will return updates to all components on that entity. However, it’s good practice to improve efficiency and bandwidth use by narrowing down which updates are returned.

To do this, specify:

  • which components (on the entities returned by the streaming query) you want updates for
  • a radius (around entities that a worker is authoritative over) that an entity must be within

You always need to include the improbable.Position component, and, if you’re writing a streaming query for a Unity or Unreal worker, you also need to include the improbable.Metadata component.

For example, the following snippet will send this worker:

  • updates to the components your.game.Status, your.game.Health, and your.game.Flammable for all entities with the component your.game.GloballyVisible
  • updates to all components from all entities with the component your.game.HighlyVisible, within a radius of 1000 world units around an entity that the worker is authoritative over
"streaming_query": [
    {
        "global_component_streaming_query": {
            "component_name": "your.game.GloballyVisible"
        },
        "components_streamed": {
            "specific_components": {
                "components": [
                    "improbable.Position"
                    "your.game.Status",
                    "your.game.Health",
                    "your.game.Flammable"
                ]
            }
        }
    },
    {
    "bounded_component_streaming_query": {
        "component_name": "your.game.HighlyVisible",
        "radius": 1000
        }
    }
]

Configuring the time interval

To configure the time interval between streaming queries, set streaming_query_interval in your launch configuration file.

The default time interval is 4 seconds.

Component delivery

Optional field.

This field configures the set of components a worker checks out when they check out an entity, and whether component updates are allowed to be sent in an unreliable manner.

By default:

  • only components with checkout_initially set to true are sent to the worker
  • the transmission for all component updates is RELIABLE_ORDERED

Components to check out initially

To specify the set of components a worker will get when they check out an entity, use the following fields of component_delivery:

  • checkout_all_initially (boolean). By default, this is set to false, which means only the components with checkout_initially set to true are sent to the worker when it checks out an entity.

    If set to true, all components are sent to the worker when it checks out an entity.

  • override maps fully-qualified component names to component delivery settings.

    Components without an override use the default message delivery settings as specified in the default field. They are only checked out initially if checkout_all_initially is true.

The 4 permutations of these settings for a particular component C therefore lead to the following behaviour:

  1. checkout_all_initially is false or isn’t specified and the checkout_initially override is false or isn’t specified for C - C is not checked out
  2. checkout_all_initially is true and the checkout_initially override is false or isn’t specified for C - C is checked out
  3. checkout_all_initially is false or isn’t specified and the checkout_initially override is true for C - C is checked out
  4. checkout_all_initially is true and the checkout_initially override is true for C - C is checked out

Individual component overrides only have an effect when checkout_all_initially is false or isn’t specified.

The worker can also change the set of components it is interested in at runtime - see the API documentation (C++, C#, Java) for more details.

Transmitting component updates unreliably (“quality of service”)

Component updates can be transmitted reliably or unreliably.

To prevent workers getting overloaded, you can choose to transmit some component updates unreliably. Quality of service describes the concept in more detail.

The default field of component_delivery sets the default way to transmit component updates. This can be either RELIABLE_ORDERED or UNRELIABLE_ORDERED.

This configuration applies only to messages sent to workers, not for messages sent from workers.

Examples

This example reliably transmits updates for all components except Position and Rotation, and mandates that the position component is checked out initially:

"component_delivery": {
    "default": "RELIABLE_ORDERED",
    "override": {
        "improbable.Position": {
            "checkout_initially": true,
            "message_delivery": "UNRELIABLE_ORDERED"
        },
        "mygame.coordinates.Rotation": {
            "message_delivery": "UNRELIABLE_ORDERED"
        }
    }
}

This example mandates that all components are checked out initially (using checkout_all_initially). Setting checkout_all_initially to true takes precedence over the individual components’ checkout_initially settings, so we don’t specify them:

"component_delivery": {
    "default": "RELIABLE_ORDERED",
    "checkout_all_initially": true,
    "override": {
        "improbable.Position": {
            "message_delivery": "UNRELIABLE_ORDERED"
        },
        "mygame.coordinates.Rotation": {
            "message_delivery": "UNRELIABLE_ORDERED"
        }
    }
}

Note that the initial checkouts of mygame.coordinates.Position and mygame.coordinates.Rotation components are not affected by the UNRELIABLE_ORDERED message delivery setting. Only subsequent updates will be unreliable.

Component settings

The component settings field allows you to configure various component settings. For now this allows you to set the authority handover timeout period. For more information, see Handing over authority between workers.

The component settings field allows you to configure settings for each individual component or configure a default which is applied to all other components. Below is an example of a typical configuration.

"componentSettings": {
    "perComponentSettings": {
        "improbable.MyFancyComponent": {
            "authorityHandoverTimeoutMs": 300
        }
    },
    "default": {
        "authorityHandoverTimeoutMs": 400
    }
}

In the example above, improbable.MyFancyComponent will have an authorityHandoverTimeoutMs of 300 and all other components will default to 400.

If no default is set, components will default to having an authorityHandoverTimeoutMs of 0.

Information about what the authorityHandoverTimeoutMs does can be found in Handing over authority between workers.

Delta-compressed component updates

This lets you configure which components you want to delta-compress when they are updated. If you’re sending large component updates, you might want to enable delta-compressed component updates for a component. This means that, instead of sending the entire new state, the component update will be sent as a description of how the state has changed.

Imagine you have submitted your book to an editor, but you want to add an additional page. A delta-compressed update would be sending that page to the editor with a note saying “insert this page after page X”, instead of sending the entire book including that new page.

This is a tradeoff: it saves bandwidth at the cost of CPU overhead and latency.

By default, no worker types have delta compression applied to component updates.

How delta compression works

Delta compression works using diff caches on both the SpatialOS Runtime and worker that store the last component state sent and received for a given component. Updates are sent as a binary diff across the network which, for some property types, can have large bandwidth savings.

This works best for small changes to larger property types, e.g. when appending an element to a long list, as opposed to sending the whole list again.

This feature is still in the very early stages. If you want to try it out, we recommend reading the documentation fully and in detail. In particular, if you aren’t careful about which components you delta-compress, it can crash your deployment, because - used badly - sending delta-compressed updates at too high a frequency can overload the SpatialOS Runtime. Because of this, please don’t use this feature in cloud deployments. If you use it in a cloud deployment, we reserve the right to shut your deployment down.

Usage

You enable delta compression on a per-worker, per-component basis. For example, to turn on delta-compressed component updates for improbable.ants.ToNest and improbable.ants.ToFood you would need to modify your worker.json like so:

"component_settings": {
    "per_component_settings": {
        "improbable.ants.ToNest": {
            "encoding": {
                "type": "BINARY_DELTA_COMPRESSION"
            },
        },
        "improbable.ants.ToFood": {
            "encoding": {
                "type": "BINARY_DELTA_COMPRESSION"
            },
        }
    }
}

Best practices

You should only delta-compress component properties that are large with small, infrequent changes, e.g. appending an item to a long list as opposed to changing all items at once. For example, any of the following:

  • list
  • map
  • string
  • large object types contained within components

We recommend you don’t enable delta compression on:

  • any of the above types that may change dramatically, for example a List that would have half of the contents replaced.
  • smaller types, such as single int fields, due to the overhead of calculating the diff.

You can enable delta compression for other component types, but diffs may be abandoned because:

  • the computed diff actually exceeds the size of the update.
  • the process hits the approximate cutoff time (default 5ms - we’re looking into making this configurable).
  • the process detects before diffing starts that one of the above cases is highly likely to occur.

Diffs that aren’t abandoned can still use CPU on the SpatialOS Runtime or worker side to compute.

We would strongly recommend you closely monitor behaviour and metrics (e.g. SpatialOS Runtime CPU usage and latency) after enabling this functionality, as it can cause workers to stop responding to pings.

Metrics

You can measure delta compression through worker metrics via the Inspector. Use this to confirm delta compression is working as intended, and get insight into the bandwidth you’re saving and your CPU/lag overhead due to diffs abandoned.

Inspect button

Search results

Was this page helpful?

Thanks for letting us know!

Thanks for your feedback

Need more help? Ask on the forums