本帖最后由 酒石酸菌 于 2018-6-2 21:26 编辑

雪崩式的灾难:严重滞后的世界生成

这篇文章来自于 2017 年 5 月 2 号 mezz 发布在 r/feedthebeast 上面的一篇帖子,很遗憾昨天我才看到它,并了解了新版本 forge 新增加的提示功能的来源故事。我对其进行了翻译和演绎,让大家了解到更多有趣的故事。部分内容可能有出入,还请多批评指正。

mezz 是一名具有多年开发经验模组作者,他制作了赫赫有名的 JEI,参与了林业模组的维护,同时还是 Forge 团队的一员。
      原帖链接:https://www.reddit.com/r/feedthebeast/comments/5x0twz/investigating_extreme_worldgen_lag/


==问题==

一些具有开拓性思维的整合作者早早地把脚步伸入了 1.11.2 的世界。然而在这里,他们却遇到了极为严重的服务器滞后,还有漫长的地图生成等待。1.11.2 版本的 All The Mods 2 整合中甚至附带了这么一句警告:

新世界的生成可能会等待 5 分钟左右才能加载好,我们仍然在研究这个问题。

这是下图的例子,Rorax 击打了一头牛,然后就像服务器时间停滞了一样,这只牛居然在空中卡顿住好几秒钟。这究竟是发生了什么?



==分析==

寻找问题的最好办法就是使用 Java VisualVM(或者其他类似的 Java 性能分析工具),在游戏发生严重滞后时,对其进行分析。通过性能分析似乎并没有发现什么模组异常的占用了计算,就像是世界生成本来就应该那么慢。

世界生成先是生成一个基础的区块,而后在其上点缀上树木,矿石之类的东西。这些生成会促使区块加载。分析器分析进一步发现,许多模组实际上会加载更多的区块,并持续重复的使这个过程更长。

当一个结构(比如说树)跨区块生成时,它会同时加载所跨的几个区块。普通情况下,这种事件发送概率很小,随机生成的树木只会有很小几率会跨区块生成。然后如果有足够多的模组重复这个过程,情况将会变得极为糟糕。

加载一个区块,也许会连锁式的加载10个区块!实际上可能比这个更加糟糕!从而带来极为严重的滞后!

这是一个原版生成,可以看到,只有一小部分区块是被额外加载了的:



这是使用了未修复版本的 All The Mods 2 整合,并在相同种子情况下生成的结果:



上图中红色部分是图一的区块加载范围,对其进行比较,可以看到居然相隔几百区块远的地方也被加载了,这势必会造成极为严重的世界生成滞后!


==原因==

Minecraft 的世界生成似乎比很多模组作者所期望的还要古怪(我想大多数人应该已经不会为此感到惊讶了)。

模组作者想法很简单,一个区块加载了,它们的模组就开始在其上生成点什么东西,然而这种想法就大错特错了。这是一个原版的世界生成,可以看到地图边缘并没有树木生成,更没有失控生成的区块,树生成的边缘似乎都有一个框来框住它们。



当一个区块的 +X,+Z 和 +XZ 方向均加载时,才会进行区块的进一步装饰性生成。原版运用这种思维,使得树木总是在区块的中间生成,这大大减小了额外区块的生成。下图是这种判断生成的具体图示:



可是很多模组作者喜欢自己造轮子,而不是使用原版的方法。简单的在一个 16X16 的范围内随机获取一个坐标进行生成。但是这有几率溢出到 -X 和 -Z 方向,导致加载更多的区块(上图红色箭头所指方向)

正确的方法应该尝试在 (-8, +8) 范围内生成,这样生成总是会在最中间,溢出区块的几率很小。



==解决效果==

在过去的几周,我与几个模组作者合作解决这个问题,但是有时候很难解释清楚,这也是我为什么发表这篇文章的原因。随着几个模组相关修复版本的更新,All The Mods 2 整合的生成开始从 5 分钟下降到了 15 秒。


==我可以做些什么?==

如果你是模组作者,请检查是否是你的模组造成了如此严重的滞后,只需要使用原版的 WorldGenMinable 类即可,它内置了偏移相关的计算。如果你自己造了轮子,请注意生成的偏移是否正确。

如果你是玩家,尝试移除一部分模组,看看生成滞后是否还存在。如果确实是模组问题,请向其作者反馈。


==后记(译者添加)==

在新版本的 Forge 中添加了世界边缘生成判定,如下图所示,如果你看到了类似的错误提示,请向该模组作者反馈。