From 732043764094b80f4ab2eab8da704bbbaa177c99 Mon Sep 17 00:00:00 2001 From: iam Date: Mon, 12 Feb 2024 15:25:46 -0500 Subject: [PATCH] Optionally load vanilla biomes (#1988) * Add biomes from vanilla * cleanup * rework biomes * nullability * getByName string * expose vanilla biomes * not null * before rename * rename * nbt cache * fix * fix * fix * final on vanilla biome --- .../java/net/minestom/codegen/Generators.java | 1 + .../src/main/java/net/minestom/demo/Main.java | 1 + gradle/libs.versions.toml | 2 +- .../minestom/server/world/biomes/Biomes.java | 135 ++++++++ .../command/builder/arguments/Argument.java | 6 +- .../minestom/server/entity/EntityType.java | 4 +- .../server/entity/EntityTypeImpl.java | 2 +- .../server/entity/damage/DamageType.java | 4 +- .../server/entity/damage/DamageTypeImpl.java | 2 +- .../minestom/server/instance/AnvilLoader.java | 8 +- .../server/instance/DynamicChunk.java | 16 +- .../server/instance/GeneratorImpl.java | 12 +- .../minestom/server/instance/block/Block.java | 4 +- .../server/instance/block/BlockImpl.java | 2 +- .../net/minestom/server/item/Enchantment.java | 4 +- .../minestom/server/item/EnchantmentImpl.java | 2 +- .../net/minestom/server/item/ItemMeta.java | 6 +- .../net/minestom/server/item/Material.java | 4 +- .../minestom/server/item/MaterialImpl.java | 2 +- .../server/item/armor/TrimMaterial.java | 4 +- .../server/item/armor/TrimMaterialImpl.java | 2 +- .../server/item/armor/TrimPattern.java | 4 +- .../server/item/armor/TrimPatternImpl.java | 2 +- .../server/item/metadata/PotionMeta.java | 4 +- .../server/play/DeclareCommandsPacket.java | 4 +- .../minestom/server/particle/Particle.java | 4 +- .../server/particle/ParticleImpl.java | 2 +- .../minestom/server/potion/PotionEffect.java | 4 +- .../server/potion/PotionEffectImpl.java | 2 +- .../minestom/server/potion/PotionType.java | 4 +- .../server/potion/PotionTypeImpl.java | 2 +- .../server/registry/ProtocolObject.java | 11 +- .../minestom/server/registry/Registry.java | 148 ++++++++- .../server/registry/StaticProtocolObject.java | 9 + .../net/minestom/server/sound/SoundEvent.java | 6 +- .../minestom/server/sound/SoundEventImpl.java | 2 +- .../server/statistic/StatisticType.java | 4 +- .../server/statistic/StatisticTypeImpl.java | 2 +- .../minestom/server/world/biomes/Biome.java | 302 +++++++----------- .../server/world/biomes/BiomeImpl.java | 123 +++++++ .../server/world/biomes/BiomeManager.java | 85 +++-- .../server/world/biomes/VanillaBiome.java | 4 + .../instance/AnvilLoaderIntegrationTest.java | 4 +- .../server/instance/BiomeIntegrationTest.java | 72 +++++ .../GeneratorForkIntegrationTest.java | 13 +- .../server/instance/GeneratorTest.java | 45 +-- 46 files changed, 762 insertions(+), 323 deletions(-) create mode 100644 src/autogenerated/java/net/minestom/server/world/biomes/Biomes.java create mode 100644 src/main/java/net/minestom/server/registry/StaticProtocolObject.java create mode 100644 src/main/java/net/minestom/server/world/biomes/BiomeImpl.java create mode 100644 src/main/java/net/minestom/server/world/biomes/VanillaBiome.java create mode 100644 src/test/java/net/minestom/server/instance/BiomeIntegrationTest.java diff --git a/code-generators/src/main/java/net/minestom/codegen/Generators.java b/code-generators/src/main/java/net/minestom/codegen/Generators.java index 911e2b59e..6cb6ded57 100644 --- a/code-generators/src/main/java/net/minestom/codegen/Generators.java +++ b/code-generators/src/main/java/net/minestom/codegen/Generators.java @@ -27,6 +27,7 @@ public class Generators { generator.generate(resource("blocks.json"), "net.minestom.server.instance.block", "Block", "BlockImpl", "Blocks"); generator.generate(resource("items.json"), "net.minestom.server.item", "Material", "MaterialImpl", "Materials"); generator.generate(resource("entities.json"), "net.minestom.server.entity", "EntityType", "EntityTypeImpl", "EntityTypes"); + generator.generate(resource("biomes.json"), "net.minestom.server.world.biomes", "Biome", "BiomeImpl", "Biomes"); generator.generate(resource("enchantments.json"), "net.minestom.server.item", "Enchantment", "EnchantmentImpl", "Enchantments"); generator.generate(resource("potion_effects.json"), "net.minestom.server.potion", "PotionEffect", "PotionEffectImpl", "PotionEffects"); generator.generate(resource("potions.json"), "net.minestom.server.potion", "PotionType", "PotionTypeImpl", "PotionTypes"); diff --git a/demo/src/main/java/net/minestom/demo/Main.java b/demo/src/main/java/net/minestom/demo/Main.java index 385cd5ee2..4b48c7249 100644 --- a/demo/src/main/java/net/minestom/demo/Main.java +++ b/demo/src/main/java/net/minestom/demo/Main.java @@ -36,6 +36,7 @@ public class Main { MinecraftServer.setCompressionThreshold(0); MinecraftServer minecraftServer = MinecraftServer.init(); + MinecraftServer.getBiomeManager().loadVanillaBiomes(); BlockManager blockManager = MinecraftServer.getBlockManager(); blockManager.registerBlockPlacementRule(new DripstonePlacementRule()); diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b864046a4..64a61ce70 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ metadata.format.version = "1.1" [versions] # Important dependencies -data = "1.20.4-rv2" +data = "1.20.4-rv3" adventure = "4.15.0" kotlin = "1.7.22" dependencyGetter = "v1.0.1" diff --git a/src/autogenerated/java/net/minestom/server/world/biomes/Biomes.java b/src/autogenerated/java/net/minestom/server/world/biomes/Biomes.java new file mode 100644 index 000000000..245a19d9f --- /dev/null +++ b/src/autogenerated/java/net/minestom/server/world/biomes/Biomes.java @@ -0,0 +1,135 @@ +package net.minestom.server.world.biomes; + +/** + * Code autogenerated, do not edit! + */ +@SuppressWarnings("unused") +interface Biomes { + Biome SNOWY_SLOPES = BiomeImpl.get("minecraft:snowy_slopes"); + + Biome OLD_GROWTH_PINE_TAIGA = BiomeImpl.get("minecraft:old_growth_pine_taiga"); + + Biome MUSHROOM_FIELDS = BiomeImpl.get("minecraft:mushroom_fields"); + + Biome TAIGA = BiomeImpl.get("minecraft:taiga"); + + Biome DEEP_OCEAN = BiomeImpl.get("minecraft:deep_ocean"); + + Biome ERODED_BADLANDS = BiomeImpl.get("minecraft:eroded_badlands"); + + Biome FROZEN_RIVER = BiomeImpl.get("minecraft:frozen_river"); + + Biome END_HIGHLANDS = BiomeImpl.get("minecraft:end_highlands"); + + Biome CHERRY_GROVE = BiomeImpl.get("minecraft:cherry_grove"); + + Biome SUNFLOWER_PLAINS = BiomeImpl.get("minecraft:sunflower_plains"); + + Biome BIRCH_FOREST = BiomeImpl.get("minecraft:birch_forest"); + + Biome WINDSWEPT_HILLS = BiomeImpl.get("minecraft:windswept_hills"); + + Biome BAMBOO_JUNGLE = BiomeImpl.get("minecraft:bamboo_jungle"); + + Biome WOODED_BADLANDS = BiomeImpl.get("minecraft:wooded_badlands"); + + Biome BADLANDS = BiomeImpl.get("minecraft:badlands"); + + Biome SAVANNA_PLATEAU = BiomeImpl.get("minecraft:savanna_plateau"); + + Biome BEACH = BiomeImpl.get("minecraft:beach"); + + Biome DARK_FOREST = BiomeImpl.get("minecraft:dark_forest"); + + Biome STONY_PEAKS = BiomeImpl.get("minecraft:stony_peaks"); + + Biome MANGROVE_SWAMP = BiomeImpl.get("minecraft:mangrove_swamp"); + + Biome SPARSE_JUNGLE = BiomeImpl.get("minecraft:sparse_jungle"); + + Biome LUKEWARM_OCEAN = BiomeImpl.get("minecraft:lukewarm_ocean"); + + Biome RIVER = BiomeImpl.get("minecraft:river"); + + Biome STONY_SHORE = BiomeImpl.get("minecraft:stony_shore"); + + Biome WARPED_FOREST = BiomeImpl.get("minecraft:warped_forest"); + + Biome SNOWY_PLAINS = BiomeImpl.get("minecraft:snowy_plains"); + + Biome DRIPSTONE_CAVES = BiomeImpl.get("minecraft:dripstone_caves"); + + Biome SNOWY_TAIGA = BiomeImpl.get("minecraft:snowy_taiga"); + + Biome GROVE = BiomeImpl.get("minecraft:grove"); + + Biome SWAMP = BiomeImpl.get("minecraft:swamp"); + + Biome JAGGED_PEAKS = BiomeImpl.get("minecraft:jagged_peaks"); + + Biome COLD_OCEAN = BiomeImpl.get("minecraft:cold_ocean"); + + Biome FOREST = BiomeImpl.get("minecraft:forest"); + + Biome LUSH_CAVES = BiomeImpl.get("minecraft:lush_caves"); + + Biome BASALT_DELTAS = BiomeImpl.get("minecraft:basalt_deltas"); + + Biome DEEP_COLD_OCEAN = BiomeImpl.get("minecraft:deep_cold_ocean"); + + Biome ICE_SPIKES = BiomeImpl.get("minecraft:ice_spikes"); + + Biome END_MIDLANDS = BiomeImpl.get("minecraft:end_midlands"); + + Biome FROZEN_OCEAN = BiomeImpl.get("minecraft:frozen_ocean"); + + Biome DESERT = BiomeImpl.get("minecraft:desert"); + + Biome DEEP_FROZEN_OCEAN = BiomeImpl.get("minecraft:deep_frozen_ocean"); + + Biome WINDSWEPT_FOREST = BiomeImpl.get("minecraft:windswept_forest"); + + Biome JUNGLE = BiomeImpl.get("minecraft:jungle"); + + Biome OCEAN = BiomeImpl.get("minecraft:ocean"); + + Biome OLD_GROWTH_SPRUCE_TAIGA = BiomeImpl.get("minecraft:old_growth_spruce_taiga"); + + Biome SNOWY_BEACH = BiomeImpl.get("minecraft:snowy_beach"); + + Biome WINDSWEPT_SAVANNA = BiomeImpl.get("minecraft:windswept_savanna"); + + Biome END_BARRENS = BiomeImpl.get("minecraft:end_barrens"); + + Biome WARM_OCEAN = BiomeImpl.get("minecraft:warm_ocean"); + + Biome DEEP_LUKEWARM_OCEAN = BiomeImpl.get("minecraft:deep_lukewarm_ocean"); + + Biome FLOWER_FOREST = BiomeImpl.get("minecraft:flower_forest"); + + Biome SOUL_SAND_VALLEY = BiomeImpl.get("minecraft:soul_sand_valley"); + + Biome NETHER_WASTES = BiomeImpl.get("minecraft:nether_wastes"); + + Biome FROZEN_PEAKS = BiomeImpl.get("minecraft:frozen_peaks"); + + Biome THE_END = BiomeImpl.get("minecraft:the_end"); + + Biome SMALL_END_ISLANDS = BiomeImpl.get("minecraft:small_end_islands"); + + Biome OLD_GROWTH_BIRCH_FOREST = BiomeImpl.get("minecraft:old_growth_birch_forest"); + + Biome CRIMSON_FOREST = BiomeImpl.get("minecraft:crimson_forest"); + + Biome THE_VOID = BiomeImpl.get("minecraft:the_void"); + + Biome DEEP_DARK = BiomeImpl.get("minecraft:deep_dark"); + + Biome MEADOW = BiomeImpl.get("minecraft:meadow"); + + Biome WINDSWEPT_GRAVELLY_HILLS = BiomeImpl.get("minecraft:windswept_gravelly_hills"); + + Biome SAVANNA = BiomeImpl.get("minecraft:savanna"); + + Biome PLAINS = BiomeImpl.get("minecraft:plains"); +} diff --git a/src/main/java/net/minestom/server/command/builder/arguments/Argument.java b/src/main/java/net/minestom/server/command/builder/arguments/Argument.java index fe4e34b12..c52b204f7 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/Argument.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/Argument.java @@ -7,7 +7,7 @@ import net.minestom.server.command.builder.CommandExecutor; import net.minestom.server.command.builder.arguments.minecraft.SuggestionType; import net.minestom.server.command.builder.exception.ArgumentSyntaxException; import net.minestom.server.command.builder.suggestion.SuggestionCallback; -import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.StaticProtocolObject; import net.minestom.server.registry.Registry; import net.minestom.server.utils.NamespaceID; import org.jetbrains.annotations.ApiStatus; @@ -30,10 +30,10 @@ import java.util.function.Supplier; */ public abstract class Argument { @ApiStatus.Internal - public static final Registry.Container CONTAINER = Registry.createContainer(Registry.Resource.COMMAND_ARGUMENTS, + public static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.COMMAND_ARGUMENTS, (namespace, properties) -> new ArgumentImpl(NamespaceID.from(namespace), properties.getInt("id"))); - record ArgumentImpl(NamespaceID namespace, int id) implements ProtocolObject { + record ArgumentImpl(NamespaceID namespace, int id) implements StaticProtocolObject { @Override public String toString() { return name(); diff --git a/src/main/java/net/minestom/server/entity/EntityType.java b/src/main/java/net/minestom/server/entity/EntityType.java index d6c899715..fef2e66db 100644 --- a/src/main/java/net/minestom/server/entity/EntityType.java +++ b/src/main/java/net/minestom/server/entity/EntityType.java @@ -1,6 +1,6 @@ package net.minestom.server.entity; -import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.StaticProtocolObject; import net.minestom.server.registry.Registry; import net.minestom.server.utils.NamespaceID; import org.jetbrains.annotations.Contract; @@ -9,7 +9,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Collection; -public sealed interface EntityType extends ProtocolObject, EntityTypes permits EntityTypeImpl { +public sealed interface EntityType extends StaticProtocolObject, EntityTypes permits EntityTypeImpl { /** * Returns the entity registry. * diff --git a/src/main/java/net/minestom/server/entity/EntityTypeImpl.java b/src/main/java/net/minestom/server/entity/EntityTypeImpl.java index 7314d3544..d919c6fee 100644 --- a/src/main/java/net/minestom/server/entity/EntityTypeImpl.java +++ b/src/main/java/net/minestom/server/entity/EntityTypeImpl.java @@ -42,7 +42,7 @@ import java.util.function.BiFunction; import static java.util.Map.entry; record EntityTypeImpl(Registry.EntityEntry registry) implements EntityType { - private static final Registry.Container CONTAINER = Registry.createContainer(Registry.Resource.ENTITIES, + private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.ENTITIES, (namespace, properties) -> new EntityTypeImpl(Registry.entity(namespace, properties))); static final Map> ENTITY_META_SUPPLIER = createMetaMap(); diff --git a/src/main/java/net/minestom/server/entity/damage/DamageType.java b/src/main/java/net/minestom/server/entity/damage/DamageType.java index deec1d4db..6f6fec6fd 100644 --- a/src/main/java/net/minestom/server/entity/damage/DamageType.java +++ b/src/main/java/net/minestom/server/entity/damage/DamageType.java @@ -1,6 +1,6 @@ package net.minestom.server.entity.damage; -import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.StaticProtocolObject; import net.minestom.server.registry.Registry; import net.minestom.server.utils.NamespaceID; import org.jetbrains.annotations.Contract; @@ -10,7 +10,7 @@ import org.jglrxavpok.hephaistos.nbt.NBTCompound; import java.util.Collection; -public sealed interface DamageType extends ProtocolObject, DamageTypes permits DamageTypeImpl { +public sealed interface DamageType extends StaticProtocolObject, DamageTypes permits DamageTypeImpl { /** * Returns the damage type registry. * diff --git a/src/main/java/net/minestom/server/entity/damage/DamageTypeImpl.java b/src/main/java/net/minestom/server/entity/damage/DamageTypeImpl.java index 2873cad2d..70f97ff4c 100644 --- a/src/main/java/net/minestom/server/entity/damage/DamageTypeImpl.java +++ b/src/main/java/net/minestom/server/entity/damage/DamageTypeImpl.java @@ -16,7 +16,7 @@ record DamageTypeImpl(Registry.DamageTypeEntry registry, int id) implements Dama static { AtomicInteger i = new AtomicInteger(); - CONTAINER = Registry.createContainer(Registry.Resource.DAMAGE_TYPES, + CONTAINER = Registry.createStaticContainer(Registry.Resource.DAMAGE_TYPES, (namespace, properties) -> new DamageTypeImpl(Registry.damageType(namespace, properties), i.getAndIncrement())); } diff --git a/src/main/java/net/minestom/server/instance/AnvilLoader.java b/src/main/java/net/minestom/server/instance/AnvilLoader.java index e7af40f3e..93048a71b 100644 --- a/src/main/java/net/minestom/server/instance/AnvilLoader.java +++ b/src/main/java/net/minestom/server/instance/AnvilLoader.java @@ -34,7 +34,7 @@ import java.util.concurrent.ConcurrentHashMap; public class AnvilLoader implements IChunkLoader { private final static Logger LOGGER = LoggerFactory.getLogger(AnvilLoader.class); - private static final Biome BIOME = Biome.PLAINS; + private final static Biome PLAINS = MinecraftServer.getBiomeManager().getByName(NamespaceID.from("minecraft:plains")); private final Map alreadyLoaded = new ConcurrentHashMap<>(); private final Path path; @@ -189,7 +189,7 @@ public class AnvilLoader implements IChunkLoader { int finalY = sectionY * Chunk.CHUNK_SECTION_SIZE + y; String biomeName = sectionBiomeInformation.getBaseBiome(); Biome biome = biomeCache.computeIfAbsent(biomeName, n -> - Objects.requireNonNullElse(MinecraftServer.getBiomeManager().getByName(NamespaceID.from(n)), BIOME)); + Objects.requireNonNullElse(MinecraftServer.getBiomeManager().getByName(NamespaceID.from(n)), PLAINS)); chunk.setBiome(finalX, finalY, finalZ, biome); } } @@ -205,7 +205,7 @@ public class AnvilLoader implements IChunkLoader { int index = x / 4 + (z / 4) * 4 + (y / 4) * 16; String biomeName = sectionBiomeInformation.getBiomes()[index]; Biome biome = biomeCache.computeIfAbsent(biomeName, n -> - Objects.requireNonNullElse(MinecraftServer.getBiomeManager().getByName(NamespaceID.from(n)), BIOME)); + Objects.requireNonNullElse(MinecraftServer.getBiomeManager().getByName(NamespaceID.from(n)), PLAINS)); chunk.setBiome(finalX, finalY, finalZ, biome); } } @@ -400,7 +400,7 @@ public class AnvilLoader implements IChunkLoader { if (x % 4 == 0 && sectionLocalY % 4 == 0 && z % 4 == 0) { int biomeIndex = (x / 4) + (sectionLocalY / 4) * 4 * 4 + (z / 4) * 4; final Biome biome = chunk.getBiome(x, y, z); - final String biomeName = biome.name().asString(); + final String biomeName = biome.name(); biomePalette.increaseReference(biomeName); palettedBiomes[biomeIndex] = biomePalette.getPaletteIndex(biomeName); diff --git a/src/main/java/net/minestom/server/instance/DynamicChunk.java b/src/main/java/net/minestom/server/instance/DynamicChunk.java index f8b447dfa..87768d763 100644 --- a/src/main/java/net/minestom/server/instance/DynamicChunk.java +++ b/src/main/java/net/minestom/server/instance/DynamicChunk.java @@ -24,6 +24,7 @@ import net.minestom.server.utils.MathUtils; import net.minestom.server.utils.ObjectPool; import net.minestom.server.utils.chunk.ChunkUtils; import net.minestom.server.world.biomes.Biome; +import net.minestom.server.world.biomes.BiomeManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jglrxavpok.hephaistos.nbt.NBT; @@ -51,6 +52,7 @@ public class DynamicChunk extends Chunk { private long lastChange; final CachedPacket chunkCache = new CachedPacket(this::createChunkPacket); + private static final BiomeManager BIOME_MANAGER = MinecraftServer.getBiomeManager(); public DynamicChunk(@NotNull Instance instance, int chunkX, int chunkZ) { super(instance, chunkX, chunkZ, true); @@ -124,10 +126,14 @@ public class DynamicChunk extends Chunk { assertLock(); this.chunkCache.invalidate(); Section section = getSectionAt(y); + + var id = BIOME_MANAGER.getId(biome); + if (id == -1) throw new IllegalStateException("Biome has not been registered: " + biome.namespace()); + section.biomePalette().set( toSectionRelativeCoordinate(x) / 4, toSectionRelativeCoordinate(y) / 4, - toSectionRelativeCoordinate(z) / 4, biome.id()); + toSectionRelativeCoordinate(z) / 4, id); } @Override @@ -180,7 +186,13 @@ public class DynamicChunk extends Chunk { final Section section = getSectionAt(y); final int id = section.biomePalette() .get(toSectionRelativeCoordinate(x) / 4, toSectionRelativeCoordinate(y) / 4, toSectionRelativeCoordinate(z) / 4); - return MinecraftServer.getBiomeManager().getById(id); + + Biome biome = BIOME_MANAGER.getById(id); + if (biome == null) { + throw new IllegalStateException("Biome with id " + id + " is not registered"); + } + + return biome; } @Override diff --git a/src/main/java/net/minestom/server/instance/GeneratorImpl.java b/src/main/java/net/minestom/server/instance/GeneratorImpl.java index e4169b8d9..e01933a8a 100644 --- a/src/main/java/net/minestom/server/instance/GeneratorImpl.java +++ b/src/main/java/net/minestom/server/instance/GeneratorImpl.java @@ -2,6 +2,7 @@ package net.minestom.server.instance; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import net.minestom.server.MinecraftServer; import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Vec; import net.minestom.server.instance.block.Block; @@ -9,6 +10,7 @@ import net.minestom.server.instance.generator.GenerationUnit; import net.minestom.server.instance.generator.UnitModifier; import net.minestom.server.instance.palette.Palette; import net.minestom.server.world.biomes.Biome; +import net.minestom.server.world.biomes.BiomeManager; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -21,6 +23,7 @@ import static net.minestom.server.utils.chunk.ChunkUtils.*; final class GeneratorImpl { private static final Vec SECTION_SIZE = new Vec(16); + private static final BiomeManager BIOME_MANAGER = MinecraftServer.getBiomeManager(); static GenerationUnit section(Section section, int sectionX, int sectionY, int sectionZ, boolean fork) { @@ -217,10 +220,13 @@ final class GeneratorImpl { @Override public void setBiome(int x, int y, int z, @NotNull Biome biome) { if (fork) throw new IllegalStateException("Cannot modify biomes of a fork"); + var id = BIOME_MANAGER.getId(biome); + if (id == -1) throw new IllegalStateException("Biome has not been registered: " + biome.namespace()); + this.biomePalette.set( toSectionRelativeCoordinate(x) / 4, toSectionRelativeCoordinate(y) / 4, - toSectionRelativeCoordinate(z) / 4, biome.id()); + toSectionRelativeCoordinate(z) / 4, id); } @Override @@ -264,7 +270,9 @@ final class GeneratorImpl { @Override public void fillBiome(@NotNull Biome biome) { if (fork) throw new IllegalStateException("Cannot modify biomes of a fork"); - this.biomePalette.fill(biome.id()); + var id = MinecraftServer.getBiomeManager().getId(biome); + if (id == -1) throw new IllegalStateException("Biome has not been registered: " + biome.namespace()); + this.biomePalette.fill(id); } private int retrieveBlockId(Block block) { diff --git a/src/main/java/net/minestom/server/instance/block/Block.java b/src/main/java/net/minestom/server/instance/block/Block.java index 967a64357..2f4f90cfb 100644 --- a/src/main/java/net/minestom/server/instance/block/Block.java +++ b/src/main/java/net/minestom/server/instance/block/Block.java @@ -3,7 +3,7 @@ package net.minestom.server.instance.block; import net.minestom.server.coordinate.Point; import net.minestom.server.instance.Instance; import net.minestom.server.instance.batch.Batch; -import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.StaticProtocolObject; import net.minestom.server.registry.Registry; import net.minestom.server.tag.Tag; import net.minestom.server.tag.TagReadable; @@ -23,7 +23,7 @@ import java.util.function.BiPredicate; *

* Implementations are expected to be immutable. */ -public sealed interface Block extends ProtocolObject, TagReadable, Blocks permits BlockImpl { +public sealed interface Block extends StaticProtocolObject, TagReadable, Blocks permits BlockImpl { /** * Creates a new block with the the property {@code property} sets to {@code value}. diff --git a/src/main/java/net/minestom/server/instance/block/BlockImpl.java b/src/main/java/net/minestom/server/instance/block/BlockImpl.java index c700a6c70..6140b7fbe 100644 --- a/src/main/java/net/minestom/server/instance/block/BlockImpl.java +++ b/src/main/java/net/minestom/server/instance/block/BlockImpl.java @@ -31,7 +31,7 @@ record BlockImpl(@NotNull Registry.BlockEntry registry, private static final ObjectArray PROPERTIES_TYPE = ObjectArray.singleThread(); // Block id -> Map private static final ObjectArray> POSSIBLE_STATES = ObjectArray.singleThread(); - private static final Registry.Container CONTAINER = Registry.createContainer(Registry.Resource.BLOCKS, + private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.BLOCKS, (namespace, properties) -> { final int blockId = properties.getInt("id"); final Registry.Properties stateObject = properties.section("states"); diff --git a/src/main/java/net/minestom/server/item/Enchantment.java b/src/main/java/net/minestom/server/item/Enchantment.java index 0acf67da9..39e27ac66 100644 --- a/src/main/java/net/minestom/server/item/Enchantment.java +++ b/src/main/java/net/minestom/server/item/Enchantment.java @@ -1,6 +1,6 @@ package net.minestom.server.item; -import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.StaticProtocolObject; import net.minestom.server.registry.Registry; import net.minestom.server.utils.NamespaceID; import org.jetbrains.annotations.Contract; @@ -9,7 +9,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Collection; -public sealed interface Enchantment extends ProtocolObject, Enchantments permits EnchantmentImpl { +public sealed interface Enchantment extends StaticProtocolObject, Enchantments permits EnchantmentImpl { /** * Returns the enchantment registry. diff --git a/src/main/java/net/minestom/server/item/EnchantmentImpl.java b/src/main/java/net/minestom/server/item/EnchantmentImpl.java index b29f07df8..4a82d4efc 100644 --- a/src/main/java/net/minestom/server/item/EnchantmentImpl.java +++ b/src/main/java/net/minestom/server/item/EnchantmentImpl.java @@ -6,7 +6,7 @@ import org.jetbrains.annotations.NotNull; import java.util.Collection; record EnchantmentImpl(Registry.EnchantmentEntry registry) implements Enchantment { - private static final Registry.Container CONTAINER = Registry.createContainer(Registry.Resource.ENCHANTMENTS, + private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.ENCHANTMENTS, (namespace, properties) -> new EnchantmentImpl(Registry.enchantment(namespace, properties))); static Enchantment get(@NotNull String namespace) { diff --git a/src/main/java/net/minestom/server/item/ItemMeta.java b/src/main/java/net/minestom/server/item/ItemMeta.java index 65d37d2d4..bb2a99038 100644 --- a/src/main/java/net/minestom/server/item/ItemMeta.java +++ b/src/main/java/net/minestom/server/item/ItemMeta.java @@ -4,7 +4,7 @@ import net.kyori.adventure.text.Component; import net.minestom.server.instance.block.Block; import net.minestom.server.item.attribute.ItemAttribute; import net.minestom.server.network.NetworkBuffer; -import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.StaticProtocolObject; import net.minestom.server.tag.Tag; import net.minestom.server.tag.TagReadable; import net.minestom.server.tag.Taggable; @@ -165,7 +165,7 @@ public sealed interface ItemMeta extends TagReadable, NetworkBuffer.Writer @Contract("_ -> this") default @NotNull Builder canPlaceOn(@NotNull Set<@NotNull Block> blocks) { - return set(ItemTags.CAN_PLACE_ON, blocks.stream().map(ProtocolObject::name).toList()); + return set(ItemTags.CAN_PLACE_ON, blocks.stream().map(StaticProtocolObject::name).toList()); } @Contract("_ -> this") @@ -175,7 +175,7 @@ public sealed interface ItemMeta extends TagReadable, NetworkBuffer.Writer @Contract("_ -> this") default @NotNull Builder canDestroy(@NotNull Set<@NotNull Block> blocks) { - return set(ItemTags.CAN_DESTROY, blocks.stream().map(ProtocolObject::name).toList()); + return set(ItemTags.CAN_DESTROY, blocks.stream().map(StaticProtocolObject::name).toList()); } @Contract("_ -> this") diff --git a/src/main/java/net/minestom/server/item/Material.java b/src/main/java/net/minestom/server/item/Material.java index a0d46c807..efb8eab74 100644 --- a/src/main/java/net/minestom/server/item/Material.java +++ b/src/main/java/net/minestom/server/item/Material.java @@ -1,7 +1,7 @@ package net.minestom.server.item; import net.minestom.server.instance.block.Block; -import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.StaticProtocolObject; import net.minestom.server.registry.Registry; import net.minestom.server.utils.NamespaceID; import org.jetbrains.annotations.Contract; @@ -10,7 +10,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Collection; -public sealed interface Material extends ProtocolObject, Materials permits MaterialImpl { +public sealed interface Material extends StaticProtocolObject, Materials permits MaterialImpl { /** * Returns the material registry. diff --git a/src/main/java/net/minestom/server/item/MaterialImpl.java b/src/main/java/net/minestom/server/item/MaterialImpl.java index 98d371a2a..38c1c8236 100644 --- a/src/main/java/net/minestom/server/item/MaterialImpl.java +++ b/src/main/java/net/minestom/server/item/MaterialImpl.java @@ -6,7 +6,7 @@ import org.jetbrains.annotations.NotNull; import java.util.Collection; record MaterialImpl(Registry.MaterialEntry registry) implements Material { - private static final Registry.Container CONTAINER = Registry.createContainer(Registry.Resource.ITEMS, + private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.ITEMS, (namespace, properties) -> new MaterialImpl(Registry.material(namespace, properties))); static Material get(@NotNull String namespace) { diff --git a/src/main/java/net/minestom/server/item/armor/TrimMaterial.java b/src/main/java/net/minestom/server/item/armor/TrimMaterial.java index 23effa3c3..adb80be3d 100644 --- a/src/main/java/net/minestom/server/item/armor/TrimMaterial.java +++ b/src/main/java/net/minestom/server/item/armor/TrimMaterial.java @@ -2,7 +2,7 @@ package net.minestom.server.item.armor; import net.kyori.adventure.text.Component; import net.minestom.server.item.Material; -import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.StaticProtocolObject; import net.minestom.server.registry.Registry; import net.minestom.server.utils.NamespaceID; import org.jetbrains.annotations.Contract; @@ -12,7 +12,7 @@ import org.jglrxavpok.hephaistos.nbt.NBTCompound; import java.util.Collection; import java.util.Map; -public interface TrimMaterial extends ProtocolObject { +public interface TrimMaterial extends StaticProtocolObject { static @NotNull TrimMaterial create(@NotNull NamespaceID namespace, @NotNull String assetName, @NotNull Material ingredient, diff --git a/src/main/java/net/minestom/server/item/armor/TrimMaterialImpl.java b/src/main/java/net/minestom/server/item/armor/TrimMaterialImpl.java index 89ff795db..95fe79b4d 100644 --- a/src/main/java/net/minestom/server/item/armor/TrimMaterialImpl.java +++ b/src/main/java/net/minestom/server/item/armor/TrimMaterialImpl.java @@ -15,7 +15,7 @@ record TrimMaterialImpl(Registry.TrimMaterialEntry registry, int id) implements private static final Registry.Container CONTAINER; static { - CONTAINER = Registry.createContainer(Registry.Resource.TRIM_MATERIALS, + CONTAINER = Registry.createStaticContainer(Registry.Resource.TRIM_MATERIALS, (namespace, properties) -> new TrimMaterialImpl(Registry.trimMaterial(namespace, properties))); } diff --git a/src/main/java/net/minestom/server/item/armor/TrimPattern.java b/src/main/java/net/minestom/server/item/armor/TrimPattern.java index 590bd2dd1..1823e42bc 100644 --- a/src/main/java/net/minestom/server/item/armor/TrimPattern.java +++ b/src/main/java/net/minestom/server/item/armor/TrimPattern.java @@ -2,7 +2,7 @@ package net.minestom.server.item.armor; import net.kyori.adventure.text.Component; import net.minestom.server.item.Material; -import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.StaticProtocolObject; import net.minestom.server.registry.Registry; import net.minestom.server.utils.NamespaceID; import org.jetbrains.annotations.Contract; @@ -11,7 +11,7 @@ import org.jglrxavpok.hephaistos.nbt.NBTCompound; import java.util.Collection; -public interface TrimPattern extends ProtocolObject { +public interface TrimPattern extends StaticProtocolObject { static @NotNull TrimPattern create(@NotNull NamespaceID namespace, @NotNull NamespaceID assetID, @NotNull Material template, diff --git a/src/main/java/net/minestom/server/item/armor/TrimPatternImpl.java b/src/main/java/net/minestom/server/item/armor/TrimPatternImpl.java index 304d65b3f..6b4ecd1aa 100644 --- a/src/main/java/net/minestom/server/item/armor/TrimPatternImpl.java +++ b/src/main/java/net/minestom/server/item/armor/TrimPatternImpl.java @@ -13,7 +13,7 @@ record TrimPatternImpl(Registry.TrimPatternEntry registry, int id) implements Tr private static final Registry.Container CONTAINER; static { - CONTAINER = Registry.createContainer(Registry.Resource.TRIM_PATTERNS, + CONTAINER = Registry.createStaticContainer(Registry.Resource.TRIM_PATTERNS, (namespace, properties) -> new TrimPatternImpl(Registry.trimPattern(namespace, properties))); } diff --git a/src/main/java/net/minestom/server/item/metadata/PotionMeta.java b/src/main/java/net/minestom/server/item/metadata/PotionMeta.java index 5b2b08899..a4ecfcc2e 100644 --- a/src/main/java/net/minestom/server/item/metadata/PotionMeta.java +++ b/src/main/java/net/minestom/server/item/metadata/PotionMeta.java @@ -4,7 +4,7 @@ import net.minestom.server.color.Color; import net.minestom.server.item.ItemMetaView; import net.minestom.server.potion.CustomPotionEffect; import net.minestom.server.potion.PotionType; -import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.StaticProtocolObject; import net.minestom.server.tag.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -13,7 +13,7 @@ import org.jetbrains.annotations.UnknownNullability; import java.util.List; public record PotionMeta(TagReadable readable) implements ItemMetaView { - private static final Tag POTION_TYPE = Tag.String("Potion").map(PotionType::fromNamespaceId, ProtocolObject::name).defaultValue(PotionType.EMPTY); + private static final Tag POTION_TYPE = Tag.String("Potion").map(PotionType::fromNamespaceId, StaticProtocolObject::name).defaultValue(PotionType.EMPTY); private static final Tag> CUSTOM_POTION_EFFECTS = Tag.Structure("CustomPotionEffects", new TagSerializer() { @Override public @Nullable CustomPotionEffect read(@NotNull TagReadable reader) { diff --git a/src/main/java/net/minestom/server/network/packet/server/play/DeclareCommandsPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/DeclareCommandsPacket.java index 91478fb12..b5705a9ad 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/DeclareCommandsPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/DeclareCommandsPacket.java @@ -5,7 +5,7 @@ import net.minestom.server.network.ConnectionState; import net.minestom.server.network.NetworkBuffer; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; -import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.StaticProtocolObject; import net.minestom.server.utils.PacketUtils; import org.jetbrains.annotations.NotNull; @@ -93,7 +93,7 @@ public record DeclareCommandsPacket(@NotNull List nodes, } if (isArgument()) { - final ProtocolObject object = Argument.CONTAINER.getId(reader.read(VAR_INT)); + final StaticProtocolObject object = Argument.CONTAINER.getId(reader.read(VAR_INT)); parser = object.name(); properties = getProperties(reader, parser); } diff --git a/src/main/java/net/minestom/server/particle/Particle.java b/src/main/java/net/minestom/server/particle/Particle.java index 0839f54be..cf9562c92 100644 --- a/src/main/java/net/minestom/server/particle/Particle.java +++ b/src/main/java/net/minestom/server/particle/Particle.java @@ -1,13 +1,13 @@ package net.minestom.server.particle; -import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.StaticProtocolObject; import net.minestom.server.utils.NamespaceID; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; -public sealed interface Particle extends ProtocolObject, Particles permits ParticleImpl { +public sealed interface Particle extends StaticProtocolObject, Particles permits ParticleImpl { static @NotNull Collection<@NotNull Particle> values() { return ParticleImpl.values(); diff --git a/src/main/java/net/minestom/server/particle/ParticleImpl.java b/src/main/java/net/minestom/server/particle/ParticleImpl.java index 2a41dabfe..82348e115 100644 --- a/src/main/java/net/minestom/server/particle/ParticleImpl.java +++ b/src/main/java/net/minestom/server/particle/ParticleImpl.java @@ -7,7 +7,7 @@ import org.jetbrains.annotations.NotNull; import java.util.Collection; record ParticleImpl(NamespaceID namespace, int id) implements Particle { - private static final Registry.Container CONTAINER = Registry.createContainer(Registry.Resource.PARTICLES, + private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.PARTICLES, (namespace, properties) -> new ParticleImpl(NamespaceID.from(namespace), properties.getInt("id"))); static Particle get(@NotNull String namespace) { diff --git a/src/main/java/net/minestom/server/potion/PotionEffect.java b/src/main/java/net/minestom/server/potion/PotionEffect.java index e0d9a2a58..0fa9778ce 100644 --- a/src/main/java/net/minestom/server/potion/PotionEffect.java +++ b/src/main/java/net/minestom/server/potion/PotionEffect.java @@ -1,6 +1,6 @@ package net.minestom.server.potion; -import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.StaticProtocolObject; import net.minestom.server.registry.Registry; import net.minestom.server.utils.NamespaceID; import org.jetbrains.annotations.Contract; @@ -9,7 +9,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Collection; -public sealed interface PotionEffect extends ProtocolObject, PotionEffects permits PotionEffectImpl { +public sealed interface PotionEffect extends StaticProtocolObject, PotionEffects permits PotionEffectImpl { @Contract(pure = true) @NotNull Registry.PotionEffectEntry registry(); diff --git a/src/main/java/net/minestom/server/potion/PotionEffectImpl.java b/src/main/java/net/minestom/server/potion/PotionEffectImpl.java index 6396afc14..0e90e62d1 100644 --- a/src/main/java/net/minestom/server/potion/PotionEffectImpl.java +++ b/src/main/java/net/minestom/server/potion/PotionEffectImpl.java @@ -6,7 +6,7 @@ import org.jetbrains.annotations.NotNull; import java.util.Collection; record PotionEffectImpl(Registry.PotionEffectEntry registry) implements PotionEffect { - private static final Registry.Container CONTAINER = Registry.createContainer(Registry.Resource.POTION_EFFECTS, + private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.POTION_EFFECTS, (namespace, properties) -> new PotionEffectImpl(Registry.potionEffect(namespace, properties))); static PotionEffect get(@NotNull String namespace) { diff --git a/src/main/java/net/minestom/server/potion/PotionType.java b/src/main/java/net/minestom/server/potion/PotionType.java index 0e32dc69c..5e28ba308 100644 --- a/src/main/java/net/minestom/server/potion/PotionType.java +++ b/src/main/java/net/minestom/server/potion/PotionType.java @@ -1,13 +1,13 @@ package net.minestom.server.potion; -import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.StaticProtocolObject; import net.minestom.server.utils.NamespaceID; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; -public sealed interface PotionType extends ProtocolObject, PotionTypes permits PotionTypeImpl { +public sealed interface PotionType extends StaticProtocolObject, PotionTypes permits PotionTypeImpl { static @NotNull Collection<@NotNull PotionType> values() { return PotionTypeImpl.values(); diff --git a/src/main/java/net/minestom/server/potion/PotionTypeImpl.java b/src/main/java/net/minestom/server/potion/PotionTypeImpl.java index daf3f1ec7..1cb427ef9 100644 --- a/src/main/java/net/minestom/server/potion/PotionTypeImpl.java +++ b/src/main/java/net/minestom/server/potion/PotionTypeImpl.java @@ -7,7 +7,7 @@ import org.jetbrains.annotations.NotNull; import java.util.Collection; record PotionTypeImpl(NamespaceID namespace, int id) implements PotionType { - private static final Registry.Container CONTAINER = Registry.createContainer(Registry.Resource.POTION_TYPES, + private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.POTION_TYPES, (namespace, properties) -> new PotionTypeImpl(NamespaceID.from(namespace), properties.getInt("id"))); static PotionType get(@NotNull String namespace) { diff --git a/src/main/java/net/minestom/server/registry/ProtocolObject.java b/src/main/java/net/minestom/server/registry/ProtocolObject.java index ab201b9b9..e88dba911 100644 --- a/src/main/java/net/minestom/server/registry/ProtocolObject.java +++ b/src/main/java/net/minestom/server/registry/ProtocolObject.java @@ -11,17 +11,14 @@ public interface ProtocolObject extends Keyed { @Contract(pure = true) @NotNull NamespaceID namespace(); - @Override - @Contract(pure = true) - default @NotNull Key key() { - return namespace(); - } - @Contract(pure = true) default @NotNull String name() { return namespace().asString(); } + @Override @Contract(pure = true) - int id(); + default @NotNull Key key() { + return namespace(); + } } diff --git a/src/main/java/net/minestom/server/registry/Registry.java b/src/main/java/net/minestom/server/registry/Registry.java index e29de8d4d..aa3960ee2 100644 --- a/src/main/java/net/minestom/server/registry/Registry.java +++ b/src/main/java/net/minestom/server/registry/Registry.java @@ -28,7 +28,7 @@ import java.util.function.Supplier; import java.util.stream.Collectors; /** - * Handles registry data, used by {@link ProtocolObject} implementations and is strictly internal. + * Handles registry data, used by {@link StaticProtocolObject} implementations and is strictly internal. * Use at your own risk. */ public final class Registry { @@ -37,6 +37,11 @@ public final class Registry { return new BlockEntry(namespace, main, null); } + @ApiStatus.Internal + public static BiomeEntry biome(String namespace, Properties properties) { + return new BiomeEntry(namespace, properties, null); + } + @ApiStatus.Internal public static MaterialEntry material(String namespace, @NotNull Properties main) { return new MaterialEntry(namespace, main, null); @@ -89,7 +94,7 @@ public final class Registry { } @ApiStatus.Internal - public static Container createContainer(Resource resource, Container.Loader loader) { + public static Container createStaticContainer(Resource resource, Container.Loader loader) { var entries = Registry.load(resource); Map namespaces = new HashMap<>(entries.size()); ObjectArray ids = ObjectArray.singleThread(entries.size()); @@ -104,9 +109,9 @@ public final class Registry { } @ApiStatus.Internal - public record Container(Resource resource, - Map namespaces, - ObjectArray ids) { + public record Container(Resource resource, + Map namespaces, + ObjectArray ids) { public Container { namespaces = Map.copyOf(namespaces); ids.trim(); @@ -149,6 +154,54 @@ public final class Registry { } } + @ApiStatus.Internal + public static DynamicContainer createDynamicContainer(Resource resource, Container.Loader loader) { + var entries = Registry.load(resource); + Map namespaces = new HashMap<>(entries.size()); + for (var entry : entries.entrySet()) { + final String namespace = entry.getKey(); + final Properties properties = Properties.fromMap(entry.getValue()); + final T value = loader.get(namespace, properties); + namespaces.put(value.name(), value); + } + return new DynamicContainer<>(resource, namespaces); + } + + @ApiStatus.Internal + public record DynamicContainer(Resource resource, Map namespaces) { + public DynamicContainer { + namespaces = Map.copyOf(namespaces); + } + + public T get(@NotNull String namespace) { + return namespaces.get(namespace); + } + + public T getSafe(@NotNull String namespace) { + return get(namespace.contains(":") ? namespace : "minecraft:" + namespace); + } + + public Collection values() { + return namespaces.values(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Container container)) return false; + return resource == container.resource; + } + + @Override + public int hashCode() { + return Objects.hash(resource); + } + + public interface Loader { + T get(String namespace, Properties properties); + } + } + @ApiStatus.Internal public enum Resource { BLOCKS("blocks.json"), @@ -168,7 +221,8 @@ public final class Registry { ENTITY_TYPE_TAGS("tags/entity_type_tags.json"), FLUID_TAGS("tags/fluid_tags.json"), GAMEPLAY_TAGS("tags/gameplay_tags.json"), - ITEM_TAGS("tags/item_tags.json"); + ITEM_TAGS("tags/item_tags.json"), + BIOMES("biomes.json"); private final String name; @@ -323,6 +377,81 @@ public final class Registry { } } + public static final class BiomeEntry implements Entry { + private final Properties custom; + private final NamespaceID namespace; + private final Integer foliageColor; + private final Integer grassColor; + private final Integer skyColor; + private final Integer waterColor; + private final Integer waterFogColor; + private final Integer fogColor; + private final float temperature; + private final float downfall; + private final boolean hasPrecipitation; + + private BiomeEntry(String namespace, Properties main, Properties custom) { + this.custom = custom; + this.namespace = NamespaceID.from(namespace); + + this.foliageColor = main.containsKey("foliageColor") ? main.getInt("foliageColor") : null; + this.grassColor = main.containsKey("grassColor") ? main.getInt("grassColor") : null; + this.skyColor = main.containsKey("skyColor") ? main.getInt("skyColor") : null; + this.waterColor = main.containsKey("waterColor") ? main.getInt("waterColor") : null; + this.waterFogColor = main.containsKey("waterFogColor") ? main.getInt("waterFogColor") : null; + this.fogColor = main.containsKey("fogColor") ? main.getInt("fogColor") : null; + + this.temperature = (float) main.getDouble("temperature", 0.5F); + this.downfall = (float) main.getDouble("downfall", 0.5F); + this.hasPrecipitation = main.getBoolean("has_precipitation", true); + } + + @Override + public Properties custom() { + return custom; + } + + public @NotNull NamespaceID namespace() { + return namespace; + } + + public @Nullable Integer foliageColor() { + return foliageColor; + } + + public @Nullable Integer grassColor() { + return grassColor; + } + + public @Nullable Integer skyColor() { + return skyColor; + } + + public @Nullable Integer waterColor() { + return waterColor; + } + + public @Nullable Integer waterFogColor() { + return waterFogColor; + } + + public @Nullable Integer fogColor() { + return fogColor; + } + + public float temperature() { + return temperature; + } + + public float downfall() { + return downfall; + } + + public boolean hasPrecipitation() { + return hasPrecipitation; + } + } + public static final class MaterialEntry implements Entry { private final NamespaceID namespace; private final int id; @@ -600,6 +729,11 @@ public final class Registry { return new PropertiesMap(map); } + @Override + public boolean containsKey(String name) { + return map.containsKey(name); + } + @Override public Map asMap() { return map; @@ -642,6 +776,8 @@ public final class Registry { Properties section(String name); + boolean containsKey(String name); + Map asMap(); @Override diff --git a/src/main/java/net/minestom/server/registry/StaticProtocolObject.java b/src/main/java/net/minestom/server/registry/StaticProtocolObject.java new file mode 100644 index 000000000..e7156e588 --- /dev/null +++ b/src/main/java/net/minestom/server/registry/StaticProtocolObject.java @@ -0,0 +1,9 @@ +package net.minestom.server.registry; + +import org.jetbrains.annotations.Contract; + +public interface StaticProtocolObject extends ProtocolObject { + + @Contract(pure = true) + int id(); +} diff --git a/src/main/java/net/minestom/server/sound/SoundEvent.java b/src/main/java/net/minestom/server/sound/SoundEvent.java index b46de721e..842790dc4 100644 --- a/src/main/java/net/minestom/server/sound/SoundEvent.java +++ b/src/main/java/net/minestom/server/sound/SoundEvent.java @@ -2,14 +2,14 @@ package net.minestom.server.sound; import net.kyori.adventure.key.Key; import net.kyori.adventure.sound.Sound; -import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.StaticProtocolObject; import net.minestom.server.utils.NamespaceID; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; -public sealed interface SoundEvent extends ProtocolObject, Sound.Type, SoundEvents permits SoundEventImpl { +public sealed interface SoundEvent extends StaticProtocolObject, Sound.Type, SoundEvents permits SoundEventImpl { static @NotNull Collection<@NotNull SoundEvent> values() { return SoundEventImpl.values(); @@ -29,6 +29,6 @@ public sealed interface SoundEvent extends ProtocolObject, Sound.Type, SoundEven @Override default @NotNull Key key() { - return ProtocolObject.super.key(); + return StaticProtocolObject.super.key(); } } diff --git a/src/main/java/net/minestom/server/sound/SoundEventImpl.java b/src/main/java/net/minestom/server/sound/SoundEventImpl.java index 586633f19..fb7bd952b 100644 --- a/src/main/java/net/minestom/server/sound/SoundEventImpl.java +++ b/src/main/java/net/minestom/server/sound/SoundEventImpl.java @@ -7,7 +7,7 @@ import org.jetbrains.annotations.NotNull; import java.util.Collection; record SoundEventImpl(NamespaceID namespace, int id) implements SoundEvent { - private static final Registry.Container CONTAINER = Registry.createContainer(Registry.Resource.SOUNDS, + private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.SOUNDS, (namespace, properties) -> new SoundEventImpl(NamespaceID.from(namespace), properties.getInt("id"))); static SoundEvent get(@NotNull String namespace) { diff --git a/src/main/java/net/minestom/server/statistic/StatisticType.java b/src/main/java/net/minestom/server/statistic/StatisticType.java index 9da3ae8af..8ed71121c 100644 --- a/src/main/java/net/minestom/server/statistic/StatisticType.java +++ b/src/main/java/net/minestom/server/statistic/StatisticType.java @@ -1,13 +1,13 @@ package net.minestom.server.statistic; -import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.StaticProtocolObject; import net.minestom.server.utils.NamespaceID; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; -public sealed interface StatisticType extends ProtocolObject, StatisticTypes permits StatisticTypeImpl { +public sealed interface StatisticType extends StaticProtocolObject, StatisticTypes permits StatisticTypeImpl { static @NotNull Collection<@NotNull StatisticType> values() { return StatisticTypeImpl.values(); diff --git a/src/main/java/net/minestom/server/statistic/StatisticTypeImpl.java b/src/main/java/net/minestom/server/statistic/StatisticTypeImpl.java index ab770034d..40606e211 100644 --- a/src/main/java/net/minestom/server/statistic/StatisticTypeImpl.java +++ b/src/main/java/net/minestom/server/statistic/StatisticTypeImpl.java @@ -7,7 +7,7 @@ import org.jetbrains.annotations.NotNull; import java.util.Collection; record StatisticTypeImpl(NamespaceID namespace, int id) implements StatisticType { - private static final Registry.Container CONTAINER = Registry.createContainer(Registry.Resource.STATISTICS, + private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.STATISTICS, (namespace, properties) -> new StatisticTypeImpl(NamespaceID.from(namespace), properties.getInt("id"))); static StatisticType get(@NotNull String namespace) { diff --git a/src/main/java/net/minestom/server/world/biomes/Biome.java b/src/main/java/net/minestom/server/world/biomes/Biome.java index c8142703f..2b942b980 100644 --- a/src/main/java/net/minestom/server/world/biomes/Biome.java +++ b/src/main/java/net/minestom/server/world/biomes/Biome.java @@ -1,206 +1,46 @@ package net.minestom.server.world.biomes; import net.minestom.server.coordinate.Point; +import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.Registry; import net.minestom.server.utils.NamespaceID; import net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jglrxavpok.hephaistos.nbt.NBT; import org.jglrxavpok.hephaistos.nbt.NBTCompound; import java.util.Locale; -import java.util.concurrent.atomic.AtomicInteger; -public final class Biome { - public static final AtomicInteger ID_COUNTER = new AtomicInteger(0); - private static final BiomeEffects DEFAULT_EFFECTS = BiomeEffects.builder() - .fogColor(0xC0D8FF) - .skyColor(0x78A7FF) - .waterColor(0x3F76E4) - .waterFogColor(0x50533) - .build(); +sealed public interface Biome extends ProtocolObject permits BiomeImpl { + /** + * Returns the entity registry. + * + * @return the entity registry or null if it was created with a builder + */ + @Contract(pure = true) + @Nullable Registry.BiomeEntry registry(); - //A plains biome has to be registered or else minecraft will crash - public static final Biome PLAINS = Biome.builder() - .category(Category.NONE) - .name(NamespaceID.from("minecraft:plains")) - .temperature(0.8F) - .downfall(0.4F) - .depth(0.125F) - .scale(0.05F) - .effects(DEFAULT_EFFECTS) - .build(); + @Override + @NotNull NamespaceID namespace(); + float depth(); + float temperature(); + float scale(); + float downfall(); + @NotNull BiomeEffects effects(); + @NotNull Precipitation precipitation(); + @NotNull TemperatureModifier temperatureModifier(); - private final int id = ID_COUNTER.getAndIncrement(); - - private final NamespaceID name; - private final float depth; - private final float temperature; - private final float scale; - private final float downfall; - private final Category category; - private final BiomeEffects effects; - private final Precipitation precipitation; - private final TemperatureModifier temperatureModifier; - - Biome(NamespaceID name, float depth, float temperature, float scale, float downfall, Category category, BiomeEffects effects, Precipitation precipitation, TemperatureModifier temperatureModifier) { - this.name = name; - this.depth = depth; - this.temperature = temperature; - this.scale = scale; - this.downfall = downfall; - this.category = category; - this.effects = effects; - this.precipitation = precipitation; - this.temperatureModifier = temperatureModifier; - } - - public static Builder builder() { - return new Builder(); - } - - public @NotNull NBTCompound toNbt() { - Check.notNull(name, "The biome namespace cannot be null"); - Check.notNull(effects, "The biome effects cannot be null"); - - return NBT.Compound(nbt -> { - nbt.setString("name", name.toString()); - nbt.setInt("id", id()); - - nbt.set("element", NBT.Compound(element -> { - element.setFloat("depth", depth); - element.setFloat("temperature", temperature); - element.setFloat("scale", scale); - element.setFloat("downfall", downfall); - element.setString("category", category.name().toLowerCase(Locale.ROOT)); - element.setByte("has_precipitation", (byte) (precipitation == Precipitation.NONE ? 0 : 1)); - element.setString("precipitation", precipitation.name().toLowerCase(Locale.ROOT)); - if (temperatureModifier != TemperatureModifier.NONE) - element.setString("temperature_modifier", temperatureModifier.name().toLowerCase(Locale.ROOT)); - element.set("effects", effects.toNbt()); - })); - }); - } - - public int id() { - return this.id; - } - - public NamespaceID name() { - return this.name; - } - - public float depth() { - return this.depth; - } - - public float temperature() { - return this.temperature; - } - - public float scale() { - return this.scale; - } - - public float downfall() { - return this.downfall; - } - - public Category category() { - return this.category; - } - - public BiomeEffects effects() { - return this.effects; - } - - public Precipitation precipitation() { - return this.precipitation; - } - - public TemperatureModifier temperatureModifier() { - return this.temperatureModifier; - } - - public enum Precipitation { + enum Precipitation { NONE, RAIN, SNOW; } - public enum Category { - NONE, TAIGA, EXTREME_HILLS, JUNGLE, MESA, PLAINS, - SAVANNA, ICY, THE_END, BEACH, FOREST, OCEAN, - DESERT, RIVER, SWAMP, MUSHROOM, NETHER, UNDERGROUND, - MOUNTAIN; - } - - public enum TemperatureModifier { + enum TemperatureModifier { NONE, FROZEN; } - public static final class Builder { - private NamespaceID name; - private float depth = 0.2f; - private float temperature = 0.25f; - private float scale = 0.2f; - private float downfall = 0.8f; - private Category category = Category.NONE; - private BiomeEffects effects = DEFAULT_EFFECTS; - private Precipitation precipitation = Precipitation.RAIN; - private TemperatureModifier temperatureModifier = TemperatureModifier.NONE; - - Builder() { - } - - public Builder name(NamespaceID name) { - this.name = name; - return this; - } - - public Builder depth(float depth) { - this.depth = depth; - return this; - } - - public Builder temperature(float temperature) { - this.temperature = temperature; - return this; - } - - public Builder scale(float scale) { - this.scale = scale; - return this; - } - - public Builder downfall(float downfall) { - this.downfall = downfall; - return this; - } - - public Builder category(Category category) { - this.category = category; - return this; - } - - public Builder effects(BiomeEffects effects) { - this.effects = effects; - return this; - } - - public Builder precipitation(Precipitation precipitation) { - this.precipitation = precipitation; - return this; - } - - public Builder temperatureModifier(TemperatureModifier temperatureModifier) { - this.temperatureModifier = temperatureModifier; - return this; - } - - public Biome build() { - return new Biome(name, depth, temperature, scale, downfall, category, effects, precipitation, temperatureModifier); - } - } - - public interface Setter { + interface Setter { void setBiome(int x, int y, int z, @NotNull Biome biome); default void setBiome(@NotNull Point blockPosition, @NotNull Biome biome) { @@ -208,11 +48,103 @@ public final class Biome { } } - public interface Getter { + interface Getter { @NotNull Biome getBiome(int x, int y, int z); default @NotNull Biome getBiome(@NotNull Point point) { return getBiome(point.blockX(), point.blockY(), point.blockZ()); } } + + default @NotNull NBTCompound toNbt() { + Check.notNull(name(), "The biome namespace cannot be null"); + Check.notNull(effects(), "The biome effects cannot be null"); + + return NBT.Compound(element -> { + element.setFloat("depth", depth()); + element.setFloat("temperature", temperature()); + element.setFloat("scale", scale()); + element.setFloat("downfall", downfall()); + element.setByte("has_precipitation", (byte) (precipitation() == Precipitation.NONE ? 0 : 1)); + element.setString("precipitation", precipitation().name().toLowerCase(Locale.ROOT)); + if (temperatureModifier() != TemperatureModifier.NONE) + element.setString("temperature_modifier", temperatureModifier().name().toLowerCase(Locale.ROOT)); + element.set("effects", effects().toNbt()); + }); + } + + static @NotNull Builder builder() { + return new Builder(); + } + + final class Builder { + private static final BiomeEffects DEFAULT_EFFECTS = BiomeEffects.builder() + .fogColor(0xC0D8FF) + .skyColor(0x78A7FF) + .waterColor(0x3F76E4) + .waterFogColor(0x50533) + .build(); + + private NamespaceID name; + private float depth = 0.2f; + private float temperature = 0.25f; + private float scale = 0.2f; + private float downfall = 0.8f; + private BiomeEffects effects = DEFAULT_EFFECTS; + private Precipitation precipitation = Precipitation.RAIN; + private TemperatureModifier temperatureModifier = TemperatureModifier.NONE; + + @Contract(value = "_ -> this", pure = true) + public @NotNull Builder name(@NotNull NamespaceID name) { + this.name = name; + return this; + } + + @Contract(value = "_ -> this", pure = true) + public @NotNull Builder depth(float depth) { + this.depth = depth; + return this; + } + + @Contract(value = "_ -> this", pure = true) + public @NotNull Builder temperature(float temperature) { + this.temperature = temperature; + return this; + } + + @Contract(value = "_ -> this", pure = true) + public @NotNull Builder scale(float scale) { + this.scale = scale; + return this; + } + + @Contract(value = "_ -> this", pure = true) + public @NotNull Builder downfall(float downfall) { + this.downfall = downfall; + return this; + } + + @Contract(value = "_ -> this", pure = true) + public @NotNull Builder effects(@NotNull BiomeEffects effects) { + this.effects = effects; + return this; + } + + @Contract(value = "_ -> this", pure = true) + public @NotNull Builder precipitation(@NotNull Biome.Precipitation precipitation) { + this.precipitation = precipitation; + return this; + } + + @Contract(value = "_ -> this", pure = true) + public @NotNull Builder temperatureModifier(@NotNull TemperatureModifier temperatureModifier) { + this.temperatureModifier = temperatureModifier; + return this; + } + + @Contract(pure = true) + public @NotNull Biome build() { + return new BiomeImpl(name, depth, temperature, scale, downfall, effects, precipitation, temperatureModifier); + } + } } diff --git a/src/main/java/net/minestom/server/world/biomes/BiomeImpl.java b/src/main/java/net/minestom/server/world/biomes/BiomeImpl.java new file mode 100644 index 000000000..069b38e0d --- /dev/null +++ b/src/main/java/net/minestom/server/world/biomes/BiomeImpl.java @@ -0,0 +1,123 @@ +package net.minestom.server.world.biomes; + +import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.Registry; +import net.minestom.server.utils.NamespaceID; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +final class BiomeImpl implements ProtocolObject, Biome { + // https://minecraft.wiki/w/Rain + private final static Double SNOW_TEMPERATURE = 0.15; + + private static final Registry.DynamicContainer CONTAINER = Registry.createDynamicContainer(Registry.Resource.BIOMES, + (namespace, properties) -> new BiomeImpl(Registry.biome(namespace, properties))); + + static Collection values() { + return CONTAINER.values(); + } + + static BiomeImpl get(@NotNull String namespace) { + return CONTAINER.get(namespace); + } + + static BiomeImpl getSafe(@NotNull String namespace) { + return CONTAINER.getSafe(namespace); + } + + @NotNull + private final NamespaceID name; + private final float depth; + private final float temperature; + private final float scale; + private final float downfall; + @NotNull + private final BiomeEffects effects; + @NotNull + private final Precipitation precipitation; + @NotNull + private final TemperatureModifier temperatureModifier; + + BiomeImpl(NamespaceID name, float depth, float temperature, float scale, float downfall, BiomeEffects effects, Precipitation precipitation, TemperatureModifier temperatureModifier) { + this.name = name; + this.depth = depth; + this.temperature = temperature; + this.scale = scale; + this.downfall = downfall; + this.effects = effects; + this.precipitation = precipitation; + this.temperatureModifier = temperatureModifier; + } + + BiomeImpl(Registry.BiomeEntry entry) { + this.name = entry.namespace(); + this.depth = 0.2f; + this.scale = 0.2f; + this.temperature = entry.temperature(); + + BiomeEffects.Builder effectsBuilder = getBuilder(entry); + this.effects = effectsBuilder.build(); + + this.precipitation = entry.hasPrecipitation() + ? temperature < SNOW_TEMPERATURE + ? Biome.Precipitation.SNOW + : Biome.Precipitation.RAIN + : Biome.Precipitation.NONE; + + this.downfall = entry.downfall(); + this.temperatureModifier = entry.temperature() < SNOW_TEMPERATURE ? TemperatureModifier.FROZEN : TemperatureModifier.NONE; + } + + @NotNull + private static BiomeEffects.Builder getBuilder(Registry.BiomeEntry entry) { + BiomeEffects.Builder effectsBuilder = BiomeEffects.builder(); + if (entry.foliageColor() != null) effectsBuilder.foliageColor(entry.foliageColor()); + if (entry.grassColor() != null) effectsBuilder.grassColor(entry.grassColor()); + if (entry.skyColor() != null) effectsBuilder.skyColor(entry.skyColor()); + if (entry.waterColor() != null) effectsBuilder.waterColor(entry.waterColor()); + if (entry.waterFogColor() != null) effectsBuilder.waterFogColor(entry.waterFogColor()); + if (entry.fogColor() != null) effectsBuilder.fogColor(entry.fogColor()); + return effectsBuilder; + } + + @Nullable + @Override + public Registry.BiomeEntry registry() { + return null; + } + + @Override + public @NotNull NamespaceID namespace() { + return this.name; + } + + public float depth() { + return this.depth; + } + + public float temperature() { + return this.temperature; + } + + public float scale() { + return this.scale; + } + + public float downfall() { + return this.downfall; + } + + public @NotNull BiomeEffects effects() { + return this.effects; + } + + public @NotNull Precipitation precipitation() { + return this.precipitation; + } + + public @NotNull TemperatureModifier temperatureModifier() { + return this.temperatureModifier; + } +} diff --git a/src/main/java/net/minestom/server/world/biomes/BiomeManager.java b/src/main/java/net/minestom/server/world/biomes/BiomeManager.java index 3c7ffdbe6..18dfdbe28 100644 --- a/src/main/java/net/minestom/server/world/biomes/BiomeManager.java +++ b/src/main/java/net/minestom/server/world/biomes/BiomeManager.java @@ -1,6 +1,9 @@ package net.minestom.server.world.biomes; import net.minestom.server.utils.NamespaceID; +import net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jglrxavpok.hephaistos.nbt.NBT; import org.jglrxavpok.hephaistos.nbt.NBTCompound; import org.jglrxavpok.hephaistos.nbt.NBTType; @@ -9,18 +12,32 @@ import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; /** * Allows servers to register custom dimensions. Also used during player joining to send the list of all existing dimensions. *

- * Contains {@link Biome#PLAINS} by default but can be removed. */ public final class BiomeManager { private final Map biomes = new ConcurrentHashMap<>(); + private final Map biomesByName = new ConcurrentHashMap<>(); + private final Map idMappings = new ConcurrentHashMap<>(); + + private final AtomicInteger ID_COUNTER = new AtomicInteger(0); + private NBTCompound nbtCache = null; public BiomeManager() { - addBiome(Biome.PLAINS); + // Need to register plains for the client to work properly + // Plains is always ID 0 + addBiome(BiomeImpl.get("minecraft:plains")); + } + + public void loadVanillaBiomes() { + for (BiomeImpl biome : BiomeImpl.values()) { + if (getByName(biome.namespace()) == null) + addBiome(biome); + } } /** @@ -28,8 +45,14 @@ public final class BiomeManager { * * @param biome the biome to add */ - public void addBiome(Biome biome) { - this.biomes.put(biome.id(), biome); + public void addBiome(@NotNull Biome biome) { + Check.stateCondition(getByName(biome.namespace()) != null, "The biome " + biome.namespace() + " has already been registered"); + + var id = ID_COUNTER.getAndIncrement(); + this.biomes.put(id, biome); + this.biomesByName.put(biome.namespace(), biome); + this.idMappings.put(biome.namespace(), id); + nbtCache = null; } /** @@ -37,8 +60,14 @@ public final class BiomeManager { * * @param biome the biome to remove */ - public void removeBiome(Biome biome) { - this.biomes.remove(biome.id()); + public void removeBiome(@NotNull Biome biome) { + var id = idMappings.get(biome.namespace()); + if (id != null) { + biomes.remove(id); + biomesByName.remove(biome.namespace()); + idMappings.remove(biome.namespace()); + nbtCache = null; + } } /** @@ -56,24 +85,44 @@ public final class BiomeManager { * @param id the id of the biome * @return the {@link Biome} linked to this id */ + @Nullable public Biome getById(int id) { return biomes.get(id); } - public Biome getByName(NamespaceID namespaceID) { - Biome biome = null; - for (final Biome biomeT : biomes.values()) { - if (biomeT.name().equals(namespaceID)) { - biome = biomeT; - break; - } - } - return biome; + @Nullable + public Biome getByName(@NotNull NamespaceID namespaceID) { + return biomesByName.get(namespaceID); } - public NBTCompound toNBT() { - return NBT.Compound(Map.of( + @Nullable + public Biome getByName(@NotNull String namespaceID) { + NamespaceID namespace = NamespaceID.from(namespaceID); + return getByName(namespace); + } + + public @NotNull NBTCompound toNBT() { + if (nbtCache != null) return nbtCache; + nbtCache = NBT.Compound(Map.of( "type", NBT.String("minecraft:worldgen/biome"), - "value", NBT.List(NBTType.TAG_Compound, biomes.values().stream().map(Biome::toNbt).toList()))); + "value", NBT.List(NBTType.TAG_Compound, biomes.values().stream().map(biome -> { + return NBT.Compound(Map.of( + "id", NBT.Int(getId(biome)), + "name", NBT.String(biome.namespace().toString()), + "element", biome.toNbt() + )); + }).toList()))); + + return nbtCache; + } + + /** + * Gets the id of a biome. + *` + * @param biome + * @return the id of the biome, or -1 if the biome is not registered + */ + public int getId(Biome biome) { + return idMappings.getOrDefault(biome.namespace(), -1); } } diff --git a/src/main/java/net/minestom/server/world/biomes/VanillaBiome.java b/src/main/java/net/minestom/server/world/biomes/VanillaBiome.java new file mode 100644 index 000000000..7a5654f10 --- /dev/null +++ b/src/main/java/net/minestom/server/world/biomes/VanillaBiome.java @@ -0,0 +1,4 @@ +package net.minestom.server.world.biomes; + +final public class VanillaBiome implements Biomes { +} diff --git a/src/test/java/net/minestom/server/instance/AnvilLoaderIntegrationTest.java b/src/test/java/net/minestom/server/instance/AnvilLoaderIntegrationTest.java index 1e8f352c6..e66729c4e 100644 --- a/src/test/java/net/minestom/server/instance/AnvilLoaderIntegrationTest.java +++ b/src/test/java/net/minestom/server/instance/AnvilLoaderIntegrationTest.java @@ -70,13 +70,11 @@ public class AnvilLoaderIntegrationTest { assertEquals(-4, chunk.getMinSection()); assertEquals(20, chunk.getMaxSection()); - // TODO: skylight - // TODO: block light for (int y = 0; y < 16; y++) { for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { Biome b = chunk.getBiome(x, y, z); - assertEquals(NamespaceID.from("minecraft:plains"), b.name()); + assertEquals(NamespaceID.from("minecraft:plains"), b.namespace()); } } } diff --git a/src/test/java/net/minestom/server/instance/BiomeIntegrationTest.java b/src/test/java/net/minestom/server/instance/BiomeIntegrationTest.java new file mode 100644 index 000000000..37230e704 --- /dev/null +++ b/src/test/java/net/minestom/server/instance/BiomeIntegrationTest.java @@ -0,0 +1,72 @@ +package net.minestom.server.instance; + +import net.minestom.server.MinecraftServer; +import net.minestom.server.instance.generator.Generator; +import net.minestom.server.utils.NamespaceID; +import net.minestom.server.world.biomes.Biome; +import net.minestom.testing.Env; +import net.minestom.testing.EnvTest; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@EnvTest +public class BiomeIntegrationTest { + private static Biome PLAINS; + private static int PLAINS_ID; + + @BeforeAll + public static void prepareTest(Env env) { + PLAINS = MinecraftServer.getBiomeManager().getByName(NamespaceID.from("minecraft:plains")); + PLAINS_ID = MinecraftServer.getBiomeManager().getId(PLAINS); + } + + @Test + public void chunkBiomeSet(Env env) { + final int minSection = -1; + final int maxSection = 5; + final int chunkX = 3; + final int chunkZ = -2; + final int sectionCount = maxSection - minSection; + Section[] sections = new Section[sectionCount]; + Arrays.setAll(sections, i -> new Section()); + var chunkUnits = GeneratorImpl.chunk(minSection, maxSection, List.of(sections), chunkX, chunkZ); + Generator generator = chunk -> { + var modifier = chunk.modifier(); + modifier.setBiome(48, 0, -32, PLAINS); + modifier.setBiome(48 + 8, 0, -32, PLAINS); + }; + generator.generate(chunkUnits); + + assertEquals(PLAINS_ID, sections[0].biomePalette().get(0, 0, 0)); + assertEquals(0, sections[0].biomePalette().get(1, 0, 0)); + assertEquals(PLAINS_ID, sections[0].biomePalette().get(2, 0, 0)); + } + + @Test + public void chunkBiomeFill(Env env) { + final int minSection = -1; + final int maxSection = 5; + final int chunkX = 3; + final int chunkZ = -2; + final int sectionCount = maxSection - minSection; + Section[] sections = new Section[sectionCount]; + Arrays.setAll(sections, i -> new Section()); + var chunkUnits = GeneratorImpl.chunk(minSection, maxSection, List.of(sections), chunkX, chunkZ); + Generator generator = chunk -> { + var modifier = chunk.modifier(); + modifier.fillBiome(PLAINS); + }; + generator.generate(chunkUnits); + for (var section : sections) { + section.biomePalette().getAll((x, y, z, value) -> + assertEquals(PLAINS_ID, value)); + } + } + +} diff --git a/src/test/java/net/minestom/server/instance/GeneratorForkIntegrationTest.java b/src/test/java/net/minestom/server/instance/GeneratorForkIntegrationTest.java index ec9b91ceb..ed3097b46 100644 --- a/src/test/java/net/minestom/server/instance/GeneratorForkIntegrationTest.java +++ b/src/test/java/net/minestom/server/instance/GeneratorForkIntegrationTest.java @@ -1,11 +1,12 @@ package net.minestom.server.instance; -import net.minestom.testing.Env; -import net.minestom.testing.EnvTest; +import net.minestom.server.MinecraftServer; import net.minestom.server.coordinate.Point; import net.minestom.server.instance.block.Block; import net.minestom.server.instance.generator.GenerationUnit; -import net.minestom.server.world.biomes.Biome; +import net.minestom.server.utils.NamespaceID; +import net.minestom.testing.Env; +import net.minestom.testing.EnvTest; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @@ -97,11 +98,13 @@ public class GeneratorForkIntegrationTest { @Test public void biome(Env env) { var manager = env.process().instance(); + + var plains = MinecraftServer.getBiomeManager().getByName(NamespaceID.from("minecraft:plains")); var instance = manager.createInstanceContainer(); instance.setGenerator(unit -> { var u = unit.fork(unit.absoluteStart(), unit.absoluteEnd().add(16, 0, 16)); - assertThrows(IllegalStateException.class, () -> u.modifier().setBiome(16, 0, 0, Biome.PLAINS)); - assertThrows(IllegalStateException.class, () -> u.modifier().fillBiome(Biome.PLAINS)); + assertThrows(IllegalStateException.class, () -> u.modifier().setBiome(16, 0, 0, plains)); + assertThrows(IllegalStateException.class, () -> u.modifier().fillBiome(plains)); }); instance.loadChunk(0, 0).join(); } diff --git a/src/test/java/net/minestom/server/instance/GeneratorTest.java b/src/test/java/net/minestom/server/instance/GeneratorTest.java index 4a53ede72..bd6c60d79 100644 --- a/src/test/java/net/minestom/server/instance/GeneratorTest.java +++ b/src/test/java/net/minestom/server/instance/GeneratorTest.java @@ -6,8 +6,10 @@ import net.minestom.server.instance.block.Block; import net.minestom.server.instance.generator.GenerationUnit; import net.minestom.server.instance.generator.Generator; import net.minestom.server.utils.MathUtils; +import net.minestom.server.utils.NamespaceID; import net.minestom.server.utils.chunk.ChunkUtils; import net.minestom.server.world.biomes.Biome; +import net.minestom.server.world.biomes.BiomeEffects; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -26,7 +28,6 @@ import static net.minestom.server.utils.chunk.ChunkUtils.floorSection; import static org.junit.jupiter.api.Assertions.*; public class GeneratorTest { - @Test public void unitSize() { assertDoesNotThrow(() -> dummyUnit(Vec.ZERO, new Vec(16))); @@ -241,48 +242,6 @@ public class GeneratorTest { } } - @Test - public void chunkBiomeSet() { - final int minSection = -1; - final int maxSection = 5; - final int chunkX = 3; - final int chunkZ = -2; - final int sectionCount = maxSection - minSection; - Section[] sections = new Section[sectionCount]; - Arrays.setAll(sections, i -> new Section()); - var chunkUnits = GeneratorImpl.chunk(minSection, maxSection, List.of(sections), chunkX, chunkZ); - Generator generator = chunk -> { - var modifier = chunk.modifier(); - modifier.setBiome(48, 0, -32, Biome.PLAINS); - modifier.setBiome(48 + 8, 0, -32, Biome.PLAINS); - }; - generator.generate(chunkUnits); - assertEquals(Biome.PLAINS.id(), sections[0].biomePalette().get(0, 0, 0)); - assertEquals(0, sections[0].biomePalette().get(1, 0, 0)); - assertEquals(Biome.PLAINS.id(), sections[0].biomePalette().get(2, 0, 0)); - } - - @Test - public void chunkBiomeFill() { - final int minSection = -1; - final int maxSection = 5; - final int chunkX = 3; - final int chunkZ = -2; - final int sectionCount = maxSection - minSection; - Section[] sections = new Section[sectionCount]; - Arrays.setAll(sections, i -> new Section()); - var chunkUnits = GeneratorImpl.chunk(minSection, maxSection, List.of(sections), chunkX, chunkZ); - Generator generator = chunk -> { - var modifier = chunk.modifier(); - modifier.fillBiome(Biome.PLAINS); - }; - generator.generate(chunkUnits); - for (var section : sections) { - section.biomePalette().getAll((x, y, z, value) -> - assertEquals(Biome.PLAINS.id(), value)); - } - } - @Test public void chunkFillHeightExact() { final int minSection = -1;