Event
Overview
Events are the backbone of Forge's modding ecosystem, there are two major event types.
FMLEventEvent
FMLEvents are events for different aspects of mod loading.
FMLFingerprintViolationEvent: fires when the mod that is running has mismatched fingerprints.FMLInterModComms$IMCEvent: fires betweenFMLInitializationEventandFMLPostInitializationEvent, for modders to receive theirInterModCommsmessages.FMLModDisabledEvent: fires when your mod is disabled for any reason.FMLModIdMappingEvent: fires when ID mappings have changed when loading into a world.
FMLStateEvents (extends FMLEvent) specifically depicts different phases of the FML lifecycle.
Initial Loading Stages:
FMLConstructionEvent: fires when Forge finishes constructing mods, annotations, mod lists are ready to be queried here.FMLPreInitializationEvent: fires when Forge is ready to start initializing mods, you can again query annotations once again, and see where different files related to your mod would situate (e.g. config files).FMLInitializationEvent: fires after registry events are fired, game objects largely is available in this event. Hence a lot of OreDictionary activity is done here.FMLPostInitializationEvent: fires afterFMLInitializationEventis dispatched to all mods, to consolidate any manipulations the mods have made.FMLLoadCompleteEvent: fires straight before the main menu shows up, mods like JustEnoughItem does all their calculations here, it is the last event in the loading FML lifecycle.
Server Loading Stages:
FMLServerAboutToStartEvent: fires after settings and properties are initialized.FMLServerStartingEvent: fires after worlds are loaded, custom commands and more can be done here.FMLServerStartedEvent: fires when the server is ready for players.
Server Stopping Stages:
FMLServerStoppingEvent: fires when shutdown is initiated.FMLServerStoppedEvent: fires before the last tick is ran, after this the shutdown will finish. On integrated servers the menu will be loaded after this.
Listening to Events
Different event types have their own ways of being listened to and unique ways of being posted.
FMLEvents are listened to by having the
@EventHandlerannotation on methods within@Modannotated classes. These must be member methods. These listeners are called reflectively
Example
@Mod(modid = "modid", name = "Mod Name", version = "1.0")
public class ExampleClass {
@EventHandler
public void runOnPreInit(FMLPreInitializationEvent event) {
// This block of code will run when FMLPreInitializationEvent is happening
}
}Other types of events are more flexible in how they're being registered. These listeners are called natively
Annotation Magic:
@EventBusSubscriberclass level annotation- These classes must withhold from being loaded before annotations are processed.
- If it is annotated with
@Mod, themodidargument isn't needed, otherwise it is needed for recognition sake. - Any methods in here that wants to listen to an event must be static.
Example
java@EventBusSubscriber(modid = "modid") public class ExampleClass { @SubscribeEvent public static void thisIsAEventListener(Event event) { // This block of code will run when whichever Event is denoted in the argument } }EVENT_BUS interaction:Events are ran on different event buses, Forge originally wanted to differentiate events properly, then realised that EventBuses are really confusing.
All the EventBuses can be found in
MinecraftForge.class, those beingEVENT_BUS,TERRAIN_GEN_BUSandORE_GEN_BUS.Technically a mod can implement their own buses, but there doesn't seem to be any in the wild.
Call
registeron any EventBuses and pass through either a class or an object that you want the buses to fire events to.- Class = static methods accepted only.
Example
javapublic class StaticExample { public static void register() { MinecraftForge.EVENT_BUS.register(EventListener.class); } public static class EventListener { @SubscribeEvent public static void thisListenerWillRun(Event event) { // This method is static // This block of code will run when whichever Event is denoted in the argument } @SubscribeEvent public void thisListenerWillNeverRun(Event event) { // This method is not static } } }- Object = member methods accepted only.
Example
javapublic class MemberExample { public static void register() { MinecraftForge.EVENT_BUS.register(new EventListener()); } public static class EventListener { @SubscribeEvent public void thisListenerWillRun(Event event) { // This method is not static // This block of code will run when whichever Event is denoted in the argument } @SubscribeEvent public static void thisListenerWillNeverRun(Event event) { // This method is static } } }
PlayerDestroyItemEvent
PlayerDestroyItemEventis fired when a player destroys an item.
Hooks
PlayerControllerMP#onPlayerDestroyBlock(BlockPos)PlayerControllerMP#processRightClick(EntityPlayer, World, EnumHand)PlayerControllerMP#processRightClickBlock(EntityPlayerSP, WorldClient, BlockPos, EnumFacing, Vec3d, EnumHand)EntityPlayer#attackTargetEntityWithCurrentItem(Entity)EntityPlayer#damageShield(float)EntityPlayer#interactOn(Entity, EnumHand)ForgeHooks#getContainerItem(ItemStack)PlayerInteractionManager#processRightClick(EntityPlayer, World, ItemStack, EnumHand)PlayerInteractionManager#processRightClickBlock(EntityPlayer, World, ItemStack, EnumHand, BlockPos, EnumFacing, float, float, float)10.PlayerInteractionManager#tryHarvestBlock(BlockPos)
Characteristics
- Event is not cancellable.
- Event does not have a result.
Behaviour
- Fired normally from ForgeEventFactory#onPlayerDestroyItem(EntityPlayer, ItemStack, EnumHand)
- Fired on MinecraftForge#EVENT_BUS
Oddities
- This event is never fired correctly for the context of the 7th hook's listed above:
ForgeHooks#getContainerItem(ItemStack). Forge's logic when trying to determine if the retrieved container item is destroyed is all wrong. This bug was introduced in MinecraftForge's PR#3388. Which meant that the event never fires for when the container item was actually destroyed. This was introduced when Forge was correcting all null-checks onItemStackstoItemStack#isEmptycalls instead. In most contexts, checkingItemStack#isEmptywould be enough, but in this particular context, the semantics was misunderstood. AnyItemStackthat are destroyed will canonically beItemStack#isEmpty() == true && ItemStack != ItemStack.EMPTY, meaning the logic ofif (!stack.isEmpty() && stack.isItemStackDamageable() && stack.getMetadata() > stack.getMaxDamage())inForgeHooks#getContainerItem(ItemStack)is indeed wrong and should have beenif (stack.isEmpty() && (stack.isItemStackDamageable() || stack.getDamage() >= stack.getMaxDamage()). - To circumvent this oddity, one would have to make sure they handle all the logic they would have done in a
PlayerDestroyItemEventlistener in their item's respective class'sgetContainerItemmethod instead.
