Events¶
Events are sent to exactly one entity. Systems can define methods, that get called when specific events get sent to entities with certain components.
Defining events¶
An event is a class that implements the interface Event
.
Its fields should be private and should be made accessible via public getters. The event should have no setters but a constructor that takes the values for all fields and sets them.
Note
For events that are intend for network transfer, a public/protected default constructor has to be provided.
Sending events¶
The recommended way of sending events is via EntityRef.send(Event)
:
entity.send(new MyEvent(myArg1, myArg2));
Note
By default, events aren’t sent over the network. See Network Events for further options to send events over the network.
Processing events¶
To make a class a system you annotate it with the @RegisterSystem
annotation. If a system implements certain engine interfaces like UpdateSubscriberSystem
, then the methods of that interface will automatically be called by the engine. In addition to that, systems can declare methods that get called when certain events occurred at certain entities.
Event processing methods must have the following method signature:
- They must have a
@ReceiveEvent
annotation. - They must be public.
- The type of the first argument must implement
Event
. - The second argument must be of type
EntityRef
. - The rest of the arguments if there are some must implement
Component
.
Example:
@ReceiveEvent(components = {MyComponent.class, LocationComponent.class})
public void onMyComponentAdded(OnAddedComponent event, EntityRef entity, MyComponent myComponent) {...}
The method gets called, when the OnAddedComponent
event occurs at an entity, which has all of the following components: MyComponent
, LocationComponent
.
Note
The listing of a component in the annotation and the method seems to be redundant, but allows direct access to the component.
Parameter Access
@ReceiveEvent(components = {MyComponent.class, LocationComponent.class})
public void onMyComponentAdded(OnAddedComponent event, EntityRef entity, MyComponent myComponent) {
myComponent.changeSomeValues();
entity.saveComponent(myComponent);
}
Direct Access
@ReceiveEvent(components = {MyComponent.class, LocationComponent.class})
public void onMyComponentAdded(OnAddedComponent event, EntityRef entity) {
MyComponent myComponent = entity.getComponent(MyComponent.class);
myComponent.changeSomeValues();
entity.saveComponent(myComponent);
}
Which style to use depends on personal preference and readability. For a large amount of components, the direct access inside the method should be preferred.
The method signature determines when the method will be called:
- The first argument controls the type of the event.
This will also include sub-classes, e.g. for
NetworkEvent
. - The method will only be called if the entity has all components specified in the
@ReceiveEvent
annotation. - The method will only be called if the entity has all components specified in the optional method parameters. Even if these components are not included in the annotation.
- The call order for multiple event listeners is set via a priority flag in the annotation:
@ReceiveEvent(priority=EventPriority.PRIORITY_NORMAL)
. Internally, this is an int value but should always be one constant ofEventPriority
. - It is possible to restrict event handlers to be processed only on the client, the server or both:
@ReceiveEvent(netFilter=RegisterMode.ALWAYS)
. TheRegisterMode
determines when the handler will be registered. This is barely needed, as the same flag can be set globally for the entire class, using the same parameter in the@RegisterSystem
annotation.
Note
Some events like the OnAddedComponent
event are implicitly linked to a component and will only be offered to methods that require those arguments. In the upper case the event fires only when LocationComponent
got added while MyComponent
was present or when MyComponent
got added while LocationComponent
was present. When another component gets added, while MyComponent
and LocationComponent
are present, the method won’t be called.
This is the case for the following lifecycle core events, which are linked to to a component and require handling methods to list them explicitely:
OnAddedComponent
OnActivatedComponent
OnChangedComponent
BeforeDeactivateComponent
BeforeRemoveComponent
All other core events and probably all module events aren’t linked to a component. Please read the Javadoc of any event you make a system for.
Consumable events¶
Normally an event is processed by the event handling methods in the order of their priority.
Events that implement ConsumableEvent
can, however, be consumed.
Once an event is consumed its event handling stops and the remaining event handlers (with lower priority) do not see the event.
One example usage is to determine what happens with user input: When the player is in a mine cart, the input movement events may be consumed by a high priority mine cart event handler before they reach the normal movement handlers to avoid the player walking out of the cart.
The sender of consumable events can check if their event got consumed.
Some consumable events are sent as a test to figure out if there is a new system that objects with the action being taken.
For example the event BeforeItemPutInInventory
can be consumed by a new system,
to prevent the placement of items in a slot that is reserved for certain other items.