PamTreeGenerator(野生果树生成)
事实基准:
harvestcraft-1.3.14-GTNH.jar(MC 1.7.10 / Forge 10.13.4.1614)CFR 0.152 反编译源码。 本条目的switch结论已用 class 字节码逐条复核。
基本信息
| 属性 | 值 | 来源 |
|---|---|---|
| 类 | com.pam.harvestcraft.PamTreeGenerator |
PamTreeGenerator.java:17 |
| 实现 | IWorldGenerator |
PamTreeGenerator.java:18 |
| 注册条件 | 无条件 | HarvestCraft.java:124 |
| 注册权重 | 0 | HarvestCraft.java:124 |
| 实例化时机 | init() —— 注意不是 preInit |
HarvestCraft.java:124 |
字节码级复核:generate 的维度 switch
PamTreeGenerator.class 的 generate 方法(code_len=105)实际布局:
| 偏移 | 指令 | 归属 |
|---|---|---|
| 0 | aload 4 |
取 chunkGenerator 参数(未使用) |
| 2 | getfield #26 |
World.provider |
| 5 | getfield #32 |
getDimensionId() |
| 8 | lookupswitch |
default +96;0→+36,6→+51,7→+72 |
| 44 | aload_0 |
case 0(主世界)起点 |
| 45–55 | 参数装配(chunkX*16、chunkZ*16) |
|
| 56 | invokevirtual #36 |
generateSurface(...) —— 其后无 goto |
| 59 | getstatic #42 |
case 6 起点;enableAroma1997sdimensionalworldfruittreeGen |
| 62 | ifeq → 80 |
开关为 false 时跳到 case 7 |
| 65–76 | 参数装配 | |
| 77 | invokevirtual #36 |
generateSurface(...) —— 其后无 goto |
| 80 | getstatic #45 |
case 7 起点;enabletwilightforestfruittreeGen |
| 83 | ifne → 89 |
开关为 true 时去做生成 |
| 86 | goto → 104 |
开关为 false 时跳过 |
| 89–100 | 参数装配 | |
| 101 | invokevirtual #36 |
generateSurface(...) |
| 104 | return |
default 落点 |
裁定:外层 switch 的落穿是真实的。 case 0(@56 后)与 case 6(@77 后)末尾都没有 goto,下一条指令分别就是 case 6(@59)与 case 7(@80)的入口。
注:上表的
getstatic #42/#45是该 class 自己的常量池下标,不与其他类可比;字段名以 CFR 源码PamTreeGenerator.java:25/:30为准。
实际运行行为
| 维度 | 行为 | 主世界被调用的次数 |
|---|---|---|
0 主世界 |
1 次 + Aroma 开关再 1 次 + 暮色森林开关再 1 次 | 1~3 次 |
6 |
Aroma 开关为 true 才 1 次,然后落入 case 7 再看暮色森林开关 |
0~2 次 |
7 |
暮色森林开关为 true 才 1 次 |
0~1 次 |
| 其它 | 无(default → return) |
0 |
默认安装下 4 个 dimensions 开关全为 false(BlockRegistry.java:438-441),主世界恰好只调用 1 次 generateSurface,看不出问题。只有当玩家把 enableAroma1997sdimensionalworldfruittreeGen / enabletwilightforestfruittreeGen 打开时,主世界才会重复跑 2~3 遍果树生成(表现为果树密度异常偏高)。
字节码级复核:generateSurface 内层 switch(这是编译期落穿,不是缺陷)
PamTreeGenerator.generateSurface(code_len=1827)里有两处按 rand.nextInt(N) 选树种的 tableswitch。CFR 输出同样没有 break,但字节码形态完全不同:
以 9 路 tableswitch(位于偏移 153,lo=0 hi=8,default → 492)为例:
| case | 起始偏移 | 指令序列 | 末指令 |
|---|---|---|---|
| 0 | 204 | getstatic #112; ifeq → 236; getstatic #117; …; invokevirtual #124 |
invokevirtual,无 goto |
| 1 | 236 | getstatic #127; ifeq → 268; … |
同上 |
| … | … | 每个 case 的 ifeq 目标恰好是下一个 case 的起始偏移 |
— |
| 8(末个) | 460 | getstatic #169; ifeq → 492; …; goto → 71 |
goto |
每个 case 体都是"条件为假就跳到下一个 case 的开头"。在字节码层面这确实落穿,但由于落穿点就是"下一个 case 的第一条指令(它自己的开关判断)",运行结果与源码写 break 完全等价。
这是 javac 对 if (开关) { ... } break; 的常规省略:break 的跳转目标恰好是下一条指令,javac 不再发射多余的 goto。因此:
- 判定:内层
switch缺break属于编译期优化 / CFR 有损呈现,不是源码缺陷。 - 25 路
tableswitch(偏移 777,lo=0 hi=24)形态相同:case 0..23的ifeq依次指向下一 case 起点,case 24末尾是goto→ 71。
CFR 在这里把"落到下一 case 开头"渲染成"没有 break",是因为源码的 break 已被 javac 省略,CFR 无从恢复。
生态筛选逻辑(generateSurface)
生态(BiomeDictionary.Type) |
树种集合 | 稀有度配置 | 配置默认值 |
|---|---|---|---|
FOREST 且非 COLD / SPOOKY / MOUNTAIN |
温带果树 9 种(苹果、鳄梨、樱桃、栗、肉豆蔻、梨、李、核桃、鹅莓) | temperatefruittreeRarity |
15(BlockRegistry.java:390) |
OCEAN |
椰子 | tropicalfruittreeRarity |
10(BlockRegistry.java:391) |
WET 且非 PLAIN/DRY,或 HOT 且非 PLAIN/DRY |
热带果树 25 种 | tropicalfruittreeRarity |
10 |
CONIFEROUS |
枫树(唯一一种,用 WorldGenPamFruitLogTree) |
coniferousfruittreeRarity |
15(BlockRegistry.java:392) |
- 整个方法开头对
DEAD生态直接return(PamTreeGenerator.java:42-44)。 - 每种树还有独立的
*treeGeneration布尔开关(fruit trees分类,共 38 项,默认全true),例如appletreeGeneration、cinnamontreeGeneration。 - 选点:
l = rand.nextInt(256),要求l-1处是Blocks.field_150349_c或Blocks.field_150346_d(SRG → 未解析;语义佐证:与 WorldGenPamFruitPalmTree 中被用作"可种树的地表"判据、并在其 :59 行被写回地面的方块一致 —— 即草方块与泥土)。 - 温带 9 种用
rand.nextInt(9)等概率选;热带 25 种用rand.nextInt(25)等概率选。 - 椰子出现两处:
OCEAN生态用new WorldGenPamFruitTree(true, 5, 3, 3, pamCoconut)(PamTreeGenerator.java:103),热带表里也有一项(:124)。 - 三种
*LogTree:肉桂(热带,:119)、纸皮树(热带,:194)、枫树(针叶,:243),都用WorldGenPamFruitLogTree。
源码缺陷
| 位置 | 问题 |
|---|---|
PamTreeGenerator.java:21-28 |
case 0 缺 break,落穿进 case 6。字节码 @56 后无 goto 可证。 |
PamTreeGenerator.java:24-28 |
case 6 缺 break,落穿进 case 7。字节码 @77 后无 goto 可证。 |
| — | 内层 rand.nextInt(9) / rand.nextInt(25) 的 switch 无 break 不是缺陷(javac 省略冗余 goto,语义等价)。 |