feat: first pass of standardizing dynamic registries

This commit is contained in:
mworzala 2024-05-18 04:39:15 -04:00
parent 323c75f8a5
commit a0a3fd3a30
No known key found for this signature in database
GPG Key ID: B148F922E64797C7
84 changed files with 2298 additions and 1547 deletions

View File

@ -23,6 +23,7 @@ allprojects {
description = shortDescription
repositories {
mavenLocal()
mavenCentral()
maven(url = "https://jitpack.io")
}

View File

@ -69,6 +69,49 @@ public class CodeGenerator {
outputFolder);
}
public void generateKeys(InputStream resourceFile, String packageName, String typeName, String generatedName) {
if (resourceFile == null) {
LOGGER.error("Failed to find resource file for " + typeName);
return;
}
ClassName typeClass = ClassName.get(packageName, typeName);
ClassName namespaceIdClass = ClassName.get("net.minestom.server.utils", "NamespaceID");
JsonObject json;
json = GSON.fromJson(new InputStreamReader(resourceFile), JsonObject.class);
ClassName materialsCN = ClassName.get(packageName, generatedName);
// BlockConstants class
TypeSpec.Builder blockConstantsClass = TypeSpec.interfaceBuilder(materialsCN)
// Add @SuppressWarnings("unused")
.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class).addMember("value", "$S", "unused").build())
.addJavadoc("Code autogenerated, do not edit!");
// Use data
json.keySet().forEach(namespace -> {
final String constantName = namespace
.replace("minecraft:", "")
.replace(".", "_")
.toUpperCase(Locale.ROOT);
blockConstantsClass.addField(
FieldSpec.builder(namespaceIdClass, constantName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.initializer(
// TypeClass.STONE = NamespaceID.from("minecraft:stone")
"$T.from($S)",
namespaceIdClass,
namespace
)
.build()
);
});
writeFiles(
List.of(JavaFile.builder(packageName, blockConstantsClass.build())
.indent(" ")
.skipJavaLangImports(true)
.build()),
outputFolder);
}
private void writeFiles(@NotNull List<JavaFile> fileList, File outputFolder) {
for (JavaFile javaFile : fileList) {
try {

View File

@ -23,23 +23,30 @@ public class Generators {
new DyeColorGenerator(resource("dye_colors.json"), outputFolder).generate();
new RecipeTypeGenerator(resource("recipe_types.json"), outputFolder).generate();
// Generic protocol object
var generator = new CodeGenerator(outputFolder);
// Static registries
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.enchant", "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");
generator.generate(resource("particles.json"), "net.minestom.server.particle", "Particle", "ParticleImpl", "Particles");
generator.generate(resource("sounds.json"), "net.minestom.server.sound", "SoundEvent", "BuiltinSoundEvent", "SoundEvents");
generator.generate(resource("custom_statistics.json"), "net.minestom.server.statistic", "StatisticType", "StatisticTypeImpl", "StatisticTypes");
generator.generate(resource("damage_types.json"), "net.minestom.server.entity.damage", "DamageType", "DamageTypeImpl", "DamageTypes");
generator.generate(resource("trim_materials.json"), "net.minestom.server.item.armor", "TrimMaterial", "TrimMaterialImpl", "TrimMaterials");
generator.generate(resource("trim_patterns.json"), "net.minestom.server.item.armor", "TrimPattern", "TrimPatternImpl", "TrimPatterns");
generator.generate(resource("attributes.json"), "net.minestom.server.entity.attribute", "Attribute", "AttributeImpl", "Attributes");
// Dynamic registries
generator.generateKeys(resource("chat_types.json"), "net.minestom.server.message", "ChatType", "ChatTypes");
generator.generateKeys(resource("dimension_types.json"), "net.minestom.server.world", "DimensionType", "DimensionTypes");
generator.generateKeys(resource("biomes.json"), "net.minestom.server.world.biome", "Biome", "Biomes");
generator.generateKeys(resource("damage_types.json"), "net.minestom.server.entity.damage", "DamageType", "DamageTypes");
generator.generateKeys(resource("trim_materials.json"), "net.minestom.server.item.armor", "TrimMaterial", "TrimMaterials");
generator.generateKeys(resource("trim_patterns.json"), "net.minestom.server.item.armor", "TrimPattern", "TrimPatterns");
generator.generateKeys(resource("banner_patterns.json"), "net.minestom.server.instance.block.banner", "BannerPattern", "BannerPatterns");
generator.generateKeys(resource("wolf_variants.json"), "net.minestom.server.entity.metadata.animal.tameable", "Wolf$Variant", "WolfVariants");
// Generate fluids
new FluidGenerator(resource("fluids.json"), outputFolder).generate();

View File

@ -37,7 +37,7 @@ public final class FluidGenerator extends MinestomCodeGenerator {
}
// Important classes we use alot
ClassName namespaceIDClassName = ClassName.get("net.minestom.server.utils", "NamespaceID");
ClassName registriesClassName = ClassName.get("net.minestom.server.registry", "Registries");
ClassName registriesClassName = ClassName.get("net.minestom.server.registry", "FluidRegistries");
JsonObject fluids = GSON.fromJson(new InputStreamReader(fluidsFile), JsonObject.class);
ClassName fluidClassName = ClassName.get("net.minestom.server.fluid", "Fluid");

View File

@ -39,7 +39,6 @@ public class Main {
MinecraftServer.setCompressionThreshold(0);
MinecraftServer minecraftServer = MinecraftServer.init();
MinecraftServer.getBiomeManager().loadVanillaBiomes();
BlockManager blockManager = MinecraftServer.getBlockManager();
blockManager.registerBlockPlacementRule(new DripstonePlacementRule());

View File

@ -42,7 +42,6 @@ import net.minestom.server.potion.CustomPotionEffect;
import net.minestom.server.potion.PotionEffect;
import net.minestom.server.utils.MathUtils;
import net.minestom.server.utils.time.TimeUnit;
import net.minestom.server.world.DimensionType;
import java.time.Duration;
import java.util.List;
@ -181,7 +180,7 @@ public class PlayerInit {
static {
InstanceManager instanceManager = MinecraftServer.getInstanceManager();
InstanceContainer instanceContainer = instanceManager.createInstanceContainer(DimensionType.OVERWORLD);
InstanceContainer instanceContainer = instanceManager.createInstanceContainer();
instanceContainer.setGenerator(unit -> {
unit.modifier().fillHeight(0, 40, Block.STONE);

View File

@ -3,12 +3,9 @@ metadata.format.version = "1.1"
[versions]
# Important dependencies
data = "1.20.5-rv1"
data = "1.20.5-dev"
adventure = "4.16.0"
kotlin = "1.7.22"
dependencyGetter = "v1.0.1"
hydrazine = "1.7.2"
hephaistos = "2.6.1"
jetbrainsAnnotations = "23.0.0"
slf4j = "2.0.7"

View File

@ -1,97 +1,99 @@
package net.minestom.server.entity.damage;
import net.minestom.server.utils.NamespaceID;
/**
* Code autogenerated, do not edit!
*/
@SuppressWarnings("unused")
interface DamageTypes {
DamageType WITHER = DamageTypeImpl.get("minecraft:wither");
NamespaceID WITHER = NamespaceID.from("minecraft:wither");
DamageType SONIC_BOOM = DamageTypeImpl.get("minecraft:sonic_boom");
NamespaceID SONIC_BOOM = NamespaceID.from("minecraft:sonic_boom");
DamageType WITHER_SKULL = DamageTypeImpl.get("minecraft:wither_skull");
NamespaceID WITHER_SKULL = NamespaceID.from("minecraft:wither_skull");
DamageType DRY_OUT = DamageTypeImpl.get("minecraft:dry_out");
NamespaceID DRY_OUT = NamespaceID.from("minecraft:dry_out");
DamageType TRIDENT = DamageTypeImpl.get("minecraft:trident");
NamespaceID TRIDENT = NamespaceID.from("minecraft:trident");
DamageType ON_FIRE = DamageTypeImpl.get("minecraft:on_fire");
NamespaceID ON_FIRE = NamespaceID.from("minecraft:on_fire");
DamageType FALL = DamageTypeImpl.get("minecraft:fall");
NamespaceID FALL = NamespaceID.from("minecraft:fall");
DamageType MOB_ATTACK = DamageTypeImpl.get("minecraft:mob_attack");
NamespaceID MOB_ATTACK = NamespaceID.from("minecraft:mob_attack");
DamageType MOB_PROJECTILE = DamageTypeImpl.get("minecraft:mob_projectile");
NamespaceID MOB_PROJECTILE = NamespaceID.from("minecraft:mob_projectile");
DamageType THROWN = DamageTypeImpl.get("minecraft:thrown");
NamespaceID THROWN = NamespaceID.from("minecraft:thrown");
DamageType FALLING_STALACTITE = DamageTypeImpl.get("minecraft:falling_stalactite");
NamespaceID FALLING_STALACTITE = NamespaceID.from("minecraft:falling_stalactite");
DamageType FIREBALL = DamageTypeImpl.get("minecraft:fireball");
NamespaceID FIREBALL = NamespaceID.from("minecraft:fireball");
DamageType FALLING_BLOCK = DamageTypeImpl.get("minecraft:falling_block");
NamespaceID FALLING_BLOCK = NamespaceID.from("minecraft:falling_block");
DamageType PLAYER_EXPLOSION = DamageTypeImpl.get("minecraft:player_explosion");
NamespaceID PLAYER_EXPLOSION = NamespaceID.from("minecraft:player_explosion");
DamageType SPIT = DamageTypeImpl.get("minecraft:spit");
NamespaceID SPIT = NamespaceID.from("minecraft:spit");
DamageType STING = DamageTypeImpl.get("minecraft:sting");
NamespaceID STING = NamespaceID.from("minecraft:sting");
DamageType UNATTRIBUTED_FIREBALL = DamageTypeImpl.get("minecraft:unattributed_fireball");
NamespaceID UNATTRIBUTED_FIREBALL = NamespaceID.from("minecraft:unattributed_fireball");
DamageType IN_WALL = DamageTypeImpl.get("minecraft:in_wall");
NamespaceID IN_WALL = NamespaceID.from("minecraft:in_wall");
DamageType IN_FIRE = DamageTypeImpl.get("minecraft:in_fire");
NamespaceID IN_FIRE = NamespaceID.from("minecraft:in_fire");
DamageType ARROW = DamageTypeImpl.get("minecraft:arrow");
NamespaceID ARROW = NamespaceID.from("minecraft:arrow");
DamageType HOT_FLOOR = DamageTypeImpl.get("minecraft:hot_floor");
NamespaceID HOT_FLOOR = NamespaceID.from("minecraft:hot_floor");
DamageType DROWN = DamageTypeImpl.get("minecraft:drown");
NamespaceID DROWN = NamespaceID.from("minecraft:drown");
DamageType STARVE = DamageTypeImpl.get("minecraft:starve");
NamespaceID STARVE = NamespaceID.from("minecraft:starve");
DamageType GENERIC_KILL = DamageTypeImpl.get("minecraft:generic_kill");
NamespaceID GENERIC_KILL = NamespaceID.from("minecraft:generic_kill");
DamageType DRAGON_BREATH = DamageTypeImpl.get("minecraft:dragon_breath");
NamespaceID DRAGON_BREATH = NamespaceID.from("minecraft:dragon_breath");
DamageType MOB_ATTACK_NO_AGGRO = DamageTypeImpl.get("minecraft:mob_attack_no_aggro");
NamespaceID MOB_ATTACK_NO_AGGRO = NamespaceID.from("minecraft:mob_attack_no_aggro");
DamageType LAVA = DamageTypeImpl.get("minecraft:lava");
NamespaceID LAVA = NamespaceID.from("minecraft:lava");
DamageType OUTSIDE_BORDER = DamageTypeImpl.get("minecraft:outside_border");
NamespaceID OUTSIDE_BORDER = NamespaceID.from("minecraft:outside_border");
DamageType FLY_INTO_WALL = DamageTypeImpl.get("minecraft:fly_into_wall");
NamespaceID FLY_INTO_WALL = NamespaceID.from("minecraft:fly_into_wall");
DamageType LIGHTNING_BOLT = DamageTypeImpl.get("minecraft:lightning_bolt");
NamespaceID LIGHTNING_BOLT = NamespaceID.from("minecraft:lightning_bolt");
DamageType PLAYER_ATTACK = DamageTypeImpl.get("minecraft:player_attack");
NamespaceID PLAYER_ATTACK = NamespaceID.from("minecraft:player_attack");
DamageType FREEZE = DamageTypeImpl.get("minecraft:freeze");
NamespaceID FREEZE = NamespaceID.from("minecraft:freeze");
DamageType FALLING_ANVIL = DamageTypeImpl.get("minecraft:falling_anvil");
NamespaceID FALLING_ANVIL = NamespaceID.from("minecraft:falling_anvil");
DamageType OUT_OF_WORLD = DamageTypeImpl.get("minecraft:out_of_world");
NamespaceID OUT_OF_WORLD = NamespaceID.from("minecraft:out_of_world");
DamageType MAGIC = DamageTypeImpl.get("minecraft:magic");
NamespaceID MAGIC = NamespaceID.from("minecraft:magic");
DamageType SWEET_BERRY_BUSH = DamageTypeImpl.get("minecraft:sweet_berry_bush");
NamespaceID SWEET_BERRY_BUSH = NamespaceID.from("minecraft:sweet_berry_bush");
DamageType FIREWORKS = DamageTypeImpl.get("minecraft:fireworks");
NamespaceID FIREWORKS = NamespaceID.from("minecraft:fireworks");
DamageType EXPLOSION = DamageTypeImpl.get("minecraft:explosion");
NamespaceID EXPLOSION = NamespaceID.from("minecraft:explosion");
DamageType BAD_RESPAWN_POINT = DamageTypeImpl.get("minecraft:bad_respawn_point");
NamespaceID BAD_RESPAWN_POINT = NamespaceID.from("minecraft:bad_respawn_point");
DamageType STALAGMITE = DamageTypeImpl.get("minecraft:stalagmite");
NamespaceID STALAGMITE = NamespaceID.from("minecraft:stalagmite");
DamageType THORNS = DamageTypeImpl.get("minecraft:thorns");
NamespaceID THORNS = NamespaceID.from("minecraft:thorns");
DamageType INDIRECT_MAGIC = DamageTypeImpl.get("minecraft:indirect_magic");
NamespaceID INDIRECT_MAGIC = NamespaceID.from("minecraft:indirect_magic");
DamageType CRAMMING = DamageTypeImpl.get("minecraft:cramming");
NamespaceID CRAMMING = NamespaceID.from("minecraft:cramming");
DamageType CACTUS = DamageTypeImpl.get("minecraft:cactus");
NamespaceID CACTUS = NamespaceID.from("minecraft:cactus");
DamageType GENERIC = DamageTypeImpl.get("minecraft:generic");
NamespaceID GENERIC = NamespaceID.from("minecraft:generic");
}

View File

@ -0,0 +1,27 @@
package net.minestom.server.entity.metadata.animal.tameable;
import net.minestom.server.utils.NamespaceID;
/**
* Code autogenerated, do not edit!
*/
@SuppressWarnings("unused")
interface WolfVariants {
NamespaceID BLACK = NamespaceID.from("minecraft:black");
NamespaceID CHESTNUT = NamespaceID.from("minecraft:chestnut");
NamespaceID SNOWY = NamespaceID.from("minecraft:snowy");
NamespaceID STRIPED = NamespaceID.from("minecraft:striped");
NamespaceID ASHEN = NamespaceID.from("minecraft:ashen");
NamespaceID SPOTTED = NamespaceID.from("minecraft:spotted");
NamespaceID RUSTY = NamespaceID.from("minecraft:rusty");
NamespaceID WOODS = NamespaceID.from("minecraft:woods");
NamespaceID PALE = NamespaceID.from("minecraft:pale");
}

View File

@ -2,7 +2,7 @@ package net.minestom.server.fluid;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.Keyed;
import net.minestom.server.registry.Registries;
import net.minestom.server.registry.FluidRegistries;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -28,7 +28,7 @@ public enum Fluid implements Keyed {
Fluid(@NotNull NamespaceID id) {
this.id = id;
Registries.fluids.put(id, this);
FluidRegistries.fluids.put(id, this);
}
@Override

View File

@ -0,0 +1,91 @@
package net.minestom.server.instance.block.banner;
import net.minestom.server.utils.NamespaceID;
/**
* Code autogenerated, do not edit!
*/
@SuppressWarnings("unused")
interface BannerPatterns {
NamespaceID BORDER = NamespaceID.from("minecraft:border");
NamespaceID SQUARE_BOTTOM_RIGHT = NamespaceID.from("minecraft:square_bottom_right");
NamespaceID STRIPE_RIGHT = NamespaceID.from("minecraft:stripe_right");
NamespaceID RHOMBUS = NamespaceID.from("minecraft:rhombus");
NamespaceID TRIANGLES_TOP = NamespaceID.from("minecraft:triangles_top");
NamespaceID GRADIENT = NamespaceID.from("minecraft:gradient");
NamespaceID GLOBE = NamespaceID.from("minecraft:globe");
NamespaceID MOJANG = NamespaceID.from("minecraft:mojang");
NamespaceID STRIPE_BOTTOM = NamespaceID.from("minecraft:stripe_bottom");
NamespaceID STRIPE_MIDDLE = NamespaceID.from("minecraft:stripe_middle");
NamespaceID FLOWER = NamespaceID.from("minecraft:flower");
NamespaceID DIAGONAL_UP_RIGHT = NamespaceID.from("minecraft:diagonal_up_right");
NamespaceID CIRCLE = NamespaceID.from("minecraft:circle");
NamespaceID HALF_HORIZONTAL = NamespaceID.from("minecraft:half_horizontal");
NamespaceID BRICKS = NamespaceID.from("minecraft:bricks");
NamespaceID TRIANGLE_BOTTOM = NamespaceID.from("minecraft:triangle_bottom");
NamespaceID CURLY_BORDER = NamespaceID.from("minecraft:curly_border");
NamespaceID BASE = NamespaceID.from("minecraft:base");
NamespaceID PIGLIN = NamespaceID.from("minecraft:piglin");
NamespaceID STRIPE_CENTER = NamespaceID.from("minecraft:stripe_center");
NamespaceID SQUARE_BOTTOM_LEFT = NamespaceID.from("minecraft:square_bottom_left");
NamespaceID SQUARE_TOP_RIGHT = NamespaceID.from("minecraft:square_top_right");
NamespaceID STRIPE_DOWNRIGHT = NamespaceID.from("minecraft:stripe_downright");
NamespaceID GRADIENT_UP = NamespaceID.from("minecraft:gradient_up");
NamespaceID DIAGONAL_RIGHT = NamespaceID.from("minecraft:diagonal_right");
NamespaceID HALF_VERTICAL_RIGHT = NamespaceID.from("minecraft:half_vertical_right");
NamespaceID SMALL_STRIPES = NamespaceID.from("minecraft:small_stripes");
NamespaceID CROSS = NamespaceID.from("minecraft:cross");
NamespaceID DIAGONAL_LEFT = NamespaceID.from("minecraft:diagonal_left");
NamespaceID SKULL = NamespaceID.from("minecraft:skull");
NamespaceID STRIPE_DOWNLEFT = NamespaceID.from("minecraft:stripe_downleft");
NamespaceID DIAGONAL_UP_LEFT = NamespaceID.from("minecraft:diagonal_up_left");
NamespaceID HALF_VERTICAL = NamespaceID.from("minecraft:half_vertical");
NamespaceID TRIANGLE_TOP = NamespaceID.from("minecraft:triangle_top");
NamespaceID HALF_HORIZONTAL_BOTTOM = NamespaceID.from("minecraft:half_horizontal_bottom");
NamespaceID SQUARE_TOP_LEFT = NamespaceID.from("minecraft:square_top_left");
NamespaceID STRIPE_TOP = NamespaceID.from("minecraft:stripe_top");
NamespaceID CREEPER = NamespaceID.from("minecraft:creeper");
NamespaceID STRIPE_LEFT = NamespaceID.from("minecraft:stripe_left");
NamespaceID TRIANGLES_BOTTOM = NamespaceID.from("minecraft:triangles_bottom");
NamespaceID STRAIGHT_CROSS = NamespaceID.from("minecraft:straight_cross");
}

View File

@ -1,27 +1,29 @@
package net.minestom.server.item.armor;
import net.minestom.server.utils.NamespaceID;
/**
* Code autogenerated, do not edit!
*/
@SuppressWarnings("unused")
interface TrimMaterials {
TrimMaterial LAPIS = TrimMaterialImpl.get("minecraft:lapis");
NamespaceID LAPIS = NamespaceID.from("minecraft:lapis");
TrimMaterial IRON = TrimMaterialImpl.get("minecraft:iron");
NamespaceID IRON = NamespaceID.from("minecraft:iron");
TrimMaterial DIAMOND = TrimMaterialImpl.get("minecraft:diamond");
NamespaceID DIAMOND = NamespaceID.from("minecraft:diamond");
TrimMaterial AMETHYST = TrimMaterialImpl.get("minecraft:amethyst");
NamespaceID AMETHYST = NamespaceID.from("minecraft:amethyst");
TrimMaterial COPPER = TrimMaterialImpl.get("minecraft:copper");
NamespaceID COPPER = NamespaceID.from("minecraft:copper");
TrimMaterial QUARTZ = TrimMaterialImpl.get("minecraft:quartz");
NamespaceID QUARTZ = NamespaceID.from("minecraft:quartz");
TrimMaterial EMERALD = TrimMaterialImpl.get("minecraft:emerald");
NamespaceID EMERALD = NamespaceID.from("minecraft:emerald");
TrimMaterial REDSTONE = TrimMaterialImpl.get("minecraft:redstone");
NamespaceID REDSTONE = NamespaceID.from("minecraft:redstone");
TrimMaterial GOLD = TrimMaterialImpl.get("minecraft:gold");
NamespaceID GOLD = NamespaceID.from("minecraft:gold");
TrimMaterial NETHERITE = TrimMaterialImpl.get("minecraft:netherite");
NamespaceID NETHERITE = NamespaceID.from("minecraft:netherite");
}

View File

@ -1,39 +1,41 @@
package net.minestom.server.item.armor;
import net.minestom.server.utils.NamespaceID;
/**
* Code autogenerated, do not edit!
*/
@SuppressWarnings("unused")
interface TrimPatterns {
TrimPattern TIDE = TrimPatternImpl.get("minecraft:tide");
NamespaceID TIDE = NamespaceID.from("minecraft:tide");
TrimPattern RIB = TrimPatternImpl.get("minecraft:rib");
NamespaceID RIB = NamespaceID.from("minecraft:rib");
TrimPattern HOST = TrimPatternImpl.get("minecraft:host");
NamespaceID HOST = NamespaceID.from("minecraft:host");
TrimPattern SILENCE = TrimPatternImpl.get("minecraft:silence");
NamespaceID SILENCE = NamespaceID.from("minecraft:silence");
TrimPattern WILD = TrimPatternImpl.get("minecraft:wild");
NamespaceID WILD = NamespaceID.from("minecraft:wild");
TrimPattern WAYFINDER = TrimPatternImpl.get("minecraft:wayfinder");
NamespaceID WAYFINDER = NamespaceID.from("minecraft:wayfinder");
TrimPattern DUNE = TrimPatternImpl.get("minecraft:dune");
NamespaceID DUNE = NamespaceID.from("minecraft:dune");
TrimPattern RAISER = TrimPatternImpl.get("minecraft:raiser");
NamespaceID RAISER = NamespaceID.from("minecraft:raiser");
TrimPattern SNOUT = TrimPatternImpl.get("minecraft:snout");
NamespaceID SNOUT = NamespaceID.from("minecraft:snout");
TrimPattern VEX = TrimPatternImpl.get("minecraft:vex");
NamespaceID VEX = NamespaceID.from("minecraft:vex");
TrimPattern SPIRE = TrimPatternImpl.get("minecraft:spire");
NamespaceID SPIRE = NamespaceID.from("minecraft:spire");
TrimPattern SENTRY = TrimPatternImpl.get("minecraft:sentry");
NamespaceID SENTRY = NamespaceID.from("minecraft:sentry");
TrimPattern EYE = TrimPatternImpl.get("minecraft:eye");
NamespaceID EYE = NamespaceID.from("minecraft:eye");
TrimPattern WARD = TrimPatternImpl.get("minecraft:ward");
NamespaceID WARD = NamespaceID.from("minecraft:ward");
TrimPattern COAST = TrimPatternImpl.get("minecraft:coast");
NamespaceID COAST = NamespaceID.from("minecraft:coast");
TrimPattern SHAPER = TrimPatternImpl.get("minecraft:shaper");
NamespaceID SHAPER = NamespaceID.from("minecraft:shaper");
}

View File

@ -0,0 +1,23 @@
package net.minestom.server.message;
import net.minestom.server.utils.NamespaceID;
/**
* Code autogenerated, do not edit!
*/
@SuppressWarnings("unused")
interface ChatTypes {
NamespaceID EMOTE_COMMAND = NamespaceID.from("minecraft:emote_command");
NamespaceID TEAM_MSG_COMMAND_INCOMING = NamespaceID.from("minecraft:team_msg_command_incoming");
NamespaceID TEAM_MSG_COMMAND_OUTGOING = NamespaceID.from("minecraft:team_msg_command_outgoing");
NamespaceID CHAT = NamespaceID.from("minecraft:chat");
NamespaceID MSG_COMMAND_INCOMING = NamespaceID.from("minecraft:msg_command_incoming");
NamespaceID MSG_COMMAND_OUTGOING = NamespaceID.from("minecraft:msg_command_outgoing");
NamespaceID SAY_COMMAND = NamespaceID.from("minecraft:say_command");
}

View File

@ -11,7 +11,7 @@ import java.util.HashMap;
/**
* AUTOGENERATED
*/
public final class Registries {
public final class FluidRegistries {
/**
* Should only be used for internal code, please use the get* methods.

View File

@ -0,0 +1,17 @@
package net.minestom.server.world;
import net.minestom.server.utils.NamespaceID;
/**
* Code autogenerated, do not edit!
*/
@SuppressWarnings("unused")
interface DimensionTypes {
NamespaceID THE_END = NamespaceID.from("minecraft:the_end");
NamespaceID OVERWORLD_CAVES = NamespaceID.from("minecraft:overworld_caves");
NamespaceID THE_NETHER = NamespaceID.from("minecraft:the_nether");
NamespaceID OVERWORLD = NamespaceID.from("minecraft:overworld");
}

View File

@ -0,0 +1,137 @@
package net.minestom.server.world.biome;
import net.minestom.server.utils.NamespaceID;
/**
* Code autogenerated, do not edit!
*/
@SuppressWarnings("unused")
interface Biomes {
NamespaceID SNOWY_SLOPES = NamespaceID.from("minecraft:snowy_slopes");
NamespaceID OLD_GROWTH_PINE_TAIGA = NamespaceID.from("minecraft:old_growth_pine_taiga");
NamespaceID MUSHROOM_FIELDS = NamespaceID.from("minecraft:mushroom_fields");
NamespaceID TAIGA = NamespaceID.from("minecraft:taiga");
NamespaceID DEEP_OCEAN = NamespaceID.from("minecraft:deep_ocean");
NamespaceID ERODED_BADLANDS = NamespaceID.from("minecraft:eroded_badlands");
NamespaceID FROZEN_RIVER = NamespaceID.from("minecraft:frozen_river");
NamespaceID END_HIGHLANDS = NamespaceID.from("minecraft:end_highlands");
NamespaceID CHERRY_GROVE = NamespaceID.from("minecraft:cherry_grove");
NamespaceID SUNFLOWER_PLAINS = NamespaceID.from("minecraft:sunflower_plains");
NamespaceID BIRCH_FOREST = NamespaceID.from("minecraft:birch_forest");
NamespaceID WINDSWEPT_HILLS = NamespaceID.from("minecraft:windswept_hills");
NamespaceID BAMBOO_JUNGLE = NamespaceID.from("minecraft:bamboo_jungle");
NamespaceID WOODED_BADLANDS = NamespaceID.from("minecraft:wooded_badlands");
NamespaceID BADLANDS = NamespaceID.from("minecraft:badlands");
NamespaceID SAVANNA_PLATEAU = NamespaceID.from("minecraft:savanna_plateau");
NamespaceID BEACH = NamespaceID.from("minecraft:beach");
NamespaceID DARK_FOREST = NamespaceID.from("minecraft:dark_forest");
NamespaceID STONY_PEAKS = NamespaceID.from("minecraft:stony_peaks");
NamespaceID MANGROVE_SWAMP = NamespaceID.from("minecraft:mangrove_swamp");
NamespaceID SPARSE_JUNGLE = NamespaceID.from("minecraft:sparse_jungle");
NamespaceID LUKEWARM_OCEAN = NamespaceID.from("minecraft:lukewarm_ocean");
NamespaceID RIVER = NamespaceID.from("minecraft:river");
NamespaceID STONY_SHORE = NamespaceID.from("minecraft:stony_shore");
NamespaceID WARPED_FOREST = NamespaceID.from("minecraft:warped_forest");
NamespaceID SNOWY_PLAINS = NamespaceID.from("minecraft:snowy_plains");
NamespaceID DRIPSTONE_CAVES = NamespaceID.from("minecraft:dripstone_caves");
NamespaceID SNOWY_TAIGA = NamespaceID.from("minecraft:snowy_taiga");
NamespaceID GROVE = NamespaceID.from("minecraft:grove");
NamespaceID SWAMP = NamespaceID.from("minecraft:swamp");
NamespaceID JAGGED_PEAKS = NamespaceID.from("minecraft:jagged_peaks");
NamespaceID COLD_OCEAN = NamespaceID.from("minecraft:cold_ocean");
NamespaceID FOREST = NamespaceID.from("minecraft:forest");
NamespaceID LUSH_CAVES = NamespaceID.from("minecraft:lush_caves");
NamespaceID BASALT_DELTAS = NamespaceID.from("minecraft:basalt_deltas");
NamespaceID DEEP_COLD_OCEAN = NamespaceID.from("minecraft:deep_cold_ocean");
NamespaceID ICE_SPIKES = NamespaceID.from("minecraft:ice_spikes");
NamespaceID END_MIDLANDS = NamespaceID.from("minecraft:end_midlands");
NamespaceID FROZEN_OCEAN = NamespaceID.from("minecraft:frozen_ocean");
NamespaceID DESERT = NamespaceID.from("minecraft:desert");
NamespaceID DEEP_FROZEN_OCEAN = NamespaceID.from("minecraft:deep_frozen_ocean");
NamespaceID WINDSWEPT_FOREST = NamespaceID.from("minecraft:windswept_forest");
NamespaceID JUNGLE = NamespaceID.from("minecraft:jungle");
NamespaceID OCEAN = NamespaceID.from("minecraft:ocean");
NamespaceID OLD_GROWTH_SPRUCE_TAIGA = NamespaceID.from("minecraft:old_growth_spruce_taiga");
NamespaceID SNOWY_BEACH = NamespaceID.from("minecraft:snowy_beach");
NamespaceID WINDSWEPT_SAVANNA = NamespaceID.from("minecraft:windswept_savanna");
NamespaceID END_BARRENS = NamespaceID.from("minecraft:end_barrens");
NamespaceID WARM_OCEAN = NamespaceID.from("minecraft:warm_ocean");
NamespaceID DEEP_LUKEWARM_OCEAN = NamespaceID.from("minecraft:deep_lukewarm_ocean");
NamespaceID FLOWER_FOREST = NamespaceID.from("minecraft:flower_forest");
NamespaceID SOUL_SAND_VALLEY = NamespaceID.from("minecraft:soul_sand_valley");
NamespaceID NETHER_WASTES = NamespaceID.from("minecraft:nether_wastes");
NamespaceID FROZEN_PEAKS = NamespaceID.from("minecraft:frozen_peaks");
NamespaceID THE_END = NamespaceID.from("minecraft:the_end");
NamespaceID SMALL_END_ISLANDS = NamespaceID.from("minecraft:small_end_islands");
NamespaceID OLD_GROWTH_BIRCH_FOREST = NamespaceID.from("minecraft:old_growth_birch_forest");
NamespaceID CRIMSON_FOREST = NamespaceID.from("minecraft:crimson_forest");
NamespaceID THE_VOID = NamespaceID.from("minecraft:the_void");
NamespaceID DEEP_DARK = NamespaceID.from("minecraft:deep_dark");
NamespaceID MEADOW = NamespaceID.from("minecraft:meadow");
NamespaceID WINDSWEPT_GRAVELLY_HILLS = NamespaceID.from("minecraft:windswept_gravelly_hills");
NamespaceID SAVANNA = NamespaceID.from("minecraft:savanna");
NamespaceID PLAINS = NamespaceID.from("minecraft:plains");
}

View File

@ -1,135 +0,0 @@
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");
}

View File

@ -4,13 +4,18 @@ import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
import net.minestom.server.advancements.AdvancementManager;
import net.minestom.server.adventure.bossbar.BossBarManager;
import net.minestom.server.command.CommandManager;
import net.minestom.server.entity.damage.DamageType;
import net.minestom.server.entity.metadata.animal.tameable.WolfMeta;
import net.minestom.server.event.GlobalEventHandler;
import net.minestom.server.exception.ExceptionManager;
import net.minestom.server.gamedata.tags.TagManager;
import net.minestom.server.instance.InstanceManager;
import net.minestom.server.instance.block.BlockManager;
import net.minestom.server.item.armor.TrimManager;
import net.minestom.server.instance.block.banner.BannerPattern;
import net.minestom.server.item.armor.TrimMaterial;
import net.minestom.server.item.armor.TrimPattern;
import net.minestom.server.listener.manager.PacketListenerManager;
import net.minestom.server.message.ChatType;
import net.minestom.server.monitoring.BenchmarkManager;
import net.minestom.server.network.ConnectionManager;
import net.minestom.server.network.PacketProcessor;
@ -18,14 +23,15 @@ import net.minestom.server.network.packet.server.common.PluginMessagePacket;
import net.minestom.server.network.packet.server.play.ServerDifficultyPacket;
import net.minestom.server.network.socket.Server;
import net.minestom.server.recipe.RecipeManager;
import net.minestom.server.registry.DynamicRegistry;
import net.minestom.server.scoreboard.TeamManager;
import net.minestom.server.thread.TickSchedulerThread;
import net.minestom.server.timer.SchedulerManager;
import net.minestom.server.utils.PacketUtils;
import net.minestom.server.utils.validate.Check;
import net.minestom.server.world.Difficulty;
import net.minestom.server.world.DimensionTypeManager;
import net.minestom.server.world.biomes.BiomeManager;
import net.minestom.server.world.DimensionType;
import net.minestom.server.world.biome.Biome;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.UnknownNullability;
@ -241,14 +247,6 @@ public final class MinecraftServer {
MinecraftServer.compressionThreshold = compressionThreshold;
}
public static DimensionTypeManager getDimensionTypeManager() {
return serverProcess.dimension();
}
public static BiomeManager getBiomeManager() {
return serverProcess.biome();
}
public static AdvancementManager getAdvancementManager() {
return serverProcess.advancement();
}
@ -257,8 +255,36 @@ public final class MinecraftServer {
return serverProcess.tag();
}
public static TrimManager getTrimManager() {
return serverProcess.trim();
public static @NotNull DynamicRegistry<ChatType> getChatTypeRegistry() {
return serverProcess.chatType();
}
public static @NotNull DynamicRegistry<DimensionType> getDimensionTypeRegistry() {
return serverProcess.dimensionType();
}
public static @NotNull DynamicRegistry<Biome> getBiomeRegistry() {
return serverProcess.biome();
}
public static @NotNull DynamicRegistry<DamageType> getDamageTypeRegistry() {
return serverProcess.damageType();
}
public static @NotNull DynamicRegistry<TrimMaterial> getTrimMaterialRegistry() {
return serverProcess.trimMaterial();
}
public static @NotNull DynamicRegistry<TrimPattern> getTrimPatternRegistry() {
return serverProcess.trimPattern();
}
public static @NotNull DynamicRegistry<BannerPattern> getBannerPatternRegistry() {
return serverProcess.bannerPattern();
}
public static @NotNull DynamicRegistry<WolfMeta.Variant> getWolfVariantRegistry() {
return serverProcess.wolfVariant();
}
public static Server getServer() {

View File

@ -49,6 +49,7 @@ public final class ServerFlag {
public static final @Nullable String MAP_RGB_REDUCTION = System.getProperty("minestom.map.rgbreduction"); // Only used if rgb mapping is "approximate"
// Experimental/Unstable
public static final boolean REGISTRY_UNSAFE_REMOVE = Boolean.getBoolean("minestom.registry.unsafe-remove");
public static final boolean EVENT_NODE_ALLOW_MULTIPLE_PARENTS = Boolean.getBoolean("minestom.event.multiple-parents");
private ServerFlag() {}

View File

@ -10,19 +10,17 @@ import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.InstanceManager;
import net.minestom.server.instance.block.BlockManager;
import net.minestom.server.instance.block.rule.BlockPlacementRule;
import net.minestom.server.item.armor.TrimManager;
import net.minestom.server.listener.manager.PacketListenerManager;
import net.minestom.server.monitoring.BenchmarkManager;
import net.minestom.server.network.ConnectionManager;
import net.minestom.server.network.PacketProcessor;
import net.minestom.server.network.socket.Server;
import net.minestom.server.recipe.RecipeManager;
import net.minestom.server.registry.Registries;
import net.minestom.server.scoreboard.TeamManager;
import net.minestom.server.snapshot.Snapshotable;
import net.minestom.server.thread.ThreadDispatcher;
import net.minestom.server.timer.SchedulerManager;
import net.minestom.server.world.DimensionTypeManager;
import net.minestom.server.world.biomes.BiomeManager;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
@ -30,7 +28,7 @@ import java.net.SocketAddress;
@ApiStatus.Experimental
@ApiStatus.NonExtendable
public interface ServerProcess extends Snapshotable {
public interface ServerProcess extends Registries, Snapshotable {
/**
* Handles incoming connections/players.
*/
@ -76,16 +74,6 @@ public interface ServerProcess extends Snapshotable {
@NotNull BenchmarkManager benchmark();
/**
* Handles registered dimensions.
*/
@NotNull DimensionTypeManager dimension();
/**
* Handles registered biomes.
*/
@NotNull BiomeManager biome();
/**
* Handles registered advancements.
*/
@ -101,8 +89,6 @@ public interface ServerProcess extends Snapshotable {
*/
@NotNull TagManager tag();
@NotNull TrimManager trim();
/**
* Handles all thrown exceptions from the server.
*/

View File

@ -5,6 +5,8 @@ import net.minestom.server.advancements.AdvancementManager;
import net.minestom.server.adventure.bossbar.BossBarManager;
import net.minestom.server.command.CommandManager;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.damage.DamageType;
import net.minestom.server.entity.metadata.animal.tameable.WolfMeta;
import net.minestom.server.event.EventDispatcher;
import net.minestom.server.event.GlobalEventHandler;
import net.minestom.server.event.server.ServerTickMonitorEvent;
@ -14,14 +16,18 @@ import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.InstanceManager;
import net.minestom.server.instance.block.BlockManager;
import net.minestom.server.item.armor.TrimManager;
import net.minestom.server.instance.block.banner.BannerPattern;
import net.minestom.server.item.armor.TrimMaterial;
import net.minestom.server.item.armor.TrimPattern;
import net.minestom.server.listener.manager.PacketListenerManager;
import net.minestom.server.message.ChatType;
import net.minestom.server.monitoring.BenchmarkManager;
import net.minestom.server.monitoring.TickMonitor;
import net.minestom.server.network.ConnectionManager;
import net.minestom.server.network.PacketProcessor;
import net.minestom.server.network.socket.Server;
import net.minestom.server.recipe.RecipeManager;
import net.minestom.server.registry.DynamicRegistry;
import net.minestom.server.scoreboard.TeamManager;
import net.minestom.server.snapshot.*;
import net.minestom.server.thread.Acquirable;
@ -30,8 +36,8 @@ import net.minestom.server.timer.SchedulerManager;
import net.minestom.server.utils.PacketUtils;
import net.minestom.server.utils.PropertyUtils;
import net.minestom.server.utils.collection.MappedCollection;
import net.minestom.server.world.DimensionTypeManager;
import net.minestom.server.world.biomes.BiomeManager;
import net.minestom.server.world.DimensionType;
import net.minestom.server.world.biome.Biome;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -59,12 +65,19 @@ final class ServerProcessImpl implements ServerProcess {
private final GlobalEventHandler eventHandler;
private final SchedulerManager scheduler;
private final BenchmarkManager benchmark;
private final DimensionTypeManager dimension;
private final BiomeManager biome;
private final AdvancementManager advancement;
private final BossBarManager bossBar;
private final TagManager tag;
private final TrimManager trim;
private final DynamicRegistry<ChatType> chatType;
private final DynamicRegistry<DimensionType> dimensionType;
private final DynamicRegistry<Biome> biome;
private final DynamicRegistry<DamageType> damageType;
private final DynamicRegistry<TrimMaterial> trimMaterial;
private final DynamicRegistry<TrimPattern> trimPattern;
private final DynamicRegistry<BannerPattern> bannerPattern;
private final DynamicRegistry<WolfMeta.Variant> wolfVariant;
private final Server server;
private final ThreadDispatcher<Chunk> dispatcher;
@ -86,12 +99,19 @@ final class ServerProcessImpl implements ServerProcess {
this.eventHandler = new GlobalEventHandler();
this.scheduler = new SchedulerManager();
this.benchmark = new BenchmarkManager();
this.dimension = new DimensionTypeManager();
this.biome = new BiomeManager();
this.advancement = new AdvancementManager();
this.bossBar = new BossBarManager();
this.tag = new TagManager();
this.trim = new TrimManager();
this.chatType = ChatType.createDefaultRegistry();
this.dimensionType = DimensionType.createDefaultRegistry();
this.biome = Biome.createDefaultRegistry();
this.damageType = DamageType.createDefaultRegistry();
this.trimMaterial = TrimMaterial.createDefaultRegistry();
this.trimPattern = TrimPattern.createDefaultRegistry();
this.bannerPattern = BannerPattern.createDefaultRegistry();
this.wolfVariant = WolfMeta.Variant.createDefaultRegistry();
this.server = new Server(packetProcessor);
this.dispatcher = ThreadDispatcher.singleThread();
@ -143,16 +163,6 @@ final class ServerProcessImpl implements ServerProcess {
return benchmark;
}
@Override
public @NotNull DimensionTypeManager dimension() {
return dimension;
}
@Override
public @NotNull BiomeManager biome() {
return biome;
}
@Override
public @NotNull AdvancementManager advancement() {
return advancement;
@ -169,8 +179,43 @@ final class ServerProcessImpl implements ServerProcess {
}
@Override
public @NotNull TrimManager trim() {
return trim;
public @NotNull DynamicRegistry<ChatType> chatType() {
return chatType;
}
@Override
public @NotNull DynamicRegistry<DimensionType> dimensionType() {
return dimensionType;
}
@Override
public @NotNull DynamicRegistry<Biome> biome() {
return biome;
}
@Override
public @NotNull DynamicRegistry<DamageType> damageType() {
return damageType;
}
@Override
public @NotNull DynamicRegistry<TrimMaterial> trimMaterial() {
return trimMaterial;
}
@Override
public @NotNull DynamicRegistry<TrimPattern> trimPattern() {
return trimPattern;
}
@Override
public @NotNull DynamicRegistry<BannerPattern> bannerPattern() {
return bannerPattern;
}
@Override
public @NotNull DynamicRegistry<WolfMeta.Variant> wolfVariant() {
return wolfVariant;
}
@Override

View File

@ -1,7 +1,9 @@
package net.minestom.server.adventure.serializer.nbt;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.serializer.ComponentSerializer;
import org.jetbrains.annotations.NotNull;
@ -9,4 +11,8 @@ public interface NbtComponentSerializer extends ComponentSerializer<Component, C
static @NotNull NbtComponentSerializer nbt() {
return NbtComponentSerializerImpl.INSTANCE;
}
@NotNull Style deserializeStyle(@NotNull BinaryTag tag);
@NotNull CompoundBinaryTag serializeStyle(@NotNull Style style);
}

View File

@ -96,6 +96,17 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer {
}
// Formatting
builder.style(deserializeStyle(compound));
return builder.build();
}
@Override
public @NotNull Style deserializeStyle(@NotNull BinaryTag tag) {
if (!(tag instanceof CompoundBinaryTag compound)) {
return Style.empty();
}
var style = Style.style();
var color = compound.getString("color");
if (!color.isEmpty()) {
@ -128,17 +139,16 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer {
BinaryTag obfuscated = compound.get("obfuscated");
if (obfuscated instanceof ByteBinaryTag b)
style.decoration(TextDecoration.OBFUSCATED, b.value() == 1 ? TextDecoration.State.TRUE : TextDecoration.State.FALSE);
builder.style(style.build());
// Interactivity
var insertion = compound.getString("insertion");
if (!insertion.isEmpty()) builder.insertion(insertion);
if (!insertion.isEmpty()) style.insertion(insertion);
var clickEvent = compound.getCompound("clickEvent");
if (clickEvent.size() > 0) builder.clickEvent(deserializeClickEvent(clickEvent));
if (clickEvent.size() > 0) style.clickEvent(deserializeClickEvent(clickEvent));
var hoverEvent = compound.getCompound("hoverEvent");
if (hoverEvent.size() > 0) builder.hoverEvent(deserializeHoverEvent(hoverEvent));
if (hoverEvent.size() > 0) style.hoverEvent(deserializeHoverEvent(hoverEvent));
return builder.build();
return style.build();
}
private @NotNull ComponentBuilder<?, ?> deserializeTextComponent(@NotNull CompoundBinaryTag compound) {
@ -289,8 +299,16 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer {
compound.put("extra", children.build());
}
// Formatting
var style = component.style();
// Formatting/Interactivity
compound.put(serializeStyle(component.style()));
return compound.build();
}
@Override
public @NotNull CompoundBinaryTag serializeStyle(@NotNull Style style) {
CompoundBinaryTag.Builder compound = CompoundBinaryTag.builder();
var color = style.color();
if (color != null) {
if (color instanceof NamedTextColor named) {
@ -318,12 +336,11 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer {
if (obfuscated != TextDecoration.State.NOT_SET)
compound.putBoolean("obfuscated", obfuscated == TextDecoration.State.TRUE);
// Interactivity
var insertion = component.insertion();
var insertion = style.insertion();
if (insertion != null) compound.putString("insertion", insertion);
var clickEvent = component.clickEvent();
var clickEvent = style.clickEvent();
if (clickEvent != null) compound.put("clickEvent", serializeClickEvent(clickEvent));
var hoverEvent = component.hoverEvent();
var hoverEvent = style.hoverEvent();
if (hoverEvent != null) compound.put("hoverEvent", serializeHoverEvent(hoverEvent));
return compound.build();

View File

@ -43,7 +43,7 @@ final class BlockCollision {
static Entity canPlaceBlockAt(Instance instance, Point blockPos, Block b) {
for (Entity entity : instance.getNearbyEntities(blockPos, 3)) {
final EntityType type = entity.getEntityType();
if (type == EntityType.ITEM || type == EntityType.ARROW)
if (!entity.hasCollision() || type == EntityType.ITEM || type == EntityType.ARROW)
continue;
// Marker Armor Stands should not prevent block placement
if (entity.getEntityMeta() instanceof ArmorStandMeta armorStandMeta && armorStandMeta.isMarker())

View File

@ -20,7 +20,10 @@ import net.minestom.server.inventory.EquipmentHandler;
import net.minestom.server.item.ItemStack;
import net.minestom.server.network.ConnectionState;
import net.minestom.server.network.packet.server.LazyPacket;
import net.minestom.server.network.packet.server.play.*;
import net.minestom.server.network.packet.server.play.CollectItemPacket;
import net.minestom.server.network.packet.server.play.EntityAnimationPacket;
import net.minestom.server.network.packet.server.play.EntityAttributesPacket;
import net.minestom.server.network.packet.server.play.SoundEffectPacket;
import net.minestom.server.network.player.PlayerConnection;
import net.minestom.server.scoreboard.Team;
import net.minestom.server.sound.SoundEvent;
@ -336,7 +339,8 @@ public class LivingEntity extends Entity implements EquipmentHandler {
sendPacketToViewersAndSelf(new EntityAnimationPacket(getEntityId(), EntityAnimationPacket.Animation.TAKE_DAMAGE));
}
sendPacketToViewersAndSelf(new DamageEventPacket(getEntityId(), damage.getType().id(), 0, 0, null));
//todo
// sendPacketToViewersAndSelf(new DamageEventPacket(getEntityId(), damage.getType().id(), 0, 0, null));
// Additional hearts support
if (this instanceof Player player) {

View File

@ -73,6 +73,7 @@ import net.minestom.server.network.player.PlayerConnection;
import net.minestom.server.network.player.PlayerSocketConnection;
import net.minestom.server.recipe.Recipe;
import net.minestom.server.recipe.RecipeManager;
import net.minestom.server.registry.DynamicRegistry;
import net.minestom.server.scoreboard.BelowNameTag;
import net.minestom.server.scoreboard.Team;
import net.minestom.server.snapshot.EntitySnapshot;
@ -121,6 +122,8 @@ import java.util.function.UnaryOperator;
public class Player extends LivingEntity implements CommandSender, Localizable, HoverEventSource<ShowEntity>, Identified, NamedAndIdentified {
private static final Logger logger = LoggerFactory.getLogger(Player.class);
private static final DynamicRegistry<DimensionType> DIMENSION_TYPE_REGISTRY = MinecraftServer.getDimensionTypeRegistry();
private static final Component REMOVE_MESSAGE = Component.text("You have been removed from the server without reason.", NamedTextColor.RED);
private static final float MIN_CHUNKS_PER_TICK = PropertyUtils.getFloat("minestom.chunk-queue.min-per-tick", 0.01f);
@ -145,7 +148,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
private PlayerSkin skin;
private Instance pendingInstance = null;
private DimensionType dimensionType;
private int dimensionTypeId;
private GameMode gameMode;
private WorldPos deathLocation;
@ -251,7 +254,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
refreshAnswerKeepAlive(true);
this.gameMode = GameMode.SURVIVAL;
this.dimensionType = DimensionType.OVERWORLD; // Default dimension
this.dimensionTypeId = DIMENSION_TYPE_REGISTRY.getId(DimensionType.OVERWORLD); // Default dimension
this.levelFlat = true;
getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).setBaseValue(0.1);
@ -290,12 +293,12 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
this.pendingInstance = null;
this.removed = false;
this.dimensionType = spawnInstance.getDimensionType();
this.dimensionTypeId = DIMENSION_TYPE_REGISTRY.getId(spawnInstance.getDimensionType().namespace());
final JoinGamePacket joinGamePacket = new JoinGamePacket(
getEntityId(), this.hardcore, List.of(), 0,
ServerFlag.CHUNK_VIEW_DISTANCE, ServerFlag.CHUNK_VIEW_DISTANCE,
false, true, false, dimensionType.getId(), spawnInstance.getDimensionName(),
false, true, false, dimensionTypeId, spawnInstance.getDimensionName(),
0, gameMode, null, false, levelFlat, deathLocation, portalCooldown, true);
sendPacket(joinGamePacket);
@ -524,7 +527,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
setOnFire(false);
refreshHealth();
sendPacket(new RespawnPacket(getDimensionType().getId(), instance.getDimensionName(),
sendPacket(new RespawnPacket(DIMENSION_TYPE_REGISTRY.getId(getDimensionType().namespace()), instance.getDimensionName(),
0, gameMode, gameMode, false, levelFlat, deathLocation, portalCooldown, RespawnPacket.COPY_ALL));
refreshClientStateAfterRespawn();
@ -1038,7 +1041,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
@Override
public boolean isImmune(@NotNull DamageType type) {
if (!getGameMode().canTakeDamage()) {
return type != DamageType.OUT_OF_WORLD;
return !DamageType.OUT_OF_WORLD.equals(type.namespace());
}
return super.isImmune(type);
}
@ -1200,7 +1203,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
final PlayerInfoRemovePacket removePlayerPacket = getRemovePlayerToList();
final PlayerInfoUpdatePacket addPlayerPacket = getAddPlayerToList();
RespawnPacket respawnPacket = new RespawnPacket(getDimensionType().getId(), instance.getDimensionName(),
RespawnPacket respawnPacket = new RespawnPacket(dimensionTypeId, instance.getDimensionName(),
0, gameMode, gameMode, false, levelFlat, deathLocation, portalCooldown, RespawnPacket.COPY_ALL);
sendPacket(removePlayerPacket);
@ -1224,7 +1227,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
}
public void setDeathLocation(@NotNull DimensionType type, @NotNull Pos position) {
this.deathLocation = new WorldPos(type.getName().asString(), position);
this.deathLocation = new WorldPos(type.name(), position);
}
public @Nullable WorldPos getDeathLocation() {
@ -1547,7 +1550,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
* @return the player current dimension
*/
public DimensionType getDimensionType() {
return dimensionType;
return DIMENSION_TYPE_REGISTRY.get(dimensionTypeId);
}
public @NotNull PlayerInventory getInventory() {
@ -1650,8 +1653,8 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
protected void sendDimension(@NotNull DimensionType dimensionType, @NotNull String dimensionName) {
Check.argCondition(instance.getDimensionName().equals(dimensionName),
"The dimension needs to be different than the current one!");
this.dimensionType = dimensionType;
sendPacket(new RespawnPacket(dimensionType.getId(), dimensionName,
this.dimensionTypeId = DIMENSION_TYPE_REGISTRY.getId(dimensionType.namespace());
sendPacket(new RespawnPacket(dimensionTypeId, dimensionName,
0, gameMode, gameMode, false, levelFlat,
deathLocation, portalCooldown, RespawnPacket.COPY_ALL));
refreshClientStateAfterRespawn();

View File

@ -160,7 +160,7 @@ public class Damage implements Taggable {
}
protected SoundEvent getPlayerSound(@NotNull Player player) {
if (type == DamageType.ON_FIRE) return SoundEvent.ENTITY_PLAYER_HURT_ON_FIRE;
if (DamageType.ON_FIRE.equals(type.namespace())) return SoundEvent.ENTITY_PLAYER_HURT_ON_FIRE;
return SoundEvent.ENTITY_PLAYER_HURT;
}

View File

@ -1,60 +1,84 @@
package net.minestom.server.entity.damage;
import net.minestom.server.network.packet.server.configuration.RegistryDataPacket;
import net.minestom.server.registry.DynamicRegistry;
import net.minestom.server.registry.DynamicRegistryImpl;
import net.minestom.server.registry.ProtocolObject;
import net.minestom.server.registry.Registry;
import net.minestom.server.registry.StaticProtocolObject;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.Contract;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
public sealed interface DamageType extends ProtocolObject, DamageTypes permits DamageTypeImpl {
@NotNull BinaryTagSerializer<DamageType> NBT_TYPE = DamageTypeImpl.NBT_TYPE;
static @NotNull DamageType create(
@NotNull NamespaceID namespace,
float exhaustion,
@NotNull String messageId,
@NotNull String scaling
) {
return new DamageTypeImpl(namespace, exhaustion, messageId, scaling, null);
}
static @NotNull Builder builder(@NotNull String namespace) {
return builder(NamespaceID.from(namespace));
}
static @NotNull Builder builder(@NotNull NamespaceID namespace) {
return new Builder(namespace);
}
public sealed interface DamageType extends StaticProtocolObject, DamageTypes permits DamageTypeImpl {
/**
* Returns the damage type registry.
* <p>Creates a new registry for damage types, loading the vanilla damage types.</p>
*
* @return the damage type registry
* @see net.minestom.server.MinecraftServer to get an existing instance of the registry
*/
@Contract(pure = true)
@NotNull Registry.DamageTypeEntry registry();
@Override
default @NotNull NamespaceID namespace() {
return registry().namespace();
@ApiStatus.Internal
static @NotNull DynamicRegistry<DamageType> createDefaultRegistry() {
return new DynamicRegistryImpl<>(
"minecraft:damage_type", NBT_TYPE, Registry.Resource.DAMAGE_TYPES,
(namespace, props) -> new DamageTypeImpl(Registry.damageType(namespace, props))
);
}
default double exhaustion() {
return registry().exhaustion();
float exhaustion();
@NotNull String messageId();
@NotNull String scaling();
@Nullable Registry.DamageTypeEntry registry();
final class Builder {
private final NamespaceID namespace;
private float exhaustion = 0f;
private String messageId;
private String scaling;
public Builder(@NotNull NamespaceID namespace) {
this.namespace = namespace;
}
public @NotNull Builder exhaustion(float exhaustion) {
this.exhaustion = exhaustion;
return this;
}
public @NotNull Builder messageId(@NotNull String messageId) {
this.messageId = messageId;
return this;
}
public @NotNull Builder scaling(@NotNull String scaling) {
this.scaling = scaling;
return this;
}
public @NotNull DamageType build() {
return new DamageTypeImpl(namespace, exhaustion, messageId, scaling, null);
}
}
default String messageId() {
return registry().messageId();
}
default String scaling() {
return registry().scaling();
}
@NotNull RegistryDataPacket.Entry toRegistryEntry();
static @NotNull Collection<@NotNull DamageType> values() {
return DamageTypeImpl.values();
}
static DamageType fromNamespaceId(@NotNull String namespaceID) {
return DamageTypeImpl.getSafe(namespaceID);
}
static DamageType fromNamespaceId(@NotNull NamespaceID namespaceID) {
return fromNamespaceId(namespaceID.asString());
}
static @Nullable DamageType fromId(int id) {
return DamageTypeImpl.getId(id);
}
static @NotNull RegistryDataPacket registryDataPacket() {
return DamageTypeImpl.registryDataPacket();
}
}

View File

@ -1,67 +1,41 @@
package net.minestom.server.entity.damage;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.network.packet.server.configuration.RegistryDataPacket;
import net.minestom.server.registry.Registry;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicInteger;
record DamageTypeImpl(
@NotNull NamespaceID namespace,
float exhaustion,
@NotNull String messageId,
@NotNull String scaling,
@Nullable Registry.DamageTypeEntry registry
) implements DamageType {
record DamageTypeImpl(Registry.DamageTypeEntry registry, int id) implements DamageType {
private static final Registry.Container<DamageType> CONTAINER;
static final BinaryTagSerializer<DamageType> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
tag -> {
throw new UnsupportedOperationException("DamageType is read-only");
},
damageType -> CompoundBinaryTag.builder()
.putFloat("exhaustion", damageType.exhaustion())
.putString("message_id", damageType.messageId())
.putString("scaling", damageType.scaling())
.build()
);
static {
AtomicInteger i = new AtomicInteger();
CONTAINER = Registry.createStaticContainer(Registry.Resource.DAMAGE_TYPES,
(namespace, properties) -> new DamageTypeImpl(Registry.damageType(namespace, properties), i.getAndIncrement()));
@SuppressWarnings("ConstantValue") // The builder can violate the nullability constraints
DamageTypeImpl {
Check.notNull(namespace, "Namespace cannot be null");
Check.argCondition(messageId == null || messageId.isEmpty(), "missing message id: {0}", namespace);
Check.argCondition(scaling == null || scaling.isEmpty(), "missing scaling: {0}", namespace);
}
static DamageType get(@NotNull String namespace) {
return CONTAINER.get(namespace);
DamageTypeImpl(@NotNull Registry.DamageTypeEntry registry) {
this(registry.namespace(), registry.exhaustion(), registry.messageId(), registry.scaling(), registry);
}
static DamageType getSafe(@NotNull String namespace) {
return CONTAINER.getSafe(namespace);
}
static DamageType getId(int id) {
return CONTAINER.getId(id);
}
@Override
public @NotNull RegistryDataPacket.Entry toRegistryEntry() {
return new RegistryDataPacket.Entry(name(), CompoundBinaryTag.builder()
.putFloat("exhaustion", registry.exhaustion())
.putString("message_id", registry.messageId())
.putString("scaling", registry.scaling())
.build());
}
static Collection<DamageType> values() {
return CONTAINER.values();
}
@Override
public String toString() {
return name();
}
@Override
public int id() {
return id;
}
private static RegistryDataPacket lazyRegistryDataPacket = null;
static @NotNull RegistryDataPacket registryDataPacket() {
if (lazyRegistryDataPacket != null) return lazyRegistryDataPacket;
return lazyRegistryDataPacket = new RegistryDataPacket(
"minecraft:damage_type",
values().stream()
.map(DamageType::toRegistryEntry)
.toList()
);
}
}

View File

@ -1,15 +1,21 @@
package net.minestom.server.entity.damage;
import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.Entity;
import net.minestom.server.registry.DynamicRegistry;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
/**
* Represents damage inflicted by an {@link Entity}.
*/
public class EntityDamage extends Damage {
private static final DynamicRegistry<DamageType> DAMAGE_TYPES = MinecraftServer.getDamageTypeRegistry();
public EntityDamage(@NotNull Entity source, float amount) {
super(DamageType.MOB_ATTACK, source, source, null, amount);
super(Objects.requireNonNull(DAMAGE_TYPES.get(DamageType.MOB_ATTACK)),
source, source, null, amount);
}
/**

View File

@ -1,16 +1,21 @@
package net.minestom.server.entity.damage;
import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.Entity;
import net.minestom.server.registry.DynamicRegistry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
/**
* Represents damage inflicted by an entity, via a projectile.
*/
public class EntityProjectileDamage extends Damage {
private static final DynamicRegistry<DamageType> DAMAGE_TYPES = MinecraftServer.getDamageTypeRegistry();
public EntityProjectileDamage(@Nullable Entity shooter, @NotNull Entity projectile, float amount) {
super(DamageType.MOB_PROJECTILE, projectile, shooter, null, amount);
super(Objects.requireNonNull(DAMAGE_TYPES.get(DamageType.MOB_PROJECTILE)), projectile, shooter, null, amount);
}
/**

View File

@ -1,8 +1,22 @@
package net.minestom.server.entity.metadata.animal.tameable;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.StringBinaryTag;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.Metadata;
import net.minestom.server.registry.DynamicRegistry;
import net.minestom.server.registry.DynamicRegistryImpl;
import net.minestom.server.registry.ProtocolObject;
import net.minestom.server.registry.Registry;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class WolfMeta extends TameableAnimalMeta {
public static final byte OFFSET = TameableAnimalMeta.MAX_OFFSET;
@ -38,4 +52,136 @@ public class WolfMeta extends TameableAnimalMeta {
super.metadata.setIndex(OFFSET + 2, Metadata.VarInt(value));
}
public sealed interface Variant extends ProtocolObject, WolfVariants permits VariantImpl {
@NotNull BinaryTagSerializer<Variant> NBT_TYPE = VariantImpl.NBT_TYPE;
static @NotNull Variant create(
@NotNull NamespaceID namespace,
@NotNull NamespaceID wildTexture,
@NotNull NamespaceID tameTexture,
@NotNull NamespaceID angryTexture,
@NotNull String biome
) {
return new VariantImpl(namespace, wildTexture, tameTexture, angryTexture, List.of(biome), null);
}
static @NotNull Builder builder(@NotNull String namespace) {
return builder(NamespaceID.from(namespace));
}
static @NotNull Builder builder(@NotNull NamespaceID namespace) {
return new Builder(namespace);
}
/**
* <p>Creates a new registry for wolf variants, loading the vanilla wolf variants.</p>
*
* @see net.minestom.server.MinecraftServer to get an existing instance of the registry
*/
@ApiStatus.Internal
static @NotNull DynamicRegistry<Variant> createDefaultRegistry() {
return new DynamicRegistryImpl<>(
"minecraft:wolf_variant", NBT_TYPE, Registry.Resource.WOLF_VARIANTS,
(namespace, props) -> new WolfMeta.VariantImpl(Registry.wolfVariant(namespace, props))
);
}
@NotNull NamespaceID wildTexture();
@NotNull NamespaceID tameTexture();
@NotNull NamespaceID angryTexture();
@NotNull List<String> biomes();
@Override
@Nullable Registry.WolfVariantEntry registry();
final class Builder {
private final NamespaceID namespace;
private NamespaceID wildTexture;
private NamespaceID tameTexture;
private NamespaceID angryTexture;
private List<String> biomes;
private Builder(@NotNull NamespaceID namespace) {
this.namespace = namespace;
}
public @NotNull Builder wildTexture(@NotNull NamespaceID wildTexture) {
this.wildTexture = wildTexture;
return this;
}
public @NotNull Builder tameTexture(@NotNull NamespaceID tameTexture) {
this.tameTexture = tameTexture;
return this;
}
public @NotNull Builder angryTexture(@NotNull NamespaceID angryTexture) {
this.angryTexture = angryTexture;
return this;
}
public @NotNull Builder biome(@NotNull String biome) {
this.biomes = List.of(biome);
return this;
}
public @NotNull Builder biomes(@NotNull List<String> biomes) {
this.biomes = biomes;
return this;
}
public @NotNull Variant build() {
return new VariantImpl(namespace, wildTexture, tameTexture, angryTexture, biomes, null);
}
}
}
record VariantImpl(
@NotNull NamespaceID namespace,
@NotNull NamespaceID wildTexture,
@NotNull NamespaceID tameTexture,
@NotNull NamespaceID angryTexture,
@NotNull List<String> biomes,
@Nullable Registry.WolfVariantEntry registry
) implements Variant {
private static final BinaryTagSerializer<List<String>> BIOMES_NBT_TYPE = BinaryTagSerializer.STRING.list();
static final BinaryTagSerializer<Variant> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
tag -> {
throw new UnsupportedOperationException("WolfVariant is read-only");
},
wolfVariant -> {
BinaryTag biomes;
if (wolfVariant.biomes().size() == 1) {
biomes = StringBinaryTag.stringBinaryTag(wolfVariant.biomes().getFirst());
} else {
biomes = BIOMES_NBT_TYPE.write(wolfVariant.biomes());
}
return CompoundBinaryTag.builder()
.putString("wild_texture", wolfVariant.wildTexture().asString())
.putString("tame_texture", wolfVariant.tameTexture().asString())
.putString("angry_texture", wolfVariant.angryTexture().asString())
.put("biomes", biomes)
.build();
}
);
VariantImpl {
// The builder can violate the nullability constraints
Check.notNull(namespace, "Namespace cannot be null");
Check.notNull(wildTexture, "missing wild texture: {0}", namespace);
Check.notNull(tameTexture, "missing tame texture: {0}", namespace);
Check.notNull(angryTexture, "missing angry texture: {0}", namespace);
Check.notNull(biomes, "missing biomes: {0}", namespace);
}
VariantImpl(@NotNull Registry.WolfVariantEntry registry) {
this(registry.namespace(), registry.wildTexture(), registry.tameTexture(),
registry.angryTexture(), registry.biomes(), registry);
}
}
}

View File

@ -4,7 +4,7 @@ import net.minestom.server.entity.EntityType;
import net.minestom.server.instance.block.Block;
import net.minestom.server.item.Material;
import net.minestom.server.registry.ProtocolObject;
import net.minestom.server.registry.Registries;
import net.minestom.server.registry.FluidRegistries;
import net.minestom.server.registry.Registry;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.NotNull;
@ -78,11 +78,11 @@ public final class Tag implements ProtocolObject {
ITEMS("minecraft:item", Registry.Resource.ITEM_TAGS,
name -> Objects.requireNonNull(Material.fromNamespaceId(name)).id()),
FLUIDS("minecraft:fluid", Registry.Resource.FLUID_TAGS,
name -> Registries.getFluid(name).ordinal()),
name -> FluidRegistries.getFluid(name).ordinal()),
ENTITY_TYPES("minecraft:entity_type", Registry.Resource.ENTITY_TYPE_TAGS,
name -> Objects.requireNonNull(EntityType.fromNamespaceId(name)).id()),
GAME_EVENTS("minecraft:game_event", Registry.Resource.GAMEPLAY_TAGS,
name -> Registries.getFluid(name).ordinal());
name -> FluidRegistries.getFluid(name).ordinal());
private final static BasicType[] VALUES = values();
private final String identifier;

View File

@ -18,7 +18,7 @@ import net.minestom.server.tag.TagHandler;
import net.minestom.server.tag.Taggable;
import net.minestom.server.utils.chunk.ChunkSupplier;
import net.minestom.server.utils.chunk.ChunkUtils;
import net.minestom.server.world.biomes.Biome;
import net.minestom.server.world.biome.Biome;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -70,8 +70,8 @@ public abstract class Chunk implements Block.Getter, Block.Setter, Biome.Getter,
this.chunkX = chunkX;
this.chunkZ = chunkZ;
this.shouldGenerate = shouldGenerate;
this.minSection = instance.getDimensionType().getMinY() / CHUNK_SECTION_SIZE;
this.maxSection = (instance.getDimensionType().getMinY() + instance.getDimensionType().getHeight()) / CHUNK_SECTION_SIZE;
this.minSection = instance.getDimensionType().minY() / CHUNK_SECTION_SIZE;
this.maxSection = (instance.getDimensionType().minY() + instance.getDimensionType().height()) / CHUNK_SECTION_SIZE;
final List<SharedInstance> shared = instance instanceof InstanceContainer instanceContainer ?
instanceContainer.getSharedInstances() : List.of();
this.viewable = instance.getEntityTracker().viewable(shared, chunkX, chunkZ);

View File

@ -21,13 +21,13 @@ import net.minestom.server.network.packet.server.play.ChunkDataPacket;
import net.minestom.server.network.packet.server.play.UpdateLightPacket;
import net.minestom.server.network.packet.server.play.data.ChunkData;
import net.minestom.server.network.packet.server.play.data.LightData;
import net.minestom.server.registry.DynamicRegistry;
import net.minestom.server.snapshot.ChunkSnapshot;
import net.minestom.server.snapshot.SnapshotImpl;
import net.minestom.server.snapshot.SnapshotUpdater;
import net.minestom.server.utils.ArrayUtils;
import net.minestom.server.utils.chunk.ChunkUtils;
import net.minestom.server.world.biomes.Biome;
import net.minestom.server.world.biomes.BiomeManager;
import net.minestom.server.world.biome.Biome;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
@ -58,7 +58,7 @@ public class DynamicChunk extends Chunk {
private long lastChange;
final CachedPacket chunkCache = new CachedPacket(this::createChunkPacket);
private static final BiomeManager BIOME_MANAGER = MinecraftServer.getBiomeManager();
private static final DynamicRegistry<Biome> BIOME_REGISTRY = MinecraftServer.getBiomeRegistry();
public DynamicChunk(@NotNull Instance instance, int chunkX, int chunkZ) {
super(instance, chunkX, chunkZ, true);
@ -71,9 +71,9 @@ public class DynamicChunk extends Chunk {
public void setBlock(int x, int y, int z, @NotNull Block block,
@Nullable BlockHandler.Placement placement,
@Nullable BlockHandler.Destroy destroy) {
if(y >= instance.getDimensionType().getMaxY() || y < instance.getDimensionType().getMinY()) {
if(y >= instance.getDimensionType().maxY() || y < instance.getDimensionType().minY()) {
LOGGER.warn("tried to set a block outside the world bounds, should be within [{}, {}): {}",
instance.getDimensionType().getMinY(), instance.getDimensionType().getMaxY(), y);
instance.getDimensionType().minY(), instance.getDimensionType().maxY(), y);
return;
}
assertLock();
@ -141,7 +141,7 @@ public class DynamicChunk extends Chunk {
this.chunkCache.invalidate();
Section section = getSectionAt(y);
var id = BIOME_MANAGER.getId(biome);
var id = BIOME_REGISTRY.getId(biome.namespace());
if (id == -1) throw new IllegalStateException("Biome has not been registered: " + biome.namespace());
section.biomePalette().set(
@ -222,7 +222,7 @@ public class DynamicChunk extends Chunk {
final int id = section.biomePalette()
.get(toSectionRelativeCoordinate(x) / 4, toSectionRelativeCoordinate(y) / 4, toSectionRelativeCoordinate(z) / 4);
Biome biome = BIOME_MANAGER.getById(id);
Biome biome = BIOME_REGISTRY.get(id);
if (biome == null) {
throw new IllegalStateException("Biome with id " + id + " is not registered");
}

View File

@ -9,8 +9,8 @@ import net.minestom.server.instance.block.Block;
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 net.minestom.server.registry.DynamicRegistry;
import net.minestom.server.world.biome.Biome;
import org.jetbrains.annotations.NotNull;
import java.util.List;
@ -23,7 +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();
private static final DynamicRegistry<Biome> BIOME_MANAGER = MinecraftServer.getBiomeRegistry();
static GenerationUnit section(Section section, int sectionX, int sectionY, int sectionZ,
boolean fork) {
@ -217,10 +217,15 @@ final class GeneratorImpl {
record SectionModifierImpl(Point size, Point start, Point end,
Palette blockPalette, Palette biomePalette,
Int2ObjectMap<Block> cache, boolean fork) implements GenericModifier {
SectionModifierImpl {
biomePalette.fill(BIOME_MANAGER.getId(Biome.PLAINS));
}
@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);
var id = BIOME_MANAGER.getId(biome.namespace());
if (id == -1) throw new IllegalStateException("Biome has not been registered: " + biome.namespace());
this.biomePalette.set(
@ -270,7 +275,7 @@ final class GeneratorImpl {
@Override
public void fillBiome(@NotNull Biome biome) {
if (fork) throw new IllegalStateException("Cannot modify biomes of a fork");
var id = MinecraftServer.getBiomeManager().getId(biome);
var id = BIOME_MANAGER.getId(biome.namespace());
if (id == -1) throw new IllegalStateException("Biome has not been registered: " + biome.namespace());
this.biomePalette.fill(id);
}

View File

@ -27,6 +27,7 @@ import net.minestom.server.instance.generator.Generator;
import net.minestom.server.instance.light.Light;
import net.minestom.server.network.packet.server.play.BlockActionPacket;
import net.minestom.server.network.packet.server.play.TimeUpdatePacket;
import net.minestom.server.registry.DynamicRegistry;
import net.minestom.server.snapshot.*;
import net.minestom.server.tag.TagHandler;
import net.minestom.server.tag.Taggable;
@ -67,9 +68,12 @@ import java.util.stream.Collectors;
public abstract class Instance implements Block.Getter, Block.Setter,
Tickable, Schedulable, Snapshotable, EventHandler<InstanceEvent>, Taggable, PacketGroupingAudience {
private static final DynamicRegistry<DimensionType> DIMENSION_REGISTRY = MinecraftServer.getDimensionTypeRegistry();
private boolean registered;
private final DimensionType dimensionType;
private final int dimensionTypeId;
private final DimensionType cachedDimensionType;
private final String dimensionName;
private final WorldBorder worldBorder;
@ -120,7 +124,7 @@ public abstract class Instance implements Block.Getter, Block.Setter,
* @param dimensionType the {@link DimensionType} of the instance
*/
public Instance(@NotNull UUID uniqueId, @NotNull DimensionType dimensionType) {
this(uniqueId, dimensionType, dimensionType.getName());
this(uniqueId, dimensionType, dimensionType.namespace());
}
/**
@ -130,10 +134,10 @@ public abstract class Instance implements Block.Getter, Block.Setter,
* @param dimensionType the {@link DimensionType} of the instance
*/
public Instance(@NotNull UUID uniqueId, @NotNull DimensionType dimensionType, @NotNull NamespaceID dimensionName) {
Check.argCondition(!dimensionType.isRegistered(),
"The dimension " + dimensionType.getName() + " is not registered! Please use DimensionTypeManager#addDimension");
this.uniqueId = uniqueId;
this.dimensionType = dimensionType;
this.dimensionTypeId = DIMENSION_REGISTRY.getId(dimensionType.namespace());
Check.argCondition(this.dimensionTypeId == -1, "The dimension " + dimensionType.name() + " is not registered! Please add it to the registry (`MinecraftServer.getDimensionTypeRegistry().registry(dimensionType)`).");
this.cachedDimensionType = dimensionType;
this.dimensionName = dimensionName.asString();
this.worldBorder = new WorldBorder(this);
@ -396,7 +400,7 @@ public abstract class Instance implements Block.Getter, Block.Setter,
* @return the dimension of the instance
*/
public DimensionType getDimensionType() {
return dimensionType;
return cachedDimensionType;
}
/**

View File

@ -31,7 +31,6 @@ import net.minestom.server.utils.chunk.ChunkSupplier;
import net.minestom.server.utils.chunk.ChunkUtils;
import net.minestom.server.utils.validate.Check;
import net.minestom.server.world.DimensionType;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
@ -88,19 +87,17 @@ public class InstanceContainer extends Instance {
private long lastBlockChangeTime; // Time at which the last block change happened (#setBlock)
public InstanceContainer(@NotNull UUID uniqueId, @NotNull DimensionType dimensionType) {
this(uniqueId, dimensionType, null, dimensionType.getName());
this(uniqueId, dimensionType, null, dimensionType.namespace());
}
public InstanceContainer(@NotNull UUID uniqueId, @NotNull DimensionType dimensionType, @NotNull NamespaceID dimensionName) {
this(uniqueId, dimensionType, null, dimensionName);
}
@ApiStatus.Experimental
public InstanceContainer(@NotNull UUID uniqueId, @NotNull DimensionType dimensionType, @Nullable IChunkLoader loader) {
this(uniqueId, dimensionType, loader, dimensionType.getName());
this(uniqueId, dimensionType, loader, dimensionType.namespace());
}
@ApiStatus.Experimental
public InstanceContainer(@NotNull UUID uniqueId, @NotNull DimensionType dimensionType, @Nullable IChunkLoader loader, @NotNull NamespaceID dimensionName) {
super(uniqueId, dimensionType, dimensionName);
setChunkSupplier(DynamicChunk::new);
@ -134,8 +131,8 @@ public class InstanceContainer extends Instance {
@Nullable BlockHandler.Placement placement, @Nullable BlockHandler.Destroy destroy,
boolean doBlockUpdates, int updateDistance) {
if (chunk.isReadOnly()) return;
if(y >= getDimensionType().getMaxY() || y < getDimensionType().getMinY()) {
LOGGER.warn("tried to set a block outside the world bounds, should be within [{}, {}): {}", getDimensionType().getMinY(), getDimensionType().getMaxY(), y);
if (y >= getDimensionType().maxY() || y < getDimensionType().minY()) {
LOGGER.warn("tried to set a block outside the world bounds, should be within [{}, {}): {}", getDimensionType().minY(), getDimensionType().maxY(), y);
return;
}
@ -440,7 +437,7 @@ public class InstanceContainer extends Instance {
@Override
public boolean isInVoid(@NotNull Point point) {
// TODO: more customizable
return point.y() < getDimensionType().getMinY() - 64;
return point.y() < getDimensionType().minY() - 64;
}
/**
@ -629,7 +626,7 @@ public class InstanceContainer extends Instance {
final int neighborX = blockPosition.blockX() + direction.normalX();
final int neighborY = blockPosition.blockY() + direction.normalY();
final int neighborZ = blockPosition.blockZ() + direction.normalZ();
if (neighborY < getDimensionType().getMinY() || neighborY > getDimensionType().getTotalHeight())
if (neighborY < getDimensionType().minY() || neighborY > getDimensionType().height())
continue;
final Block neighborBlock = cache.getBlock(neighborX, neighborY, neighborZ, Condition.TYPE);
if (neighborBlock == null)

View File

@ -10,10 +10,7 @@ import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
/**
@ -57,7 +54,8 @@ public final class InstanceManager {
@ApiStatus.Experimental
public @NotNull InstanceContainer createInstanceContainer(@Nullable IChunkLoader loader) {
return createInstanceContainer(DimensionType.OVERWORLD, loader);
DimensionType defaultDimension = Objects.requireNonNull(MinecraftServer.getDimensionTypeRegistry().get(DimensionType.OVERWORLD));
return createInstanceContainer(defaultDimension, loader);
}
/**
@ -66,7 +64,8 @@ public final class InstanceManager {
* @return the created {@link InstanceContainer}
*/
public @NotNull InstanceContainer createInstanceContainer() {
return createInstanceContainer(DimensionType.OVERWORLD, null);
DimensionType defaultDimension = Objects.requireNonNull(MinecraftServer.getDimensionTypeRegistry().get(DimensionType.OVERWORLD));
return createInstanceContainer(defaultDimension, null);
}
/**

View File

@ -204,7 +204,7 @@ public class LightingChunk extends DynamicChunk {
if (this.occlusionMap != null) return this.occlusionMap;
var occlusionMap = new int[CHUNK_SIZE_X * CHUNK_SIZE_Z];
int minY = instance.getDimensionType().getMinY();
int minY = instance.getDimensionType().minY();
highestBlock = minY - 1;
synchronized (this) {
@ -243,9 +243,9 @@ public class LightingChunk extends DynamicChunk {
List<byte[]> skyLights = new ArrayList<>();
List<byte[]> blockLights = new ArrayList<>();
int chunkMin = instance.getDimensionType().getMinY();
int chunkMin = instance.getDimensionType().minY();
int highestNeighborBlock = instance.getDimensionType().getMinY();
int highestNeighborBlock = instance.getDimensionType().minY();
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
Chunk neighborChunk = instance.getChunk(chunkX + i, chunkZ + j);
@ -280,7 +280,7 @@ public class LightingChunk extends DynamicChunk {
final int sectionMinY = index * 16 + chunkMin;
index++;
if ((wasUpdatedSky) && this.instance.getDimensionType().isSkylightEnabled() && sectionMinY <= (highestNeighborBlock + 16)) {
if ((wasUpdatedSky) && this.instance.getDimensionType().hasSkylight() && sectionMinY <= (highestNeighborBlock + 16)) {
final byte[] skyLight = section.skyLight().array();
if (skyLight.length != 0 && skyLight != emptyContent) {
@ -422,7 +422,7 @@ public class LightingChunk extends DynamicChunk {
Set<Point> collected = new HashSet<>();
collected.add(point);
int highestRegionPoint = instance.getDimensionType().getMinY() - 1;
int highestRegionPoint = instance.getDimensionType().minY() - 1;
for (int x = point.blockX() - 1; x <= point.blockX() + 1; x++) {
for (int z = point.blockZ() - 1; z <= point.blockZ() + 1; z++) {
@ -444,7 +444,7 @@ public class LightingChunk extends DynamicChunk {
for (int y = point.blockY() - 1; y <= point.blockY() + 1; y++) {
Point sectionPosition = new Vec(x, y, z);
int sectionHeight = instance.getDimensionType().getMinY() + 16 * y;
int sectionHeight = instance.getDimensionType().minY() + 16 * y;
if ((sectionHeight + 16) > highestRegionPoint && type == LightType.SKY) continue;
if (sectionPosition.blockY() < chunkCheck.getMaxSection() && sectionPosition.blockY() >= chunkCheck.getMinSection()) {

View File

@ -9,13 +9,13 @@ import net.minestom.server.instance.Instance;
import net.minestom.server.instance.Section;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.block.BlockHandler;
import net.minestom.server.registry.DynamicRegistry;
import net.minestom.server.utils.ArrayUtils;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.async.AsyncUtils;
import net.minestom.server.utils.chunk.ChunkUtils;
import net.minestom.server.utils.validate.Check;
import net.minestom.server.world.biomes.Biome;
import net.minestom.server.world.biomes.BiomeManager;
import net.minestom.server.world.biome.Biome;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
@ -35,8 +35,8 @@ import java.util.concurrent.locks.ReentrantLock;
public class AnvilLoader implements IChunkLoader {
private final static Logger LOGGER = LoggerFactory.getLogger(AnvilLoader.class);
private static final BiomeManager BIOME_MANAGER = MinecraftServer.getBiomeManager();
private final static Biome PLAINS = BIOME_MANAGER.getByName(NamespaceID.from("minecraft:plains"));
private static final DynamicRegistry<Biome> BIOME_REGISTRY = MinecraftServer.getBiomeRegistry();
private final static int PLAINS_ID = BIOME_REGISTRY.getId(NamespaceID.from("minecraft:plains"));
private final Map<String, RegionFile> alreadyLoaded = new ConcurrentHashMap<>();
private final Path path;
@ -182,12 +182,12 @@ public class AnvilLoader implements IChunkLoader {
{ // Biomes
final CompoundBinaryTag biomesTag = sectionData.getCompound("biomes");
final ListBinaryTag biomePaletteTag = biomesTag.getList("palette", BinaryTagTypes.STRING);
Biome[] convertedPalette = loadBiomePalette(biomePaletteTag);
int[] convertedBiomePalette = loadBiomePalette(biomePaletteTag);
if (convertedPalette.length == 1) {
if (convertedBiomePalette.length == 1) {
// One solid block, no need to check the data
section.biomePalette().fill(BIOME_MANAGER.getId(convertedPalette[0]));
} else if (convertedPalette.length > 1) {
section.biomePalette().fill(convertedBiomePalette[0]);
} else if (convertedBiomePalette.length > 1) {
final long[] packedIndices = biomesTag.getLongArray("data");
Check.stateCondition(packedIndices.length == 0, "Missing packed biomes data");
int[] biomeIndices = new int[64];
@ -195,8 +195,7 @@ public class AnvilLoader implements IChunkLoader {
section.biomePalette().setAll((x, y, z) -> {
final int index = x + z * 4 + y * 16;
final Biome biome = convertedPalette[biomeIndices[index]];
return BIOME_MANAGER.getId(biome);
return convertedBiomePalette[biomeIndices[index]];
});
}
}
@ -266,11 +265,13 @@ public class AnvilLoader implements IChunkLoader {
return convertedPalette;
}
private Biome[] loadBiomePalette(@NotNull ListBinaryTag paletteTag) {
Biome[] convertedPalette = new Biome[paletteTag.size()];
private int[] loadBiomePalette(@NotNull ListBinaryTag paletteTag) {
int[] convertedPalette = new int[paletteTag.size()];
for (int i = 0; i < convertedPalette.length; i++) {
final String name = paletteTag.getString(i);
convertedPalette[i] = Objects.requireNonNullElse(BIOME_MANAGER.getByName(name), PLAINS);
int biomeId = BIOME_REGISTRY.getId(NamespaceID.from(name));
if (biomeId == -1) biomeId = PLAINS_ID;
convertedPalette[i] = biomeId;
}
return convertedPalette;
}

View File

@ -0,0 +1,78 @@
package net.minestom.server.instance.block.banner;
import net.minestom.server.registry.DynamicRegistry;
import net.minestom.server.registry.DynamicRegistryImpl;
import net.minestom.server.registry.ProtocolObject;
import net.minestom.server.registry.Registry;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public sealed interface BannerPattern extends ProtocolObject, BannerPatterns permits BannerPatternImpl {
@NotNull BinaryTagSerializer<BannerPattern> NBT_TYPE = BannerPatternImpl.NBT_TYPE;
static @NotNull BannerPattern create(
@NotNull NamespaceID namespace,
@NotNull NamespaceID assetId,
@NotNull String translationKey
) {
return new BannerPatternImpl(namespace, assetId, translationKey, null);
}
static @NotNull Builder builder(@NotNull String namespace) {
return builder(NamespaceID.from(namespace));
}
static @NotNull Builder builder(@NotNull NamespaceID namespace) {
return new Builder(namespace);
}
/**
* <p>Creates a new registry for banner patterns, loading the vanilla banner patterns.</p>
*
* @see net.minestom.server.MinecraftServer to get an existing instance of the registry
*/
@ApiStatus.Internal
static @NotNull DynamicRegistry<BannerPattern> createDefaultRegistry() {
return new DynamicRegistryImpl<>(
"minecraft:banner_pattern", NBT_TYPE, Registry.Resource.BANNER_PATTERNS,
(namespace, props) -> new BannerPatternImpl(Registry.bannerPattern(namespace, props))
);
}
@NotNull NamespaceID assetId();
@NotNull String translationKey();
@Nullable Registry.BannerPatternEntry registry();
final class Builder {
private final NamespaceID namespace;
private NamespaceID assetId;
private String translationKey;
private Builder(@NotNull NamespaceID namespace) {
this.namespace = namespace;
}
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder assetId(@NotNull NamespaceID assetId) {
this.assetId = assetId;
return this;
}
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder translationKey(@NotNull String translationKey) {
this.translationKey = translationKey;
return this;
}
@Contract(pure = true)
public @NotNull BannerPattern build() {
return new BannerPatternImpl(namespace, assetId, translationKey, null);
}
}
}

View File

@ -0,0 +1,39 @@
package net.minestom.server.instance.block.banner;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.registry.Registry;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
record BannerPatternImpl(
@NotNull NamespaceID namespace,
@NotNull NamespaceID assetId,
@NotNull String translationKey,
@Nullable Registry.BannerPatternEntry registry
) implements BannerPattern {
static final BinaryTagSerializer<BannerPattern> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
tag -> {
throw new UnsupportedOperationException("BannerPattern is read-only");
},
bannerPattern -> CompoundBinaryTag.builder()
.putString("asset_id", bannerPattern.assetId().asString())
.putString("translation_key", bannerPattern.translationKey())
.build()
);
@SuppressWarnings("ConstantValue") // The builder can violate the nullability constraints
BannerPatternImpl {
Check.notNull(namespace, "Namespace cannot be null");
Check.argCondition(assetId == null, "missing asset id: {0}", namespace);
Check.argCondition(translationKey == null || translationKey.isEmpty(), "missing translation key: {0}", namespace);
}
BannerPatternImpl(@NotNull Registry.BannerPatternEntry registry) {
this(registry.namespace(), registry.assetId(), registry.translationKey(), registry);
}
}

View File

@ -2,7 +2,7 @@ package net.minestom.server.instance.generator;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.world.biomes.Biome;
import net.minestom.server.world.biome.Biome;
import org.jetbrains.annotations.NotNull;
public interface UnitModifier extends Block.Setter, Biome.Setter {

View File

@ -16,7 +16,7 @@ public abstract class Heightmap {
public Heightmap(Chunk chunk) {
this.chunk = chunk;
minHeight = chunk.getInstance().getDimensionType().getMinY() - 1;
minHeight = chunk.getInstance().getDimensionType().minY() - 1;
}
protected abstract boolean checkBlock(@NotNull Block block);
@ -57,13 +57,13 @@ public abstract class Heightmap {
}
public long[] getNBT() {
final int dimensionHeight = chunk.getInstance().getDimensionType().getHeight();
final int dimensionHeight = chunk.getInstance().getDimensionType().height();
final int bitsForHeight = MathUtils.bitsToRepresent(dimensionHeight);
return encode(heights, bitsForHeight);
}
public void loadFrom(long[] data) {
final int dimensionHeight = chunk.getInstance().getDimensionType().getHeight();
final int dimensionHeight = chunk.getInstance().getDimensionType().height();
final int bitsPerEntry = MathUtils.bitsToRepresent(dimensionHeight);
final int entriesPerLong = 64 / bitsPerEntry;
@ -94,7 +94,7 @@ public abstract class Heightmap {
}
public static int getHighestBlockSection(Chunk chunk) {
int y = chunk.getInstance().getDimensionType().getMaxY();
int y = chunk.getInstance().getDimensionType().maxY();
final int sectionsCount = chunk.getMaxSection() - chunk.getMinSection();
for (int i = 0; i < sectionsCount; i++) {

View File

@ -57,7 +57,7 @@ final class SkyLight implements Light {
if (c instanceof LightingChunk lc) {
int[] heightmap = lc.getOcclusionMap();
int maxY = c.getInstance().getDimensionType().getMinY() + c.getInstance().getDimensionType().getHeight();
int maxY = c.getInstance().getDimensionType().minY() + c.getInstance().getDimensionType().height();
int sectionMaxY = (sectionY + 1) * 16 - 1;
int sectionMinY = sectionY * 16;

View File

@ -1,96 +0,0 @@
package net.minestom.server.item.armor;
import net.kyori.adventure.nbt.BinaryTagTypes;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.ListBinaryTag;
import net.minestom.server.item.Material;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
import java.util.Set;
public class TrimManager {
private final Set<TrimMaterial> trimMaterials;
private final Set<TrimPattern> trimPatterns;
private CompoundBinaryTag trimMaterialCache = null;
private CompoundBinaryTag trimPatternCache = null;
public TrimManager() {
this.trimMaterials = new HashSet<>();
this.trimPatterns = new HashSet<>();
}
public @Nullable TrimMaterial fromIngredient(Material ingredient) {
return this.trimMaterials.stream().filter(trimMaterial -> trimMaterial.ingredient().equals(ingredient)).findFirst().orElse(null);
}
public @Nullable TrimPattern fromTemplate(Material material) {
return this.trimPatterns.stream().filter(trimPattern -> trimPattern.template().equals(material)).findFirst().orElse(null);
}
public CompoundBinaryTag getTrimMaterialNBT() {
if (trimMaterialCache == null) {
ListBinaryTag.Builder<CompoundBinaryTag> entries = ListBinaryTag.builder(BinaryTagTypes.COMPOUND);
for (TrimMaterial trimMaterial : this.trimMaterials)
entries.add(trimMaterial.asNBT());
trimMaterialCache = CompoundBinaryTag.builder()
.putString("type", "minecraft:trim_material")
.put("value", entries.build())
.build();
}
return trimMaterialCache;
}
public CompoundBinaryTag getTrimPatternNBT() {
if (trimPatternCache == null) {
ListBinaryTag.Builder<CompoundBinaryTag> entries = ListBinaryTag.builder(BinaryTagTypes.COMPOUND);
for (TrimPattern trimPattern : this.trimPatterns)
entries.add(trimPattern.asNBT());
trimPatternCache = CompoundBinaryTag.builder()
.putString("type", "minecraft:trim_pattern")
.put("value", entries.build())
.build();
}
return trimPatternCache;
}
public Set<TrimMaterial> getTrimMaterials() {
return Set.copyOf(this.trimMaterials);
}
public Set<TrimPattern> getTrimPatterns() {
return Set.copyOf(trimPatterns);
}
public void addDefaultTrimMaterials() {
this.trimMaterialCache = null;
this.trimMaterials.addAll(TrimMaterial.values());
}
public void addDefaultTrimPatterns() {
this.trimPatternCache = null;
this.trimPatterns.addAll(TrimPattern.values());
}
public boolean addTrimMaterial(TrimMaterial trimMaterial) {
this.trimMaterialCache = null;
return this.trimMaterials.add(trimMaterial);
}
public boolean removeTrimMaterial(TrimMaterial trimMaterial) {
this.trimMaterialCache = null;
return this.trimMaterials.remove(trimMaterial);
}
public boolean addTrimPattern(TrimPattern trimPattern) {
this.trimPatternCache = null;
return this.trimPatterns.add(trimPattern);
}
public boolean removeTrimPattern(TrimPattern trimPattern) {
this.trimPatternCache = null;
return this.trimPatterns.remove(trimPattern);
}
}

View File

@ -1,98 +1,130 @@
package net.minestom.server.item.armor;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.text.Component;
import net.minestom.server.item.Material;
import net.minestom.server.registry.DynamicRegistry;
import net.minestom.server.registry.DynamicRegistryImpl;
import net.minestom.server.registry.ProtocolObject;
import net.minestom.server.registry.Registry;
import net.minestom.server.registry.StaticProtocolObject;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public interface TrimMaterial extends StaticProtocolObject {
static @NotNull TrimMaterial create(@NotNull NamespaceID namespace,
@NotNull String assetName,
@NotNull Material ingredient,
float itemModelIndex,
@NotNull Map<String, String> overrideArmorMaterials,
@NotNull Component description,
Registry.Properties custom) {
public sealed interface TrimMaterial extends ProtocolObject permits TrimMaterialImpl {
@NotNull BinaryTagSerializer<TrimMaterial> NBT_TYPE = TrimMaterialImpl.NBT_TYPE;
static @NotNull TrimMaterial create(
@NotNull NamespaceID namespace,
@NotNull String assetName,
@NotNull Material ingredient,
float itemModelIndex,
@NotNull Map<String, String> overrideArmorMaterials,
@NotNull Component description
) {
return new TrimMaterialImpl(
new Registry.TrimMaterialEntry(
namespace,
assetName,
ingredient,
itemModelIndex,
overrideArmorMaterials,
description,
custom
)
namespace, assetName, ingredient, itemModelIndex,
overrideArmorMaterials, description, null
);
}
static @NotNull TrimMaterial create(@NotNull NamespaceID namespace,
@NotNull String assetName,
@NotNull Material ingredient,
float itemModelIndex,
@NotNull Map<String, String> overrideArmorMaterials,
@NotNull Component description) {
return new TrimMaterialImpl(
new Registry.TrimMaterialEntry(
namespace,
assetName,
ingredient,
itemModelIndex,
overrideArmorMaterials,
description,
null
)
static @NotNull Builder builder(@NotNull String namespace) {
return builder(NamespaceID.from(namespace));
}
static @NotNull Builder builder(@NotNull NamespaceID namespace) {
return new Builder(namespace);
}
/**
* <p>Creates a new registry for trim materials, loading the vanilla trim materials.</p>
*
* @see net.minestom.server.MinecraftServer to get an existing instance of the registry
*/
@ApiStatus.Internal
static @NotNull DynamicRegistry<TrimMaterial> createDefaultRegistry() {
return new DynamicRegistryImpl<>(
"minecraft:trim_material", NBT_TYPE, Registry.Resource.TRIM_MATERIALS,
(namespace, props) -> new TrimMaterialImpl(Registry.trimMaterial(namespace, props))
);
}
static @Nullable TrimMaterial fromId(int id) {
return TrimMaterialImpl.fromId(id);
}
@NotNull String assetName();
static @Nullable TrimMaterial fromNamespaceId(@NotNull String id) {
return TrimMaterialImpl.fromNamespaceId(id);
}
@NotNull Material ingredient();
static Collection<TrimMaterial> values() {
return TrimMaterialImpl.values();
}
float itemModelIndex();
@NotNull Map<String, String> overrideArmorMaterials();
@NotNull Component description();
/**
* Returns the raw registry entry of this trim, only if the trim is a vanilla trim. Otherwise, returns null.
*/
@Contract(pure = true)
@NotNull Registry.TrimMaterialEntry registry();
@Nullable Registry.TrimMaterialEntry registry();
@Override
default @NotNull NamespaceID namespace() {
return registry().namespace();
final class Builder {
private final NamespaceID namespace;
private String assetName;
private Material ingredient;
private float itemModelIndex;
private final Map<String, String> overrideArmorMaterials = new HashMap<>();
private Component description;
Builder(@NotNull NamespaceID namespace) {
this.namespace = namespace;
}
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder assetName(@NotNull String assetName) {
this.assetName = assetName;
return this;
}
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder ingredient(@NotNull Material ingredient) {
this.ingredient = ingredient;
return this;
}
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder itemModelIndex(float itemModelIndex) {
this.itemModelIndex = itemModelIndex;
return this;
}
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder overrideArmorMaterials(@NotNull Map<String, String> overrideArmorMaterials) {
this.overrideArmorMaterials.putAll(overrideArmorMaterials);
return this;
}
@Contract(value = "_, _ -> this", pure = true)
public @NotNull Builder overrideArmorMaterial(@NotNull String slot, @NotNull String material) {
this.overrideArmorMaterials.put(slot, material);
return this;
}
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder description(@NotNull Component description) {
this.description = description;
return this;
}
@Contract(pure = true)
public @NotNull TrimMaterial build() {
return new TrimMaterialImpl(
namespace, assetName, ingredient, itemModelIndex,
overrideArmorMaterials, description, null
);
}
}
default @NotNull String assetName() {
return registry().assetName();
}
default @NotNull Material ingredient() {
return registry().ingredient();
}
default float itemModelIndex() {
return registry().itemModelIndex();
}
default @NotNull Map<String, String> overrideArmorMaterials() {
return registry().overrideArmorMaterials();
}
default @NotNull Component description() {
return registry().description();
}
CompoundBinaryTag asNBT();
}

View File

@ -1,55 +1,61 @@
package net.minestom.server.item.armor;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.StringBinaryTag;
import net.minestom.server.adventure.serializer.nbt.NbtComponentSerializer;
import net.kyori.adventure.text.Component;
import net.minestom.server.item.Material;
import net.minestom.server.registry.Registry;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
record TrimMaterialImpl(Registry.TrimMaterialEntry registry, int id) implements TrimMaterial {
static final AtomicInteger i = new AtomicInteger();
private static final Registry.Container<TrimMaterial> CONTAINER;
record TrimMaterialImpl(
@NotNull NamespaceID namespace,
@NotNull String assetName,
@NotNull Material ingredient,
float itemModelIndex,
@NotNull Map<String, String> overrideArmorMaterials,
@NotNull Component description,
@Nullable Registry.TrimMaterialEntry registry
) implements TrimMaterial {
static {
CONTAINER = Registry.createStaticContainer(Registry.Resource.TRIM_MATERIALS,
(namespace, properties) -> new TrimMaterialImpl(Registry.trimMaterial(namespace, properties)));
static final BinaryTagSerializer<TrimMaterial> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
tag -> {
throw new UnsupportedOperationException("TrimMaterial is read-only");
},
trimMaterial -> {
CompoundBinaryTag.Builder overrideArmorMaterials = CompoundBinaryTag.builder();
for (Map.Entry<String, String> entry : trimMaterial.overrideArmorMaterials().entrySet()) {
overrideArmorMaterials.putString(entry.getKey(), entry.getValue());
}
return CompoundBinaryTag.builder()
.putString("asset_name", trimMaterial.assetName())
.put("ingredient", Material.NBT_TYPE.write(trimMaterial.ingredient()))
.putFloat("item_model_index", trimMaterial.itemModelIndex())
.put("override_armor_materials", overrideArmorMaterials.build())
.put("description", BinaryTagSerializer.NBT_COMPONENT.write(trimMaterial.description()))
.build();
}
);
@SuppressWarnings("ConstantValue") // The builder can violate the nullability constraints
TrimMaterialImpl {
Check.notNull(namespace, "Namespace cannot be null");
Check.argCondition(assetName == null || assetName.isEmpty(), "missing asset name: {0}", namespace);
Check.argCondition(ingredient == null, "missing ingredient: {0}", namespace);
Check.argCondition(overrideArmorMaterials == null, "missing override armor materials: {0}", namespace);
Check.argCondition(description == null, "missing description: {0}", namespace);
overrideArmorMaterials = Map.copyOf(overrideArmorMaterials);
}
public TrimMaterialImpl(Registry.TrimMaterialEntry registry) {
this(registry, i.getAndIncrement());
}
public static TrimMaterial get(String namespace) {
return CONTAINER.get(namespace);
}
static @Nullable TrimMaterial fromId(int id) {
return CONTAINER.getId(id);
}
static @Nullable TrimMaterial fromNamespaceId(@NotNull String id) {
return CONTAINER.get(id);
}
static Collection<TrimMaterial> values() {
return CONTAINER.values();
}
public CompoundBinaryTag asNBT() {
return CompoundBinaryTag.builder()
.putString("asset_name", assetName())
.putString("ingredient", ingredient().namespace().asString())
.putFloat("item_model_index", itemModelIndex())
.put("override_armor_materials", CompoundBinaryTag.from(overrideArmorMaterials().entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, entry -> StringBinaryTag.stringBinaryTag(entry.getValue())))))
.put("description", NbtComponentSerializer.nbt().serialize(description()))
.build();
TrimMaterialImpl(@NotNull Registry.TrimMaterialEntry registry) {
this(registry.namespace(), registry.assetName(), registry.ingredient(),
registry.itemModelIndex(), registry.overrideArmorMaterials(),
registry.description(), registry);
}
}

View File

@ -1,75 +1,106 @@
package net.minestom.server.item.armor;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.text.Component;
import net.minestom.server.item.Material;
import net.minestom.server.registry.DynamicRegistry;
import net.minestom.server.registry.DynamicRegistryImpl;
import net.minestom.server.registry.ProtocolObject;
import net.minestom.server.registry.Registry;
import net.minestom.server.registry.StaticProtocolObject;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
public sealed interface TrimPattern extends ProtocolObject permits TrimPatternImpl {
@NotNull BinaryTagSerializer<TrimPattern> NBT_TYPE = TrimPatternImpl.NBT_TYPE;
public interface TrimPattern extends StaticProtocolObject {
static @NotNull TrimPattern create(@NotNull NamespaceID namespace,
@NotNull NamespaceID assetID,
@NotNull Material template,
@NotNull Component description,
boolean decal,
@NotNull Registry.Properties custom) {
return new TrimPatternImpl(
new Registry.TrimPatternEntry(namespace, assetID, template, description, decal, custom)
static @NotNull TrimPattern create(
@NotNull NamespaceID namespace,
@NotNull NamespaceID assetId,
@NotNull Material template,
@NotNull Component description,
boolean decal
) {
return new TrimPatternImpl(namespace, assetId, template, description, decal, null);
}
static @NotNull Builder builder(@NotNull String namespace) {
return builder(NamespaceID.from(namespace));
}
static @NotNull Builder builder(@NotNull NamespaceID namespace) {
return new Builder(namespace);
}
/**
* <p>Creates a new registry for trim materials, loading the vanilla trim materials.</p>
*
* @see net.minestom.server.MinecraftServer to get an existing instance of the registry
*/
@ApiStatus.Internal
static @NotNull DynamicRegistry<TrimPattern> createDefaultRegistry() {
return new DynamicRegistryImpl<>(
"minecraft:trim_pattern", NBT_TYPE, Registry.Resource.TRIM_PATTERNS,
(namespace, props) -> new TrimPatternImpl(Registry.trimPattern(namespace, props))
);
}
static @NotNull TrimPattern create(@NotNull NamespaceID namespace,
@NotNull NamespaceID assetID,
@NotNull Material template,
@NotNull Component description,
boolean decal) {
return new TrimPatternImpl(
new Registry.TrimPatternEntry(namespace, assetID, template, description, decal, null)
);
}
@NotNull NamespaceID assetId();
static @Nullable TrimPattern fromId(int id) {
return TrimPatternImpl.fromId(id);
}
@NotNull Material template();
static @Nullable TrimPattern fromNamespaceId(@NotNull String id) {
return TrimPatternImpl.fromNamespaceId(id);
}
@NotNull Component description();
static Collection<TrimPattern> values() {
return TrimPatternImpl.values();
}
boolean isDecal();
@Contract(pure = true)
@NotNull Registry.TrimPatternEntry registry();
@Nullable Registry.TrimPatternEntry registry();
@Override
default @NotNull NamespaceID namespace() {
return registry().namespace();
final class Builder {
private final NamespaceID namespace;
private NamespaceID assetId;
private Material template;
private Component description;
private boolean decal;
Builder(@NotNull NamespaceID namespace) {
this.namespace = namespace;
}
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder assetId(@NotNull String assetId) {
return assetId(NamespaceID.from(assetId));
}
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder assetId(@NotNull NamespaceID assetId) {
this.assetId = assetId;
return this;
}
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder template(@NotNull Material template) {
this.template = template;
return this;
}
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder description(@NotNull Component description) {
this.description = description;
return this;
}
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder decal(boolean decal) {
this.decal = decal;
return this;
}
@Contract(pure = true)
public @NotNull TrimPattern build() {
return new TrimPatternImpl(namespace, assetId, template, description, decal, null);
}
}
default @NotNull NamespaceID assetID() {
return registry().assetID();
}
default @NotNull Material template() {
return registry().template();
}
default @NotNull Component description() {
return registry().description();
}
default boolean decal() {
return registry().decal();
}
CompoundBinaryTag asNBT();
}

View File

@ -1,48 +1,45 @@
package net.minestom.server.item.armor;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.adventure.serializer.nbt.NbtComponentSerializer;
import net.kyori.adventure.text.Component;
import net.minestom.server.item.Material;
import net.minestom.server.registry.Registry;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicInteger;
record TrimPatternImpl(
@NotNull NamespaceID namespace,
@NotNull NamespaceID assetId,
@NotNull Material template,
@NotNull Component description,
boolean isDecal,
@Nullable Registry.TrimPatternEntry registry
) implements TrimPattern {
record TrimPatternImpl(Registry.TrimPatternEntry registry, int id) implements TrimPattern {
static final AtomicInteger i = new AtomicInteger();
private static final Registry.Container<TrimPattern> CONTAINER;
static final BinaryTagSerializer<TrimPattern> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
tag -> {
throw new UnsupportedOperationException("TrimMaterial is read-only");
},
trimPattern -> CompoundBinaryTag.builder()
.putString("asset_id", trimPattern.assetId().asString())
.put("template_item", Material.NBT_TYPE.write(trimPattern.template()))
.put("description", BinaryTagSerializer.NBT_COMPONENT.write(trimPattern.description()))
.putBoolean("decal", trimPattern.isDecal())
.build()
);
static {
CONTAINER = Registry.createStaticContainer(Registry.Resource.TRIM_PATTERNS,
(namespace, properties) -> new TrimPatternImpl(Registry.trimPattern(namespace, properties)));
TrimPatternImpl {
Check.notNull(namespace, "Namespace cannot be null");
Check.notNull(assetId, "missing asset id: {0}", namespace);
Check.notNull(template, "missing template: {0}", namespace);
Check.notNull(description, "missing description: {0}", namespace);
}
public TrimPatternImpl(Registry.TrimPatternEntry registry) {
this(registry, i.getAndIncrement());
TrimPatternImpl(@NotNull Registry.TrimPatternEntry registry) {
this(registry.namespace(), registry.assetID(), registry.template(),
registry.description(), registry.decal(), registry);
}
public static TrimPattern fromId(int id) {
return CONTAINER.getId(id);
}
public static TrimPattern fromNamespaceId(String namespace) {
return CONTAINER.getSafe(namespace);
}
public static TrimPattern get(String namespace) {
return CONTAINER.get(namespace);
}
static Collection<TrimPattern> values() {
return CONTAINER.values();
}
public CompoundBinaryTag asNBT() {
return CompoundBinaryTag.builder()
.putString("asset_id", assetID().asString())
.putString("template_item", template().namespace().asString())
.put("description", NbtComponentSerializer.nbt().serialize(description()))
.putBoolean("decal", decal())
.build();
}
}

View File

@ -7,32 +7,32 @@ import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
public record ArmorTrim(@NotNull TrimMaterial material, @NotNull TrimPattern pattern, boolean showInTooltip) {
public static final NetworkBuffer.Type<ArmorTrim> NETWORK_TYPE = new NetworkBuffer.Type<>() {
@Override
public void write(@NotNull NetworkBuffer buffer, ArmorTrim value) {
buffer.write(NetworkBuffer.VAR_INT, value.material.id());
buffer.write(NetworkBuffer.VAR_INT, value.pattern.id());
// buffer.write(NetworkBuffer.VAR_INT, value.material.id());
// buffer.write(NetworkBuffer.VAR_INT, value.pattern.id());
buffer.write(NetworkBuffer.BOOLEAN, value.showInTooltip);
}
@Override
public ArmorTrim read(@NotNull NetworkBuffer buffer) {
TrimMaterial material = Objects.requireNonNull(TrimMaterial.fromId(buffer.read(NetworkBuffer.VAR_INT)), "unknown trim material");
TrimPattern pattern = Objects.requireNonNull(TrimPattern.fromId(buffer.read(NetworkBuffer.VAR_INT)), "unknown trim pattern");
return new ArmorTrim(material, pattern, buffer.read(NetworkBuffer.BOOLEAN));
// TrimMaterial material = Objects.requireNonNull(TrimMaterial.fromId(buffer.read(NetworkBuffer.VAR_INT)), "unknown trim material");
// TrimPattern pattern = Objects.requireNonNull(TrimPattern.fromId(buffer.read(NetworkBuffer.VAR_INT)), "unknown trim pattern");
// return new ArmorTrim(material, pattern, buffer.read(NetworkBuffer.BOOLEAN));
throw new UnsupportedOperationException("Not implemented");
}
};
public static final BinaryTagSerializer<ArmorTrim> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
tag -> {
TrimMaterial material = Objects.requireNonNull(TrimMaterial.fromNamespaceId(tag.getString("material")), "unknown trim material");
TrimPattern pattern = Objects.requireNonNull(TrimPattern.fromNamespaceId(tag.getString("pattern")), "unknown trim pattern");
boolean showInTooltip = tag.getBoolean("show_in_tooltip", true);
return new ArmorTrim(material, pattern, showInTooltip);
// TrimMaterial material = Objects.requireNonNull(TrimMaterial.fromNamespaceId(tag.getString("material")), "unknown trim material");
// TrimPattern pattern = Objects.requireNonNull(TrimPattern.fromNamespaceId(tag.getString("pattern")), "unknown trim pattern");
// boolean showInTooltip = tag.getBoolean("show_in_tooltip", true);
// return new ArmorTrim(material, pattern, showInTooltip);
throw new UnsupportedOperationException("Not implemented");
},
value -> CompoundBinaryTag.builder()
.putString("material", value.material.name())

View File

@ -116,8 +116,8 @@ public class BlockPlacementListener {
}
}
if (placementPosition.y() >= instance.getDimensionType().getMaxY()
|| placementPosition.y() < instance.getDimensionType().getMinY()) return;
if (placementPosition.y() >= instance.getDimensionType().maxY()
|| placementPosition.y() < instance.getDimensionType().minY()) return;
// Ensure that the final placement position is inside the world border.
if (!instance.getWorldBorder().isInside(placementPosition)) {

View File

@ -13,6 +13,7 @@ import net.minestom.server.network.ConnectionState;
import net.minestom.server.network.packet.client.ClientPacket;
import net.minestom.server.network.packet.client.common.*;
import net.minestom.server.network.packet.client.configuration.ClientFinishConfigurationPacket;
import net.minestom.server.network.packet.client.configuration.ClientSelectKnownPacksPacket;
import net.minestom.server.network.packet.client.handshake.ClientHandshakePacket;
import net.minestom.server.network.packet.client.login.ClientEncryptionResponsePacket;
import net.minestom.server.network.packet.client.login.ClientLoginAcknowledgedPacket;
@ -55,6 +56,7 @@ public final class PacketListenerManager {
setConfigurationListener(ClientKeepAlivePacket.class, KeepAliveListener::listener);
setConfigurationListener(ClientPongPacket.class, (packet, player) -> {/* empty */});
setConfigurationListener(ClientResourcePackStatusPacket.class, ResourcePackListener::listener);
setConfigurationListener(ClientSelectKnownPacksPacket.class, ConfigListener::selectKnownPacks);
setConfigurationListener(ClientFinishConfigurationPacket.class, ConfigListener::finishConfigListener);
setListener(ConnectionState.CONFIGURATION, ClientCookieResponsePacket.class, CookieListener::handleCookieResponse);

View File

@ -4,11 +4,16 @@ import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.Player;
import net.minestom.server.network.ConnectionManager;
import net.minestom.server.network.packet.client.configuration.ClientFinishConfigurationPacket;
import net.minestom.server.network.packet.client.configuration.ClientSelectKnownPacksPacket;
import org.jetbrains.annotations.NotNull;
public final class ConfigListener {
private static final ConnectionManager CONNECTION_MANAGER = MinecraftServer.getConnectionManager();
public static void selectKnownPacks(@NotNull ClientSelectKnownPacksPacket packet, @NotNull Player player) {
System.out.println("SelectKnownPacksPacket: " + packet);
}
public static void finishConfigListener(@NotNull ClientFinishConfigurationPacket packet, @NotNull Player player) {
CONNECTION_MANAGER.transitionConfigToPlay(player);
}

View File

@ -0,0 +1,75 @@
package net.minestom.server.message;
import net.minestom.server.registry.DynamicRegistry;
import net.minestom.server.registry.DynamicRegistryImpl;
import net.minestom.server.registry.ProtocolObject;
import net.minestom.server.registry.Registry;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public sealed interface ChatType extends ProtocolObject, ChatTypes permits ChatTypeImpl {
@NotNull BinaryTagSerializer<ChatType> NBT_TYPE = ChatTypeImpl.NBT_TYPE;
static @NotNull ChatType create(
@NotNull NamespaceID namespace,
@NotNull ChatTypeDecoration chat,
@NotNull ChatTypeDecoration narration
) {
return new ChatTypeImpl(namespace, chat, narration, null);
}
static @NotNull Builder builder(@NotNull String namespace) {
return builder(NamespaceID.from(namespace));
}
static @NotNull Builder builder(@NotNull NamespaceID namespace) {
return new Builder(namespace);
}
/**
* <p>Creates a new registry for chat types, loading the vanilla chat types.</p>
*
* @see net.minestom.server.MinecraftServer to get an existing instance of the registry
*/
@ApiStatus.Internal
static @NotNull DynamicRegistry<ChatType> createDefaultRegistry() {
return new DynamicRegistryImpl<>(
"minecraft:chat_type", NBT_TYPE, Registry.Resource.CHAT_TYPES,
(namespace, props) -> new ChatTypeImpl(Registry.chatType(namespace, props))
);
}
@NotNull ChatTypeDecoration chat();
@NotNull ChatTypeDecoration narration();
@Override
@Nullable Registry.ChatTypeEntry registry();
final class Builder {
private final NamespaceID namespace;
private ChatTypeDecoration chat;
private ChatTypeDecoration narration;
public Builder(@NotNull NamespaceID namespace) {
this.namespace = namespace;
}
public Builder chat(@NotNull ChatTypeDecoration chat) {
this.chat = chat;
return this;
}
public Builder narration(@NotNull ChatTypeDecoration narration) {
this.narration = narration;
return this;
}
public @NotNull ChatType build() {
return new ChatTypeImpl(namespace, chat, narration, null);
}
}
}

View File

@ -0,0 +1,44 @@
package net.minestom.server.message;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.text.format.Style;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public record ChatTypeDecoration(
@NotNull String translationKey,
@NotNull List<Parameter> parameters,
@NotNull Style style
) {
public static final BinaryTagSerializer<ChatTypeDecoration> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
tag -> {
String translationKey = tag.getString("translation_key");
List<Parameter> parameters = Parameter.LIST_NBT_TYPE.read(tag.getList("parameters"));
Style style = Style.empty();
if (tag.get("style") instanceof CompoundBinaryTag styleTag)
style = BinaryTagSerializer.NBT_COMPONENT_STYLE.read(styleTag);
return new ChatTypeDecoration(translationKey, parameters, style);
},
deco -> {
CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder();
builder.putString("translation_key", deco.translationKey());
builder.put("parameters", Parameter.LIST_NBT_TYPE.write(deco.parameters()));
if (!deco.style.isEmpty())
builder.put("style", BinaryTagSerializer.NBT_COMPONENT_STYLE.write(deco.style()));
return builder.build();
}
);
public enum Parameter {
SENDER,
TARGET,
CONTENT;
public static final BinaryTagSerializer<Parameter> NBT_TYPE = BinaryTagSerializer.fromEnumStringable(Parameter.class);
private static final BinaryTagSerializer<List<Parameter>> LIST_NBT_TYPE = NBT_TYPE.list();
}
}

View File

@ -0,0 +1,38 @@
package net.minestom.server.message;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.registry.Registry;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
record ChatTypeImpl(
@NotNull NamespaceID namespace,
@NotNull ChatTypeDecoration chat,
@NotNull ChatTypeDecoration narration,
@Nullable Registry.ChatTypeEntry registry
) implements ChatType {
static final BinaryTagSerializer<ChatType> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
tag -> {
throw new UnsupportedOperationException("ChatType is read-only");
},
chatType -> CompoundBinaryTag.builder()
.put("chat", ChatTypeDecoration.NBT_TYPE.write(chatType.chat()))
.put("narration", ChatTypeDecoration.NBT_TYPE.write(chatType.narration()))
.build()
);
ChatTypeImpl {
Check.notNull(namespace, "Namespace cannot be null");
Check.notNull(chat, "missing chat: {0}", namespace);
Check.notNull(narration, "missing narration: {0}", namespace);
}
ChatTypeImpl(@NotNull Registry.ChatTypeEntry registry) {
this(registry.namespace(), registry.chat(), registry.narration(), registry);
}
}

View File

@ -1,19 +1,14 @@
package net.minestom.server.message;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.TagStringIO;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.entity.Player;
import net.minestom.server.network.packet.server.configuration.RegistryDataPacket;
import net.minestom.server.network.packet.server.play.SystemChatPacket;
import net.minestom.server.utils.PacketUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
@ -28,38 +23,6 @@ public final class Messenger {
private static final UUID NO_SENDER = new UUID(0, 0);
private static final SystemChatPacket CANNOT_SEND_PACKET = new SystemChatPacket(CANNOT_SEND_MESSAGE, false);
private static final RegistryDataPacket REGISTRY_DATA_PACKET;
static {
try {
REGISTRY_DATA_PACKET = new RegistryDataPacket(
"minecraft:chat_type", List.of(
new RegistryDataPacket.Entry(
"minecraft:chat",
TagStringIO.get().asCompound(
"""
{
"chat":{
"translation_key":"chat.type.text",
"parameters":["sender", "content"]
},
"narration":{
"translation_key":"chat.type.text.narrate",
"parameters":["sender", "content"]
}
}"""
)
)
));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static @NotNull RegistryDataPacket registryDataPacket() {
return REGISTRY_DATA_PACKET;
}
/**
* Sends a message to a player, respecting their chat settings.
*

View File

@ -5,13 +5,11 @@ import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.MinecraftServer;
import net.minestom.server.ServerFlag;
import net.minestom.server.entity.Player;
import net.minestom.server.entity.damage.DamageType;
import net.minestom.server.event.EventDispatcher;
import net.minestom.server.event.player.AsyncPlayerConfigurationEvent;
import net.minestom.server.event.player.AsyncPlayerPreLoginEvent;
import net.minestom.server.instance.Instance;
import net.minestom.server.listener.preplay.LoginListener;
import net.minestom.server.message.Messenger;
import net.minestom.server.network.packet.client.login.ClientLoginStartPacket;
import net.minestom.server.network.packet.server.CachedPacket;
import net.minestom.server.network.packet.server.common.KeepAlivePacket;
@ -19,6 +17,7 @@ import net.minestom.server.network.packet.server.common.PluginMessagePacket;
import net.minestom.server.network.packet.server.common.TagsPacket;
import net.minestom.server.network.packet.server.configuration.FinishConfigurationPacket;
import net.minestom.server.network.packet.server.configuration.ResetChatPacket;
import net.minestom.server.network.packet.server.configuration.SelectKnownPacksPacket;
import net.minestom.server.network.packet.server.login.LoginSuccessPacket;
import net.minestom.server.network.packet.server.play.StartConfigurationPacket;
import net.minestom.server.network.player.PlayerConnection;
@ -287,14 +286,18 @@ public final class ConnectionManager {
// Registry data (if it should be sent)
if (event.willSendRegistryData()) {
// minecraft:trim_pattern, minecraft:trim_material, minecraft:wolf_variant, and minecraft:banner_pattern.
player.sendPacket(new SelectKnownPacksPacket(List.of(SelectKnownPacksPacket.MINECRAFT_CORE)));
player.sendPacket(Messenger.registryDataPacket());
player.sendPacket(MinecraftServer.getDimensionTypeManager().registryDataPacket());
player.sendPacket(MinecraftServer.getBiomeManager().registryDataPacket());
player.sendPacket(DamageType.registryDataPacket());
// registry.put("minecraft:trim_material", MinecraftServer.getTrimManager().getTrimMaterialNBT());
// registry.put("minecraft:trim_pattern", MinecraftServer.getTrimManager().getTrimPatternNBT());
boolean excludeVanilla = false;
var serverProcess = MinecraftServer.process();
player.sendPacket(serverProcess.chatType().registryDataPacket(excludeVanilla));
player.sendPacket(serverProcess.dimensionType().registryDataPacket(excludeVanilla));
player.sendPacket(serverProcess.biome().registryDataPacket(excludeVanilla));
player.sendPacket(serverProcess.damageType().registryDataPacket(excludeVanilla));
player.sendPacket(serverProcess.trimMaterial().registryDataPacket(excludeVanilla));
player.sendPacket(serverProcess.trimPattern().registryDataPacket(excludeVanilla));
player.sendPacket(serverProcess.bannerPattern().registryDataPacket(excludeVanilla));
player.sendPacket(serverProcess.wolfVariant().registryDataPacket(excludeVanilla));
player.sendPacket(TagsPacket.DEFAULT_TAGS);
}

View File

@ -1,5 +1,6 @@
package net.minestom.server.network.packet.server.configuration;
import net.minestom.server.MinecraftServer;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
@ -13,6 +14,8 @@ public record SelectKnownPacksPacket(
) implements ServerPacket.Configuration {
private static final int MAX_ENTRIES = 64;
public static final Entry MINECRAFT_CORE = new Entry("minecraft", "core", MinecraftServer.VERSION_NAME);
public SelectKnownPacksPacket {
Check.argCondition(entries.size() > MAX_ENTRIES, "Too many known packs: {0} > {1}", entries.size(), MAX_ENTRIES);
}

View File

@ -0,0 +1,69 @@
package net.minestom.server.registry;
import net.minestom.server.entity.Player;
import net.minestom.server.network.packet.server.SendablePacket;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public interface DynamicRegistry<T extends ProtocolObject> {
@Nullable T get(int id);
@Nullable T get(@NotNull NamespaceID namespace);
int getId(@NotNull NamespaceID id);
/**
* <p>Returns the entries in this registry as an immutable list. The indices in the returned list correspond
* to the protocol ID of each entry.</p>
*
* <p>Note: The returned list is not guaranteed to update with the registry,
* it should be fetched again for updated values.</p>
*
* @return An immutable list of the entries in this registry.
*/
@NotNull List<T> values();
/**
* <p>Register an object to this registry, overwriting the previous entry if any is present.</p>
*
* <p>Note: the new registry will not be sent to existing players. They must be returned to
* the configuration phase to receive new registry data. See {@link Player#startConfigurationPhase()}.</p>
*
* @param object The entry to register
* @return The new ID of the registered object
*/
int register(@NotNull T object);
/**
* <p>Removes an object from this registry.</p>
*
* <p><b>WARNING:</b> This operation will cause all subsequent IDs to be remapped, meaning that any loaded entry
* with existing IDs may be incorrect. For example, loading a world with 0=plains, 1=desert, 2=badlands would store
* those IDs in the palette. If you then deleted entry 1 (desert), any desert biomes in the loaded world would
* become badlands, and any badlands would become invalid. <b>This behavior is disabled by default, and must be
* enabled by setting the system property <code>minestom.registry.unsafe-remove</code> to <code>true</code>.</b></p>
*
* <p>Note: the new registry will not be sent to existing players. They must be returned to
* the configuration phase to receive new registry data. See {@link Player#startConfigurationPhase()}.</p>
*
* @param object The entry to remove
* @return True if the object was removed, false if it was not present
* @throws UnsupportedOperationException If the system property <code>minestom.registry.unsafe-remove</code> is not set to <code>true</code>
*/
boolean remove(@NotNull T object) throws UnsupportedOperationException;
/**
* <p>Returns a {@link SendablePacket} potentially excluding vanilla entries if possible. It is never possible to
* exclude vanilla entries if one has been overridden (e.g. via {@link #register(ProtocolObject)}.</p>
*
* @param excludeVanilla Whether to exclude vanilla entries
* @return A {@link SendablePacket} containing the registry data
*/
@ApiStatus.Internal
@NotNull SendablePacket registryDataPacket(boolean excludeVanilla);
}

View File

@ -0,0 +1,150 @@
package net.minestom.server.registry;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.ServerFlag;
import net.minestom.server.network.packet.server.CachedPacket;
import net.minestom.server.network.packet.server.SendablePacket;
import net.minestom.server.network.packet.server.configuration.RegistryDataPacket;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantLock;
@ApiStatus.Internal
public class DynamicRegistryImpl<T extends ProtocolObject> implements DynamicRegistry<T> {
private static final UnsupportedOperationException UNSAFE_REMOVE_EXCEPTION = new UnsupportedOperationException("Unsafe remove is disabled. Enable by setting the system property 'minestom.registry.unsafe-remove' to 'true'");
private final CachedPacket vanillaRegistryDataPacket = new CachedPacket(() -> createRegistryDataPacket(true));
private final ReentrantLock lock = new ReentrantLock(); // Protects entryById (except when used alone, it is safe), entryByName, and idByName
private final List<T> entryById = new CopyOnWriteArrayList<>(); // We use a CopyOnWriteArrayList even with the lock above because it handles concurrent iteration
private final Map<NamespaceID, T> entryByName = new HashMap<>();
private final List<NamespaceID> idByName = new ArrayList<>();
private final String id;
private final BinaryTagSerializer<T> nbtType;
public DynamicRegistryImpl(@NotNull String id, BinaryTagSerializer<T> nbtType) {
this.id = id;
this.nbtType = nbtType;
}
public DynamicRegistryImpl(@NotNull String id, BinaryTagSerializer<T> nbtType, @NotNull Registry.Resource resource, @NotNull Registry.Container.Loader<T> loader) {
this(id, nbtType);
loadStaticRegistry(resource, loader);
}
@Override
public @Nullable T get(int id) {
if (id < 0 || id >= entryById.size()) {
return null;
}
return entryById.get(id);
}
@Override
public @Nullable T get(@NotNull NamespaceID namespace) {
lock.lock();
try {
return entryByName.get(namespace);
} finally {
lock.unlock();
}
}
@Override
public int getId(@NotNull NamespaceID id) {
lock.lock();
try {
return idByName.indexOf(id);
} finally {
lock.unlock();
}
}
@Override
public @NotNull List<T> values() {
return Collections.unmodifiableList(entryById);
}
@Override
public int register(@NotNull T object) {
lock.lock();
try {
int id = idByName.indexOf(object.namespace());
if (id == -1) id = entryById.size();
entryById.add(id, object);
entryByName.put(object.namespace(), object);
idByName.add(object.namespace());
vanillaRegistryDataPacket.invalidate();
return id;
} finally {
lock.unlock();
}
}
@Override
public boolean remove(@NotNull T object) throws UnsupportedOperationException {
if (!ServerFlag.REGISTRY_UNSAFE_REMOVE) throw UNSAFE_REMOVE_EXCEPTION;
lock.lock();
try {
int id = idByName.indexOf(object.namespace());
if (id == -1) return false;
entryById.remove(id);
entryByName.remove(object.namespace());
idByName.remove(id);
vanillaRegistryDataPacket.invalidate();
return true;
} finally {
lock.unlock();
}
}
@Override
public @NotNull SendablePacket registryDataPacket(boolean excludeVanilla) {
// We cache the vanilla packet because that is by far the most common case. If some client claims not to have
// the vanilla datapack we can compute the entire thing.
return excludeVanilla ? vanillaRegistryDataPacket : createRegistryDataPacket(false);
}
private @NotNull RegistryDataPacket createRegistryDataPacket(boolean excludeVanilla) {
lock.lock();
try {
var entries = new ArrayList<RegistryDataPacket.Entry>(entryById.size());
for (var entry : entryById) {
CompoundBinaryTag data = null;
// sorta todo, sorta just a note:
// Right now we very much only support the minecraft:core (vanilla) 'pack'. Any entry which was not loaded
// from static data will be treated as non vanilla and always sent completely. However, we really should
// support arbitrary packs and associate all registry data with a datapack. Additionally, we should generate
// all data for the experimental datapacks built in to vanilla such as the next update experimental (1.21 at
// the time of writing). Datagen currently behaves kind of badly in that the registry inspecting generators
// like material, block, etc generate entries which are behind feature flags, whereas the ones which inspect
// static assets (the traditionally dynamic registries), do not generate those assets.
if (!excludeVanilla || entry.registry() == null) {
data = (CompoundBinaryTag) nbtType.write(entry);
}
entries.add(new RegistryDataPacket.Entry(entry.name(), data));
}
return new RegistryDataPacket(id, entries);
} finally {
lock.unlock();
}
}
private void loadStaticRegistry(@NotNull Registry.Resource resource, @NotNull Registry.Container.Loader<T> loader) {
for (var entry : Registry.load(resource).entrySet()) {
final String namespace = entry.getKey();
final Registry.Properties properties = Registry.Properties.fromMap(entry.getValue());
register(loader.get(namespace, properties));
}
}
}

View File

@ -5,6 +5,7 @@ import net.kyori.adventure.key.Keyed;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public interface ProtocolObject extends Keyed {
@ -21,4 +22,8 @@ public interface ProtocolObject extends Keyed {
default @NotNull Key key() {
return namespace();
}
default @Nullable Object registry() {
return null;
}
}

View File

@ -0,0 +1,34 @@
package net.minestom.server.registry;
import net.minestom.server.entity.damage.DamageType;
import net.minestom.server.entity.metadata.animal.tameable.WolfMeta;
import net.minestom.server.instance.block.banner.BannerPattern;
import net.minestom.server.item.armor.TrimMaterial;
import net.minestom.server.item.armor.TrimPattern;
import net.minestom.server.message.ChatType;
import net.minestom.server.world.DimensionType;
import net.minestom.server.world.biome.Biome;
import org.jetbrains.annotations.NotNull;
/**
* Provides access to all the dynamic registries.
*/
public interface Registries {
@NotNull DynamicRegistry<ChatType> chatType();
@NotNull DynamicRegistry<DimensionType> dimensionType();
@NotNull DynamicRegistry<Biome> biome();
@NotNull DynamicRegistry<DamageType> damageType();
@NotNull DynamicRegistry<TrimMaterial> trimMaterial();
@NotNull DynamicRegistry<TrimPattern> trimPattern();
@NotNull DynamicRegistry<BannerPattern> bannerPattern();
@NotNull DynamicRegistry<WolfMeta.Variant> wolfVariant();
}

View File

@ -1,10 +1,14 @@
package net.minestom.server.registry;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.ToNumberPolicy;
import com.google.gson.stream.JsonReader;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.nbt.TagStringIOExt;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.json.JSONComponentSerializer;
import net.minestom.server.MinecraftServer;
import net.minestom.server.collision.BoundingBox;
@ -17,6 +21,7 @@ import net.minestom.server.entity.EntityType;
import net.minestom.server.entity.EquipmentSlot;
import net.minestom.server.instance.block.Block;
import net.minestom.server.item.Material;
import net.minestom.server.message.ChatTypeDecoration;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.collection.ObjectArray;
import net.minestom.server.utils.validate.Check;
@ -42,6 +47,11 @@ public final class Registry {
return new BlockEntry(namespace, main, null);
}
@ApiStatus.Internal
public static DimensionTypeEntry dimensionType(String namespace, @NotNull Properties main) {
return new DimensionTypeEntry(namespace, main, null);
}
@ApiStatus.Internal
public static BiomeEntry biome(String namespace, Properties properties) {
return new BiomeEntry(namespace, properties, null);
@ -87,6 +97,21 @@ public final class Registry {
return new AttributeEntry(namespace, main, null);
}
@ApiStatus.Internal
public static BannerPatternEntry bannerPattern(String namespace, @NotNull Properties main) {
return new BannerPatternEntry(namespace, main, null);
}
@ApiStatus.Internal
public static WolfVariantEntry wolfVariant(String namespace, @NotNull Properties main) {
return new WolfVariantEntry(namespace, main, null);
}
@ApiStatus.Internal
public static ChatTypeEntry chatType(String namespace, @NotNull Properties main) {
return new ChatTypeEntry(namespace, main, null);
}
@ApiStatus.Internal
public static Map<String, Map<String, Object>> load(Resource resource) {
Map<String, Map<String, Object>> map = new HashMap<>();
@ -232,8 +257,12 @@ public final class Registry {
FLUID_TAGS("tags/fluid_tags.json"),
GAMEPLAY_TAGS("tags/gameplay_tags.json"),
ITEM_TAGS("tags/item_tags.json"),
DIMENSION_TYPES("dimension_types.json"),
BIOMES("biomes.json"),
ATTRIBUTES("attributes.json");
ATTRIBUTES("attributes.json"),
BANNER_PATTERNS("banner_patterns.json"),
WOLF_VARIANTS("wolf_variants.json"),
CHAT_TYPES("chat_types.json");
private final String name;
@ -400,6 +429,49 @@ public final class Registry {
}
}
public record DimensionTypeEntry(
NamespaceID namespace,
boolean ultrawarm,
boolean natural,
double coordinateScale,
boolean hasSkylight,
boolean hasCeiling,
float ambientLight,
Long fixedTime,
boolean piglinSafe,
boolean bedWorks,
boolean respawnAnchorWorks,
boolean hasRaids,
int logicalHeight,
int minY,
int height,
String infiniburn,
String effects,
Properties custom
) implements Entry {
public DimensionTypeEntry(String namespace, Properties main, Properties custom) {
this(NamespaceID.from(namespace),
main.getBoolean("ultrawarm"),
main.getBoolean("natural"),
main.getDouble("coordinate_scale"),
main.getBoolean("has_skylight"),
main.getBoolean("has_ceiling"),
main.getFloat("ambient_light"),
(Long) main.asMap().get("fixed_time"),
main.getBoolean("piglin_safe"),
main.getBoolean("bed_works"),
main.getBoolean("respawn_anchor_works"),
main.getBoolean("has_raids"),
main.getInt("logical_height"),
main.getInt("min_y"),
main.getInt("height"),
main.getString("infiniburn"),
main.getString("effects"),
custom);
}
}
public static final class BiomeEntry implements Entry {
private final Properties custom;
private final NamespaceID namespace;
@ -772,6 +844,89 @@ public final class Registry {
}
}
public record BannerPatternEntry(NamespaceID namespace, NamespaceID assetId, String translationKey, Properties custom) implements Entry {
public BannerPatternEntry(String namespace, Properties main, Properties custom) {
this(NamespaceID.from(namespace),
NamespaceID.from(main.getString("asset_id")),
main.getString("translation_key"),
custom);
}
}
public record WolfVariantEntry(NamespaceID namespace, NamespaceID wildTexture, NamespaceID tameTexture, NamespaceID angryTexture, List<String> biomes, Properties custom) implements Entry {
public WolfVariantEntry(String namespace, Properties main, Properties custom) {
this(NamespaceID.from(namespace),
NamespaceID.from(main.getString("wild_texture")),
NamespaceID.from(main.getString("tame_texture")),
NamespaceID.from(main.getString("angry_texture")),
readBiomesList(main.asMap().get("biomes")),
custom);
}
private static @NotNull List<String> readBiomesList(Object biomes) {
if (biomes instanceof List<?> list) {
return list.stream().map(Object::toString).collect(Collectors.toList());
} else if (biomes instanceof String single) {
return List.of(single);
} else {
throw new IllegalArgumentException("invalid biomes entry: " + biomes);
}
}
}
public static final class ChatTypeEntry implements Entry {
private final NamespaceID namespace;
private final ChatTypeDecoration chat;
private final ChatTypeDecoration narration;
private final Properties custom;
public ChatTypeEntry(String namespace, Properties main, Properties custom) {
this.namespace = NamespaceID.from(namespace);
this.chat = readChatTypeDecoration(main.section("chat"));
this.narration = readChatTypeDecoration(main.section("narration"));
this.custom = custom;
}
public NamespaceID namespace() {
return namespace;
}
public ChatTypeDecoration chat() {
return chat;
}
public ChatTypeDecoration narration() {
return narration;
}
@Override
public Properties custom() {
return custom;
}
private static @NotNull ChatTypeDecoration readChatTypeDecoration(Properties properties) {
List<ChatTypeDecoration.Parameter> parameters = new ArrayList<>();
if (properties.asMap().get("parameters") instanceof List<?> paramList) {
for (Object param : paramList) {
parameters.add(ChatTypeDecoration.Parameter.valueOf(param.toString().toUpperCase(Locale.ROOT)));
}
}
Style style = Style.empty();
if (properties.containsKey("style")) {
// This is admittedly a pretty cursed way to handle deserialization here, however I do not want to
// write a standalone JSON style deserializer for this one use case.
// The methodology is to just convert it to a valid text component and deserialize that, then take the style.
Gson gson = GsonComponentSerializer.gson().serializer();
JsonObject textComponentJson = gson.toJsonTree(properties.section("style").asMap()).getAsJsonObject();
textComponentJson.addProperty("text", "IGNORED_VALUE");
Component textComponent = GsonComponentSerializer.gson().deserializeFromTree(textComponentJson);
style = textComponent.style();
}
return new ChatTypeDecoration(properties.getString("translation_key"), parameters, style);
}
}
public interface Entry {
@ApiStatus.Experimental
Properties custom();
@ -834,6 +989,17 @@ public final class Registry {
return ((Number) element(name)).intValue();
}
@Override
public float getFloat(String name, float defaultValue) {
var element = element(name);
return element != null ? ((Number) element).floatValue() : defaultValue;
}
@Override
public float getFloat(String name) {
return ((Number) element(name)).floatValue();
}
@Override
public boolean getBoolean(String name, boolean defaultValue) {
var element = element(name);
@ -893,6 +1059,10 @@ public final class Registry {
int getInt(String name);
float getFloat(String name, float defaultValue);
float getFloat(String name);
boolean getBoolean(String name, boolean defaultValue);
boolean getBoolean(String name);

View File

@ -2,7 +2,7 @@ package net.minestom.server.snapshot;
import net.minestom.server.instance.block.Block;
import net.minestom.server.tag.TagReadable;
import net.minestom.server.world.biomes.Biome;
import net.minestom.server.world.biome.Biome;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;

View File

@ -4,7 +4,7 @@ import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.tag.TagReadable;
import net.minestom.server.world.DimensionType;
import net.minestom.server.world.biomes.Biome;
import net.minestom.server.world.biome.Biome;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;

View File

@ -13,7 +13,7 @@ import net.minestom.server.tag.TagReadable;
import net.minestom.server.utils.collection.IntMappedArray;
import net.minestom.server.utils.collection.MappedCollection;
import net.minestom.server.world.DimensionType;
import net.minestom.server.world.biomes.Biome;
import net.minestom.server.world.biome.Biome;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -103,7 +103,7 @@ public final class SnapshotImpl {
final Section section = sections[getChunkCoordinate(y) - minSection];
final int id = section.biomePalette()
.get(toSectionRelativeCoordinate(x) / 4, toSectionRelativeCoordinate(y) / 4, toSectionRelativeCoordinate(z) / 4);
return MinecraftServer.getBiomeManager().getById(id);
return MinecraftServer.getBiomeRegistry().get(id);
}
@Override

View File

@ -2,7 +2,9 @@ package net.minestom.server.utils.nbt;
import net.kyori.adventure.nbt.*;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.minestom.server.adventure.serializer.nbt.NbtComponentSerializer;
import net.minestom.server.item.ItemStack;
import net.minestom.server.utils.UniqueIdUtils;
import net.minestom.server.utils.Unit;
@ -176,6 +178,28 @@ public interface BinaryTagSerializer<T> {
s -> GsonComponentSerializer.gson().deserialize(s),
c -> GsonComponentSerializer.gson().serialize(c)
);
BinaryTagSerializer<Component> NBT_COMPONENT = new BinaryTagSerializer<>() {
@Override
public @NotNull BinaryTag write(@NotNull Component value) {
return NbtComponentSerializer.nbt().serialize(value);
}
@Override
public @NotNull Component read(@NotNull BinaryTag tag) {
return NbtComponentSerializer.nbt().deserialize(tag);
}
};
BinaryTagSerializer<Style> NBT_COMPONENT_STYLE = new BinaryTagSerializer<>() {
@Override
public @NotNull BinaryTag write(@NotNull Style value) {
return NbtComponentSerializer.nbt().serializeStyle(value);
}
@Override
public @NotNull Style read(@NotNull BinaryTag tag) {
return NbtComponentSerializer.nbt().deserializeStyle(tag);
}
};
BinaryTagSerializer<ItemStack> ITEM = COMPOUND.map(ItemStack::fromItemNBT, ItemStack::toItemNBT);
BinaryTagSerializer<UUID> UUID = new BinaryTagSerializer<>() {

View File

@ -1,361 +1,213 @@
package net.minestom.server.world;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.network.packet.server.configuration.RegistryDataPacket;
import net.minestom.server.registry.DynamicRegistry;
import net.minestom.server.registry.DynamicRegistryImpl;
import net.minestom.server.registry.ProtocolObject;
import net.minestom.server.registry.Registry;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
/**
* https://minecraft.wiki/w/Custom_dimension
*/
public class DimensionType {
public sealed interface DimensionType extends ProtocolObject, DimensionTypes permits DimensionTypeImpl {
@NotNull BinaryTagSerializer<DimensionType> NBT_TYPE = DimensionTypeImpl.NBT_TYPE;
private static final AtomicInteger idCounter = new AtomicInteger(0);
int VANILLA_MIN_Y = -64;
int VANILLA_MAX_Y = 319;
private static final int VANILLA_MIN_Y = -64;
private static final int VANILLA_MAX_Y = 319;
public static final DimensionType OVERWORLD = DimensionType.builder(NamespaceID.from("minecraft:overworld"))
.ultrawarm(false)
.natural(true)
.piglinSafe(false)
.respawnAnchorSafe(false)
.bedSafe(true)
.raidCapable(true)
.skylightEnabled(true)
.ceilingEnabled(false)
.fixedTime(null)
.ambientLight(0.0f)
.height(384)
.minY(-64)
.logicalHeight(384)
.infiniburn(NamespaceID.from("minecraft:infiniburn_overworld"))
.build();
private final int id = idCounter.getAndIncrement();
protected volatile boolean registered;
private final NamespaceID name;
private final boolean natural;
private final float ambientLight;
private final boolean ceilingEnabled;
private final boolean skylightEnabled;
@Nullable
private final Long fixedTime;
private final boolean raidCapable;
private final boolean respawnAnchorSafe;
private final boolean ultrawarm;
private final boolean bedSafe;
private final String effects;
private final boolean piglinSafe;
private final int minY;
private final int height;
private final int logicalHeight;
private final double coordinateScale;
private final NamespaceID infiniburn;
DimensionType(NamespaceID name, boolean natural, float ambientLight, boolean ceilingEnabled,
boolean skylightEnabled, @Nullable Long fixedTime, boolean raidCapable,
boolean respawnAnchorSafe, boolean ultrawarm, boolean bedSafe, String effects, boolean piglinSafe,
int minY, int height, int logicalHeight, double coordinateScale, NamespaceID infiniburn) {
this.name = name;
this.natural = natural;
this.ambientLight = ambientLight;
this.ceilingEnabled = ceilingEnabled;
this.skylightEnabled = skylightEnabled;
this.fixedTime = fixedTime;
this.raidCapable = raidCapable;
this.respawnAnchorSafe = respawnAnchorSafe;
this.ultrawarm = ultrawarm;
this.bedSafe = bedSafe;
this.effects = effects;
this.piglinSafe = piglinSafe;
this.minY = minY;
this.height = height;
this.logicalHeight = logicalHeight;
this.coordinateScale = coordinateScale;
this.infiniburn = infiniburn;
static @NotNull Builder builder(@NotNull String namespace) {
return builder(NamespaceID.from(namespace));
}
public static DimensionTypeBuilder builder(NamespaceID name) {
return hiddenBuilder().name(name);
static @NotNull Builder builder(@NotNull NamespaceID namespace) {
return new Builder(namespace);
}
public static DimensionTypeBuilder hiddenBuilder() {
return new DimensionTypeBuilder();
/**
* <p>Creates a new registry for dimension types, loading the vanilla dimension types.</p>
*
* @see net.minestom.server.MinecraftServer to get an existing instance of the registry
*/
@ApiStatus.Internal
static @NotNull DynamicRegistry<DimensionType> createDefaultRegistry() {
return new DynamicRegistryImpl<>(
"minecraft:dimension_type", NBT_TYPE, Registry.Resource.DIMENSION_TYPES,
(namespace, props) -> new DimensionTypeImpl(Registry.dimensionType(namespace, props))
);
}
public static DimensionType fromNBT(CompoundBinaryTag nbt) {
return DimensionType.builder(NamespaceID.from(nbt.getString("name")))
.ambientLight(nbt.getFloat("ambient_light"))
.infiniburn(NamespaceID.from(nbt.getString("infiniburn").replaceFirst("#", "")))
.natural(nbt.getBoolean("natural"))
.ceilingEnabled(nbt.getBoolean("has_ceiling"))
.skylightEnabled(nbt.getBoolean("has_skylight"))
.ultrawarm(nbt.getBoolean("ultrawarm"))
.raidCapable(nbt.getBoolean("has_raids"))
.respawnAnchorSafe(nbt.getBoolean("respawn_anchor_works"))
.bedSafe(nbt.getBoolean("bed_works"))
.effects(nbt.getString("effects"))
.piglinSafe(nbt.getBoolean("piglin_safe"))
.logicalHeight(nbt.getInt("logical_height"))
.coordinateScale(nbt.getDouble("coordinate_scale"))
.build();
boolean ultrawarm();
boolean natural();
double coordinateScale();
boolean hasSkylight();
boolean hasCeiling();
float ambientLight();
@Nullable Long fixedTime();
boolean piglinSafe();
boolean bedWorks();
boolean respawnAnchorWorks();
boolean hasRaids();
int logicalHeight();
int minY();
default int maxY() {
return minY() + height();
}
public @NotNull RegistryDataPacket.Entry toRegistryEntry() {
return new RegistryDataPacket.Entry(name.toString(), toNBT());
int height();
@NotNull NamespaceID infiniburn();
@NotNull String effects();
default int totalHeight() {
return minY() + height();
}
@NotNull
public CompoundBinaryTag toNBT() {
CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder();
builder.putFloat("ambient_light", ambientLight);
builder.putString("infiniburn", "#" + infiniburn.toString());
builder.putByte("natural", (byte) (natural ? 0x01 : 0x00));
builder.putByte("has_ceiling", (byte) (ceilingEnabled ? 0x01 : 0x00));
builder.putByte("has_skylight", (byte) (skylightEnabled ? 0x01 : 0x00));
builder.putByte("ultrawarm", (byte) (ultrawarm ? 0x01 : 0x00));
builder.putByte("has_raids", (byte) (raidCapable ? 0x01 : 0x00));
builder.putByte("respawn_anchor_works", (byte) (respawnAnchorSafe ? 0x01 : 0x00));
builder.putByte("bed_works", (byte) (bedSafe ? 0x01 : 0x00));
builder.putString("effects", effects);
builder.putByte("piglin_safe", (byte) (piglinSafe ? 0x01 : 0x00));
builder.putInt("min_y", minY);
builder.putInt("height", height);
builder.putInt("logical_height", logicalHeight);
builder.putDouble("coordinate_scale", coordinateScale);
builder.putString("name", name.toString());
builder.putInt("monster_spawn_block_light_limit", 0);
builder.putInt("monster_spawn_light_level", 11);
if (fixedTime != null) builder.putLong("fixed_time", fixedTime);
return builder.build();
}
@Override
public String toString() {
return name.toString();
}
public int getId() {
return this.id;
}
public boolean isRegistered() {
return registered;
}
public NamespaceID getName() {
return this.name;
}
public boolean isNatural() {
return this.natural;
}
public float getAmbientLight() {
return this.ambientLight;
}
public boolean isCeilingEnabled() {
return this.ceilingEnabled;
}
public boolean isSkylightEnabled() {
return this.skylightEnabled;
}
@Nullable
public Long getFixedTime() {
return this.fixedTime;
}
public boolean isRaidCapable() {
return this.raidCapable;
}
public boolean isRespawnAnchorSafe() {
return this.respawnAnchorSafe;
}
public boolean isUltrawarm() {
return this.ultrawarm;
}
public boolean isBedSafe() {
return this.bedSafe;
}
public String getEffects() {
return effects;
}
public boolean isPiglinSafe() {
return this.piglinSafe;
}
public int getMinY() {
return minY;
}
public int getHeight() {
return height;
}
public int getMaxY() {
return getMinY() + getHeight();
}
public int getLogicalHeight() {
return this.logicalHeight;
}
public double getCoordinateScale() {
return this.coordinateScale;
}
public NamespaceID getInfiniburn() {
return this.infiniburn;
}
public int getTotalHeight() {
return minY + height;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DimensionType that = (DimensionType) o;
return id == that.id &&
Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
public static class DimensionTypeBuilder {
private NamespaceID name;
private boolean natural;
private float ambientLight;
private boolean ceilingEnabled;
private boolean skylightEnabled;
@Nullable
private Long fixedTime = null;
private boolean raidCapable;
private boolean respawnAnchorSafe;
private boolean ultrawarm;
private boolean bedSafe = true;
private String effects = "minecraft:overworld";
private boolean piglinSafe = false;
private int minY = VANILLA_MIN_Y;
private int logicalHeight = VANILLA_MAX_Y - VANILLA_MIN_Y + 1;
private int height = VANILLA_MAX_Y - VANILLA_MIN_Y + 1;
final class Builder {
// Defaults match the vanilla overworld
private final NamespaceID namespace;
private boolean ultrawarm = false;
private boolean natural = true;
private double coordinateScale = 1.0;
private boolean hasSkylight = true;
private boolean hasCeiling = false;
private float ambientLight = 0f;
private Long fixedTime = null;
private boolean piglinSafe = false;
private boolean bedWorks = true;
private boolean respawnAnchorWorks = false;
private boolean hasRaids = true;
private int logicalHeight = VANILLA_MAX_Y - VANILLA_MIN_Y + 1;
private int minY = VANILLA_MIN_Y;
private int height = VANILLA_MAX_Y - VANILLA_MIN_Y + 1;
private NamespaceID infiniburn = NamespaceID.from("minecraft:infiniburn_overworld");
private String effects = "minecraft:overworld";
DimensionTypeBuilder() {
public Builder(@NotNull NamespaceID namespace) {
this.namespace = namespace;
}
public DimensionType.DimensionTypeBuilder name(NamespaceID name) {
this.name = name;
return this;
}
public DimensionType.DimensionTypeBuilder natural(boolean natural) {
this.natural = natural;
return this;
}
public DimensionType.DimensionTypeBuilder ambientLight(float ambientLight) {
this.ambientLight = ambientLight;
return this;
}
public DimensionType.DimensionTypeBuilder ceilingEnabled(boolean ceilingEnabled) {
this.ceilingEnabled = ceilingEnabled;
return this;
}
public DimensionType.DimensionTypeBuilder skylightEnabled(boolean skylightEnabled) {
this.skylightEnabled = skylightEnabled;
return this;
}
public DimensionType.DimensionTypeBuilder fixedTime(Long fixedTime) {
this.fixedTime = fixedTime;
return this;
}
public DimensionType.DimensionTypeBuilder raidCapable(boolean raidCapable) {
this.raidCapable = raidCapable;
return this;
}
public DimensionType.DimensionTypeBuilder respawnAnchorSafe(boolean respawnAnchorSafe) {
this.respawnAnchorSafe = respawnAnchorSafe;
return this;
}
public DimensionType.DimensionTypeBuilder ultrawarm(boolean ultrawarm) {
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder ultrawarm(boolean ultrawarm) {
this.ultrawarm = ultrawarm;
return this;
}
public DimensionType.DimensionTypeBuilder bedSafe(boolean bedSafe) {
this.bedSafe = bedSafe;
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder natural(boolean natural) {
this.natural = natural;
return this;
}
public DimensionType.DimensionTypeBuilder effects(String effects) {
this.effects = effects;
return this;
}
public DimensionType.DimensionTypeBuilder piglinSafe(boolean piglinSafe) {
this.piglinSafe = piglinSafe;
return this;
}
public DimensionType.DimensionTypeBuilder minY(int minY) {
this.minY = minY;
return this;
}
public DimensionType.DimensionTypeBuilder height(int height) {
this.height = height;
return this;
}
public DimensionType.DimensionTypeBuilder logicalHeight(int logicalHeight) {
this.logicalHeight = logicalHeight;
return this;
}
public DimensionType.DimensionTypeBuilder coordinateScale(double coordinateScale) {
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder coordinateScale(double coordinateScale) {
this.coordinateScale = coordinateScale;
return this;
}
public DimensionType.DimensionTypeBuilder infiniburn(NamespaceID infiniburn) {
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder hasSkylight(boolean hasSkylight) {
this.hasSkylight = hasSkylight;
return this;
}
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder hasCeiling(boolean hasCeiling) {
this.hasCeiling = hasCeiling;
return this;
}
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder ambientLight(float ambientLight) {
this.ambientLight = ambientLight;
return this;
}
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder fixedTime(@Nullable Long fixedTime) {
this.fixedTime = fixedTime;
return this;
}
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder piglinSafe(boolean piglinSafe) {
this.piglinSafe = piglinSafe;
return this;
}
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder bedWorks(boolean bedWorks) {
this.bedWorks = bedWorks;
return this;
}
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder respawnAnchorWorks(boolean respawnAnchorWorks) {
this.respawnAnchorWorks = respawnAnchorWorks;
return this;
}
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder hasRaids(boolean hasRaids) {
this.hasRaids = hasRaids;
return this;
}
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder logicalHeight(int logicalHeight) {
this.logicalHeight = logicalHeight;
return this;
}
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder minY(int minY) {
this.minY = minY;
return this;
}
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder height(int height) {
this.height = height;
return this;
}
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder infiniburn(@NotNull NamespaceID infiniburn) {
this.infiniburn = infiniburn;
return this;
}
public DimensionType build() {
return new DimensionType(name, natural, ambientLight, ceilingEnabled, skylightEnabled,
fixedTime, raidCapable, respawnAnchorSafe, ultrawarm, bedSafe, effects,
piglinSafe, minY, height, logicalHeight, coordinateScale, infiniburn);
@Contract(value = "_ -> this", pure = true)
public @NotNull Builder effects(@NotNull String effects) {
this.effects = effects;
return this;
}
@Contract(pure = true)
public @NotNull DimensionType build() {
return new DimensionTypeImpl(
namespace, ultrawarm, natural, coordinateScale, hasSkylight, hasCeiling, ambientLight,
fixedTime, piglinSafe, bedWorks, respawnAnchorWorks, hasRaids, logicalHeight, minY, height,
infiniburn, effects, null
);
}
}
}

View File

@ -0,0 +1,69 @@
package net.minestom.server.world;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.registry.Registry;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
record DimensionTypeImpl(
@NotNull NamespaceID namespace,
boolean ultrawarm,
boolean natural,
double coordinateScale,
boolean hasSkylight,
boolean hasCeiling,
float ambientLight,
@Nullable Long fixedTime,
boolean piglinSafe,
boolean bedWorks,
boolean respawnAnchorWorks,
boolean hasRaids,
int logicalHeight,
int minY,
int height,
@NotNull NamespaceID infiniburn,
@NotNull String effects,
@Nullable Registry.DimensionTypeEntry registry
) implements DimensionType {
static final BinaryTagSerializer<DimensionType> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
tag -> {
throw new UnsupportedOperationException("DimensionType is read-only");
},
dimensionType -> {
CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder()
.putBoolean("ultrawarm", dimensionType.ultrawarm())
.putBoolean("natural", dimensionType.natural())
.putDouble("coordinate_scale", dimensionType.coordinateScale())
.putBoolean("has_skylight", dimensionType.hasSkylight())
.putBoolean("has_ceiling", dimensionType.hasCeiling())
.putFloat("ambient_light", dimensionType.ambientLight())
.putBoolean("piglin_safe", dimensionType.piglinSafe())
.putBoolean("bed_works", dimensionType.bedWorks())
.putBoolean("respawn_anchor_works", dimensionType.respawnAnchorWorks())
.putBoolean("has_raids", dimensionType.hasRaids())
.putInt("logical_height", dimensionType.logicalHeight())
.putInt("min_y", dimensionType.minY())
.putInt("height", dimensionType.height())
.putString("infiniburn", dimensionType.infiniburn().toString())
.putString("effects", dimensionType.effects())
//todo load these from registry
.putInt("monster_spawn_block_light_limit", 0)
.putInt("monster_spawn_light_level", 0);
Long fixedTime = dimensionType.fixedTime();
if (fixedTime != null) builder.putLong("fixed_time", fixedTime);
return builder.build();
}
);
DimensionTypeImpl(@NotNull Registry.DimensionTypeEntry registry) {
this(registry.namespace(), registry.ultrawarm(), registry.natural(), registry.coordinateScale(),
registry.hasSkylight(), registry.hasCeiling(), registry.ambientLight(), registry.fixedTime(),
registry.piglinSafe(), registry.bedWorks(), registry.respawnAnchorWorks(), registry.hasRaids(),
registry.logicalHeight(), registry.minY(), registry.height(), NamespaceID.from(registry.infiniburn()),
registry.effects(), registry);
}
}

View File

@ -1,99 +0,0 @@
package net.minestom.server.world;
import net.minestom.server.network.packet.server.CachedPacket;
import net.minestom.server.network.packet.server.SendablePacket;
import net.minestom.server.network.packet.server.configuration.RegistryDataPacket;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Allows servers to register custom dimensions. Also used during player login to send the list of all existing dimensions.
* <p>
* Contains {@link DimensionType#OVERWORLD} by default but can be removed.
*/
public final class DimensionTypeManager {
private final CachedPacket registryDataPacket = new CachedPacket(this::createRegistryDataPacket);
private final List<DimensionType> dimensionTypes = new CopyOnWriteArrayList<>();
public DimensionTypeManager() {
addDimension(DimensionType.OVERWORLD);
}
/**
* Adds a new dimension type. This does NOT send the new list to players.
*
* @param dimensionType the dimension to add
*/
public void addDimension(@NotNull DimensionType dimensionType) {
dimensionType.registered = true;
this.dimensionTypes.add(dimensionType);
registryDataPacket.invalidate();
}
/**
* Removes a dimension type. This does NOT send the new list to players.
*
* @param dimensionType the dimension to remove
* @return if the dimension type was removed, false if it was not present before
*/
public boolean removeDimension(@NotNull DimensionType dimensionType) {
dimensionType.registered = false;
boolean removed = dimensionTypes.remove(dimensionType);
if (removed) registryDataPacket.invalidate();
return removed;
}
/**
* @param namespaceID The dimension name
* @return true if the dimension is registered
*/
public boolean isRegistered(@NotNull NamespaceID namespaceID) {
return isRegistered(getDimension(namespaceID));
}
/**
* @param dimensionType dimension to check if is registered
* @return true if the dimension is registered
*/
public boolean isRegistered(@Nullable DimensionType dimensionType) {
return dimensionType != null && dimensionTypes.contains(dimensionType) && dimensionType.isRegistered();
}
/**
* Return to a @{@link DimensionType} only if present and registered
*
* @param namespaceID The Dimension Name
* @return a DimensionType if it is present and registered
*/
public @Nullable DimensionType getDimension(@NotNull NamespaceID namespaceID) {
return unmodifiableList().stream().filter(dimensionType -> dimensionType.getName().equals(namespaceID)).filter(DimensionType::isRegistered).findFirst().orElse(null);
}
/**
* Returns an immutable copy of the dimension types already registered.
*
* @return an unmodifiable {@link List} containing all the added dimensions
*/
public @NotNull List<DimensionType> unmodifiableList() {
return Collections.unmodifiableList(dimensionTypes);
}
public @NotNull SendablePacket registryDataPacket() {
return registryDataPacket;
}
private @NotNull RegistryDataPacket createRegistryDataPacket() {
return new RegistryDataPacket(
"minecraft:dimension_type",
dimensionTypes.stream()
.map(DimensionType::toRegistryEntry)
.toList()
);
}
}

View File

@ -1,35 +1,53 @@
package net.minestom.server.world.biomes;
package net.minestom.server.world.biome;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.coordinate.Point;
import net.minestom.server.network.packet.server.configuration.RegistryDataPacket;
import net.minestom.server.registry.DynamicRegistry;
import net.minestom.server.registry.DynamicRegistryImpl;
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 net.minestom.server.utils.nbt.BinaryTagSerializer;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Locale;
public sealed interface Biome extends Biomes, ProtocolObject permits BiomeImpl {
@NotNull BinaryTagSerializer<Biome> NBT_TYPE = BiomeImpl.NBT_TYPE;
static @NotNull Builder builder(@NotNull String namespace) {
return builder(NamespaceID.from(namespace));
}
static @NotNull Builder builder(@NotNull NamespaceID namespace) {
return new Builder(namespace);
}
sealed public interface Biome extends ProtocolObject permits BiomeImpl {
/**
* Returns the entity registry.
* <p>Creates a new registry for biomes, loading the vanilla trim biomes.</p>
*
* @return the entity registry or null if it was created with a builder
* @see net.minestom.server.MinecraftServer to get an existing instance of the registry
*/
@Contract(pure = true)
@Nullable Registry.BiomeEntry registry();
@ApiStatus.Internal
static @NotNull DynamicRegistry<Biome> createDefaultRegistry() {
return new DynamicRegistryImpl<>(
"minecraft:worldgen/biome", NBT_TYPE, Registry.Resource.BIOMES,
(namespace, props) -> new BiomeImpl(Registry.biome(namespace, props))
);
}
@Override
@NotNull NamespaceID namespace();
float temperature();
float downfall();
@NotNull BiomeEffects effects();
@NotNull Precipitation precipitation();
@NotNull TemperatureModifier temperatureModifier();
@Nullable Registry.BiomeEntry registry();
enum Precipitation {
NONE, RAIN, SNOW;
}
@ -54,30 +72,6 @@ sealed public interface Biome extends ProtocolObject permits BiomeImpl {
}
}
default @NotNull RegistryDataPacket.Entry toRegistryEntry() {
return new RegistryDataPacket.Entry(namespace().toString(), toNbt());
}
default @NotNull CompoundBinaryTag toNbt() {
Check.notNull(name(), "The biome namespace cannot be null");
Check.notNull(effects(), "The biome effects cannot be null");
CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder()
.putFloat("temperature", temperature())
.putFloat("downfall", downfall())
.putByte("has_precipitation", (byte) (precipitation() == Precipitation.NONE ? 0 : 1))
.putString("precipitation", precipitation().name().toLowerCase(Locale.ROOT));
if (temperatureModifier() != TemperatureModifier.NONE)
builder.putString("temperature_modifier", temperatureModifier().name().toLowerCase(Locale.ROOT));
return builder
.put("effects", effects().toNbt())
.build();
}
static @NotNull Builder builder() {
return new Builder();
}
final class Builder {
private static final BiomeEffects DEFAULT_EFFECTS = BiomeEffects.builder()
.fogColor(0xC0D8FF)
@ -86,17 +80,15 @@ sealed public interface Biome extends ProtocolObject permits BiomeImpl {
.waterFogColor(0x50533)
.build();
private NamespaceID name;
private final NamespaceID namespace;
private float temperature = 0.25f;
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;
Builder(@NotNull NamespaceID namespace) {
this.namespace = namespace;
}
@Contract(value = "_ -> this", pure = true)
@ -131,7 +123,7 @@ sealed public interface Biome extends ProtocolObject permits BiomeImpl {
@Contract(pure = true)
public @NotNull Biome build() {
return new BiomeImpl(name, temperature, downfall, effects, precipitation, temperatureModifier);
return new BiomeImpl(namespace, temperature, downfall, effects, precipitation, temperatureModifier, null);
}
}
}

View File

@ -1,4 +1,4 @@
package net.minestom.server.world.biomes;
package net.minestom.server.world.biome;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.utils.NamespaceID;

View File

@ -0,0 +1,66 @@
package net.minestom.server.world.biome;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.registry.Registry;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Locale;
record BiomeImpl(
@NotNull NamespaceID namespace,
float temperature,
float downfall,
@NotNull BiomeEffects effects,
@NotNull Precipitation precipitation,
@NotNull TemperatureModifier temperatureModifier,
@Nullable Registry.BiomeEntry registry
) implements Biome {
// https://minecraft.wiki/w/Rain
private final static Double SNOW_TEMPERATURE = 0.15;
static final BinaryTagSerializer<Biome> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
tag -> {
throw new UnsupportedOperationException("Biome is read-only");
},
biome -> {
CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder()
.putFloat("temperature", biome.temperature())
.putFloat("downfall", biome.downfall())
.putByte("has_precipitation", (byte) (biome.precipitation() == Precipitation.NONE ? 0 : 1))
.putString("precipitation", biome.precipitation().name().toLowerCase(Locale.ROOT));
if (biome.temperatureModifier() != TemperatureModifier.NONE)
builder.putString("temperature_modifier", biome.temperatureModifier().name().toLowerCase(Locale.ROOT));
return builder
.put("effects", biome.effects().toNbt())
.build();
}
);
BiomeImpl(Registry.BiomeEntry entry) {
this(entry.namespace(), entry.temperature(), entry.downfall(), getBuilder(entry).build(),
entry.hasPrecipitation()
? entry.temperature() < SNOW_TEMPERATURE
? Precipitation.SNOW
: Precipitation.RAIN
: Precipitation.NONE,
entry.temperature() < SNOW_TEMPERATURE ? TemperatureModifier.FROZEN : TemperatureModifier.NONE,
entry
);
}
@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;
}
}

View File

@ -1,4 +1,4 @@
package net.minestom.server.world.biomes;
package net.minestom.server.world.biome;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.instance.block.Block;

View File

@ -1,111 +0,0 @@
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<BiomeImpl> CONTAINER = Registry.createDynamicContainer(Registry.Resource.BIOMES,
(namespace, properties) -> new BiomeImpl(Registry.biome(namespace, properties)));
static Collection<BiomeImpl> values() {
return CONTAINER.values();
}
static BiomeImpl get(@NotNull String namespace) {
return CONTAINER.get(namespace);
}
static BiomeImpl getSafe(@NotNull String namespace) {
return CONTAINER.getSafe(namespace);
}
private Registry.BiomeEntry entry;
@NotNull
private final NamespaceID name;
private final float temperature;
private final float downfall;
@NotNull
private final BiomeEffects effects;
@NotNull
private final Precipitation precipitation;
@NotNull
private final TemperatureModifier temperatureModifier;
BiomeImpl(NamespaceID name, float temperature, float downfall, BiomeEffects effects, Precipitation precipitation, TemperatureModifier temperatureModifier) {
this.name = name;
this.temperature = temperature;
this.downfall = downfall;
this.effects = effects;
this.precipitation = precipitation;
this.temperatureModifier = temperatureModifier;
}
BiomeImpl(Registry.BiomeEntry entry) {
this.entry = entry;
this.name = entry.namespace();
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 this.entry;
}
@Override
public @NotNull NamespaceID namespace() {
return this.name;
}
public float temperature() {
return this.temperature;
}
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;
}
}

View File

@ -1,143 +0,0 @@
package net.minestom.server.world.biomes;
import net.minestom.server.network.packet.server.CachedPacket;
import net.minestom.server.network.packet.server.SendablePacket;
import net.minestom.server.network.packet.server.configuration.RegistryDataPacket;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
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.
*/
public final class BiomeManager {
private final CachedPacket registryDataPacket = new CachedPacket(this::createRegistryDataPacket);
private final List<Biome> biomes = new CopyOnWriteArrayList<>();
private final Map<NamespaceID, Biome> biomesByName = new ConcurrentHashMap<>();
private final Map<NamespaceID, Integer> idMappings = new ConcurrentHashMap<>();
private final AtomicInteger ID_COUNTER = new AtomicInteger(0);
public BiomeManager() {
// 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);
}
}
/**
* Adds a new biome. This does NOT send the new list to players.
*
* @param biome the biome to add
*/
public void addBiome(@NotNull Biome biome) {
Check.stateCondition(getByName(biome.namespace()) != null, "The biome " + biome.namespace() + " has already been registered");
var id = this.biomes.size();
this.biomes.add(biome);
this.biomesByName.put(biome.namespace(), biome);
this.idMappings.put(biome.namespace(), id);
registryDataPacket.invalidate();
}
//todo supporting remove is probably challenging at best since we no longer send ids explicitly, so you cannot skip an ID.
// /**
// * Removes a biome. This does NOT send the new list to players.
// *
// * @param biome the biome to remove
// */
// public void removeBiome(@NotNull Biome biome) {
// var id = idMappings.get(biome.namespace());
// if (id != null) {
// biomes.remove(id.intValue());
// biomesByName.remove(biome.namespace());
// idMappings.remove(biome.namespace());
// registryDataPacket.invalidate();
// }
// }
/**
* Returns an immutable copy of the biomes already registered.
*
* @return an immutable copy of the biomes already registered
*/
public Collection<Biome> unmodifiableCollection() {
return Collections.unmodifiableCollection(biomes);
}
/**
* Gets a biome by its id.
*
* @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);
}
@Nullable
public Biome getByName(@NotNull NamespaceID namespaceID) {
return biomesByName.get(namespaceID);
}
@Nullable
public Biome getByName(@NotNull String namespaceID) {
NamespaceID namespace = NamespaceID.from(namespaceID);
return getByName(namespace);
}
/**
* 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);
}
//
// 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 -> {
// return NBT.Compound(Map.of(
// "id", NBT.Int(getId(biome)),
// "name", NBT.String(biome.namespace().toString()),
// "element", biome.toNbt()
// ));
// }).toList())));
//
// return nbtCache;
// }
public @NotNull SendablePacket registryDataPacket() {
return registryDataPacket;
}
private @NotNull RegistryDataPacket createRegistryDataPacket() {
return new RegistryDataPacket(
"minecraft:worldgen/biome",
biomes.stream()
.map(Biome::toRegistryEntry)
.toList()
);
}
}

View File

@ -1,4 +0,0 @@
package net.minestom.server.world.biomes;
final public class VanillaBiome implements Biomes {
}

View File

@ -3,13 +3,12 @@ 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.server.world.biome.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;

View File

@ -6,10 +6,7 @@ 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;

View File

@ -6,7 +6,7 @@ import net.minestom.server.instance.Section;
import net.minestom.server.instance.block.Block;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.world.biomes.Biome;
import net.minestom.server.world.biome.Biome;
import net.minestom.testing.Env;
import net.minestom.testing.EnvTest;
import org.jetbrains.annotations.NotNull;