生物群落清洁工

生物群落清洁工

从你的世界中移除微型生物群系。

世界生成

Biome Cleaner

清理 Minecraft 地形生成所产生的杂乱、零散的生物群系斑块。小型生物群系区域会被合并到更大的相邻区域中,让你的世界拥有更清晰的边界与更自然的感觉——同时不改变地形形状、结构或玩法。

该模组仅在服务端运行(无需客户端安装),并且完全在自身的后台线程上运行,因此不会影响服务器的 tick 速率。

前后对比

在默认设置下,森林中央那些仅 1 个区块大小的平原斑块,或者出现在不该出现位置的尴尬海滩细条,都会被吸收到周围的生物群系中。结果是生物群系区域更加平滑、连贯,同时保持整体世界布局不变。

特性

  • 可配置阈值 —— 设置生物群系区域必须有多小才会被合并(默认:512 quarts)
  • 生物群系保护 —— 将特定生物群系(如稀有生物群系)标记为永不被替换
  • 替换控制 —— 阻止某些生物群系(如海洋)扩散到其他区域
  • 生物群系组 —— 定义组以进行批量配置(例如所有海洋变种)
  • 按组阈值 —— 为不同生物群系类型使用不同的大小阈值
  • 大小范围裁剪 —— 通过将过大的区域裁剪缩小而非直接替换,来限制生物群系的最大尺寸(例如保持石岸较小)
  • 组感知保留 —— 将某个组标记为“偏好”,使其成员之间会先相互合并,然后再翻转为组外生物群系;没有组内邻居的中等区域会被保留而非替换(例如保留内陆湖泊,但仍清理微小的海洋水洼)
  • 边界要求 —— 如果某个生物群系的边界未接触到正确的邻居,则无论大小都强制清理(例如清除附近没有海洋或河流的内陆海滩)
  • 后台预处理 —— 区块会在玩家到达之前被清理,因此新地形加载时不会有卡顿
  • 仅服务端 —— 无需客户端安装

添加到现有世界

该模组在新世界上效果最佳,但也可以添加到现有世界,但有一个注意事项:在你已探索区域的边界处可能会出现生物群系接缝。

这是因为该模组只会在区块首次生成时对其产生影响。如果一个小型生物群系斑块横跨你已探索区块和未探索区块之间的边界,旧的一侧会保留其原始生物群系,而新的一侧会被清理——从而在两侧相接处产生可见的不匹配。

这些接缝只会出现在你之前已探索区域的边缘,并且只有当小型生物群系斑块恰好跨越该边界时才会出现。你的其余新地形将按预期被完全清理。一旦你越过边界区域,一切都会无缝衔接。

开新世界? 完全没问题——每个区块在生成时都会被清理。

配置

主配置

位于 config/biomecleaner/common.json:

选项 默认值 描述
enabled true 生物群系清理的总开关
sizeThreshold 512 最小区域大小,单位为 quarts(4x4 方块区域)。小于此值的区域会被合并。范围:1--1024
neverReplace ["rare"] 永不替换的生物群系/组,即使它们很小
neverUseAsReplacement ["oceans", "rivers"] 永不替换其他生物群系的生物群系/组
allowIntraGroupReplacement ["oceans"] 允许组内生物群系相互替换的组
preprocessingEnabled true 启用后台预处理(推荐)。启用后,区块会在玩家到达之前被清理
preprocessingThreadCount 0 用于预处理的工作线程数。0 = 自动(基于你的 CPU)
cacheMemoryMB 128 预处理器缓存的内存预算,单位为 MB。如果你有富余的 RAM 并希望获得更长的提前时间,可以增加此值

什么是 quart? Minecraft 以 1/4 方块分辨率存储生物群系(每个 4x4x4 方块立方体对应一个生物群系值)。“quart”就是这些 4x4 生物群系单元之一。默认阈值 512 quarts 大约对应 90x90 方块区域——小于此大小的斑块会被清理。

