From 34ecbb3f144804a9623c6839c515bf0e0533b0da Mon Sep 17 00:00:00 2001 From: paulevsGitch Date: Fri, 13 Aug 2021 21:23:12 +0300 Subject: [PATCH] End biome source (WIP) --- src/main/java/ru/bclib/BCLib.java | 2 + src/main/java/ru/bclib/api/BiomeAPI.java | 88 +++++++++++- src/main/java/ru/bclib/api/PostInitAPI.java | 1 + src/main/java/ru/bclib/config/Configs.java | 1 + .../bclib/interfaces/BiomeListProvider.java | 10 ++ .../common/WeightedBiomePickerAccessor.java | 37 +++++ .../world/generator/BCLibEndBiomeSource.java | 128 ++++++++++++++++++ .../world/generator/GeneratorOptions.java | 40 ++++++ 8 files changed, 300 insertions(+), 7 deletions(-) create mode 100644 src/main/java/ru/bclib/interfaces/BiomeListProvider.java create mode 100644 src/main/java/ru/bclib/mixin/common/WeightedBiomePickerAccessor.java create mode 100644 src/main/java/ru/bclib/world/generator/BCLibEndBiomeSource.java create mode 100644 src/main/java/ru/bclib/world/generator/GeneratorOptions.java diff --git a/src/main/java/ru/bclib/BCLib.java b/src/main/java/ru/bclib/BCLib.java index a1185fbc..061ac50f 100644 --- a/src/main/java/ru/bclib/BCLib.java +++ b/src/main/java/ru/bclib/BCLib.java @@ -16,6 +16,7 @@ import ru.bclib.recipes.CraftingRecipes; import ru.bclib.registry.BaseBlockEntities; import ru.bclib.registry.BaseRegistry; import ru.bclib.util.Logger; +import ru.bclib.world.generator.BCLibEndBiomeSource; import ru.bclib.world.surface.BCLSurfaceBuilders; import java.util.List; @@ -29,6 +30,7 @@ public class BCLib implements ModInitializer { BaseRegistry.register(); BaseBlockEntities.register(); BCLSurfaceBuilders.register(); + BCLibEndBiomeSource.register(); TagAPI.init(); CraftingRecipes.init(); WorldDataAPI.registerModCache(MOD_ID); diff --git a/src/main/java/ru/bclib/api/BiomeAPI.java b/src/main/java/ru/bclib/api/BiomeAPI.java index 93c5d594..27e80151 100644 --- a/src/main/java/ru/bclib/api/BiomeAPI.java +++ b/src/main/java/ru/bclib/api/BiomeAPI.java @@ -1,10 +1,11 @@ package ru.bclib.api; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.google.common.collect.Sets; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.fabricmc.fabric.impl.biome.InternalBiomeData; +import net.fabricmc.fabric.impl.biome.WeightedBiomePicker; import net.minecraft.client.Minecraft; import net.minecraft.core.Registry; import net.minecraft.data.BuiltinRegistries; @@ -14,13 +15,15 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.Biome.ClimateParameters; import net.minecraft.world.level.biome.Biomes; +import ru.bclib.interfaces.BiomeListProvider; import ru.bclib.util.MHelper; import ru.bclib.world.biomes.BCLBiome; +import ru.bclib.world.generator.BiomePicker; -import java.util.HashMap; +import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.Random; -import java.util.Set; public class BiomeAPI { /** @@ -29,9 +32,13 @@ public class BiomeAPI { */ public static final BCLBiome EMPTY_BIOME = new BCLBiome(Biomes.THE_VOID.location(), BuiltinRegistries.BIOME.get(Biomes.THE_VOID), 1, 0); - private static final Set NETHER_BIOMES = Sets.newHashSet(); - private static final Set END_LAND_BIOMES = Sets.newHashSet(); - private static final Set END_VOID_BIOMES = Sets.newHashSet(); + private static final Map NETHER_BIOMES = Maps.newHashMap(); + private static final Map END_LAND_BIOMES = Maps.newHashMap(); + private static final Map END_VOID_BIOMES = Maps.newHashMap(); + + public static final BiomePicker NETHER_BIOME_PICKER = new BiomePicker(); + public static final BiomePicker END_LAND_BIOME_PICKER = new BiomePicker(); + public static final BiomePicker END_VOID_BIOME_PICKER = new BiomePicker(); private static final Map ID_MAP = Maps.newHashMap(); private static final Map CLIENT = Maps.newHashMap(); @@ -65,6 +72,7 @@ public class BiomeAPI { */ public static void registerNetherBiome(BCLBiome biome) { registerBiome(biome); + NETHER_BIOME_PICKER.addBiome(biome); Random random = new Random(biome.getID().hashCode()); ClimateParameters parameters = new ClimateParameters( MHelper.randRange(-1.5F, 1.5F, random), @@ -77,6 +85,18 @@ public class BiomeAPI { InternalBiomeData.addNetherBiome(key, parameters); } + /** + * Register {@link BCLBiome} instance and its {@link Biome} if necessary. + * After that biome will be added to BCLib Nether Biome Generator and into Fabric Biome API. + * @param biome {@link BCLBiome} + */ + public static void registerNetherBiome(Biome biome) { + ResourceKey key = BuiltinRegistries.BIOME.getResourceKey(biome).get(); + BCLBiome bclBiome = new BCLBiome(key.location(), biome, 1, 1); + NETHER_BIOME_PICKER.addBiome(bclBiome); + registerBiome(bclBiome); + } + /** * Register {@link BCLBiome} instance and its {@link Biome} if necessary. * After that biome will be added to BCLib End Biome Generator and into Fabric Biome API as a land biome (will generate only on islands). @@ -84,12 +104,25 @@ public class BiomeAPI { */ public static void registerEndLandBiome(BCLBiome biome) { registerBiome(biome); + END_LAND_BIOME_PICKER.addBiome(biome); float weight = biome.getGenChance(); ResourceKey key = BuiltinRegistries.BIOME.getResourceKey(biome.getBiome()).get(); InternalBiomeData.addEndBiomeReplacement(Biomes.END_HIGHLANDS, key, weight); InternalBiomeData.addEndBiomeReplacement(Biomes.END_MIDLANDS, key, weight); } + /** + * Register {@link BCLBiome} wrapper for {@link Biome}. + * After that biome will be added to BCLib End Biome Generator and into Fabric Biome API as a land biome (will generate only on islands). + * @param biome {@link BCLBiome} + */ + public static void registerEndLandBiome(Biome biome) { + ResourceKey key = BuiltinRegistries.BIOME.getResourceKey(biome).get(); + BCLBiome bclBiome = new BCLBiome(key.location(), biome, 1, 1); + END_LAND_BIOME_PICKER.addBiome(bclBiome); + registerBiome(bclBiome); + } + /** * Register {@link BCLBiome} instance and its {@link Biome} if necessary. * After that biome will be added to BCLib End Biome Generator and into Fabric Biome API as a void biome (will generate only in the End void - between islands). @@ -97,11 +130,24 @@ public class BiomeAPI { */ public static void registerEndVoidBiome(BCLBiome biome) { registerBiome(biome); + END_VOID_BIOME_PICKER.addBiome(biome); float weight = biome.getGenChance(); ResourceKey key = BuiltinRegistries.BIOME.getResourceKey(biome.getBiome()).get(); InternalBiomeData.addEndBiomeReplacement(Biomes.SMALL_END_ISLANDS, key, weight); } + /** + * Register {@link BCLBiome} instance and its {@link Biome} if necessary. + * After that biome will be added to BCLib End Biome Generator and into Fabric Biome API as a void biome (will generate only in the End void - between islands). + * @param biome {@link BCLBiome} + */ + public static void registerEndVoidBiome(Biome biome) { + ResourceKey key = BuiltinRegistries.BIOME.getResourceKey(biome).get(); + BCLBiome bclBiome = new BCLBiome(key.location(), biome, 1, 1); + END_VOID_BIOME_PICKER.addBiome(bclBiome); + registerBiome(bclBiome); + } + /** * Adds {@link BCLBiome} to FabricAPI biomes as the Nether biome (with random {@link ClimateParameters}). * @@ -116,7 +162,7 @@ public class BiomeAPI { MHelper.randRange(-2F, 2F, random), MHelper.randRange(-2F, 2F, random), MHelper.randRange(-2F, 2F, random), - MHelper.randRange(-2F, 2F, random) + random.nextFloat() ); InternalBiomeData.addNetherBiome(key, parameters); } @@ -226,4 +272,32 @@ public class BiomeAPI { public static boolean hasBiome(ResourceLocation biomeID) { return ID_MAP.containsKey(biomeID); } + + /** + * Load biomes from Fabric API. For internal usage only. + */ + public static void loadFabricAPIBiomes() { + List> biomes = Lists.newArrayList(); + biomes.addAll(getBiomes(InternalBiomeData.getEndBiomesMap().get(Biomes.SMALL_END_ISLANDS))); + biomes.addAll(getBiomes(InternalBiomeData.getEndBarrensMap().get(Biomes.END_BARRENS))); + biomes.forEach((key) -> registerEndVoidBiome(BuiltinRegistries.BIOME.get(key.location()))); + + biomes.clear(); + biomes.addAll(getBiomes(InternalBiomeData.getEndBiomesMap().get(Biomes.END_MIDLANDS))); + biomes.addAll(getBiomes(InternalBiomeData.getEndBiomesMap().get(Biomes.END_HIGHLANDS))); + biomes.forEach((key) -> registerEndLandBiome(BuiltinRegistries.BIOME.get(key.location()))); + } + + private static List> getBiomes(WeightedBiomePicker picker) { + BiomeListProvider biomeList = (BiomeListProvider) (Object) picker; + return biomeList == null ? Collections.emptyList() : biomeList.getBiomes(); + } + + public static boolean isNetherBiome(ResourceLocation biomeID) { + return NETHER_BIOMES.containsKey(biomeID); + } + + public static boolean isEndBiome(ResourceLocation biomeID) { + return END_LAND_BIOMES.containsKey(biomeID) || END_VOID_BIOMES.containsKey(biomeID); + } } diff --git a/src/main/java/ru/bclib/api/PostInitAPI.java b/src/main/java/ru/bclib/api/PostInitAPI.java index 5dae1dd7..29adf17b 100644 --- a/src/main/java/ru/bclib/api/PostInitAPI.java +++ b/src/main/java/ru/bclib/api/PostInitAPI.java @@ -48,6 +48,7 @@ public class PostInitAPI { } }); postInitFunctions = null; + BiomeAPI.loadFabricAPIBiomes(); } @Environment(EnvType.CLIENT) diff --git a/src/main/java/ru/bclib/config/Configs.java b/src/main/java/ru/bclib/config/Configs.java index 8b7dec1a..b603763a 100644 --- a/src/main/java/ru/bclib/config/Configs.java +++ b/src/main/java/ru/bclib/config/Configs.java @@ -5,6 +5,7 @@ import net.fabricmc.api.Environment; import ru.bclib.BCLib; public class Configs { + public static final PathConfig GENERATOR_CONFIG = new PathConfig(BCLib.MOD_ID, "generator"); public static final PathConfig MAIN_CONFIG = new PathConfig(BCLib.MOD_ID, "main"); public static final String MAIN_PATCH_CATEGORY = "patches"; public static final String MAIN_SYNC_CATEGORY = "client_sync"; diff --git a/src/main/java/ru/bclib/interfaces/BiomeListProvider.java b/src/main/java/ru/bclib/interfaces/BiomeListProvider.java new file mode 100644 index 00000000..79cea98d --- /dev/null +++ b/src/main/java/ru/bclib/interfaces/BiomeListProvider.java @@ -0,0 +1,10 @@ +package ru.bclib.interfaces; + +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.biome.Biome; + +import java.util.List; + +public interface BiomeListProvider { + List> getBiomes(); +} diff --git a/src/main/java/ru/bclib/mixin/common/WeightedBiomePickerAccessor.java b/src/main/java/ru/bclib/mixin/common/WeightedBiomePickerAccessor.java new file mode 100644 index 00000000..97f82dcd --- /dev/null +++ b/src/main/java/ru/bclib/mixin/common/WeightedBiomePickerAccessor.java @@ -0,0 +1,37 @@ +package ru.bclib.mixin.common; + +import com.google.common.collect.Lists; +import net.fabricmc.fabric.impl.biome.InternalBiomeData; +import net.fabricmc.fabric.impl.biome.WeightedBiomePicker; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.Biomes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import ru.bclib.interfaces.BiomeListProvider; + +import java.util.List; + +@Mixin(value = WeightedBiomePicker.class, remap = false) +public class WeightedBiomePickerAccessor implements BiomeListProvider { + private final List> biomes = Lists.newArrayList(); + + @Inject(method = "addBiome", at = @At("TAIL")) + private void bclib_addBiome(final ResourceKey biome, final double weight, CallbackInfo info) { + if (be_isCorrectPicker(WeightedBiomePicker.class.cast(this))) { + biomes.add(biome); + } + } + + private boolean be_isCorrectPicker(WeightedBiomePicker picker) { + return picker == InternalBiomeData.getEndBiomesMap().get(Biomes.SMALL_END_ISLANDS) || + picker == InternalBiomeData.getEndBarrensMap().get(Biomes.END_BARRENS); + } + + @Override + public List> getBiomes() { + return biomes; + } +} diff --git a/src/main/java/ru/bclib/world/generator/BCLibEndBiomeSource.java b/src/main/java/ru/bclib/world/generator/BCLibEndBiomeSource.java new file mode 100644 index 00000000..0f96c365 --- /dev/null +++ b/src/main/java/ru/bclib/world/generator/BCLibEndBiomeSource.java @@ -0,0 +1,128 @@ +package ru.bclib.world.generator; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.Registry; +import net.minecraft.resources.RegistryLookupCodec; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.biome.Biomes; +import net.minecraft.world.level.biome.TheEndBiomeSource; +import net.minecraft.world.level.levelgen.WorldgenRandom; +import net.minecraft.world.level.levelgen.synth.SimplexNoise; +import ru.bclib.BCLib; +import ru.bclib.api.BiomeAPI; +import ru.bclib.noise.OpenSimplexNoise; +import ru.bclib.world.biomes.BCLBiome; + +import java.awt.Point; +import java.util.List; +import java.util.function.Function; + +public class BCLibEndBiomeSource extends BiomeSource { + public static final Codec CODEC = RecordCodecBuilder.create((instance) -> { + return instance.group(RegistryLookupCodec.create(Registry.BIOME_REGISTRY).forGetter((theEndBiomeSource) -> { + return theEndBiomeSource.biomeRegistry; + }), Codec.LONG.fieldOf("seed").stable().forGetter((theEndBiomeSource) -> { + return theEndBiomeSource.seed; + })).apply(instance, instance.stable(BCLibEndBiomeSource::new)); + }); + private static final OpenSimplexNoise SMALL_NOISE = new OpenSimplexNoise(8324); + private Function endLandFunction; + private final Registry biomeRegistry; + private final SimplexNoise noise; + private final Biome centerBiome; + private final Biome barrens; + private BiomeMap mapLand; + private BiomeMap mapVoid; + private final long seed; + private final Point pos; + + public BCLibEndBiomeSource(Registry biomeRegistry, long seed) { + super(getBiomes(biomeRegistry)); + + biomeRegistry.forEach(biome -> { + ResourceLocation key = biomeRegistry.getKey(biome); + BCLBiome bclBiome = BiomeAPI.getBiome(key); + bclBiome.updateActualBiomes(biomeRegistry); + if (!BiomeAPI.END_LAND_BIOME_PICKER.containsImmutable(key)) { + BiomeAPI.END_LAND_BIOME_PICKER.addBiomeMutable(bclBiome); + } + }); + + this.mapLand = new BiomeMap(seed, GeneratorOptions.getBiomeSizeEndLand(), BiomeAPI.END_LAND_BIOME_PICKER); + this.mapVoid = new BiomeMap(seed, GeneratorOptions.getBiomeSizeEndVoid(), BiomeAPI.END_VOID_BIOME_PICKER); + this.centerBiome = biomeRegistry.getOrThrow(Biomes.THE_END); + this.barrens = biomeRegistry.getOrThrow(Biomes.END_BARRENS); + this.biomeRegistry = biomeRegistry; + this.seed = seed; + + WorldgenRandom chunkRandom = new WorldgenRandom(seed); + chunkRandom.consumeCount(17292); + this.noise = new SimplexNoise(chunkRandom); + + this.endLandFunction = GeneratorOptions.getEndLandFunction(); + this.pos = new Point(); + } + + private static List getBiomes(Registry biomeRegistry) { + return biomeRegistry.stream().filter(biome -> BiomeAPI.isEndBiome(biomeRegistry.getKey(biome))).toList(); + } + + @Override + public Biome getNoiseBiome(int biomeX, int biomeY, int biomeZ) { + long i = (long) biomeX * (long) biomeX; + long j = (long) biomeZ * (long) biomeZ; + long dist = i + j; + + if ((biomeX & 31) == 0 && (biomeZ & 31) == 0) { + mapLand.clearCache(); + mapVoid.clearCache(); + } + + BCLBiome endBiome = null; + if (endLandFunction == null) { + if (dist <= 65536L) return centerBiome; + float height = TheEndBiomeSource.getHeightValue( + noise, + (biomeX >> 1) + 1, + (biomeZ >> 1) + 1 + ) + (float) SMALL_NOISE.eval(biomeX, biomeZ) * 5; + + if (height > -20F && height < -5F) { + return barrens; + } + + if (height < -10F) { + return mapVoid.getBiome(biomeX << 2, biomeZ << 2).getActualBiome(); + } + else { + return mapLand.getBiome(biomeX << 2, biomeZ << 2).getActualBiome(); + } + } + else { + pos.setLocation(biomeX, biomeZ); + if (endLandFunction.apply(pos)) { + return dist <= 65536L ? centerBiome : mapLand.getBiome(biomeX << 2, biomeZ << 2).getActualBiome(); + } + else { + return dist <= 65536L ? barrens : mapVoid.getBiome(biomeX << 2, biomeZ << 2).getActualBiome(); + } + } + } + + @Override + public BiomeSource withSeed(long seed) { + return new BCLibEndBiomeSource(biomeRegistry, seed); + } + + @Override + protected Codec codec() { + return CODEC; + } + + public static void register() { + Registry.register(Registry.BIOME_SOURCE, BCLib.makeID("better_end_biome_source"), CODEC); + } +} diff --git a/src/main/java/ru/bclib/world/generator/GeneratorOptions.java b/src/main/java/ru/bclib/world/generator/GeneratorOptions.java new file mode 100644 index 00000000..4eca70cc --- /dev/null +++ b/src/main/java/ru/bclib/world/generator/GeneratorOptions.java @@ -0,0 +1,40 @@ +package ru.bclib.world.generator; + +import net.minecraft.util.Mth; +import ru.bclib.config.Configs; + +import java.awt.Point; +import java.util.function.Function; + +public class GeneratorOptions { + private static int biomeSizeNether; + private static int biomeSizeEndLand; + private static int biomeSizeEndVoid; + private static Function endLandFunction; + + public static void init() { + biomeSizeNether = Configs.GENERATOR_CONFIG.getInt("nether.biomeMap", "biomeSize", 256); + biomeSizeEndLand = Configs.GENERATOR_CONFIG.getInt("end.biomeMap", "biomeSizeLand", 256); + biomeSizeEndVoid = Configs.GENERATOR_CONFIG.getInt("end.biomeMap", "biomeSizeVoid", 256); + } + + public static int getBiomeSizeNether() { + return Mth.clamp(biomeSizeNether, 1, 8192); + } + + public static int getBiomeSizeEndLand() { + return Mth.clamp(biomeSizeEndLand, 1, 8192); + } + + public static int getBiomeSizeEndVoid() { + return Mth.clamp(biomeSizeEndVoid, 1, 8192); + } + + public static void setEndLandFunction(Function endLandFunction) { + GeneratorOptions.endLandFunction = endLandFunction; + } + + public static Function getEndLandFunction() { + return endLandFunction; + } +}