Event Simplifier
Listen to any Forge event in one line — with lambdas, filters, expiration, and one-shot support.
Introduction
Forge events are powerful, but registering a listener the traditional way requires creating a dedicated
class annotated with @Mod.EventBusSubscriber, writing a static method, annotating it with
@SubscribeEvent, and making sure the class is registered. That's a lot of boilerplate for
something as simple as "do X when a player dies."
The Event Simplifier module collapses all of that into a single method call. You pass a lambda (an inline function) directly — no extra class, no annotations, no registration ceremony. It supports optional filters, priority levels, one-shot listeners, and automatic expiration.
A lambda is a small inline function you write directly where you need it. Instead of
new MyEventListener(), you write e -> doSomething(e). The
e is the event object, and the arrow -> means "with this event, do…".
You don't need a separate file — the code lives right where you call it.
What problems does it solve?
- No boilerplate — no dedicated listener class, no annotations
- Inline filters — only trigger when a condition is met
- One-shot — automatically unregister after the first call
- Auto-expiry — listeners that clean themselves up after N ticks
- Programmatic control — unregister a listener from code at any time
- Mod-scoped cleanup — unregister all listeners for a mod in one call
EriEvents — Static API
EriEvents is the main entry point. It exposes a single static method, on(),
which accepts an event class and returns either a registered handle (simple form) or a builder (chainable
form).
Simple one-liner
When you don't need any options, pass the event class and a lambda together. EriEvents registers the
listener immediately and returns an EventHandle you can save if you ever want to cancel it.
EriEvents.on(LivingDeathEvent.class, e -> {
System.out.println(e.getEntity().getName() + " died!");
});
That's it. No class, no annotation, no MinecraftForge.EVENT_BUS.register(). The listener
stays active until you explicitly unregister it or the mod unloads.
Builder form
When you need more control (filters, priority, one-shot, expiry), call on() with only the
event class. This returns an EventBuilder you can configure with chained method calls before
calling .handle() to finalize registration.
EriEvents.on(PlayerLoggedInEvent.class)
.filter(e -> e.player.getName().equals("Steve"))
.once()
.handle(e -> e.player.sendMessage(new TextComponentString("Welcome Steve!")));
Use the one-liner when you just want to react to every occurrence of an event. Use the builder when you need conditions, priority, or automatic cleanup. Both are equivalent under the hood — the one-liner is simply a shorthand for the builder with no options.
EventBuilder — Options
The EventBuilder is what EriEvents.on(EventClass.class) returns. Every method
returns this, so you can chain them in any order. Call .handle(lambda) last
to register the listener and get back an EventHandle.
EriEvents.on(LivingDeathEvent.class)
.filter(e -> e.getEntity() instanceof EntityPlayer)
.priority(EventPriority.HIGH)
.once()
.expireAfter(6000) // 6000 ticks = 5 minutes
.modId("mymod")
.handle(e -> handleDeath(e));
Method reference
You only need to call the methods relevant to your use case. Every option has a sensible default: no filter (always fires), normal priority, persistent (not once), never expires, no mod ID.
| Method | Parameter | Description |
|---|---|---|
.filter(predicate) |
Predicate<T> |
Only invoke the handler when the condition is true. The lambda receives the event object and must return a boolean. Successive calls to filter(...) are combined with logical AND — all predicates must return true for the handler to fire. This lets you chain multiple conditions without losing any. |
.priority(level) |
EventPriority |
Sets the Forge event priority (LOWEST, LOW, NORMAL, HIGH, HIGHEST). Defaults to NORMAL. |
.once() |
— | Automatically unregisters the listener after it fires once. Useful for one-time initialization or waiting for a specific moment. |
.expireAfter(ticks) |
int |
Automatically unregisters the listener after the given number of server ticks (20 ticks = 1 second). The listener is removed even if it never fired. |
.modId(id) |
String |
Tags the listener with your mod ID. Allows bulk-unregistration with EventRegistry.unregisterAll("mymod"). |
.handle(consumer) |
Consumer<T> |
Finalizes the builder. Registers the listener on the Forge event bus and returns an EventHandle. Always call this last. |
Using filter
The filter predicate receives the same event object as the handler. If the predicate returns
false, the handler is skipped entirely — the event is not cancelled, it just isn't handled
by your listener for that occurrence.
// Only react to EntityPlayer deaths (not mobs)
EriEvents.on(LivingDeathEvent.class)
.filter(e -> e.getEntity() instanceof EntityPlayer)
.handle(e -> System.out.println("A player died!"));
// Only react when it's daytime
EriEvents.on(TickEvent.ServerTickEvent.class)
.filter(e -> e.phase == TickEvent.Phase.END)
.filter(e -> someServer.getWorld(0).isDaytime())
.handle(e -> checkDaytimeLogic());
Using once
// Wait for the first player to join, then do something once
EriEvents.on(PlayerLoggedInEvent.class)
.once()
.handle(e -> System.out.println("First login: " + e.player.getName()));
Using expireAfter
// Listen for block breaks only during the next 30 seconds
EriEvents.on(BlockEvent.BreakEvent.class)
.expireAfter(600) // 600 ticks = 30 seconds
.handle(e -> System.out.println("Block broken at " + e.getPos()));
EventHandle — Control
Every call to .handle() (or the one-liner form of EriEvents.on()) returns an
EventHandle. This object is your reference to the registered listener. Hold onto it if you
need to control the listener's lifecycle from elsewhere in your code.
EventHandle handle = EriEvents.on(TickEvent.class).handle(e -> doStuff());
// Cancel the listener at any time
handle.unregister();
// Check if the listener is still active
boolean active = handle.isActive();
// Check if the listener has fired at least once
boolean fired = handle.wasExecuted();
Method reference
| Method | Returns | Description |
|---|---|---|
unregister() |
void |
Immediately removes this listener from the Forge event bus. Safe to call multiple times. |
isActive() |
boolean |
Returns true if the listener is still registered and will fire on future events. Returns false after unregister(), .once() firing, or expiry. |
wasExecuted() |
boolean |
Returns true if the handler has been called at least once. Useful for checking whether a one-shot listener already fired. |
If your listener is permanent and you never need to stop it, you can discard the handle — just don't assign it to a variable. Only save it when you need conditional cancellation, or when you want to check whether the listener is still active.
EventRegistry — Cleanup
EventRegistry is a singleton that tracks every listener registered through
EriEvents. It lets you bulk-unregister listeners, which is especially useful during mod
unload or when switching game states.
// Unregister all listeners tagged with "mymod"
EventRegistry.getInstance().unregisterAll("mymod");
// Unregister every listener registered through EriEvents
EventRegistry.getInstance().unregisterAll();
// Get the number of currently active listeners
int count = EventRegistry.getInstance().activeCount();
Method reference
| Method | Returns | Description |
|---|---|---|
getInstance() |
EventRegistry |
Returns the singleton instance. |
unregisterAll(modId) |
void |
Unregisters all listeners that were tagged with the given mod ID via .modId(). |
unregisterAll() |
void |
Unregisters every listener currently tracked by EriEvents, regardless of mod ID. |
activeCount() |
int |
Returns the number of listeners currently registered and active. |
Recommended pattern for mods
Tag all your listeners with your mod ID, then unregister them cleanly during mod unload or when leaving a game session:
// Registration (e.g. in your @Mod init)
EriEvents.on(LivingDeathEvent.class)
.modId("mymod")
.handle(e -> onDeath(e));
EriEvents.on(PlayerLoggedInEvent.class)
.modId("mymod")
.handle(e -> onLogin(e));
// Cleanup (e.g. in FMLServerStoppingEvent)
EventRegistry.getInstance().unregisterAll("mymod");
If your mod registers listeners during a game session (not just at startup), always unregister them when the session ends. Stale listeners referencing destroyed objects can cause memory leaks or NullPointerExceptions on the next login.
Complete Example
The following example combines several features: event filtering, the Chat Builder module for rich broadcast messages, and a one-shot pattern that fires only when a specific entity is killed by a player.
// Announce when a player kills a boss
EriEvents.on(LivingDeathEvent.class)
.filter(e -> e.getEntity().getName().equals("Ender Dragon"))
.handle(e -> {
Entity source = e.getSource().getTrueSource();
if (source instanceof EntityPlayer) {
EriChat.create()
.text(source.getName()).color(TextFormatting.GOLD).bold()
.text(" killed the ").color(TextFormatting.WHITE)
.text("Dragon!").color(TextFormatting.RED).bold()
.broadcast();
}
});
Step-by-step breakdown
-
EriEvents.on(LivingDeathEvent.class)— listen to any living entity death. -
.filter(e -> e.getEntity().getName().equals("Ender Dragon"))— skip every death except the Ender Dragon's. -
.handle(e -> { ... })— when the filter passes, run this code block. -
e.getSource().getTrueSource()— get who or what caused the death. We then check it's actually a player before proceeding. -
EriChat.create()...— use the Chat Builder module to send a formatted broadcast message to all players on the server.
EriAPI modules are designed to work together. Here, Event Simplifier handles the event detection and Chat Builder handles the rich message formatting. You can similarly combine Event Simplifier with the Scheduler module to delay a reaction, or with Notifications to show toast popups to specific players.
More real-world patterns
EriEvents.on(WorldEvent.Load.class)
.filter(e -> !e.getWorld().isRemote) // server side only
.once()
.handle(e -> initServerData(e.getWorld()));
EventHandle tracker = EriEvents.on(LivingHurtEvent.class)
.filter(e -> e.getEntity() instanceof EntityPlayer)
.expireAfter(200) // 200 ticks = 10 seconds
.modId("mymod")
.handle(e -> {
EntityPlayer p = (EntityPlayer) e.getEntity();
System.out.println(p.getName() + " took " + e.getAmount() + " damage");
});
// Cancel early if needed
if (someCondition) {
tracker.unregister();
}