Events
Events are Bolt's way of setting up a RPC-like call over the network.
Here we describe the types of events that can be created on Photon Bolt, their main differences, and the main usage scenarios.
In relation to how Bolt handles event, keep in mind that Bolt does not send events as a separate packet.
The way Bolt works is that it packs everything (state, events, etc) into a single packet each send tick based on your SendRate
.
Event Types
Global Events
Global Events can be both ReliableOrdered
or Unreliable
by passing in different parameters to the Create
or Post
method.
In general most Global Events will be sent in Reliable
mode (the default mode).
Global Events are sent at a connection level and not to a particular entity, meaning that the target player just need to be connected to the server in order to receive this kind of events.
They are intended for things that exist around the game like:
- Dealing with Authentication;
- Handling player inventories;
Global events have a number of overrides that allow them to fine tune who receives the event (either defined in the Bolt Event
in the user interface or overridden through code).
Entity Events
Entity Events
are always Unreliable, they are intended for small one-off effects like showing a damage indicator or maybe playing an explosion, things that are ephemeral and if one player misses it it doesn't matter.
Entity events are always sent to a particular BoltEntity
, meaning that you need a reference to the target Entity, either by getting it from a BoltEntity
component or similar.
Events Reliability
Reliable Events
Reliable events in Bolt are guaranteed to arrive and to arrive in the order they were sent.
The only reliable events that can be sent in Bolt are Global Events
.
There is a 3 byte overhead per reliable events.
Unreliable Events
Unreliable events, on the other hand, are treted by Bolt differently.
Bolt will try to pack the event into a packet in two subsequent send ticks; if it was unable to fit the event in either of those attempts, it will drop the event.
Essentially Bolt will try to fit unreliable events into the end of the packet if any space is remaining.
It will try in two subsequent packets to fit the event into the packet and after that it just gives up and deletes the event.
Keep in mind "unreliable" in this context has nothing to do with an "unreliable" network packet in the traditional sense.
There is a 1 byte overhead per unreliable events.
Buffering
Also note that Bolt will not buffer events.
If an event arrives for an entity (via an entity event) and the entity doesn’t exist, Bolt will just drop the event.
If you need a reliable entity event you should just use a global event with the entity as a field of the event.
However, keep in mind that since Bolt packs events and states together in a single packet, not everything can make it into a packet for each send tick - this means that if you try to send an event to an entity right after creating it, you will find that sometimes the event arrives before or after the entity is created.
Bolt events and entities are created in separate logical streams in the packet and they will not be ordered with respect to each other.
Bolt Event Generation
In order to create a new Event definition, go to Bolt Assets
window (Bolt/Assets
menu), click with the right mouse button, and choose New Event
.
On the Bolt Editor
window, you will be able to set the Event name, configure who can send this particular event, and create/setup all properties that will define the event.
One important aspect is who is allowed to send the event.
For Global Events
, you can choose between:
(i) Everyone
, meaning that both Server and Client peers can send this event globally,
(ii) Only Server
, so, only the server can create such event,
(iii) Only Clients
, all clients can send, but not the server, and
(iv) None
, so no one will be able to send this event globally.
For Entity Events
, the options are:
(i) Everyone
, which allows anyone to send this event,
(ii) Only Owner
, only the peer that has created the entity is able to send this event,
(iii) Only Controller
, the entity controller is the only one able to send events from this entity, and
(iv) None
, meaning that no one is able to send this event as an entity event.
After you define the Event, you need to compile Bolt (Bolt/Compile Assembly
menu).
At this moment, Bolt will generate a new class for your event derived from the Bolt Event
class.
So if we created an event EventTest
then Bolt would generate (you can see this event metadata by right clicking on it in Visual Studio
and clicking Go To Definition
):
C#
public class EventTest : Event
{
// ...
}
You then can create the event with one of the the static creation methods.
Each of these methods allows you to specify endpoint information.
If you specify no target information the defaults from the Event specified in Bolt will be used.
If you just want to send the event to a particular connection there is an override specifically for that.
C#
// For Global Events
public static EventTest Create();
public static EventTest Create(ReliabilityModes reliability);
public static EventTest Create(GlobalTargets targets);
public static EventTest Create(GlobalTargets targets, ReliabilityModes reliability);
public static EventTest Create(BoltConnection connection);
public static EventTest Create(BoltConnection connection, ReliabilityModes reliability);
// For Entity Events
public static EventTest Create(BoltEntity entity);
public static EventTest Create(BoltEntity entity, EntityTargets targets);
Once you create your event you can populate the event with your data.
The Bolt code generator will generate fields for all of the data you added when you created the event in Bolt and compiled Bolt.
For example, if we had added a field called MyField
as an integer
and a MyText
as an String
in Bolt for this event we could do:
C#
void SendEvent()
{
// Create and setup
var myEvent = EventTest.Create(GlobalTargets.AllClients);
myEvent.MyField = 5;
myEvent.MyText = "hello event!";
// Send the event
myEvent.Send()
}
Extending the Create
API, Bolt also creates methods with the name Post
(available starting on Bolt version 1.2.13
), that has the exact same signature as the Create
methods described above, but also includes parameters for all fields of the event.
The Post
methods also send the events automatically when invoked.
So, the previous code snippet could be rewritten as:
C#
void SendEvent()
{
// Create, setup and send
EventTest.Post(GlobalTargets.AllClients, 5, "hello event!");
}
You can use both methods without any problems.
Receiving Global Events
You can receive global events by deriving a class from Bolt.GlobalEventListener
and adding it to the scene.
Bolt will automatically find all GlobalEventListeners
and register them if they are in the scene when Bolt starts.
However, Bolt will de-register them after it shuts down, so if you are using a singleton listener and you want the listeners to persist make sure that you override PersistBetweenStartupAndShutdown
in your derived class and return true
.
You can also decorate a GlobalEventListener
derived class with [BoltGlobalBehaviour()]
, and using the various parameters have that listener be automatically created when Bolt starts (the two common scenarios using this attribute are adding network type specific listeners (i.e. client vs server) or scene specific listeners).
This is used in the tutorials.
Once you have a GlobalEventListener
derived class you can simply override the event handler for the Global Event
you are interested in and implement your logic for receiving the event.
You can also register your own listeners with BoltNetwork.AddGlobalEventListener
and BoltNetwork.RemoveGlobalEventListener
.
You could also forgot GlobalEventListener
and implement the interfaces yourself and manually register the listeners.
In addition to the properties that you've set to your event, Bolt also includes a few other fields that may be useful at some point:
- evt.FromSelf: Returns true if this event was sent from the own connection;
- evt.IsGlobalEvent: Returns true if this is a global event, false if an entity event;
- evt.RaisedBy: The connection which raised this event;
- evt.BinaryData:: The raw bytes of the event data.
You can read more about those properties on our API Docs here.
IEventListener
This interface can be implemented on Bolt.GlobalEventListener
, Bolt.EntityEventListener
and Bolt.EntityEventListener<T>
in order to modify if the event is still raised if the MonoBehaviour/GameObject
is disabled (by default it will not be raised).
C#
public interface IEventListener
{
bool InvokeIfDisabled { get; }
bool InvokeIfGameObjectIsInactive { get; }
}
Example:
C#
[BoltGlobalBehaviour(BoltNetworkModes.Server)]
public class BoltServerCallbacks : Bolt.GlobalEventListener, Bolt.IEventListener
{
public bool InvokeIfDisabled { return true; }
public bool InvokeIfGameObjectIsInactive { return true; }
// event callback overrides below
}