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 工具通过通用的"可否采集"判定。
实现:
if (isEnabled)为假直接返回false;- 若
classDualHarvestTool.isInstance(item.getItem()),反射调getSecondHarvestType()拿第二工具类型; - 若
classHarvestTool.isInstance(item.getItem()),反射调getHarvestType(); - 返回
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
这是有意为之(地雷不该显示额外信息,只改显示物品),但源码里没有注释说明这个不对称是有意的。
相关条目
- 配置项 —
Tinkers' Construct的 4 个键与showBurnTime缺陷 - 通用方块 addon —
canHarvest的唯一调用方 - 架构与 WAILA 接入方式 — 反射机制总览
- 工具类 —
getInventoryStacks的 null 返回