用户:MashKJo/1.21.1模组开发教程/2.事件

事件(Event)是一种能让Modder干涉已有代码(原版和其他Mod的)行为的机制。比如我想修改原版的某个机制,或在原版的某个机制触发时添加我自己的额外逻辑,就可以通过监听相应的事件来达成目的。

NeoForge提供了相当之丰富的事件类,绝大多数事件类可以在net.neoforged.neoforge.eventnet.neoforged.neoforge.client.event这两个包及其子包下找到,它们都是Event类的子类。

事件系统的原理和起作用的过程是这样:事件总线收集各种来源的事件监听器,并在特定时机发布某一种事件,调用该种事件所对应的所有事件监听器。这个说法可能有些抽象,我们一步步捋这些概念。

事件监听器和事件总线

事件监听器是一种满足特定条件的方法:返回值类型必须为void,形参个数只能为1,且类型必须是Event类的非抽象子类:

public (static) void onExampleEventCalled(ExampleEvent event) {
   //这里的ExampleEvent记得在实际应用中替换为一个实际存在的事件类型。
}

在方法体内,你可以通过event形参提供的各种方法对游戏逻辑进行修改。

然后我们就可以将事件监听器注册进相应的事件总线了。事件总线对象的接口是IEventBus,用于存储不同来源的事件监听器。NeoForge提供了2个事件总线:Mod总线和游戏总线(NeoForge.EVENT_BUS);前者用于处理用于游戏初始化的事件(实现了接口IModBusEvent),后者用于处理游戏进行时发布的事件。还记得上一节中Mod入口类构造方法传进来的IEventBus吗?那个就是Mod总线。

将事件监听器注册进事件总线的方法很多,最简单的方法是调用IEventBus#addListener,传进去一个方法引用即可。

或者你也可以将事件监听器方法打上@SubscribeEvent注解,调用IEventBus#register,传入的东西依你的事件监听器方法是否为静态的而定。若是静态的,传入方法所在的类的Class<?>对象;若是非静态的,则需要new一个方法所在类的实例。

还可以通过@EventBusSubscriber注解来实现自动注册——将它打在监听器方法所在类的上面即可,NeoForge会自动将所有打上了@SubscribeEvent注解的方法注册进对应的事件总线的。注解的modid参数并不必须,但为了debug方便我们还是加上;value参数是指定监听事件的物理端,默认为客户端和服务端都监听。注意使用自动注册时,事件监听器方法必须是静态的。

可取消的事件

可取消的事件类型都实现了接口ICancellableEvent,你可以调用#cancel方法来取消可取消的事件,这通常会导致2个效果:

  • 后续的事件监听器均不能监听到该事件(除非后续的监听器将receiveCanceled参数设定为true)
  • 原有的代码逻辑不会继续执行

事件的优先级

NeoForge提供了枚举类型EventPriority来表示事件的优先级,我们可以在@SubscribeEvent中指定priority参数。优先级高的事件监听器方法会先被执行。

事件的发布

通过调用IEventBus#post,即可发布一个事件。这个过程NeoForge已经帮我们用二进制patch为所有其提供的事件类做好了。如果我们想提供我们自己的事件类供其他Mod联动用,那么就需要在我们自己的代码里发布事件了。

生命周期事件

生命周期事件是一类特殊的Mod总线事件,它们有很多都继承了类ParallelDispatchEvent,这代表它们是被并行发布的,因此监听它们时指定事件优先级是无效的。监听它们时,务必将代码包装在lambda中,传入ParallelDispatchEvent#enqueueWork中,以保证你的代码在主线程上被执行。