[1.19.x] Add stock biome modifier types for adding features and spawns (#8697)

This commit is contained in:
Joseph B authored and GitHub committed 2022-06-15 00:39:03 +01:00
1 parent 2a05c7c01d
commit 683bf99fe4
10 files changed
+391 -84

No files matched your search

@@ -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<Codec<NoneBiomeModifier>> 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<Codec<AddFeaturesBiomeModifier>> 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<Codec<RemoveFeaturesBiomeModifier>> 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<List<Decoration>, Decoration>(Decoration.CODEC.listOf(), Decoration.CODEC).<Set<Decoration>>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<Codec<AddSpawnsBiomeModifier>> 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<Codec<RemoveSpawnsBiomeModifier>> 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".
*/
@@ -39,7 +39,7 @@ import org.slf4j.Logger;
*
* @param <T> the type of thing being generated.
*/
public final class JsonCodecProvider<T> implements DataProvider
public class JsonCodecProvider<T> implements DataProvider
{
private static final Logger LOGGER = LogUtils.getLogger();
protected final DataGenerator dataGenerator;
@@ -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.
/**
* <p>Stock biome modifier that adds features to biomes. Has the following json format:</p>
* <pre>
* {
* "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
* }
* </pre>
* <p>Be wary of using this to add vanilla PlacedFeatures to biomes, as doing so may cause a feature cycle violation.</p>
*
* @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<Biome> biomes, HolderSet<PlacedFeature> features, Decoration step) implements BiomeModifier
{
@Override
public void modify(Holder<Biome> 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<? extends BiomeModifier> codec()
{
return ForgeMod.ADD_FEATURES_BIOME_MODIFIER_TYPE.get();
}
}
/**
* <p>Stock biome modifier that removes features from biomes. Has the following json format:</p>
* <pre>
* {
* "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
* }
* </pre>
*
* @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<Biome> biomes, HolderSet<PlacedFeature> features, Set<Decoration> 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<Biome> biomes, HolderSet<PlacedFeature> features)
{
return new RemoveFeaturesBiomeModifier(biomes, features, EnumSet.allOf(Decoration.class));
}
@Override
public void modify(Holder<Biome> 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<? extends BiomeModifier> codec()
{
return ForgeMod.REMOVE_FEATURES_BIOME_MODIFIER_TYPE.get();
}
}
/**
* <p>Stock biome modifier that adds a mob spawn to a biome. Has the following json format:</p>
* <pre>
* {
* "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
* }
* }
* </pre>
* <p>Optionally accepts a list of spawner objects instead of a single spawner:</p>
* <pre>
* {
* "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
* }
* ]
* }
* </pre>
*
* @param biomes Biomes to add mob spawns to.
* @param spawners List of SpawnerDatas specifying EntityType, weight, and pack size.
*/
public record AddSpawnsBiomeModifier(HolderSet<Biome> biomes, List<SpawnerData> 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<Biome> biomes, SpawnerData spawner)
{
return new AddSpawnsBiomeModifier(biomes, List.of(spawner));
}
@Override
public void modify(Holder<Biome> 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<? extends BiomeModifier> codec()
{
return ForgeMod.ADD_SPAWNS_BIOME_MODIFIER_TYPE.get();
}
}
/**
* <p>Stock biome modifier that removes mob spawns from a biome. Has the following json format:</p>
* <pre>
* {
* "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
* }
* </pre>
*
* @param biomes Biomes to add mob spawns to.
* @param entityTypes EntityTypes to remove from spawn lists.
*/
public record RemoveSpawnsBiomeModifier(HolderSet<Biome> biomes, HolderSet<EntityType<?>> entityTypes) implements BiomeModifier
{
@Override
public void modify(Holder<Biome> biome, Phase phase, Builder builder)
{
if (phase == Phase.REMOVE && this.biomes.contains(biome))
{
MobSpawnSettingsBuilder spawnBuilder = builder.getMobSpawnSettings();
for (MobCategory category : MobCategory.values())
{
List<SpawnerData> spawns = spawnBuilder.getSpawner(category);
spawns.removeIf(spawnerData -> this.entityTypes.contains(ForgeRegistries.ENTITIES.getHolder(spawnerData.type).get()));
}
}
}
@Override
public Codec<? extends BiomeModifier> codec()
{
return ForgeMod.REMOVE_SPAWNS_BIOME_MODIFIER_TYPE.get();
}
}
}