Mod 生命周期

Mod 生命周期

Mod 加载过程中,各种生命周期事件会在各 Mod 专属的事件总线上触发。这些事件期间会执行许多操作,例如注册对象、准备数据生成或与其他 Mod 通信。

Mod 生命周期事件的用途分组、构造与注册时机及跨 Mod 通信先后关系

原创说明图,依据 Forge 1.21.x 官方文档 绘制。

事件监听器应使用 @EventBusSubscriber(bus = Bus.MOD) 注册,或在 Mod 构造函数中注册:

@Mod.EventBusSubscriber(modid = "mymod", bus = Mod.EventBusSubscriber.Bus.MOD)
public class MyModEventSubscriber {
  @SubscribeEvent
  static void onCommonSetup(FMLCommonSetupEvent event) { ... }
}

@Mod("mymod")
public class MyMod {
  public MyMod(FMLModLoadingContext context) {
    context.getModEventBus().addListener(this::onCommonSetup);
  }

  private void onCommonSetup(FMLCommonSetupEvent event) { ... }
}

警告:大多数生命周期事件并行触发,所有 Mod 会同时收到相同的事件。Mod 必须注意线程安全,例如调用其他 Mod 的 API 或访问原版系统时。通过 ParallelDispatchEvent#enqueueWork 将代码推迟到稍后执行。

注册表事件

注册表事件在 Mod 实例构造后触发,共有三个:NewRegistryEvent、DataPackRegistryEvent$NewRegistry 和 RegisterEvent。它们在 Mod 加载期间同步触发。

NewRegistryEvent 允许开发者使用 RegistryBuilder 类注册自定义注册表。

DataPackRegistryEvent$NewRegistry 允许开发者提供用于从 JSON 编码和解码对象的 Codec,以注册自定义数据包注册表。

RegisterEvent 用于向注册表注册对象,每个注册表都会触发一次。

注意:尽可能优先使用 DeferredRegister,而不是直接使用注册表事件。DeferredRegister 会代为处理时机,不易出错。

数据生成

如果游戏配置为运行数据生成器,GatherDataEvent 将是最后触发的事件。此事件用于将 Mod 的数据提供器注册到相应的数据生成器,也以同步方式触发。

通用设置

FMLCommonSetupEvent 用于物理客户端和物理服务端共有的操作,例如注册能力。

端专属设置

端专属设置事件在相应的物理端触发:FMLClientSetupEvent 在物理客户端触发,FMLDedicatedServerSetupEvent 在独立服务器触发。物理端专属的初始化应在此进行,例如注册客户端按键绑定。

InterModComms

此阶段可以向其他 Mod 发送消息,实现跨 Mod 兼容。共有两个事件:InterModEnqueueEvent 和 InterModProcessEvent。

InterModComms 类负责保存 Mod 的消息。由于其底层使用 ConcurrentMap,可以在线程并行的生命周期事件期间安全地调用它的方法。

在 InterModEnqueueEvent 期间,使用 InterModComms#sendTo 向不同 Mod 发送消息。这些方法接收目标 Mod ID、消息数据关联的键,以及保存消息数据的 supplier。也可以指定消息发送方,默认为调用者的 Mod ID。

随后,在 InterModProcessEvent 期间,使用 InterModComms#getMessages 获取所有已收到消息的流。传入的 Mod ID 几乎总是调用该方法的 Mod 自身 ID。还可以指定谓词来过滤消息键。方法返回 IMCMessage 流,每条消息包含数据发送方、接收方、数据键和 supplier 提供的数据本身。

注意:还有两个生命周期事件:FMLConstructModEvent 在 Mod 实例构造完成后立即触发,但早于 RegisterEvent;FMLLoadCompleteEvent 在 InterModComms 事件之后触发,表示 Mod 加载过程完成。

原文:Mod Lifecycle。

译注:原文示例使用 FMLModLoadingContext 获取事件总线,而文档其他示例使用 FMLJavaModLoadingContext;请核对目标版本的 API,示例尚未编译验证。