NEI 模块(appeng.integration.modules.NEI)

基本信息

属性 值
源码 appeng/integration/modules/NEI.java
类声明 82,public class
实现接口 INEI(integration/abstraction/INEI.java:16)+ IContainerTooltipHandler + IIntegrationModule + IContainerObjectHandler
静态单例 public static NEI instance
modID NotEnoughItems(IntegrationType.java:46)
side CLIENT(IntegrationType.java:46)
本 wiki 收录情况 Not Enough Items 未被本 wiki 收录 —— 写全名 Not Enough Items

本模块的特殊之处:构造期全量反射探测 + 后期混合调用

NEI 确实 import 了 15 个 codechicken.nei.* 类型(:65-79): API、IBookmarkContainerHandler、INEIGuiHandler、ItemFilter.ItemFilterProvider、 IContainerObjectHandler、IContainerTooltipHandler、Recipe、Recipe.RecipeId、 ItemsGrid、ItemPanels、LayoutManager、 SearchTokenParser.ISearchParserProvider、IStackPositioner、 BookmarkPanel.BookmarkViewMode、GuiContainerManager。

但入口类本身是反射拿的(:118):

this.apiClass = Class.forName("codechicken.nei.api.API");

构造器的三重存在性探测

NEI 有一大段构造期代码在反射探测(不是 init()):

行号 探测内容
101 IntegrationHelper.testClassExistence(this, codechicken.nei.api.API.class)
105 同上,codechicken.nei.recipe.ICraftingHandler.class
106 同上,codechicken.nei.recipe.IUsageHandler.class
107 同上,codechicken.nei.api.IBookmarkContainerHandler.class
111 Class.forName("codechicken.nei.ItemPanels")
118 this.apiClass = Class.forName("codechicken.nei.api.API")
173 Class.forName("codechicken.nei.api.IOverlayHandler")

⚠️ IntegrationHelper.testClassExistence(integration/IntegrationHelper.java:15-17) 不做任何事:

public static void testClassExistence(final Object o, final Class<?> clz) {
    clz.isInstance(o);
}

Class.isInstance 返回 boolean,但返回值被丢弃,方法返回 void。 所以 :101 / :105 / :106 / :107 这 4 处「探测」的唯一实际作用是 触发类字面量解析(类不存在时抛 NoClassDefFoundError), 逻辑判断是零。这是本移植版的一处死代码。

反射与直接调用混用

机制 例子
反射 getDeclaredMethod + invoke registerRecipeHandler(:125)、registerUsageHandler(:127)、registerNEIGuiHandler(:129)、addItemFilter(:131)、registerBookmarkContainerHandler(:148)
直接调用 API.addKeyBind(:123)、GuiContainerManager.addTooltipHandler(:167)、GuiContainerManager.addObjectHandler(:168)

生命周期

阶段 行号 动作
构造器 — 上述反射探测(:101-173)
init() 122 加快捷键 + 反射注册 7 个 handler + 2 个 filter + 6 个 bookmark GUI
postInit() 207 空 {}

注入内容 1:客户端快捷键

// NEI.java:123
API.addKeyBind("gui.pattern_view", Keyboard.KEY_P);

新增快捷键 P → 打开「配方视图」GUI(GuiPatternTerm 系)。 org.lwjgl.input.Keyboard 来自 :29 import。

注入内容 2:4 个 recipe handler

handler 源文件 注册行
NEIAEShapedRecipeHandler integration/modules/NEIHelpers/NEIAEShapedRecipeHandler.java 133
NEIAEShapelessRecipeHandler integration/modules/NEIHelpers/NEIAEShapelessRecipeHandler.java 134
NEIInscriberRecipeHandler integration/modules/NEIHelpers/NEIInscriberRecipeHandler.java 135
NEIWorldCraftingHandler integration/modules/NEIHelpers/NEIWorldCraftingHandler.java 136

注入内容 3:2 个 usage handler

