Event system changes in NeoForge 20.2
Introduction
Over the past few weeks, we have been working on updating our event system. This blog post will go through all of the changes that were made, to act as a migration guide for modders updating to NeoForge 20.2.
Note that this post does not cover specific events, but rather changes made to the event machinery itself.
Key changes
Package change
The root package was changed from net.minecraftforge.eventbus to net.neoforged.bus.
Accordingly, the API is now in net.neoforged.bus.api.
Example migration:
- import net.minecraftforge.eventbus.api.EventBus;
+ import net.neoforged.bus.api.EventBus;
In case you missed it, there is a remapping script that you can use to apply all the package changes.
Cancellable event changes
Cancellable events should now implement ICancellableEvent instead of using the @Cancelable annotation:
- @Cancelable
- public class MyEvent extends Event {
+ public class MyEvent extends Event implements ICancellableEvent {
// Your event code
}
Use setCanceled(true) to cancel events and isCanceled() to check if events are canceled. This did not change.
post now returns the posted event instead of whether the event was canceled. You can call isCanceled() on the result to achieve the previous behavior:
- if (NeoForge.EVENT_BUS.post(new MyEvent())) {
+ if (NeoForge.EVENT_BUS.post(new MyEvent()).isCanceled()) {
// Do something if the event was canceled
}
Updated @SubscribeEvent semantics
We changed some details about how @SubscribeEvent methods are detected when an object or class is registered to the an event bus:
This the new behavior for registering objects or classes to new behavior is as follows:
- All
@SubscribeEventmethods (regardless of visibility) in the target object are registered. There is no inheritance of@SubscribeEventmethods anymore, and private methods can now be used. - Superclasses or superinterfaces of the registered class are not allowed to have any declared method with
@SubscribeEvent. This prevents mistakes where a developer would assume that inheritance works. - An error will be thrown if any
@SubscribeEventmethod has mismatching static-ness: registrations with aClassmust bestatic, and registrations with an object must be non-static. This prevents mistakes wherestaticis forgotten or unnecessary. - An error will be thrown if no
@SubscribeEventmethod exists. This prevents forgetting the@SubscribeEventannotation.
abstract events cannot be listened to anymore
abstract events cannot be listened to anymore.
This should help prevent mistakes where a developer would accidentally listen to a superclass,
for example by listening to SomeEvent instead of SomeEvent.Pre.
All the superclasses of abstract events must now be abstract themselves.
A number of NeoForge events were made abstract to guard against developer mistakes.
Updated mod bus semantics
The Forge bus will not allow listeners to events that implement IModBusEvent anymore.
This should prevent subscribing to the wrong event bus.
Additionally, events dispatched on the mod busses via ModLoader such as all the
NeoForge registration events now respect event priority across different busses.
(For example, a listener registered with EventPriority.LOW will
always run before listeners from other mods registered with EventPriority.NORMAL.)
More addListener overloads
We added a few convenient overloads for lambda registration with IEventBus#addListener. For example, the following is now possible:
bus.addListener(SomeEvent.class, event -> {
// Listener code here.
});
Generic events are deprecated for removal
Generic events are deprecated for removal, and will be removed in the future1. We encourage modders to move away from them as soon as possible. NeoForge is still using them for the AttachCapabilitiesEvent only. We will address this in the capability rework.
Event results are being phased out
For now, only the @Event.HasResult annotation is deprecated for removal.
We will eventually remove the getResult() and setResult(result) methods, however many events in NeoForge still rely on them.
If you use this annotation for some events, we encourage you to use a custom enum type instead,
as they are clearer to users of your API.
If you are a user of only the getResult and setResult methods, there is nothing for you to do.
Other changes
- Removal of the subclass transformer:
Previously, the no-arguments constructor of an event subclass had to be
public. This is no longer the case - you can now make such constructorsprotected, package-private orprivateif you want to. Event#getPhaseandEvent#setPhasewere removed.@Event.HasResultis now checked when callingEvent#setResult.Event#hasResultandEvent#getResultare now final.EventListenerHelperwas removed from the API.EventListeners usetoStringfor human-readable description (listenerNamewas removed).- Performance improvement:
isCanceledchecks are automatically removed for non-cancelable events. IEventListeneris renamed toEventListenerand changed to an abstract class for performance reasons.- The ModLauncher hooks were removed, greatly simplifying the implementation of the event bus.
IEventBusInvokeDispatcherwas removed.
-
Not before Minecraft 1.21, don’t worry. ↩︎