EventDispatcher<L extends GameListener, E> class final

A pre-resolved set of listeners for one event type.

This is the whole point of the event system, and the thing the old one got wrong. Dispatch used to walk - from object to object at runtime, type testing each candidate, so a class that could never accept an event was still visited and still checked, every time. Here the walk happens once, at boot: EventDescriptor.has creates the dispatcher and the collect pass fills it, so by the time an event is dispatched the receivers are already known and dispatch is an indexed loop over a plain list.

Scope is the declaring owner

A dispatcher belongs to whoever declared it in describeEvents, and it collects from that owner's composition and no further. Declared on an EntityStruct, it reaches that struct's listeners only - which is what makes onEntityMounted on MyPlayer fire for MyPlayer entities and nothing else. Declared on a GameState, the same event reaches everything beneath it, because a GameState collects from its scenes and a scene from its prefabs.

There is no event object

Delivery is a closure captured once, at declare time, rather than a method on an event class:

late final EventDispatcher<EntityLifecycleListener, Entity> entityMounted;

@override void describeEvents(EventDescriptor d) {
super.describeEvents(d); entityMounted =
d.has((listener, entity) => listener.onEntityMounted(entity)); }

// and firing it:
entityMounted.call(entity);

This replaced a GameEvent<L> class per event with a dispatchListener override. That design still allocated: an event carrying a payload had to be constructed per dispatch, so the spawn path built one object per entity and the tick built one per frame. Passing the payload as an argument removes the object entirely - zero allocation per dispatch, whatever the payload (the hot-path rules). The closure is built once during describeEvents, which rule 5 explicitly permits.

It also deleted eight classes: an event that carries a Duration is now EventDispatcher<Tickable, Duration> and needs no type of its own.

E is the payload type. For an event that carries nothing, see SignalDispatcher and EventDescriptor.hasSignal.

Properties

hashCode int
The hash code for this object.
no setterinherited
listenerCount int
How many listeners were collected. Diagnostics and tests - the point of the design is that this number is settled before the first dispatch.
no setterinherited
reverse bool
Whether to deliver in reverse collection order.
final
runtimeType Type
A representation of the runtime type of the object.
no setterinherited

Methods

call(E payload) → void
Delivers payload to every collected listener that is currently listening, in collection order.
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
toString() String
A string representation of this object.
inherited

Operators

operator ==(Object other) bool
The equality operator.
inherited