Broadcasting Events
You may want to broadcast your own events, which Arcade makes simple. To get started you need to implement your event class, this is a class that contains all the data for a given event.
Events are split into two types, ServerSideEvent and ClientSideEvent, which can only be broadcast from GlobalEventHandler.Server and GlobalEventHandler.Client respectively. All the examples here are server-side, but the exact same ideas apply to the client, you just implement ClientSideEvent and broadcast from GlobalEventHandler.Client instead.
Your event class must implement one of these interfaces:
class MyEvent(
val foo: String,
val bar: Int
): ServerSideEventIf your event is player, level, or minigame related you should implement their respective event interfaces, PlayerEvent, LevelEvent, MinigameEvent, all of which are themselves ServerSideEvents. Your event can implement multiple of these.
class MyPlayerEvent(
override val player: ServerPlayer
): PlayerEventThis is for compatability with the minigames-event system, so it can tell whether a given event is relevant to the minigame, more information about this in the Minigame Event's Section.
Then all you need to do to broadcast your event is pass the event instance into the GlobalEventHandler#broadcast method, this will automatically invoke any listeners.
class MyEvent(
val foo: String,
val bar: Int
): ServerSideEvent
fun broadcastMyEvent() {
val event = MyEvent("Foo", 10)
GlobalEventHandler.Server.broadcast(event)
}As previously mentioned in the event listening section, events can have different phases, we can specify the phase we want our event to broadcast with:
class MyEvent(
val foo: String,
val bar: Int
): ServerSideEvent
fun broadcastDoingSomething() {
val event = MyEvent("Foo", 10)
// Broadcast in the "default" and "pre" phases
GlobalEventHandler.Server.broadcast(event, BuiltInEventPhases.PRE_PHASES)
// Do Something ...
// Broadcast in the "post" phase
GlobalEventHandler.Server.broadcast(event, BuiltInEventPhases.POST_PHASES)
}You do not have to use these specific phases, you can use any you wish, however, it should be noted that these should be documented and additionally, you likely want to always broadcast a "default" phase.
Cancellable Events
You may want to implement an event that is cancellable. To do this your event class must extend CancellableEvent.Simple or CancellableEvent.WithResult<T> depending on whether you want your cancellable event to have a return type, as discussed in the Cancelling Events Section. As CancellableEvent isn't itself an event, you still need to implement ServerSideEvent (or ClientSideEvent) as well.
The obvious use-case is for mixing into vanilla and adding events that allow listeners to cancel said behaviour.
class MyCancellableEvent: CancellableEvent.Simple(), ServerSideEvent@Mixin(Foo.class)
public class FooMixin {
@Inject(
method = "foo",
at = @At("HEAD"),
cancellable = true
)
private void onFoo(CallbackInfo ci) {
MyCancellableEvent event = new MyCancellableEvent();
GlobalEventHandler.Server.broadcast(event);
if (event.isCancelled()) {
ci.cancel();
}
}
}Thread Safety
By default, arcade assumes that all event listeners expect events to be broadcast from the main thread, with the exception if the event type inherits AsyncEvent. As a result, if an event is broadcast off the main thread, then it will be pushed to the main thread for listeners. The listener can explicitly specify a threading strategy, which by default pushes all non-AsyncEvents to the main thread. By default, we can choose ThreadingTarget.ForceMainThread which forces all events to be broadcasted on the main thread, or ThreadingTarget.UseCurrentThread which will force non-AsyncEvents to be broadcast on the broadcasting thread.
EventListener.of<ServerTickEvent>(
strategy = ThreadingTarget.ForceMainThread,
listener = { _ ->
}
)The reason for not wanting to require the main thread would be to allow you to mutate or cancel events that were broadcast off-thread, as mutating them after they've been pushed to the main thread is not very useful.
