生物群系修改器

生物群系修改器

生物群系修改器(Biome Modifier)是一套数据驱动系统,可以修改生物群系的许多方面,包括添加或移除已放置特征(PlacedFeature)、添加或移除生物生成、改变气候,以及调整植被和水的颜色。NeoForge 提供若干默认修改器,涵盖玩家和 Mod 开发者的大多数用途。

推荐阅读章节

应用生物群系修改器

要让 NeoForge 将修改器 JSON 文件加载到游戏中,需要将其放在 Mod 资源或数据包的 data/<modid>/neoforge/biome_modifier/<path>.json 中。加载修改器后,NeoForge 会读取其中的指令,在世界加载时对所有目标生物群系应用指定修改。数据包可以在完全相同的位置使用同名新 JSON 文件,覆盖 Mod 已有的生物群系修改器。

可以按照内置生物群系修改器中的示例手动创建 JSON,也可以按照生成生物群系修改器数据一节进行数据生成。

内置生物群系修改器

这些修改器由 NeoForge 注册,所有人都可使用。

不执行修改(None)

此修改器不执行任何操作。整合包作者和玩家可以在数据包中用以下 JSON 覆盖 Mod 的修改器 JSON,从而禁用它。

JSON

{
    "type": "neoforge:none"
}

数据生成

// Define the ResourceKey for our BiomeModifier.
public static final ResourceKey<BiomeModifier> NO_OP_EXAMPLE = ResourceKey.create(
    NeoForgeRegistries.Keys.BIOME_MODIFIERS, // The registry this key is for
    Identifier.fromNamespaceAndPath(MOD_ID, "no_op_example") // The registry name
);

// BUILDER is a RegistrySetBuilder passed to DatapackBuiltinEntriesProvider
// in a listener for the `GatherDataEvent`s.
BUILDER.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, bootstrap -> {
    // Register the biome modifiers.
    bootstrap.register(NO_OP_EXAMPLE, NoneBiomeModifier.INSTANCE);
});

添加特征(Add Features)

此类型向生物群系添加 PlacedFeature(例如树木或矿石),使它们能在世界生成时出现。修改器接收目标生物群系的 ID 或标签、要添加的 PlacedFeature ID 或标签,以及生成这些特征的 GenerationStep.Decoration。

JSON

{
    "type": "neoforge:add_features",
    // Can either be a biome id, such as "minecraft:plains",
    // or a list of biome ids, such as ["minecraft:plains", "minecraft:badlands", ...],
    // or a biome tag, such as "#c:is_overworld".
    "biomes": "#namespace:your_biome_tag",
    // Can either be a placed feature id, such as "examplemod:add_features_example",
    // or a list of placed feature ids, such as ["examplemod:add_features_example", minecraft:ice_spike", ...],
    // or a placed feature tag, such as "#examplemod:placed_feature_tag".
    "features": "namespace:your_feature",
    // See the GenerationStep.Decoration enum in code for a list of valid enum names.
    // The decoration step section further down also has the list of values for reference.
    "step": "underground_ores"
}

数据生成

// Assume we have some PlacedFeature named EXAMPLE_PLACED_FEATURE.
// Define the ResourceKey for our BiomeModifier.
public static final ResourceKey<BiomeModifier> ADD_FEATURES_EXAMPLE = ResourceKey.create(
    NeoForgeRegistries.Keys.BIOME_MODIFIERS, // The registry this key is for
    Identifier.fromNamespaceAndPath(MOD_ID, "add_features_example") // The registry name
);

