Tinkers’ Construct 集成

基本信息

属性 值
判定 modid TConstruct(Wawla.java:70)
条件 Loader.isModLoaded("TConstruct")
文件 src/main/java/net/darkhax/wawla/addons/tinkersconstruct/AddonTinkersTiles.java(253 行,全 mod 最长的 addon)
实现接口 IWailaDataProvider
配置分类 Tinkers' Construct(4 个键,带单引号)
特殊能力 反射缓存 2 个 Method 并 setAccessible(true);提供跨 addon 的公开静态方法 canHarvest
GTNH 状态 整合包含 TConstruct,此 addon 会加载

反射缓存

字段 目标 类型
classHarvestTool tconstruct.library.tools.HarvestTool Class
classDualHarvestTool tconstruct.library.tools.DualHarvestTool Class
classDryingRackLogic tconstruct.blocks.logic.DryingRackLogic Class
classFurnaceLogic tconstruct.tools.logic.FurnaceLogic Class
classBlockLandmine tconstruct.mechworks.blocks.BlockLandmine Class
classDryingRack tconstruct.armor.blocks.DryingRack Class
classFurnaceSlab tconstruct.tools.blocks.FurnaceSlab Class
getHarvestType HarvestTool.getDeclaredMethod("getHarvestType") Method
getSecondHarvestType DualHarvestTool.getDeclaredMethod("getSecondHarvestType") Method

两个 Method 在 :39-40 被 setAccessible(true)。WAWLA 全 mod 只有这一处反射方法调用。

注册明细

目标 Body NBT Stack
classDryingRack ✅ ✅ —
classFurnaceSlab ✅ ❌ —
classBlockLandmine ❌ 无 body ✅ ✅

⚠️ classFurnaceSlab 缺 registerNBTProvider——但 getWailaBody 里读 data.getNBTData().getInteger("Fuel") 等(:106 / :120 / :133)。半砖熔炉的 HUD 能否工作完全依赖 WAILA 自身是否已提供该 Block 的 NBT(大概率会,因为 WAILA 有内建的 furnace provider),但本 addon 自己没注册。

4 类功能

1. 隐藏地雷(唯一的 getWailaStack 实现)

if (cfg.getConfig(CONFIG_HIDE_LANDMINES) && data.getTileEntity() != null
        && Utilities.compareByClass(classBlockLandmine, data.getBlock().getClass())) {
    if (data.getNBTData() != null) {
        ItemStack cover = Utilities.getInventoryStacks(data.getNBTData(), 4)[3];
        if (cover != null) return cover;
    }
}
return data.getStack();

开关 wawla.tinkers.hideLandmine 开启时,用地雷槽位 3(TConstruct 的 Landmine 物品槽)的伪装物品替换 HUD 显示。这是 WAWLA 唯一使用 IWailaStackProvider 的地方——它不只是加行,而是替换方块的显示。

2. 晾干架(classDryingRackLogic)

门控键 输出 计算
wawla.tinkers.showDryerItem 物品: <名> getInventoryStacks(tag, 1)[0]
wawla.tinkers.showDryerTime 干燥度: <N>% getProgression(tag.getInteger("Time"), tag.getInteger("MaxTime")),2 位小数

⚠️ 干燥度有双重过滤:percent > 0 && !(percent > 100)(:110)——即只显示 0 < 进度 ≤ 100 的值。 ⚠️ MaxTime == 0 时 float 除零得 Infinity(0/0 得 NaN),两者都会被上面的比较过滤掉,所以不会崩,但也不会显示。

3. 半砖熔炉(classFurnaceLogic)

⚠️ 这一段是整个 addon 问题最集中的地方。

行 门控键 输出
:118-127 wawla.tinkers.showBurnTime 且 "wawla.furnace.burntime" 燃烧时间: <Fuel/20> 秒
:135 "wawla.furnace.input"(硬编码) 烧制物品: <名> X <数量>
:141 "wawla.furnace.fuel"(硬编码) 燃料: <名> X <数量>
:147 "wawla.furnace.output"(硬编码) 成品: <名> X <数量>

后 3 行还需玩家潜行。

4. canHarvest —— 跨 addon 的公开 API

public static boolean canHarvest(ItemStack item, String required)

被 通用方块 addon(AddonGenericTiles.java:59)调用,用来让 TConstruct 工具通过通用的"可否采集"判定。

