End Biomes Api [1.16] Take Two (#1164)

* Create EndBiomes.java

* Revert "Create EndBiomes.java"

This reverts commit 4d1736fad9.

* Revert "Revert "Create EndBiomes.java""

This reverts commit 673c508d82.

* Rename ContinentalBiomeEntry to WeightedBiomeEntry

* Complete API for adding biomes to the end

* Mixin to end biome source

* Revert "Rename ContinentalBiomeEntry to WeightedBiomeEntry"

This reverts commit 849197e15d18c26af2a6f2aa1766b7d73d9a7144.

* Revert "Revert "Rename ContinentalBiomeEntry to WeightedBiomeEntry""

This reverts commit c2aa4ab097eb3daed71e82d4e77142671ed04316.

* Create SimpleLayerRandomnessSource for use with end biome source mixin

* Renamed MAIN region to MAIN_ISLANDS

* Create test mod

* Added pickFromNoise method to WeightedBiomePicker

* Javadoc and licensing info

* Comply with checkstyle

* Internalize EndRegion

* Added stretching when getting the biome for the end

* Rename EndBiomes to TheEndBiomes to be consistent with yarn mappings

* Fix typo

Courtesy of i509VBC

Co-authored-by: i509VCB <i509vcb@gmail.com>

* Add @Unique to private fields in MixinTheEndBiomeSource

* Made end test biome consistent in style with previous test

Courtesy of i509VBC

Co-authored-by: i509VCB <i509vcb@gmail.com>

* Fix imports

* Didn't know test mods needed to follow the checkstyle

* SimpleLayerRandomnessSource's random is now use to instantiate its noise sampler

* A very important note

* Update javadoc for adding biomes to the Main Island region

* Remove method that is leftover from when EndRegion was part of the API

* Ported 1.16.1 biomes-api-v1 to 1.16.2.
Public API changes:
- Removed flagging biomes as suitable for player spawns since that is now handled in the Biome Builder.
- Changed API over to RegistryKey's, not because it's necessary, but because it is more ergonomic since Vanilla Biomes in BuiltInBiomes are now all exposed as keys, rather than instances.

* Increase memory size to fix build failures.

* Add the ability to add modded dimensions to the regions of the end.

* Hopefully fixed everything that went wrong during the rebase

* Update fabric-biome-api-v1.mixins.json

* Finally got custom end biomes to generate

* Thanks for nothing, GitHub Desktop

* Bad GitHub Desktop

* Bad newline no cookie

* Got everything to follow the checkstyle

* Bring branch up-to-date with main repo (#11)

* Revert "Bring branch up-to-date with main repo (#11)"

This reverts commit dc47106241.

* Remove some stuff that's not supposed to be there

* Fix git silliness

* Update FabricBiomeTest.java

* TheEndBiomes is now Deprecated to match OverworldBiomes and NetherBiomes

* Update some checkNotNull messages

* Change the noise scale when replacing end biomes

* Bad space no cookie

* Remove unnecessary code

* Remove unused imports

* Set up InternalBiomeData to treat End Midlands and End Barrens as border biomes

* Changed the API to reflect midlands and barrens biomes being considered border biomes

* Start work on getting the new system fully working

* Finally got everything working and cleaned up the Javadoc

* Fixed checkstyle violations

* Fix checkstyle violations again

* Drop fabric_ prefix

* Calling SimpleLayerRandomnessSource#nextInt() now throws an exception

* If the midlands or barrens biome picker is null, the replacement key defaults to the vanilla one

* Fix usage of vanilla identifier

* Update MixinTheEndBiomeSource.java

* Fix checkstyle violations

Co-authored-by: i509VCB <i509vcb@gmail.com>
Co-authored-by: Sebastian Hartte <sebastian@hartte.de>
This commit is contained in:
RedstoneParadox 2020-11-15 11:21:24 -08:00 committed by GitHub
parent 6a2618f55c
commit 18f3e47f61
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 348 additions and 13 deletions

View file

@ -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() { }
/**
* <p>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.</p>
*
* @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> biome, double weight) {
InternalBiomeData.addEndBiomeReplacement(BiomeKeys.THE_END, biome, weight);
}
/**
* <p>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.</p>
*
* @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> biome, double weight) {
InternalBiomeData.addEndBiomeReplacement(BiomeKeys.END_HIGHLANDS, biome, weight);
}
/**
* <p>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.</p>
*
* @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> biome, double weight) {
InternalBiomeData.addEndBiomeReplacement(BiomeKeys.SMALL_END_ISLANDS, biome, weight);
}
/**
* <p>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.</p>
*
* @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<Biome> highlands, RegistryKey<Biome> midlands, double weight) {
InternalBiomeData.addEndMidlandsReplacement(highlands, midlands, weight);
}
/**
* <p>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.</p>
*
* @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<Biome> highlands, RegistryKey<Biome> barrens, double weight) {
InternalBiomeData.addEndBarrensReplacement(highlands, barrens, weight);
}
}

View file

@ -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<OverworldClimate, WeightedBiomePicker> OVERWORLD_MODDED_CONTINENTAL_BIOME_PICKERS = new EnumMap<>(OverworldClimate.class);
private static final Map<RegistryKey<Biome>, WeightedBiomePicker> OVERWORLD_HILLS_MAP = new HashMap<>();
private static final Map<RegistryKey<Biome>, WeightedBiomePicker> OVERWORLD_SHORE_MAP = new HashMap<>();
private static final Map<RegistryKey<Biome>, WeightedBiomePicker> OVERWORLD_EDGE_MAP = new HashMap<>();
private static final Map<RegistryKey<Biome>, VariantTransformer> OVERWORLD_VARIANT_TRANSFORMERS = new HashMap<>();
private static final Map<RegistryKey<Biome>, RegistryKey<Biome>> OVERWORLD_RIVER_MAP = new HashMap<>();
private static final Map<RegistryKey<Biome>, WeightedBiomePicker> OVERWORLD_HILLS_MAP = new IdentityHashMap<>();
private static final Map<RegistryKey<Biome>, WeightedBiomePicker> OVERWORLD_SHORE_MAP = new IdentityHashMap<>();
private static final Map<RegistryKey<Biome>, WeightedBiomePicker> OVERWORLD_EDGE_MAP = new IdentityHashMap<>();
private static final Map<RegistryKey<Biome>, VariantTransformer> OVERWORLD_VARIANT_TRANSFORMERS = new IdentityHashMap<>();
private static final Map<RegistryKey<Biome>, RegistryKey<Biome>> OVERWORLD_RIVER_MAP = new IdentityHashMap<>();
private static final Set<RegistryKey<Biome>> NETHER_BIOMES = new HashSet<>();
private static final Map<RegistryKey<Biome>, Biome.MixedNoisePoint> NETHER_BIOME_NOISE_POINTS = new HashMap<>();
private static final Map<RegistryKey<Biome>, WeightedBiomePicker> END_BIOMES_MAP = new IdentityHashMap<>();
private static final Map<RegistryKey<Biome>, WeightedBiomePicker> END_MIDLANDS_MAP = new IdentityHashMap<>();
private static final Map<RegistryKey<Biome>, 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> 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<Biome> replaced, RegistryKey<Biome> 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<Biome> highlands, RegistryKey<Biome> 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<Biome> highlands, RegistryKey<Biome> 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<RegistryKey<Biome>, WeightedBiomePicker> getOverworldHills() {
return OVERWORLD_HILLS_MAP;
}
@ -184,6 +219,18 @@ public final class InternalBiomeData {
return NETHER_BIOMES.contains(biome);
}
public static Map<RegistryKey<Biome>, WeightedBiomePicker> getEndBiomesMap() {
return END_BIOMES_MAP;
}
public static Map<RegistryKey<Biome>, WeightedBiomePicker> getEndMidlandsMap() {
return END_MIDLANDS_MAP;
}
public static Map<RegistryKey<Biome>, WeightedBiomePicker> getEndBarrensMap() {
return END_BARRENS_MAP;
}
private static class DefaultHillsData {
private static final ImmutableMap<RegistryKey<Biome>, RegistryKey<Biome>> DEFAULT_HILLS;

View file

@ -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<ContinentalBiomeEntry> moddedBiomes) {
public static int searchForBiome(double reqWeightSum, int vanillaArrayWeight, List<WeightedBiomeEntry> 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));
}

View file

@ -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;
}
}

View file

@ -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> 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> biome, final double weight, final double upperWeightBound) {
WeightedBiomeEntry(final RegistryKey<Biome> biome, final double weight, final double upperWeightBound) {
this.biome = biome;
this.weight = weight;
this.upperWeightBound = upperWeightBound;

View file

@ -30,7 +30,7 @@ import net.minecraft.world.biome.layer.util.LayerRandomnessSource;
*/
public final class WeightedBiomePicker {
private double currentTotal;
private List<ContinentalBiomeEntry> entries;
private final List<WeightedBiomeEntry> entries;
WeightedBiomePicker() {
currentTotal = 0;
@ -40,7 +40,7 @@ public final class WeightedBiomePicker {
void addBiome(final RegistryKey<Biome> 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<Biome> 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");

View file

@ -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<Biome> 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<Biome> cir) {
Biome vanillaBiome = cir.getReturnValue();
// Since all vanilla biomes are added to the registry, this will never fail.
RegistryKey<Biome> vanillaKey = biomeRegistry.getKey(vanillaBiome).get();
RegistryKey<Biome> 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<Biome> 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));
}
}

View file

@ -20,6 +20,7 @@
"MixinAddRiversLayer",
"MixinMultiNoiseBiomeSource",
"MixinSetBaseBiomesLayer",
"MixinTheEndBiomeSource",
"MultiNoiseBiomeSourceAccessor",
"VanillaLayeredBiomeSourceAccessor"
],

View file

@ -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;
/**
* <b>NOTES FOR TESTING:</b>
@ -47,8 +62,13 @@ public class FabricBiomeTest implements ModInitializer {
public static final String MOD_ID = "fabric-biome-api-v1-testmod";
private static final RegistryKey<Biome> TEST_CRIMSON_FOREST = RegistryKey.of(Registry.BIOME_KEY, new Identifier(MOD_ID, "test_crimson_forest"));
private static final RegistryKey<Biome> CUSTOM_PLAINS = RegistryKey.of(Registry.BIOME_KEY, new Identifier(MOD_ID, "custom_plains"));
private static final RegistryKey<Biome> TEST_END_HIGHLANDS = RegistryKey.of(Registry.BIOME_KEY, new Identifier(MOD_ID, "test_end_highlands"));
private static final RegistryKey<Biome> TEST_END_MIDLANDS = RegistryKey.of(Registry.BIOME_KEY, new Identifier(MOD_ID, "test_end_midlands"));
private static final RegistryKey<Biome> 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<TernarySurfaceConfig> 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 <SC extends SurfaceConfig> ConfiguredSurfaceBuilder<SC> registerTestSurfaceBuilder(Identifier id, ConfiguredSurfaceBuilder<SC> configuredSurfaceBuilder) {
return BuiltinRegistries.add(BuiltinRegistries.CONFIGURED_SURFACE_BUILDER, id, configuredSurfaceBuilder);
}
}