Skip to main content
Version: 26.1.x

Creating Your Own Events

Beyond the events the API ships with, you can define your own events with dev.architectury.event.EventFactory. This is handy for letting other mods (or other parts of your own mod) hook into things your mod does.

The pattern

An event is just a listener interface plus a public static final Event<T> constant created by EventFactory:

public interface ManaChangedEvent {
Event<ManaChangedEvent> EVENT = EventFactory.createLoop();

void onManaChanged(Player player, int oldMana, int newMana);
}

Fire it by calling the listener method on the event's invoker():

ManaChangedEvent.EVENT.invoker().onManaChanged(player, oldMana, newMana);

Anyone can now subscribe exactly like they would to a built-in event:

ManaChangedEvent.EVENT.register((player, oldMana, newMana) -> {
// react to the change
});

Choosing a factory method

EventFactory offers several creation strategies. Which one you pick determines how listener return values are combined.

MethodListener returnsDispatch behavior
createLoop()voidCalls every listener. Pure notification, nothing can cancel.
createEventResult()EventResultCalls listeners until one interrupts; returns that result, else pass().
createCompoundEventResult()CompoundEventResult<T>Like createEventResult, but carries extra data.
createInteractionResult()InteractionResultCalls listeners until one returns a non-PASS vanilla InteractionResult.
createConsumerLoop()(listeners are Consumer<T>)A loop whose listener type is Consumer<T>.
createEventActorLoop()EventActor<T> returning EventResultA loop of actors that can interrupt via EventResult.

There is also EventFactory.of(Function<List<T>, T>) if you need a fully custom strategy for combining listeners - the higher-level methods above are built on top of it.

Call the factory methods with no arguments

The create* methods infer the listener type from the variable you assign to, so call them empty:

Event<ManaChangedEvent> EVENT = EventFactory.createLoop(); // correct
Event<ManaChangedEvent> EVENT = EventFactory.createLoop(myThing); // throws at startup

If you'd rather be explicit, each method also has an overload that takes the listener Class<T> directly.

Example: a cancellable event

Use createEventResult() when listeners should be able to cancel or override the outcome:

public interface SpellCastEvent {
Event<SpellCastEvent> EVENT = EventFactory.createEventResult();

EventResult onCast(Player player, Spell spell);
}
// Firing it - respect a listener that cancelled the cast.
EventResult result = SpellCastEvent.EVENT.invoker().onCast(player, spell);
if (result.isFalse()) {
return; // a listener denied the cast
}
// ...otherwise proceed

See Event Results for the full set of result values and how interruption works.