mirror of
https://github.com/FabricMC/fabric.git
synced 2025-04-21 11:20:55 -04:00
Ported 1.16.1 biomes-api-v1 to 1.16.2 (#1053)
* 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:
parent
7490af87a1
commit
c29459abaf
33 changed files with 556 additions and 620 deletions
build.gradle
fabric-biome-api-v1
build.gradle
src
main
java/net/fabricmc/fabric
api/biome/v1
impl/biome
BiomeVariant.javaContinentalBiomeEntry.javaInternalBiomeData.javaInternalBiomeUtils.javaVariantTransformer.javaWeightedBiomePicker.java
mixin/biome
resources
testmod
fabric-biomes-v1
build.gradle
settings.gradlesrc
main/java/net/fabricmc/fabric
api/biomes/v1
impl/biome
mixin/biome
testmod/java/net/fabricmc/fabric/test/biome
|
@ -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 {
|
||||
|
|
2
fabric-biome-api-v1/build.gradle
Normal file
2
fabric-biome-api-v1/build.gradle
Normal file
|
@ -0,0 +1,2 @@
|
|||
archivesBaseName = "fabric-biome-api-v1"
|
||||
version = getSubprojectVersion(project, "2.0.0")
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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).
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
|
@ -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();
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) {
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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");
|
||||
}
|
||||
}
|
Before ![]() (image error) Size: 1.5 KiB After ![]() (image error) Size: 1.5 KiB ![]() ![]() |
|
@ -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
|
|
@ -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"
|
||||
]
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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": [
|
|
@ -1,2 +0,0 @@
|
|||
archivesBaseName = "fabric-biomes-v1"
|
||||
version = getSubprojectVersion(project, "1.0.2")
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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'
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue