怎么才能在特定的生物群系或结构生成生物?我们来研究一下alexmob,看看它是怎么写的。
自定义生物群系生成规则
先研究源码以便更好的开发模组
AMMobSpawnBiomeModifier类
package com.github.alexthe666.alexsmobs.world;import com.github.alexthe666.alexsmobs.AlexsMobs;
import com.mojang.serialization.Codec;
import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.biome.Biome;
import net.minecraftforge.common.world.BiomeModifier;
import net.minecraftforge.common.world.ModifiableBiomeInfo;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;public class AMMobSpawnBiomeModifier implements BiomeModifier {private static final RegistryObject<Codec<? extends BiomeModifier>> SERIALIZER = RegistryObject.create(new ResourceLocation(AlexsMobs.MODID, "am_mob_spawns"), ForgeRegistries.Keys.BIOME_MODIFIER_SERIALIZERS, AlexsMobs.MODID);public AMMobSpawnBiomeModifier() {}public void modify(Holder<Biome> biome, Phase phase, ModifiableBiomeInfo.BiomeInfo.Builder builder) {if (phase == Phase.ADD) {AMWorldRegistry.addBiomeSpawns(biome, builder);}}public Codec<? extends BiomeModifier> codec() {return (Codec)SERIALIZER.get();}public static Codec<AMMobSpawnBiomeModifier> makeCodec() {return Codec.unit(AMMobSpawnBiomeModifier::new);}
}
这段代码是 Minecraft Forge 模组 Alex’s Mobs 中用于动态修改生物群系(Biome)生成规则的实现。它的核心功能是向特定生物群系中添加自定义生物的生成规则。以下是对代码的详细解释:
1. 类定义与接口实现
public class AMMobSpawnBiomeModifier implements BiomeModifier {
• 类名 AMMobSpawnBiomeModifier
表明这是一个与生物生成(Mob Spawn)相关的生物群系修改器。
• 实现了 Forge 的 BiomeModifier
接口,允许在生物群系加载时动态修改其属性(如添加生物生成规则)。
2. 序列化器注册
private static final RegistryObject<Codec<? extends BiomeModifier>> SERIALIZER = RegistryObject.create(new ResourceLocation(AlexsMobs.MODID, "am_mob_spawns"), ForgeRegistries.Keys.BIOME_MODIFIER_SERIALIZERS, AlexsMobs.MODID);
• 作用: 注册一个序列化器(Codec),用于将配置数据与 AMMobSpawnBiomeModifier
实例相互转换。
• 关键参数:
• ResourceLocation
: 唯一标识符,格式为 <modid>:am_mob_spawns
(例如 alexsmobs:am_mob_spawns
)。
• ForgeRegistries.Keys.BIOME_MODIFIER_SERIALIZERS
: 表明注册到 Forge 的生物群系修改器序列化器注册表中。
• 通过 RegistryObject
延迟获取序列化器,确保注册顺序正确。
3. 核心方法 modify
public void modify(Holder<Biome> biome, Phase phase, ModifiableBiomeInfo.BiomeInfo.Builder builder) {if (phase == Phase.ADD) {AMWorldRegistry.addBiomeSpawns(biome, builder);}
}
• 作用: 在生物群系加载时,根据指定的 Phase
阶段修改其属性。
• 参数:
• biome
: 当前正在处理的生物群系。
• phase
: 修改阶段(ADD
表示添加新内容,MODIFY
表示修改现有内容)。
• builder
: 用于构建生物群系信息的工具类。
• 逻辑:
• 仅在 Phase.ADD
阶段触发,调用 AMWorldRegistry.addBiomeSpawns
方法,将自定义生物的生成规则添加到该生物群系中。
• AMWorldRegistry.addBiomeSpawns
的具体实现(未在此展示)可能涉及添加生物生成规则(如生物类型、生成权重、最小/最大数量等)。
4. 序列化相关方法
public Codec<? extends BiomeModifier> codec() {return (Codec)SERIALIZER.get();
}public static Codec<AMMobSpawnBiomeModifier> makeCodec() {return Codec.unit(AMMobSpawnBiomeModifier::new);
}
• codec()
:
• 返回该类使用的序列化器(Codec),即之前注册的 SERIALIZER
。
• makeCodec()
:
• 创建一个简单的 Codec,直接通过构造函数 AMMobSpawnBiomeModifier::new
生成实例。
• Codec.unit
表示该 Codec 不需要额外配置参数,直接返回一个默认实例。
5. 整体工作流程
-
注册序列化器:
• 在 Forge 的注册表中声明AMMobSpawnBiomeModifier
的序列化器,使游戏能识别并加载相关配置。 -
生物群系修改阶段:
• 当游戏加载生物群系时,Forge 会调用modify
方法。
• 在Phase.ADD
阶段,通过AMWorldRegistry.addBiomeSpawns
向生物群系添加自定义生物生成规则。 -
数据驱动:
• 通过 Codec 序列化/反序列化,可能支持通过 JSON 文件配置生物生成规则(如不同群系的生成参数)。
6. 实际应用场景
• 假设模组添加了一个名为“袋鼠”的生物,需要在沙漠和草原群系中生成。
• 在 AMWorldRegistry.addBiomeSpawns
中,会根据生物群系类型(通过 biome
参数判断)调用类似 builder.getMobSpawnSettings().addSpawn(...)
的代码,添加生成规则。
总结
这段代码是 Alex’s Mobs 模组中用于动态管理生物生成的核心逻辑,利用 Forge 的 BiomeModifier
系统,在生物群系加载时注入自定义生成规则。通过 Codec 实现数据驱动,使得生物生成配置更加灵活且易于维护。
和Biometag的联系:
这两段代码分别属于 生物群系(Biome)的动态修改 和 生物群系的标签分类 系统,在 Minecraft 模组开发中密切相关但功能不同。以下是它们的联系与区别分析:
联系
-
共同目标
两者均围绕 生物群系的特性管理,但角度不同:
•AMMobSpawnBiomeModifier
:动态添加生物生成规则到符合条件的生物群系。
•BiomeTags
:定义生物群系的分类标签(如是否为海洋、森林,是否生成特定结构等),供游戏逻辑判断。 -
潜在依赖关系
AMMobSpawnBiomeModifier
在实现时 可能依赖BiomeTags
的标签,例如:// 假设在 AMWorldRegistry.addBiomeSpawns 中: if (biome.is(BiomeTags.IS_FOREST)) {builder.getMobSpawnSettings().addSpawn(...); // 仅在森林群系添加生物 }
• 通过
BiomeTags
的标签,可以精确控制生物生成的目标群系。 -
数据驱动协同
•BiomeTags
提供 静态标签分类(如IS_OCEAN
),用于描述生物群系的属性。
•AMMobSpawnBiomeModifier
利用这些标签 动态注入逻辑(如只在海洋群系生成某种鱼类)。
区别
特性 | AMMobSpawnBiomeModifier (模组代码) | BiomeTags (原版代码) |
---|---|---|
功能 | 动态修改生物群系的生成规则(如添加生物) | 定义生物群系的分类标签(元数据) |
所属系统 | Forge 的 BiomeModifier 系统 | 原版 Minecraft 的标签系统(TagKey ) |
实现方式 | 通过 BiomeModifier 接口和 Codec 序列化 | 静态定义 TagKey 常量 |
数据驱动 | 可能依赖 JSON 配置或代码硬编码规则 | 标签通过资源包或数据包动态配置 |
代码层级 | 模组业务逻辑(添加自定义内容) | 原版基础设施(提供全局分类标准) |
典型应用场景 | 模组生物的生成控制 | 原版结构生成、天气逻辑、生物行为等 |
核心细节解析
-
BiomeTags
的作用
• 用于标记生物群系的属性,例如:public static final TagKey<Biome> IS_OCEAN = create("is_ocean"); public static final TagKey<Biome> HAS_OCEAN_MONUMENT = create("has_structure/ocean_monument");
• 游戏逻辑可通过这些标签快速判断群系特性,例如:
◦ 海洋神庙只生成在HAS_OCEAN_MONUMENT
标签的群系。
◦ 溺尸生成频率可能在MORE_FREQUENT_DROWNED_SPAWNS
标签的群系中提高。 -
AMMobSpawnBiomeModifier
的工作流程
• 注册序列化器:通过SERIALIZER
声明该修改器的配置方式。
• 修改阶段:在Phase.ADD
阶段调用AMWorldRegistry.addBiomeSpawns
,向群系注入生物生成规则。
• 潜在标签依赖:若模组生物需在特定标签的群系生成(如森林),需通过BiomeTags.IS_FOREST
判断。
协作示例
假设模组要添加一个 “森林精灵”生物,仅在森林群系(IS_FOREST
)且无巡逻队生成(WITHOUT_PATROL_SPAWNS
)的群系中出现:
- 在
BiomeTags
中:利用现有标签IS_FOREST
和WITHOUT_PATROL_SPAWNS
。 - 在
AMMobSpawnBiomeModifier
中:
• 直接依赖public void modify(Holder<Biome> biome, Phase phase, ModifiableBiomeInfo.BiomeInfo.Builder builder) {if (phase == Phase.ADD && biome.is(BiomeTags.IS_FOREST) && biome.is(BiomeTags.WITHOUT_PATROL_SPAWNS)) {builder.getMobSpawnSettings().addSpawn(...); // 添加“森林精灵”生成规则} }
BiomeTags
的标签实现精准控制。
总结
• BiomeTags
是“群系属性字典”:提供全局的群系分类标准,供游戏和模组共用。
• AMMobSpawnBiomeModifier
是“动态规则引擎”:利用标签或硬编码逻辑,向群系注入自定义内容。
• 二者协作:标签系统为动态修改提供判断依据,动态修改器实现具体游戏内容的变化。
(例如:原版用标签控制结构生成,模组用同一标签控制生物生成,实现无缝兼容。)
ModifiableBiomeInfo类
这段代码是 Minecraft Forge 中用于动态修改生物群系(Biome)属性的核心类 ModifiableBiomeInfo
,其原理围绕 封装生物群系数据 和 应用模组修改逻辑 展开。以下是代码的详细分析:
1. 类结构与作用
ModifiableBiomeInfo
类
• 作用:
封装一个生物群系的原始数据,并提供接口让模组(Mod)按阶段动态修改其属性(如气候、特效、地形生成规则、刷怪规则等)。
• 成员变量:
• originalBiomeInfo
:原始生物群系数据(不可变)。
• modifiedBiomeInfo
:修改后的生物群系数据(初始为 null
,应用修改后生成)。
BiomeInfo
内部记录类(Record)
• 作用:
存储生物群系的完整配置,包含四个核心属性:
• climateSettings
:气候设置(温度、降水量等)。
• effects
:视觉效果(天空颜色、水颜色等)。
• generationSettings
:地形生成规则(如结构、植被)。
• mobSpawnSettings
:刷怪规则(生物种类、权重等)。
Builder
内部类
• 作用:
基于建造者模式(Builder Pattern)逐步构建 BiomeInfo
对象,允许复制原始数据并分步骤修改。
2. 核心方法解析
applyBiomeModifiers
方法
public void applyBiomeModifiers(Holder<Biome> biome, List<BiomeModifier> biomeModifiers) {if (this.modifiedBiomeInfo != null) {throw new IllegalStateException("Biome already modified");}BiomeInfo original = this.getOriginalBiomeInfo();BiomeInfo.Builder builder = BiomeInfo.Builder.copyOf(original); // 复制原始数据// 按阶段(Phase)应用所有 BiomeModifierfor (Phase phase : Phase.values()) {for (BiomeModifier modifier : biomeModifiers) {modifier.modify(biome, phase, builder); // 调用修改器}}this.modifiedBiomeInfo = builder.build(); // 构建修改后的数据
}
• 流程:
- 检查是否已修改:若已修改则抛出异常,避免重复修改。
- 复制原始数据:通过
Builder.copyOf
创建一个可修改的副本。 - 按阶段应用修改器:遍历所有
BiomeModifier
,并按阶段(如ADD
、REMOVE
、MODIFY
)调用其modify
方法。 - 构建最终数据:将修改后的
Builder
转换为不可变的BiomeInfo
。
• 关键点:
• 阶段(Phase):允许不同模组按顺序修改生物群系。例如:
◦ ADD
阶段添加新规则。
◦ MODIFY
阶段调整现有规则。
◦ REMOVE
阶段删除某些规则。
• 不可变数据:修改后的 BiomeInfo
是不可变对象,确保线程安全和数据一致性。
3. 设计模式与原理
建造者模式(Builder Pattern)
• BiomeInfo.Builder
类:
提供逐步构建 BiomeInfo
的能力,通过方法链式调用修改各个属性:
BiomeInfo.Builder builder = BiomeInfo.Builder.copyOf(original);
builder.getClimateSettings().setTemperature(0.5f);
builder.getMobSpawnSettings().addSpawn(...);
BiomeInfo modified = builder.build();
• 优点:
将对象构建与表示分离,支持灵活修改且避免参数爆炸。
不可变对象(Immutable Object)
• BiomeInfo
为记录类(Record):
所有字段在构造后不可变,确保在多线程环境下无需同步即可安全访问。
• 修改流程:
通过复制原始数据生成新对象,避免直接修改原数据。
事件驱动修改
• BiomeModifier
接口:
模组通过实现此接口并注册到 Forge 事件系统,在游戏加载生物群系时动态修改其属性。例如:
public class MyBiomeModifier implements BiomeModifier {@Overridepublic void modify(Holder<Biome> biome, Phase phase, BiomeInfo.Builder builder) {if (phase == Phase.ADD) {builder.getMobSpawnSettings().addSpawn(...); // 添加新生物}}
}
4. 与 Alex’s Mobs 的关联
在用户之前提供的 AMWorldRegistry
代码中,addBiomeSpawns
方法可能通过注册 BiomeModifier
来修改生物群系的刷怪规则。例如:
// 伪代码:注册一个 BiomeModifier
BiomeModifierManager.addModifier(context -> {context.getBuilder().getMobSpawnSettings().addSpawn(...); // 添加模组生物
});
ModifiableBiomeInfo
类是这一过程的底层支持,负责协调所有模组的修改请求,并按阶段生成最终配置。
5. 总结
• 功能:
ModifiableBiomeInfo
是 Forge 提供的核心工具类,用于实现生物群系属性的动态修改,支持多模组协作。
• 设计亮点:
• 使用建造者模式逐步构建不可变数据。
• 分阶段应用修改器,确保逻辑顺序和兼容性。
• 通过不可变对象保证线程安全。
• 应用场景:
模组开发者通过注册 BiomeModifier
,在游戏加载时修改生物群系的气候、刷怪规则、地形生成等,实现自定义内容与原版或其他模组的无缝集成。
Biome类
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//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 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.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;
import net.minecraftforge.common.world.ModifiableBiomeInfo;public final class Biome {public static final Codec<Biome> DIRECT_CODEC = RecordCodecBuilder.create((p_220544_) -> p_220544_.group(Biome.ClimateSettings.CODEC.forGetter((p_151717_) -> p_151717_.modifiableBiomeInfo().getOriginalBiomeInfo().climateSettings()), BiomeSpecialEffects.CODEC.fieldOf("effects").forGetter((p_220550_) -> p_220550_.modifiableBiomeInfo().getOriginalBiomeInfo().effects()), 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<Biome> 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<Holder<Biome>> CODEC;public static final Codec<HolderSet<Biome>> LIST_CODEC;private static final PerlinSimplexNoise TEMPERATURE_NOISE;static final PerlinSimplexNoise FROZEN_TEMPERATURE_NOISE;/** @deprecated */@Deprecated(forRemoval = true)public static final PerlinSimplexNoise BIOME_INFO_NOISE;private static final int TEMPERATURE_CACHE_SIZE = 1024;private final ClimateSettings climateSettings;private final BiomeGenerationSettings generationSettings;private final MobSpawnSettings mobSettings;private final BiomeSpecialEffects specialEffects;private final ThreadLocal<Long2FloatLinkedOpenHashMap> temperatureCache = ThreadLocal.withInitial(() -> (Long2FloatLinkedOpenHashMap)Util.make(() -> {Long2FloatLinkedOpenHashMap long2floatlinkedopenhashmap = new Long2FloatLinkedOpenHashMap(1024, 0.25F) {protected void rehash(int p_47580_) {}};long2floatlinkedopenhashmap.defaultReturnValue(Float.NaN);return long2floatlinkedopenhashmap;}));private final ModifiableBiomeInfo modifiableBiomeInfo;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_;this.modifiableBiomeInfo = new ModifiableBiomeInfo(new ModifiableBiomeInfo.BiomeInfo(p_220530_, p_220531_, p_220532_, p_220533_));}public int getSkyColor() {return this.specialEffects.getSkyColor();}public MobSpawnSettings getMobSettings() {return this.modifiableBiomeInfo().get().mobSpawnSettings();}public boolean hasPrecipitation() {return this.climateSettings.hasPrecipitation();}public Precipitation getPrecipitationAt(BlockPos p_265163_) {if (!this.hasPrecipitation()) {return Biome.Precipitation.NONE;} else {return this.coldEnoughToSnow(p_265163_) ? Biome.Precipitation.SNOW : Biome.Precipitation.RAIN;}}private float getHeightAdjustedTemperature(BlockPos p_47529_) {float f = this.climateSettings.temperatureModifier.modifyTemperature(p_47529_, this.getBaseTemperature());if (p_47529_.getY() > 80) {float f1 = (float)(TEMPERATURE_NOISE.getValue((double)((float)p_47529_.getX() / 8.0F), (double)((float)p_47529_.getZ() / 8.0F), false) * (double)8.0F);return f - (f1 + (float)p_47529_.getY() - 80.0F) * 0.05F / 40.0F;} else {return f;}}/** @deprecated */@Deprecatedprivate float getTemperature(BlockPos p_47506_) {long i = p_47506_.asLong();Long2FloatLinkedOpenHashMap long2floatlinkedopenhashmap = (Long2FloatLinkedOpenHashMap)this.temperatureCache.get();float f = long2floatlinkedopenhashmap.get(i);if (!Float.isNaN(f)) {return f;} else {float f1 = this.getHeightAdjustedTemperature(p_47506_);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_)) {return false;} else {if (p_47482_.getY() >= p_47481_.getMinBuildHeight() && p_47482_.getY() < p_47481_.getMaxBuildHeight() && 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_) {return !this.warmEnoughToRain(p_198905_);}public boolean warmEnoughToRain(BlockPos p_198907_) {return this.getTemperature(p_198907_) >= 0.15F;}public boolean shouldMeltFrozenOceanIcebergSlightly(BlockPos p_198909_) {return this.getTemperature(p_198909_) > 0.1F;}public boolean shouldSnow(LevelReader p_47520_, BlockPos p_47521_) {if (this.warmEnoughToRain(p_47521_)) {return false;} else {if (p_47521_.getY() >= p_47520_.getMinBuildHeight() && p_47521_.getY() < p_47520_.getMaxBuildHeight() && 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.modifiableBiomeInfo().get().generationSettings();}public int getFogColor() {return this.specialEffects.getFogColor();}public int getGrassColor(double p_47465_, double p_47466_) {int i = (Integer)this.specialEffects.getGrassColorOverride().orElseGet(this::getGrassColorFromTexture);return this.specialEffects.getGrassColorModifier().modifyColor(p_47465_, p_47466_, i);}private int getGrassColorFromTexture() {double d0 = (double)Mth.clamp(this.climateSettings.temperature, 0.0F, 1.0F);double d1 = (double)Mth.clamp(this.climateSettings.downfall, 0.0F, 1.0F);return GrassColor.get(d0, d1);}public int getFoliageColor() {return (Integer)this.specialEffects.getFoliageColorOverride().orElseGet(this::getFoliageColorFromTexture);}private int getFoliageColorFromTexture() {double d0 = (double)Mth.clamp(this.climateSettings.temperature, 0.0F, 1.0F);double d1 = (double)Mth.clamp(this.climateSettings.downfall, 0.0F, 1.0F);return FoliageColor.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<AmbientParticleSettings> getAmbientParticle() {return this.specialEffects.getAmbientParticleSettings();}public Optional<Holder<SoundEvent>> getAmbientLoop() {return this.specialEffects.getAmbientLoopSoundEvent();}public Optional<AmbientMoodSettings> getAmbientMood() {return this.specialEffects.getAmbientMoodSettings();}public Optional<AmbientAdditionsSettings> getAmbientAdditions() {return this.specialEffects.getAmbientAdditionsSettings();}public Optional<Music> getBackgroundMusic() {return this.specialEffects.getBackgroundMusic();}public ModifiableBiomeInfo modifiableBiomeInfo() {return this.modifiableBiomeInfo;}public ClimateSettings getModifiedClimateSettings() {return this.modifiableBiomeInfo().get().climateSettings();}public BiomeSpecialEffects getModifiedSpecialEffects() {return this.modifiableBiomeInfo().get().effects();}static {CODEC = RegistryFileCodec.create(Registries.BIOME, DIRECT_CODEC);LIST_CODEC = RegistryCodecs.homogeneousList(Registries.BIOME, DIRECT_CODEC);TEMPERATURE_NOISE = new PerlinSimplexNoise(new WorldgenRandom(new LegacyRandomSource(1234L)), ImmutableList.of(0));FROZEN_TEMPERATURE_NOISE = new PerlinSimplexNoise(new WorldgenRandom(new LegacyRandomSource(3456L)), ImmutableList.of(-2, -1, 0));BIOME_INFO_NOISE = new PerlinSimplexNoise(new WorldgenRandom(new LegacyRandomSource(2345L)), ImmutableList.of(0));}public static class BiomeBuilder {private boolean hasPrecipitation = true;@Nullableprivate Float temperature;private TemperatureModifier temperatureModifier;@Nullableprivate Float downfall;@Nullableprivate BiomeSpecialEffects specialEffects;@Nullableprivate MobSpawnSettings mobSpawnSettings;@Nullableprivate BiomeGenerationSettings generationSettings;public BiomeBuilder() {this.temperatureModifier = Biome.TemperatureModifier.NONE;}public BiomeBuilder hasPrecipitation(boolean p_265480_) {this.hasPrecipitation = p_265480_;return this;}public BiomeBuilder temperature(float p_47610_) {this.temperature = p_47610_;return this;}public BiomeBuilder downfall(float p_47612_) {this.downfall = p_47612_;return this;}public BiomeBuilder specialEffects(BiomeSpecialEffects p_47604_) {this.specialEffects = p_47604_;return this;}public BiomeBuilder mobSpawnSettings(MobSpawnSettings p_47606_) {this.mobSpawnSettings = p_47606_;return this;}public BiomeBuilder generationSettings(BiomeGenerationSettings p_47602_) {this.generationSettings = p_47602_;return this;}public BiomeBuilder temperatureAdjustment(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 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);}}public String toString() {return "BiomeBuilder{\nhasPrecipitation=" + this.hasPrecipitation + ",\ntemperature=" + this.temperature