叠加层与 GUI 挂钩

基本信息

属性 值
类型 GUI 叠加层基础设施与死代码清单
相关类 utils/CustomOverlayHandler、utils/SetRecipeCommandHandler、utils/ModItemFilter、utils/LabeledPositionedStack、utils/ColorUtils
自定义 IOverlayHandler 0 个生效——唯一实现是死代码
生效的叠加层 只有 DefaultOverlayHandler(NEI 自带),由 合成工作台集成 批量注册

本条目是 mod 内看起来存在但实际不生效的那部分代码的集中说明,避免读者按类名误判功能。

CustomOverlayHandler(IOverlayHandler,死代码)

public class CustomOverlayHandler implements IOverlayHandler {
    public CustomOverlayHandler(String command, int xOffs, int yOffs, boolean invert,
            Class<? extends Slot> craftingSlot) { ... }
}

证据:grep -rn "CustomOverlayHandler" src 只有两处命中——第 33 行类声明与第 40 行构造函数自身。没有任何 new、没有工厂方法、没有反射入口。

它实现的能力(记录下来是因为设计意图明确,一旦有人补上注册就能用):

行为 触发条件
2D 叠加层预览 shift == false(或 invert 取反后为 false)。构造 new OffsetPositioner(xOffs, yOffs) 喂给 new DefaultOverlayRenderer(ingr, positioner) 并赋给静态字段 LayoutManager.overlayRenderer
服务端一键填装 shift == true 且 ClientHandler.enabledCommands.contains(command)。把每个 ingredient 换算成 3×3 槽位号((relx - 25) / 18、(rely - 6) / 18,索引 y * 3 + x),结果栈固定放槽位 9,打包成 data.stacks 发出
纯客户端手动搬运 服务端不支持且 NEIClientUtils.getHeldItem() == null(必须空手)。用 GuiContainerManager.handleSlotClick 模拟:先左键清空所有 craftingSlot 实例槽位,再对每个 ingredient 做「拿起 → 右键放 1 个 → 放回」

findMatchingSlot 按 slot.xDisplayPosition == pstack.relx + xOffs 定位目标槽;找不到时打 "Failed to find matching slot - (%d,%d) in %s" 并 continue(不中断整条配方的搬运)。

isValidSlot 只允许两种槽位参与搬运:slot.inventory == Minecraft.getMinecraft().thePlayer.inventory(玩家背包),或 slot.getClass() == Slot.class(精确等于,子类不算)。这是为了避开机器内部的功能槽。

⚠️ 服务端填装分支里有一个源码自述的怪异之处:ItemStack stack = recipe.getResultStack(recipeIndex).items[0]; 写在 for (PositionedStack pstack : ingr) 循环体内部,每轮都追加一次结果栈到 stacksnbt。源码注释写着 "This is back-asswards but i don't see a better way :("。结果槽 9 会被重复追加多次。

SetRecipeCommandHandler(SubPacketHandler,死代码)

证据:grep -rn "SetRecipeCommandHandler" src 只有第 19 行与第 24 行自身。ServerHandler.registerHandler 全仓只有一处调用,注册的是 AE2 的 SetFakeSlotCommandHandler,不是它。

它设计的服务端行为:收到 stacks(NBT 列表,每项含 slot 与物品 NBT)后,若 ContainerClass.isInstance(player.openContainer),就遍历容器里所有 SlotClass 实例槽位——stmap 里有对应 slot.getSlotIndex() 的 putStack,没有的 putStack(null) 清空。

stacks 用 data.getTagList("stacks", 10) 读取,类型 10 即 NBTTagList。这与 CustomOverlayHandler 发包时 data.setTag("stacks", stacksnbt) 的格式一致——两个死代码是配套设计的,只是一起被弃用了。

ModItemFilter(ItemFilter,死代码)

证据:grep -rn "ModItemFilter" src 只有第 15 行与第 20 行自身。

matches(ItemStack) 的逻辑是先按 items 布尔过滤物品/方块,再按 modid 精确匹配:

情况 结果
item == null 或 item.getItem() == null false
item.getItem() instanceof ItemBlock && items == true false
!(item.getItem() instanceof ItemBlock) && items == false false
注册名无 : 分隔符 false
正常 s[0].equals(modId)

即 new ModItemFilter("Forestry", true) 会匹配所有 modid 为 Forestry 的非方块物品。

实际生效的 GUI 挂钩

挂钩点 实现类 注册 API 状态
工作台合成叠加层 NEI 自带 DefaultOverlayHandler API.registerGuiOverlay + API.registerGuiOverlayHandler 生效,见 合成工作台集成
AE2 拖拽 AppEngGuiHandler(INEIGuiAdapter) API.registerNEIGuiHandler 生效
槽位调试 tooltip DeveloperGuiHandler(IContainerTooltipHandler) GuiContainerManager.addTooltipHandler 生效但默认关闭
任意物品的配方叠加 CustomOverlayHandler(IOverlayHandler) — 死代码
物品过滤 ModItemFilter(ItemFilter) — 死代码

LabeledPositionedStack 与颜色

utils/LabeledPositionedStack extends PositionedStack,是育种类与产物类 handler 共用的带标签槽位。构造参数 (stack, x, y, label, yoffs, tooltip...)。

drawLabel() 的行为:标签含空格时按空格逐词换行,第 i 词画在 relx + 8, rely + 8 + yoffs + 9 * i;不含空格时单行画在 relx + 8, rely + 8 + yoffs。颜色取 ColorUtils.neiLabel。因为是逐词下移而非按宽度折行,长单词会溢出而非换行。

getTooltip() 返回构造时传入的 varargs 列表(可能是空 ArrayList)。

5 个 ColorResource

utils/ColorUtils 用 GTNHLib new ColorResource.Factory("neiaddons") 定义 5 个颜色,全部带硬编码默认值,且不在 .cfg 里——由资源包 assets/neiaddons/neiaddons_at.cfg 覆盖:

名称 默认值 用在哪
neiChanceTextNormal 0xFFFFFF 育种页正常概率数字
neiChanceTextRed 0xFFFFFF 育种页红色概率 / DERP 标记
neiLabel 0xFFFFFF 所有 LabeledPositionedStack 的标签
neiProd 0xFFFFFF 产物页 Prod: 段标题
neiSpec 0xFFF200 产物页 Spec: 段标题(唯一带色默认值的,亮黄)

⚠️ 仓库里的 src/main/resources/META-INF/neiaddons_at.cfg 不是颜色文件,而是一份 Forge Access Transformer,文件头注释 # Shamelessly stolen from NEI:

目标字段 注释名
net.minecraft.client.gui.inventory.GuiContainer.field_146999_f #xSize
net.minecraft.client.gui.inventory.GuiContainer.field_147000_g #ySize
net.minecraft.client.gui.inventory.GuiContainer.field_147003_i #guiLeft
net.minecraft.client.gui.inventory.GuiContainer.field_147009_r #guiTop

AT 的用途是给 GuiContainer 的 4 个私有字段开公有名。AppEngGuiHandler 与 DeveloperGuiHandler 都直接访问 gui.guiLeft / gui.guiTop,Forge 的 MCP 映射把它们命名为 field_147003_i / field_147009_r,本 mod 靠 AT 改名才编译得过——所以 NEI Addons 强依赖一个 core-mod 式 AT 生效的环境(Forge 1.7.10 默认即读取 jar 内 META-INF/*_at.cfg,无需额外设置)。

相关条目