// BUILDER is a RegistrySetBuilder passed to DatapackBuiltinEntriesProvider
// in a listener for the `GatherDataEvent`s.
BUILDER.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, bootstrap -> {
    // Lookup any necessary registries.
    // Static registries only need to be looked up if you need to grab the tag data.
    HolderGetter<Biome> biomes = bootstrap.lookup(Registries.BIOME);
    HolderGetter<PlacedFeature> placedFeatures = bootstrap.lookup(Registries.PLACED_FEATURE);

    // Register the biome modifiers.
    bootstrap.register(ADD_FEATURES_EXAMPLE,
        new AddFeaturesBiomeModifier(
            // The biome(s) to generate within
            HolderSet.direct(biomes.getOrThrow(Biomes.PLAINS)),
            // The feature(s) to generate within the biomes
            HolderSet.direct(placedFeatures.getOrThrow(EXAMPLE_PLACED_FEATURE)),
            // The generation step
            GenerationStep.Decoration.LOCAL_MODIFICATIONS
        )
    );
})

警告 向生物群系添加原版 PlacedFeature 时需格外注意:可能出现特征循环冲突,即两个生物群系的特征列表中有相同的两个特征,但在同一 GenerationStep 中排列顺序不同,从而导致崩溃。出于类似原因,不应在多个生物群系修改器中使用同一个 PlacedFeature。

原版 PlacedFeature 可以在生物群系 JSON 中引用,也可以通过修改器添加,但不能两种方式同时使用。如果确实需要这样添加,在自己的命名空间下复制原版 PlacedFeature,是避免这些问题最简单的办法。

移除特征(Remove Features)

此类型从生物群系中移除特征(例如树木或矿石),使它们不再在世界生成时出现。修改器接收目标生物群系的 ID 或标签、要移除的 PlacedFeature ID 或标签,以及从哪些 GenerationStep.Decoration 中移除这些特征。

JSON

{
    "type": "neoforge:remove_features",
    // Can either be a biome id, such as "minecraft:plains",
    // or a list of biome ids, such as ["minecraft:plains", "minecraft:badlands", ...],
    // or a biome tag, such as "#c:is_overworld".
    "biomes": "#namespace:your_biome_tag",
    // Can either be a placed feature id, such as "examplemod:add_features_example",
    // or a list of placed feature ids, such as ["examplemod:add_features_example", "minecraft:ice_spike", ...],
    // or a placed feature tag, such as "#examplemod:placed_feature_tag".
    "features": "namespace:problematic_feature",
    // Optional field specifying a GenerationStep, or a list of GenerationSteps, to remove features from.
    // If omitted, defaults to all GenerationSteps.
    // See the GenerationStep.Decoration enum in code for a list of valid enum names.
    // The decoration step section further down also has the list of values for reference.
    "steps": ["underground_ores", "underground_decoration"]
}

数据生成

// Define the ResourceKey for our BiomeModifier.
public static final ResourceKey<BiomeModifier> REMOVE_FEATURES_EXAMPLE = ResourceKey.create(
    NeoForgeRegistries.Keys.BIOME_MODIFIERS, // The registry this key is for
    Identifier.fromNamespaceAndPath(MOD_ID, "remove_features_example") // The registry name
);

// BUILDER is a RegistrySetBuilder passed to DatapackBuiltinEntriesProvider
// in a listener for the `GatherDataEvent`s.
BUILDER.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, bootstrap -> {
    // Lookup any necessary registries.
    // Static registries only need to be looked up if you need to grab the tag data.
    HolderGetter<Biome> biomes = bootstrap.lookup(Registries.BIOME);
    HolderGetter<PlacedFeature> placedFeatures = bootstrap.lookup(Registries.PLACED_FEATURE);

    // Register the biome modifiers.
    bootstrap.register(REMOVE_FEATURES_EXAMPLE,
        new RemoveFeaturesBiomeModifier(
            // The biome(s) to remove from
            biomes.getOrThrow(Tags.Biomes.IS_OVERWORLD),
            // The feature(s) to remove from the biomes
            HolderSet.direct(placedFeatures.getOrThrow(OrePlacements.ORE_DIAMOND)),
            // The generation steps to remove from
            Set.of(
                GenerationStep.Decoration.LOCAL_MODIFICATIONS,
                GenerationStep.Decoration.UNDERGROUND_ORES
            )
        )
    );
});

添加生成(Add Spawns)

另请参阅生物实体/自然生成。