handler 源文件 注册行
NEICellViewHandler integration/modules/NEIHelpers/NEICellViewHandler.java 138
NEIPatternViewHandler integration/modules/NEIHelpers/NEIPatternViewHandler.java 139

注入内容 4:2 个受 feature 门控的 recipe handler

// NEI.java:141-147
if (AEConfig.instance.isFeatureEnabled(AEFeature.GrindStone)) {
    this.registerRecipeHandler(new NEIGrinderRecipeHandler());
}
if (AEConfig.instance.isFeatureEnabled(AEFeature.Facades)
        && AEConfig.instance.isFeatureEnabled(AEFeature.EnableFacadeCrafting)) {
    this.registerRecipeHandler(new NEIFacadeRecipeHandler());
}
handler 门控条件 行号
NEIGrinderRecipeHandler AEFeature.GrindStone 141-143
NEIFacadeRecipeHandler AEFeature.Facades 且 AEFeature.EnableFacadeCrafting 144-147

⚠️ NEIFacadeRecipeHandler 需要两个开关同时开。 对应条目:AEFeature.GrindStone、 AEFeature.Facades、 AEFeature.EnableFacadeCrafting。

注入内容 5:item filter

filter 源文件 注册行
NEIOreDictionaryFilter integration/modules/NEIHelpers/NEIOreDictionaryFilter.java 132

NEICellSearchFilter(integration/modules/NEIHelpers/NEICellSearchFilter.java) 在别处使用,不在 init() 里注册。

注入内容 6:bookmark container handler

反射签名(NEI.java:148-149): getDeclaredMethod("registerBookmarkContainerHandler", Class.class, IBookmarkContainerHandler.class)

目标 GUI handler 行号
GuiSkyChest.class NEIAEBookmarkContainerHandler 150(注释 // Skystone chests)
GuiCraftingTerm.class NEIAETerminalBookmarkContainerHandler 153(注释 // Crafting Terminal)
GuiMEMonitorable.class NEIAETerminalBookmarkContainerHandler 156(注释 // Terminal)
GuiPatternTerm.class NEIAETerminalBookmarkContainerHandler 159(注释 // Pattern terminal)
GuiPatternTermEx.class NEIAETerminalBookmarkContainerHandler 162(注释 // Big pattern terminal)
GuiMEPortableCell.class NEIAETerminalBookmarkContainerHandler 165(注释 // ME chest)

6 个 GUI,共用 2 个 handler 类(只有 GuiSkyChest 用专用的 NEIAEBookmarkContainerHandler,其余 5 个全用 NEIAETerminalBookmarkContainerHandler)。

⚠️ 5 行里传的是同一个 handler 的 5 个新实例(每次 new), 而非同一实例。

紧随其后(:167-168)直接调用:

GuiContainerManager.addTooltipHandler(this);
GuiContainerManager.addObjectHandler(this);

对应 AE2 GUI 在 appeng/client/gui/(不在本分片): GuiSkyChest(方块天空石箱 的 GUI)、 GuiCraftingTerm(加工终端 的 GUI)、 GuiPatternTerm(配方终端)、 GuiPatternTermEx(扩展配方终端)。

注入内容 7:AE2 侧反查(NBT)

NEI.java:339:

FMLInterModComms.sendMessage("NotEnoughItems", "registerHandlerInfo", aNBT);

与 IntegrationType.java:46 的 modID "NotEnoughItems" 大小写一致。

mod 没装时是否安全跳过

是,且双保险:

  1. NotEnoughItems 未装 → FAILED;且 side = CLIENT, 服务端根本不会 add(IntegrationRegistry.java:30-32)
  2. 装了但 API 变了 → getDeclaredMethod 抛 NoSuchMethodException → 被 IntegrationNode.java:102 的 catch (Throwable) 吞掉 → FAILED

配套的 17 个 NEIHelpers

integration/modules/NEIHelpers/ 共 17 个 .java 文件, 见 nei-helpers 总览。

相关条目