## FML生命周期事件
FML生命周期事件,顾名思义,就是FML加载、关闭、和Mod加载等等相关的事件,这些希望监听对应事件的方法使用`@EventHandler`注解修饰,并且应在被`@Mod`注解修饰的主类下,Forge会寻找并注册仅含一个参数并且参数符合特定类型的方法。如下面三个FML生命周期事件是最常用的:
* `FMLPreInitializationEvent`
* `FMLInitializationEvent`
* `FMLPostInitializationEvent`
这三个事件的使用方法已经讲过,此处不再赘述。
还有下面两个事件:
* `FMLConstructionEvent`在Mod开始加载时触发。
* `FMLLoadCompleteEvent`在Mod加载完成时触发。
除上面这些之外,还有下面的这些比较常用的用于服务端的FML生命周期事件:
* `FMLServerAboutToStartEvent`
* `FMLServerStartingEvent`
* `FMLServerStartedEvent`
* `FMLServerStoppingEvent`
* `FMLServerStoppedEvent`
想必读者已经可以猜出来这五个事件的异同,并了解这些事件被触发的条件了。
## 注册事件
Forge的事件系统一直在Forge中占有十分重要的地位,可以这么说,没有事件,就没有Mod。大家可以注意到,主类的`preInit`,`init`,`postInit`方法,全部都是事件驱动的。换句话说,理论上一个Mod的开发教程本身应该从事件讲起。
Forge的事件系统几乎涵盖了方方面面,从服务端到客户端,从世界生成到物品方块行为,从玩家行为到一般实体行为,等等。
Forge的事件系统分为两类,一类是FML生命周期事件,一类是Minecraft事件。
Forge本身提供了很多Minecraft事件,这些事件基本上可以完成对Minecraft大部分物品、方块、实体等特性的修改,并且这些事件的数量还在不断地上升。开发者只需要注册一个包含监听这些事件的方法的类,Forge就会挂钩上这些方法。这些方法使用`@SubscribeEvent`注解进行修饰,Forge寻找并挂钩这些方法的方式和上面的FML生命周期事件类似,只不过由于挂钩的方式不同,调用的时候效率要更高。
首先我们创造一个类。在包`net.xiaobang.bm.common`下新建一个文件`EventLoader.java`:
`src/main/java/net/xiaobang/bm/common/EventLoader.java`
~~~
package net.xiaobang.bm.common;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
public class EventLoader {
public EventLoader(FMLInitializationEvent e) {
MinecraftForge.EVENT_BUS.register(this);//注册本类的所有事件
/*
除此之外,Forge还提供了需要在MinecraftForge.TERRAIN_GEN_BUS上注册的地形生成事件,需要在
MinecraftForge.ORE_GEN_BUS上注册的矿物生成事件等等。
*/
}
@SubscribeEvent//当forge注册所有事件的时候,会扫描具有此注解的方法,再挂钩。Forge会根据方法的参数类型
//来区分不同的事件,比如onPlayerInteract方法挂钩的就是玩家在和物品或方块互动的时候触发的事件PlayerInteractEvent
public void onPlayerInteract(PlayerInteractEvent event){//玩家点击事件
if (!event.world.isRemote)//判断是否是客户端调用此事件,event.world.isRemote与event.entityPlayer.isServerWorld同理
System.out.println("玩家" + event.entityPlayer.getDisplayName() + "左键/右键了一下");
}
}
~~~
(注释写不下,写这里了)`@SubscribeEvent`注解有两个参数,其中一个是`receiveCanceled`,与是否取消该事件相关,默认为`false`,这个参数不太常用,我们不去管它。还有一个参数是`priority`,比较常用,表示事件的优先级,可能的情况有五种:
* `EventPriority.HIGHEST`
* `EventPriority.HIGH`
* `EventPriority.NORMAL`
* `EventPriority.LOW`
* `EventPriority.LOWEST`
默认的优先级是`EventPriority.NORMAL`,当然,如果想自定优先级,往往都会选择`EventPriority.HIGH`,和`EventPriority.HIGHEST`。如果没有特殊需求,这一项最好默认。
最后在CommonProxy注册EventLoader:
**`src/main/java/net/xiaobang/bm/common/CommonProxy.java`**(部分)
~~~
public void init(FMLInitializationEvent event)
{
new CraftingLoader();
new EventLoader();
}
~~~
最后的最后,打开游戏尝试一下把。
## Event类解析
Forge提供的所有事件,都是`cpw.mods.fml.common.eventhandler.Event`类的子类。当前Forge版本下这个类的所有的子类的用法zzzz大佬在他的教程[附录](https://fmltutor.ustc-zzzz.net/%E9%99%84%E5%BD%95A-%E4%BA%8B%E4%BB%B6%E5%88%97%E8%A1%A8)(高版本)中列出了一张表,以供读者参考。这一部分针对Event类本身。
Event类添加了下面几个公开方法:
* `public boolean isCancelable()`
返回该事件是否可以被取消。
* `public boolean isCanceled()`
返回该事件是否已被取消。
* `public void setCanceled(boolean cancel)`
设置该事件是否被取消。
* `public boolean hasResult()`
返回该事件是否有结果,添加了`@HasResult`注解的事件默认为`true`,否则为`false`。
* `public Result getResult()`
返回该事件的结果,有`Result.DENY`,`Result.DEFAULT`,`Result.ALLOW`三种,默认为`Result.DEFAULT`。
* `public void setResult(Result value)`
为该事件设置一个结果。
* `public ListenerList getListenerList()`
获取所有注册该事件的监听器。
* `public EventPriority getPhase()`
获取该事件的优先级,上面已有说明。
* `public void setPhase(EventPriority value)`
设置该事件的优先级,上面已有说明。
(以上大部分来自zzzz大佬的教程)
