IntegrationNode(appeng.integration.IntegrationNode)

基本信息

属性 值
源码 appeng/integration/IntegrationNode.java
类声明 21,public final class
构造器 :34,四参数 (String displayName, String modID, IntegrationType shortName, String name)
持有者 IntegrationRegistry.modules(IntegrationRegistry.java:27)
初始状态 state = IntegrationStage.PRE_INIT(:26)

职责

单个兼容目标的运行时状态机:判断是否启用 → 反射加载模块类 → 按 PRE_INIT / INIT / POST_INIT 三段调用 → 失败则隔离。

字段

字段 行号 说明
displayName 23 用于配置项名(IntegrationNode.java:68 去掉空格)
modID 24 可为 null(见 IntegrationType)
shortName 25 其实是 IntegrationType 枚举,不是短名
state 26 当前阶段
failedStage 27 失败发生在哪一段
exception 28 失败异常
name 29 模块类的全限定名
classValue 30 反射拿到的 Class
instance 31 模块实例
mod 32 同一实例,类型为 IIntegrationModule

启用判定(:63-91 的 PRE_INIT 分支)

顺序严格:

  1. AEConfig.instance.addCustomCategoryComment("ModIntegration", ...)(:64-66)—— 首次运行才加配置注释,说明值域是 AUTO / ON / OFF。
  2. 读配置:AEConfig.instance.get("ModIntegration", displayName.replace(" ", ""), "AUTO") (:67-68)。默认 AUTO。
  3. 三分支(:70-79):
配置值 enabled
ON(忽略大小写) true
OFF(忽略大小写) false
其它(含 AUTO) modID == null || ModAPIManager.INSTANCE.hasAPI(modID) || Loader.isModLoaded(modID)
  1. enabled → 反射加载(:82-85);否则 throw new ModNotInstalled(modID)(:87)。

⚠️ 目标 mod 没装时是否安全跳过:是。Loader.isModLoaded 返回 false → 抛 ModNotInstalled → :102 捕获 → state = FAILED → 仅打 info 日志 (:112 明确排除 ModNotInstalled 不走 AELog.integration)。 其余 26 个目标全部走这条路,不会因缺 mod 而崩游戏。

⚠️ 但配置项名是 displayName.replace(" ", "")(:68)—— 例如 CoFHWrench 的配置键是 CoFHWrench, IC2 的是 IC2,RF 的是 RedstoneFluxPower-Tiles, RFItem 的是 RedstoneFluxPower-Items。两个 CoFHAPI 目标配置键不同。

反射加载的 4 条硬约束

// IntegrationNode.java:82-85
this.classValue = this.getClass().getClassLoader().loadClass(this.name);
this.mod = (IIntegrationModule) this.classValue.getConstructor().newInstance();
final Field f = this.classValue.getField("instance");
f.set(this.classValue, this.setInstance(this.mod));
# 约束 违反后果
1 类名 = appeng.integration.modules. + 常量名(PACKAGE_PREFIX,IntegrationRegistry.java:25) ClassNotFoundException
2 implements IIntegrationModule ClassCastException
3 有公开无参构造器 NoSuchMethodException
4 有 public static instance 字段(getField 只返回 public 字段) NoSuchFieldException

RB(:40)与 CraftGuide(:48)违反约束 1 —— appeng/integration/modules/RB.java 与 CraftGuide.java 不存在, 运行时必失败。详见 IntegrationType。

阶段跃迁的幂等保护

// IntegrationNode.java:56-59
if (this.getState() != IntegrationStage.FAILED) {
    if (this.getState().ordinal() > stage.ordinal()) {
        return;
    }
  • FAILED 之后所有后续阶段调用直接跳过(:56)
  • 已推进到更后阶段时,重复调早阶段直接返回(:57-59)

IntegrationRegistry.init() 因此把 PRE_INIT 与 INIT 分两轮遍历 (IntegrationRegistry.java:42-48)是安全的。

isActive() 的副作用

// IntegrationNode.java:47-53
boolean isActive() {
    if (this.getState() == IntegrationStage.PRE_INIT) {
        this.call(IntegrationStage.PRE_INIT);
    }
    return this.getState() != IntegrationStage.FAILED;
}

getState() != FAILED 即为「启用」—— PRE_INIT 本身也算启用状态(不是 READY 才算)。

⚠️ 「查询」会触发加载:IntegrationRegistry.isEnabled(name)(:73) → node.isActive() → 可能当场 loadClass + newInstance。 getInstanceIfEnabled(:88)同样。这在 ASMIntegration 的类转换阶段是可行的 (所有目标此时都已 add),但意味着查询不是纯函数。

日志时机

call(stage) 尾部(:109-118)只在 stage == POST_INIT 时打印:

情况 输出 行号
失败且原因是 ModNotInstalled AELog.info(displayName + " - Integration Disabled") 111
失败(其它原因) 同上 + AELog.integration(exception) 111 + 113
成功 AELog.info(displayName + " - Integration Enable") 116

措辞是 Enable(无 d),源码原文如此。

toString() 与包私有访问器

toString()(:42-45)格式为 SHORTNAME:STATE。 getInstance(:121)、getShortName(:130)、getState(:134)、 isActive(:47)、call(:55)都是包私有 —— 只有 IntegrationRegistry(同包)能用。

相关条目