Skip to main content
Version: 1.21.x

Biome Modifiers

Biome Modifiers are a data-driven system that allows for changing many aspects of a biome, including the ability to inject or remove PlacedFeatures, add or remove mob spawns, change the climate, and adjust foliage and water color. NeoForge provides several default biome modifiers that cover the majority of use cases for both players and modders.

Applying Biome Modifiers

To have NeoForge load a biome modifier JSON file into the game, the file will need to be under data/<modid>/neoforge/biome_modifier/<path>.json folder in the mod's resources, or in a Datapack. Then, once NeoForge loads the biome modifier, it will read its instructions and apply the described modifications to all target biomes when the world is loaded up. Pre-existing biome modifiers from mods can be overridden by datapacks having a new JSON file at the exact same location and name.

The JSON file can be created by hand following the examples in the 'Built-in NeoForge Biome Modifiers' section or be datagenned as shown in the 'Datagenning Biome Modifiers' section.

Built-in Biome Modifiers

These biome modifiers are registered by NeoForge for anyone to use.

None

This biome modifier has no operation and will do no modification. Pack makers and players can use this in a datapack to disable mods' biome modifiers by overriding their biome modifier JSONs with the JSON below.

{
"type": "neoforge:none"
}

Add Features

This biome modifier type adds PlacedFeatures (such as trees or ores) to biomes so that they can spawn during world generation. The modifier takes in the biome id or tag of the biomes the features are added to, a PlacedFeature id or tag to add to the selected biomes, and the GenerationStep.Decoration the features will be generated within.

{
"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"
}
warning

Care should be taken when adding vanilla PlacedFeatures to biomes, as doing so may cause what is known as a feature cycle violation (two biomes having the same two features in their feature lists, but in different orders within the same GenerationStep), leading to a crash. For similar reasons, you should not use the same PlacedFeature in more than one biome modifier.

Vanilla PlacedFeatures can be referenced in biome JSONs or added via biome modifiers, but should not be used in both. If you still need to add them this way, making a copy of the vanilla PlacedFeature under your own namespace is the easiest solution to avoid these problems.

Remove Features

This biome modifier type removes features (such as trees or ores) from biomes so that they will no longer spawn during world generation. The modifier takes in the biome id or tag of the biomes the features are removed from, a PlacedFeature id or tag to remove from the selected biomes, and the GenerationStep.Decorations that the features will be removed from.

{
"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"]
}

Add Spawns

This biome modifier type adds entity spawns to biomes. The modifier takes in the biome id or tag of the biomes the entity spawns are added to, and the SpawnerData of the entities to add. Each SpawnerData contains the entity id, the spawn weight, and the minimum/maximum number of entities to spawn at a given time.

note

If you are a modder adding a new entity, make sure the entity has a spawn restriction registered to RegisterSpawnPlacementsEvent. Spawn restrictions are used to make entities spawn on surfaces or in water safely. If you do not register a spawn restriction, your entity could spawn in mid-air, fall and die.

{
"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
}
]
}

Remove Spawns

This biome modifier type removes entity spawns from biomes. The modifier takes in the biome id or tag of the biomes the entity spawns are removed from, and the EntityType id or tag of the entities to remove.

{
"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"
}

Add Spawn Costs

Allows for adding new spawn costs to biomes. Spawn costs are a newer way of making mobs spawn spread out in a biome to reduce clustering. It works by having the entities give off a charge that surrounds them and adds up with other entities' charge. When spawning a new entity, the spawning algorithm looks for a spot where the total charge field at the location multiplied by the spawning entity's charge value is less than the spawning entity's energy_budget. This is an advanced way of spawning mobs, so it is a good idea to reference the Soul Sand Valley biome (which is the most prominent user of this system) for existing values to borrow.

The modifier takes in the biome id or tag of the biomes the spawn costs are added to, the EntityType id or tag of the entity types to add spawn costs for, and the MobSpawnSettings.MobSpawnCost of the entity. The MobSpawnCost contains the energy budget, which indicates the maximum number of entities that can spawn in a location based on the charge provided for each entity spawned.

note

If you are a modder adding a new entity, make sure the entity has a spawn restriction registered to RegisterSpawnPlacementsEvent.

{
"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
}
}

Remove Spawn Costs

Allows for removing a spawn cost from a biome. Spawn costs are a newer way of making mobs spawn spread out in a biome to reduce clustering. The modifier takes in the biome id or tag of the biomes the spawn costs are removed from, and the EntityType id or tag of the entities to remove the spawn cost for.

{
"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"
}

Add Legacy Carvers

