package net.minecraft.world.level.biome; import com.google.common.collect.ImmutableList; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import com.mojang.serialization.codecs.RecordCodecBuilder.Instance; import it.unimi.dsi.fastutil.longs.Long2FloatLinkedOpenHashMap; import java.util.Optional; import javax.annotation.Nullable; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.HolderSet; import net.minecraft.core.RegistryCodecs; import net.minecraft.core.registries.Registries; import net.minecraft.resources.RegistryFileCodec; import net.minecraft.sounds.Music; import net.minecraft.sounds.SoundEvent; import net.minecraft.util.Mth; import net.minecraft.util.StringRepresentable; import net.minecraft.util.random.WeightedList; import net.minecraft.world.level.DryFoliageColor; import net.minecraft.world.level.FoliageColor; import net.minecraft.world.level.GrassColor; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.LiquidBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.levelgen.LegacyRandomSource; import net.minecraft.world.level.levelgen.WorldgenRandom; import net.minecraft.world.level.levelgen.synth.PerlinSimplexNoise; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.Fluids; public final class Biome { public static final Codec DIRECT_CODEC = RecordCodecBuilder.create( p_220544_ -> p_220544_.group( Biome.ClimateSettings.CODEC.forGetter(p_151717_ -> p_151717_.climateSettings), BiomeSpecialEffects.CODEC.fieldOf("effects").forGetter(p_220550_ -> p_220550_.specialEffects), BiomeGenerationSettings.CODEC.forGetter(p_220548_ -> p_220548_.generationSettings), MobSpawnSettings.CODEC.forGetter(p_220546_ -> p_220546_.mobSettings) ) .apply(p_220544_, Biome::new) ); public static final Codec NETWORK_CODEC = RecordCodecBuilder.create( p_220540_ -> p_220540_.group( Biome.ClimateSettings.CODEC.forGetter(p_220542_ -> p_220542_.climateSettings), BiomeSpecialEffects.CODEC.fieldOf("effects").forGetter(p_220538_ -> p_220538_.specialEffects) ) .apply(p_220540_, (p_220535_, p_220536_) -> new Biome(p_220535_, p_220536_, BiomeGenerationSettings.EMPTY, MobSpawnSettings.EMPTY)) ); public static final Codec> CODEC = RegistryFileCodec.create(Registries.BIOME, DIRECT_CODEC); public static final Codec> LIST_CODEC = RegistryCodecs.homogeneousList(Registries.BIOME, DIRECT_CODEC); private static final PerlinSimplexNoise TEMPERATURE_NOISE = new PerlinSimplexNoise(new WorldgenRandom(new LegacyRandomSource(1234L)), ImmutableList.of(0)); static final PerlinSimplexNoise FROZEN_TEMPERATURE_NOISE = new PerlinSimplexNoise(new WorldgenRandom(new LegacyRandomSource(3456L)), ImmutableList.of(-2, -1, 0)); @Deprecated( forRemoval = true ) public static final PerlinSimplexNoise BIOME_INFO_NOISE = new PerlinSimplexNoise(new WorldgenRandom(new LegacyRandomSource(2345L)), ImmutableList.of(0)); private static final int TEMPERATURE_CACHE_SIZE = 1024; private final Biome.ClimateSettings climateSettings; private final BiomeGenerationSettings generationSettings; private final MobSpawnSettings mobSettings; private final BiomeSpecialEffects specialEffects; private final ThreadLocal temperatureCache = ThreadLocal.withInitial(() -> Util.make(() -> { Long2FloatLinkedOpenHashMap long2floatlinkedopenhashmap = new Long2FloatLinkedOpenHashMap(1024, 0.25F) { @Override protected void rehash(int p_47580_) { } }; long2floatlinkedopenhashmap.defaultReturnValue(Float.NaN); return long2floatlinkedopenhashmap; })); Biome(Biome.ClimateSettings p_220530_, BiomeSpecialEffects p_220531_, BiomeGenerationSettings p_220532_, MobSpawnSettings p_220533_) { this.climateSettings = p_220530_; this.generationSettings = p_220532_; this.mobSettings = p_220533_; this.specialEffects = p_220531_; } public int getSkyColor() { return this.specialEffects.getSkyColor(); } public MobSpawnSettings getMobSettings() { return this.mobSettings; } public boolean hasPrecipitation() { return this.climateSettings.hasPrecipitation(); } public Biome.Precipitation getPrecipitationAt(BlockPos p_265163_, int p_366614_) { if (!this.hasPrecipitation()) { return Biome.Precipitation.NONE; } else { return this.coldEnoughToSnow(p_265163_, p_366614_) ? Biome.Precipitation.SNOW : Biome.Precipitation.RAIN; } } private float getHeightAdjustedTemperature(BlockPos p_47529_, int p_368747_) { float f = this.climateSettings.temperatureModifier.modifyTemperature(p_47529_, this.getBaseTemperature()); int i = p_368747_ + 17; if (p_47529_.getY() > i) { float f1 = (float)(TEMPERATURE_NOISE.getValue(p_47529_.getX() / 8.0F, p_47529_.getZ() / 8.0F, false) * 8.0); return f - (f1 + p_47529_.getY() - i) * 0.05F / 40.0F; } else { return f; } } @Deprecated private float getTemperature(BlockPos p_47506_, int p_365043_) { long i = p_47506_.asLong(); Long2FloatLinkedOpenHashMap long2floatlinkedopenhashmap = this.temperatureCache.get(); float f = long2floatlinkedopenhashmap.get(i); if (!Float.isNaN(f)) { return f; } else { float f1 = this.getHeightAdjustedTemperature(p_47506_, p_365043_); if (long2floatlinkedopenhashmap.size() == 1024) { long2floatlinkedopenhashmap.removeFirstFloat(); } long2floatlinkedopenhashmap.put(i, f1); return f1; } } public boolean shouldFreeze(LevelReader p_47478_, BlockPos p_47479_) { return this.shouldFreeze(p_47478_, p_47479_, true); } public boolean shouldFreeze(LevelReader p_47481_, BlockPos p_47482_, boolean p_47483_) { if (this.warmEnoughToRain(p_47482_, p_47481_.getSeaLevel())) { return false; } else { if (p_47481_.isInsideBuildHeight(p_47482_.getY()) && p_47481_.getBrightness(LightLayer.BLOCK, p_47482_) < 10) { BlockState blockstate = p_47481_.getBlockState(p_47482_); FluidState fluidstate = p_47481_.getFluidState(p_47482_); if (fluidstate.getType() == Fluids.WATER && blockstate.getBlock() instanceof LiquidBlock) { if (!p_47483_) { return true; } boolean flag = p_47481_.isWaterAt(p_47482_.west()) && p_47481_.isWaterAt(p_47482_.east()) && p_47481_.isWaterAt(p_47482_.north()) && p_47481_.isWaterAt(p_47482_.south()); if (!flag) { return true; } } } return false; } } public boolean coldEnoughToSnow(BlockPos p_198905_, int p_362496_) { return !this.warmEnoughToRain(p_198905_, p_362496_); } public boolean warmEnoughToRain(BlockPos p_198907_, int p_362136_) { return this.getTemperature(p_198907_, p_362136_) >= 0.15F; } public boolean shouldMeltFrozenOceanIcebergSlightly(BlockPos p_198909_, int p_365025_) { return this.getTemperature(p_198909_, p_365025_) > 0.1F; } public boolean shouldSnow(LevelReader p_47520_, BlockPos p_47521_) { if (this.warmEnoughToRain(p_47521_, p_47520_.getSeaLevel())) { return false; } else { if (p_47520_.isInsideBuildHeight(p_47521_.getY()) && p_47520_.getBrightness(LightLayer.BLOCK, p_47521_) < 10) { BlockState blockstate = p_47520_.getBlockState(p_47521_); if ((blockstate.isAir() || blockstate.is(Blocks.SNOW)) && Blocks.SNOW.defaultBlockState().canSurvive(p_47520_, p_47521_)) { return true; } } return false; } } public BiomeGenerationSettings getGenerationSettings() { return this.generationSettings; } public int getFogColor() { return this.specialEffects.getFogColor(); } public int getGrassColor(double p_47465_, double p_47466_) { int i = this.getBaseGrassColor(); return this.specialEffects.getGrassColorModifier().modifyColor(p_47465_, p_47466_, i); } private int getBaseGrassColor() { Optional optional = this.specialEffects.getGrassColorOverride(); return optional.isPresent() ? optional.get() : this.getGrassColorFromTexture(); } private int getGrassColorFromTexture() { double d0 = Mth.clamp(this.climateSettings.temperature, 0.0F, 1.0F); double d1 = Mth.clamp(this.climateSettings.downfall, 0.0F, 1.0F); return GrassColor.get(d0, d1); } public int getFoliageColor() { return this.specialEffects.getFoliageColorOverride().orElseGet(this::getFoliageColorFromTexture); } private int getFoliageColorFromTexture() { double d0 = Mth.clamp(this.climateSettings.temperature, 0.0F, 1.0F); double d1 = Mth.clamp(this.climateSettings.downfall, 0.0F, 1.0F); return FoliageColor.get(d0, d1); } public int getDryFoliageColor() { return this.specialEffects.getDryFoliageColorOverride().orElseGet(this::getDryFoliageColorFromTexture); } private int getDryFoliageColorFromTexture() { double d0 = Mth.clamp(this.climateSettings.temperature, 0.0F, 1.0F); double d1 = Mth.clamp(this.climateSettings.downfall, 0.0F, 1.0F); return DryFoliageColor.get(d0, d1); } public float getBaseTemperature() { return this.climateSettings.temperature; } public BiomeSpecialEffects getSpecialEffects() { return this.specialEffects; } public int getWaterColor() { return this.specialEffects.getWaterColor(); } public int getWaterFogColor() { return this.specialEffects.getWaterFogColor(); } public Optional getAmbientParticle() { return this.specialEffects.getAmbientParticleSettings(); } public Optional> getAmbientLoop() { return this.specialEffects.getAmbientLoopSoundEvent(); } public Optional getAmbientMood() { return this.specialEffects.getAmbientMoodSettings(); } public Optional getAmbientAdditions() { return this.specialEffects.getAmbientAdditionsSettings(); } public Optional> getBackgroundMusic() { return this.specialEffects.getBackgroundMusic(); } public float getBackgroundMusicVolume() { return this.specialEffects.getBackgroundMusicVolume(); } public static class BiomeBuilder { private boolean hasPrecipitation = true; @Nullable private Float temperature; private Biome.TemperatureModifier temperatureModifier = Biome.TemperatureModifier.NONE; @Nullable private Float downfall; @Nullable private BiomeSpecialEffects specialEffects; @Nullable private MobSpawnSettings mobSpawnSettings; @Nullable private BiomeGenerationSettings generationSettings; public Biome.BiomeBuilder hasPrecipitation(boolean p_265480_) { this.hasPrecipitation = p_265480_; return this; } public Biome.BiomeBuilder temperature(float p_47610_) { this.temperature = p_47610_; return this; } public Biome.BiomeBuilder downfall(float p_47612_) { this.downfall = p_47612_; return this; } public Biome.BiomeBuilder specialEffects(BiomeSpecialEffects p_47604_) { this.specialEffects = p_47604_; return this; } public Biome.BiomeBuilder mobSpawnSettings(MobSpawnSettings p_47606_) { this.mobSpawnSettings = p_47606_; return this; } public Biome.BiomeBuilder generationSettings(BiomeGenerationSettings p_47602_) { this.generationSettings = p_47602_; return this; } public Biome.BiomeBuilder temperatureAdjustment(Biome.TemperatureModifier p_47600_) { this.temperatureModifier = p_47600_; return this; } public Biome build() { if (this.temperature != null && this.downfall != null && this.specialEffects != null && this.mobSpawnSettings != null && this.generationSettings != null) { return new Biome( new Biome.ClimateSettings(this.hasPrecipitation, this.temperature, this.temperatureModifier, this.downfall), this.specialEffects, this.generationSettings, this.mobSpawnSettings ); } else { throw new IllegalStateException("You are missing parameters to build a proper biome\n" + this); } } @Override public String toString() { return "BiomeBuilder{\nhasPrecipitation=" + this.hasPrecipitation + ",\ntemperature=" + this.temperature + ",\ntemperatureModifier=" + this.temperatureModifier + ",\ndownfall=" + this.downfall + ",\nspecialEffects=" + this.specialEffects + ",\nmobSpawnSettings=" + this.mobSpawnSettings + ",\ngenerationSettings=" + this.generationSettings + ",\n}"; } } record ClimateSettings(boolean hasPrecipitation, float temperature, Biome.TemperatureModifier temperatureModifier, float downfall) { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( p_264995_ -> p_264995_.group( Codec.BOOL.fieldOf("has_precipitation").forGetter(p_264996_ -> p_264996_.hasPrecipitation), Codec.FLOAT.fieldOf("temperature").forGetter(p_151737_ -> p_151737_.temperature), Biome.TemperatureModifier.CODEC .optionalFieldOf("temperature_modifier", Biome.TemperatureModifier.NONE) .forGetter(p_151735_ -> p_151735_.temperatureModifier), Codec.FLOAT.fieldOf("downfall").forGetter(p_151733_ -> p_151733_.downfall) ) .apply(p_264995_, Biome.ClimateSettings::new) ); } public static enum Precipitation implements StringRepresentable { NONE("none"), RAIN("rain"), SNOW("snow"); public static final Codec CODEC = StringRepresentable.fromEnum(Biome.Precipitation::values); private final String name; private Precipitation(final String p_311702_) { this.name = p_311702_; } @Override public String getSerializedName() { return this.name; } } public static enum TemperatureModifier implements StringRepresentable { NONE("none") { @Override public float modifyTemperature(BlockPos p_47767_, float p_47768_) { return p_47768_; } }, FROZEN("frozen") { @Override public float modifyTemperature(BlockPos p_47774_, float p_47775_) { double d0 = Biome.FROZEN_TEMPERATURE_NOISE.getValue(p_47774_.getX() * 0.05, p_47774_.getZ() * 0.05, false) * 7.0; double d1 = Biome.BIOME_INFO_NOISE.getValue(p_47774_.getX() * 0.2, p_47774_.getZ() * 0.2, false); double d2 = d0 + d1; if (d2 < 0.3) { double d3 = Biome.BIOME_INFO_NOISE.getValue(p_47774_.getX() * 0.09, p_47774_.getZ() * 0.09, false); if (d3 < 0.8) { return 0.2F; } } return p_47775_; } }; private final String name; public static final Codec CODEC = StringRepresentable.fromEnum(Biome.TemperatureModifier::values); public abstract float modifyTemperature(BlockPos p_47754_, float p_47755_); TemperatureModifier(final String p_47745_) { this.name = p_47745_; } public String getName() { return this.name; } @Override public String getSerializedName() { return this.name; } } }