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);
+ }
}