此类型向生物群系添加实体生成。修改器接收目标生物群系的 ID 或标签,以及要添加的实体 SpawnerData。每个 SpawnerData 包含实体 ID、生成权重,以及一次生成的最小和最大实体数量。

提示 添加新实体时,请确保向 RegisterSpawnPlacementsEvent 注册其生成限制。生成限制用于让实体安全生成在表面或水中。若不注册,实体可能生成在半空,随后跌落死亡。

JSON

{
    "type": "neoforge:add_spawns",
    // Can either be a biome id, such as "minecraft:plains",
    // or a list of biome ids, such as ["minecraft:plains", "minecraft:badlands", ...],
    // or a biome tag, such as "#c:is_overworld".
    "biomes": "#namespace:biome_tag",
    // Can be either a single object or a list of objects.
    "spawners": [
        {
            "type": "namespace:entity_type", // The id of the entity type to spawn
            "weight": 100, // non-negative int, spawn weight
            "minCount": 1, // positive int, minimum group size
            "maxCount": 4 // positive int, maximum group size
        },
        {
            "type": "minecraft:ghast",
            "weight": 1,
            "minCount": 5,
            "maxCount": 10
        }
    ]
}

数据生成

// Assume we have some EntityType<?> named EXAMPLE_ENTITY.
// Define the ResourceKey for our BiomeModifier.
public static final ResourceKey<BiomeModifier> ADD_SPAWNS_EXAMPLE = ResourceKey.create(
    NeoForgeRegistries.Keys.BIOME_MODIFIERS, // The registry this key is for
    Identifier.fromNamespaceAndPath(MOD_ID, "add_spawns_example") // The registry name
);

// BUILDER is a RegistrySetBuilder passed to DatapackBuiltinEntriesProvider
// in a listener for the `GatherDataEvent`s.
BUILDER.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, bootstrap -> {
    // Lookup any necessary registries.
    // Static registries only need to be looked up if you need to grab the tag data.
    HolderGetter<Biome> biomes = bootstrap.lookup(Registries.BIOME);

    // Register the biome modifiers.
    bootstrap.register(ADD_SPAWNS_EXAMPLE,
        new AddSpawnsBiomeModifier(
            // The biome(s) to spawn the mobs within
            HolderSet.direct(biomes.getOrThrow(Biomes.PLAINS)),
            // The spawners of the entities to add
            List.of(
                new SpawnerData(EXAMPLE_ENTITY, 100, 1, 4),
                new SpawnerData(EntityType.GHAST, 1, 5, 10)
            )
        )
    );
});

移除生成(Remove Spawns)

此类型从生物群系移除实体生成。修改器接收目标生物群系的 ID 或标签,以及要移除的实体 EntityType ID 或标签。

JSON

{
    "type": "neoforge:remove_spawns",
    // Can either be a biome id, such as "minecraft:plains",
    // or a list of biome ids, such as ["minecraft:plains", "minecraft:badlands", ...],
    // or a biome tag, such as "#c:is_overworld".
    "biomes": "#namespace:biome_tag",
    // Can either be an entity type id, such as "minecraft:ghast",
    // or a list of entity type ids, such as ["minecraft:ghast", "minecraft:skeleton", ...],
    // or an entity type tag, such as "#minecraft:skeletons".
    "entity_types": "#namespace:entitytype_tag"
}

数据生成

// Define the ResourceKey for our BiomeModifier.
public static final ResourceKey<BiomeModifier> REMOVE_SPAWNS_EXAMPLE = ResourceKey.create(
    NeoForgeRegistries.Keys.BIOME_MODIFIERS, // The registry this key is for
    Identifier.fromNamespaceAndPath(MOD_ID, "remove_spawns_example") // The registry name
);

