自动事件订阅器(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早退时静默, 未打任何日志,排障时无迹可循。
相关条目
- EventBusSubscriber 注解 - 被本类处理的注解
- KotlinAdapter - 启用注入的前提条件