生物群系修改器(Biome Modifiers)
生物群系修改器(Biome Modifiers)是一套数据驱动的系统,允许你更改生物群系的许多方面,包括注入或移除已放置特征(PlacedFeatures)、添加或移除生物生成、改变气候设置,以及调整植被和水的颜色。NeoForge 提供了多种默认的生物群系修改器,能够覆盖绝大多数玩家和模组开发者的常见需求。
推荐阅读章节:
-
玩家或整合包开发者:
-
仅需简单添加或移除生物群系内容的模组开发者:
-
需要自定义或复杂生物群系修改的模组开发者:
应用生物群系修改器(Applying Biome Modifiers)
要让 NeoForge 在游戏中加载一个生物群系修改器的 JSON 文件,需要将该文件放在模组资源目录下的 data/<modid>/neoforge/biome_modifier/<path>.json 文件夹内,或者放在一个 数据包 中。之后,当 NeoForge 加载这个生物群系修改器时,会读取其中的指令,并在世界加载时将描述的修改应用到所有目标生物群系。如果模组已经存在相关的生物群系修改器,可以通过在相同位置和名称下放置新的 JSON 文件(来自数据包)来覆盖它。
你可以手动创建 JSON 文件,具体格式可参考 “内置的 NeoForge 生物群系修改器” 章节中的示例,或者按照 “数据生成生物群系修改器” 章节的方法进行数据生成。
内置的 NeoForge 生物群系修改器(Built-in Biome Modifiers)
这些生物群系修改器由 NeoForge 注册,任何人都可以直接使用。
None
这个生物群系修改器不会进行任何操作,也不会产生任何修改。整合包作者和玩家可以在数据包中使用它,通过用下方的 JSON 覆盖模组的生物群系修改器 JSON,从而禁用对应的生物群系修改功能。
- JSON
- Datagen
{
"type": "neoforge:none"
}
// 定义我们 BiomeModifier(生物群系修饰器)的 ResourceKey(资源键)。
public static final ResourceKey<BiomeModifier> NO_OP_EXAMPLE = ResourceKey.create(
NeoForgeRegistries.Keys.BIOME_MODIFIERS, // 该键所对应的注册表(registry)
ResourceLocation.fromNamespaceAndPath(MOD_ID, "no_op_example") // 注册表名称
);
// BUILDER 是传递给 DatapackBuiltinEntriesProvider 的 RegistrySetBuilder,
// 用于 `GatherDataEvent` 事件的监听器中。
BUILDER.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, bootstrap -> {
// 注册生物群系修饰器(biome modifiers)。
bootstrap.register(NO_OP_EXAMPLE, NoneBiomeModifier.INSTANCE);
});
添加特性(Add Features)
这种生物群系修饰器类型会向生物群系中添加 PlacedFeature(已放置特性,例如树木或矿石),使其能够在世界生成时出现。该修饰器需要指定要添加特性的生物群系的 id 或标签(tag),要添加到选定生物群系的 PlacedFeature 的 id 或标签,以及特性将在其中生成的 GenerationStep.Decoration(生成阶段装饰)。
- JSON
- Datagen
{
"type": "neoforge:add_features",
// 可以是单个生物群系 id,例如 "minecraft:plains",
// 也可以是生物群系 id 列表,例如 ["minecraft:plains", "minecraft:badlands", ...],
// 或者是生物群系标签(tag),例如 "#c:is_overworld"。
"biomes": "#namespace:your_biome_tag",
// 可以是单个已放置特性(placed feature)id,例如 "examplemod:add_features_example",
// 也可以是已放置特性 id 列表,例如 ["examplemod:add_features_example", "minecraft:ice_spike", ...],
// 或者是已放置特性标签(tag),例如 "#examplemod:placed_feature_tag"。
"features": "namespace:your_feature",
// 有效的枚举名请参见代码中的 GenerationStep.Decoration 枚举。
// 后文的装饰阶段(decoration step)部分也列出了所有可用的取值供参考。
"step": "underground_ores"
}
// 假设我们有一个名为 EXAMPLE_PLACED_FEATURE 的 PlacedFeature(放置特性)。
// 为我们的 BiomeModifier(生物群系修改器)定义 ResourceKey(资源键)。
public static final ResourceKey<BiomeModifier> ADD_FEATURES_EXAMPLE = ResourceKey.create(
NeoForgeRegistries.Keys.BIOME_MODIFIERS, // 此资源键对应的注册表(registry)
ResourceLocation.fromNamespaceAndPath(MOD_ID, "add_features_example") // 注册表名称
);
// BUILDER 是一个传递给 DatapackBuiltinEntriesProvider 的 RegistrySetBuilder,
// 用于 `GatherDataEvent` 事件的监听器中。
BUILDER.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, bootstrap -> {
// 查询所有需要的注册表(registry)。
// 只有在需要获取标签(tag)数据时,才需要查询静态注册表。
HolderGetter<Biome> biomes = bootstrap.lookup(Registries.BIOME);
HolderGetter<PlacedFeature> placedFeatures = bootstrap.lookup(Registries.PLACED_FEATURE);
// 注册生物群系修改器(biome modifiers)。
bootstrap.register(ADD_FEATURES_EXAMPLE,
new AddFeaturesBiomeModifier(
// 需要生成的生物群系(biome)
HolderSet.direct(biomes.getOrThrow(Biomes.PLAINS)),
// 要在生物群系中生成的特性(feature)
HolderSet.direct(placedFeatures.getOrThrow(EXAMPLE_PLACED_FEATURE)),
// 生成阶段(generation step)
GenerationStep.Decoration.LOCAL_MODIFICATIONS
)
);
})
当你向生物群系中添加原版的 PlacedFeature(放置特性)时需要格外小心,因为这可能会导致所谓的特性循环冲突(feature cycle violation):即两个生物群系在同一 GenerationStep(生成阶段)中拥有同样的两个特性,但顺序不同,这会导致崩溃。出于类似原因,同一个 PlacedFeature 不应被用于多个生物群系修改器。
原版的 PlacedFeature 可以在生物群系的 JSON 文件中引用,或通过生物群系修改器添加,但不能两者兼用。如果你确实需要这样做,最简单的解决办法是将原版 PlacedFeature 复制到你自己的命名空间下,从而避免这些问题。
移除特性(Remove Features)
这种类型的生物群系修改器(biome modifier)可以从生物群系中移除特性(如树木或矿石),使其在世界生成时不再出现。该修改器需要指定要移除特性的生物群系的 id 或标签(tag)、要移除的 PlacedFeature 的 id 或标签,以及这些特性将被移除的 GenerationStep.Decoration 阶段。
- JSON
- Datagen
{
"type": "neoforge:remove_features",
// 可以是生物群系 ID,例如 "minecraft:plains",
// 也可以是生物群系 ID 列表,例如 ["minecraft:plains", "minecraft:badlands", ...],
// 或者是生物群系标签(tag),例如 "#c:is_overworld"。
"biomes": "#namespace:your_biome_tag",
// 可以是已放置特性(placed feature)的 ID,例如 "examplemod:add_features_example",
// 也可以是已放置特性 ID 列表,例如 ["examplemod:add_features_example", "minecraft:ice_spike", ...],
// 或者是已放置特性标签,例如 "#examplemod:placed_feature_tag"。
"features": "namespace:problematic_feature",
// 可选字段,指定要移除特性的 GenerationStep(生成阶段),也可以是 GenerationStep 列表。
// 如果省略,默认为所有 GenerationStep。
// 有效的枚举名称请参见代码中的 GenerationStep.Decoration 枚举。
// 本文稍后在装饰阶段部分也会列出所有可用的值以供参考。
"steps": ["underground_ores", "underground_decoration"]
}
// 为我们的 BiomeModifier(生物群系修改器)定义 ResourceKey。
public static final ResourceKey<BiomeModifier> REMOVE_FEATURES_EXAMPLE = ResourceKey.create(
NeoForgeRegistries.Keys.BIOME_MODIFIERS, // 此 key 对应的注册表(Registry)
ResourceLocation.fromNamespaceAndPath(MOD_ID, "remove_features_example") // 注册表名称
);
// BUILDER 是传递给 DatapackBuiltinEntriesProvider 的 RegistrySetBuilder,
// 用于 `GatherDataEvent` 事件监听器中。
BUILDER.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, bootstrap -> {
// 查询(lookup)所需的注册表。
// 如果你需要获取标签(tag)数据,静态注册表只需要在此查找一次即可。
HolderGetter<Biome> biomes = bootstrap.lookup(Registries.BIOME);
HolderGetter<PlacedFeature> placedFeatures = bootstrap.lookup(Registries.PLACED_FEATURE);
// 注册生物群系修改器(biome modifiers)。
bootstrap.register(REMOVE_FEATURES_EXAMPLE,
new RemoveFeaturesBiomeModifier(
// 要移除特性的生物群系
biomes.getOrThrow(Tags.Biomes.IS_OVERWORLD),
// 要从生物群系中移除的特性
HolderSet.direct(placedFeatures.getOrThrow(OrePlacements.ORE_DIAMOND)),
// 要移除特性的生成阶段
Set.of(
GenerationStep.Decoration.LOCAL_MODIFICATIONS,
GenerationStep.Decoration.UNDERGROUND_ORES
)
)
);
});
添加生物生成(Add Spawns)
另请参阅 生物实体/自然生成。
这种生物群系修改器(biome modifier)类型可以向生物群系添加实体(entity)生成。该修改器接收实体生成要添加到的生物群系的 ID 或标签(tag),以及要添加的实体的 SpawnerData。每个 SpawnerData 包含实体的 ID、生成权重(spawn weight)、以及每次生成时的最小/最大实体数量。
如果你是一名模组开发者,并且添加了一个新的实体(entity),请确保为该实体注册了生成限制(spawn restriction),方法是注册到 RegisterSpawnPlacementsEvent。生成限制用于确保实体能安全地生成在地面或水中。如果你没有注册生成限制,你的实体可能会在空中生成,掉落并死亡。
- JSON
- Datagen
{
"type": "neoforge:add_spawns",
// 可以是生物群系(biome)的 id,例如 "minecraft:plains",
// 或一个生物群系 id 列表,例如 ["minecraft:plains", "minecraft:badlands", ...],
// 也可以是生物群系标签(tag),如 "#c:is_overworld"。
"biomes": "#namespace:biome_tag",
// 可以是单个对象,也可以是对象列表。
"spawners": [
{
"type": "namespace:entity_type", // 要生成的实体类型(entity type)的 id
"weight": 100, // 非负整数,生成权重
"minCount": 1, // 正整数,最小生成组数
"maxCount": 4 // 正整数,最大生成组数
},
{
"type": "minecraft:ghast",
"weight": 1,
"minCount": 5,
"maxCount": 10
}
]
}
// 假设我们有一个名为 EXAMPLE_ENTITY 的 EntityType<?>。
// 为我们的 BiomeModifier(生物群系修改器)定义 ResourceKey。
public static final ResourceKey<BiomeModifier> ADD_SPAWNS_EXAMPLE = ResourceKey.create(
NeoForgeRegistries.Keys.BIOME_MODIFIERS, // 此 key 所属的注册表(registry)
ResourceLocation.fromNamespaceAndPath(MOD_ID, "add_spawns_example") // 注册表名称
);
// BUILDER 是一个 RegistrySetBuilder,会在 `GatherDataEvent` 的监听器中
// 传递给 DatapackBuiltinEntriesProvider。
BUILDER.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, bootstrap -> {
// 查询所需的注册表。
// 只有当你需要获取标签数据时,才需要查询静态注册表。
HolderGetter<Biome> biomes = bootstrap.lookup(Registries.BIOME);
// 注册生物群系修改器。
bootstrap.register(ADD_SPAWNS_EXAMPLE,
new AddSpawnsBiomeModifier(
// 要在其中生成生物的生物群系
HolderSet.direct(biomes.getOrThrow(Biomes.PLAINS)),
// 要添加的实体生成器(spawner)
List.of(
new SpawnerData(EXAMPLE_ENTITY, 100, 1, 4),
new SpawnerData(EntityType.GHAST, 1, 5, 10)
)
)
);
});
移除生成(Remove Spawns)
此类生物群系修改器(biome modifier)用于从生物群系中移除实体的生成。该修改器需要传入要移除实体生成的生物群系 id 或标签(tag),以及要移除的实体类型(EntityType)的 id 或标签。
- JSON
- Datagen
{
"type": "neoforge:remove_spawns",
// 可以是生物群系(biome)的 id,例如 "minecraft:plains",
// 也可以是生物群系 id 的列表,例如 ["minecraft:plains", "minecraft:badlands", ...],
// 还可以是生物群系标签(biome tag),例如 "#c:is_overworld"。
"biomes": "#namespace:biome_tag",
// 可以是实体类型(entity type)的 id,例如 "minecraft:ghast",
// 也可以是实体类型 id 的列表,例如 ["minecraft:ghast", "minecraft:skeleton", ...],
// 还可以是实体类型标签(entity type tag),例如 "#minecraft:skeletons"。
"entity_types": "#namespace:entitytype_tag"
}
// 定义我们的 BiomeModifier(生物群系修改器)的 ResourceKey。
public static final ResourceKey<BiomeModifier> REMOVE_SPAWNS_EXAMPLE = ResourceKey.create(
NeoForgeRegistries.Keys.BIOME_MODIFIERS, // 此键对应的注册表(registry)
ResourceLocation.fromNamespaceAndPath(MOD_ID, "remove_spawns_example") // 注册表名称
);
// BUILDER 是一个 RegistrySetBuilder,会在监听 `GatherDataEvent` 时
// 传递给 DatapackBuiltinEntriesProvider。
BUILDER.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, bootstrap -> {
// 查询所需的注册表(registry)。
// 只有当你需要获取标签(tag)数据时,静态注册表才需要被查询。
HolderGetter<Biome> biomes = bootstrap.lookup(Registries.BIOME);
HolderGetter<EntityType<?>> entities = bootstrap.lookup(Registries.ENTITY_TYPE);
// 注册生物群系修改器(biome modifiers)。
bootstrap.register(REMOVE_SPAWNS_EXAMPLE,
new RemoveSpawnsBiomeModifier(
// 要移除生成的生物群系(biome)
biomes.getOrThrow(Tags.Biomes.IS_OVERWORLD),
// 要移除生成的实体(entity)
entities.getOrThrow(EntityTypeTags.SKELETONS)
)
);
});
添加生成消耗(Add Spawn Costs)
允许为生物群系(biome)添加新的生成消耗(spawn costs)。生成消耗是一种较新的机制,用于让生物(mob)在生物群系中更分散地生成,从而减少扎堆现象。其原理是,每个实体会释放出一个 charge(电荷),这个电荷会在实体周围叠加,如果附近有多个实体,则它们的电荷会相加。当尝试生成新的实体时,生成算法会查找某个位置,使得该位置的总 charge 字段乘以新生成实体的 charge 值后,小于该实体的 energy_budget(能量预算)。这种机制是更高级的生物生成方式,因此建议参考灵魂沙峡谷(Soul Sand Valley)生物群系(该机制的典型应用者)中的现有参数。
该修改器(modifier)需要提供要添加生成消耗的生物群系 id 或标签(tag),要添加生成消耗的实体类型(EntityType)id 或标签,以及该实体的 MobSpawnSettings.MobSpawnCost。MobSpawnCost 包含能量预算(energy budget),它表示在每个生成位置,基于每个实体生成时所提供的电荷,最多可以生成多少实体。
如果你是一个添加新实体(entity)的模组开发者,请确保你的实体已经在 RegisterSpawnPlacementsEvent 中注册了生成限制(spawn restriction)。
- JSON
- Datagen
{
"type": "neoforge:add_spawn_costs",
// 可以是一个生物群系(biome)的 ID,例如 "minecraft:plains",
// 也可以是一个生物群系 ID 的列表,例如 ["minecraft:plains", "minecraft:badlands", ...],
// 或者是一个生物群系标签(biome tag),例如 "#c:is_overworld"。
"biomes": "#namespace:biome_tag",
// 可以是一个实体类型(entity type)的 ID,例如 "minecraft:ghast",
// 也可以是实体类型 ID 的列表,例如 ["minecraft:ghast", "minecraft:skeleton", ...],
// 或者是一个实体类型标签(entity type tag),例如 "#minecraft:skeletons"。
"entity_types": "#minecraft:skeletons",
"spawn_cost": {
// 能量预算(energy budget)
"energy_budget": 1.0,
// 每个实体从预算中消耗的能量(charge)
"charge": 0.1
}
}
// 为我们的 BiomeModifier 定义 ResourceKey(资源键)。
public static final ResourceKey<BiomeModifier> ADD_SPAWN_COSTS_EXAMPLE = ResourceKey.create(
NeoForgeRegistries.Keys.BIOME_MODIFIERS, // 该资源键所属的注册表(registry)
ResourceLocation.fromNamespaceAndPath(MOD_ID, "add_spawn_costs_example") // 注册表名称
);
// BUILDER 是一个 RegistrySetBuilder,会在监听 `GatherDataEvent` 时传递给 DatapackBuiltinEntriesProvider。
BUILDER.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, bootstrap -> {
// 查找所需的注册表(registry)。
// 如果你需要获取标签(tag)数据,只有静态注册表才需要被查找。
HolderGetter<Biome> biomes = bootstrap.lookup(Registries.BIOME);
HolderGetter<EntityType<?>> entities = bootstrap.lookup(Registries.ENTITY_TYPE);
// 注册(register)生物群系修改器(biome modifiers)。
bootstrap.register(ADD_SPAWN_COSTS_EXAMPLE,
new AddSpawnCostsBiomeModifier(
// 要添加生成消耗(spawn costs)的生物群系
biomes.getOrThrow(Tags.Biomes.IS_OVERWORLD),
// 要为其添加生成消耗的实体
entities.getOrThrow(EntityTypeTags.SKELETONS),
new MobSpawnSettings.MobSpawnCost(
1.0, // 能量预算(energy budget)
0.1 // 每个实体消耗的能量(charge)
)
)
);
});
移除生成消耗(Remove Spawn Costs)
允许你从某个生物群系(biome)中移除生成消耗(spawn cost)。生成消耗是一种较新的机制,用于让生物在群系中分散生成,从而减少聚集。此修改器(modifier)需要传入要移除生成消耗的生物群系的 ID 或标签(tag),以及要移除生成消耗的实体类型(EntityType)的 ID 或标签。
- JSON
- Datagen
{
"type": "neoforge:remove_spawn_costs",
// 可以是一个生物群系 ID,例如 "minecraft:plains",
// 也可以是一个生物群系 ID 列表,例如 ["minecraft:plains", "minecraft:badlands", ...],
// 或者是一个生物群系标签(tag),如 "#c:is_overworld"。
"biomes": "#namespace:biome_tag",
// 可以是一个实体类型 ID,例如 "minecraft:ghast",
// 也可以是一个实体类型 ID 列表,例如 ["minecraft:ghast", "minecraft:skeleton", ...],
// 或者是一个实体类型标签(tag),如 "#minecraft:skeletons"。
"entity_types": "#minecraft:skeletons"
}
// 为我们的 BiomeModifier 定义 ResourceKey(资源键)。
public static final ResourceKey<BiomeModifier> REMOVE_SPAWN_COSTS_EXAMPLE = ResourceKey.create(
NeoForgeRegistries.Keys.BIOME_MODIFIERS, // 该键对应的注册表
ResourceLocation.fromNamespaceAndPath(MOD_ID, "remove_spawn_costs_example") // 注册表名称
);
// BUILDER 是传递给 DatapackBuiltinEntriesProvider 的 RegistrySetBuilder
// 用于 `GatherDataEvent` 事件监听器中。
BUILDER.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, bootstrap -> {
// 查找所有需要的注册表。
// 静态注册表只有在需要获取标签(tag)数据时才需要查找。
HolderGetter<Biome> biomes = bootstrap.lookup(Registries.BIOME);
HolderGetter<EntityType<?>> entities = bootstrap.lookup(Registries.ENTITY_TYPE);
// 注册生物群系修饰器(biome modifiers)。
bootstrap.register(REMOVE_SPAWN_COSTS_EXAMPLE,
new RemoveSpawnCostsBiomeModifier(
// 要移除生成消耗的生物群系
biomes.getOrThrow(Tags.Biomes.IS_OVERWORLD),
// 要移除生成消耗的实体
entities.getOrThrow(EntityTypeTags.SKELETONS)
)
);
});
添加传统地形切割器(Add Legacy Carvers)
这种生物群系修饰器类型允许向生物群系添加地形切割器(carver)洞穴和峡谷。这些机制用于洞穴生成,属于洞穴与悬崖更新(Caves and Cliffs)之前的旧系统。它无法向生物群系添加噪声洞穴(noise caves),因为噪声洞穴属于特定的基于噪声的区块生 成器系统,并不实际绑定到生物群系上。
- JSON
- Datagen
{
"type": "neoforge:add_carvers",
// 可以是一个生物群系 ID,例如 "minecraft:plains",
// 也可以是一个生物群系 ID 列表,例如 ["minecraft:plains", "minecraft:badlands", ...],
// 或者是一个生物群系标签(tag),如 "#c:is_overworld"。
"biomes": "minecraft:plains",
// 可以是一个地形切割器(carver)ID,例如 "examplemod:add_carvers_example",
// 也可以是一个地形切割器 ID 列表,例如 ["examplemod:add_carvers_example", "minecraft:canyon", ...],
// 或者是一个地形切割器标签(tag),如 "#examplemod:configured_carver_tag"。
"carvers": "examplemod:add_carvers_example"
}
// 假设我们有一个名为 EXAMPLE_CARVER 的 ConfiguredWorldCarver。
// 为我们的 BiomeModifier(生物群系修改器)定义 ResourceKey(资源键)。
public static final ResourceKey<BiomeModifier> ADD_CARVERS_EXAMPLE = ResourceKey.create(
NeoForgeRegistries.Keys.BIOME_MODIFIERS, // 该资源键所对应的注册表(registry)
ResourceLocation.fromNamespaceAndPath(MOD_ID, "add_carvers_example") // 注册表名称
);
// BUILDER 是一个 RegistrySetBuilder,会在监听 `GatherDataEvent` 时
// 被传递给 DatapackBuiltinEntriesProvider。
BUILDER.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, bootstrap -> {
// 查找所有需要用到的注册表(registry)。
// 只有当你需要获取标签(tag)数据时,才需要查找静态注册表。
HolderGetter<Biome> biomes = bootstrap.lookup(Registries.BIOME);
HolderGetter<ConfiguredWorldCarver<?>> carvers = bootstrap.lookup(Registries.CONFIGURED_CARVER);
// 注册生物群系修改器(biome modifiers)。
bootstrap.register(ADD_CARVERS_EXAMPLE,
new AddCarversBiomeModifier(
// 要生成的生物群系
HolderSet.direct(biomes.getOrThrow(Biomes.PLAINS)),
// 要在这些生物群系中生成的地形雕刻器(carver)
HolderSet.direct(carvers.getOrThrow(EXAMPLE_CARVER))
)
);
});
移除传统地形雕刻器(Removing Legacy Carvers)
这种生物群系修改器(biome modifier)类型允许你从生物群系(biome)中移除传统的地形雕刻器洞穴和峡谷(carver caves and ravines)。这些雕刻器用于洞穴生成,属于洞穴与山崖(Caves and Cliffs)更新之前的旧机制。它无法移除噪声洞穴(noise caves),因为噪声洞穴已经集成在维度(dimension)的噪声设置系统中,并不直接关联到生物群系。
- JSON
- Datagen
{
"type": "neoforge:remove_carvers",
// 可以是一个生物群系 id,例如 "minecraft:plains",
// 也可以是一个生物群系 id 列表,例如 ["minecraft:plains", "minecraft:badlands", ...],
// 或者是一个生物群系标签(tag),如 "#c:is_overworld"。
"biomes": "minecraft:plains",
// 可以是一个地形雕刻器(carver)id,例如 "examplemod:add_carvers_example",
// 也可以是一个雕刻器 id 列表,例如 ["examplemod:add_carvers_example", "minecraft:canyon", ...],
// 或者是一个雕刻器标签,如 "#examplemod:configured_carver_tag"。
"carvers": "examplemod:add_carvers_example"
}
// 为我们的 BiomeModifier 定义 ResourceKey(资源键)。
public static final ResourceKey<BiomeModifier> REMOVE_CARVERS_EXAMPLE = ResourceKey.create(
NeoForgeRegistries.Keys.BIOME_MODIFIERS, // 此资源键对应的注册表(registry)
ResourceLocation.fromNamespaceAndPath(MOD_ID, "remove_carvers_example") // 注册表名称
);
// BUILDER 是一个 RegistrySetBuilder,会在监听 `GatherDataEvent` 事件时传递给 DatapackBuiltinEntriesProvider。
// 在监听器中使用 BUILDER 添加内容。
BUILDER.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, bootstrap -> {
// 查询所有需要用到的注册表(registries)。
// 只有在需要获取标签(tag)数据时,才需要查询静态注册表。
HolderGetter<Biome> biomes = bootstrap.lookup(Registries.BIOME);
HolderGetter<ConfiguredWorldCarver<?>> carvers = bootstrap.lookup(Registries.CONFIGURED_CARVER);
// 注册生物群系修改器(biome modifiers)。
bootstrap.register(REMOVE_CARVERS_EXAMPLE,
new AddFeaturesBiomeModifier(
// 要移除的生物群系(biome)
biomes.getOrThrow(Tags.Biomes.IS_OVERWORLD),
// 要从生物群系中移除的地形雕刻器(carver)
HolderSet.direct(carvers.getOrThrow(Carvers.CAVE))
)
);
});
装饰步骤可用的取值(Available Values for Decoration Steps)
许多前述 JSON 文件中的 step 或 steps 字段,指的是 GenerationStep.Decoration 枚举(enum)。该枚举按如下顺序列出了所有步骤,这也是游戏在世界生成(worldgen)时所使用的顺序。请尽量将特性(feature)放在最适合它们的步骤中。
| 步骤(Step) | 描述(Description) |
|---|---|
raw_generation | 最先执行。用于生成特殊的地形类特性,例如小型末地岛。 |
lakes | 专门用于生成类似池塘的地形特性,如熔岩湖。 |
local_modifications | 用于对地形进行局部修改,例如晶洞、冰山、巨石或溶洞石笋。 |
underground_structures | 用于生成小型地下结构类特性,如地牢或化石。 |
surface_structures | 用于仅在地表生成的小型结构类特性,如沙漠井。 |
strongholds | 专门用于要塞(Stronghold)结构。在未修改的 Minecraft 中,这里不会添加任何特性。 |
underground_ores | 本阶段添加所有矿石和矿脉,包括金矿、泥土、花岗岩等。 |
underground_decoration | 通常用于装饰洞穴。例如石笋簇和幽匿脉(Sculk Vein)会在此阶段生成。 |
fluid_springs | 小型熔岩瀑布和瀑布等特性会在此阶段生成。 |
vegetal_decoration | 几乎所有植物(花、树、藤蔓等)都会在此阶段添加。 |
top_layer_modification | 最后执行。用于在寒冷生物群系的地表放置雪和冰。 |
创建自定义生物群系修改器(Custom Biome Modifiers)
BiomeModifier 的实现(Implementation)
在底层,生物群系修改器(Biome Modifiers)由三个部分组成:
- 数据包注册 的
BiomeModifier,用于修改生物群系构建器(biome builder)。 - 静态注册 的
MapCodec,用于对修改器进行编码和解码。 - 构建
BiomeModifier的 JSON 文件,其中使用注册的MapCodec的 id 作为可索引类型。
一个 BiomeModifier 包含两个方法:#modify 和 #codec。modify 方法接收当前 Biome 的 Holder,当前的 BiomeModifier.Phase,以及要修改的生物群系构建器。每个 BiomeModifier 会在每个 Phase 阶段被调用一次,以便有序地对生物群系进行特定的修改:
| 阶段(Phase) | 描述(Description) |
|---|---|
BEFORE_EVERYTHING | 用于所有需要在标准阶段之前运行的内容。 |
ADD | 添加特性、生成生物等。 |
REMOVE | 移除特性、移除生成生物等。 |
MODIFY | 修改单个数值(例如气候、颜色等)。 |
AFTER_EVERYTHING | 用于所有需要在标准阶段之后运行的内容。 |
所有的 BiomeModifier 都包含一个 type 键,用于引用该 BiomeModifier 所用 MapCodec 的 id。codec 接收一个用于编码和解码 modifier 的 MapCodec。这个 MapCodec 是静态注册的,其 id 会被用作 BiomeModifier 的 type。 |
public record ExampleBiomeModifier(HolderSet<Biome> biomes, int value) implements BiomeModifier {
@Override
public void modify(Holder<Biome> biome, Phase phase, ModifiableBiomeInfo.BiomeInfo.Builder builder) {
if (phase == /* 选择最符合你想要修改内容的阶段 */) {
// 修改 'builder',并检查该生物群系自身的任何信息
}
}
@Override
public MapCodec<? extends BiomeModifier> codec() {
return EXAMPLE_BIOME_MODIFIER.get();
}
}
// 在某个注册类中
private static final DeferredRegister<MapCodec<? extends BiomeModifier>> BIOME_MODIFIERS =
DeferredRegister.create(NeoForgeRegistries.Keys.BIOME_MODIFIER_SERIALIZERS, MOD_ID);
public static final Supplier<MapCodec<ExampleBiomeModifier>> EXAMPLE_BIOME_MODIFIER =
BIOME_MODIFIERS.register("example_biome_modifier", () -> RecordCodecBuilder.mapCodec(instance ->
instance.group(
Biome.LIST_CODEC.fieldOf("biomes").forGetter(ExampleBiomeModifier::biomes),
Codec.INT.fieldOf("value").forGetter(ExampleBiomeModifier::value)
).apply(instance, ExampleBiomeModifier::new)
));
通过数据生成(Datagen)创建生物群系修改器(Biome Modifiers)
可以通过数据生成,为 BiomeModifier 创建 JSON 文件,只需将 RegistrySetBuilder 传递给 DatapackBuiltinEntriesProvider。生成的 JSON 会被放置在 data/<modid>/neoforge/biome_modifier/<path>.json 路径下。
关于 RegistrySetBuilder 和 DatapackBuiltinEntriesProvider 的详细用法,请参阅数据包注册表的数据生成相关文章。
// 为我们的 BiomeModifier 定义 ResourceKey(资源键)。
public static final ResourceKey<BiomeModifier> EXAMPLE_MODIFIER = ResourceKey.create(
NeoForgeRegistries.Keys.BIOME_MODIFIERS, // 该键所对应的注册表
ResourceLocation.fromNamespaceAndPath(MOD_ID, "example_modifier") // 注册表名称
);
// BUILDER 是传递给 DatapackBuiltinEntriesProvider 的 RegistrySetBuilder
// 在 `GatherDataEvent` 的监听器中使用。
BUILDER.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, bootstrap -> {
// 查询所有需要的注册表。
// 静态注册表只有在需要获取标签数据时才需要查询。
HolderGetter<Biome> biomes = bootstrap.lookup(Registries.BIOME);
// 注册生物群系修改器。
bootstrap.register(EXAMPLE_MODIFIER,
new ExampleBiomeModifier(
biomes.getOrThrow(Tags.Biomes.IS_OVERWORLD),
20
)
);
});
这样会生成如下的 JSON 文件:
// 在 data/examplemod/neoforge/biome_modifier/example_modifier.json 中
{
// 修饰器的 MapCodec 的注册表(Registry)键
"type": "examplemod:example_biome_modifier",
// 所有额外的设置都应用在根对象上
"biomes": "#c:is_overworld",
"value": 20
}
目标为可能不存在的生物群系(Biome)
有时候,生物群系修饰器(biome modifier)需要作用于一个并不总是存在于游戏中的生物群系(biome)。如果修饰器直接指定了一个未注册的生物群系,那么在世界加载时就会导致崩溃。为了解决这个问题,可以创建一个生物群系标签(biome tag),并通过将 required 设置为 false,把目标生物群系作为一个可选的标签项(tag entry)添加进去。示例如下:
{
"replace": false,
"values": [
{
"id": "minecraft:pale_garden",
"required": false
}
]
}
使用这样的生物群系标签作为生物群系修饰器的目标时,如果该生物群系没有被注册,就不会导致崩溃。其中一个典型用例就是 Pale Garden 生物群系:它只会在 1.21.3 版本中启用 Winter Drop 数据包(datapack)时才会生成,否则在生物群系注册表(biome registry)中根本不存在。另一个用例是兼容模组生物群系:即使添加这些生物群系的模组没有安装,修饰器依然能够正常工作。
要为生物群系标签的数据生成(datagen)生成 可选项,数据生成代码大致如下:
// 在 TagsProvider<Biome> 的子类中
// 假设我们有一个 TagKey<Biome> 类型的 OPTIONAL_BIOMES_TAG
@Override
protected void addTags(HolderLookup.Provider registries) {
this.tag(OPTIONAL_BIOMES_TAG)
// 必须是代表注册对象(registry object)的 ResourceLocation
.addOptional(WinterDropBiomes.PALE_GARDEN.location());
}