Event Simplifier

Listen to any Forge event in one line — with lambdas, filters, expiration, and one-shot support.

fr.eri.eriapi.event Forge Events Java 8 Lambdas

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.

What is a lambda?

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.

Java — Simple one-liner
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.

Java — Builder form
EriEvents.on(PlayerLoggedInEvent.class)
    .filter(e -> e.player.getName().equals("Steve"))
    .once()
    .handle(e -> e.player.sendMessage(new TextComponentString("Welcome Steve!")));
Which form should I use?

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.

Java — Full builder example
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

All methods are optional

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.

Java — Filter examples
// 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

Java — One-shot listener
// 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

Java — Expiring listener
// 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.

Java — EventHandle usage
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.
When to save the handle

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.

Java — EventRegistry
// 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:

Java — Mod-scoped cleanup pattern
// 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");
Always clean up on unload

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.

Java — Announce when a player kills the Ender Dragon
// 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.
Combining modules

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

Java — Wait for world load, then run once
EriEvents.on(WorldEvent.Load.class)
    .filter(e -> !e.getWorld().isRemote) // server side only
    .once()
    .handle(e -> initServerData(e.getWorld()));
Java — Temporary 10-second damage tracker
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();
}