// BUILDER is a RegistrySetBuilder passed to DatapackBuiltinEntriesProvider
// in a listener for the `GatherDataEvent`s.
BUILDER.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, bootstrap -> {
    // Lookup any necessary registries.
    // Static registries only need to be looked up if you need to grab the tag data.
    HolderGetter<Biome> biomes = bootstrap.lookup(Registries.BIOME);
    HolderGetter<EntityType<?>> entities = bootstrap.lookup(Registries.ENTITY_TYPE);

    // Register the biome modifiers.
    bootstrap.register(REMOVE_SPAWNS_EXAMPLE,
        new RemoveSpawnsBiomeModifier(
            // The biome(s) to remove the spawns from
            biomes.getOrThrow(Tags.Biomes.IS_OVERWORLD),
            // The entities to remove spawns for
            entities.getOrThrow(EntityTypeTags.SKELETONS)
        )
    );
});

添加生成成本(Add Spawn Costs)

此类型为生物群系添加新的生成成本。生成成本是一种较新的机制,可使生物在群系中分散生成、减少聚集。实体会在周围产生 charge,并与其他实体的 charge 叠加。生成新实体时,算法会寻找这样一个位置:该处总 charge 场乘以待生成实体的 charge 值,小于该实体的 energy_budget。这是较高级的生物生成方式,建议参考最常使用此系统的灵魂沙峡谷生物群系,借鉴现有数值。

修改器接收目标生物群系的 ID 或标签、要添加生成成本的实体类型 EntityType ID 或标签,以及实体的 MobSpawnSettings.MobSpawnCost。MobSpawnCost 包含能量预算,结合每个已生成实体提供的 charge,决定某位置可生成的最大实体数量。

提示 添加新实体时,请确保向 RegisterSpawnPlacementsEvent 注册其生成限制。

JSON

{
    "type": "neoforge:add_spawn_costs",
    // Can either be a biome id, such as "minecraft:plains",
    // or a list of biome ids, such as ["minecraft:plains", "minecraft:badlands", ...],
    // or a biome tag, such as "#c:is_overworld".
    "biomes": "#namespace:biome_tag",
    // Can either be an entity type id, such as "minecraft:ghast",
    // or a list of entity type ids, such as ["minecraft:ghast", "minecraft:skeleton", ...],
    // or an entity type tag, such as "#minecraft:skeletons".
    "entity_types": "#minecraft:skeletons",
    "spawn_cost": {
        // The energy budget
        "energy_budget": 1.0,
        // The amount of charge each entity takes up from the budget
        "charge": 0.1
    }
}

数据生成

// Define the ResourceKey for our BiomeModifier.
public static final ResourceKey<BiomeModifier> ADD_SPAWN_COSTS_EXAMPLE = ResourceKey.create(
    NeoForgeRegistries.Keys.BIOME_MODIFIERS, // The registry this key is for
    Identifier.fromNamespaceAndPath(MOD_ID, "add_spawn_costs_example") // The registry name
);

// BUILDER is a RegistrySetBuilder passed to DatapackBuiltinEntriesProvider
// in a listener for the `GatherDataEvent`s.
BUILDER.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, bootstrap -> {
    // Lookup any necessary registries.
    // Static registries only need to be looked up if you need to grab the tag data.
    HolderGetter<Biome> biomes = bootstrap.lookup(Registries.BIOME);
    HolderGetter<EntityType<?>> entities = bootstrap.lookup(Registries.ENTITY_TYPE);

    // Register the biome modifiers.
    bootstrap.register(ADD_SPAWN_COSTS_EXAMPLE,
        new AddSpawnCostsBiomeModifier(
            // The biome(s) to add the spawn costs to
            biomes.getOrThrow(Tags.Biomes.IS_OVERWORLD),
            // The entities to add the spawn costs for
            entities.getOrThrow(EntityTypeTags.SKELETONS),
            new MobSpawnSettings.MobSpawnCost(
                1.0, // The energy budget
                0.1  // The amount of charge each entity takes up from the budget
            )
        )
    );
});

移除生成成本(Remove Spawn Costs)

此类型从生物群系移除生成成本。生成成本用于让生物分散生成、减少聚集。修改器接收目标生物群系的 ID 或标签,以及要移除生成成本的实体 EntityType ID 或标签。

JSON

