Path: blob/1.20.x/src/main/java/net/minecraftforge/common/world/ForgeBiomeModifiers.java
15849 views
/*1* Copyright (c) Forge Development LLC and contributors2* SPDX-License-Identifier: LGPL-2.1-only3*/45package net.minecraftforge.common.world;67import java.util.EnumSet;8import java.util.List;9import java.util.Set;10import java.util.function.Function;1112import com.mojang.datafixers.util.Either;13import com.mojang.serialization.Codec;14import com.mojang.serialization.MapCodec;15import com.mojang.serialization.codecs.RecordCodecBuilder;1617import net.minecraft.core.Holder;18import net.minecraft.core.HolderSet;19import net.minecraft.core.RegistryCodecs;20import net.minecraft.world.entity.EntityType;21import net.minecraft.world.entity.MobCategory;22import net.minecraft.world.level.biome.Biome;23import net.minecraft.world.level.biome.MobSpawnSettings.SpawnerData;24import net.minecraft.world.level.levelgen.GenerationStep.Decoration;25import net.minecraft.world.level.levelgen.placement.PlacedFeature;26import net.minecraftforge.common.world.ModifiableBiomeInfo.BiomeInfo.Builder;27import net.minecraftforge.registries.ForgeRegistries;2829public final class ForgeBiomeModifiers {30private ForgeBiomeModifiers() {} // Utility class.3132/**33* <p>Stock biome modifier that adds features to biomes. Has the following json format:</p>34* <pre>35* {36* "type": "forge:add_features", // required37* "biomes": "#namespace:your_biome_tag" // accepts a biome id, [list of biome ids], or #namespace:biome_tag38* "features": "namespace:your_feature", // accepts a placed feature id, [list of placed feature ids], or #namespace:feature_tag39* "step": "underground_ores" // accepts a Decoration enum name40* }41* </pre>42* <p>Be wary of using this to add vanilla PlacedFeatures to biomes, as doing so may cause a feature cycle violation.</p>43*44* @param biomes Biomes to add features to.45* @param features PlacedFeatures to add to biomes.46* @param step Decoration step to run features in.47*/48public static record AddFeaturesBiomeModifier(HolderSet<Biome> biomes, HolderSet<PlacedFeature> features, Decoration step) implements BiomeModifier {49public static final MapCodec<AddFeaturesBiomeModifier> CODEC = RecordCodecBuilder.mapCodec(builder -> builder.group(50Biome.LIST_CODEC.fieldOf("biomes").forGetter(AddFeaturesBiomeModifier::biomes),51PlacedFeature.LIST_CODEC.fieldOf("features").forGetter(AddFeaturesBiomeModifier::features),52Decoration.CODEC.fieldOf("step").forGetter(AddFeaturesBiomeModifier::step)53).apply(builder, AddFeaturesBiomeModifier::new));5455@Override56public void modify(Holder<Biome> biome, Phase phase, Builder builder) {57if (phase == Phase.ADD && this.biomes.contains(biome)) {58BiomeGenerationSettingsBuilder generationSettings = builder.getGenerationSettings();59this.features.forEach(holder -> generationSettings.addFeature(this.step, holder));60}61}6263@Override64public MapCodec<? extends BiomeModifier> codec() {65return CODEC;66}67}6869/**70* <p>Stock biome modifier that removes features from biomes. Has the following json format:</p>71* <pre>72* {73* "type": "forge:removefeatures", // required74* "biomes": "#namespace:your_biome_tag", // accepts a biome id, [list of biome ids], or #namespace:biome_tag75* "features": "namespace:your_feature", // accepts a placed feature id, [list of placed feature ids], or #namespace:feature_tag76* "steps": "underground_ores" OR ["underground_ores", "vegetal_decoration"] // one or more decoration steps; optional field, defaults to all steps if not specified77* }78* </pre>79*80* @param biomes Biomes to remove features from.81* @param features PlacedFeatures to remove from biomes.82* @param steps Decoration steps to remove features from.83*/84public static record RemoveFeaturesBiomeModifier(HolderSet<Biome> biomes, HolderSet<PlacedFeature> features, Set<Decoration> steps) implements BiomeModifier {85public static final MapCodec<RemoveFeaturesBiomeModifier> CODEC = RecordCodecBuilder.mapCodec(builder ->86builder.group(87Biome.LIST_CODEC.fieldOf("biomes").forGetter(RemoveFeaturesBiomeModifier::biomes),88PlacedFeature.LIST_CODEC.fieldOf("features").forGetter(RemoveFeaturesBiomeModifier::features),89Codec.either(Decoration.CODEC.listOf(), Decoration.CODEC).<Set<Decoration>>xmap(90either -> either.map(Set::copyOf, Set::of), // convert list/singleton to set when decoding91set -> set.size() == 1 ? Either.right(set.toArray(Decoration[]::new)[0]) : Either.left(List.copyOf(set))92).optionalFieldOf("steps", EnumSet.allOf(Decoration.class)).forGetter(RemoveFeaturesBiomeModifier::steps)93).apply(builder, RemoveFeaturesBiomeModifier::new));9495/**96* Creates a modifier that removes the given features from all decoration steps in the given biomes.97* @param biomes Biomes to remove features from.98* @param features PlacedFeatures to remove from biomes.99*/100public static RemoveFeaturesBiomeModifier allSteps(HolderSet<Biome> biomes, HolderSet<PlacedFeature> features) {101return new RemoveFeaturesBiomeModifier(biomes, features, EnumSet.allOf(Decoration.class));102}103104@Override105public void modify(Holder<Biome> biome, Phase phase, Builder builder) {106if (phase == Phase.REMOVE && this.biomes.contains(biome)) {107BiomeGenerationSettingsBuilder generationSettings = builder.getGenerationSettings();108for (Decoration step : this.steps)109generationSettings.getFeatures(step).removeIf(this.features::contains);110}111}112113@Override114public MapCodec<? extends BiomeModifier> codec() {115return CODEC;116}117}118119/**120* <p>Stock biome modifier that adds a mob spawn to a biome. Has the following json format:</p>121* <pre>122* {123* "type": "forge:add_spawns", // Required124* "biomes": "#namespace:biome_tag", // Accepts a biome id, [list of biome ids], or #namespace:biome_tag125* "spawners":126* {127* "type": "namespace:entity_type", // Type of mob to spawn128* "weight": 100, // int, spawn weighting129* "minCount": 1, // int, minimum pack size130* "maxCount": 4, // int, maximum pack size131* }132* }133* </pre>134* <p>Optionally accepts a list of spawner objects instead of a single spawner:</p>135* <pre>136* {137* "type": "forge:add_spawns", // Required138* "biomes": "#namespace:biome_tag", // Accepts a biome id, [list of biome ids], or #namespace:biome_tag139* "spawners":140* [141* {142* "type": "namespace:entity_type", // Type of mob to spawn143* "weight": 100, // int, spawn weighting144* "minCount": 1, // int, minimum pack size145* "maxCount": 4, // int, maximum pack size146* },147* {148* // additional spawner object149* }150* ]151* }152* </pre>153*154* @param biomes Biomes to add mob spawns to.155* @param spawners List of SpawnerDatas specifying EntityType, weight, and pack size.156*/157public record AddSpawnsBiomeModifier(HolderSet<Biome> biomes, List<SpawnerData> spawners) implements BiomeModifier {158public static final MapCodec<AddSpawnsBiomeModifier> CODEC = RecordCodecBuilder.mapCodec(builder ->159builder.group(160Biome.LIST_CODEC.fieldOf("biomes").forGetter(AddSpawnsBiomeModifier::biomes),161// Allow either a list or single spawner, attempting to decode the list format first.162Codec.either(SpawnerData.CODEC.listOf(), SpawnerData.CODEC).xmap(163either -> either.map(Function.identity(), List::of), // convert list/singleton to list when decoding164list -> list.size() == 1 ? Either.right(list.get(0)) : Either.left(list) // convert list to singleton/list when encoding165).fieldOf("spawners").forGetter(AddSpawnsBiomeModifier::spawners)166).apply(builder, AddSpawnsBiomeModifier::new));167168/**169* Convenience method for using a single spawn data.170* @param biomes Biomes to add mob spawns to.171* @param spawner SpawnerData specifying EntityTYpe, weight, and pack size.172* @return AddSpawnsBiomeModifier that adds a single spawn entry to the specified biomes.173*/174public static AddSpawnsBiomeModifier singleSpawn(HolderSet<Biome> biomes, SpawnerData spawner) {175return new AddSpawnsBiomeModifier(biomes, List.of(spawner));176}177178@Override179public void modify(Holder<Biome> biome, Phase phase, Builder builder) {180if (phase == Phase.ADD && this.biomes.contains(biome)) {181var spawns = builder.getMobSpawnSettings();182for (var spawner : this.spawners)183spawns.addSpawn(spawner.type.getCategory(), spawner);184}185}186187@Override188public MapCodec<? extends BiomeModifier> codec() {189return CODEC;190}191}192193/**194* <p>Stock biome modifier that removes mob spawns from a biome. Has the following json format:</p>195* <pre>196* {197* "type": "forge:add_spawns", // Required198* "biomes": "#namespace:biome_tag", // Accepts a biome id, [list of biome ids], or #namespace:biome_tag199* "entity_types": #namespace:entitytype_tag // Accepts an entity type, [list of entity types], or #namespace:entitytype_tag200* }201* </pre>202*203* @param biomes Biomes to add mob spawns to.204* @param entityTypes EntityTypes to remove from spawn lists.205*/206public record RemoveSpawnsBiomeModifier(HolderSet<Biome> biomes, HolderSet<EntityType<?>> entityTypes) implements BiomeModifier {207public static final MapCodec<RemoveSpawnsBiomeModifier> CODEC = RecordCodecBuilder.mapCodec(builder ->208builder.group(209Biome.LIST_CODEC.fieldOf("biomes").forGetter(RemoveSpawnsBiomeModifier::biomes),210RegistryCodecs.homogeneousList(ForgeRegistries.Keys.ENTITY_TYPES).fieldOf("entity_types").forGetter(RemoveSpawnsBiomeModifier::entityTypes)211).apply(builder, RemoveSpawnsBiomeModifier::new));212213@Override214public void modify(Holder<Biome> biome, Phase phase, Builder builder) {215if (phase == Phase.REMOVE && this.biomes.contains(biome)) {216var spawns = builder.getMobSpawnSettings();217for (var category : MobCategory.values())218spawns.getSpawner(category).removeIf(data -> this.entityTypes.contains(ForgeRegistries.ENTITY_TYPES.getHolder(data.type).get()));219}220}221222@Override223public MapCodec<? extends BiomeModifier> codec() {224return CODEC;225}226}227}228229230