跳到正文
烈焰甘蔗Wiki

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,语义等价)。

相关条目

浏览全部模组