{
    "type": "neoforge:remove_spawn_costs",
    // Can either be a biome id, such as "minecraft:plains",
    // or a list of biome ids, such as ["minecraft:plains", "minecraft:badlands", ...],
    // or a biome tag, such as "#c:is_overworld".
    "biomes": "#namespace:biome_tag",
    // Can either be an entity type id, such as "minecraft:ghast",
    // or a list of entity type ids, such as ["minecraft:ghast", "minecraft:skeleton", ...],
    // or an entity type tag, such as "#minecraft:skeletons".
    "entity_types": "#minecraft:skeletons"
}

数据生成

// Define the ResourceKey for our BiomeModifier.
public static final ResourceKey<BiomeModifier> REMOVE_SPAWN_COSTS_EXAMPLE = ResourceKey.create(
    NeoForgeRegistries.Keys.BIOME_MODIFIERS, // The registry this key is for
    Identifier.fromNamespaceAndPath(MOD_ID, "remove_spawn_costs_example") // The registry name
);

// BUILDER is a RegistrySetBuilder passed to DatapackBuiltinEntriesProvider
// in a listener for the `GatherDataEvent`s.
BUILDER.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, bootstrap -> {
    // Lookup any necessary registries.
    // Static registries only need to be looked up if you need to grab the tag data.
    HolderGetter<Biome> biomes = bootstrap.lookup(Registries.BIOME);
    HolderGetter<EntityType<?>> entities = bootstrap.lookup(Registries.ENTITY_TYPE);

    // Register the biome modifiers.
    bootstrap.register(REMOVE_SPAWN_COSTS_EXAMPLE,
        new RemoveSpawnCostsBiomeModifier(
            // The biome(s) to remove the spawn costs from
            biomes.getOrThrow(Tags.Biomes.IS_OVERWORLD),
            // The entities to remove spawn costs for
            entities.getOrThrow(EntityTypeTags.SKELETONS)
        )
    );
});

添加旧式雕刻器(Add Legacy Carvers)

此类型向生物群系添加由雕刻器生成的洞穴和峡谷,也就是洞穴与山崖更新前使用的洞穴生成方式。它不能添加噪声洞穴,因为噪声洞穴属于特定的基于噪声的区块生成器系统,实际上不与生物群系关联。

JSON

    {
    "type": "neoforge:add_carvers",
    // Can either be a biome id, such as "minecraft:plains",
    // or a list of biome ids, such as ["minecraft:plains", "minecraft:badlands", ...],
    // or a biome tag, such as "#c:is_overworld".
    "biomes": "minecraft:plains",
    // Can either be a carver id, such as "examplemod:add_carvers_example",
    // or a list of carver ids, such as ["examplemod:add_carvers_example", "minecraft:canyon", ...],
    // or a carver tag, such as "#examplemod:configured_carver_tag".
    "carvers": "examplemod:add_carvers_example"
}

数据生成

// Assume we have some ConfiguredWorldCarver named EXAMPLE_CARVER.
// Define the ResourceKey for our BiomeModifier.
public static final ResourceKey<BiomeModifier> ADD_CARVERS_EXAMPLE = ResourceKey.create(
    NeoForgeRegistries.Keys.BIOME_MODIFIERS, // The registry this key is for
    Identifier.fromNamespaceAndPath(MOD_ID, "add_carvers_example") // The registry name
);

// BUILDER is a RegistrySetBuilder passed to DatapackBuiltinEntriesProvider
// in a listener for the `GatherDataEvent`s.
BUILDER.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, bootstrap -> {
    // Lookup any necessary registries.
    // Static registries only need to be looked up if you need to grab the tag data.
    HolderGetter<Biome> biomes = bootstrap.lookup(Registries.BIOME);
    HolderGetter<ConfiguredWorldCarver<?>> carvers = bootstrap.lookup(Registries.CONFIGURED_CARVER);

    // Register the biome modifiers.
    bootstrap.register(ADD_CARVERS_EXAMPLE,
        new AddCarversBiomeModifier(
            // The biome(s) to generate within
            HolderSet.direct(biomes.getOrThrow(Biomes.PLAINS)),
            // The carver(s) to generate within the biomes
            HolderSet.direct(carvers.getOrThrow(EXAMPLE_CARVER))
        )
    );
});

