Ported 1.16.1 biomes-api-v1 to 1.16.2 ()

* 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.

* Changed to CheaterCode's mixin for modifying the nether biomes, saving us one accessor.

* Move Bonanza

* Finished moving and updating of the module, added experimental markers.

* Adjusted experimental notes.

* Finished rename from biomes-v1 to biome-api-v1

* Reverted memory settings in preparation for loom-0.5
This commit is contained in:
shartte 2020-09-18 19:11:51 +02:00 committed by GitHub
parent 7490af87a1
commit c29459abaf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 556 additions and 620 deletions

View file

@ -73,7 +73,7 @@ allprojects {
dependencies {
minecraft "com.mojang:minecraft:$Globals.mcVersion"
mappings "net.fabricmc:yarn:${Globals.mcVersion}${Globals.yarnVersion}:v2"
modCompile "net.fabricmc:fabric-loader:0.8.9+build.203"
modCompile "net.fabricmc:fabric-loader:0.9.2+build.206"
}
configurations {

View file

@ -0,0 +1,2 @@
archivesBaseName = "fabric-biome-api-v1"
version = getSubprojectVersion(project, "2.0.0")

View file

@ -14,26 +14,33 @@
* limitations under the License.
*/
package net.fabricmc.fabric.api.biomes.v1;
package net.fabricmc.fabric.api.biome.v1;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.biome.Biome;
import net.fabricmc.fabric.impl.biome.InternalBiomeData;
/**
* API that exposes the internals of Minecraft's nether biome code.
*
* @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 NetherBiomes {
private NetherBiomes() { }
private NetherBiomes() {
}
/**
* Adds a biome to the Nether generator.
*
* @param biome The biome to add. Must not be null.
* @param biome The biome to add. Must not be null.
* @param mixedNoisePoint data about the given {@link Biome}'s spawning information in the nether.
* @see Biome.MixedNoisePoint
*/
public static void addNetherBiome(Biome biome, Biome.MixedNoisePoint mixedNoisePoint) {
public static void addNetherBiome(RegistryKey<Biome> biome, Biome.MixedNoisePoint mixedNoisePoint) {
InternalBiomeData.addNetherBiome(biome, mixedNoisePoint);
}
}

View file

@ -14,28 +14,35 @@
* limitations under the License.
*/
package net.fabricmc.fabric.api.biomes.v1;
package net.fabricmc.fabric.api.biome.v1;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.biome.Biome;
import net.fabricmc.fabric.impl.biome.InternalBiomeData;
/**
* API that exposes some internals of the minecraft default biome source for the overworld.
*
* @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 OverworldBiomes {
private OverworldBiomes() { }
private OverworldBiomes() {
}
/**
* Adds the biome to the specified climate group, with the specified weight. This is only for the biomes that make up the initial continents in generation.
*
* @param biome the biome to be added
* @param biome the biome to be added
* @param climate the climate group whereto the biome is 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.
* @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.
* @see OverworldClimate for a list of vanilla biome weights
*/
public static void addContinentalBiome(Biome biome, OverworldClimate climate, double weight) {
public static void addContinentalBiome(RegistryKey<Biome> biome, OverworldClimate climate, double weight) {
InternalBiomeData.addOverworldContinentalBiome(climate, biome, weight);
}
@ -43,12 +50,12 @@ public final class OverworldBiomes {
* Adds the biome as a hills variant of the parent biome, with the specified weight.
*
* @param parent the biome to where the hills variant is added
* @param hills the biome to be set as a hills variant
* @param hills the biome to be set as a hills variant
* @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.
* Mods should use 1.0 as the default/normal weight.
* heavier biomes being more likely to be selected and lighter biomes being selected with less likelihood.
* Mods should use 1.0 as the default/normal weight.
*/
public static void addHillsBiome(Biome parent, Biome hills, double weight) {
public static void addHillsBiome(RegistryKey<Biome> parent, RegistryKey<Biome> hills, double weight) {
InternalBiomeData.addOverworldHillsBiome(parent, hills, weight);
}
@ -56,12 +63,12 @@ public final class OverworldBiomes {
* Adds the biome as a shore/beach biome for the parent biome, with the specified weight.
*
* @param parent the base biome to where the shore biome is added
* @param shore the biome to be added as a shore biome
* @param shore the biome to be added as a shore 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.
* Mods should use 1.0 as the default/normal weight.
* heavier biomes being more likely to be selected and lighter biomes being selected with less likelihood.
* Mods should use 1.0 as the default/normal weight.
*/
public static void addShoreBiome(Biome parent, Biome shore, double weight) {
public static void addShoreBiome(RegistryKey<Biome> parent, RegistryKey<Biome> shore, double weight) {
InternalBiomeData.addOverworldShoreBiome(parent, shore, weight);
}
@ -69,12 +76,12 @@ public final class OverworldBiomes {
* Adds the biome as an an edge biome (excluding as a beach) of the parent biome, with the specified weight.
*
* @param parent the base biome to where the edge biome is added
* @param edge the biome to be added as an edge biome
* @param edge the biome to be added as an edge 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.
* Mods should use 1.0 as the default/normal weight.
* heavier biomes being more likely to be selected and lighter biomes being selected with less likelihood.
* Mods should use 1.0 as the default/normal weight.
*/
public static void addEdgeBiome(Biome parent, Biome edge, double weight) {
public static void addEdgeBiome(RegistryKey<Biome> parent, RegistryKey<Biome> edge, double weight) {
InternalBiomeData.addOverworldEdgeBiome(parent, edge, weight);
}
@ -89,11 +96,11 @@ public final class OverworldBiomes {
* any climate assigned at this point in the generation.
*
* @param replaced the base biome that is replaced by a variant
* @param variant the biome to be added as a variant
* @param chance the chance of replacement of the biome into the variant
* @param variant the biome to be added as a variant
* @param chance the chance of replacement of the biome into the variant
* @param climates the climates in which the variants will occur in (none listed = add variant to all climates)
*/
public static void addBiomeVariant(Biome replaced, Biome variant, double chance, OverworldClimate... climates) {
public static void addBiomeVariant(RegistryKey<Biome> replaced, RegistryKey<Biome> variant, double chance, OverworldClimate... climates) {
InternalBiomeData.addOverworldBiomeReplacement(replaced, variant, chance, climates);
}
@ -102,9 +109,9 @@ public final class OverworldBiomes {
* generate in this biome.
*
* @param parent the base biome in which the river biome is to be set
* @param river the river biome for this biome
* @param river the river biome for this biome
*/
public static void setRiverBiome(Biome parent, Biome river) {
public static void setRiverBiome(RegistryKey<Biome> parent, RegistryKey<Biome> river) {
InternalBiomeData.setOverworldRiverBiome(parent, river);
}
}

View file

@ -14,11 +14,16 @@
* limitations under the License.
*/
package net.fabricmc.fabric.api.biomes.v1;
package net.fabricmc.fabric.api.biome.v1;
/**
* Represents the climates of biomes on the overworld continents.
*
* @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 enum OverworldClimate {
/**
* Includes Snowy Tundra (with a weight of 3) and Snowy Taiga (with a weight of 1).

View file

@ -16,20 +16,21 @@
package net.fabricmc.fabric.impl.biome;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.biome.Biome;
/**
* Represents a biome variant and its corresponding chance.
*/
final class BiomeVariant {
private final Biome variant;
private final RegistryKey<Biome> variant;
private final double chance;
/**
* @param variant the variant biome
* @param chance the chance of replacement of the biome into the variant
*/
BiomeVariant(final Biome variant, final double chance) {
BiomeVariant(final RegistryKey<Biome> variant, final double chance) {
this.variant = variant;
this.chance = chance;
}
@ -37,7 +38,7 @@ final class BiomeVariant {
/**
* @return the variant biome
*/
Biome getVariant() {
RegistryKey<Biome> getVariant() {
return variant;
}

View file

@ -16,13 +16,14 @@
package net.fabricmc.fabric.impl.biome;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.biome.Biome;
/**
* Represents a biome and its corresponding weight.
*/
final class ContinentalBiomeEntry {
private final Biome biome;
private final RegistryKey<Biome> biome;
private final double weight;
private final double upperWeightBound;
@ -31,13 +32,13 @@ 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 Biome biome, final double weight, final double upperWeightBound) {
ContinentalBiomeEntry(final RegistryKey<Biome> biome, final double weight, final double upperWeightBound) {
this.biome = biome;
this.weight = weight;
this.upperWeightBound = upperWeightBound;
}
Biome getBiome() {
RegistryKey<Biome> getBiome() {
return biome;
}

View file

@ -0,0 +1,223 @@
/*
* 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.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeKeys;
import net.minecraft.world.biome.layer.BiomeLayers;
import net.minecraft.world.biome.source.VanillaLayeredBiomeSource;
import net.minecraft.world.gen.feature.StructureFeature;
import net.fabricmc.fabric.api.biome.v1.OverworldClimate;
import net.fabricmc.fabric.mixin.biome.VanillaLayeredBiomeSourceAccessor;
/**
* Lists and maps for internal use only! Stores data that is used by the various mixins into the world generation
*/
public final class InternalBiomeData {
private 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 Set<RegistryKey<Biome>> NETHER_BIOMES = new HashSet<>();
private static final Map<RegistryKey<Biome>, Biome.MixedNoisePoint> NETHER_BIOME_NOISE_POINTS = new HashMap<>();
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");
Preconditions.checkArgument(!Double.isNaN(weight), "Weight is NaN");
Preconditions.checkArgument(weight > 0.0, "Weight is less than or equal to 0.0 (%s)", weight);
InternalBiomeUtils.ensureIdMapping(biome);
OVERWORLD_MODDED_CONTINENTAL_BIOME_PICKERS.computeIfAbsent(climate, k -> new WeightedBiomePicker()).addBiome(biome, weight);
injectOverworldBiome(biome);
}
public static void addOverworldHillsBiome(RegistryKey<Biome> primary, RegistryKey<Biome> hills, double weight) {
Preconditions.checkArgument(primary != null, "Primary biome is null");
Preconditions.checkArgument(hills != null, "Hills biome is null");
Preconditions.checkArgument(!Double.isNaN(weight), "Weight is NaN");
Preconditions.checkArgument(weight > 0.0, "Weight is less than or equal to 0.0 (%s)", weight);
InternalBiomeUtils.ensureIdMapping(primary);
InternalBiomeUtils.ensureIdMapping(hills);
OVERWORLD_HILLS_MAP.computeIfAbsent(primary, biome -> DefaultHillsData.injectDefaultHills(primary, new WeightedBiomePicker())).addBiome(hills, weight);
injectOverworldBiome(hills);
}
public static void addOverworldShoreBiome(RegistryKey<Biome> primary, RegistryKey<Biome> shore, double weight) {
Preconditions.checkArgument(primary != null, "Primary biome is null");
Preconditions.checkArgument(shore != null, "Shore biome is null");
Preconditions.checkArgument(!Double.isNaN(weight), "Weight is NaN");
Preconditions.checkArgument(weight > 0.0, "Weight is less than or equal to 0.0 (%s)", weight);
InternalBiomeUtils.ensureIdMapping(primary);
InternalBiomeUtils.ensureIdMapping(shore);
OVERWORLD_SHORE_MAP.computeIfAbsent(primary, biome -> new WeightedBiomePicker()).addBiome(shore, weight);
injectOverworldBiome(shore);
}
public static void addOverworldEdgeBiome(RegistryKey<Biome> primary, RegistryKey<Biome> edge, double weight) {
Preconditions.checkArgument(primary != null, "Primary biome is null");
Preconditions.checkArgument(edge != null, "Edge biome is null");
Preconditions.checkArgument(!Double.isNaN(weight), "Weight is NaN");
Preconditions.checkArgument(weight > 0.0, "Weight is less than or equal to 0.0 (%s)", weight);
InternalBiomeUtils.ensureIdMapping(primary);
InternalBiomeUtils.ensureIdMapping(edge);
OVERWORLD_EDGE_MAP.computeIfAbsent(primary, biome -> new WeightedBiomePicker()).addBiome(edge, weight);
injectOverworldBiome(edge);
}
public static void addOverworldBiomeReplacement(RegistryKey<Biome> replaced, RegistryKey<Biome> variant, double chance, OverworldClimate[] climates) {
Preconditions.checkArgument(replaced != null, "Replaced biome is null");
Preconditions.checkArgument(variant != null, "Variant biome is null");
Preconditions.checkArgument(chance > 0 && chance <= 1, "Chance is not greater than 0 or less than or equal to 1");
InternalBiomeUtils.ensureIdMapping(replaced);
InternalBiomeUtils.ensureIdMapping(variant);
OVERWORLD_VARIANT_TRANSFORMERS.computeIfAbsent(replaced, biome -> new VariantTransformer()).addBiome(variant, chance, climates);
injectOverworldBiome(variant);
}
public static void setOverworldRiverBiome(RegistryKey<Biome> primary, RegistryKey<Biome> river) {
Preconditions.checkArgument(primary != null, "Primary biome is null");
InternalBiomeUtils.ensureIdMapping(primary);
InternalBiomeUtils.ensureIdMapping(river);
OVERWORLD_RIVER_MAP.put(primary, river);
if (river != null) {
injectOverworldBiome(river);
}
}
/**
* Adds the biomes in world gen to the array for the vanilla layered biome source.
* This helps with {@link VanillaLayeredBiomeSource#hasStructureFeature(StructureFeature)} returning correctly for modded biomes as well as in {@link VanillaLayeredBiomeSource#getTopMaterials()}}
*/
private static void injectOverworldBiome(RegistryKey<Biome> biome) {
List<RegistryKey<Biome>> biomes = VanillaLayeredBiomeSourceAccessor.getBIOMES();
if (biomes instanceof ImmutableList) {
biomes = new ArrayList<>(biomes);
VanillaLayeredBiomeSourceAccessor.setBIOMES(biomes);
}
biomes.add(biome);
}
public static void addNetherBiome(RegistryKey<Biome> biome, Biome.MixedNoisePoint spawnNoisePoint) {
Preconditions.checkArgument(biome != null, "Biome is null");
Preconditions.checkArgument(spawnNoisePoint != null, "Biome.MixedNoisePoint is null");
InternalBiomeUtils.ensureIdMapping(biome);
NETHER_BIOMES.add(biome);
NETHER_BIOME_NOISE_POINTS.put(biome, spawnNoisePoint);
}
public static Map<RegistryKey<Biome>, WeightedBiomePicker> getOverworldHills() {
return OVERWORLD_HILLS_MAP;
}
public static Map<RegistryKey<Biome>, WeightedBiomePicker> getOverworldShores() {
return OVERWORLD_SHORE_MAP;
}
public static Map<RegistryKey<Biome>, WeightedBiomePicker> getOverworldEdges() {
return OVERWORLD_EDGE_MAP;
}
public static Map<RegistryKey<Biome>, RegistryKey<Biome>> getOverworldRivers() {
return OVERWORLD_RIVER_MAP;
}
public static EnumMap<OverworldClimate, WeightedBiomePicker> getOverworldModdedContinentalBiomePickers() {
return OVERWORLD_MODDED_CONTINENTAL_BIOME_PICKERS;
}
public static Map<RegistryKey<Biome>, VariantTransformer> getOverworldVariantTransformers() {
return OVERWORLD_VARIANT_TRANSFORMERS;
}
public static Map<RegistryKey<Biome>, Biome.MixedNoisePoint> getNetherBiomeNoisePoints() {
return NETHER_BIOME_NOISE_POINTS;
}
private static class DefaultHillsData {
private static final ImmutableMap<RegistryKey<Biome>, RegistryKey<Biome>> DEFAULT_HILLS;
static WeightedBiomePicker injectDefaultHills(RegistryKey<Biome> base, WeightedBiomePicker picker) {
RegistryKey<Biome> defaultHill = DEFAULT_HILLS.get(base);
if (defaultHill != null) {
picker.addBiome(defaultHill, 1);
} else if (BiomeLayers.areSimilar(InternalBiomeUtils.getRawId(base), InternalBiomeUtils.getRawId(BiomeKeys.WOODED_BADLANDS_PLATEAU))) {
picker.addBiome(BiomeKeys.BADLANDS, 1);
} else if (base == BiomeKeys.DEEP_OCEAN || base == BiomeKeys.DEEP_LUKEWARM_OCEAN || base == BiomeKeys.DEEP_COLD_OCEAN) {
picker.addBiome(BiomeKeys.PLAINS, 1);
picker.addBiome(BiomeKeys.FOREST, 1);
} else if (base == BiomeKeys.DEEP_FROZEN_OCEAN) {
// Note: Vanilla Deep Frozen Oceans only have a 1/3 chance of having default hills.
// This is a clever trick that ensures that when a mod adds hills with a weight of 1, the 1/3 chance is fulfilled.
// 0.5 + 1.0 = 1.5, and 0.5 / 1.5 = 1/3.
picker.addBiome(BiomeKeys.PLAINS, 0.25);
picker.addBiome(BiomeKeys.FOREST, 0.25);
} else if (base == BiomeKeys.PLAINS) {
picker.addBiome(BiomeKeys.WOODED_HILLS, 1);
picker.addBiome(BiomeKeys.FOREST, 2);
}
return picker;
}
static {
// This map mirrors the hardcoded logic in AddHillsLayer#sample
ImmutableMap.Builder<RegistryKey<Biome>, RegistryKey<Biome>> builder = ImmutableMap.builder();
builder.put(BiomeKeys.DESERT, BiomeKeys.DESERT_HILLS);
builder.put(BiomeKeys.FOREST, BiomeKeys.WOODED_HILLS);
builder.put(BiomeKeys.BIRCH_FOREST, BiomeKeys.BIRCH_FOREST_HILLS);
builder.put(BiomeKeys.DARK_FOREST, BiomeKeys.PLAINS);
builder.put(BiomeKeys.TAIGA, BiomeKeys.TAIGA_HILLS);
builder.put(BiomeKeys.GIANT_TREE_TAIGA, BiomeKeys.GIANT_TREE_TAIGA_HILLS);
builder.put(BiomeKeys.SNOWY_TAIGA, BiomeKeys.SNOWY_TAIGA_HILLS);
builder.put(BiomeKeys.SNOWY_TUNDRA, BiomeKeys.SNOWY_MOUNTAINS);
builder.put(BiomeKeys.JUNGLE, BiomeKeys.JUNGLE_HILLS);
builder.put(BiomeKeys.BAMBOO_JUNGLE, BiomeKeys.BAMBOO_JUNGLE_HILLS);
builder.put(BiomeKeys.OCEAN, BiomeKeys.DEEP_OCEAN);
builder.put(BiomeKeys.LUKEWARM_OCEAN, BiomeKeys.DEEP_LUKEWARM_OCEAN);
builder.put(BiomeKeys.COLD_OCEAN, BiomeKeys.DEEP_COLD_OCEAN);
builder.put(BiomeKeys.FROZEN_OCEAN, BiomeKeys.DEEP_FROZEN_OCEAN);
builder.put(BiomeKeys.MOUNTAINS, BiomeKeys.WOODED_MOUNTAINS);
builder.put(BiomeKeys.SAVANNA, BiomeKeys.SAVANNA_PLATEAU);
DEFAULT_HILLS = builder.build();
}
}
}

View file

@ -20,28 +20,35 @@ import java.util.List;
import java.util.Map;
import java.util.function.IntConsumer;
import net.minecraft.util.registry.BuiltinRegistries;
import net.minecraft.util.Identifier;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.layer.util.LayerRandomnessSource;
import net.minecraft.world.dimension.DimensionOptions;
import net.minecraft.world.gen.GeneratorOptions;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.fabricmc.fabric.api.biomes.v1.OverworldClimate;
import net.fabricmc.fabric.mixin.biome.DimensionOptionsAccessor;
import net.fabricmc.fabric.mixin.biome.DimensionTypeAccessor;
import net.minecraft.util.registry.BuiltinRegistries;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BuiltinBiomes;
import net.minecraft.world.biome.layer.util.LayerRandomnessSource;
import net.fabricmc.fabric.api.biome.v1.OverworldClimate;
import net.fabricmc.fabric.mixin.biome.AddHillsLayerAccessor;
import net.fabricmc.fabric.mixin.biome.BuiltinBiomesAccessor;
/**
* Internal utilities used for biome sampling.
*/
public final class InternalBiomeUtils {
private InternalBiomeUtils() { }
private static final Logger LOGGER = LogManager.getLogger();
private InternalBiomeUtils() {
}
/**
* @param north raw id of the biome to the north
* @param east raw id of the biome to the east
* @param south raw id of the biome to the south
* @param west raw id of the biome to the west
* @param north raw id of the biome to the north
* @param east raw id of the biome to the east
* @param south raw id of the biome to the south
* @param west raw id of the biome to the west
* @param center central biome that comparisons are relative to
* @return whether the central biome is an edge of a biome
*/
@ -50,7 +57,7 @@ public final class InternalBiomeUtils {
}
/**
* @param mainBiomeId the main raw biome id in comparison
* @param mainBiomeId the main raw biome id in comparison
* @param secondaryBiomeId the secondary raw biome id in comparison
* @return whether the two biomes are unsimilar
*/
@ -58,21 +65,23 @@ public final class InternalBiomeUtils {
if (mainBiomeId == secondaryBiomeId) { // for efficiency, determine if the ids are equal first
return false;
} else {
Biome secondaryBiome = BuiltinRegistries.BIOME.get(secondaryBiomeId);
Biome mainBiome = BuiltinRegistries.BIOME.get(mainBiomeId);
// Regard a biome as "similar" to it's derived biome, i.e.
// No edge between plains and sunflower plains
boolean isUnsimilar = secondaryBiome.hasParent() ? !(mainBiomeId == BuiltinRegistries.BIOME.getRawId(BuiltinRegistries.BIOME.get(new Identifier(secondaryBiome.getParent())))) : true;
isUnsimilar = isUnsimilar && (mainBiome.hasParent() ? !(secondaryBiomeId == BuiltinRegistries.BIOME.getRawId(BuiltinRegistries.BIOME.get(new Identifier(mainBiome.getParent())))) : true);
return isUnsimilar;
// The parent-child relationship previously modeled in Biome itself is gone,
// and has been - for the time being - replaced by a hardcoded raw-id map
// in AddHillsLayer.
Int2IntMap parentChildMap = AddHillsLayerAccessor.getBaseToVariantMap();
return parentChildMap.get(mainBiomeId) != secondaryBiomeId
&& parentChildMap.get(secondaryBiomeId) != mainBiomeId;
}
}
/**
* @param north raw id of the biome to the north
* @param east raw id of the biome to the east
* @param east raw id of the biome to the east
* @param south raw id of the biome to the south
* @param west raw id of the biome to the west
* @param west raw id of the biome to the west
* @return whether a biome in any direction is an ocean around the central biome
*/
public static boolean neighborsOcean(int north, int east, int south, int west) {
@ -105,20 +114,21 @@ public final class InternalBiomeUtils {
/**
* Potentially transforms a biome into its variants based on the provided randomness source.
*
* @param random The randomness source
* @param random The randomness source
* @param existing The base biome
* @param climate The climate in which the biome resides, or null to indicate an unknown climate
* @param climate The climate in which the biome resides, or null to indicate an unknown climate
* @return The potentially transformed biome
*/
public static int transformBiome(LayerRandomnessSource random, Biome existing, OverworldClimate climate) {
Map<Biome, VariantTransformer> overworldVariantTransformers = InternalBiomeData.getOverworldVariantTransformers();
public static int transformBiome(LayerRandomnessSource random, RegistryKey<Biome> existing, OverworldClimate climate) {
Map<RegistryKey<Biome>, VariantTransformer> overworldVariantTransformers = InternalBiomeData.getOverworldVariantTransformers();
VariantTransformer transformer = overworldVariantTransformers.get(existing);
if (transformer != null) {
return BuiltinRegistries.BIOME.getRawId(transformer.transformBiome(existing, random, climate));
RegistryKey<Biome> key = transformer.transformBiome(existing, random, climate);
return getRawId(key);
}
return BuiltinRegistries.BIOME.getRawId(existing);
return getRawId(existing);
}
public static void injectBiomesIntoClimate(LayerRandomnessSource random, int[] vanillaArray, OverworldClimate climate, IntConsumer result) {
@ -138,7 +148,7 @@ public final class InternalBiomeUtils {
if (reqWeightSum < vanillaArray.length) {
// Vanilla biome; look it up from the vanilla array and transform accordingly.
result.accept(transformBiome(random, BuiltinRegistries.BIOME.get(vanillaArray[(int) reqWeightSum]), climate));
result.accept(transformBiome(random, BuiltinBiomes.fromRawId(vanillaArray[(int) reqWeightSum]), climate));
} else {
// Modded biome; use a binary search, and then transform accordingly.
@ -148,7 +158,22 @@ public final class InternalBiomeUtils {
}
}
public static void recreateChunkGenerators(GeneratorOptions generatorOptions) {
((DimensionOptionsAccessor) (Object) generatorOptions.getDimensionMap().get(DimensionOptions.NETHER)).setChunkGenerator(DimensionTypeAccessor.createNetherGenerator(generatorOptions.getSeed()));
public static int getRawId(RegistryKey<Biome> key) {
return BuiltinRegistries.BIOME.getRawId(BuiltinRegistries.BIOME.getOrThrow(key));
}
/**
* Makes sure that the given registry key is mapped in {@link BuiltinBiomes}. This mapping may be absent
* if mods register their biomes only in {@link BuiltinRegistries#BIOME}, and not using the
* private method in {@link BuiltinBiomes}.
*/
public static void ensureIdMapping(RegistryKey<Biome> biomeKey) {
int rawId = getRawId(biomeKey);
Int2ObjectMap<RegistryKey<Biome>> biomes = BuiltinBiomesAccessor.getBY_RAW_ID();
if (!biomes.containsKey(rawId)) {
LOGGER.debug("Automatically creating layer-related raw-id mapping for biome {}", biomeKey);
biomes.put(rawId, biomeKey);
}
}
}

View file

@ -21,10 +21,11 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.layer.util.LayerRandomnessSource;
import net.fabricmc.fabric.api.biomes.v1.OverworldClimate;
import net.fabricmc.fabric.api.biome.v1.OverworldClimate;
/**
* Deals with picking variants for you.
@ -38,7 +39,7 @@ final class VariantTransformer {
* @param chance the chance of replacement of the biome into the variant
* @param climates the climates that the variant can replace the base biome in, empty/null indicates all climates
*/
void addBiome(Biome variant, double chance, OverworldClimate[] climates) {
void addBiome(RegistryKey<Biome> variant, double chance, OverworldClimate[] climates) {
if (climates == null || climates.length == 0) {
defaultTransformer.addBiome(variant, chance);
climates = OverworldClimate.values();
@ -56,7 +57,7 @@ final class VariantTransformer {
* @param random the {@link LayerRandomnessSource} from the layer
* @return the transformed biome
*/
Biome transformBiome(Biome replaced, LayerRandomnessSource random, OverworldClimate climate) {
RegistryKey<Biome> transformBiome(RegistryKey<Biome> replaced, LayerRandomnessSource random, OverworldClimate climate) {
if (climate == null) {
return defaultTransformer.transformBiome(replaced, random);
}
@ -77,7 +78,7 @@ final class VariantTransformer {
* @param variant the variant that the replaced biome is replaced with
* @param chance the chance of replacement of the biome into the variant
*/
private void addBiome(Biome variant, double chance) {
private void addBiome(RegistryKey<Biome> variant, double chance) {
variants.add(new BiomeVariant(variant, chance));
}
@ -88,7 +89,7 @@ final class VariantTransformer {
* @param random the {@link LayerRandomnessSource} from the layer
* @return the transformed biome
*/
private Biome transformBiome(Biome replaced, LayerRandomnessSource random) {
private RegistryKey<Biome> transformBiome(RegistryKey<Biome> replaced, LayerRandomnessSource random) {
for (BiomeVariant variant : variants) {
if (random.nextInt(Integer.MAX_VALUE) < variant.getChance() * Integer.MAX_VALUE) {
return variant.getVariant();

View file

@ -21,6 +21,7 @@ import java.util.List;
import com.google.common.base.Preconditions;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.layer.util.LayerRandomnessSource;
@ -36,7 +37,7 @@ public final class WeightedBiomePicker {
entries = new ArrayList<>();
}
void addBiome(final Biome biome, final double weight) {
void addBiome(final RegistryKey<Biome> biome, final double weight) {
currentTotal += weight;
entries.add(new ContinentalBiomeEntry(biome, weight, currentTotal));
@ -46,7 +47,7 @@ public final class WeightedBiomePicker {
return currentTotal;
}
public Biome pickRandom(LayerRandomnessSource random) {
public RegistryKey<Biome> pickRandom(LayerRandomnessSource random) {
double target = random.nextInt(Integer.MAX_VALUE) * getCurrentWeightTotal() / Integer.MAX_VALUE;
return search(target).getBiome();

View file

@ -0,0 +1,38 @@
/*
* 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 it.unimi.dsi.fastutil.ints.Int2IntMap;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import net.minecraft.world.biome.layer.AddHillsLayer;
@Mixin(AddHillsLayer.class)
public interface AddHillsLayerAccessor {
/**
* This field contains a raw-id to raw-id map for establishing parent/child relationships that
* model derived biomes.
*
* <p>For example, it contains a mapping for 1 -> 129 where 1 is the raw id of plains, while 129 is the raw id
* of the sunflower plains, which is derived from plains.
*/
@Accessor("field_26727")
static Int2IntMap getBaseToVariantMap() {
throw new AssertionError("mixin");
}
}

View file

@ -16,14 +16,18 @@
package net.fabricmc.fabric.mixin.biome;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import net.minecraft.world.dimension.DimensionOptions;
import net.minecraft.world.gen.chunk.ChunkGenerator;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BuiltinBiomes;
@Mixin(DimensionOptions.class)
public interface DimensionOptionsAccessor {
@Mixin(BuiltinBiomes.class)
public interface BuiltinBiomesAccessor {
@Accessor
void setChunkGenerator(ChunkGenerator generatorOptions);
static Int2ObjectMap<RegistryKey<Biome>> getBY_RAW_ID() {
throw new AssertionError("mixin");
}
}

View file

@ -21,28 +21,29 @@ 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.util.registry.BuiltinRegistries;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BuiltinBiomes;
import net.minecraft.world.biome.layer.AddEdgeBiomesLayer;
import net.minecraft.world.biome.layer.util.LayerRandomnessSource;
import net.fabricmc.fabric.api.biomes.v1.OverworldBiomes;
import net.fabricmc.fabric.api.biome.v1.OverworldBiomes;
import net.fabricmc.fabric.impl.biome.InternalBiomeData;
import net.fabricmc.fabric.impl.biome.InternalBiomeUtils;
/**
* Adds edges and shores specified in {@link OverworldBiomes#addEdgeBiome(Biome, Biome, double)} and {@link OverworldBiomes#addShoreBiome(Biome, Biome, double)} to the edges layer.
* Adds edges and shores specified in {@link OverworldBiomes#addEdgeBiome(RegistryKey, RegistryKey, double)} and {@link OverworldBiomes#addShoreBiome(RegistryKey, RegistryKey, double)} to the edges layer.
*/
@Mixin(AddEdgeBiomesLayer.class)
public class MixinAddEdgeBiomesLayer {
@Inject(at = @At("HEAD"), method = "sample", cancellable = true)
private void sample(LayerRandomnessSource rand, int north, int east, int south, int west, int center, CallbackInfoReturnable<Integer> info) {
Biome centerBiome = BuiltinRegistries.BIOME.get(center);
RegistryKey<Biome> centerBiome = BuiltinBiomes.fromRawId(center);
if (InternalBiomeData.getOverworldShores().containsKey(centerBiome) && InternalBiomeUtils.neighborsOcean(north, east, south, west)) {
info.setReturnValue(BuiltinRegistries.BIOME.getRawId(InternalBiomeData.getOverworldShores().get(centerBiome).pickRandom(rand)));
info.setReturnValue(InternalBiomeUtils.getRawId(InternalBiomeData.getOverworldShores().get(centerBiome).pickRandom(rand)));
} else if (InternalBiomeData.getOverworldEdges().containsKey(centerBiome) && InternalBiomeUtils.isEdge(north, east, south, west, center)) {
info.setReturnValue(BuiltinRegistries.BIOME.getRawId(InternalBiomeData.getOverworldEdges().get(centerBiome).pickRandom(rand)));
info.setReturnValue(InternalBiomeUtils.getRawId(InternalBiomeData.getOverworldEdges().get(centerBiome).pickRandom(rand)));
}
}
}

View file

@ -16,28 +16,35 @@
package net.fabricmc.fabric.mixin.biome;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
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.util.registry.BuiltinRegistries;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.Biomes;
import net.minecraft.world.biome.BuiltinBiomes;
import net.minecraft.world.biome.layer.AddHillsLayer;
import net.minecraft.world.biome.layer.BiomeLayers;
import net.minecraft.world.biome.layer.util.LayerRandomnessSource;
import net.minecraft.world.biome.layer.util.LayerSampler;
import net.fabricmc.fabric.api.biomes.v1.OverworldBiomes;
import net.fabricmc.fabric.api.biome.v1.OverworldBiomes;
import net.fabricmc.fabric.impl.biome.InternalBiomeData;
import net.fabricmc.fabric.impl.biome.InternalBiomeUtils;
import net.fabricmc.fabric.impl.biome.WeightedBiomePicker;
/**
* Injects hills biomes specified from {@link OverworldBiomes#addHillsBiome(Biome, Biome, double)}into the default hills layer.
* Injects hills biomes specified from {@link OverworldBiomes#addHillsBiome(RegistryKey, RegistryKey, double)} into the default hills layer.
*/
@Mixin(AddHillsLayer.class)
public class MixinAddHillsLayer {
// This maps between from a biome to it's "modified variant" biome, which was previously modeled via parent biomes
@Shadow
private static Int2IntMap field_26727;
@Inject(at = @At("HEAD"), method = "sample", cancellable = true)
private void sample(LayerRandomnessSource rand, LayerSampler biomeSampler, LayerSampler noiseSampler, int chunkX, int chunkZ, CallbackInfoReturnable<Integer> info) {
if (InternalBiomeData.getOverworldHills().isEmpty()) {
@ -48,9 +55,13 @@ public class MixinAddHillsLayer {
final int biomeId = biomeSampler.sample(chunkX, chunkZ);
int noiseSample = noiseSampler.sample(chunkX, chunkZ);
int processedNoiseSample = (noiseSample - 2) % 29;
final Biome biome = BuiltinRegistries.BIOME.get(biomeId);
RegistryKey<Biome> key = BuiltinBiomes.fromRawId(biomeId);
WeightedBiomePicker hillPicker = InternalBiomeData.getOverworldHills().get(biome);
if (key == null) {
throw new IllegalStateException("Biome sampler returned unregistered Biome ID: " + biomeId);
}
WeightedBiomePicker hillPicker = InternalBiomeData.getOverworldHills().get(key);
if (hillPicker == null) {
// No hills for this biome, fall through to vanilla logic.
@ -59,12 +70,10 @@ public class MixinAddHillsLayer {
}
if (rand.nextInt(3) == 0 || processedNoiseSample == 0) {
int biomeReturn = BuiltinRegistries.BIOME.getRawId(hillPicker.pickRandom(rand));
Biome parent;
int biomeReturn = InternalBiomeUtils.getRawId(hillPicker.pickRandom(rand));
if (processedNoiseSample == 0 && biomeReturn != biomeId) {
parent = Biomes.getMutated(BuiltinRegistries.BIOME.get(biomeReturn));
biomeReturn = parent == null ? biomeId : BuiltinRegistries.BIOME.getRawId(parent);
biomeReturn = field_26727.getOrDefault(biomeReturn, biomeId);
}
if (biomeReturn != biomeId) {

View file

@ -18,42 +18,39 @@ package net.fabricmc.fabric.mixin.biome;
import java.util.Map;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
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.util.registry.BuiltinRegistries;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeKeys;
import net.minecraft.world.biome.BuiltinBiomes;
import net.minecraft.world.biome.layer.AddRiversLayer;
import net.minecraft.world.biome.layer.util.LayerRandomnessSource;
import net.minecraft.world.biome.layer.util.LayerSampler;
import net.fabricmc.fabric.api.biomes.v1.OverworldBiomes;
import net.fabricmc.fabric.api.biome.v1.OverworldBiomes;
import net.fabricmc.fabric.impl.biome.InternalBiomeData;
import net.fabricmc.fabric.impl.biome.InternalBiomeUtils;
/**
* Sets river biomes specified with {@link OverworldBiomes#setRiverBiome(Biome, Biome)}.
* Sets river biomes specified with {@link OverworldBiomes#setRiverBiome(RegistryKey, RegistryKey)}.
*/
@Mixin(AddRiversLayer.class)
public class MixinAddRiversLayer {
@Shadow
@Final
private static int RIVER_ID;
@Inject(at = @At("HEAD"), method = "sample", cancellable = true)
private void sample(LayerRandomnessSource rand, LayerSampler landSampler, LayerSampler riverSampler, int x, int z, CallbackInfoReturnable<Integer> info) {
int landBiomeId = landSampler.sample(x, z);
Biome landBiome = BuiltinRegistries.BIOME.get(landBiomeId);
RegistryKey<Biome> landBiomeKey = BuiltinBiomes.fromRawId(landBiomeId);
int riverBiomeId = riverSampler.sample(x, z);
Map<Biome, Biome> overworldRivers = InternalBiomeData.getOverworldRivers();
Map<RegistryKey<Biome>, RegistryKey<Biome>> overworldRivers = InternalBiomeData.getOverworldRivers();
if (overworldRivers.containsKey(landBiome) && riverBiomeId == RIVER_ID) {
Biome riverBiome = overworldRivers.get(landBiome);
info.setReturnValue(riverBiome == null ? landBiomeId : BuiltinRegistries.BIOME.getRawId(riverBiome));
if (overworldRivers.containsKey(landBiomeKey) && BuiltinBiomes.fromRawId(riverBiomeId) == BiomeKeys.RIVER) {
RegistryKey<Biome> riverBiome = overworldRivers.get(landBiomeKey);
info.setReturnValue(riverBiome == null ? landBiomeId : InternalBiomeUtils.getRawId(riverBiome));
}
}
}

View file

@ -23,30 +23,32 @@ import java.util.function.Supplier;
import com.mojang.datafixers.util.Pair;
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.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.ModifyArgs;
import org.spongepowered.asm.mixin.injection.invoke.arg.Args;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.source.MultiNoiseBiomeSource;
import net.fabricmc.fabric.impl.biome.InternalBiomeData;
@Mixin(MultiNoiseBiomeSource.class)
/**
* This Mixin is responsible for adding mod-biomes to the NETHER preset in the MultiNoiseBiomeSource.
*/
@Mixin(MultiNoiseBiomeSource.Preset.class)
public class MixinMultiNoiseBiomeSource {
@Inject(method = "method_28467", at = @At("RETURN"))
private static void modifyNoisePoints(long l, CallbackInfoReturnable<MultiNoiseBiomeSource> cir) {
MultiNoiseBiomeSource returnedSource = cir.getReturnValue();
// collect existing noise points in non-immutable map
List<Pair<Biome.MixedNoisePoint, Supplier<Biome>>> existingPoints = new ArrayList<>(((MultiNoiseBiomeSourceAccessor) returnedSource).getBiomePoints());
// NOTE: This is a lambda-function in the NETHER preset field initializer
@ModifyArgs(method = "method_31088", at = @At(value = "INVOKE", target = "net/minecraft/world/biome/source/MultiNoiseBiomeSource.<init> (JLjava/util/List;Ljava/util/Optional;Lnet/minecraft/world/biome/source/MultiNoiseBiomeSource$1;)V"))
private static void appendNetherBiomes(Args args, MultiNoiseBiomeSource.Preset preset, Registry<Biome> registry, Long seed) {
List<Pair<Biome.MixedNoisePoint, Supplier<Biome>>> biomes = new ArrayList<>(args.get(1));
// add fabric biome noise point data to list && BiomeSource biome list
InternalBiomeData.getNetherBiomeNoisePoints().forEach((biome, noisePoint) -> {
existingPoints.add(Pair.of(noisePoint, () -> biome));
returnedSource.getBiomes().add(biome);
InternalBiomeData.getNetherBiomeNoisePoints().forEach((biomeKey, noisePoint) -> {
Biome biome = registry.getOrThrow(biomeKey);
// NOTE: Even though we have to pass in suppliers, BiomeSource's ctor will resolve them immediately
biomes.add(Pair.of(noisePoint, () -> biome));
});
// modify MultiNoiseBiomeSource list with updated data
((MultiNoiseBiomeSourceAccessor) returnedSource).setBiomePoints(existingPoints);
args.set(1, biomes);
}
}

View file

@ -24,12 +24,14 @@ 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.util.registry.BuiltinRegistries;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeKeys;
import net.minecraft.world.biome.BuiltinBiomes;
import net.minecraft.world.biome.layer.SetBaseBiomesLayer;
import net.minecraft.world.biome.layer.util.LayerRandomnessSource;
import net.fabricmc.fabric.api.biomes.v1.OverworldClimate;
import net.fabricmc.fabric.api.biome.v1.OverworldClimate;
import net.fabricmc.fabric.impl.biome.InternalBiomeUtils;
/**
@ -57,22 +59,6 @@ public class MixinSetBaseBiomesLayer {
@Mutable
private static int[] DRY_BIOMES;
@Shadow
@Final
private static int WOODED_BADLANDS_PLATEAU_ID;
@Shadow
@Final
private static int BADLANDS_PLATEAU_ID;
@Shadow
@Final
private static int JUNGLE_ID;
@Shadow
@Final
private static int GIANT_TREE_TAIGA_ID;
@Inject(at = @At(value = "FIELD", target = "Lnet/minecraft/world/biome/layer/SetBaseBiomesLayer;chosenGroup1:[I"), method = "sample", cancellable = true)
private void injectDryBiomes(LayerRandomnessSource random, int value, CallbackInfoReturnable<Integer> info) {
InternalBiomeUtils.injectBiomesIntoClimate(random, DRY_BIOMES, OverworldClimate.DRY, info::setReturnValue);
@ -96,16 +82,16 @@ public class MixinSetBaseBiomesLayer {
@Inject(at = @At("RETURN"), method = "sample", cancellable = true)
private void transformVariants(LayerRandomnessSource random, int value, CallbackInfoReturnable<Integer> info) {
int biomeId = info.getReturnValueI();
Biome biome = BuiltinRegistries.BIOME.get(biomeId);
RegistryKey<Biome> biome = BuiltinBiomes.fromRawId(biomeId);
// Determine what special case this is...
OverworldClimate climate;
if (biomeId == BADLANDS_PLATEAU_ID || biomeId == WOODED_BADLANDS_PLATEAU_ID) {
if (biome == BiomeKeys.BADLANDS_PLATEAU || biome == BiomeKeys.WOODED_BADLANDS_PLATEAU) {
climate = OverworldClimate.DRY;
} else if (biomeId == JUNGLE_ID) {
} else if (biome == BiomeKeys.JUNGLE) {
climate = OverworldClimate.TEMPERATE;
} else if (biomeId == GIANT_TREE_TAIGA_ID) {
} else if (biome == BiomeKeys.GIANT_TREE_TAIGA) {
climate = OverworldClimate.TEMPERATE;
} else {
climate = null;

View file

@ -16,16 +16,27 @@
package net.fabricmc.fabric.mixin.biome;
import java.util.List;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
import org.spongepowered.asm.mixin.gen.Accessor;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.gen.chunk.ChunkGenerator;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.source.VanillaLayeredBiomeSource;
@Mixin(DimensionType.class)
public interface DimensionTypeAccessor {
@Invoker("createNetherGenerator")
static ChunkGenerator createNetherGenerator(long seed) {
throw new UnsupportedOperationException();
/**
* This accessor gives us access to the hardcoded list of overworld biomes.
*/
@Mixin(VanillaLayeredBiomeSource.class)
public interface VanillaLayeredBiomeSourceAccessor {
@Accessor
static List<RegistryKey<Biome>> getBIOMES() {
throw new AssertionError("mixin");
}
@Accessor
static void setBIOMES(List<RegistryKey<Biome>> biomes) {
throw new AssertionError("mixin");
}
}

View file

@ -3,17 +3,15 @@
"package": "net.fabricmc.fabric.mixin.biome",
"compatibilityLevel": "JAVA_8",
"mixins": [
"DimensionOptionsAccessor",
"DimensionTypeAccessor",
"AddHillsLayerAccessor",
"BuiltinBiomesAccessor",
"MixinAddEdgeBiomesLayer",
"MixinAddHillsLayer",
"MixinAddRiversLayer",
"MixinBiomeSource",
"MixinSetBaseBiomesLayer",
"MixinMinecraftDedicatedServer",
"MixinMultiNoiseBiomeSource",
"MixinVanillaLayeredBiomeSource",
"MultiNoiseBiomeSourceAccessor"
"MixinSetBaseBiomesLayer",
"MultiNoiseBiomeSourceAccessor",
"VanillaLayeredBiomeSourceAccessor"
],
"injectors": {
"defaultRequire": 1

View file

@ -1,11 +1,11 @@
{
"schemaVersion": 1,
"id": "fabric-biomes-v1",
"name": "Fabric Biomes (v1)",
"id": "fabric-biome-api-v1",
"name": "Fabric Biome API (v1)",
"version": "${version}",
"environment": "*",
"license": "Apache-2.0",
"icon": "assets/fabric-biomes-v1/icon.png",
"icon": "assets/fabric-biome-api-v1/icon.png",
"contact": {
"homepage": "https://fabricmc.net",
"irc": "irc://irc.esper.net:6667/fabric",
@ -16,11 +16,11 @@
"FabricMC"
],
"depends": {
"fabricloader": ">=0.4.0",
"minecraft": ">=1.16-alpha.20.22.a"
"fabricloader": ">=0.9.2",
"minecraft": ">=1.16.2"
},
"description": "Hooks for adding biomes to the default world generator.",
"mixins": [
"fabric-biomes-v1.mixins.json"
"fabric-biome-api-v1.mixins.json"
]
}

View file

@ -0,0 +1,67 @@
/*
* 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.test.biome;
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.BiomeKeys;
import net.minecraft.world.biome.DefaultBiomeCreator;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.biome.v1.NetherBiomes;
import net.fabricmc.fabric.api.biome.v1.OverworldBiomes;
import net.fabricmc.fabric.api.biome.v1.OverworldClimate;
/**
* <b>NOTES FOR TESTING:</b>
* When running with this test-mod, also test this when running a dedicated server since there
* are significant differences between server + client and how they sync biomes.
*
* <p>Ingame, you can use <code>/locatebiome</code> since we use nether- and end-biomes in the overworld,
* and vice-versa, making them easy to find to verify the injection worked.
*
* <p>If you don't find a biome right away, teleport far away (~10000 blocks) from spawn and try again.
*/
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"));
@Override
public void onInitialize() {
Registry.register(BuiltinRegistries.BIOME, TEST_CRIMSON_FOREST.getValue(), DefaultBiomeCreator.createCrimsonForest());
NetherBiomes.addNetherBiome(BiomeKeys.BEACH, new Biome.MixedNoisePoint(0.0F, 0.5F, 0.0F, 0.0F, 0.1F));
NetherBiomes.addNetherBiome(TEST_CRIMSON_FOREST, new Biome.MixedNoisePoint(0.0F, 0.5F, 0.0F, 0.0F, 0.275F));
Registry.register(BuiltinRegistries.BIOME, CUSTOM_PLAINS.getValue(), DefaultBiomeCreator.createPlains(false));
OverworldBiomes.addBiomeVariant(BiomeKeys.PLAINS, CUSTOM_PLAINS, 1);
OverworldBiomes.addEdgeBiome(BiomeKeys.PLAINS, BiomeKeys.END_BARRENS, 0.9);
OverworldBiomes.addShoreBiome(BiomeKeys.FOREST, BiomeKeys.NETHER_WASTES, 0.9);
OverworldBiomes.addHillsBiome(BiomeKeys.BAMBOO_JUNGLE, BiomeKeys.BASALT_DELTAS, 0.9);
OverworldBiomes.addContinentalBiome(BiomeKeys.END_HIGHLANDS, OverworldClimate.DRY, 0.5);
}
}

View file

@ -1,12 +1,12 @@
{
"schemaVersion": 1,
"id": "fabric-biomes-v1-testmod",
"name": "Fabric Biomes (v1) Test Mod",
"id": "fabric-biome-api-v1-testmod",
"name": "Fabric Biome API (v1) Test Mod",
"version": "1.0.0",
"environment": "*",
"license": "Apache-2.0",
"depends": {
"fabric-biomes-v1": "*"
"fabric-biome-api-v1": "*"
},
"entrypoints": {
"main": [

View file

@ -1,2 +0,0 @@
archivesBaseName = "fabric-biomes-v1"
version = getSubprojectVersion(project, "1.0.2")

View file

@ -1,37 +0,0 @@
/*
* 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.biomes.v1;
import net.minecraft.world.biome.Biome;
import net.fabricmc.fabric.impl.biome.InternalBiomeData;
/**
* General API that applies to all biome sources.
*/
public final class FabricBiomes {
private FabricBiomes() { }
/**
* Allows players to naturally spawn in this biome.
*
* @param biome a biome the player should be able to spawn in
*/
public static void addSpawnBiome(Biome biome) {
InternalBiomeData.addSpawnBiome(biome);
}
}

View file

@ -1,222 +0,0 @@
/*
* 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.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import net.minecraft.util.registry.BuiltinRegistries;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.Biomes;
import net.minecraft.world.biome.layer.BiomeLayers;
import net.minecraft.world.biome.source.VanillaLayeredBiomeSource;
import net.fabricmc.fabric.api.biomes.v1.OverworldClimate;
/**
* Lists and maps for internal use only! Stores data that is used by the various mixins into the world generation
*/
public final class InternalBiomeData {
private InternalBiomeData() { }
private static final EnumMap<OverworldClimate, WeightedBiomePicker> OVERWORLD_MODDED_CONTINENTAL_BIOME_PICKERS = new EnumMap<>(OverworldClimate.class);
private static final Map<Biome, WeightedBiomePicker> OVERWORLD_HILLS_MAP = new HashMap<>();
private static final Map<Biome, WeightedBiomePicker> OVERWORLD_SHORE_MAP = new HashMap<>();
private static final Map<Biome, WeightedBiomePicker> OVERWORLD_EDGE_MAP = new HashMap<>();
private static final Map<Biome, VariantTransformer> OVERWORLD_VARIANT_TRANSFORMERS = new HashMap<>();
private static final Map<Biome, Biome> OVERWORLD_RIVER_MAP = new HashMap<>();
private static final Set<Biome> NETHER_BIOMES = new HashSet<>();
private static final Map<Biome, Biome.MixedNoisePoint> NETHER_BIOME_NOISE_POINTS = new HashMap<>();
private static final Set<Biome> SPAWN_BIOMES = new HashSet<>();
private static Method injectBiomeMethod = null;
public static void addOverworldContinentalBiome(OverworldClimate climate, Biome biome, double weight) {
Preconditions.checkArgument(climate != null, "Climate is null");
Preconditions.checkArgument(biome != null, "Biome is null");
Preconditions.checkArgument(!Double.isNaN(weight), "Weight is NaN");
Preconditions.checkArgument(weight > 0.0, "Weight is less than or equal to 0.0 (%s)", weight);
OVERWORLD_MODDED_CONTINENTAL_BIOME_PICKERS.computeIfAbsent(climate, k -> new WeightedBiomePicker()).addBiome(biome, weight);
injectOverworldBiome(biome);
}
public static void addOverworldHillsBiome(Biome primary, Biome hills, double weight) {
Preconditions.checkArgument(primary != null, "Primary biome is null");
Preconditions.checkArgument(hills != null, "Hills biome is null");
Preconditions.checkArgument(!Double.isNaN(weight), "Weight is NaN");
Preconditions.checkArgument(weight > 0.0, "Weight is less than or equal to 0.0 (%s)", weight);
OVERWORLD_HILLS_MAP.computeIfAbsent(primary, biome -> DefaultHillsData.injectDefaultHills(primary, new WeightedBiomePicker())).addBiome(hills, weight);
injectOverworldBiome(hills);
}
public static void addOverworldShoreBiome(Biome primary, Biome shore, double weight) {
Preconditions.checkArgument(primary != null, "Primary biome is null");
Preconditions.checkArgument(shore != null, "Shore biome is null");
Preconditions.checkArgument(!Double.isNaN(weight), "Weight is NaN");
Preconditions.checkArgument(weight > 0.0, "Weight is less than or equal to 0.0 (%s)", weight);
OVERWORLD_SHORE_MAP.computeIfAbsent(primary, biome -> new WeightedBiomePicker()).addBiome(shore, weight);
injectOverworldBiome(shore);
}
public static void addOverworldEdgeBiome(Biome primary, Biome edge, double weight) {
Preconditions.checkArgument(primary != null, "Primary biome is null");
Preconditions.checkArgument(edge != null, "Edge biome is null");
Preconditions.checkArgument(!Double.isNaN(weight), "Weight is NaN");
Preconditions.checkArgument(weight > 0.0, "Weight is less than or equal to 0.0 (%s)", weight);
OVERWORLD_EDGE_MAP.computeIfAbsent(primary, biome -> new WeightedBiomePicker()).addBiome(edge, weight);
injectOverworldBiome(edge);
}
public static void addOverworldBiomeReplacement(Biome replaced, Biome variant, double chance, OverworldClimate[] climates) {
Preconditions.checkArgument(replaced != null, "Replaced biome is null");
Preconditions.checkArgument(variant != null, "Variant biome is null");
Preconditions.checkArgument(chance > 0 && chance <= 1, "Chance is not greater than 0 or less than or equal to 1");
OVERWORLD_VARIANT_TRANSFORMERS.computeIfAbsent(replaced, biome -> new VariantTransformer()).addBiome(variant, chance, climates);
injectOverworldBiome(variant);
}
public static void setOverworldRiverBiome(Biome primary, Biome river) {
Preconditions.checkArgument(primary != null, "Primary biome is null");
OVERWORLD_RIVER_MAP.put(primary, river);
if (river != null) {
injectOverworldBiome(river);
}
}
public static void addSpawnBiome(Biome biome) {
Preconditions.checkArgument(biome != null, "Biome is null");
SPAWN_BIOMES.add(biome);
}
private static void injectOverworldBiome(Biome biome) {
try {
if (injectBiomeMethod == null) {
injectBiomeMethod = VanillaLayeredBiomeSource.class.getDeclaredMethod("fabric_injectBiome", Biome.class);
injectBiomeMethod.setAccessible(true);
}
injectBiomeMethod.invoke(null, biome);
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
public static void addNetherBiome(Biome biome, Biome.MixedNoisePoint spawnNoisePoint) {
Preconditions.checkArgument(biome != null, "Biome is null");
Preconditions.checkArgument(spawnNoisePoint != null, "Biome.MixedNoisePoint is null");
NETHER_BIOMES.add(biome);
NETHER_BIOME_NOISE_POINTS.put(biome, spawnNoisePoint);
}
public static Set<Biome> getSpawnBiomes() {
return SPAWN_BIOMES;
}
public static Map<Biome, WeightedBiomePicker> getOverworldHills() {
return OVERWORLD_HILLS_MAP;
}
public static Map<Biome, WeightedBiomePicker> getOverworldShores() {
return OVERWORLD_SHORE_MAP;
}
public static Map<Biome, WeightedBiomePicker> getOverworldEdges() {
return OVERWORLD_EDGE_MAP;
}
public static Map<Biome, Biome> getOverworldRivers() {
return OVERWORLD_RIVER_MAP;
}
public static EnumMap<OverworldClimate, WeightedBiomePicker> getOverworldModdedContinentalBiomePickers() {
return OVERWORLD_MODDED_CONTINENTAL_BIOME_PICKERS;
}
public static Map<Biome, VariantTransformer> getOverworldVariantTransformers() {
return OVERWORLD_VARIANT_TRANSFORMERS;
}
public static Set<Biome> getNetherBiomes() {
return Collections.unmodifiableSet(NETHER_BIOMES);
}
public static Map<Biome, Biome.MixedNoisePoint> getNetherBiomeNoisePoints() {
return NETHER_BIOME_NOISE_POINTS;
}
private static class DefaultHillsData {
private static final ImmutableMap<Biome, Biome> DEFAULT_HILLS;
static WeightedBiomePicker injectDefaultHills(Biome base, WeightedBiomePicker picker) {
Biome defaultHill = DEFAULT_HILLS.get(base);
if (defaultHill != null) {
picker.addBiome(defaultHill, 1);
} else if (BiomeLayers.areSimilar(BuiltinRegistries.BIOME.getRawId(base), BuiltinRegistries.BIOME.getRawId(Biomes.WOODED_BADLANDS_PLATEAU))) {
picker.addBiome(Biomes.BADLANDS, 1);
} else if (base == Biomes.DEEP_OCEAN || base == Biomes.DEEP_LUKEWARM_OCEAN || base == Biomes.DEEP_COLD_OCEAN) {
picker.addBiome(Biomes.PLAINS, 1);
picker.addBiome(Biomes.FOREST, 1);
} else if (base == Biomes.DEEP_FROZEN_OCEAN) {
// Note: Vanilla Deep Frozen Oceans only have a 1/3 chance of having default hills.
// This is a clever trick that ensures that when a mod adds hills with a weight of 1, the 1/3 chance is fulfilled.
// 0.5 + 1.0 = 1.5, and 0.5 / 1.5 = 1/3.
picker.addBiome(Biomes.PLAINS, 0.25);
picker.addBiome(Biomes.FOREST, 0.25);
} else if (base == Biomes.PLAINS) {
picker.addBiome(Biomes.WOODED_HILLS, 1);
picker.addBiome(Biomes.FOREST, 2);
}
return picker;
}
static {
ImmutableMap.Builder<Biome, Biome> builder = ImmutableMap.builder();
builder.put(Biomes.DESERT, Biomes.DESERT_HILLS);
builder.put(Biomes.FOREST, Biomes.WOODED_HILLS);
builder.put(Biomes.BIRCH_FOREST, Biomes.BIRCH_FOREST_HILLS);
builder.put(Biomes.DARK_FOREST, Biomes.PLAINS);
builder.put(Biomes.TAIGA, Biomes.TAIGA_HILLS);
builder.put(Biomes.GIANT_TREE_TAIGA, Biomes.GIANT_TREE_TAIGA_HILLS);
builder.put(Biomes.SNOWY_TAIGA, Biomes.SNOWY_TAIGA_HILLS);
builder.put(Biomes.SNOWY_TUNDRA, Biomes.SNOWY_MOUNTAINS);
builder.put(Biomes.JUNGLE, Biomes.JUNGLE_HILLS);
builder.put(Biomes.BAMBOO_JUNGLE, Biomes.BAMBOO_JUNGLE_HILLS);
builder.put(Biomes.OCEAN, Biomes.DEEP_OCEAN);
builder.put(Biomes.LUKEWARM_OCEAN, Biomes.DEEP_LUKEWARM_OCEAN);
builder.put(Biomes.COLD_OCEAN, Biomes.DEEP_COLD_OCEAN);
builder.put(Biomes.FROZEN_OCEAN, Biomes.DEEP_FROZEN_OCEAN);
builder.put(Biomes.MOUNTAINS, Biomes.WOODED_MOUNTAINS);
builder.put(Biomes.SAVANNA, Biomes.SAVANNA_PLATEAU);
DEFAULT_HILLS = builder.build();
}
}
}

View file

@ -1,53 +0,0 @@
/*
* 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 java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
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.Biome;
import net.minecraft.world.biome.source.BiomeSource;
import net.fabricmc.fabric.impl.biome.InternalBiomeData;
/**
* Adds spawn biomes to the base {@link BiomeSource} class.
*/
@Mixin(BiomeSource.class)
public class MixinBiomeSource {
@Shadow
@Final
private static List<Biome> SPAWN_BIOMES;
@Inject(at = @At("RETURN"), cancellable = true, method = "getSpawnBiomes")
private void getSpawnBiomes(CallbackInfoReturnable<List<Biome>> info) {
Set<Biome> biomes = new LinkedHashSet<>(info.getReturnValue());
if (biomes.addAll(InternalBiomeData.getSpawnBiomes())) {
info.setReturnValue(new ArrayList<>(biomes));
}
}
}

View file

@ -1,41 +0,0 @@
/*
* 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.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import net.minecraft.server.dedicated.MinecraftDedicatedServer;
import net.minecraft.server.dedicated.ServerPropertiesLoader;
import net.fabricmc.fabric.impl.biome.InternalBiomeUtils;
@Mixin(MinecraftDedicatedServer.class)
public class MixinMinecraftDedicatedServer {
@Shadow
@Final
private ServerPropertiesLoader propertiesLoader;
@Inject(method = "setupServer", at = @At("HEAD"))
public void setupServer(CallbackInfoReturnable<Boolean> cir) {
InternalBiomeUtils.recreateChunkGenerators(propertiesLoader.getPropertiesHandler().generatorOptions);
}
}

View file

@ -1,54 +0,0 @@
/*
* 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 java.util.ArrayList;
import java.util.List;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.source.VanillaLayeredBiomeSource;
import net.minecraft.world.gen.feature.StructureFeature;
/**
* Adds the biomes in world gen to the array for the vanilla layered biome source.
* This helps with {@link VanillaLayeredBiomeSource#hasStructureFeature(StructureFeature)} returning correctly for modded biomes as well as in {@link VanillaLayeredBiomeSource#getTopMaterials()}}
*/
@Mixin(VanillaLayeredBiomeSource.class)
public class MixinVanillaLayeredBiomeSource {
@Shadow
@Final
@Mutable
private static List<Biome> BIOMES;
@Inject(method = "<clinit>", at = @At("RETURN"))
private static void cinit(CallbackInfo info) {
BIOMES = new ArrayList<>(BIOMES);
}
//Called via reflection
private static void fabric_injectBiome(Biome biome) {
BIOMES.add(biome);
}
}

View file

@ -1,41 +0,0 @@
/*
* 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.test.biome;
import net.minecraft.world.biome.DefaultBiomeCreator;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.BuiltinRegistries;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.Biomes;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.biomes.v1.NetherBiomes;
import net.fabricmc.fabric.api.biomes.v1.OverworldBiomes;
public class FabricBiomeTest implements ModInitializer {
public static final String MOD_ID = "fabric-biome-api-v1-testmod";
@Override public void onInitialize() {
Biome biome = Registry.register(BuiltinRegistries.BIOME, new Identifier(MOD_ID, "test_crimson_forest"), DefaultBiomeCreator.createCrimsonForest());
NetherBiomes.addNetherBiome(Biomes.BEACH, new Biome.MixedNoisePoint(0.0F, 0.5F, 0.0F, 0.0F, 0.1F));
NetherBiomes.addNetherBiome(biome, new Biome.MixedNoisePoint(0.0F, 0.5F, 0.0F, 0.0F, 0.275F));
Biome customPlains = Registry.register(BuiltinRegistries.BIOME, new Identifier(MOD_ID, "custom_plains"), DefaultBiomeCreator.createPlains(null, false));
OverworldBiomes.addBiomeVariant(Biomes.PLAINS, customPlains, 1);
}
}

View file

@ -14,7 +14,7 @@ rootProject.name = "fabric-api"
include 'fabric-api-base'
//include 'fabric-biomes-v1'
include 'fabric-biome-api-v1'
include 'fabric-blockrenderlayer-v1'
include 'fabric-commands-v0'
include 'fabric-command-api-v1'