BoltEntity
The BoltEntity
is a Unity GameObject that will be represented on the network by Photon Bolt.
In order to mark a GameObject as networked, you need to use the BoltEntity
component.
By using this component, you transform an ordinary Unity Prefab
into a networked element, allowing Bolt to extract, sync, and manage its data.
It's also because of this component, that you are able to interact with the Entity's state, modifying it to reflect the state of your game.
BoltEntity is similar to a Unity/uLink NetworkView or PUN's PhotonView. It is the representation of a network-aware object, and is the base for having Bolt control and replicate an actual GameObject in Unity.
Entity Ownership
A BoltEntity
is neither from a Server or Client, in Bolt perspective, the ownership of entities is broken down into two separate categories: Owner
and Controller
.
They are not mutually exclusive, you can be any of the following:
- Owner: where
entity.isOwner == true
; - Controlled: where
entity.hasControl == true
; - Proxy: where
entity.isOwner == false && entity.hasControl == false
.
Owner
Being an Owner is a non-changeable property that is assigned to the peer which the BoltNetwork.Instantiate
call is issued on.
This peer is the only one where BoltEntity.isOwner
will return true.
Ownership of an entity can not be transferred to anyone else.
The owner of an entity has absolute control over everything (state, transform, etc.).
Controller
Being a Controller of an entity is something which can be assigned, taken away and transferred to other peers.
Only the Owner can assign and take away the control of an entity.
The Owner can assign control of an entity to another peer by calling entity.AssignControl(otherConnection)
and passing in the connection to the other peer.
Control can also be removed by calling entity.RevokeControl()
.
This peer will now be considered the Controller of this entity.
The Owner can also take control of an entity himself, he will then be considered both the Owner and Controller.
Taking control of an entity you are the Owner of is done by calling entity.TakeControl()
and releasing control is done with entity.ReleaseControl()
.
The Controller is the only one that the entity.hasControl
property will return true on.
Neither
If you are neither the Owner or Controller of an entity, both entity.isOwner
and entity.hasControl
will return false.
In general you can't do very much with the entity in question and it's usually something you don't have any control of in the game (like another players avatar for example).
BoltEntity Component Settings
Here is a breakdown of all the settings available on the BoltEntity
component.
You also see the description of each field on Untiy by enabling the Show BoltEntity Settings hints
in the Bolt Settings
window, under the Miscellaneous
section.
Prefab & State
- Type: utility field which shows the Unity
PrefabType
value of the current Game Object; - Scene Id: the unique ID of this
Entity
, if it's aScene Entity
that Bolt uses to reference it over the network for the initial setup; - Id: the internal ID assigned to this prefab inside Bolt. This is always
0
for scene objects; - State: the state that will be used for this
Bolt Entity
.
Settings
- Replication Rate: controls how often Bolt should try to send updates for this entity: (i) 1 = every packet, 2 = every other packet, etc;
- Persistent: if Bolt should keep this object around between scenes loaded with
BoltNetwork.LoadScene
; - Always Replicate: this setting lets you control if Bolt should ignore the current loading state of the game and always proxy this entity even if you are in the middle of a scene load.
This can be seen as Bolt's version ofGameObject.DontDestroyOnLoad
in Unity.
This is useful for 'meta' object which contain information about the game as a whole and not a specific map or character; - Proxy When Frozen: if enabled Bolt will allow this entity to perform its first replication even if its frozen;
- Detach On Disable: if enabled this entity will be detached from the network when its disabled;
- Auto Attach On Load: if enabled, scene entities will be automatically attached on map load;
- Auto Freeze Frames: if larger than 0, this entity will be automatically frozen by Bolt for non-owners if it has not received a network update for the amount of frames specified;
- Controller Predicted Movement: this setting lets you specify to Bolt if you are using local prediction on the controller of an entity, this means you are using
SimulateController
andExecuteCommand
with a character motor that is able to do local prediction and corrections; - Remove Parent On Detach: if enabled this tells Bolt to search the entire transform hierarchy of the entity being detached for nested entities and set their transform.parent to null.
- Allow Client Instantiate: is only available if you have switched
Instantiate Mode
toIndividual On Each Prefab
in theBolt Settings
window.
Lets you control if the clients can callBoltNetwork.Instantiate
for this prefab. - IEntityBehaviour Query: Bolt dynamically queries for
IBoltEntityBehaviour
implementors when instantiating new entities. By default it uses the Global Setting which does aGetComponentsInChildren
, but you can change this behaviour here. - IPriorityCalculator Query: Bolt dynamically queries for
IPriorityCalculator
implementors when instantiating new entities. By default it uses the Global Setting which does aGetComponentsInChildren
, but you can change this behaviour here. Note: most people will not have this implemented at all and should just chooseNone
for performance reasons either in global or in the prefab. - IEntityReplicationFilter Query: Bolt dynamically queries for
IEntityReplicationFilter
implementors when instantiating new entities. By default it uses the Global Settings which does aGetComponentsInChildren
, but you can change this behaviour here. Note: most people will not have this implemented at all and should just chooseNone
for performance reasons either in global or in the prefab.
Bolt Entity Details
Synching the BoltEntity to others
To sync state from the Owner
to the other peers you'll need to create a State
.
This can be done through Bolt/Assets
window.
By creating a new State
, you are creating the description of all properties that need to be networked and will be attached to a particular BoltEntity
.
You can read more about the Bolt State
at it's own dedicated page here.
The properties types availables are:
- Array: a collection of values of the same type, organized into an array format. You can access individually items by index;
- Bool: a boolean value;
- Color: Unity Color instance;
- Color32: Unity Color32 instance;
- Entity: reference to other
BoltEntity
; - Float: a float value;
- Guid: a instance of
System.Guid
; - Integer: a integer value;
- Matrix4x4: Unity Matrix4x4 instance.
- NetworkId: reference to any
Bolt.NetworkId
; - Object: instance of a
Object Bolt Asset
type; - PrefabId: reference to any
Bolt.PrefabId
; - ProtocolToken: reference to a custom token that implements the
Bolt.IProtocolToken
; - Quaternion: Unity Quaternion instance.
- String: a string value;
- Transform: Unity Transform instance.
- Trigger: special one-fire state.
- Vector: Unity Vector3 instance.
Make sure to recompile Bolt Assets after creating/changing a State
Controlling a BoltEntity
To setup how a BoltEntity
is controlled you'll need to override the entity's ExecuteCommand()
and SimulateController()
methods.
This is (usually) done for client-side prediction.
Note that the Owner
will still have full control over the actual State
of the BoltEntity
.
Note that to be able to use authoritative and client predicted movement, you have to use either the transform component directly or a Character Controller
component. You can not use mecanim root motion or rigidbodies to control your characters.
Method: BoltEntity.SimulateController()
Only runs on the person which has been assigned control of an entity.
This can either be the Owner
that has given itself control by calling entity.TakeControl()
on an Entity, or it can be someone which has a remote proxy of an object and has been given control from the owner, which the owner does by calling entity.AssignControl(BoltConnection connection)
.
You are allowed to call entity.QueueCommand
inside of SimulateController
, which is used for queuing up a command for execution.
Method: BoltEntity.ExecuteCommand(BoltCommand cmd, bool resetState)
This function runs on both the owner and controller, but has different behaviors depending on if it runs on the owner or a remote controller.
On the owner, no matter if the Owner
is the controller or not, it only runs once for each command.
No matter if the command was created locally, by the owner itself or if it came from a remote connection which has been given control.
The second parameter called resetState
will never be true on the owner.
On a controller, if it's not the owner, ExecuteCommand
is a little bit more complex.
It needs to be able to handle both local prediction of the movement but also corrections of the state from the Owner
.
The first thing that happens every frame is that Bolt will pass in the last command which has it's state verified from the Owner
, when this command get passed in the resetState
parameter will be true.
Once the latest verified command has executed with resetState
, Bolt will then run all other commands which have not been verified by the Owner
yet.
What this means in practice is that ExecuteCommand
will be called several times for one command on a remote controller during multiple frames in a row until it has been verified by the owner.
When the command which has resetState
set to true executes, Bolt wants you to set the local state of the character motor you are using to the state that is represented by the command result.
You should not use the BoltCommand.Input
here, only the BoltCommand.Result
.
This is what allows Bolt to correct the local movement of a remote controller (often a client) with the correct state from the owner (often the server).
Note that the state you need to reset is all of the state which effects movement of your entity in any way, usually this is position, rotation and some state variables like if you are grounded or crouching, etc.
If you have a complex controller other things you want to reset could be velocity, acceleration, external forces, etc.
Incorrectly/incompletely resetting state is one of the most common problems with "jittery movement" on the Controller's side
What is BoltCommand.isFirstExecution
used for?
Code which perform direct actions from your commands, for example firing your weapon or setting the animation state - should be wrapped in a block that looks like this:
C#
public override void ExecuteCommand(Bolt.Command cmd, bool resetState)
{
if (resetState)
{
// reset code
}
else
{
if (cmd.isFirstExecution)
{
// First Execution code ...
}
}
}
Since on a remote controller ExecuteCommand
will be called several times for the same command, if you don't check so that things like animations and other direct actions only happen on the first execution - your character will act very weird on the remote controllers.
What about normal proxies, which are not the controller?
The position, rotation, animation and state sync is done through the Bolt state object for these entities.
This means that any type of state, action, event, etc. you want visible to everyone connected has to be sent with either the Bolt State mechanism or over a Bolt Event, as Bolt Commands never execute on anyone but the Controller and Owner.
Entity Pooling
Internally Bolt implements an interface IPrefabPool
. The default Bolt implementation simply dynamically allocates and destroys prefabs when requested (clearly, the default internal Bolt implementation does no pooling internally). If you wish to override this behaviour, you can implement IPrefabPool
on your own custom class and override Bolt’s implementation using BoltNetwork.SetPrefabPool(IPrefabPool pool)
. If you do this, you have complete control over how Bolt prefabs are created and destroyed. You can then simply implement calls to your own custom pooling solution to convert Bolt from using dynamic instantiation to a pooling pattern. You can even replace prefabs with different prefabs if you wish - for example, if you wanted to have a server prefab and a client prefab, this is possible using this mechanism.
This is the simple pool implementation that Bolt uses by default. Notice that it does not actually pool anything (it just dynamically instantiates and destroys the entity).
C#
using UnityEngine;
using Bolt;
public class DefaultPrefabPool : IPrefabPool
{
public GameObject Instantiate(PrefabId prefabId, Vector3 position, Quaternion rotation)
{
GameObject go;
go = GameObject.Instantiate(LoadPrefab(prefabId), position, rotation);
go.GetComponent<BoltEntity>().enabled = true;
return go;
}
public void Destroy(GameObject gameObject)
{
GameObject.Destroy(gameObject);
}
public GameObject LoadPrefab(PrefabId prefabId)
{
return PrefabDatabase.Find(prefabId);
}
}
In order to use your implementation class, you need to register it when Bolt finishes the startup:
C#
public class MyGlobalListener : Bolt.GlobalEventListener
{
public override void BoltStartDone()
{
BoltNetwork.SetPrefabPool(new MyPool());
}
}
Back to top