移除旧式雕刻器(Removing Legacy Carvers)

此类型从生物群系移除由雕刻器生成的洞穴和峡谷。它不能移除噪声洞穴,因为噪声洞穴被固化在维度噪声设置系统中,实际上不与生物群系关联。

JSON

{
    "type": "neoforge:remove_carvers",
    // Can either be a biome id, such as "minecraft:plains",
    // or a list of biome ids, such as ["minecraft:plains", "minecraft:badlands", ...],
    // or a biome tag, such as "#c:is_overworld".
    "biomes": "minecraft:plains",
    // Can either be a carver id, such as "examplemod:add_carvers_example",
    // or a list of carver ids, such as ["examplemod:add_carvers_example", "minecraft:canyon", ...],
    // or a carver tag, such as "#examplemod:configured_carver_tag".
    "carvers": "examplemod:add_carvers_example"
}

数据生成

// Define the ResourceKey for our BiomeModifier.
public static final ResourceKey<BiomeModifier> REMOVE_CARVERS_EXAMPLE = ResourceKey.create(
    NeoForgeRegistries.Keys.BIOME_MODIFIERS, // The registry this key is for
    Identifier.fromNamespaceAndPath(MOD_ID, "remove_carvers_example") // The registry name
);

// BUILDER is a RegistrySetBuilder passed to DatapackBuiltinEntriesProvider
// in a listener for the `GatherDataEvent`s.
BUILDER.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, bootstrap -> {
    // Lookup any necessary registries.
    // Static registries only need to be looked up if you need to grab the tag data.
    HolderGetter<Biome> biomes = bootstrap.lookup(Registries.BIOME);
    HolderGetter<ConfiguredWorldCarver<?>> carvers = bootstrap.lookup(Registries.CONFIGURED_CARVER);

    // Register the biome modifiers.
    bootstrap.register(REMOVE_CARVERS_EXAMPLE,
        new AddFeaturesBiomeModifier(
            // The biome(s) to remove from
            biomes.getOrThrow(Tags.Biomes.IS_OVERWORLD),
            // The carver(s) to remove from the biomes
            HolderSet.direct(carvers.getOrThrow(Carvers.CAVE))
        )
    );
});

装饰步骤的可用值

前述许多 JSON 的 step 或 steps 字段指的是 GenerationStep.Decoration 枚举。其步骤顺序如下,也就是游戏在世界生成时使用的顺序。请尽量将特征放在最合适的步骤中。

步骤 描述
raw_generation 最先运行,用于小型末地岛等特殊地形特征。
lakes 专门生成熔岩湖等池塘状特征。
local_modifications 修改地形,例如晶洞、冰山、巨石或滴水石。
underground_structures 地下小型结构状特征,例如地牢或化石。
surface_structures 仅位于地表的小型结构状特征,例如沙漠水井。
strongholds 专用于要塞结构;未经修改的 Minecraft 不在这里添加特征。
underground_ores 添加所有矿石和矿脉,包括金矿石、泥土、花岗岩等。
underground_decoration 通常用于洞穴装饰,例如滴水石簇和幽匿脉络。
fluid_springs 小型熔岩瀑布和水瀑布来自此阶段的特征。
vegetal_decoration 几乎所有植物,包括花、树木、藤蔓等。
top_layer_modification 最后运行,用于在寒冷生物群系表面放置雪和冰。

创建自定义生物群系修改器

实现 BiomeModifier

从底层实现看,生物群系修改器包含三个部分:

  • 通过数据包注册表注册的 BiomeModifier,用于修改生物群系构建器。
  • 静态注册的 MapCodec,用于编解码修改器。
  • 构造 BiomeModifier 的 JSON,以已注册 MapCodec 的 ID 作为可索引类型。