高级配置

位于 config/biomecleaner/advanced.json:

字段 描述
groups 定义用于主配置中的命名生物群系组
sizeThresholdOverrides 为特定生物群系或组覆盖大小阈值。接受单个数字(最小大小)或 [min, max] 对
intraGroupPreference 将某个组标记为“偏好”,使其成员之间会先相互合并,然后再翻转为不同组,并保留没有组内邻居的中等区域
requiredBoundaryGroups 要求某个生物群系的边界至少接触到所列组之一——未满足的区域无论大小都会被清理

sizeThresholdOverrides

每个条目接受单个数字或 [min, max] 对:

  • "biome": N —— 小于 N quarts 的区域会被替换。无上限。(与 1.1.0 之前的行为相同。)
  • "biome": [min, max] —— 小于 min 的区域会被替换;介于 min 与 max 之间的区域会被保留;大于 max 的区域会被裁剪缩小为 max 大小的斑块,而非直接替换。

默认配置为 minecraft:stony_shore 使用 [32, 64],这样非常小的石岸斑块仍会被完全清理,但过大的石岸区域会被缩小而非从世界中抹除。

当同一生物群系既被直接命名(例如 "minecraft:stony_shore")又通过其所属的组命名(例如 "beaches")时,更具体的生物群系 ID 条目优先。

intraGroupPreference

每个条目将一个组名映射到一个偏好对象:

  • mode: "preferred" —— 此组中的生物群系区域在可能时优先合并到同组的其他成员中。
  • fallbackOutOfGroupBelowSize: N —— 如果没有可用的组内邻居,小于 N quarts 的区域会回退到正常的组外替换;大于等于 N 的区域则会被保留而非翻转。

默认配置将 oceans 标记为偏好,回退大小为 8。实际效果:

  • 位于陆地中央的微小海洋水洼(小于 8 quarts)会像正常情况一样被清理为周围陆地——附近没有其他海洋可合并,且小到我们不想保留它。
  • 较大的内陆湖泊(8 quarts 或以上)会被保留而非替换,即使它没有其他海洋作为邻居。
  • 紧邻不同海洋变种的沿海海洋细条(例如紧挨着 frozen_ocean 的小块 ocean 斑块)会合并到相邻的变种中,而不是变成海滩。因此,一半海洋一半冰冻海洋的区域会被清理为单一的海洋变种。

requiredBoundaryGroups

每个条目将一个生物群系(或组)映射到一个必须出现在其边界上的组列表。如果没有任何所需组存在,该区域无论大小都会被清理。

默认配置要求 beaches 接触到 oceans 或 rivers。若没有此规则,一个异常巨大的海滩斑块,即使周围只有森林,也会因为足够大而越过大小阈值从而被保留。有了此规则,任何附近没有水域的内陆海滩都会被清理,无论它有多大。

默认高级配置
{
  "groups": {
    "rare": ["minecraft:mushroom_fields", "minecraft:stony_peaks"],
    "oceans": [
      "minecraft:ocean", "minecraft:warm_ocean", "minecraft:lukewarm_ocean",
      "minecraft:cold_ocean", "minecraft:frozen_ocean", "minecraft:deep_ocean",
      "minecraft:deep_lukewarm_ocean", "minecraft:deep_cold_ocean", "minecraft:deep_frozen_ocean"
    ],
    "rivers": ["minecraft:river", "minecraft:frozen_river"],
    "beaches": ["minecraft:beach", "minecraft:snowy_beach", "minecraft:stony_shore"]
  },
  "sizeThresholdOverrides": {
    "beaches": 32,
    "minecraft:stony_shore": [32, 64],
    "rivers": 48
  },
  "intraGroupPreference": {
    "oceans": {
      "mode": "preferred",
      "fallbackOutOfGroupBelowSize": 8
    }
  },
  "requiredBoundaryGroups": {
    "beaches": ["oceans", "rivers"]
  }
}

