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 个特性常量。容易数错的两个陷阱:
- 2 个构造器声明不是常量。 文本匹配
名称(会同时命中AEFeature(final String cat)(:145)与AEFeature(final String cat, final boolean defaultValue)(:149), 得出 102。减去这 2 行才是 100。 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()。