diff --git a/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/api/biome/v1/TheEndBiomes.java b/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/api/biome/v1/TheEndBiomes.java new file mode 100644 index 000000000..eb709e968 --- /dev/null +++ b/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/api/biome/v1/TheEndBiomes.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.api.biome.v1; + +import net.minecraft.util.registry.RegistryKey; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.BiomeKeys; + +import net.fabricmc.fabric.impl.biome.InternalBiomeData; + +/** + * API that exposes some internals of the minecraft default biome source for The End. + * + * @deprecated Experimental feature, may be removed or changed without further notice. + * Because of the volatility of world generation in Minecraft 1.16, this API is marked experimental + * since it is likely to change in future Minecraft versions. + */ +@Deprecated +public final class TheEndBiomes { + private TheEndBiomes() { } + + /** + *

Adds the biome as a main end island biome with the specified weight; note that this includes the main island + * and some of the land encircling the empty space. Note that adding a biome to this region could potentially mess + * with the generation of the center island and cause it to generate incorrectly; this method only exists for + * consistency.

+ * + * @param biome the biome to be added + * @param weight the weight of the entry. The weight in this method corresponds to its selection likelihood, with + * heavier biomes being more likely to be selected and lighter biomes being selected with less likelihood. + * Vanilla biomes have a weight of 1.0 + */ + public static void addMainIslandBiome(RegistryKey biome, double weight) { + InternalBiomeData.addEndBiomeReplacement(BiomeKeys.THE_END, biome, weight); + } + + /** + *

Adds the biome as an end highlands biome with the specified weight. End Highlands biomes make up the + * center region of the large outer islands in The End.

+ * + * @param biome the biome to be added + * @param weight the weight of the entry. The weight in this method corresponds to its selection likelihood, with + * heavier biomes being more likely to be selected and lighter biomes being selected with less likelihood. + * The vanilla biome has a weight of 1.0. + */ + public static void addHighlandsBiome(RegistryKey biome, double weight) { + InternalBiomeData.addEndBiomeReplacement(BiomeKeys.END_HIGHLANDS, biome, weight); + } + + /** + *

Adds a custom biome as a small end islands biome with the specified weight; small end island biomes + * make up the smaller islands in between the larger islands of the end.

+ * + * @param biome the biome to be added + * @param weight the weight of the entry. The weight in this method corresponds to its selection likelihood, with + * heavier biomes being more likely to be selected and lighter biomes being selected with less likelihood. + * The vanilla biome has a weight of 1.0. + */ + public static void addSmallIslandsBiome(RegistryKey biome, double weight) { + InternalBiomeData.addEndBiomeReplacement(BiomeKeys.SMALL_END_ISLANDS, biome, weight); + } + + /** + *

Adds the biome as an end midlands of the parent end highlands biome. End Midlands make up the area on + * the large outer islands between the highlands and the barrens and are similar to edge biomes in the + * overworld. If you don't call this method, the vanilla biome will be used by default.

+ * + * @param highlands The highlands biome to where the midlands biome is added + * @param midlands the biome to be added as a midlands biome + * @param weight the weight of the entry. The weight in this method corresponds to its selection likelihood, with + * heavier biomes being more likely to be selected and lighter biomes being selected with less likelihood. + * The vanilla biome has a weight of 1.0. + */ + public static void addMidlandsBiome(RegistryKey highlands, RegistryKey midlands, double weight) { + InternalBiomeData.addEndMidlandsReplacement(highlands, midlands, weight); + } + + /** + *

Adds the biome as an end barrens of the parent end highlands biome. End Midlands make up the area on + * the edge of the large outer islands and are similar to edge biomes in the overworld. If you don't call + * this method, the vanilla biome will be used by default.

+ * + * @param highlands The highlands biome to where the barrends biome is added + * @param barrens the biome to be added as a barrens biome + * @param weight the weight of the entry. The weight in this method corresponds to its selection likelihood, with + * heavier biomes being more likely to be selected and lighter biomes being selected with less likelihood. + * The vanilla biome has a weight of 1.0. + */ + public static void addBarrensBiome(RegistryKey highlands, RegistryKey barrens, double weight) { + InternalBiomeData.addEndBarrensReplacement(highlands, barrens, weight); + } +} diff --git a/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/impl/biome/InternalBiomeData.java b/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/impl/biome/InternalBiomeData.java index b6827bd81..4d696c210 100644 --- a/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/impl/biome/InternalBiomeData.java +++ b/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/impl/biome/InternalBiomeData.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; +import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -48,15 +49,28 @@ public final class InternalBiomeData { } private static final EnumMap OVERWORLD_MODDED_CONTINENTAL_BIOME_PICKERS = new EnumMap<>(OverworldClimate.class); - private static final Map, WeightedBiomePicker> OVERWORLD_HILLS_MAP = new HashMap<>(); - private static final Map, WeightedBiomePicker> OVERWORLD_SHORE_MAP = new HashMap<>(); - private static final Map, WeightedBiomePicker> OVERWORLD_EDGE_MAP = new HashMap<>(); - private static final Map, VariantTransformer> OVERWORLD_VARIANT_TRANSFORMERS = new HashMap<>(); - private static final Map, RegistryKey> OVERWORLD_RIVER_MAP = new HashMap<>(); + private static final Map, WeightedBiomePicker> OVERWORLD_HILLS_MAP = new IdentityHashMap<>(); + private static final Map, WeightedBiomePicker> OVERWORLD_SHORE_MAP = new IdentityHashMap<>(); + private static final Map, WeightedBiomePicker> OVERWORLD_EDGE_MAP = new IdentityHashMap<>(); + private static final Map, VariantTransformer> OVERWORLD_VARIANT_TRANSFORMERS = new IdentityHashMap<>(); + private static final Map, RegistryKey> OVERWORLD_RIVER_MAP = new IdentityHashMap<>(); private static final Set> NETHER_BIOMES = new HashSet<>(); private static final Map, Biome.MixedNoisePoint> NETHER_BIOME_NOISE_POINTS = new HashMap<>(); + private static final Map, WeightedBiomePicker> END_BIOMES_MAP = new IdentityHashMap<>(); + private static final Map, WeightedBiomePicker> END_MIDLANDS_MAP = new IdentityHashMap<>(); + private static final Map, WeightedBiomePicker> END_BARRENS_MAP = new IdentityHashMap<>(); + + static { + END_BIOMES_MAP.computeIfAbsent(BiomeKeys.THE_END, key -> new WeightedBiomePicker()).addBiome(BiomeKeys.THE_END, 1.0); + END_BIOMES_MAP.computeIfAbsent(BiomeKeys.END_HIGHLANDS, key -> new WeightedBiomePicker()).addBiome(BiomeKeys.END_HIGHLANDS, 1.0); + END_BIOMES_MAP.computeIfAbsent(BiomeKeys.SMALL_END_ISLANDS, key -> new WeightedBiomePicker()).addBiome(BiomeKeys.SMALL_END_ISLANDS, 1.0); + + END_MIDLANDS_MAP.computeIfAbsent(BiomeKeys.END_HIGHLANDS, key -> new WeightedBiomePicker()).addBiome(BiomeKeys.END_MIDLANDS, 1.0); + END_BARRENS_MAP.computeIfAbsent(BiomeKeys.END_HIGHLANDS, key -> new WeightedBiomePicker()).addBiome(BiomeKeys.END_BARRENS, 1.0); + } + public static void addOverworldContinentalBiome(OverworldClimate climate, RegistryKey biome, double weight) { Preconditions.checkArgument(climate != null, "Climate is null"); Preconditions.checkArgument(biome != null, "Biome is null"); @@ -144,6 +158,27 @@ public final class InternalBiomeData { NETHER_BIOMES.clear(); // Reset cached overall biome list } + public static void addEndBiomeReplacement(RegistryKey replaced, RegistryKey variant, double weight) { + Preconditions.checkNotNull(replaced, "replaced biome is null"); + Preconditions.checkNotNull(variant, "variant biome is null"); + Preconditions.checkArgument(weight > 0.0, "Weight is less than or equal to 0.0 (got %s)", weight); + END_BIOMES_MAP.computeIfAbsent(replaced, key -> new WeightedBiomePicker()).addBiome(variant, weight); + } + + public static void addEndMidlandsReplacement(RegistryKey highlands, RegistryKey midlands, double weight) { + Preconditions.checkNotNull(highlands, "highlands biome is null"); + Preconditions.checkNotNull(midlands, "midlands biome is null"); + Preconditions.checkArgument(weight > 0.0, "Weight is less than or equal to 0.0 (got %s)", weight); + END_MIDLANDS_MAP.computeIfAbsent(highlands, key -> new WeightedBiomePicker()).addBiome(midlands, weight); + } + + public static void addEndBarrensReplacement(RegistryKey highlands, RegistryKey barrens, double weight) { + Preconditions.checkNotNull(highlands, "highlands biome is null"); + Preconditions.checkNotNull(barrens, "midlands biome is null"); + Preconditions.checkArgument(weight > 0.0, "Weight is less than or equal to 0.0 (got %s)", weight); + END_BARRENS_MAP.computeIfAbsent(highlands, key -> new WeightedBiomePicker()).addBiome(barrens, weight); + } + public static Map, WeightedBiomePicker> getOverworldHills() { return OVERWORLD_HILLS_MAP; } @@ -184,6 +219,18 @@ public final class InternalBiomeData { return NETHER_BIOMES.contains(biome); } + public static Map, WeightedBiomePicker> getEndBiomesMap() { + return END_BIOMES_MAP; + } + + public static Map, WeightedBiomePicker> getEndMidlandsMap() { + return END_MIDLANDS_MAP; + } + + public static Map, WeightedBiomePicker> getEndBarrensMap() { + return END_BARRENS_MAP; + } + private static class DefaultHillsData { private static final ImmutableMap, RegistryKey> DEFAULT_HILLS; diff --git a/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/impl/biome/InternalBiomeUtils.java b/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/impl/biome/InternalBiomeUtils.java index 6cfb4c82f..da10da0a6 100644 --- a/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/impl/biome/InternalBiomeUtils.java +++ b/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/impl/biome/InternalBiomeUtils.java @@ -93,7 +93,7 @@ public final class InternalBiomeUtils { return biome != null && biome.getCategory() == Biome.Category.OCEAN; } - public static int searchForBiome(double reqWeightSum, int vanillaArrayWeight, List moddedBiomes) { + public static int searchForBiome(double reqWeightSum, int vanillaArrayWeight, List moddedBiomes) { reqWeightSum -= vanillaArrayWeight; int low = 0; int high = moddedBiomes.size() - 1; @@ -152,7 +152,7 @@ public final class InternalBiomeUtils { } else { // Modded biome; use a binary search, and then transform accordingly. - ContinentalBiomeEntry found = picker.search(reqWeightSum - vanillaArrayWeight); + WeightedBiomeEntry found = picker.search(reqWeightSum - vanillaArrayWeight); result.accept(transformBiome(random, found.getBiome(), climate)); } diff --git a/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/impl/biome/SimpleLayerRandomnessSource.java b/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/impl/biome/SimpleLayerRandomnessSource.java new file mode 100644 index 000000000..8d7925037 --- /dev/null +++ b/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/impl/biome/SimpleLayerRandomnessSource.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.impl.biome; + +import java.util.Random; + +import net.minecraft.util.math.noise.PerlinNoiseSampler; +import net.minecraft.world.biome.layer.util.LayerRandomnessSource; + +public class SimpleLayerRandomnessSource implements LayerRandomnessSource { + private final PerlinNoiseSampler sampler; + + public SimpleLayerRandomnessSource(long seed) { + Random random = new Random(seed); + this.sampler = new PerlinNoiseSampler(random); + } + + @Override + public int nextInt(int bound) { + throw new UnsupportedOperationException("SimpleLayerRandomnessSource does not support calling nextInt(int)."); + } + + @Override + public PerlinNoiseSampler getNoiseSampler() { + return sampler; + } +} diff --git a/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/impl/biome/ContinentalBiomeEntry.java b/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/impl/biome/WeightedBiomeEntry.java similarity index 90% rename from fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/impl/biome/ContinentalBiomeEntry.java rename to fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/impl/biome/WeightedBiomeEntry.java index 6fdf53317..b62a5464b 100644 --- a/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/impl/biome/ContinentalBiomeEntry.java +++ b/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/impl/biome/WeightedBiomeEntry.java @@ -22,7 +22,7 @@ import net.minecraft.world.biome.Biome; /** * Represents a biome and its corresponding weight. */ -final class ContinentalBiomeEntry { +final class WeightedBiomeEntry { private final RegistryKey biome; private final double weight; private final double upperWeightBound; @@ -32,7 +32,7 @@ final class ContinentalBiomeEntry { * @param weight how often a biome will be chosen * @param upperWeightBound the upper weight bound within the context of the other entries, used for the binary search */ - ContinentalBiomeEntry(final RegistryKey biome, final double weight, final double upperWeightBound) { + WeightedBiomeEntry(final RegistryKey biome, final double weight, final double upperWeightBound) { this.biome = biome; this.weight = weight; this.upperWeightBound = upperWeightBound; diff --git a/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/impl/biome/WeightedBiomePicker.java b/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/impl/biome/WeightedBiomePicker.java index 1a8ae9141..f212e8a31 100644 --- a/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/impl/biome/WeightedBiomePicker.java +++ b/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/impl/biome/WeightedBiomePicker.java @@ -30,7 +30,7 @@ import net.minecraft.world.biome.layer.util.LayerRandomnessSource; */ public final class WeightedBiomePicker { private double currentTotal; - private List entries; + private final List entries; WeightedBiomePicker() { currentTotal = 0; @@ -40,7 +40,7 @@ public final class WeightedBiomePicker { void addBiome(final RegistryKey biome, final double weight) { currentTotal += weight; - entries.add(new ContinentalBiomeEntry(biome, weight, currentTotal)); + entries.add(new WeightedBiomeEntry(biome, weight, currentTotal)); } double getCurrentWeightTotal() { @@ -53,13 +53,19 @@ public final class WeightedBiomePicker { return search(target).getBiome(); } + public RegistryKey pickFromNoise(LayerRandomnessSource source, double x, double y, double z) { + double target = Math.abs(source.getNoiseSampler().sample(x, y, z, 0.0, 0.0)) * getCurrentWeightTotal(); + + return search(target).getBiome(); + } + /** * Searches with the specified target value. * * @param target The target value, must satisfy the constraint 0 <= target <= currentTotal * @return The result of the search */ - ContinentalBiomeEntry search(final double target) { + WeightedBiomeEntry search(final double target) { // Sanity checks, fail fast if stuff is going wrong. Preconditions.checkArgument(target <= currentTotal, "The provided target value for biome selection must be less than or equal to the weight total"); Preconditions.checkArgument(target >= 0, "The provided target value for biome selection cannot be negative"); diff --git a/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/mixin/biome/MixinTheEndBiomeSource.java b/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/mixin/biome/MixinTheEndBiomeSource.java new file mode 100644 index 000000000..9722aacce --- /dev/null +++ b/fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/mixin/biome/MixinTheEndBiomeSource.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.biome; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import net.minecraft.world.biome.BiomeKeys; +import net.minecraft.world.biome.Biome; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.registry.RegistryKey; +import net.minecraft.world.biome.layer.util.LayerRandomnessSource; +import net.minecraft.world.biome.source.TheEndBiomeSource; + +import net.fabricmc.fabric.impl.biome.InternalBiomeData; +import net.fabricmc.fabric.impl.biome.SimpleLayerRandomnessSource; +import net.fabricmc.fabric.impl.biome.WeightedBiomePicker; + +@Mixin(TheEndBiomeSource.class) +public class MixinTheEndBiomeSource { + @Shadow + @Final + private Registry biomeRegistry; + @Shadow + @Final + private long seed; + @Unique + private LayerRandomnessSource randomnessSource = new SimpleLayerRandomnessSource(seed); + + @Inject(method = "getBiomeForNoiseGen", at = @At("RETURN"), cancellable = true) + private void getWeightedEndBiome(int biomeX, int biomeY, int biomeZ, CallbackInfoReturnable cir) { + Biome vanillaBiome = cir.getReturnValue(); + + // Since all vanilla biomes are added to the registry, this will never fail. + RegistryKey vanillaKey = biomeRegistry.getKey(vanillaBiome).get(); + RegistryKey replacementKey; + + // The x and z of the biome are divided by 64 to ensure custom biomes are large enough; going larger than this] + // seems to make custom biomes too hard to find. + if (vanillaKey == BiomeKeys.END_MIDLANDS || vanillaKey == BiomeKeys.END_BARRENS) { + // Since the highlands picker is statically populated by InternalBiomeData, picker will never be null. + WeightedBiomePicker highlandsPicker = InternalBiomeData.getEndBiomesMap().get(BiomeKeys.END_HIGHLANDS); + RegistryKey highlandsKey = highlandsPicker.pickFromNoise(randomnessSource, biomeX/64.0, 0, biomeZ/64.0); + + if (vanillaKey == BiomeKeys.END_MIDLANDS) { + WeightedBiomePicker midlandsPicker = InternalBiomeData.getEndMidlandsMap().get(highlandsKey); + replacementKey = (midlandsPicker == null) ? vanillaKey : midlandsPicker.pickFromNoise(randomnessSource, biomeX/64.0, 0, biomeZ/64.0); + } else { + WeightedBiomePicker barrensPicker = InternalBiomeData.getEndBarrensMap().get(highlandsKey); + replacementKey = (barrensPicker == null) ? vanillaKey : barrensPicker.pickFromNoise(randomnessSource, biomeX/64.0, 0, biomeZ/64.0); + } + } else { + // Since the main island and small islands pickers are statically populated by InternalBiomeData, picker will never be null. + WeightedBiomePicker picker = InternalBiomeData.getEndBiomesMap().get(vanillaKey); + replacementKey = picker.pickFromNoise(randomnessSource, biomeX/64.0, 0, biomeZ/64.0); + } + + cir.setReturnValue(biomeRegistry.get(replacementKey)); + } +} diff --git a/fabric-biome-api-v1/src/main/resources/fabric-biome-api-v1.mixins.json b/fabric-biome-api-v1/src/main/resources/fabric-biome-api-v1.mixins.json index 8c6825455..fb64bfffa 100644 --- a/fabric-biome-api-v1/src/main/resources/fabric-biome-api-v1.mixins.json +++ b/fabric-biome-api-v1/src/main/resources/fabric-biome-api-v1.mixins.json @@ -20,6 +20,7 @@ "MixinAddRiversLayer", "MixinMultiNoiseBiomeSource", "MixinSetBaseBiomesLayer", + "MixinTheEndBiomeSource", "MultiNoiseBiomeSourceAccessor", "VanillaLayeredBiomeSourceAccessor" ], diff --git a/fabric-biome-api-v1/src/testmod/java/net/fabricmc/fabric/test/biome/FabricBiomeTest.java b/fabric-biome-api-v1/src/testmod/java/net/fabricmc/fabric/test/biome/FabricBiomeTest.java index e26ce65d8..9be893ea1 100644 --- a/fabric-biome-api-v1/src/testmod/java/net/fabricmc/fabric/test/biome/FabricBiomeTest.java +++ b/fabric-biome-api-v1/src/testmod/java/net/fabricmc/fabric/test/biome/FabricBiomeTest.java @@ -16,14 +16,28 @@ package net.fabricmc.fabric.test.biome; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.sound.BiomeMoodSound; import net.minecraft.util.Identifier; import net.minecraft.util.registry.BuiltinRegistries; import net.minecraft.util.registry.Registry; import net.minecraft.util.registry.RegistryKey; import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.BiomeEffects; import net.minecraft.world.biome.BiomeKeys; import net.minecraft.world.biome.DefaultBiomeCreator; +import net.minecraft.world.biome.GenerationSettings; +import net.minecraft.world.biome.SpawnSettings; +import net.minecraft.world.gen.GenerationStep; +import net.minecraft.world.gen.feature.ConfiguredFeatures; +import net.minecraft.world.gen.feature.ConfiguredStructureFeatures; +import net.minecraft.world.gen.feature.DefaultBiomeFeatures; import net.minecraft.world.gen.surfacebuilder.ConfiguredSurfaceBuilders; +import net.minecraft.world.gen.surfacebuilder.ConfiguredSurfaceBuilder; +import net.minecraft.world.gen.surfacebuilder.SurfaceBuilder; +import net.minecraft.world.gen.surfacebuilder.SurfaceConfig; +import net.minecraft.world.gen.surfacebuilder.TernarySurfaceConfig; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.biome.v1.BiomeModifications; @@ -32,6 +46,7 @@ import net.fabricmc.fabric.api.biome.v1.ModificationPhase; import net.fabricmc.fabric.api.biome.v1.NetherBiomes; import net.fabricmc.fabric.api.biome.v1.OverworldBiomes; import net.fabricmc.fabric.api.biome.v1.OverworldClimate; +import net.fabricmc.fabric.api.biome.v1.TheEndBiomes; /** * NOTES FOR TESTING: @@ -47,8 +62,13 @@ public class FabricBiomeTest implements ModInitializer { public static final String MOD_ID = "fabric-biome-api-v1-testmod"; private static final RegistryKey TEST_CRIMSON_FOREST = RegistryKey.of(Registry.BIOME_KEY, new Identifier(MOD_ID, "test_crimson_forest")); - private static final RegistryKey CUSTOM_PLAINS = RegistryKey.of(Registry.BIOME_KEY, new Identifier(MOD_ID, "custom_plains")); + private static final RegistryKey TEST_END_HIGHLANDS = RegistryKey.of(Registry.BIOME_KEY, new Identifier(MOD_ID, "test_end_highlands")); + private static final RegistryKey TEST_END_MIDLANDS = RegistryKey.of(Registry.BIOME_KEY, new Identifier(MOD_ID, "test_end_midlands")); + private static final RegistryKey TEST_END_BARRRENS = RegistryKey.of(Registry.BIOME_KEY, new Identifier(MOD_ID, "test_end_barrens")); + + private static BlockState STONE = Blocks.STONE.getDefaultState(); + private static ConfiguredSurfaceBuilder TEST_END_SURFACE_BUILDER = registerTestSurfaceBuilder(new Identifier(MOD_ID, "end"), SurfaceBuilder.DEFAULT.withConfig(new TernarySurfaceConfig(STONE, STONE, STONE))); @Override public void onInitialize() { @@ -60,6 +80,15 @@ public class FabricBiomeTest implements ModInitializer { Registry.register(BuiltinRegistries.BIOME, CUSTOM_PLAINS.getValue(), DefaultBiomeCreator.createPlains(false)); OverworldBiomes.addBiomeVariant(BiomeKeys.PLAINS, CUSTOM_PLAINS, 1); + Registry.register(BuiltinRegistries.BIOME, TEST_END_HIGHLANDS.getValue(), createEndHighlands()); + Registry.register(BuiltinRegistries.BIOME, TEST_END_MIDLANDS.getValue(), createEndMidlands()); + Registry.register(BuiltinRegistries.BIOME, TEST_END_BARRRENS.getValue(), createEndBarrens()); + // TESTING HINT: to get to the end: + // /execute in minecraft:the_end run tp @s 0 90 0 + TheEndBiomes.addHighlandsBiome(TEST_END_HIGHLANDS, 5.0); + TheEndBiomes.addMidlandsBiome(TEST_END_HIGHLANDS, TEST_END_MIDLANDS, 1.0); + TheEndBiomes.addBarrensBiome(TEST_END_HIGHLANDS, TEST_END_BARRRENS, 1.0); + OverworldBiomes.addEdgeBiome(BiomeKeys.PLAINS, BiomeKeys.END_BARRENS, 0.9); OverworldBiomes.addShoreBiome(BiomeKeys.FOREST, BiomeKeys.NETHER_WASTES, 0.9); @@ -80,4 +109,30 @@ public class FabricBiomeTest implements ModInitializer { context.getGenerationSettings().setBuiltInSurfaceBuilder(ConfiguredSurfaceBuilders.CRIMSON_FOREST); }); } + + // These are used for testing the spacing of custom end biomes. + private static Biome createEndHighlands() { + GenerationSettings.Builder builder = (new GenerationSettings.Builder()).surfaceBuilder(TEST_END_SURFACE_BUILDER).structureFeature(ConfiguredStructureFeatures.END_CITY).feature(GenerationStep.Feature.SURFACE_STRUCTURES, ConfiguredFeatures.END_GATEWAY).feature(GenerationStep.Feature.VEGETAL_DECORATION, ConfiguredFeatures.CHORUS_PLANT); + return composeEndSpawnSettings(builder); + } + + public static Biome createEndMidlands() { + GenerationSettings.Builder builder = (new GenerationSettings.Builder()).surfaceBuilder(TEST_END_SURFACE_BUILDER).structureFeature(ConfiguredStructureFeatures.END_CITY); + return composeEndSpawnSettings(builder); + } + + public static Biome createEndBarrens() { + GenerationSettings.Builder builder = (new GenerationSettings.Builder()).surfaceBuilder(TEST_END_SURFACE_BUILDER); + return composeEndSpawnSettings(builder); + } + + private static Biome composeEndSpawnSettings(GenerationSettings.Builder builder) { + SpawnSettings.Builder builder2 = new SpawnSettings.Builder(); + DefaultBiomeFeatures.addEndMobs(builder2); + return (new Biome.Builder()).precipitation(Biome.Precipitation.NONE).category(Biome.Category.THEEND).depth(0.1F).scale(0.2F).temperature(0.5F).downfall(0.5F).effects((new BiomeEffects.Builder()).waterColor(4159204).waterFogColor(329011).fogColor(10518688).skyColor(0).moodSound(BiomeMoodSound.CAVE).build()).spawnSettings(builder2.build()).generationSettings(builder.build()).build(); + } + + private static ConfiguredSurfaceBuilder registerTestSurfaceBuilder(Identifier id, ConfiguredSurfaceBuilder configuredSurfaceBuilder) { + return BuiltinRegistries.add(BuiltinRegistries.CONFIGURED_SURFACE_BUILDER, id, configuredSurfaceBuilder); + } }