推荐的 JVM 参数

该模组的工作线程被设计为以低于主服务器线程的优先级运行,因此当服务器需要 CPU 时间时,它们会自动让出。默认情况下,Java 会忽略线程优先级设置。将以下两个 JVM 标志添加到你的服务器启动脚本中即可启用它们:

-XX:+UseThreadPriorities -XX:ThreadPriorityPolicy=1

这是可选的,但推荐使用——它确保模组的后台工作即使在重负载下也永远不会与 tick 处理竞争。

性能

关于这些数据的说明: 本节中的每个数字都是在 1.0.3 版本上测得的。1.1.0 版本包含重写的清理算法和三个新的配置特性,并且没有已知的性能退化,但以下数字尚未针对新构建重新测量。更新后的测试结果会在未来版本中发布。

TL;DR:你根本不会注意到它。 在 1.0.3 上,该模组在默认设置下增加了约 2.4% 的 CPU 开销,并且完全在自身的后台线程上运行——服务器 tick 速率未受影响。我们预计 1.1.0 会处于同一水平。

这些数字来自默认的 sizeThreshold = 512。更低的阈值开销更小,更高的阈值开销略高——但都保持在 3% CPU 以下。

项目 结果
CPU 开销 2.4% 总计,全部在专用工作线程上
对原版服务器代码的影响 无可测量影响(在原版 0.1% 以内)
区块处理时间 92.7% 的区块在 1 ms 内完成清理
缓存命中率 100%——区块在你到达之前就已就绪
平均提前时间 领先玩家 6.1 秒
GC 暂停 全部低于 16 ms(tick 预算为 50 ms)

该模组会预测玩家前进方向,并在区块需要之前就在后台预先清理它们。在基准测试中,79% 的区块在玩家到达前 5 秒以上就已就绪。

阈值对比

更高的阈值清理更激进(合并更大的斑块),代价是略微增加开销:

256 512(默认) 1024
CPU 开销 2.0% 2.4% 2.9%
区块清理 < 1 ms 97.8% 92.7% 87.1%
收到替换的区块 2.7% 4.1% 5.9%
缓存命中率 100% 100% 100%
提前时间 6.1 s 6.1 s 5.9 s

所有阈值都在专用后台线程上运行,不影响原版服务器性能。

详细基准测试数据

测试设置

参数 值
Minecraft 1.21.11
运行次数 每种配置 10 次
行进距离 5,000 方块
视野/模拟距离 10
后台线程 16
服务器内存 6 GB

CPU 细分

指标 原版 256 512 1024
模组 CPU 工作 -- 2.0% 2.4% 2.9%
非模组代码相对原版差异 -- -0.4% -0.1% -1.0%

非模组代码在所有阈值下都保持在原版 1% 以内,确认该模组不会从服务器窃取 CPU 时间。

区块处理

百分位 256 512 1024
中位数 (p50) 0.060 ms 0.047 ms 0.038 ms
p95 0.745 ms 1.117 ms 1.948 ms
p99 1.356 ms 1.975 ms 3.331 ms
最大值 9.97 ms 11.19 ms 24.94 ms

默认阈值下的平均处理时间为每区块 0.249 ms。

垃圾回收

指标 原版 256 512 1024
平均总暂停/次运行 228 ms 282 ms 284 ms 286 ms
平均暂停时长 7.46 ms 8.85 ms 8.93 ms 9.07 ms
最大单次暂停 12.3 ms 23.9 ms 15.5 ms 15.4 ms

GC 影响在各阈值间几乎相同。所有暂停都远低于 50 ms 的 tick 预算。

缓存与预热

指标 256 512 1024
每次运行处理区块数 ~8,500 ~8,500 ~8,400
缓存命中率 100.0% 100.0% 100.0%
平均提前时间 6.1 s 6.1 s 5.9 s
提前 5 秒以上预热 76.9% 78.9% 75.6%
收到替换的区块 2.7% 4.1% 5.9%