实现:

  1. if (isEnabled) 为假直接返回 false;
  2. 若 classDualHarvestTool.isInstance(item.getItem()),反射调 getSecondHarvestType() 拿第二工具类型;
  3. 若 classHarvestTool.isInstance(item.getItem()),反射调 getHarvestType();
  4. 返回 tooltypes.contains(required)。

⚠️ 用的是 isInstance(支持子类),与 Pixelmon 里 compareByClass 的字符串比较不同——这里是对的。 ⚠️ 两个反射调用各自 catch (Exception) { e.printStackTrace(); },静默吞掉 IllegalAccessException / InvocationTargetException。工具类型拿不到时 tooltypes 为空列表 → 返回 false,即恒判定为"不可采集"(绿色变红色),无任何提示。

⚠️ 源码缺陷

缺陷 1(最严重):isEnabled 在 try 之前置 true,导致 NPE 连锁

AddonTinkersTiles.java:23-58:

public AddonTinkersTiles() {
    if (Loader.isModLoaded("TConstruct")) {
        isEnabled = true;          // ← :27  在 try 之前
        try {
            classHarvestTool = Class.forName(...);
            ... 7 个类 + 2 个 Method
        }
        catch (ClassNotFoundException e) { e.printStackTrace(); }
        catch (NoSuchMethodException e) { e.printStackTrace(); }
        catch (SecurityException e) { e.printStackTrace(); }
    }
}

isEnabled 应当在所有反射成功之后才置 true。现在它先置真,于是:TConstruct 装了但类路径变了(版本不兼容 / 其它 mod 抢先加载)→ 反射失败被 catch 掉 → isEnabled 仍为 true 而若干 Class 字段仍为 null → canHarvest 在 classDualHarvestTool.isInstance(...)(:205)或 classHarvestTool.isInstance(...)(:218)上 NPE。

而 canHarvest 被 AddonGenericTiles.java:59 无条件调用(只靠 || 的右半支),触发路径是任意方块的 HUD 刷新。换言之:TConstruct 版本不匹配会导致 WAWLA 的通用方块 HUD 大面积崩溃。

正确写法应是先用一个局部 boolean 收集成功标志,末尾再赋给 isEnabled。

缺陷 2:wawla.tinkers.showBurnTime 被读取但从未 addConfig 注册

CONFIG_BURN_TIME(:240)在 :118 被 cfg.getConfig 读取,但 registerAddon(:180-183)的 4 个 addConfig 里没有它——只有 hideLandmine / showDryerTime / showDryerItem / showFurnace。

WAILA 的配置界面因此不存在这个开关。同一行还额外要求 "wawla.furnace.burntime"(:122,硬编码),那是 原版方块 注册的键。双重门控 + 其一永远不可达 = 燃烧时间行在正常配置下永不显示。

缺陷 3:硬编码复用其它 addon 的配置键

:122 / :135 / :141 / :147 用了 4 个字面量 "wawla.furnace.burntime" / "input" / "fuel" / "output",而这些键的注册者是 AddonVanillaTiles。耦合隐式且未加注释:若原版 addon 被移除或 WAILA 改了键名,Tinkers 的这 4 行会静默失效。同类位置本文件里本来就有 CONFIG_* 常量可用。

缺陷 4:getInventoryStacks(...) 的 4 处调用都可能 NPE

:69([3],invSize=4)、:96([0],invSize=1)、:133(invSize=3)——NBT 无 Items 键时返回 null,直接下标即崩。地雷未放置伪装物时(:69)和晾干架空置时(:96)都是常态。

缺陷 5:getWailaStack 里 data.getBlock().getClass() 无 null 保护

:65 在 data.getTileEntity() != null 之后直接解引用 data.getBlock()。

缺陷 6:注册粒度与方法内部判据不匹配

body provider 注册在 classDryingRack(Block) 与 classFurnaceSlab(Block) 上,但 getWailaBody 内部用 compareTileEntityByClass(te, classDryingRackLogic) / classFurnaceLogic(TileEntity 类)判断。逻辑上可行(handler 自己再判一次),但注册表里看不出对应关系,排错时容易误判覆盖范围。

缺陷 7:classBlockLandmine 有 NBT + Stack 但无 body

这是有意为之(地雷不该显示额外信息,只改显示物品),但源码里没有注释说明这个不对称是有意的。

相关条目