Bolt implements a concept called “State”. Using Bolt’s State implementation you define the network state of the object as a template (using the
Bolt Assets window - see various tutorials) which includes properties for such things as transforms and many built-in primitive types. In addition, you can implement a state property as a IProtocolToken, which is essentially a way to implement arbitrary datatypes as a state property.
The power of Bolt State is that it allows the user to sync network properties, automatically, and only when they change. In addition, state makes implementing late joining almost transparent. Whenever the owner of Bolt State changes a property, Bolt will send that value to clients who are currently scoped to that entity. The user can subscribe to callbacks (in the
Attached) callback of the Entity behaviour (see here). There is a lot of other documentation on state - but I will list out a number of notes about Bolt state that are often asked:
- Bolt has a default value for each state property. When Bolt has to sync over an entity to a new connection, it does not send any properties that are set to the default value (this is an optimization).
- There is no way to customize the default value of a Bolt state property.
- State property changes are only sent to connections scoped to that entity (by default, all of them, but if using manual scoping, only those explicitly scoped).
- Bolt sends state changes at each send tick (defined in your settings). A change occurs when you change a state property value on the owner (or the controller, if the state property is marked as Everyone but controller - but in this case only the controller receives the callback. This implements some useful transparency when in this mode).
- State properties are not immediately sent. They are essentially marked “dirty” and they are not evaluated until the next send tick (or simulation tick - I would need to check). This means that if you change a value multiple times before the next time the property is sent, only the final value will be sent to the client (i.e. none of the intermediate ones).
- State properties are sent to clients based on priority (where a higher priority # in the Bolt settings is a higher priority). However, before even getting to state Bolt will sort entities on priority first and then iterate them. For each entity, bolt sorts dirty properties and sends a certain number (defined in your settings) for each entity (sorted also by entity priority) until it runs out of packet space (see Packets Under the Hood). It does this per-connection. Due to both packet size and Bolt settings, it is never guaranteed that after changing a state property that Bolt will send it in the next send tick.
- State properties are not guaranteed to all arrive on the client when the entity is attached. If you require some data to absolutely be there, use an attach token for this data (i.e. send the data in a token when you instantiate it). Keep in mind that attach tokens are relatively heavy - they get sent each send tick for the creation operation until that creation operation is acked.
- State properties are not guaranteed to arrive in any order, although they typically will be received in priority order, but this is not guaranteed. This is called “eventual consistency” which means eventually state will be consistent with the entity owner.
- In order to get callbacks for the entity you implement
- When a new entity is received, you will receive a callback for each of the initial changed properties you received in that initial packet (after
- If one of the built-in state properties is not sufficient, you can implement a state property as a ProtocolToken. This allows you complete control over the serialization for custom business logic. Keep in mind, though, that changing data inside the token will not trigger Bolt to send the property as changed. Bolt will only send the token if the token reference itself changes - i.e. if you change an existing token to another token or set it to null (or change a null token to a non-null token). Remember that tokens have to be registered with Bolt since they are created out of a factory.
- When a new connection joins, entities will automatically sync to him (based on scope). All changed properties will also be synced to him. Depending on the number of entities/properties this could take a variable amount of time. Bolt will only send so many packets per second (see Packets Under the Hood). But eventually all of the data will arrive on the client. This makes implementing late joining very simple and avoids all of the usual hacks involved (generally some sort of message buffering).
- NEVER change the maximum packet size to be higher than the default number in the Bolt settings (1200). Bolt doesn’t handle packet fragmentation (i.e. it won’t recombine packet fragments) - so if you set it higher (which will go over MTU), it will eventually crash and burn. Keep in mind the default value is 1200 which is a bit lower than MTU.
- Bolt implements a special cased “Transform” state property. This is discussed in the web documentation linked at the top. The Transform property implements interpolation / extrapolation transparently as well as implementing optional simple “render” interpolation as well. Bolt will only send Transform state changes when the transform moves. If an object doesn’t move there will be no bandwidth used.