This biome modifier type allows adding carver caves and ravines to biomes. These are what was used for cave generation before the Caves and Cliffs update. It CANNOT add noise caves to biomes, because noise caves are a part of certain noise-based chunk generator systems and not actually tied to biomes.

    {
"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"
}

Removing Legacy Carvers

This biome modifier type allows removing carver caves and ravines from biomes. These are what was used for cave generation before the Caves and Cliffs update. It CANNOT remove noise caves from biomes, because noise caves are baked into the dimension's noise settings system and not actually tied to biomes.

{
"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"
}

Available Values for Decoration Steps

The step or steps fields in many of the aforementioned JSONs are referring to the GenerationStep.Decoration enum. This enum has the steps listed out in the following order, which is the same order that the game uses for generating during worldgen. Try to put features in the step that makes the most sense for them.

StepDescription
raw_generationFirst to run. This is used for special terrain-like features such as Small End Islands.
lakesDedicated to spawning pond-like feature such as Lava Lakes.
local_modificationsFor modifications to terrain such as Geodes, Icebergs, Boulders, or Dripstone.
underground_structuresUsed for small underground structure-like features such as Dungeons or Fossils.
surface_structuresFor small surface only structure-like features such as Desert Wells.
strongholdsDedicated for Stronghold structures. No feature is added here in unmodified Minecraft.
underground_oresThe step for all Ores and Veins to be added to. This includes Gold, Dirt, Granite, etc.
underground_decorationUsed typically for decorating caves. Dripstone Cluster and Sculk Vein are here.
fluid_springsThe small Lavafalls and Waterfalls come from features in this stage.
vegetal_decorationNearly all plants (flowers, trees, vines, and more) are added to this stage.
top_layer_modificationLast to run. Used for placing Snow and Ice on the surface of cold biomes.

Creating Custom Biome Modifiers

The BiomeModifier Implementation

Under the hood, Biome Modifiers are made up of three parts:

  • The datapack registered BiomeModifier used to modify the biome builder.
  • The statically registered MapCodec that encodes and decodes the modifiers.
  • The JSON that constructs the BiomeModifier, using the registered id of the MapCodec as the indexable type.

A BiomeModifier contains two methods: #modify and #codec. modify takes in a Holder of the current Biome, the current BiomeModifier.Phase, and the builder of the biome to modify. Every BiomeModifier is called once per Phase to organize when certain modifications to the biome should occur:

PhaseDescription
BEFORE_EVERYTHINGA catch-all for everything that needs to run before the standard phases.
ADDAdding features, mob spawns, etc.
REMOVERemoving features, mob spawns, etc.
MODIFYModifying single values (e.g., climate, colors).
AFTER_EVERYTHINGA catch-all for everything that needs to run after the standard phases.

All BiomeModifiers contain a type key that references the id of the MapCodec used for the BiomeModifier. The codec takes in the MapCodec that encodes and decodes the modifiers. This MapCodec is statically registered, with its id used as the type of the BiomeModifier.

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.value();
}
}

// 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 Holder<MapCodec<? extends BiomeModifier>> 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)
));

Datagenning Biome Modifiers

A BiomeModifier JSON can be created through data generation by passing a RegistrySetBuilder to DatapackBuiltinEntriesProvider. The JSON will be placed at data/<modid>/neoforge/biome_modifier/<path>.json.

For more information on how RegistrySetBuilder and DatapackBuiltinEntriesProvider work, please see the article on Data Generation for Datapack Registries.

// 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
ResourceLocation.fromNamespaceAndPath(MOD_ID, "example_modifier") // The registry name
);

// BUILDER is a RegistrySetBuilder passed to DatapackBuiltinEntriesProvider
// in a listener for GatherDataEvent.
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
)
);
});

This will then result in the following JSON being created:

// In data/examplemod/neoforge/biome_modifier/example_modifier.json
{
// The registy 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
}

Targeting Biomes That May Not Exist

There may be times when a biome modifier needs to target a biome that is not always present in the game. If a biome modifier targets the unregistered biome directly, it will crash on world loading. The way to work around this is to create a biome tag and add the target biome as an optional tag entry by setting required to false for the entry. An example is below:

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

Using that biome tag for a biome modifier will now not crash if the biome is not registered. A use case for this is the Pale Garden biome in 1.21.3. That biome is only created when you turn on the Winter Drop datapack in-game. Otherwise, the biome does not exist in the biome registry at all. Another use case can be to target modded biomes while still functioning when the mods adding these biomes are not present.

To datagen optional entries for biome tags, the datagen code would look something along these lines:

// In a TagsProvider<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 ResourceLocation representing the registry object
.addOptional(WinterDropBiomes.PALE_GARDEN.location());
}