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 没装时是否安全跳过
是,且双保险:
NotEnoughItems未装 →FAILED;且 side = CLIENT, 服务端根本不会add(IntegrationRegistry.java:30-32)- 装了但 API 变了 →
getDeclaredMethod抛NoSuchMethodException→ 被IntegrationNode.java:102的catch (Throwable)吞掉 →FAILED
配套的 17 个 NEIHelpers
integration/modules/NEIHelpers/ 共 17 个 .java 文件,
见 nei-helpers 总览。