BiomeModifier 包含 #modify 与 #codec 两个方法。modify 接收当前 Biome 的 Holder、当前 BiomeModifier.Phase,以及待修改生物群系的构建器。每个修改器会在每个 Phase 调用一次,借此组织各类修改的执行时间:

阶段 描述
BEFORE_EVERYTHING 所有标准阶段前必须运行的操作。
ADD 添加特征、生物生成等。
REMOVE 移除特征、生物生成等。
MODIFY 修改单个值,例如气候和颜色。
AFTER_EVERYTHING 所有标准阶段后必须运行的操作。

所有 BiomeModifier 都包含 type 键,引用该修改器所用 MapCodec 的 ID。codec 提供用于编解码修改器的 MapCodec。这个 MapCodec 需要静态注册,其 ID 用作修改器的 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 == /* Pick the phase that best matches what your want to modify */) {
            // Modify the 'builder', checking any information about the biome itself
        }
    }

    @Override
    public MapCodec<? extends BiomeModifier> codec() {
        return EXAMPLE_BIOME_MODIFIER.get();
    }
}

// In some registration class
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)
    ));

生成生物群系修改器数据

将 RegistrySetBuilder 传给 DatapackBuiltinEntriesProvider,即可通过数据生成创建 BiomeModifier JSON。生成文件位于 data/<modid>/neoforge/biome_modifier/<path>.json。

关于 RegistrySetBuilder 和 DatapackBuiltinEntriesProvider 的工作方式,请参阅数据包注册表的数据生成。

// Define the ResourceKey for our BiomeModifier.
public static final ResourceKey<BiomeModifier> EXAMPLE_MODIFIER = ResourceKey.create(
    NeoForgeRegistries.Keys.BIOME_MODIFIERS, // The registry this key is for
    Identifier.fromNamespaceAndPath(MOD_ID, "example_modifier") // The registry name
);

// BUILDER is a RegistrySetBuilder passed to DatapackBuiltinEntriesProvider
// in a listener for the `GatherDataEvent`s.
BUILDER.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, bootstrap -> {
    // Lookup any necessary registries.
    // Static registries only need to be looked up if you need to grab the tag data.
    HolderGetter<Biome> biomes = bootstrap.lookup(Registries.BIOME);

    // Register the biome modifiers.
    bootstrap.register(EXAMPLE_MODIFIER,
        new ExampleBiomeModifier(
            biomes.getOrThrow(Tags.Biomes.IS_OVERWORLD),
            20
        )
    );
});

这样会生成以下 JSON:

// In data/examplemod/neoforge/biome_modifier/example_modifier.json
{
    // The registry key of the MapCodec for the modifier
    "type": "examplemod:example_biome_modifier",
    // All additional settings are applied to the root object
    "biomes": "#c:is_overworld",
    "value": 20
}

面向可能不存在的生物群系

有时修改器需要面向并非总在游戏中存在的生物群系。直接引用未注册的群系,会在加载世界时崩溃。解决办法是创建生物群系标签,并把目标群系作为可选条目加入标签:将该条目的 required 设为 false。例如:

{
    "replace": false,
    "values": [
        {
            "id": "minecraft:pale_garden",
            "required": false
        }
    ]
}

修改器使用此标签后,即使群系没有注册也不会崩溃。原文举了苍白之园的历史情形:在 1.21.3 中,只有启用 Winter Drop 数据包才会创建该群系;否则它根本不会出现在群系注册表中。另一个用途是引用其他 Mod 的群系,同时在未安装这些 Mod 时仍能正常工作。

通过数据生成添加生物群系标签可选条目时,代码大致如下:

// In a KeyTagProvider<Biome> subclass
// Assume we have some example TagKey<Biome> OPTIONAL_BIOMES_TAG
@Override
protected void addTags(HolderLookup.Provider registries) {
    this.tag(OPTIONAL_BIOMES_TAG)
        // Must be a ResourceKey<Biome>
        .addOptional(Biomes.PALE_GARDEN);
}

来源: NeoForged/Documentation(26.1)。中文翻译依据提交 4a680cdd4d15。