源码缺陷清单
以下均为读源码实测得到的问题,附文件与行号。
1. 总开关 disableVariants 覆盖不全
GeneralConfig.disableVariants(Config/GeneralConfig.java:16)被描述为 “Disables all changes made by Variable Horizons”,且全部 19 个 MixinBuilder 的条件都检查了它。
但全仓 grep -rn "disableVariants" src/main --include=*.java 显示,除 Mixins.java 外没有任何其它文件引用它。这意味着 CommonProxy.serverStarted 中运行时施加的改动不受总开关保护:
CrimsonApocalypse.applyDifficultyToServer()—— 强制难度 HARD、屏蔽难度按钮AlteredRecipeTime.applyToServer()—— 全局配方时间乘数ChancedRecipes.applyToServer()—— GT 配方概率改写
以及 12 个 VariantLoader 本身的行为(NoPollution 改 PollutionConfig.pollution、InfinitePower 发配方、GardenOfGrind 改 GT 配置等)。
结论:把 disableVariants 设为 true 并不能完整关掉本 mod。
2. 枚举名与对外 id 不一致
VariantNames.java 中:
ALTERED_RECIPE_TIME("ALTERED_TIME", new AlteredRecipeTime()),
枚举常量名是 ALTERED_RECIPE_TIME,但对外 id 是 ALTERED_TIME。玩家、配置文件与本地化键(variants.ALTERED_TIME.name)用的都是后者。
这是全 21 个枚举中唯一 id 与常量名不符的项(其余 20 个均相同)。查 getVariantFromID("ALTERED_RECIPE_TIME") 会返回 null。
3. 文档与源码不符
README.md:8 声称:
“the player can do a simple /island create (requires permission level 1)”
但 IslandCommands.getRequiredPermissionLevel() 返回 0,即所有玩家都能用 /island create 自建岛屿。以源码为准。
en_US.lang中没有该描述——问题只出在 README。
4. 跨模组方块无存在性检查
util/islands/skyIslands.java 的 7 套岛屿模板用 GameRegistry.findBlock(modid, name) 硬编码字符串取用 11 个其它 mod 的方块(共 71 处引用):HardcoreEnderExpansion(16)、Thaumcraft(14)、BiomesOPlenty(13)、GalacticraftCore(7)、Natura(6)、chisel(5)、thaumicbases(3)、etfuturum(3)、Ztones(2)、miscutils(1)、OpenBlocks(1)、EnderIO(1)。
该文件内 == null / != null 判定共 0 处。整合包若缺少其中任一 mod,岛屿生成会异常。
util/blockUtils/BlocksRegistry.java内部确有checkIfBlacklisted()黑名单跳过与 3 处 null 判定,但那是 SKYGRID 的 tileentity 白/黑名单机制(配合skygridTEWhitelist/skygridBlacklist配置),不覆盖岛屿模板。
注:这是 mod 合法引用外部内容,不属于「跨 mod 物品混入」类幻觉。
5. 仅 en_US 语言
src/main/resources/assets/variablehorizons/lang/ 下只有 en_US.lang(161 行),无任何其它语言。中文玩家看到的变体名与描述会全部退化为键名。
6. @Mod 的 dependencies 与 build 依赖不一致
@Mod(..., dependencies = "after:dreamcraft")
声明了 after:dreamcraft,但 dependencies.gradle 中没有 DreamCraft 条目。after: 只是加载顺序软依赖,不要求 DreamCraft 存在,但 DreamCraft 未收录于本 wiki,且该 mod 大量直接引用 GregTech API(GTMod、GTRecipe、Worldgen、WirelessNetworkManager、Materials),实际只能在 GTNH 整合包内运行。
7. 维度 ID 魔法数字
| 位置 | 值 | 含义 |
|---|---|---|
ADD_SEMINING_RECIPE 条件 |
startingDimID != 100 |
排除维度 ID 100,但源码无注释说明 100 是哪个维度 |
REMOVE_ORECHID_IGNEM_DIMRESTRICTION 条件 |
startingDimID != -1 |
排除下界(-1) |
8. 仅 GUI 可写的空转配置
ALTERED_EFFICIENCY、CHEAP_MODE、EXPENSIVE_MODE 三个枚举项没有 VariantLoader 实现类,且在 Mixins.java 的 19 个 builder 条件中均未出现。
配置项 GeneralConfig.efficiencyMultiplier 的全部引用只在两处:
Config/GeneralConfig.java:32 (声明)
selectionUI/VariantGuiMain.java:84, 92, 400, 403 (读取 / 写回 / 变更检测 / 文本框绑定)
即只有选择 GUI 在读写它,没有任何 VariantLoader 或 mixin 消费这个值。在 GUI 里改它会触发 hasChanged() 但不会改变任何游戏行为——属于未完成功能。
NO_RECIPE_ADDITIONS 同样是纯标志位,但确实作为 incompatible / composedOf 成员参与组合判定,并非完全无用。
9. 选择 GUI 的存在
selectionUI/VariantGuiMain.java 是单/多人世界选择界面的变体切换 GUI(README 称 “Variants are easily configurable using the new gui in the singleplayer world selection screen”),是除 /variants 命令外的第二个切换入口,并额外提供 recipeTimeMultiplier、efficiencyMultiplier 与 4 个概率乘数的滑块/文本框(见 VariantGuiMain.java:400 一带)。
10. getActiveVariantsToCheck() 的排除列表
VariantNames.getActiveVariantsToCheck() 会移除 3 个变体:
activeVariantsToCheck.remove(VariantNames.CHANCED_RECIPES.id);
activeVariantsToCheck.remove(VariantNames.NO_POLLUTION.id);
activeVariantsToCheck.remove(VariantNames.CRIMSON_APOCALYPSE.id);
这 3 个是直接改 GT 配置而非走 mixin 条件,故不需要参与 mixin 判定。逻辑自洽,不算缺陷,但调用方需注意传入的是过滤后的集合。