自动事件订阅器(ForgelinAutomaticEventSubscriber)

基本信息

属性 值
所属 Forgelin(modid forgelin)
类型 内部 object,在 PreInit 阶段运行
源码 ForgelinAutomaticEventSubscriber.kt:20-132
触发 Forgelin.onPreInit(Forgelin.kt:18-22)遍历 Loader.instance().modList

解决的问题

Kotlin 的 object 声明在字节码层面是带静态 INSTANCE 字段的 final 类。 FML 的 1.7.10 版 EventBusSubscriber 扫描逻辑会把 object 上的实例方法 误当成静态方法处理,导致事件回调根本不被调用(或被错误调用)。

Forgelin 在 PreInit 阶段遍历所有 mod,主动撤销 FML 的错误静态注册, 再以实例身份注册到两个事件总线:

if (!hasStaticEventHandlers(subscriberClass) && subscriberClass !in unregistered) {
    FMLCommonHandler.instance().bus().unregister(subscriberClass)
    MinecraftForge.EVENT_BUS.unregister(subscriberClass)
    unregistered += subscriberClass
}
if (hasObjectEventHandlers(objectInstance) && objectInstance !in registered) {
    FMLCommonHandler.instance().bus().register(objectInstance)
    MinecraftForge.EVENT_BUS.register(objectInstance)
    registered += objectInstance
}

(ForgelinAutomaticEventSubscriber.kt:63-74)

判定依据是两个纯函数:非静态且带 @SubscribeEvent 的方法 (hasObjectEventHandlers,行 84-88)、静态且带 @SubscribeEvent 的方法 (hasStaticEventHandlers,行 90-94)。只有非静态方法会被重新注册。

生效条件与跳过规则

条件 结果 行号
@Mod 未使用 KotlinAdapter 跳过整个 mod,debug 日志 39-42
注解的 value 不含当前 Side 该订阅者在 subscribers 阶段即被过滤掉 32
无法确定所属 mod 跳过,打 debug 日志 48-52
所属 mod 与当前遍历的 mod 不同 跳过,防止跨 mod 误注册 54-57
objectInstance 与 companionObjectInstance 均为 null 跳过(非 object 的普通类) 61

所属 mod 的判定见 getOwnerModID(行 121-130):容器内只有 1 个 @Mod 时直接取它; 有多个时用「订阅者类名以 @Mod 类名开头」的前缀匹配。

幂等性

unregistered(Set<Class<*>>)与 registered(Set<Any>)两个集合 保证每个类只被撤销/注册一次(行 24-25、63、69),PreInit 被重复触发时不会重复注册。

已知源码问题

  • 异常被包装后重抛:catch (e: Throwable) 处理完打日志后又 throw Exception(e) (行 76-79)。任何一个 mod 的订阅者类加载失败,会让 Forgelin 的整个 PreInit 抛异常, 进而影响所有使用 KotlinAdapter 的 mod —— 失败范围被放大。 源码未做 per-mod 隔离。
  • parseTargetSides 反射依赖内部结构:读取 ModAnnotation.EnumHolder.value 私有字段并 setAccessible(true)(行 110-111)。若 FML 更换该内部类结构会直接失效。
  • parseModId 的命名与实际行为不符:函数名叫 parseModId,但当注解没有显式 modid 时返回 null/空(行 98-100),真正的解析发生在 getOwnerModID 里。
  • ⚠️ 源码第 28 行 asm.getAnnotationsFor(mod) ?: return 早退时静默, 未打任何日志,排障时无迹可循。

相关条目