8,455 bytes added
, 17:05, 23 October 2020
Forge uses an event bus that allows mods to intercept events from various vanilla and mod behaviors.
Example: An event can be used to perform an action when a Vanilla stick is right clicked.
The main event bus used for most events is located at <code>MinecraftForge.EVENT_BUS</code>. There is another event bus for mod specific events located at <code>FMLJavaModLoadingContext.get().getModEventBus()</code> that you should only use in specific cases, more information about this bus can be found below.
Every event is fired on one of these busses, most events are fired on the main event bus but some are fired on the mod specific events bus.
== Sub Events ==
Many events have different variations of themselves, these can be different but all based around one common factor (e.g. <code>PlayerEvent</code>) or can be an event that has multiple phases (e.g. <code>PotionBrewEvent</code>). Take note that if you listen to the parent event class, you will receive calls to your method for ''all'' subclasses.
== Registering Event Handlers ==
[[File:Guide_to_Event_Handlers.png|768px|All available ways to register an event handler. Credits to Will BL of the MMD discord.]]
=== Annotated Event Handlers ===
<syntaxhighlight lang="java">
public class MyForgeEventHandler {
@SubscribeEvent
public void pickupItem(EntityItemPickupEvent event) {
System.out.println("Item picked up!");
}
}
</syntaxhighlight>
This event handler listens for the <code>EntityItemPickupEvent</code>, which is, as the name states, posted to the event bus whenever an <code>Entity</code> picks up an item.
To register this event handler, use <code>MinecraftForge.EVENT_BUS.register(<instance>)</code> and pass it an instance of your event handler class. If you want to register this handler to the mod specific event bus you should use <code>FMLJavaModLoadingContext.get().getModEventBus().register(<instance>)</code> instead.
=== Static Event Handlers ===
An event handler may also be static. The handling method is still annotated with <code>@SubscribeEvent</code> with the only difference from an instance handler is that it is also marked static. In order to register a static event handler, an instance of the class won’t do, the Class itself has to be passed in. An example:
<syntaxhighlight lang="java">
public class MyStaticForgeEventHandler {
@SubscribeEvent
public static void arrowNocked(ArrowNockEvent event) {
System.out.println("Arrow nocked!");
}
}
</syntaxhighlight>
which must be registered <code>MinecraftForge.EVENT_BUS.register(MyStaticForgeEventHandler.class)</code>.
<h3 id="eventbussubscriber">Using <tt style="font-size: 100%">@Mod.EventBusSubscriber</tt></h3>
A class may be annotated with the <code>@Mod.EventBusSubscriber</code> annotation. Such a class is automatically registered to <code>MinecraftForge.EVENT_BUS</code> when the <code>@Mod</code> class itself is constructed. This is essentially equivalent to adding <code>MinecraftForge.EVENT_BUS.register(AnnotatedClass.class);</code> at the end of the <code>@Mod</code> class's constructor.
The mod ID must be specified unless your class is already annotated with an <code>@Mod</code> annotation. You can pass the bus you want to listen to to the <code>@Mod.EventBusSubscriber</code> annotation. You can also specify the <code>Dist</code>s to load this event subscriber on. This can be used to not load client specific event subscribers on the dedicated server.
An example for a static event listener listening to <code>RenderWorldLastEvent</code> which will only be called on the physical client:
<syntaxhighlight lang="java">
@Mod.EventBusSubscriber(modid = "examplemod", dist = Dist.CLIENT)
public class MyStaticClientOnlyEventHandler {
@SubscribeEvent
public static void drawLast(RenderWorldLastEvent event) {
System.out.println("Drawing!");
}
}
</syntaxhighlight>
{{Colored box|title=Important|content=This does not register an instance of the class; it registers the class itself (i.e. the event handling methods must be static).}}
=== Non-Annotated Event Handlers ===
Event handlers do not need to be annotated if they are directly referred to using <code>IEventBus::addListener</code> or <code>IEventBus::addGenericListener</code> for generic events.
<syntaxhighlight lang="java">
@Mod("examplemod")
public class MyMainModClass {
public MyMainModClass() {
FMLJavaModLoadingContext.get().getModEventBus().addGenericListener(Entity.class, this::entityCap);
}
private void entityCap(AttachCapabilitiesEvent<Entity> event) {
System.out.println("Capability attached!");
}
}
</syntaxhighlight>
{{Colored box|title=Important|content=The generic passed into the event must be referenced directly within the code itself. Otherwise, the event will not be called whatsoever. For example, if you use <code>AttachCapabilitiesEvent<LivingEntity></code>, the event will never be called as no call of this event uses <code>LivingEntity</code>, only <code>Entity</code>.}}
== Canceling ==
If an event can be canceled, it will be marked with the <code>@Cancelable</code> annotation, and the method <code>Event#isCancelable()</code> will return <code>true</code>. The cancel state of a cancelable event may be modified by calling <code>Event#setCanceled(boolean canceled)</code>, wherein passing the boolean value <code>true</code> is interpreted as canceling the event, and passing the boolean value <code>false</code> is interpreted as “un-canceling” the event.
{{Colored box|title=Important|content=Not all events can be canceled! Attempting to cancel an event that is not cancelable will result in an unchecked <code>UnsupportedOperationException</code> being thrown, which is expected to result in the game crashing. Always check that an event can be canceled using <code>Event#isCancelable()</code> before attempting to cancel it.}}
== Results ==
Some events have an <code>Event.Result</code> as denoted by <code>@HasResult</code>. A result can be one of three things: <code>DENY</code> which stops the event, <code>DEFAULT</code> which uses the Vanilla behavior, and <code>ALLOW</code> which forces the action to take place, regardless of if it would have originally. The result of an event can be set by calling <code>setResult</code> with an Event.Result on the event.
{{Colored box|title=Information|content=Different events may use results in different ways, refer to the event's JavaDoc before using the result.}}
== Priority ==
Event handler methods have a priority. You can set the <code>priority</code> of an event handler method by setting the priority value of the annotation or the listener. The priority can be any value of the <code>EventPriority</code> enum (<code>HIGHEST</code>, <code>HIGH</code>, <code>NORMAL</code>, <code>LOW</code>, and <code>LOWEST</code>). Event handlers with priority <code>HIGHEST</code> are executed first and from there in descending order until <code>LOWEST</code> events which are executed last.
== Mod Event Bus ==
The mod event bus is primarily used for listening to lifecycle events in which mods should initialize. Many of these events are also ran in parallel so mods can be initialized at the same time. This does mean you cannot directly execute code from other mods in these events. Use the <code>InterModComms</code> system for that.
These are the four most commonly used lifecycle events that are called during mod initialization on the mod event bus:
* <code>FMLCommonSetupEvent</code>
* <code>FMLClientSetupEvent</code> & <code>FMLDedicatedServerSetupEvent</code> (''These events are only called on their respective [[Sides#Different_Kinds_of_Sides|physical side]].'')
* <code>InterModEnqueueEvent</code>
* <code>InterModProcessEvent</code>
These four lifecycle events are all ran in parallel. If you want to run code on the main thread during these events you can use the <code>enqueueWork</code> methods within the events to do so.
Next to the lifecycle events there are a few miscellaneous events that are fired on the mod eventbus where you can register, set up, or initialize various things. Most of these events are not ran in parallel in contrast to the lifecycle events:
* <code>ColorHandlerEvent</code>
* <code>ModelBakeEvent</code>
* <code>TextureStitchEvent</code>
* <code>RegistryEvent</code>
A good rule of thumb: events are fired on the mod eventbus when they should be handled during initialization of a mod.