diff --git a/src/generated_test/resources/data/biome_modifiers_test/forge/biome_modifier/add_basalt.json b/src/generated_test/resources/data/biome_modifiers_test/forge/biome_modifier/add_basalt.json new file mode 100644 index 0000000000..4d1a1f99e2 --- /dev/null +++ b/src/generated_test/resources/data/biome_modifiers_test/forge/biome_modifier/add_basalt.json @@ -0,0 +1,6 @@ +{ + "type": "forge:add_features", + "biomes": "#minecraft:is_badlands", + "features": "biome_modifiers_test:large_basalt_columns", + "step": "top_layer_modification" +} \ No newline at end of file diff --git a/src/generated_test/resources/data/biome_modifiers_test/forge/biome_modifier/add_magma_cubes.json b/src/generated_test/resources/data/biome_modifiers_test/forge/biome_modifier/add_magma_cubes.json new file mode 100644 index 0000000000..2f6f0a039e --- /dev/null +++ b/src/generated_test/resources/data/biome_modifiers_test/forge/biome_modifier/add_magma_cubes.json @@ -0,0 +1,10 @@ +{ + "type": "forge:add_spawns", + "biomes": "#minecraft:is_badlands", + "spawners": { + "type": "minecraft:magma_cube", + "maxCount": 4, + "minCount": 1, + "weight": 100 + } +} \ No newline at end of file diff --git a/src/generated_test/resources/data/biome_modifiers_test/forge/biome_modifier/modify_badlands.json b/src/generated_test/resources/data/biome_modifiers_test/forge/biome_modifier/modify_badlands.json index 18110a46d3..1b6b2c2d9e 100644 --- a/src/generated_test/resources/data/biome_modifiers_test/forge/biome_modifier/modify_badlands.json +++ b/src/generated_test/resources/data/biome_modifiers_test/forge/biome_modifier/modify_badlands.json @@ -1,14 +1,6 @@ { - "type": "biome_modifiers_test:test", + "type": "biome_modifiers_test:modify_biomes", "biomes": "#minecraft:is_badlands", - "features": "biome_modifiers_test:large_basalt_columns", - "generation_stage": "TOP_LAYER_MODIFICATION", "precipitation": "snow", - "spawn": { - "type": "minecraft:magma_cube", - "maxCount": 4, - "minCount": 1, - "weight": 100 - }, "water_color": 16711680 } \ No newline at end of file diff --git a/src/generated_test/resources/data/biome_modifiers_test/forge/biome_modifier/remove_forest_skeletons.json b/src/generated_test/resources/data/biome_modifiers_test/forge/biome_modifier/remove_forest_skeletons.json new file mode 100644 index 0000000000..5262cccec0 --- /dev/null +++ b/src/generated_test/resources/data/biome_modifiers_test/forge/biome_modifier/remove_forest_skeletons.json @@ -0,0 +1,5 @@ +{ + "type": "forge:remove_spawns", + "biomes": "#minecraft:is_forest", + "entity_types": "#minecraft:skeletons" +} \ No newline at end of file diff --git a/src/generated_test/resources/data/biome_modifiers_test/forge/biome_modifier/remove_forest_trees.json b/src/generated_test/resources/data/biome_modifiers_test/forge/biome_modifier/remove_forest_trees.json new file mode 100644 index 0000000000..7dd978a205 --- /dev/null +++ b/src/generated_test/resources/data/biome_modifiers_test/forge/biome_modifier/remove_forest_trees.json @@ -0,0 +1,5 @@ +{ + "type": "forge:remove_features", + "biomes": "#minecraft:is_forest", + "features": "minecraft:trees_birch_and_oak" +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/common/ForgeMod.java b/src/main/java/net/minecraftforge/common/ForgeMod.java index 3c42c6668a..0d04f73dcf 100644 --- a/src/main/java/net/minecraftforge/common/ForgeMod.java +++ b/src/main/java/net/minecraftforge/common/ForgeMod.java @@ -20,10 +20,16 @@ import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.MobSpawnSettings.SpawnerData; +import net.minecraft.world.level.levelgen.GenerationStep.Decoration; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.item.Items; import net.minecraft.sounds.SoundEvent; +import net.minecraft.util.ExtraCodecs; import net.minecraft.core.Registry; +import net.minecraft.core.RegistryCodecs; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.pathfinder.BlockPathTypes; import net.minecraft.world.phys.Vec3; @@ -42,6 +48,10 @@ import net.minecraftforge.common.extensions.IForgePlayer; import net.minecraftforge.common.loot.CanToolPerformAction; import net.minecraftforge.common.loot.LootTableIdCondition; import net.minecraftforge.common.world.BiomeModifier; +import net.minecraftforge.common.world.ForgeBiomeModifiers.AddFeaturesBiomeModifier; +import net.minecraftforge.common.world.ForgeBiomeModifiers.AddSpawnsBiomeModifier; +import net.minecraftforge.common.world.ForgeBiomeModifiers.RemoveFeaturesBiomeModifier; +import net.minecraftforge.common.world.ForgeBiomeModifiers.RemoveSpawnsBiomeModifier; import net.minecraftforge.common.world.NoneBiomeModifier; import net.minecraftforge.common.world.NoneStructureModifier; import net.minecraftforge.common.world.StructureModifier; @@ -92,11 +102,14 @@ import net.minecraftforge.fml.common.Mod; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.MarkerManager; +import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.function.Consumer; +import java.util.function.Function; @Mod("forge") public class ForgeMod @@ -147,6 +160,55 @@ public class ForgeMod */ public static final RegistryObject> NONE_BIOME_MODIFIER_TYPE = BIOME_MODIFIER_SERIALIZERS.register("none", () -> Codec.unit(NoneBiomeModifier.INSTANCE)); + /** + * Stock biome modifier for adding features to biomes. + */ + public static final RegistryObject> ADD_FEATURES_BIOME_MODIFIER_TYPE = BIOME_MODIFIER_SERIALIZERS.register("add_features", () -> + RecordCodecBuilder.create(builder -> builder.group( + Biome.LIST_CODEC.fieldOf("biomes").forGetter(AddFeaturesBiomeModifier::biomes), + PlacedFeature.LIST_CODEC.fieldOf("features").forGetter(AddFeaturesBiomeModifier::features), + Decoration.CODEC.fieldOf("step").forGetter(AddFeaturesBiomeModifier::step) + ).apply(builder, AddFeaturesBiomeModifier::new)) + ); + + /** + * Stock biome modifier for removing features from biomes. + */ + public static final RegistryObject> REMOVE_FEATURES_BIOME_MODIFIER_TYPE = BIOME_MODIFIER_SERIALIZERS.register("remove_features", () -> + RecordCodecBuilder.create(builder -> builder.group( + Biome.LIST_CODEC.fieldOf("biomes").forGetter(RemoveFeaturesBiomeModifier::biomes), + PlacedFeature.LIST_CODEC.fieldOf("features").forGetter(RemoveFeaturesBiomeModifier::features), + new ExtraCodecs.EitherCodec, Decoration>(Decoration.CODEC.listOf(), Decoration.CODEC).>xmap( + either -> either.map(Set::copyOf, Set::of), // convert list/singleton to set when decoding + set -> set.size() == 1 ? Either.right(set.toArray(Decoration[]::new)[0]) : Either.left(List.copyOf(set)) + ).optionalFieldOf("steps", EnumSet.allOf(Decoration.class)).forGetter(RemoveFeaturesBiomeModifier::steps) + ).apply(builder, RemoveFeaturesBiomeModifier::new)) + ); + + /** + * Stock biome modifier for adding mob spawns to biomes. + */ + public static final RegistryObject> ADD_SPAWNS_BIOME_MODIFIER_TYPE = BIOME_MODIFIER_SERIALIZERS.register("add_spawns", () -> + RecordCodecBuilder.create(builder -> builder.group( + Biome.LIST_CODEC.fieldOf("biomes").forGetter(AddSpawnsBiomeModifier::biomes), + // Allow either a list or single spawner, attempting to decode the list format first. + // Uses the better EitherCodec that logs both errors if both formats fail to parse. + new ExtraCodecs.EitherCodec<>(SpawnerData.CODEC.listOf(), SpawnerData.CODEC).xmap( + either -> either.map(Function.identity(), List::of), // convert list/singleton to list when decoding + list -> list.size() == 1 ? Either.right(list.get(0)) : Either.left(list) // convert list to singleton/list when encoding + ).fieldOf("spawners").forGetter(AddSpawnsBiomeModifier::spawners) + ).apply(builder, AddSpawnsBiomeModifier::new)) + ); + + /** + * Stock biome modifier for removing mob spawns from biomes. + */ + public static final RegistryObject> REMOVE_SPAWNS_BIOME_MODIFIER_TYPE = BIOME_MODIFIER_SERIALIZERS.register("remove_spawns", () -> + RecordCodecBuilder.create(builder -> builder.group( + Biome.LIST_CODEC.fieldOf("biomes").forGetter(RemoveSpawnsBiomeModifier::biomes), + RegistryCodecs.homogeneousList(ForgeRegistries.Keys.ENTITY_TYPES).fieldOf("entity_types").forGetter(RemoveSpawnsBiomeModifier::entityTypes) + ).apply(builder, RemoveSpawnsBiomeModifier::new)) + ); /** * Noop structure modifier. Can be used in a structure modifier json with "type": "forge:none". */ diff --git a/src/main/java/net/minecraftforge/common/data/JsonCodecProvider.java b/src/main/java/net/minecraftforge/common/data/JsonCodecProvider.java index 415e275448..1a62ba0543 100644 --- a/src/main/java/net/minecraftforge/common/data/JsonCodecProvider.java +++ b/src/main/java/net/minecraftforge/common/data/JsonCodecProvider.java @@ -39,7 +39,7 @@ import org.slf4j.Logger; * * @param the type of thing being generated. */ -public final class JsonCodecProvider implements DataProvider +public class JsonCodecProvider implements DataProvider { private static final Logger LOGGER = LogUtils.getLogger(); protected final DataGenerator dataGenerator; diff --git a/src/main/java/net/minecraftforge/common/world/ForgeBiomeModifiers.java b/src/main/java/net/minecraftforge/common/world/ForgeBiomeModifiers.java new file mode 100644 index 0000000000..403d03966a --- /dev/null +++ b/src/main/java/net/minecraftforge/common/world/ForgeBiomeModifiers.java @@ -0,0 +1,220 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.common.world; + +import java.util.EnumSet; +import java.util.List; +import java.util.Set; + +import com.mojang.serialization.Codec; + +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.MobSpawnSettings.SpawnerData; +import net.minecraft.world.level.levelgen.GenerationStep.Decoration; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; +import net.minecraftforge.common.ForgeMod; +import net.minecraftforge.common.world.ModifiableBiomeInfo.BiomeInfo.Builder; +import net.minecraftforge.registries.ForgeRegistries; + +public final class ForgeBiomeModifiers +{ + private ForgeBiomeModifiers() {} // Utility class. + + /** + *

Stock biome modifier that adds features to biomes. Has the following json format:

+ *
+     * {
+     *   "type": "forge:add_features", // required
+     *   "biomes": "#namespace:your_biome_tag" // accepts a biome id, [list of biome ids], or #namespace:biome_tag
+     *   "features": "namespace:your_feature", // accepts a placed feature id, [list of placed feature ids], or #namespace:feature_tag
+     *   "step": "underground_ores" // accepts a Decoration enum name
+     * }
+     * 
+ *

Be wary of using this to add vanilla PlacedFeatures to biomes, as doing so may cause a feature cycle violation.

+ * + * @param biomes Biomes to add features to. + * @param features PlacedFeatures to add to biomes. + * @param step Decoration step to run features in. + */ + public static record AddFeaturesBiomeModifier(HolderSet biomes, HolderSet features, Decoration step) implements BiomeModifier + { + @Override + public void modify(Holder biome, Phase phase, Builder builder) + { + if (phase == Phase.ADD && this.biomes.contains(biome)) + { + BiomeGenerationSettingsBuilder generationSettings = builder.getGenerationSettings(); + this.features.forEach(holder -> generationSettings.addFeature(this.step, holder)); + } + } + + @Override + public Codec codec() + { + return ForgeMod.ADD_FEATURES_BIOME_MODIFIER_TYPE.get(); + } + } + + + /** + *

Stock biome modifier that removes features from biomes. Has the following json format:

+ *
+     * {
+     *   "type": "forge:removefeatures", // required
+     *   "biomes": "#namespace:your_biome_tag", // accepts a biome id, [list of biome ids], or #namespace:biome_tag
+     *   "features": "namespace:your_feature", // accepts a placed feature id, [list of placed feature ids], or #namespace:feature_tag
+     *   "steps": "underground_ores" OR ["underground_ores", "vegetal_decoration"] // one or more decoration steps; optional field, defaults to all steps if not specified
+     * }
+     * 
+ * + * @param biomes Biomes to remove features from. + * @param features PlacedFeatures to remove from biomes. + * @param steps Decoration steps to remove features from. + */ + public static record RemoveFeaturesBiomeModifier(HolderSet biomes, HolderSet features, Set steps) implements BiomeModifier + { + /** + * Creates a modifier that removes the given features from all decoration steps in the given biomes. + * @param biomes Biomes to remove features from. + * @param features PlacedFeatures to remove from biomes. + */ + public static RemoveFeaturesBiomeModifier allSteps(HolderSet biomes, HolderSet features) + { + return new RemoveFeaturesBiomeModifier(biomes, features, EnumSet.allOf(Decoration.class)); + } + + @Override + public void modify(Holder biome, Phase phase, Builder builder) + { + if (phase == Phase.REMOVE && this.biomes.contains(biome)) + { + BiomeGenerationSettingsBuilder generationSettings = builder.getGenerationSettings(); + for (Decoration step : this.steps) + { + generationSettings.getFeatures(step).removeIf(this.features::contains); + } + } + } + + @Override + public Codec codec() + { + return ForgeMod.REMOVE_FEATURES_BIOME_MODIFIER_TYPE.get(); + } + } + + /** + *

Stock biome modifier that adds a mob spawn to a biome. Has the following json format:

+ *
+     * {
+     *   "type": "forge:add_spawns", // Required
+     *   "biomes": "#namespace:biome_tag", // Accepts a biome id, [list of biome ids], or #namespace:biome_tag
+     *   "spawners":
+     *   {
+     *     "type": "namespace:entity_type", // Type of mob to spawn
+     *     "weight": 100, // int, spawn weighting
+     *     "minCount": 1, // int, minimum pack size
+     *     "maxCount": 4, // int, maximum pack size
+     *   }
+     * }
+     * 
+ *

Optionally accepts a list of spawner objects instead of a single spawner:

+ *
+     * {
+     *   "type": "forge:add_spawns", // Required
+     *   "biomes": "#namespace:biome_tag", // Accepts a biome id, [list of biome ids], or #namespace:biome_tag
+     *   "spawners":
+     *   [
+     *     {
+     *       "type": "namespace:entity_type", // Type of mob to spawn
+     *       "weight": 100, // int, spawn weighting
+     *       "minCount": 1, // int, minimum pack size
+     *       "maxCount": 4, // int, maximum pack size
+     *     },
+     *     {
+     *       // additional spawner object
+     *     }
+     *   ]
+     * }
+     * 
+ * + * @param biomes Biomes to add mob spawns to. + * @param spawners List of SpawnerDatas specifying EntityType, weight, and pack size. + */ + public record AddSpawnsBiomeModifier(HolderSet biomes, List spawners) implements BiomeModifier + { + /** + * Convenience method for using a single spawn data. + * @param biomes Biomes to add mob spawns to. + * @param spawner SpawnerData specifying EntityTYpe, weight, and pack size. + * @return AddSpawnsBiomeModifier that adds a single spawn entry to the specified biomes. + */ + public static AddSpawnsBiomeModifier singleSpawn(HolderSet biomes, SpawnerData spawner) + { + return new AddSpawnsBiomeModifier(biomes, List.of(spawner)); + } + + @Override + public void modify(Holder biome, Phase phase, Builder builder) + { + if (phase == Phase.ADD && this.biomes.contains(biome)) + { + MobSpawnSettingsBuilder spawns = builder.getMobSpawnSettings(); + for (SpawnerData spawner : this.spawners) + { + EntityType type = spawner.type; + spawns.addSpawn(type.getCategory(), spawner); + } + } + } + + @Override + public Codec codec() + { + return ForgeMod.ADD_SPAWNS_BIOME_MODIFIER_TYPE.get(); + } + } + + /** + *

Stock biome modifier that removes mob spawns from a biome. Has the following json format:

+ *
+     * {
+     *   "type": "forge:add_spawns", // Required
+     *   "biomes": "#namespace:biome_tag", // Accepts a biome id, [list of biome ids], or #namespace:biome_tag
+     *   "entity_types": #namespace:entitytype_tag // Accepts an entity type, [list of entity types], or #namespace:entitytype_tag
+     * }
+     * 
+ * + * @param biomes Biomes to add mob spawns to. + * @param entityTypes EntityTypes to remove from spawn lists. + */ + public record RemoveSpawnsBiomeModifier(HolderSet biomes, HolderSet> entityTypes) implements BiomeModifier + { + @Override + public void modify(Holder biome, Phase phase, Builder builder) + { + if (phase == Phase.REMOVE && this.biomes.contains(biome)) + { + MobSpawnSettingsBuilder spawnBuilder = builder.getMobSpawnSettings(); + for (MobCategory category : MobCategory.values()) + { + List spawns = spawnBuilder.getSpawner(category); + spawns.removeIf(spawnerData -> this.entityTypes.contains(ForgeRegistries.ENTITIES.getHolder(spawnerData.type).get())); + } + } + } + + @Override + public Codec codec() + { + return ForgeMod.REMOVE_SPAWNS_BIOME_MODIFIER_TYPE.get(); + } + } +} diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 76dc9e456e..c1da523902 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -277,6 +277,7 @@ public net.minecraft.server.network.ServerLoginPacketListenerImpl f_10021_ # gam public net.minecraft.server.packs.AbstractPackResources f_10203_ # file public net.minecraft.server.packs.PackType f_143750_ # bridgeType public net.minecraft.server.packs.resources.FallbackResourceManager f_10599_ # fallbacks +public net.minecraft.util.ExtraCodecs$EitherCodec public net.minecraft.util.datafix.fixes.StructuresBecomeConfiguredFix$Conversion public net.minecraft.util.thread.BlockableEventLoop m_18689_(Ljava/lang/Runnable;)Ljava/util/concurrent/CompletableFuture; # submitAsync #group public net.minecraft.world.damagesource.DamageSource *() #All methods public, most are already diff --git a/src/test/java/net/minecraftforge/debug/world/BiomeModifierTest.java b/src/test/java/net/minecraftforge/debug/world/BiomeModifierTest.java index 81a77eab51..21ebca406d 100644 --- a/src/test/java/net/minecraftforge/debug/world/BiomeModifierTest.java +++ b/src/test/java/net/minecraftforge/debug/world/BiomeModifierTest.java @@ -8,12 +8,8 @@ package net.minecraftforge.debug.world; import java.util.List; import java.util.Map; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import com.google.gson.JsonElement; import com.mojang.serialization.Codec; -import com.mojang.serialization.DataResult; import com.mojang.serialization.JsonOps; import com.mojang.serialization.codecs.RecordCodecBuilder; @@ -22,12 +18,12 @@ import net.minecraft.core.HolderSet; import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; import net.minecraft.data.DataGenerator; -import net.minecraft.data.DataProvider; import net.minecraft.data.worldgen.features.NetherFeatures; import net.minecraft.resources.RegistryOps; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.BiomeTags; +import net.minecraft.tags.EntityTypeTags; import net.minecraft.world.entity.EntityType; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.Biome.Precipitation; @@ -39,8 +35,11 @@ import net.minecraft.world.level.levelgen.placement.CountOnEveryLayerPlacement; import net.minecraft.world.level.levelgen.placement.PlacedFeature; import net.minecraftforge.common.data.ExistingFileHelper; import net.minecraftforge.common.data.JsonCodecProvider; -import net.minecraftforge.common.world.BiomeGenerationSettingsBuilder; import net.minecraftforge.common.world.BiomeModifier; +import net.minecraftforge.common.world.ForgeBiomeModifiers.AddFeaturesBiomeModifier; +import net.minecraftforge.common.world.ForgeBiomeModifiers.AddSpawnsBiomeModifier; +import net.minecraftforge.common.world.ForgeBiomeModifiers.RemoveFeaturesBiomeModifier; +import net.minecraftforge.common.world.ForgeBiomeModifiers.RemoveSpawnsBiomeModifier; import net.minecraftforge.common.world.ModifiableBiomeInfo.BiomeInfo.Builder; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.fml.common.Mod; @@ -53,28 +52,42 @@ import net.minecraftforge.registries.RegistryObject; /** *

This tests the following features and requirements of biome modifier jsons::

*
    - *
  • A biome modifier json is created via datagen.
  • - *
  • The biome modifier modifies all four modifiable fields in biomes, to ensure patches and coremods apply correctly (generation, spawns, climate, and client effects).
  • - *
  • The biome modifier uses a biome tag to determine which biomes to modify.
  • - *
  • The biome modifier adds json feature to modified biomes, to ensure json features are usable in biome modifiers.
  • + *
  • Biome modifier jsons are created via datagen.
  • + *
  • Biome modifiers modify all four modifiable fields in biomes, to ensure patches and coremods apply correctly (generation, spawns, climate, and client effects).
  • + *
  • Biome modifiers use biome tags to determine which biomes to modify.
  • + *
  • Biome modifiers add a json feature to modified biomes, to ensure json features are usable in biome modifiers.
  • *
- *

If the biome modifier is applied correctly, then badlands biomes should generate large basalt columns, - * spawn magma cubes, have red-colored water, and be snowy.

+ *

If the biome modifiers are applied correctly, then badlands biomes should generate large basalt columns, + * spawn magma cubes, have red-colored water, and be snowy. Additionally, biomes in the is_forest tag are missing + * oak trees, pine trees, and skeletons.

*/ @Mod(BiomeModifierTest.MODID) public class BiomeModifierTest { - public static final Logger LOGGER = LogManager.getLogger(); public static final String MODID = "biome_modifiers_test"; - public static final boolean ENABLED = true; - public static final String BASALT_PILLARS = "large_basalt_columns"; - public static final ResourceLocation BASALT_PILLARS_RL = new ResourceLocation(MODID, BASALT_PILLARS); - public static final ResourceKey BASALT_PILLARS_KEY = ResourceKey.create(Registry.PLACED_FEATURE_REGISTRY, BASALT_PILLARS_RL); - public static final String TEST = "test"; - public static final ResourceLocation ADD_FEATURES_TO_BIOMES_RL = new ResourceLocation(MODID, TEST); - public static final String MODIFY_BADLANDS = "modify_badlands"; - public static final ResourceLocation MODIFY_BADLANDS_RL = new ResourceLocation(MODID, MODIFY_BADLANDS); - public static final ResourceKey MODIFY_BADLANDS_KEY = ResourceKey.create(ForgeRegistries.Keys.BIOME_MODIFIERS, MODIFY_BADLANDS_RL); + private static final boolean ENABLED = true; + + private static final String LARGE_BASALT_COLUMNS = "large_basalt_columns"; + private static final ResourceLocation LARGE_BASALT_COLUMNS_RL = new ResourceLocation(MODID, LARGE_BASALT_COLUMNS); + private static final ResourceKey LARGE_BASALT_COLUMNS_KEY = ResourceKey.create(Registry.PLACED_FEATURE_REGISTRY, LARGE_BASALT_COLUMNS_RL); + + private static final String MODIFY_BIOMES = "modify_biomes"; + private static final ResourceLocation MODIFY_BIOMES_RL = new ResourceLocation(MODID, MODIFY_BIOMES); + + private static final String ADD_BASALT = "add_basalt"; + private static final ResourceLocation ADD_BASALT_RL = new ResourceLocation(MODID, ADD_BASALT); + + private static final String ADD_MAGMA_CUBES = "add_magma_cubes"; + private static final ResourceLocation ADD_MAGMA_CUBES_RL = new ResourceLocation(MODID, ADD_MAGMA_CUBES); + + private static final String MODIFY_BADLANDS = "modify_badlands"; + private static final ResourceLocation MODIFY_BADLANDS_RL = new ResourceLocation(MODID, MODIFY_BADLANDS); + + private static final String REMOVE_FOREST_TREES = "remove_forest_trees"; + private static final ResourceLocation REMOVE_FOREST_TREES_RL = new ResourceLocation(MODID, REMOVE_FOREST_TREES); + + private static final String REMOVE_FOREST_SKELETONS = "remove_forest_skeletons"; + private static final ResourceLocation REMOVE_FOREST_SKELETONS_RL = new ResourceLocation(MODID, REMOVE_FOREST_SKELETONS); public BiomeModifierTest() { @@ -86,7 +99,7 @@ public class BiomeModifierTest // Serializer types can be registered via deferred register. final DeferredRegister> serializers = DeferredRegister.create(ForgeRegistries.Keys.BIOME_MODIFIER_SERIALIZERS, MODID); serializers.register(modBus); - serializers.register(TEST, TestModifier::makeCodec); + serializers.register(MODIFY_BIOMES, TestModifier::makeCodec); modBus.addListener(this::onGatherData); } @@ -96,58 +109,66 @@ public class BiomeModifierTest // Example of how to datagen datapack registry objects. final DataGenerator generator = event.getGenerator(); final ExistingFileHelper existingFileHelper = event.getExistingFileHelper(); - - // Any reference holders our objects have must come from this same RegistryAccess instance, - // or encoding our objects will fail. - final RegistryAccess registries = RegistryAccess.builtinCopy(); - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registries); - final Registry> configuredFeatures = ops.registry(Registry.CONFIGURED_FEATURE_REGISTRY).get(); - final Registry placedFeatures = ops.registry(Registry.PLACED_FEATURE_REGISTRY).get(); + final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, RegistryAccess.builtinCopy()); - // Create our PlacedFeature so we can generate it. + // Create our placed feature and biome modifiers. final ResourceKey> configuredFeatureKey = NetherFeatures.LARGE_BASALT_COLUMNS.unwrapKey().get().cast(Registry.CONFIGURED_FEATURE_REGISTRY).get(); - final Holder> configuredFeatureHolder = configuredFeatures.getOrCreateHolderOrThrow(configuredFeatureKey); - final PlacedFeature placedFeature = new PlacedFeature(configuredFeatureHolder, List.of(CountOnEveryLayerPlacement.of(1), BiomeFilter.biome())); + // Make sure we're using the holder from the registryaccess/registryops, the static ones won't work. + final Holder> configuredFeatureHolder = ops.registry(Registry.CONFIGURED_FEATURE_REGISTRY).get().getOrCreateHolderOrThrow(configuredFeatureKey); + final PlacedFeature basaltFeature = new PlacedFeature( + configuredFeatureHolder, + List.of(CountOnEveryLayerPlacement.of(1), BiomeFilter.biome())); + + final HolderSet.Named badlandsTag = new HolderSet.Named<>(ops.registry(Registry.BIOME_REGISTRY).get(), BiomeTags.IS_BADLANDS); + final HolderSet.Named forestsTag = new HolderSet.Named<>(ops.registry(Registry.BIOME_REGISTRY).get(), BiomeTags.IS_FOREST); + + final BiomeModifier addBasaltFeature = new AddFeaturesBiomeModifier( + badlandsTag, + HolderSet.direct(ops.registry(Registry.PLACED_FEATURE_REGISTRY).get().getOrCreateHolderOrThrow(ResourceKey.create(Registry.PLACED_FEATURE_REGISTRY, LARGE_BASALT_COLUMNS_KEY.location()))), + Decoration.TOP_LAYER_MODIFICATION); + + final BiomeModifier addSpawn = AddSpawnsBiomeModifier.singleSpawn( + badlandsTag, + new SpawnerData(EntityType.MAGMA_CUBE, 100, 1, 4)); - // Create our BiomeModifier so we can generate it. - final HolderSet placedFeatureHolderSet = - HolderSet.direct(placedFeatures.getOrCreateHolderOrThrow(BASALT_PILLARS_KEY)); final BiomeModifier biomeModifier = new TestModifier( new HolderSet.Named<>(ops.registry(Registry.BIOME_REGISTRY).get(), BiomeTags.IS_BADLANDS), - Decoration.TOP_LAYER_MODIFICATION, - placedFeatureHolderSet, - new SpawnerData(EntityType.MAGMA_CUBE, 100, 1, 4), Precipitation.SNOW, 0xFF0000 ); - - // Create and add our data providers. - final DataProvider placedFeatureProvider = - JsonCodecProvider.forDatapackRegistry(generator, existingFileHelper, MODID, ops, Registry.PLACED_FEATURE_REGISTRY, - Map.of(BASALT_PILLARS_RL, placedFeature)); - generator.addProvider(event.includeServer(), placedFeatureProvider); - final DataProvider biomeModifierProvider = - JsonCodecProvider.forDatapackRegistry(generator, existingFileHelper, MODID, ops, ForgeRegistries.Keys.BIOME_MODIFIERS, - Map.of(MODIFY_BADLANDS_RL, biomeModifier)); - generator.addProvider(event.includeServer(), biomeModifierProvider); + final BiomeModifier removeFeature = RemoveFeaturesBiomeModifier.allSteps( + forestsTag, + HolderSet.direct(ops.registry(Registry.PLACED_FEATURE_REGISTRY).get().getOrCreateHolderOrThrow(ResourceKey.create(Registry.PLACED_FEATURE_REGISTRY, new ResourceLocation("trees_birch_and_oak")))) + ); + + final BiomeModifier removeSpawn = new RemoveSpawnsBiomeModifier( + forestsTag, + new HolderSet.Named<>(ops.registry(Registry.ENTITY_TYPE_REGISTRY).get(), EntityTypeTags.SKELETONS) + ); + + // Create and add dataproviders. + generator.addProvider(event.includeServer(), JsonCodecProvider.forDatapackRegistry( + generator, existingFileHelper, MODID, ops, Registry.PLACED_FEATURE_REGISTRY, Map.of( + LARGE_BASALT_COLUMNS_RL, basaltFeature))); + + generator.addProvider(event.includeServer(), JsonCodecProvider.forDatapackRegistry( + generator, existingFileHelper, MODID, ops, ForgeRegistries.Keys.BIOME_MODIFIERS, Map.of( + MODIFY_BADLANDS_RL, biomeModifier, + ADD_BASALT_RL, addBasaltFeature, + ADD_MAGMA_CUBES_RL, addSpawn, + REMOVE_FOREST_TREES_RL, removeFeature, + REMOVE_FOREST_SKELETONS_RL, removeSpawn))); } - public record TestModifier(HolderSet biomes, Decoration generationStage, HolderSet features, SpawnerData spawn, Precipitation precipitation, int waterColor) - implements BiomeModifier + public record TestModifier(HolderSet biomes, Precipitation precipitation, int waterColor) implements BiomeModifier { - private static final RegistryObject> SERIALIZER = RegistryObject.create(ADD_FEATURES_TO_BIOMES_RL, ForgeRegistries.Keys.BIOME_MODIFIER_SERIALIZERS, MODID); + private static final RegistryObject> SERIALIZER = RegistryObject.create(MODIFY_BIOMES_RL, ForgeRegistries.Keys.BIOME_MODIFIER_SERIALIZERS, MODID); @Override public void modify(Holder biome, Phase phase, Builder builder) { - if (phase == Phase.ADD && this.biomes.contains(biome)) - { - BiomeGenerationSettingsBuilder generation = builder.getGenerationSettings(); - this.features.forEach(holder -> generation.addFeature(this.generationStage, holder)); - builder.getMobSpawnSettings().addSpawn(this.spawn.type.getCategory(), this.spawn); - } - else if (phase == Phase.MODIFY && this.biomes.contains(biome)) + if (phase == Phase.MODIFY && this.biomes.contains(biome)) { builder.getClimateSettings().setPrecipitation(this.precipitation); builder.getEffects().waterColor(this.waterColor); @@ -166,24 +187,9 @@ public class BiomeModifierTest { return RecordCodecBuilder.create(builder -> builder.group( Biome.LIST_CODEC.fieldOf("biomes").forGetter(TestModifier::biomes), - Codec.STRING.comapFlatMap(TestModifier::generationStageFromString, Decoration::toString).fieldOf("generation_stage").forGetter(TestModifier::generationStage), - PlacedFeature.LIST_CODEC.fieldOf("features").forGetter(TestModifier::features), - SpawnerData.CODEC.fieldOf("spawn").forGetter(TestModifier::spawn), Precipitation.CODEC.fieldOf("precipitation").forGetter(TestModifier::precipitation), Codec.INT.fieldOf("water_color").forGetter(TestModifier::waterColor) ).apply(builder, TestModifier::new)); } - - private static DataResult generationStageFromString(String name) - { - try - { - return DataResult.success(Decoration.valueOf(name)); - } - catch (Exception e) - { - return DataResult.error("Not a decoration stage: " + name); - } - } } }