预言(Predictions)权重重写
基本信息
| 属性 | 值 |
|---|---|
| 实现类 | core/entities/PredictionHandler.java |
| 调用时机 | postInit 末尾,WitcheryExtras.java:89 PredictionHandler.adjustPredictions() |
| 作用对象 | Witchery 本体的 PredictionManager 注册表 |
| 本 mod 注册的预言数 | 0(只改权重,不新增) |
| 数据结构 | 反射拿到的 Hashtable<Integer, Prediction> |
Witchery 的预言系统(
com.emoniph.witchery.predictions.*)不在本 wiki 收录范围 (见 分类页)。本页只记本 mod 如何覆写既有预言的权重。
注册表访问靠反射
core/types/Witchery_Predictions.java:13-25:
Object f = ReflectionUtils.getField(PredictionManager.instance(), "predictions");
Hashtable<Integer, Prediction> registry;
try {
if (f != null) {
registry = (Hashtable<Integer, Prediction>) f;
if (registry != null) {
return registry;
}
}
} catch (Throwable t) {}
return new Hashtable<Integer, Prediction>();
- 反射字段名:
PredictionManager实例的predictions catch (Throwable t) {}是空 catch(Witchery_Predictions.java:22)——反射失败被静默吞掉, 返回一个空 Hashtable,调用方无法区分「没有预言」和「反射失败」- 对外 API:
RecipeManager.Predictions.getPrediction(int aID)/.getPredictions()/.add()/.remove()(api/RecipeManager.java:431-466)
主流程
PredictionHandler.java:79-98:
public static void adjustPredictions() {
int[] aNewValues = new int[] { AsmConfig.chancePredictionSpawnZombie, /* ... */ };
for (int i = 0; i < 18; i++) {
Prediction aOld = RecipeManager.Predictions.getPrediction(i);
if (aOld != null) {
if (aOld.itemWeight != aNewValues[i - 1]) {
RecipeManager.Predictions.remove(aOld);
RecipeManager.Predictions.add(generateNewPrediction(aOld, aNewValues[i - 1]));
}
}
}
}
只在新旧权重不同时才重建(PredictionHandler.java:92):
remove(aOld) 后 add(generateNewPrediction(aOld, newWeight))。
⚠️ 源码缺陷:数组越界 + 下标错位
实测(脚本解析 PredictionHandler.java:80-88):数组字面量共 17 个元素。
| 下标 | 配置项 |
|---|---|
| 0 | chancePredictionSpawnZombie |
| 1 | chancePredictionSpawnSkeleton |
| 2 | chancePredictionSpawnEnt |
| 3 | chancePredictionStumbleAndFall |
| 4 | chancePredictionExtraIron |
| 5 | chancePredictionFindShinies |
| 6 | chancePredictionFindShinies(重复) |
| 7 | chancePredictionSpawnBuriedTreasure |
| 8 | chancePredictionVillagerLove |
| 9 | chancePredictionSpawnBabaYagaBad |
| 10 | chancePredictionSpawnBabaYagaGood |
| 11 | chancePredictionSpawnFriendlyWolf |
| 12 | chancePredictionSpawnProtectiveAnimal |
| 13 | chancePredictionSpawnProtectiveAnimal(重复) |
| 14 | chancePredictionGetWet |
| 15 | chancePredictionTeleportNether |
| 16 | chancePredictionExtraCoal |
三重不一致:
| 问题 | 位置 | 说明 |
|---|---|---|
| 数组越界 | PredictionHandler.java:89-92 |
循环 i = 0..17(18 次),但下标取 aNewValues[i - 1]。i = 0 时取 aNewValues[-1] → ArrayIndexOutOfBoundsException |
| 下标错位 | PredictionHandler.java:92、94 |
作者显然想让「预言 ID i」对应「数组第 i-1 项」,但循环却从 i = 0 起。正确写法应为 for (int i = 1; i < 18; i++) 或改用 aNewValues[i] |
| 末尾漏配 | 同上 | 实际只有 17 个权重,没有任何配置项能影响下标 17。而循环覆盖预言 ID 0..17 共 18 个 ID |
触发条件:越界只在 getPrediction(0) != null 时发生——i = 0 分支被
if (aOld != null) 包裹(PredictionHandler.java:91)。
即取决于 Witchery 是否注册了 ID 为 0 的预言;
Witchery 不在本 wiki 范围,无法从本仓库确认该 ID 是否存在。
若 ID 0 不存在,则退化为「预言 ID 17 的权重永远无法配置」。
另注:chancePredictionFindShinies 与 chancePredictionSpawnProtectiveAnimal
各占 2 个数组槽(15 个配置项 → 17 个槽位)。这可能是有意的
(两个不同预言共用一个权重开关),源码无注释说明,不要当作笔误断言。
重建逻辑:按类型反射复制字段
generateNewPrediction()(PredictionHandler.java:100-193)在 static 块
(PredictionHandler.java:49-77)预先反射了 12 个私有字段:
| 字段 | 所属类 |
|---|---|
selfFulfillmentProbabilityPerSec |
Prediction |
regularFulfillmentDurationInTicks |
PredictionAlwaysForced |
regularFulfillmentProbability |
PredictionAlwaysForced |
entityClass |
PredictionRescue |
chestGenHook |
PredictionBuriedTreasure |
block / itemPrototype / minExtra / maxExtra |
PredictionMultiMine |
entityClass / bindTameable |
PredictionFight |
按 instanceof 分支重建,共处理 9 种具体类型:
PredictionMultiMine、PredictionBuriedTreasure、PredictionFallInLove、
PredictionRescue(以上 4 种嵌在 PredictionAlwaysForced 分支内)、
PredictionFight、PredictionArrow、PredictionFall、PredictionWet、
PredictionNetherTrip。
⚠️ 未覆盖的类型:
PredictionMultiMine之外的PredictionAlwaysForced子类若不是上面 4 种之一,会走到PredictionHandler.java:107-109读取字段后aNewPrediction始终保持null(PredictionHandler.java:101初始化), 而remove(aOld)已在PredictionHandler.java:93执行—— 该预言会被静默删除。新增 Witchery 版本若引入新的AlwaysForced子类即触发。 源码没有任何兜底分支处理aNewPrediction == null。
关联
- 15 个权重开关的完整默认值见 配置项