架构与 WAILA 接入方式
基本信息
| 属性 | 值 |
|---|---|
@Mod 类 |
net.darkhax.wawla.Wawla(Wawla.java:14-20) |
| modid | wawla(Constants.MODID,Constants.java:7) |
| 显示名 | What Are We Looking At(Constants.java:8) |
| 硬依赖 | @Mod(dependencies = "required-after:Waila")(Wawla.java:19) |
acceptableRemoteVersions |
"*"(Wawla.java:18) |
| 源码规模 | 18 个 .java,共 2388 行 |
| 事件处理器 | 只有 2 个:preInit / init(无 postInit 调用) |
| 入口方法体 | preInit 只有 1 行有效代码(proxy.preInit()),另一行是注释掉的 devMode 探测 |
⚠️ 本 mod 是什么:一个纯 WAILA 插件库
WAWLA 不是 WAILA 的 fork,也不是 WAILA 本体。 它是 Darkhax 写的、依附于 WAILA 运行的插件(addon)包。源码里的硬证据:
| 证据 | 位置 |
|---|---|
@Mod(dependencies = "required-after:Waila") |
Wawla.java:19 |
全部 9 处 FMLInterModComms.sendMessage("Waila", "register", "<本类>.registerAddon") |
Wawla.java:40-78 |
所有 provider 实现的都是 WAILA 的 API 接口 mcp.mobius.waila.api.IWailaDataProvider / IWailaEntityProvider |
13 个 addon 类 |
| 源码里没有一个 WAILA 的实现类被复制或修改 | 全仓 |
README 也写明 “Required Mods: WAILA”,并把 ProfMobius(WAILA 作者)列为独立致谢对象——若 WAWLA 是 WAILA 的 fork,不会存在这种分离的署名关系。
因此本 mod 在 GTNH 整合包里的定位,与 wailaplugins、wailaharvestability 属于同一类(WAILA 插件),而不是 WAILA 本体。真正的 WAILA 本体是分类 waila。
加载流程
preInit(FMLPreInitializationEvent)
└─ proxy.preInit() ← 仅客户端有实现
├─ MinecraftForge.EVENT_BUS.register(new AddonGenericTooltips()) ← 唯一绕过 WAILA 的接入
├─ Utilities.currentBlockDamage = ReflectionHelper.findField(PlayerControllerMP, "g", ...) ← 破坏进度反射
└─ FMLInterModComms.sendMessage("llibrary", "update-checker", "https://github.com/Darkhax-Minecraft/WAWLA/master/versions.json")
init(FMLInitializationEvent)
└─ proxy.init() ← ProxyCommon 与 ProxyClient 的 init() 都是空的
└─ 向 "Waila" 发送 9 条 "register" IMC ← 真正的注册全在这里
⚠️ ProxyCommon.postInit() 与 ProxyClient.postInit() 都存在但 Wawla 类里没有 postInit 事件处理器,两个方法永远不会被调用(死代码)。
⚠️ ProxyClient 里没有任何 @SideOnly 标注,而是靠 @SidedProxy 分离;但 AddonGenericTooltips.onTooltip 直接 import net.minecraft.client.Minecraft 并调用 Minecraft.getMinecraft(),该事件监听器在服务端会随 EVENT_BUS 一起被注册(ProxyClient.preInit 只在客户端跑,故实际安全,但这层保护来自代理分离而非注解)。
代理结构
| 类 | 侧 | 内容 |
|---|---|---|
ProxyCommon |
通用 | preInit / init / postInit 三个空方法 |
ProxyClient |
客户端 | 3 行 preInit(见上),init 空,postInit 空 |
| 静态入口 | — | Wawla.proxy(@SidedProxy)、Wawla.instance(@Mod.Instance("wawla")) |
9 条 IMC 注册(Wawla.init 逐条)
| # | 条件 | 注册的类 | 载入的类 |
|---|---|---|---|
| 1 | 无条件 | AddonVanillaEntities |
mcp.mobius.waila.api.IWailaEntityProvider |
| 2 | 无条件 | AddonVanillaTiles |
IWailaDataProvider |
| 3 | 无条件 | AddonGenericEntities |
IWailaEntityProvider |
| 4 | 无条件 | AddonGenericTiles |
IWailaDataProvider |
| 5 | Loader.isModLoaded("pixelmon") |
AddonPixelmonEntities |
IWailaEntityProvider |
| 6 | 同上 | AddonPixelmonTiles |
IWailaDataProvider |
| 7 | Loader.isModLoaded("Thaumcraft") |
AddonThaumcraftTiles |
IWailaDataProvider |
| 8 | Loader.isModLoaded("TConstruct") |
AddonTinkersTiles |
IWailaDataProvider |
| 9 | Loader.isModLoaded("Jewelrycraft") |
AddonJewelrycraftTiles |
IWailaDataProvider |
⚠️ 这 5 个 Loader.isModLoaded 的判定 modid 与 addon 构造函数内部判定的 modid 有一处不一致——第 9 条在 Wawla.java:75 判 "Jewelrycraft",而 AddonJewelrycraftTiles.java:24 判的是 "Jewelrycraft2"。两者必须同时为真才能工作,实际只取决于 Wawla.java 那一次。详见 JewelryCraft 集成。
⚠️ dependencies 只声明了 required-after:Waila,而 5 个集成分别需要 pixelmon / Thaumcraft / TConstruct / Jewelrycraft / HungerOverhaul。虽然都有 isModLoaded 守卫,但 AddonGenericEntities.java:18 在类层面 import 了 iguanaman.hungeroverhaul.config.IguanaConfig(编译期硬引用),AddonGenericEntities.java:77-78 直接访问其静态字段。若玩家装了 HungerOverhaul 的不兼容版本,字段缺失会在运行时抛 NoSuchFieldError 而非被守卫拦住。
13 个 addon 类的注册状态
| 包 | 类 | 是否被 IMC 注册 | 条目 |
|---|---|---|---|
vanillamc |
AddonVanillaEntities |
✅ 条件 1 | 原版实体 |
vanillamc |
AddonVanillaTiles |
✅ 条件 2 | 原版方块 |
generic |
AddonGenericEntities |
✅ 条件 3 | 通用实体 |
generic |
AddonGenericTiles |
✅ 条件 4 | 通用方块 |
pixelmon |
AddonPixelmonEntities + AddonPixelmonTiles |
✅ 条件 5/6 | Pixelmon 集成 |
thaumcraft |
AddonThaumcraftTiles |
✅ 条件 7 | 神秘时代集成 |
tinkersconstruct |
AddonTinkersTiles |
✅ 条件 8 | Tinkers 集成 |
jewelrycraft |
AddonJewelrycraftTiles |
✅ 条件 9 | JewelryCraft 集成 |
generic |
AddonGenericTooltips |
⚠️ 不走 IMC,直接 EVENT_BUS.register |
物品悬浮提示 |
developer |
AddonDeveloperEntities |
❌ 未注册 | 未接线的 addon |
developer |
AddonDeveloperTiles |
❌ 未注册 | 未接线的 addon |
morph |
AddonMorphEntities |
❌ 未注册 | 未接线的 addon |
util |
Utilities |
— 工具类 | 工具类 |
反射取类机制
所有跨 mod 集成都不用编译期依赖,而是 Class.forName + 静态 Class 字段缓存:
| addon | 缓存字段数 | 例外 |
|---|---|---|
| Pixelmon entities | 3(classEntityPixelmon / enumNature / enumGrowth) |
另用 Utilities.generateElementArray 把枚举转成 String[] |
| Pixelmon tiles | 2(TileEntity + Block) | — |
| Thaumcraft | 12(8 个 TileEntity + 4 个 Block) | TileEntity 字段是实例字段,Block 字段是 static |
| Tinkers | 7 个 Class + 2 个 Method | getHarvestType / getSecondHarvestType,setAccessible(true) |
| JewelryCraft | 2 | 判定 modid 与 Wawla.java 不一致(见上) |
⚠️ Tinkers 的 isEnabled 在 try 块之前就被置 true(AddonTinkersTiles.java:27)。若后续任一 Class.forName / getDeclaredMethod 抛异常被 catch 掉,isEnabled 仍为 true 而 7 个 Class 字段中部分为 null,canHarvest() 随后在 classDualHarvestTool.isInstance(...)(AddonTinkersTiles.java:205)上 NPE。而 canHarvest 被 AddonGenericTiles.java:59 无条件调用,等于任意 Tinkers 方块的 HUD 刷新都可能崩。详见 Tinkers 集成。
⚠️ Class.forName 失败只 printStackTrace(),不阻止后续 registerBodyProvider(dataProvider, null)(如 AddonPixelmonEntities.java:148、AddonThaumcraftTiles.java:209-216)。此时静态 Class 字段仍为 null,WAILA 侧收到的是 null 类引用。