AEFeature

基本信息

属性 值
枚举类型 appeng.core.features.AEFeature
源码 appeng/core/features/AEFeature.java(共 185 行)
所属 modid appliedenergistics2
常量总数 100(实测,见下方集合运算)
内部私有枚举 Constants(AEFeature.java:163-184),只存放分类名字符串常量,不产生任何特性开关
构造器 2 个:AEFeature(String cat)(:145,defaultValue=true)、AEFeature(String cat, boolean)(:149)

常量数是怎么算出来的

AEFeature.java 全文只有 100 个特性常量。容易数错的两个陷阱:

  1. 2 个构造器声明不是常量。 文本匹配 名称( 会同时命中 AEFeature(final String cat)(:145)与 AEFeature(final String cat, final boolean defaultValue)(:149), 得出 102。减去这 2 行才是 100。
  2. Constants 私有枚举里的 18 个 CATEGORY_* 字段不是特性。 它们是 String 常量(:166-183),声明在 private enum Constants { ; ... } 内。

集合运算:候选 102 − 构造器声明 2 = 100;去重后仍为 100(无同名重复); CATEGORY_* 泄漏计数 0。

本枚举中没有被注释掉的常量

⚠️ 逐行读完全文 185 行:AEFeature.java 自身没有任何被注释掉的枚举常量, 也没有 lumen 相关的定义。被注释掉的是另一个文件里的电缆定义,见下方 「源码存在但实现被注释」。

门控机制

AEConfig.java:321-328 读取全部 AEFeature.values():

for (final AEFeature feature : AEFeature.values()) {
    if (feature.isVisible()) {
        if (this.get("Features." + feature.category, feature.name(), feature.defaultValue)
                .getBoolean(feature.defaultValue)) {
            this.featureFlags.add(feature);
        }
    } else {
        this.featureFlags.add(feature);   // 不可见 -> 无条件开启
    }
}

配置键形如 Features.<分类>.<常量名>,默认值取 defaultValue。 判定入口是 AEConfig.isFeatureEnabled(AEFeature)(AEConfig.java:494-496), 实质是 featureFlags.contains(f)。

特性只有 1 个 不可见常量:Core(:18-24)用常量体重写了 isVisible() 返回 false,因此无法被关闭。

AEConfig.java:332-338 还有一条硬编码例外:检测到 ImmibisCore 版本为 59.0.0 / 59.0.1 / 59.0.2 时,会把 AEFeature.AlphaPass 从 featureFlags 中强制移除,无视配置文件。

三条消费路径

统计 100 个常量的消费方式(对 src/main/java 全量 grep 后分类):

消费路径 常量数 说明
仅直接判断 69 运行时 isFeatureEnabled(...)
仅声明式标记 25 setFeature(EnumSet.of(...)) 或构造器传参
两者都有 5 SpatialIO、CraftingCPU、AlphaPass、Facades、GrindStone
无任何消费点 1 DenseCables

只统计 isFeatureEnabled 会得出「68 个常量从未被检查」的错误结论。 声明式标记由 FeaturedActiveChecker.getActivityState()(core/features/FeaturedActiveChecker.java:26-32) 统一求值:只要 EnumSet 里有一个特性被关,整个对象就判为 ActivityState.Disabled。

「仅直接判断」这一类的实际效果分两种,取决于 isFeatureEnabled 的调用位置: 控制注册流程的会让对象不再注册;控制运行时行为的只是关掉某个功能。

声明式门控的后果是「跳过注册」而非「隐藏」

  • ItemFeatureHandler.register()(core/features/ItemFeatureHandler.java:51-70) 只在 this.enabled 为真时调用 GameRegistry.registerItem。
  • AEBlockFeatureHandler.register()(core/features/AEBlockFeatureHandler.java:48-60) 同理决定 GameRegistry.registerBlock + registerItem。
  • ItemMultiPart.createPart()(items/parts/ItemMultiPart.java:112-113)与 ItemMultiMaterial.createMaterial()(items/materials/ItemMultiMaterial.java:157-158) 逐个求值其 EnumSet。

所以声明式特性被关闭后,对象在存档、配方、NEI 里都不存在。

分类分布(19 个分类,含 1 个 null)

分类 常量数
Tools 15
Misc 14
NetworkBuses 13
World 12
P2PTunnels 11
Crafting 6
NetworkFeatures 5
Storage 5
CraftingFeatures 5
ToolsClassifications 3
HigherCapacity 2
BlockFeatures 2
PortableCell / NetworkTool / AdvancedNetworkTool / Facades / Rendering / Commands 各 1
(null,即 Core) 1

默认开关值

默认值 常量数
true 89
false 11

11 个默认关闭的常量全部来自第二构造器的显式 false 实参(:109-122、:140): UnsupportedDeveloperTools、GrinderLogging、IntegrationLogging、WebsiteRecipes、 LogSecurityAudits、UpdateLogging、PacketLogging、CraftingLog、 ComplexPatternLog、DebugLogging、ChunkLoggerTrace。 除 UnsupportedDeveloperTools 外全部是日志类特性。

源码缺陷

1. DenseCables 是没有任何消费点的孤儿开关(AEFeature.java:90)

全仓 grep 只命中它自己的声明行;其余 99 个常量都至少有一处消费。 配置里写什么都不会改变任何游戏内容。结合下面的 lumen 电缆注释, 这个常量看起来是上游 AE2 遗留、尚未接线的预留开关。

2. 源码存在但实现被注释:4 条 lumen 电缆

本移植版的 lumen(流明)电缆没有实现。相关代码在源码中确实存在,但被注释:

文件:行号 内容
appeng/core/api/definitions/ApiParts.java:32-35 4 个 AEColoredItemDefinition lumenCable* 字段声明被注释
appeng/core/api/definitions/ApiParts.java:85-88 构造函数内 4 条 this.lumenCable* = Optional.absent(); 被注释,注释文字为 // has yet to be implemented, no PartType defined for it yet
appeng/core/Registration.java:336-339 assignParts() 内 4 条 // target.partLumenCable* = source.lumenCable*(); 被注释

⚠️ 这些不在注册集合内——ApiParts 构造时不会执行,PartType 枚举里也没有 任何 Lumen* 常量(全文检索 lumen 在 PartType.java 上 0 命中)。

另外 ApiParts.java:159-177 保留了 4 个会抛异常的访问器 lumenCableSmart() / lumenCableCovered() / lumenCableGlass() / lumenCableDense(), 方法体是 throw new MissingDefinition("Lumen ... has yet to be implemented."), 其后的 return this.lumenCable*; 才是被注释的那行。也就是说调用这些访问器会在 运行期抛 MissingDefinition,而不是返回 Optional.absent()。

相关条目