diff --git a/bom/build.gradle.kts b/bom/build.gradle.kts index c087880974a..dcfc9fd2257 100644 --- a/bom/build.gradle.kts +++ b/bom/build.gradle.kts @@ -29,8 +29,6 @@ dependencies { // Libraries api(libs.gson) api(libs.jcTools) - // Path finding - api(libs.hydrazine) // Adventure, for user-interface api(libs.bundles.adventure) @@ -45,10 +43,6 @@ dependencies { // Minestom Data (From MinestomDataGenerator) api(libs.minestomData) - // NBT parsing/manipulation/saving - api("io.github.jglrxavpok.hephaistos:common:${libs.versions.hephaistos.get()}") - api("io.github.jglrxavpok.hephaistos:gson:${libs.versions.hephaistos.get()}") - // BStats api(libs.bstats.base) } diff --git a/build.gradle.kts b/build.gradle.kts index c89afe7145f..6c6dce9622e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,7 +10,7 @@ plugins { group = "net.onelitefeather.microtus" -version = System.getenv("TAG_VERSION") ?: "1.4.3-SNAPSHOT" +version = System.getenv("TAG_VERSION") ?: "1.4.0-SNAPSHOT" allprojects { group = "net.onelitefeather.microtus" @@ -58,7 +58,7 @@ tasks { addStringOption("-release", "21") // Links to external javadocs links("https://docs.oracle.com/en/java/javase/21/docs/api/") - links("https://jd.advntr.dev/api/${libs.versions.adventure.get()}/") + links("https://jd.adventure.kyori.net/api/${libs.versions.adventure.get()}/") } } withType { @@ -78,11 +78,11 @@ tasks { } dependencies { - // Testing Framework - testImplementation(project(mapOf("path" to ":testing"))) - // Only here to ensure J9 module support for extensions and our classloaders - testCompileOnly(libs.mockito.core) - + // Core dependencies + api(libs.slf4j) + api(libs.jetbrainsAnnotations) + api(libs.bundles.adventure) + implementation(libs.minestomData) // Logging implementation(libs.bundles.logging) @@ -93,31 +93,22 @@ dependencies { implementation(libs.caffeine) api(libs.fastutil) implementation(libs.bundles.flare) - - // Libraries - api(libs.gson) - implementation(libs.jcTools) - // Path finding - api(libs.hydrazine) - - // Adventure, for user-interface - api(libs.bundles.adventure) - - // Kotlin Libraries - api(libs.bundles.kotlin) - + // BStats + api(libs.bstats.base) + // Maven api(libs.maven.resolver) api(libs.maven.connector) api(libs.maven.transport.http) - // Minestom Data (From MinestomDataGenerator) - implementation(libs.minestomData) - - // NBT parsing/manipulation/saving - api("io.github.jglrxavpok.hephaistos:common:${libs.versions.hephaistos.get()}") - api("io.github.jglrxavpok.hephaistos:gson:${libs.versions.hephaistos.get()}") + // Libraries + api(libs.gson) + implementation(libs.jcTools) - // BStats - api(libs.bstats.base) + // Testing + testImplementation(libs.bundles.junit) + testImplementation(project(":testing")) + // Only here to ensure J9 module support for extensions and our classloaders + testCompileOnly(libs.mockito.core) } + diff --git a/code-generators/src/main/java/net/minestom/codegen/CodeGenerator.java b/code-generators/src/main/java/net/minestom/codegen/CodeGenerator.java index e1d2d6277a7..8aa876ec201 100644 --- a/code-generators/src/main/java/net/minestom/codegen/CodeGenerator.java +++ b/code-generators/src/main/java/net/minestom/codegen/CodeGenerator.java @@ -3,45 +3,40 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; -import com.squareup.javapoet.AnnotationSpec; -import com.squareup.javapoet.ClassName; -import com.squareup.javapoet.FieldSpec; -import com.squareup.javapoet.JavaFile; -import com.squareup.javapoet.TypeSpec; -import org.jetbrains.annotations.ApiStatus; +import com.squareup.javapoet.*; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.lang.model.SourceVersion; import javax.lang.model.element.Modifier; import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.util.HashMap; -import java.util.Map; +import java.util.List; +import java.util.Locale; -import static net.minestom.codegen.MinestomCodeGenerator.DEFAULT_INDENT; -import static net.minestom.codegen.MinestomCodeGenerator.extractNamespaces; - -@ApiStatus.Internal -public class CodeGenerator implements CodeExporter { +public class CodeGenerator { protected static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); private static final Logger LOGGER = LoggerFactory.getLogger(CodeGenerator.class); + private final File outputFolder; - public CodeGenerator(@NotNull File outputFolder) { + public CodeGenerator(File outputFolder) { this.outputFolder = outputFolder; } public void generate(InputStream resourceFile, String packageName, String typeName, String loaderName, String generatedName) { if (resourceFile == null) { - LOGGER.error("Failed to find resource file for {}", typeName); + LOGGER.error("Failed to find resource file for " + typeName); return; } ClassName typeClass = ClassName.get(packageName, typeName); ClassName loaderClass = ClassName.get(packageName, loaderName); - JsonObject json = GSON.fromJson(new InputStreamReader(resourceFile), JsonObject.class); + JsonObject json; + json = GSON.fromJson(new InputStreamReader(resourceFile), JsonObject.class); ClassName materialsCN = ClassName.get(packageName, generatedName); // BlockConstants class TypeSpec.Builder blockConstantsClass = TypeSpec.interfaceBuilder(materialsCN) @@ -49,16 +44,17 @@ public void generate(InputStream resourceFile, String packageName, String typeNa .addAnnotation(AnnotationSpec.builder(SuppressWarnings.class).addMember("value", "$S", "unused").build()) .addJavadoc("Code autogenerated, do not edit!"); - Map replacementOptions = new HashMap<>(); - replacementOptions.put("minecraft:", ""); - replacementOptions.put(".", "_"); // Use data json.keySet().forEach(namespace -> { - final String constantName = extractNamespaces(namespace, replacementOptions); + final String constantName = namespace + .replace("minecraft:", "") + .replace(".", "_") + .toUpperCase(Locale.ROOT); blockConstantsClass.addField( FieldSpec.builder(typeClass, constantName) .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) .initializer( + // TypeClass.STONE = MaterialLoader.fromNamespaceId("minecraft:stone") "$T.get($S)", loaderClass, namespace @@ -66,12 +62,69 @@ public void generate(InputStream resourceFile, String packageName, String typeNa .build() ); }); - writeFile( - JavaFile.builder(packageName, blockConstantsClass.build()) - .indent(DEFAULT_INDENT) + writeFiles( + List.of(JavaFile.builder(packageName, blockConstantsClass.build()) + .indent(" ") + .skipJavaLangImports(true) + .build()), + 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.bestGuess(packageName + "." + typeName); // Use bestGuess to handle nested class + ClassName registryKeyClass = ClassName.get("net.minestom.server.registry", "DynamicRegistry", "Key"); + ParameterizedTypeName typedRegistryKeyClass = ParameterizedTypeName.get(registryKeyClass, typeClass); + + 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 -> { + String constantName = namespace + .replace("minecraft:", "") + .replace(".", "_") + .toUpperCase(Locale.ROOT); + if (!SourceVersion.isName(constantName)) { + constantName = "_" + constantName; + } + blockConstantsClass.addField( + FieldSpec.builder(typedRegistryKeyClass, constantName) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + .initializer( + // TypeClass.STONE = NamespaceID.from("minecraft:stone") + "$T.of($S)", + registryKeyClass, + namespace + ) + .build() + ); + }); + writeFiles( + List.of(JavaFile.builder(packageName, blockConstantsClass.build()) + .indent(" ") .skipJavaLangImports(true) - .build(), - outputFolder - ); + .build()), + outputFolder); + } + + private void writeFiles(@NotNull List fileList, File outputFolder) { + for (JavaFile javaFile : fileList) { + try { + javaFile.writeTo(outputFolder); + } catch (IOException e) { + LOGGER.error("An error occured while writing source code to the file system.", e); + } + } } } diff --git a/code-generators/src/main/java/net/minestom/codegen/ConstantsGenerator.java b/code-generators/src/main/java/net/minestom/codegen/ConstantsGenerator.java new file mode 100644 index 00000000000..0ce81d41a30 --- /dev/null +++ b/code-generators/src/main/java/net/minestom/codegen/ConstantsGenerator.java @@ -0,0 +1,82 @@ +package net.minestom.codegen; + +import com.google.gson.JsonObject; +import com.squareup.javapoet.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.lang.model.element.Modifier; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.List; + +public class ConstantsGenerator extends MinestomCodeGenerator { + private static final Logger LOGGER = LoggerFactory.getLogger(ConstantsGenerator.class); + private final InputStream constantsFile; + private final File outputFolder; + + public ConstantsGenerator(@Nullable InputStream constantsFile, @NotNull File outputFolder) { + this.constantsFile = constantsFile; + this.outputFolder = outputFolder; + } + + @Override + public void generate() { + if (constantsFile == null) { + LOGGER.error("Failed to find constants.json."); + LOGGER.error("Stopped code generation for recipe types."); + return; + } + if (!outputFolder.exists() && !outputFolder.mkdirs()) { + LOGGER.error("Output folder for code generation does not exist and could not be created."); + return; + } + + // Important classes we use alot + JsonObject constants = GSON.fromJson(new InputStreamReader(constantsFile), JsonObject.class); + ClassName minecraftConstantsCN = ClassName.get("net.minestom.server", "MinecraftConstants"); + TypeSpec.Builder constantsInterface = TypeSpec.interfaceBuilder(minecraftConstantsCN) + .addJavadoc("AUTOGENERATED by " + getClass().getSimpleName()); + + constantsInterface.addField(FieldSpec.builder(String.class, "VERSION_NAME") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + .initializer("$S", constants.get("name").getAsString()) + .build() + ); + constantsInterface.addField(FieldSpec.builder(TypeName.INT, "PROTOCOL_VERSION") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + .initializer("$L", constants.get("protocol").getAsInt()) + .build() + ); + constantsInterface.addField(FieldSpec.builder(TypeName.INT, "DATA_VERSION") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + .initializer("$L", constants.get("world").getAsInt()) + .build() + ); + constantsInterface.addField(FieldSpec.builder(TypeName.INT, "RESOURCE_PACK_VERSION") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + .initializer("$L", constants.get("resourcepack").getAsInt()) + .build() + ); + constantsInterface.addField(FieldSpec.builder(TypeName.INT, "DATA_PACK_VERSION") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + .initializer("$L", constants.get("datapack").getAsInt()) + .build() + ); + + // Write files to outputFolder + writeFiles( + List.of( + JavaFile.builder("net.minestom.server", constantsInterface.build()) + .indent(" ") + .skipJavaLangImports(true) + .build() + ), + outputFolder + ); + } + +} diff --git a/code-generators/src/main/java/net/minestom/codegen/Generators.java b/code-generators/src/main/java/net/minestom/codegen/Generators.java index 140c1afc966..deddd0e9094 100644 --- a/code-generators/src/main/java/net/minestom/codegen/Generators.java +++ b/code-generators/src/main/java/net/minestom/codegen/Generators.java @@ -1,6 +1,9 @@ package net.minestom.codegen; import net.minestom.codegen.color.DyeColorGenerator; +import net.minestom.codegen.fluid.FluidGenerator; +import net.minestom.codegen.particle.ParticleGenerator; +import net.minestom.codegen.recipe.RecipeTypeGenerator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,32 +20,51 @@ public static void main(String[] args) { } File outputFolder = new File(args[0]); - - // Generate DyeColors + // Special generators new DyeColorGenerator(resource("dye_colors.json"), outputFolder).generate(); - + new RecipeTypeGenerator(resource("recipe_types.json"), outputFolder).generate(); + new ParticleGenerator(resource("particles.json"), outputFolder).generate(); + new ConstantsGenerator(resource("constants.json"), outputFolder).generate(); 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", "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", "SoundEventImpl", "SoundEvents"); + 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("banner_patterns.json"), "net.minestom.server.item.banner", "BannerPattern", "BannerPatternImpl", "BannerPatterns"); // Microtus - Banner and Shield Meta - generator.generate(resource("villager_professions.json"), "net.minestom.server.entity.villager", "VillagerProfession", "VillagerProfessionImpl", "VillagerProfessions"); - generator.generate(resource("villager_types.json"), "net.minestom.server.entity.villager", "VillagerType", "VillagerTypeImpl", "VillagerTypes"); - generator.generate(resource("attributes.json"), "net.minestom.server.attribute", "Attribute", "AttributeImpl", "Attributes"); - generator.generate(resource("fluids.json"), "net.minestom.server.fluid", "Fluid", "FluidImpl", "Fluids"); - generator.generate(resource("feature_flags.json"), "net.minestom.server.featureflag", "FeatureFlag", "FeatureFlagImpl", "FeatureFlags"); + generator.generate(resource("attributes.json"), "net.minestom.server.entity.attribute", "Attribute", "AttributeImpl", "Attributes"); + generator.generate(resource("feature_flags.json"), "net.minestom.server", "FeatureFlag", "FeatureFlagImpl", "FeatureFlags"); + + // 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", "WolfMeta.Variant", "WolfVariants"); + generator.generateKeys(resource("enchantments.json"), "net.minestom.server.item.enchant", "Enchantment", "Enchantments"); + generator.generateKeys(resource("painting_variants.json"), "net.minestom.server.entity.metadata.other", "PaintingMeta.Variant", "PaintingVariants"); + generator.generateKeys(resource("jukebox_songs.json"), "net.minestom.server.instance.block.jukebox", "JukeboxSong", "JukeboxSongs"); + + // Generate fluids + new FluidGenerator(resource("fluids.json"), outputFolder).generate(); + // TODO: Generate villager professions +// new VillagerProfessionGenerator( +// new File(inputFolder, targetVersion + "_villager_professions.json"), +// outputFolder +// ).generate(); + // TODO: Generate villager types +// new VillagerTypeGenerator( +// new File(inputFolder, targetVersion + "_villager_types.json"), +// outputFolder +// ).generate(); LOGGER.info("Finished generating code"); } diff --git a/code-generators/src/main/java/net/minestom/codegen/MinestomCodeGenerator.java b/code-generators/src/main/java/net/minestom/codegen/MinestomCodeGenerator.java index 1966f7d2c62..63b26f97d26 100644 --- a/code-generators/src/main/java/net/minestom/codegen/MinestomCodeGenerator.java +++ b/code-generators/src/main/java/net/minestom/codegen/MinestomCodeGenerator.java @@ -22,17 +22,11 @@ public abstract class MinestomCodeGenerator implements CodeExporter { protected static final Modifier[] CONSTANT_MODIFIERS = {Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL}; protected static final Modifier[] PRIVATE_FINAL_MODIFIERS = {Modifier.PRIVATE, Modifier.FINAL}; protected static final String DEFAULT_INDENT = " "; - protected String packageName; /** * Creates a new code generator. - * @param packageName the package name of the generated class */ - protected MinestomCodeGenerator(@NotNull String packageName) { - if (packageName.trim().isEmpty()) { - throw new IllegalArgumentException("Package name cannot be empty"); - } - this.packageName = packageName; + protected MinestomCodeGenerator() { } public abstract void generate(); @@ -49,4 +43,8 @@ protected MinestomCodeGenerator(@NotNull String packageName) { } return namespace.toUpperCase(Locale.ROOT); } + + protected static String toConstant(String namespace) { + return namespace.replace("minecraft:", "").toUpperCase(Locale.ROOT); + } } diff --git a/code-generators/src/main/java/net/minestom/codegen/color/DyeColorGenerator.java b/code-generators/src/main/java/net/minestom/codegen/color/DyeColorGenerator.java index c27b85e155b..da85a1d66e5 100644 --- a/code-generators/src/main/java/net/minestom/codegen/color/DyeColorGenerator.java +++ b/code-generators/src/main/java/net/minestom/codegen/color/DyeColorGenerator.java @@ -5,8 +5,6 @@ import com.google.gson.JsonObject; import com.squareup.javapoet.*; import net.minestom.codegen.MinestomCodeGenerator; -import net.minestom.codegen.util.GenerationHelper; -import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -20,21 +18,12 @@ import java.util.List; import java.util.stream.StreamSupport; -@ApiStatus.NonExtendable -@ApiStatus.Internal -public final class DyeColorGenerator extends MinestomCodeGenerator { - - private static final String CLASS_NAME = "DyeColor"; // Microtus - Banner and shield meta - private static final String TEXTURE_DIFFUSE_COLOR = "textureDiffuseColor"; - private static final String TEXT_COLOR = "textColor"; - private static final String FIREWORK_COLOR = "fireworkColor"; - private static final String MAP_COLOR_ID = "mapColorId"; +public class DyeColorGenerator extends MinestomCodeGenerator { private static final Logger LOGGER = LoggerFactory.getLogger(DyeColorGenerator.class); private final InputStream dyeColorsFile; private final File outputFolder; public DyeColorGenerator(@Nullable InputStream dyeColorsFile, @NotNull File outputFolder) { - super("net.minestom.server.color"); this.dyeColorsFile = dyeColorsFile; this.outputFolder = outputFolder; } @@ -50,24 +39,34 @@ public void generate() { LOGGER.error("Output folder for code generation does not exist and could not be created."); return; } - // Important classes we use a lot - ClassName colorCN = ClassName.get(packageName, "Color"); + // Important classes we use alot + ClassName colorCN = ClassName.get("net.minestom.server.color", "Color"); JsonArray dyeColors = GSON.fromJson(new InputStreamReader(dyeColorsFile), JsonArray.class); - ClassName dyeColorCN = ClassName.get(packageName, CLASS_NAME); + ClassName dyeColorCN = ClassName.get("net.minestom.server.color", "DyeColor"); // Dye Color Enum TypeSpec.Builder dyeColorEnum = TypeSpec.enumBuilder(dyeColorCN) .addSuperinterface(ClassName.get("net.kyori.adventure.util", "RGBLike")) .addModifiers(Modifier.PUBLIC).addJavadoc("AUTOGENERATED by " + getClass().getSimpleName()); + ClassName networkBufferCN = ClassName.get("net.minestom.server.network", "NetworkBuffer"); + ParameterizedTypeName networkBufferTypeCN = ParameterizedTypeName.get(networkBufferCN.nestedClass("Type"), dyeColorCN); + ClassName binaryTagSerializerCN = ClassName.get("net.minestom.server.utils.nbt", "BinaryTagSerializer"); + ParameterizedTypeName binaryTagSerializerTypeCN = ParameterizedTypeName.get(binaryTagSerializerCN, dyeColorCN); + // Fields dyeColorEnum.addFields( List.of( - FieldSpec.builder(colorCN, TEXTURE_DIFFUSE_COLOR, PRIVATE_FINAL_MODIFIERS).build(), - FieldSpec.builder(colorCN, TEXT_COLOR, PRIVATE_FINAL_MODIFIERS).build(), - FieldSpec.builder(colorCN, FIREWORK_COLOR, PRIVATE_FINAL_MODIFIERS).build(), - FieldSpec.builder(TypeName.INT, MAP_COLOR_ID, PRIVATE_FINAL_MODIFIERS).build(), - FieldSpec.builder(ArrayTypeName.of(dyeColorCN), "VALUES", CONSTANT_MODIFIERS).initializer(CLASS_NAME + ".values()").build() // Microtus - Banner and shield meta + FieldSpec.builder(networkBufferTypeCN, "NETWORK_TYPE", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + .initializer("$T.Enum($T.class)", networkBufferCN, dyeColorCN) + .build(), + FieldSpec.builder(binaryTagSerializerTypeCN, "NBT_TYPE", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + .initializer("$T.fromEnumStringable($T.class)", binaryTagSerializerCN, dyeColorCN) + .build(), + FieldSpec.builder(colorCN, "textureDiffuseColor", Modifier.PRIVATE, Modifier.FINAL).build(), + FieldSpec.builder(colorCN, "textColor", Modifier.PRIVATE, Modifier.FINAL).build(), + FieldSpec.builder(colorCN, "fireworkColor", Modifier.PRIVATE, Modifier.FINAL).build(), + FieldSpec.builder(TypeName.INT, "mapColorId", Modifier.PRIVATE, Modifier.FINAL).build() ) ); @@ -78,28 +77,28 @@ public void generate() { MethodSpec.constructorBuilder() .addParameters( List.of( - ParameterSpec.builder(colorCN, TEXTURE_DIFFUSE_COLOR).addAnnotation(NotNull.class).build(), - ParameterSpec.builder(colorCN, TEXT_COLOR).addAnnotation(NotNull.class).build(), - ParameterSpec.builder(colorCN, FIREWORK_COLOR).addAnnotation(NotNull.class).build(), - ParameterSpec.builder(TypeName.INT, MAP_COLOR_ID).build() + ParameterSpec.builder(colorCN, "textureDiffuseColor").addAnnotation(NotNull.class).build(), + ParameterSpec.builder(colorCN, "textColor").addAnnotation(NotNull.class).build(), + ParameterSpec.builder(colorCN, "fireworkColor").addAnnotation(NotNull.class).build(), + ParameterSpec.builder(TypeName.INT, "mapColorId").build() ) ) - .addStatement(GenerationHelper.VARIABLE_SETTER, TEXTURE_DIFFUSE_COLOR) - .addStatement(GenerationHelper.VARIABLE_SETTER, TEXT_COLOR) - .addStatement(GenerationHelper.VARIABLE_SETTER, FIREWORK_COLOR) - .addStatement(GenerationHelper.VARIABLE_SETTER, MAP_COLOR_ID) + .addStatement("this.textureDiffuseColor = textureDiffuseColor") + .addStatement("this.textColor = textColor") + .addStatement("this.fireworkColor = fireworkColor") + .addStatement("this.mapColorId = mapColorId") .build(), MethodSpec.methodBuilder("color") .addModifiers(Modifier.PUBLIC) .returns(colorCN.annotated(AnnotationSpec.builder(NotNull.class).build())) .addStatement("return this.textureDiffuseColor") .build(), - MethodSpec.methodBuilder(TEXT_COLOR) + MethodSpec.methodBuilder("textColor") .addModifiers(Modifier.PUBLIC) .returns(colorCN.annotated(AnnotationSpec.builder(NotNull.class).build())) .addStatement("return this.textColor") .build(), - MethodSpec.methodBuilder(FIREWORK_COLOR) + MethodSpec.methodBuilder("fireworkColor") .addModifiers(Modifier.PUBLIC) .returns(colorCN.annotated(AnnotationSpec.builder(NotNull.class).build())) .addStatement("return this.fireworkColor") @@ -122,30 +121,23 @@ public void generate() { .addAnnotation(Override.class) .addStatement("return this.textureDiffuseColor.blue()") .build(), - MethodSpec.methodBuilder(MAP_COLOR_ID) + MethodSpec.methodBuilder("mapColorId") .addModifiers(Modifier.PUBLIC) .returns(TypeName.INT) .addStatement("return this.mapColorId") - .build(), - MethodSpec.methodBuilder("getValue") // Microtus start - Banner and shield meta - .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .addParameter(ParameterSpec.builder(TypeName.INT, "id").build()) - .addAnnotation(Nullable.class) - .returns(ClassName.get(packageName, CLASS_NAME)) - .addCode("return VALUES[$L];", "id") - .build() // Microtus end - Banner and shield meta + .build() ) ); // Use data for (JsonObject dyeColorObject : StreamSupport.stream(dyeColors.spliterator(), true).map(JsonElement::getAsJsonObject).sorted(Comparator.comparingInt(o -> o.get("id").getAsInt())).toList()) { String dyeColorName = dyeColorObject.get("name").getAsString(); - dyeColorEnum.addEnumConstant(extractNamespace(dyeColorName), TypeSpec.anonymousClassBuilder( + dyeColorEnum.addEnumConstant(toConstant(dyeColorName), TypeSpec.anonymousClassBuilder( "new $T(0x$L), new $T(0x$L), new $T(0x$L), $L", - colorCN, Integer.toString(dyeColorObject.get(TEXTURE_DIFFUSE_COLOR).getAsInt(), 16), - colorCN, Integer.toString(dyeColorObject.get(TEXT_COLOR).getAsInt(), 16), - colorCN, Integer.toString(dyeColorObject.get(FIREWORK_COLOR).getAsInt(), 16), - dyeColorObject.get(MAP_COLOR_ID).getAsInt() + colorCN, Integer.toString(dyeColorObject.get("textureDiffuseColor").getAsInt(), 16), + colorCN, Integer.toString(dyeColorObject.get("textColor").getAsInt(), 16), + colorCN, Integer.toString(dyeColorObject.get("fireworkColor").getAsInt(), 16), + dyeColorObject.get("mapColorId").getAsInt() ).build() ); } @@ -153,8 +145,8 @@ public void generate() { // Write files to outputFolder writeFiles( List.of( - JavaFile.builder(packageName, dyeColorEnum.build()) - .indent(DEFAULT_INDENT) + JavaFile.builder("net.minestom.server.color", dyeColorEnum.build()) + .indent(" ") .skipJavaLangImports(true) .build() ), diff --git a/code-generators/src/main/java/net/minestom/codegen/entity/VillagerProfessionGenerator.java b/code-generators/src/main/java/net/minestom/codegen/entity/VillagerProfessionGenerator.java new file mode 100644 index 00000000000..9309c8ec68d --- /dev/null +++ b/code-generators/src/main/java/net/minestom/codegen/entity/VillagerProfessionGenerator.java @@ -0,0 +1,194 @@ +package net.minestom.codegen.entity; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.squareup.javapoet.*; +import net.minestom.codegen.MinestomCodeGenerator; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import net.minestom.codegen.util.GenerationHelper; + +import javax.lang.model.element.Modifier; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.List; + +import static net.minestom.codegen.util.GenerationHelper.*; + +@ApiStatus.NonExtendable +@ApiStatus.Internal +public final class VillagerProfessionGenerator extends MinestomCodeGenerator { + + private static final String VILLAGER_PROFESSION_DATE = "villagerProfessionData"; + private static final Logger LOGGER = LoggerFactory.getLogger(VillagerProfessionGenerator.class); + private final InputStream villagerProfessionsFile; + private final File outputFolder; + + + public VillagerProfessionGenerator(@Nullable InputStream villagerProfessionsFile, @NotNull File outputFolder) { + this.villagerProfessionsFile = villagerProfessionsFile; + this.outputFolder = outputFolder; + } + + @Override + public void generate() { + if (villagerProfessionsFile == null) { + LOGGER.error("Failed to find villager_professions.json."); + LOGGER.error("Stopped code generation for villager professions."); + return; + } + if (!outputFolder.exists() && !outputFolder.mkdirs()) { + LOGGER.error("Output folder for code generation does not exist and could not be created."); + return; + } + // Important classes we use alot + ClassName rawVillagerProfessionDataClassName = ClassName.get("net.minestom.server.raw_data", "RawVillagerProfessionData"); + + JsonArray villagerProfessions = GSON.fromJson(new InputStreamReader(villagerProfessionsFile), JsonArray.class); + ClassName villagerProfessionClassName = ClassName.get("net.minestom.server.entity.metadata.villager", "VillagerProfession"); + + // Particle + TypeSpec.Builder villagerProfessionClass = TypeSpec.classBuilder(villagerProfessionClassName) + .addSuperinterface(KEYORI_ADVENTURE_KEY) + .addModifiers(Modifier.PUBLIC).addJavadoc("AUTOGENERATED by " + getClass().getSimpleName()); + villagerProfessionClass.addField( + FieldSpec.builder(NAMESPACE_ID_CLASS, "id") + .addModifiers(Modifier.PRIVATE, Modifier.FINAL).addAnnotation(NotNull.class).build() + ); + villagerProfessionClass.addField( + FieldSpec.builder(rawVillagerProfessionDataClassName, VILLAGER_PROFESSION_DATE) + .addModifiers(Modifier.PRIVATE, Modifier.VOLATILE) + .addAnnotation(NotNull.class) + .build() + ); + villagerProfessionClass.addMethod( + MethodSpec.constructorBuilder() + .addParameter(ParameterSpec.builder(NAMESPACE_ID_CLASS, "id").addAnnotation(NotNull.class).build()) + .addParameter(ParameterSpec.builder(rawVillagerProfessionDataClassName, VILLAGER_PROFESSION_DATE).addAnnotation(NotNull.class).build()) + .addStatement(VARIABLE_SETTER, "id") + .addStatement(VARIABLE_SETTER, VILLAGER_PROFESSION_DATE) + .addModifiers(Modifier.PROTECTED) + .build() + ); + // Override key method (adventure) + villagerProfessionClass.addMethod(GenerationHelper.ADVENTURE_KEY_METHOD); + // getId method + villagerProfessionClass.addMethod(GenerationHelper.ID_GETTER); + // getVillagerProfessionData method + villagerProfessionClass.addMethod( + MethodSpec.methodBuilder("getVillagerProfessionData") + .returns(rawVillagerProfessionDataClassName) + .addAnnotation(NotNull.class) + .addStatement("return this.villagerProfessionData") + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .build() + ); + // setVillagerProfessionData method + villagerProfessionClass.addMethod( + MethodSpec.methodBuilder("setVillagerProfessionData") + .addParameter(ParameterSpec.builder(rawVillagerProfessionDataClassName, VILLAGER_PROFESSION_DATE).addAnnotation(NotNull.class).build()) + .addStatement("this.$L1 = $L1", VILLAGER_PROFESSION_DATE) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .build() + ); + // getNumericalId + villagerProfessionClass.addMethod( + MethodSpec.methodBuilder("getNumericalId") + .returns(TypeName.INT) + .addStatement("return $T.VILLAGER_PROFESSION_REGISTRY.getId(this)", REGISTRY_CLASS) + .addModifiers(Modifier.PUBLIC) + .build() + ); + // fromId Method + villagerProfessionClass.addMethod( + MethodSpec.methodBuilder("fromId") + .returns(villagerProfessionClassName) + .addAnnotation(Nullable.class) + .addParameter(TypeName.INT, "id") + .addStatement("return $T.VILLAGER_PROFESSION_REGISTRY.get((short) id)", REGISTRY_CLASS) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .build() + ); + // fromId Method + villagerProfessionClass.addMethod( + MethodSpec.methodBuilder("fromId") + .returns(villagerProfessionClassName) + .addAnnotation(NotNull.class) + .addParameter(ADVENTURE_KEY, "id") + .addStatement("return $T.VILLAGER_PROFESSION_REGISTRY.get(id)", REGISTRY_CLASS) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .build() + ); + // toString method + villagerProfessionClass.addMethod(GenerationHelper.TO_STRING); + // values method + villagerProfessionClass.addMethod( + MethodSpec.methodBuilder("values") + .addAnnotation(NotNull.class) + .returns(ParameterizedTypeName.get(ClassName.get(List.class), villagerProfessionClassName)) + .addStatement("return $T.VILLAGER_PROFESSION_REGISTRY.values()", REGISTRY_CLASS) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .build() + ); + CodeBlock.Builder staticBlock = CodeBlock.builder(); + // Use data + for (JsonElement vp : villagerProfessions) { + JsonObject villagerProfession = vp.getAsJsonObject(); + + String villagerProfessionName = villagerProfession.get("name").getAsString(); + JsonElement workSound = villagerProfession.get("workSound"); + if (workSound == null) { + villagerProfessionClass.addField( + FieldSpec.builder( + villagerProfessionClassName, + villagerProfessionName + ).initializer( + "new $T($T.from($S), new $T(() -> null))", + villagerProfessionClassName, + NAMESPACE_ID_CLASS, + villagerProfession.get("id").getAsString(), + + rawVillagerProfessionDataClassName + ).addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).build() + ); + } else { + villagerProfessionClass.addField( + FieldSpec.builder( + villagerProfessionClassName, + villagerProfessionName + ).initializer( + "new $T($T.from($S), new $T(() -> $T.SOUND_EVENT_REGISTRY.get($S)))", + villagerProfessionClassName, + NAMESPACE_ID_CLASS, + villagerProfession.get("id").getAsString(), + + rawVillagerProfessionDataClassName, + REGISTRY_CLASS, + workSound.getAsString() + ).addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).build() + ); + } + + // Add to static init. + staticBlock.addStatement("$T.VILLAGER_PROFESSION_REGISTRY.register($N)", REGISTRY_CLASS, villagerProfessionName); + } + + villagerProfessionClass.addStaticBlock(staticBlock.build()); + + // Write files to outputFolder + writeFiles( + List.of( + JavaFile.builder("net.minestom.server.entity.metadata.villager", villagerProfessionClass.build()) + .indent(DEFAULT_INDENT) + .skipJavaLangImports(true) + .build() + ), + outputFolder + ); + } +} diff --git a/code-generators/src/main/java/net/minestom/codegen/entity/VillagerTypeGenerator.java b/code-generators/src/main/java/net/minestom/codegen/entity/VillagerTypeGenerator.java new file mode 100644 index 00000000000..0f59af765a7 --- /dev/null +++ b/code-generators/src/main/java/net/minestom/codegen/entity/VillagerTypeGenerator.java @@ -0,0 +1,151 @@ +package net.minestom.codegen.entity; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.squareup.javapoet.*; +import net.minestom.codegen.MinestomCodeGenerator; +import net.minestom.codegen.util.GenerationHelper; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.lang.model.element.Modifier; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.List; + +import static net.minestom.codegen.util.GenerationHelper.ADVENTURE_KEY; +import static net.minestom.codegen.util.GenerationHelper.TO_STRING; + +@ApiStatus.NonExtendable +@ApiStatus.Internal +public final class VillagerTypeGenerator extends MinestomCodeGenerator { + private static final Logger LOGGER = LoggerFactory.getLogger(VillagerTypeGenerator.class); + private final InputStream villagerTypesFile; + private final File outputFolder; + + public VillagerTypeGenerator(@Nullable InputStream villagerTypesFile, @NotNull File outputFolder) { + this.villagerTypesFile = villagerTypesFile; + this.outputFolder = outputFolder; + } + + @Override + public void generate() { + if (villagerTypesFile == null) { + LOGGER.error("Failed to find villager_types.json."); + LOGGER.error("Stopped code generation for villager types."); + return; + } + if (!outputFolder.exists() && !outputFolder.mkdirs()) { + LOGGER.error("Output folder for code generation does not exist and could not be created."); + return; + } + // Important classes we use alot + JsonArray villagerTypes = GSON.fromJson(new InputStreamReader(villagerTypesFile), JsonArray.class); + ClassName villagerTypeClassName = ClassName.get("net.minestom.server.entity.metadata.villager", "VillagerType"); + + // Particle + TypeSpec.Builder villagerTypeClass = TypeSpec.classBuilder(villagerTypeClassName) + .addSuperinterface(KEYORI_ADVENTURE_KEY) + .addModifiers(Modifier.PUBLIC).addJavadoc("AUTOGENERATED by " + getClass().getSimpleName()); + villagerTypeClass.addField( + FieldSpec.builder(NAMESPACE_ID_CLASS, "id") + .addModifiers(Modifier.PRIVATE, Modifier.FINAL).addAnnotation(NotNull.class).build() + ); + villagerTypeClass.addMethod( + MethodSpec.constructorBuilder() + .addParameter(ParameterSpec.builder(NAMESPACE_ID_CLASS, "id").addAnnotation(NotNull.class).build()) + .addStatement("this.id = id") + .addModifiers(Modifier.PROTECTED) + .build() + ); + // Override key method (adventure) + villagerTypeClass.addMethod(GenerationHelper.ID_GETTER); + // getId method + villagerTypeClass.addMethod( + MethodSpec.methodBuilder("getId") + .returns(NAMESPACE_ID_CLASS) + .addAnnotation(NotNull.class) + .addStatement("return this.id") + .addModifiers(Modifier.PUBLIC) + .build() + ); + // getNumericalId + villagerTypeClass.addMethod( + MethodSpec.methodBuilder("getNumericalId") + .returns(TypeName.INT) + .addStatement("return $T.VILLAGER_TYPE_REGISTRY.getId(this)", REGISTRY_CLASS) + .addModifiers(Modifier.PUBLIC) + .build() + ); + // fromId Method + villagerTypeClass.addMethod( + MethodSpec.methodBuilder("fromId") + .returns(villagerTypeClassName) + .addAnnotation(Nullable.class) + .addParameter(TypeName.INT, "id") + .addStatement("return $T.VILLAGER_TYPE_REGISTRY.get((short) id)", REGISTRY_CLASS) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .build() + ); + // fromId Method + villagerTypeClass.addMethod( + MethodSpec.methodBuilder("fromId") + .returns(villagerTypeClassName) + .addAnnotation(NotNull.class) + .addParameter(ADVENTURE_KEY, "id") + .addStatement("return $T.VILLAGER_TYPE_REGISTRY.get(id)", REGISTRY_CLASS) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .build() + ); + // toString method + villagerTypeClass.addMethod(TO_STRING); + // values method + villagerTypeClass.addMethod( + MethodSpec.methodBuilder("values") + .addAnnotation(NotNull.class) + .returns(ParameterizedTypeName.get(ClassName.get(List.class), villagerTypeClassName)) + .addStatement("return $T.VILLAGER_TYPE_REGISTRY.values()", REGISTRY_CLASS) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .build() + ); + CodeBlock.Builder staticBlock = CodeBlock.builder(); + // Use data + for (JsonElement vp : villagerTypes) { + JsonObject villagerProfession = vp.getAsJsonObject(); + + String villagerProfessionName = villagerProfession.get("name").getAsString(); + + villagerTypeClass.addField( + FieldSpec.builder( + villagerTypeClassName, + villagerProfessionName + ).initializer( + "new $T($T.from($S))", + villagerTypeClassName, + NAMESPACE_ID_CLASS, + villagerProfession.get("id").getAsString() + ).addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).build() + ); + // Add to static init. + staticBlock.addStatement("$T.VILLAGER_TYPE_REGISTRY.register($N)", REGISTRY_CLASS, villagerProfessionName); + } + + villagerTypeClass.addStaticBlock(staticBlock.build()); + + // Write files to outputFolder + writeFiles( + List.of( + JavaFile.builder("net.minestom.server.entity.metadata.villager", villagerTypeClass.build()) + .indent(DEFAULT_INDENT) + .skipJavaLangImports(true) + .build() + ), + outputFolder + ); + } +} diff --git a/code-generators/src/main/java/net/minestom/codegen/fluid/FluidGenerator.java b/code-generators/src/main/java/net/minestom/codegen/fluid/FluidGenerator.java new file mode 100644 index 00000000000..642489970cf --- /dev/null +++ b/code-generators/src/main/java/net/minestom/codegen/fluid/FluidGenerator.java @@ -0,0 +1,144 @@ +package net.minestom.codegen.fluid; + +import com.google.gson.JsonObject; +import com.squareup.javapoet.*; +import net.minestom.codegen.MinestomCodeGenerator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.lang.model.element.Modifier; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.List; + +public final class FluidGenerator extends MinestomCodeGenerator { + private static final Logger LOGGER = LoggerFactory.getLogger(FluidGenerator.class); + private final InputStream fluidsFile; + private final File outputFolder; + + public FluidGenerator(@Nullable InputStream fluidsFile, @NotNull File outputFolder) { + this.fluidsFile = fluidsFile; + this.outputFolder = outputFolder; + } + + @Override + public void generate() { + if (fluidsFile == null) { + LOGGER.error("Failed to find fluids.json."); + LOGGER.error("Stopped code generation for fluids."); + return; + } + if (!outputFolder.exists() && !outputFolder.mkdirs()) { + LOGGER.error("Output folder for code generation does not exist and could not be created."); + return; + } + // Important classes we use alot + ClassName namespaceIDClassName = ClassName.get("net.minestom.server.utils", "NamespaceID"); + 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"); + + // Particle + TypeSpec.Builder fluidClass = TypeSpec.enumBuilder(fluidClassName) + .addSuperinterface(ClassName.get("net.kyori.adventure.key", "Keyed")) + .addModifiers(Modifier.PUBLIC).addJavadoc("AUTOGENERATED by " + getClass().getSimpleName()); + + fluidClass.addField( + FieldSpec.builder(namespaceIDClassName, "id") + .addModifiers(Modifier.PRIVATE, Modifier.FINAL).addAnnotation(NotNull.class).build() + ); + // static field + fluidClass.addField( + FieldSpec.builder(ArrayTypeName.of(fluidClassName), "VALUES") + .addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL) + .initializer("values()") + .build() + ); + + fluidClass.addMethod( + MethodSpec.constructorBuilder() + .addParameter(ParameterSpec.builder(namespaceIDClassName, "id").addAnnotation(NotNull.class).build()) + .addStatement("this.id = id") + .addStatement("$T.fluids.put(id, this)", registriesClassName) + .build() + ); + // Override key method (adventure) + fluidClass.addMethod( + MethodSpec.methodBuilder("key") + .returns(ClassName.get("net.kyori.adventure.key", "Key")) + .addAnnotation(Override.class) + .addAnnotation(NotNull.class) + .addStatement("return this.id") + .addModifiers(Modifier.PUBLIC) + .build() + ); + // getId method + fluidClass.addMethod( + MethodSpec.methodBuilder("getId") + .returns(TypeName.SHORT) + .addStatement("return (short) ordinal()") + .addModifiers(Modifier.PUBLIC) + .build() + ); + // getNamespaceID method + fluidClass.addMethod( + MethodSpec.methodBuilder("getNamespaceID") + .returns(namespaceIDClassName) + .addAnnotation(NotNull.class) + .addStatement("return this.id") + .addModifiers(Modifier.PUBLIC) + .build() + ); + // toString method + fluidClass.addMethod( + MethodSpec.methodBuilder("toString") + .addAnnotation(NotNull.class) + .addAnnotation(Override.class) + .returns(String.class) + // this resolves to [Namespace] + .addStatement("return \"[\" + this.id + \"]\"") + .addModifiers(Modifier.PUBLIC) + .build() + ); + + // fromId Method + fluidClass.addMethod( + MethodSpec.methodBuilder("fromId") + .returns(fluidClassName) + .addAnnotation(Nullable.class) + .addParameter(TypeName.SHORT, "id") + .beginControlFlow("if(id >= 0 && id < VALUES.length)") + .addStatement("return VALUES[id]") + .endControlFlow() + .addStatement("return null") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .build() + ); + + // Use data + fluids.entrySet().forEach(entry -> { + final String fluidName = entry.getKey(); + fluidClass.addEnumConstant(toConstant(fluidName), TypeSpec.anonymousClassBuilder( + "$T.from($S)", + namespaceIDClassName, + fluidName + ).build() + ); + }); + + // Write files to outputFolder + writeFiles( + List.of( + JavaFile.builder("net.minestom.server.fluid", fluidClass.build()) + .indent(" ") + .skipJavaLangImports(true) + .build() + ), + outputFolder + ); + } +} diff --git a/code-generators/src/main/java/net/minestom/codegen/particle/ParticleGenerator.java b/code-generators/src/main/java/net/minestom/codegen/particle/ParticleGenerator.java new file mode 100644 index 00000000000..14b13e3e0a4 --- /dev/null +++ b/code-generators/src/main/java/net/minestom/codegen/particle/ParticleGenerator.java @@ -0,0 +1,99 @@ +package net.minestom.codegen.particle; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.FieldSpec; +import com.squareup.javapoet.JavaFile; +import com.squareup.javapoet.TypeSpec; +import net.minestom.codegen.MinestomCodeGenerator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.lang.model.element.Modifier; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +public class ParticleGenerator extends MinestomCodeGenerator { + private static final Logger LOGGER = LoggerFactory.getLogger(ParticleGenerator.class); + + private final InputStream particlesFile; + private final File outputFolder; + + public ParticleGenerator(@Nullable InputStream particlesFile, @NotNull File outputFolder) { + this.particlesFile = particlesFile; + this.outputFolder = outputFolder; + } + + @Override + public void generate() { + if (particlesFile == null) { + LOGGER.error("Failed to find particles.json."); + LOGGER.error("Stopped code generation for particles."); + return; + } + if (!outputFolder.exists() && !outputFolder.mkdirs()) { + LOGGER.error("Output folder for code generation does not exist and could not be created."); + return; + } + + // Important classes we use alot + ClassName particleCN = ClassName.get("net.minestom.server.particle", "Particle"); + ClassName particleImplCN = ClassName.get("net.minestom.server.particle", "ParticleImpl"); + + JsonObject particleObject = GSON.fromJson(new InputStreamReader(particlesFile), JsonObject.class); + List> orderedParticleIdObjectEntries = particleObject.entrySet().stream() + .sorted(Comparator.comparingInt(o -> o.getValue().getAsJsonObject().get("id").getAsInt())).toList(); + + + // Start code gen + ClassName particlesCN = ClassName.get("net.minestom.server.particle", "Particles"); + TypeSpec.Builder particlesInterface = TypeSpec.interfaceBuilder(particlesCN) + .addJavadoc("AUTOGENERATED by " + getClass().getSimpleName()); + + for (Map.Entry particleIdObjectEntry : orderedParticleIdObjectEntries) { + final String key = particleIdObjectEntry.getKey(); + final JsonObject value = particleIdObjectEntry.getValue().getAsJsonObject(); + + ClassName fieldCN = particleCN; + if (value.get("hasData").getAsBoolean()) { + // This particle has data, use the particle implementation class + fieldCN = ClassName.get("net.minestom.server.particle", "Particle", + toPascalCase(key.replace("minecraft:", ""))); + } + + String cast = ""; + if (!fieldCN.equals(particleCN)) { + // This is one of the unique particle classes with particle data, cast this + cast = "(Particle." + fieldCN.simpleName() + ") "; + } + + String fieldName = key.replace("minecraft:", "").toUpperCase(); + + particlesInterface.addField(FieldSpec.builder(fieldCN, fieldName) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + .initializer("$L$T.get($S)", cast, particleImplCN, key).build()); + } + + writeFiles( + List.of(JavaFile.builder("net.minestom.server.particle", particlesInterface.build()) + .indent(" ") + .skipJavaLangImports(true) + .build()), + outputFolder); + } + + private static String toPascalCase(@NotNull String input) { + String camelCase = Pattern.compile("_([a-z])") + .matcher(input) + .replaceAll(m -> m.group(1).toUpperCase()); + return camelCase.substring(0, 1).toUpperCase() + camelCase.substring(1); + } +} diff --git a/code-generators/src/main/java/net/minestom/codegen/recipe/RecipeTypeGenerator.java b/code-generators/src/main/java/net/minestom/codegen/recipe/RecipeTypeGenerator.java new file mode 100644 index 00000000000..749dfc091aa --- /dev/null +++ b/code-generators/src/main/java/net/minestom/codegen/recipe/RecipeTypeGenerator.java @@ -0,0 +1,113 @@ +package net.minestom.codegen.recipe; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.squareup.javapoet.*; +import net.minestom.codegen.MinestomCodeGenerator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.lang.model.element.Modifier; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Comparator; +import java.util.List; +import java.util.stream.StreamSupport; + +public class RecipeTypeGenerator extends MinestomCodeGenerator { + private static final Logger LOGGER = LoggerFactory.getLogger(RecipeTypeGenerator.class); + private final InputStream recipeTypesFile; + private final File outputFolder; + + public RecipeTypeGenerator(@Nullable InputStream recipeTypesFile, @NotNull File outputFolder) { + this.recipeTypesFile = recipeTypesFile; + this.outputFolder = outputFolder; + } + + @Override + public void generate() { + if (recipeTypesFile == null) { + LOGGER.error("Failed to find recipe_types.json."); + LOGGER.error("Stopped code generation for recipe types."); + return; + } + if (!outputFolder.exists() && !outputFolder.mkdirs()) { + LOGGER.error("Output folder for code generation does not exist and could not be created."); + return; + } + + // Important classes we use alot + JsonArray recipeTypes = GSON.fromJson(new InputStreamReader(recipeTypesFile), JsonArray.class); + ClassName recipeTypeCN = ClassName.get("net.minestom.server.recipe", "RecipeType"); + TypeSpec.Builder recipeTypeEnum = TypeSpec.enumBuilder(recipeTypeCN) + .addSuperinterface(ClassName.get("net.minestom.server.registry", "StaticProtocolObject")) + .addModifiers(Modifier.PUBLIC).addJavadoc("AUTOGENERATED by " + getClass().getSimpleName()); + ClassName namespaceIdCN = ClassName.get("net.minestom.server.utils", "NamespaceID"); + + ClassName networkBufferCN = ClassName.get("net.minestom.server.network", "NetworkBuffer"); + ParameterizedTypeName networkBufferTypeCN = ParameterizedTypeName.get(networkBufferCN.nestedClass("Type"), recipeTypeCN); + + // Fields + recipeTypeEnum.addFields( + List.of( + FieldSpec.builder(networkBufferTypeCN, "NETWORK_TYPE", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + .initializer("$T.Enum($T.class)", networkBufferCN, recipeTypeCN) + .build(), + FieldSpec.builder(namespaceIdCN, "namespace", Modifier.PRIVATE, Modifier.FINAL).build() + ) + ); + + // Methods + recipeTypeEnum.addMethods( + List.of( + // Constructor + MethodSpec.constructorBuilder() + .addParameter(ParameterSpec.builder(namespaceIdCN, "namespace").addAnnotation(NotNull.class).build()) + .addStatement("this.namespace = namespace") + .build(), + MethodSpec.methodBuilder("namespace") + .addModifiers(Modifier.PUBLIC) + .addAnnotation(NotNull.class) + .addAnnotation(Override.class) + .returns(namespaceIdCN) + .addStatement("return this.namespace") + .build(), + MethodSpec.methodBuilder("id") + .addModifiers(Modifier.PUBLIC) + .returns(TypeName.INT) + .addAnnotation(Override.class) + .addStatement("return this.ordinal()") + .build() + ) + ); + + // Use data + for (JsonObject recipeTypeObject : StreamSupport.stream(recipeTypes.spliterator(), true).map(JsonElement::getAsJsonObject).sorted(Comparator.comparingInt(o -> o.get("id").getAsInt())).toList()) { + String recipeTypeName = recipeTypeObject.get("name").getAsString(); + recipeTypeEnum.addEnumConstant(recipeTypeConstantName(recipeTypeName), TypeSpec.anonymousClassBuilder( + "$T.from($S)", + namespaceIdCN, recipeTypeName + ).build() + ); + } + + // Write files to outputFolder + writeFiles( + List.of( + JavaFile.builder("net.minestom.server.recipe", recipeTypeEnum.build()) + .indent(" ") + .skipJavaLangImports(true) + .build() + ), + outputFolder + ); + } + + private static @NotNull String recipeTypeConstantName(@NotNull String name) { + return toConstant(name).replace("CRAFTING_", ""); + } +} diff --git a/demo/src/main/java/net/minestom/demo/Main.java b/demo/src/main/java/net/minestom/demo/Main.java index 6471f85641a..39c157b0913 100644 --- a/demo/src/main/java/net/minestom/demo/Main.java +++ b/demo/src/main/java/net/minestom/demo/Main.java @@ -16,12 +16,14 @@ import net.minestom.server.extras.lan.OpenToLAN; import net.minestom.server.extras.lan.OpenToLANConfig; import net.minestom.server.instance.block.BlockManager; +import net.minestom.server.item.ItemComponent; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; import net.minestom.server.network.packet.server.play.DeclareRecipesPacket; import net.minestom.server.ping.ResponseData; import net.minestom.server.recipe.RecipeCategory; import net.minestom.server.recipe.ShapedRecipe; +import net.minestom.server.recipe.ShapelessRecipe; import net.minestom.server.utils.identity.NamedAndIdentified; import net.minestom.server.utils.time.TimeUnit; import org.jetbrains.annotations.NotNull; @@ -37,7 +39,6 @@ public static void main(String[] args) { MinecraftServer.setCompressionThreshold(0); MinecraftServer minecraftServer = MinecraftServer.init(); - MinecraftServer.getBiomeManager().loadVanillaBiomes(); BlockManager blockManager = MinecraftServer.getBlockManager(); blockManager.registerBlockPlacementRule(new DripstonePlacementRule()); @@ -77,6 +78,9 @@ public static void main(String[] args) { commandManager.register(new RelightCommand()); commandManager.register(new KillCommand()); commandManager.register(new WeatherCommand()); + commandManager.register(new PotionCommand()); + commandManager.register(new CookieCommand()); + commandManager.register(new WorldBorderCommand()); commandManager.setUnknownCommandCallback((sender, command) -> sender.sendMessage(Component.text("Unknown command", NamedTextColor.RED))); @@ -133,6 +137,22 @@ public boolean shouldShow(@NotNull Player player) { } }; MinecraftServer.getRecipeManager().addRecipe(ironBlockRecipe); + var recipe = new ShapelessRecipe( + "minestom:test2", "abc", + RecipeCategory.Crafting.MISC, + List.of( + new DeclareRecipesPacket.Ingredient(List.of(ItemStack.of(Material.DIRT))) + ), + ItemStack.builder(Material.GOLD_BLOCK) + .set(ItemComponent.CUSTOM_NAME, Component.text("abc")) + .build() + ) { + @Override + public boolean shouldShow(@NotNull Player player) { + return true; + } + }; + MinecraftServer.getRecipeManager().addRecipe(recipe); PlayerInit.init(); diff --git a/demo/src/main/java/net/minestom/demo/PlayerInit.java b/demo/src/main/java/net/minestom/demo/PlayerInit.java index 0aecc87244d..65c4d2fde7a 100644 --- a/demo/src/main/java/net/minestom/demo/PlayerInit.java +++ b/demo/src/main/java/net/minestom/demo/PlayerInit.java @@ -1,6 +1,8 @@ package net.minestom.demo; +import net.kyori.adventure.sound.Sound; import net.kyori.adventure.text.Component; +import net.minestom.server.FeatureFlag; import net.minestom.server.MinecraftServer; import net.minestom.server.advancements.FrameType; import net.minestom.server.adventure.MinestomAdventure; @@ -11,42 +13,50 @@ import net.minestom.server.entity.GameMode; import net.minestom.server.entity.ItemEntity; import net.minestom.server.entity.Player; +import net.minestom.server.entity.attribute.Attribute; +import net.minestom.server.entity.attribute.AttributeModifier; +import net.minestom.server.entity.attribute.AttributeOperation; import net.minestom.server.entity.damage.Damage; import net.minestom.server.event.Event; import net.minestom.server.event.EventNode; import net.minestom.server.event.entity.EntityAttackEvent; import net.minestom.server.event.item.ItemDropEvent; import net.minestom.server.event.item.PickupItemEvent; -import net.minestom.server.event.player.AsyncPlayerConfigurationEvent; -import net.minestom.server.event.player.PlayerBlockInteractEvent; -import net.minestom.server.event.player.PlayerBlockPlaceEvent; -import net.minestom.server.event.player.PlayerDeathEvent; -import net.minestom.server.event.player.PlayerDisconnectEvent; -import net.minestom.server.event.player.PlayerPacketEvent; -import net.minestom.server.event.player.PlayerPacketOutEvent; -import net.minestom.server.event.player.PlayerSpawnEvent; -import net.minestom.server.event.player.PlayerUseItemOnBlockEvent; +import net.minestom.server.event.player.*; import net.minestom.server.event.server.ServerTickMonitorEvent; import net.minestom.server.instance.Instance; import net.minestom.server.instance.InstanceContainer; import net.minestom.server.instance.InstanceManager; import net.minestom.server.instance.LightingChunk; import net.minestom.server.instance.block.Block; +import net.minestom.server.instance.block.predicate.BlockPredicate; +import net.minestom.server.instance.block.predicate.BlockTypeFilter; import net.minestom.server.inventory.Inventory; import net.minestom.server.inventory.InventoryType; +import net.minestom.server.item.ItemComponent; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; -import net.minestom.server.item.metadata.BundleMeta; +import net.minestom.server.item.component.BlockPredicates; +import net.minestom.server.item.component.EnchantmentList; +import net.minestom.server.item.component.LodestoneTracker; +import net.minestom.server.item.component.PotionContents; +import net.minestom.server.item.enchant.Enchantment; import net.minestom.server.monitoring.BenchmarkManager; import net.minestom.server.monitoring.TickMonitor; +import net.minestom.server.network.packet.server.common.CustomReportDetailsPacket; +import net.minestom.server.network.packet.server.common.ServerLinksPacket; import net.minestom.server.notifications.Notification; +import net.minestom.server.potion.CustomPotionEffect; +import net.minestom.server.potion.PotionEffect; +import net.minestom.server.sound.SoundEvent; import net.minestom.server.utils.MathUtils; +import net.minestom.server.utils.NamespaceID; import net.minestom.server.utils.time.TimeUnit; -import net.minestom.server.world.DimensionType; import java.time.Duration; +import java.util.List; +import java.util.Map; import java.util.Random; -import java.util.Set; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicReference; @@ -94,6 +104,10 @@ public class PlayerInit { .addListener(AsyncPlayerConfigurationEvent.class, event -> { final Player player = event.getPlayer(); + // Show off adding and removing feature flags + event.addFeatureFlag(FeatureFlag.BUNDLE); + event.removeFeatureFlag(FeatureFlag.TRADE_REBALANCE); // not enabled by default, just removed for demonstration + var instances = MinecraftServer.getInstanceManager().getInstances(); Instance instance = instances.stream().skip(new Random().nextInt(instances.size())).findFirst().orElse(null); event.setSpawningInstance(instance); @@ -101,38 +115,87 @@ public class PlayerInit { int z = Math.abs(ThreadLocalRandom.current().nextInt()) % 500 - 250; player.setRespawnPoint(new Pos(0, 40f, 0)); }) + .addListener(PlayerHandAnimationEvent.class, event -> { + class A { + static boolean b = false; + } + if (A.b) { + event.getPlayer().getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).removeModifier(NamespaceID.from("test")); + } else { + event.getPlayer().getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).addModifier(new AttributeModifier(NamespaceID.from("test"), 0.5, AttributeOperation.ADD_VALUE)); + } + A.b = !A.b; + }) + .addListener(PlayerSpawnEvent.class, event -> { final Player player = event.getPlayer(); player.setGameMode(GameMode.CREATIVE); player.setPermissionLevel(4); ItemStack itemStack = ItemStack.builder(Material.STONE) .amount(64) - .meta(itemMetaBuilder -> - itemMetaBuilder.canPlaceOn(Set.of(Block.STONE)) - .canDestroy(Set.of(Block.DIAMOND_ORE))) + .set(ItemComponent.CAN_PLACE_ON, new BlockPredicates(new BlockPredicate(new BlockTypeFilter.Blocks(Block.STONE), null, null))) + .set(ItemComponent.CAN_BREAK, new BlockPredicates(new BlockPredicate(new BlockTypeFilter.Blocks(Block.DIAMOND_ORE), null, null))) .build(); player.getInventory().addItemStack(itemStack); + player.sendPacket(new CustomReportDetailsPacket(Map.of( + "hello", "world" + ))); + + player.sendPacket(new ServerLinksPacket( + new ServerLinksPacket.Entry(ServerLinksPacket.KnownLinkType.NEWS, "https://minestom.net"), + new ServerLinksPacket.Entry(ServerLinksPacket.KnownLinkType.BUG_REPORT, "https://minestom.net"), + new ServerLinksPacket.Entry(Component.text("Hello world!"), "https://minestom.net") + )); + ItemStack bundle = ItemStack.builder(Material.BUNDLE) - .meta(BundleMeta.class, bundleMetaBuilder -> { - bundleMetaBuilder.addItem(ItemStack.of(Material.DIAMOND, 5)); - bundleMetaBuilder.addItem(ItemStack.of(Material.RABBIT_FOOT, 5)); - }) + .set(ItemComponent.BUNDLE_CONTENTS, List.of( + ItemStack.of(Material.DIAMOND, 5), + ItemStack.of(Material.RABBIT_FOOT, 5) + )) .build(); player.getInventory().addItemStack(bundle); + player.getInventory().addItemStack(ItemStack.builder(Material.COMPASS) + .set(ItemComponent.LODESTONE_TRACKER, new LodestoneTracker(player.getInstance().getDimensionName(), new Vec(10, 10, 10), true)) + .build()); + + player.getInventory().addItemStack(ItemStack.builder(Material.STONE_SWORD) + .set(ItemComponent.ENCHANTMENTS, new EnchantmentList(Map.of( + Enchantment.SHARPNESS, 10 + ))) + .build()); + + player.getInventory().addItemStack(ItemStack.builder(Material.STONE_SWORD) + .build()); + + player.getInventory().addItemStack(ItemStack.builder(Material.BLACK_BANNER) + .build()); + + player.getInventory().addItemStack(ItemStack.builder(Material.POTION) + .set(ItemComponent.POTION_CONTENTS, new PotionContents(null, null, List.of( + new CustomPotionEffect(PotionEffect.JUMP_BOOST, new CustomPotionEffect.Settings((byte) 4, + 45 * 20, false, true, true, null)) + ))) + .customName(Component.text("Sharpness 10 Sword").append(Component.space()).append(Component.text("§c§l[LEGENDARY]"))) + .build()); + + if (event.isFirstSpawn()) { Notification notification = Notification.builder() .frameType(FrameType.TASK) .title(Component.text("Welcome!")) .icon(Material.IRON_SWORD).build(); notification.send(player); + + player.playSound(Sound.sound(SoundEvent.ENTITY_EXPERIENCE_ORB_PICKUP, Sound.Source.PLAYER, 0.5f, 1f)); } }) .addListener(PlayerPacketOutEvent.class, event -> { //System.out.println("out " + event.getPacket().getClass().getSimpleName()); }) .addListener(PlayerPacketEvent.class, event -> { + //System.out.println("in " + event.getPacket().getClass().getSimpleName()); }) .addListener(PlayerUseItemOnBlockEvent.class, event -> { @@ -141,9 +204,9 @@ public class PlayerInit { var itemStack = event.getItemStack(); var block = event.getInstance().getBlock(event.getPosition()); - if ("false" .equals(block.getProperty("waterlogged")) && itemStack.material().equals(Material.WATER_BUCKET)) { + if ("false".equals(block.getProperty("waterlogged")) && itemStack.material().equals(Material.WATER_BUCKET)) { block = block.withProperty("waterlogged", "true"); - } else if ("true" .equals(block.getProperty("waterlogged")) && itemStack.material().equals(Material.BUCKET)) { + } else if ("true".equals(block.getProperty("waterlogged")) && itemStack.material().equals(Material.BUCKET)) { block = block.withProperty("waterlogged", "false"); } else return; @@ -165,7 +228,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); @@ -210,7 +273,7 @@ public static void init() { BenchmarkManager benchmarkManager = MinecraftServer.getBenchmarkManager(); MinecraftServer.getSchedulerManager().buildTask(() -> { - if (MinecraftServer.getConnectionManager().getOnlinePlayerCount() != 0) + if (LAST_TICK.get() == null || MinecraftServer.getConnectionManager().getOnlinePlayerCount() == 0) return; long ramUsage = benchmarkManager.getUsedMemory(); @@ -224,6 +287,6 @@ public static void init() { .append(Component.text("ACQ TIME: " + MathUtils.round(tickMonitor.getAcquisitionTime(), 2) + "ms")); final Component footer = benchmarkManager.getCpuMonitoringMessage(); Audiences.players().sendPlayerListHeaderAndFooter(header, footer); - }).repeat(10, TimeUnit.SERVER_TICK); //.schedule(); + }).repeat(10, TimeUnit.SERVER_TICK).schedule(); } } diff --git a/demo/src/main/java/net/minestom/demo/block/CampfireHandler.java b/demo/src/main/java/net/minestom/demo/block/CampfireHandler.java index c399993e391..663c10914f6 100644 --- a/demo/src/main/java/net/minestom/demo/block/CampfireHandler.java +++ b/demo/src/main/java/net/minestom/demo/block/CampfireHandler.java @@ -1,5 +1,9 @@ package net.minestom.demo.block; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.BinaryTagTypes; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.ListBinaryTag; import net.minestom.server.instance.block.BlockHandler; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; @@ -10,10 +14,6 @@ import net.minestom.server.utils.NamespaceID; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jglrxavpok.hephaistos.nbt.NBT; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; -import org.jglrxavpok.hephaistos.nbt.NBTList; -import org.jglrxavpok.hephaistos.nbt.NBTType; import java.util.ArrayList; import java.util.Collection; @@ -22,16 +22,17 @@ public class CampfireHandler implements BlockHandler { public static final Tag> ITEMS = Tag.View(new TagSerializer<>() { - private final Tag internal = Tag.NBT("Items"); + private final Tag internal = Tag.NBT("Items"); @Override public @Nullable List read(@NotNull TagReadable reader) { - NBTList item = (NBTList) reader.getTag(internal); + ListBinaryTag item = (ListBinaryTag) reader.getTag(internal); if (item == null) return null; List result = new ArrayList<>(); - item.forEach(nbtCompound -> { - int amount = nbtCompound.getAsByte("Count"); + item.forEach(childTag -> { + CompoundBinaryTag nbtCompound = (CompoundBinaryTag) childTag; + int amount = nbtCompound.getByte("Count"); String id = nbtCompound.getString("id"); Material material = Material.fromNamespaceId(id); result.add(ItemStack.of(material, amount)); @@ -45,14 +46,14 @@ public void write(@NotNull TagWritable writer, @Nullable List value) writer.removeTag(internal); return; } - writer.setTag(internal, NBT.List( - NBTType.TAG_Compound, + writer.setTag(internal, ListBinaryTag.listBinaryTag( + BinaryTagTypes.COMPOUND, value.stream() - .map(item -> NBT.Compound(nbt -> { - nbt.setByte("Count", (byte) item.amount()); - nbt.setByte("Slot", (byte) 1); - nbt.setString("id", item.material().name()); - })) + .map(item -> (BinaryTag) CompoundBinaryTag.builder() + .putByte("Count", (byte) item.amount()) + .putByte("Slot", (byte) 1) + .putString("id", item.material().name()) + .build()) .toList() )); } diff --git a/demo/src/main/java/net/minestom/demo/commands/CookieCommand.java b/demo/src/main/java/net/minestom/demo/commands/CookieCommand.java new file mode 100644 index 00000000000..eb71677a318 --- /dev/null +++ b/demo/src/main/java/net/minestom/demo/commands/CookieCommand.java @@ -0,0 +1,63 @@ +package net.minestom.demo.commands; + +import net.minestom.server.command.CommandSender; +import net.minestom.server.command.builder.Command; +import net.minestom.server.command.builder.CommandContext; +import net.minestom.server.command.builder.arguments.Argument; +import net.minestom.server.command.builder.arguments.ArgumentType; +import net.minestom.server.entity.Player; +import org.jetbrains.annotations.NotNull; + +public class CookieCommand extends Command { + public CookieCommand() { + super("cookie"); + + addSubcommand(new Store()); + addSubcommand(new Fetch()); + } + + public static class Store extends Command { + private final Argument keyArg = ArgumentType.ResourceLocation("key"); + private final Argument valueArg = ArgumentType.StringArray("value"); + + public Store() { + super("store"); + + addSyntax(this::store, keyArg, valueArg); + } + + private void store(@NotNull CommandSender sender, @NotNull CommandContext context) { + if (!(sender instanceof Player player)) return; + + String key = context.get(keyArg); + byte[] value = String.join(" ", context.get(valueArg)).getBytes(); + + player.getPlayerConnection().storeCookie(key, value); + player.sendMessage(key + " stored"); + } + } + + public static class Fetch extends Command { + private final Argument keyArg = ArgumentType.ResourceLocation("key"); + + public Fetch() { + super("fetch"); + + addSyntax(this::fetch, keyArg); + } + + private void fetch(@NotNull CommandSender sender, @NotNull CommandContext context) { + if (!(sender instanceof Player player)) return; + + String key = context.get(keyArg); + + player.getPlayerConnection().fetchCookie(key).thenAccept(value -> { + if (value == null) { + player.sendMessage(key + ": null"); + } else { + player.sendMessage(key + ": " + new String(value)); + } + }); + } + } +} diff --git a/demo/src/main/java/net/minestom/demo/commands/DisplayCommand.java b/demo/src/main/java/net/minestom/demo/commands/DisplayCommand.java index 07b3701defd..26277a795ea 100644 --- a/demo/src/main/java/net/minestom/demo/commands/DisplayCommand.java +++ b/demo/src/main/java/net/minestom/demo/commands/DisplayCommand.java @@ -6,7 +6,6 @@ import net.minestom.server.command.builder.Command; import net.minestom.server.command.builder.CommandContext; import net.minestom.server.command.builder.arguments.ArgumentType; -import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.Entity; import net.minestom.server.entity.EntityType; import net.minestom.server.entity.Player; @@ -58,7 +57,7 @@ public void spawnBlock(@NotNull CommandSender sender, @NotNull CommandContext co var entity = new Entity(EntityType.BLOCK_DISPLAY); var meta = (BlockDisplayMeta) entity.getEntityMeta(); meta.setTransformationInterpolationDuration(20); - meta.setBlockState(Block.ORANGE_CANDLE_CAKE.stateId()); + meta.setBlockState(Block.ORANGE_CANDLE_CAKE); entity.setInstance(player.getInstance(), player.getPosition()).join(); if (context.has("follow")) { diff --git a/demo/src/main/java/net/minestom/demo/commands/PotionCommand.java b/demo/src/main/java/net/minestom/demo/commands/PotionCommand.java new file mode 100644 index 00000000000..bda604f4140 --- /dev/null +++ b/demo/src/main/java/net/minestom/demo/commands/PotionCommand.java @@ -0,0 +1,23 @@ +package net.minestom.demo.commands; + +import net.minestom.server.command.CommandSender; +import net.minestom.server.command.builder.Command; +import net.minestom.server.command.builder.CommandContext; +import net.minestom.server.command.builder.arguments.Argument; +import net.minestom.server.command.builder.arguments.ArgumentType; +import org.jetbrains.annotations.NotNull; + +public class PotionCommand extends Command { + private final Argument potionArg = ArgumentType.Resource("potion", "minecraft:potion"); + + public PotionCommand() { + super("potion"); + + addSyntax(this::potionCommand, potionArg); + } + + private void potionCommand(@NotNull CommandSender sender, @NotNull CommandContext context) { + final String potion = context.get(potionArg); + sender.sendMessage("Potion: " + potion); + } +} diff --git a/demo/src/main/java/net/minestom/demo/commands/WorldBorderCommand.java b/demo/src/main/java/net/minestom/demo/commands/WorldBorderCommand.java new file mode 100644 index 00000000000..7fc3a36ac9e --- /dev/null +++ b/demo/src/main/java/net/minestom/demo/commands/WorldBorderCommand.java @@ -0,0 +1,63 @@ +package net.minestom.demo.commands; + +import net.minestom.server.command.CommandSender; +import net.minestom.server.command.builder.Command; +import net.minestom.server.command.builder.CommandContext; +import net.minestom.server.command.builder.arguments.ArgumentType; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.coordinate.Vec; +import net.minestom.server.entity.Player; +import net.minestom.server.utils.location.RelativeVec; + +public class WorldBorderCommand extends Command { + public WorldBorderCommand() { + super("worldborder"); + + var diameterOptions = ArgumentType.Word("diameterOptions").from("set", "add"); // "center", "warning-time", "warning-distance" + var sizeInBlocks = ArgumentType.Integer("sizeInBlocks").setDefaultValue(0); + var timeInSeconds = ArgumentType.Double("timeInSeconds").setDefaultValue(0.0); + + var centerOption = ArgumentType.Word("centerOption").from("center"); + var centerCoordinate = ArgumentType.RelativeVec2("coordinate"); + + var warningTimeOption = ArgumentType.Word("warningTimeOption").from("warning-time"); + + var warningDistanceOption = ArgumentType.Word("warningDistanceOption").from("warning-distance"); + + addSyntax(this::handleDiameter, diameterOptions, sizeInBlocks, timeInSeconds); + addSyntax(this::handleCenter, centerOption, centerCoordinate); + addSyntax(this::handleWarningTime, warningTimeOption, timeInSeconds); + addSyntax(this::handleWarningDistance, warningDistanceOption, sizeInBlocks); + } + + private void handleDiameter(CommandSender source, CommandContext context) { + Player player = (Player) source; + int size = context.get("sizeInBlocks"); + double timeInSeconds = context.get("timeInSeconds"); + double diameter = size; + if ((context.get("diameterOptions")).equals("add")) { + diameter += player.getInstance().getWorldBorder().diameter(); + } + + player.getInstance().setWorldBorder(player.getInstance().getWorldBorder().withDiameter(diameter), timeInSeconds); + } + + private void handleCenter(CommandSender source, CommandContext context) { + Player player = (Player) source; + RelativeVec coords = context.get("coordinate"); + Vec vec = coords.from(new Pos(0, 0, 0)); + player.getInstance().setWorldBorder(player.getInstance().getWorldBorder().withCenter(vec.x(), vec.z())); + } + + private void handleWarningTime(CommandSender source, CommandContext context) { + Player player = (Player) source; + double timeInSeconds = context.get("timeInSeconds"); + player.getInstance().setWorldBorder(player.getInstance().getWorldBorder().withWarningTime((int)timeInSeconds)); + } + + private void handleWarningDistance(CommandSender source, CommandContext context) { + Player player = (Player) source; + int sizeInBlocks = context.get("sizeInBlocks"); + player.getInstance().setWorldBorder(player.getInstance().getWorldBorder().withWarningDistance(sizeInBlocks)); + } +} diff --git a/demo/src/main/java/net/minestom/demo/entity/ChickenCreature.java b/demo/src/main/java/net/minestom/demo/entity/ChickenCreature.java index 27a3d6be82b..14ea776f0c7 100644 --- a/demo/src/main/java/net/minestom/demo/entity/ChickenCreature.java +++ b/demo/src/main/java/net/minestom/demo/entity/ChickenCreature.java @@ -1,9 +1,9 @@ package net.minestom.demo.entity; -import net.minestom.server.attribute.VanillaAttribute; import net.minestom.server.entity.EntityCreature; import net.minestom.server.entity.EntityType; import net.minestom.server.entity.ai.goal.RandomStrollGoal; +import net.minestom.server.entity.attribute.Attribute; import java.util.List; @@ -35,7 +35,7 @@ public ChickenCreature() { // .build() // ); - getAttribute(VanillaAttribute.GENERIC_MOVEMENT_SPEED).setBaseValue(0.1f); + getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).setBaseValue(0.1); } @Override diff --git a/jmh-benchmarks/build.gradle.kts b/jmh-benchmarks/build.gradle.kts index 589b3fbbb81..e824fd81ac7 100644 --- a/jmh-benchmarks/build.gradle.kts +++ b/jmh-benchmarks/build.gradle.kts @@ -1,10 +1,9 @@ plugins { - id("me.champeau.jmh") version ("0.6.6") - id("minestom.common-conventions") + id("me.champeau.jmh") version ("0.7.2") } dependencies { jmhImplementation(rootProject) jmh(libs.jmh.core) jmhAnnotationProcessor(libs.jmh.annotationprocessor) -} \ No newline at end of file +} diff --git a/jmh-benchmarks/src/jmh/java/net/minestom/server/command/CommandBenchmark.java b/jmh-benchmarks/src/jmh/java/net/minestom/server/command/CommandBenchmark.java index 27faee7edaa..49050cf1cd1 100644 --- a/jmh-benchmarks/src/jmh/java/net/minestom/server/command/CommandBenchmark.java +++ b/jmh-benchmarks/src/jmh/java/net/minestom/server/command/CommandBenchmark.java @@ -4,15 +4,10 @@ import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.infra.Blackhole; -import java.lang.String; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.Function; -import static net.minestom.server.command.builder.arguments.ArgumentType.Double; -import static net.minestom.server.command.builder.arguments.ArgumentType.Float; -import static net.minestom.server.command.builder.arguments.ArgumentType.Integer; -import static net.minestom.server.command.builder.arguments.ArgumentType.Long; import static net.minestom.server.command.builder.arguments.ArgumentType.*; @BenchmarkMode(Mode.AverageTime) @@ -28,8 +23,8 @@ public class CommandBenchmark { public void setup() { var graph = Graph.merge(Set.of( new Command("tp", "teleport") {{ - addSyntax((sender, context) -> {}, Potion("pos")); - addSyntax((sender, context) -> {}, Entity("entity"), Potion("pos")); + addSyntax((sender, context) -> {}, RelativeVec3("pos")); + addSyntax((sender, context) -> {}, Entity("entity"), RelativeVec3("pos")); }}, new Command("setblock", "set") {{ addSyntax((sender, context) -> {}, RelativeBlockPosition("pos"), BlockState("block")); @@ -56,7 +51,7 @@ public void setup() { }} )); final CommandParser commandParser = CommandParser.parser(); - this.parser = input -> commandParser.parse(graph, input); + this.parser = input -> commandParser.parse(null, graph, input); } @Benchmark diff --git a/settings.gradle.kts b/settings.gradle.kts index db75c2599c8..b306354c05e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,18 +1,15 @@ dependencyResolutionManagement { repositories { maven("https://s01.oss.sonatype.org/content/repositories/snapshots") - maven("https://s01.oss.sonatype.org/content/groups/public/") maven("https://jitpack.io") mavenCentral() } versionCatalogs { create("libs") { // Important dependencies - version("adventure", "4.15.0") + version("adventure", "4.17.0") version("kotlin", "2.0.0") - version("hydrazine", "1.7.2") - version("data", "1.20.4-rv10") - version("hephaistos", "2.6.1") + version("data", "1.21-rv3") version("jetbrainsAnnotations", "24.1.0") version("logback", "1.4.5") version("slf4j", "2.0.7") @@ -55,6 +52,7 @@ dependencyResolutionManagement { // Libs library("adventure-api", "net.kyori", "adventure-api").versionRef("adventure") + library("adventure-nbt", "net.kyori", "adventure-nbt").versionRef("adventure") library("adventure-serializer-gson", "net.kyori", "adventure-text-serializer-gson").versionRef("adventure") library("adventure-serializer-legacy", "net.kyori", "adventure-text-serializer-legacy").versionRef("adventure") library("adventure-serializer-plain", "net.kyori", "adventure-text-serializer-plain").versionRef("adventure") @@ -71,8 +69,7 @@ dependencyResolutionManagement { library("kotlin-stdlib-jdk8", "org.jetbrains.kotlin", "kotlin-stdlib-jdk8").versionRef("kotlin") // Miscellaneous - library("hydrazine", "com.github.MadMartian", "hydrazine-path-finding").versionRef("hydrazine") - library("minestomData", "net.onelitefeather.microtus", "data").versionRef("data") + library("minestomData", "net.minestom", "data").versionRef("data") library("jetbrainsAnnotations", "org.jetbrains", "annotations").versionRef("jetbrainsAnnotations"); // Logging @@ -121,7 +118,7 @@ dependencyResolutionManagement { bundle("kotlin", listOf("kotlin-stdlib-jdk8", "kotlin-reflect")) bundle("flare", listOf("flare", "flare-fastutil")) - bundle("adventure", listOf("adventure-api", "adventure-mini-message", "adventure-serializer-gson", "adventure-serializer-legacy", "adventure-serializer-plain", "adventure-text-logger-slf4j")) + bundle("adventure", listOf("adventure-api", "adventure-nbt", "adventure-mini-message", "adventure-serializer-gson", "adventure-serializer-legacy", "adventure-serializer-plain", "adventure-text-logger-slf4j")) bundle("logging", listOf("tinylog-api", "tinylog-impl", "tinylog-slf4j")) bundle("terminal", listOf("jline", "jline-jansi")) bundle("logback", listOf("logback-core", "logback-classic")) @@ -146,7 +143,7 @@ pluginManagement { rootProject.name = "Microtus" include("code-generators") include("jmh-benchmarks") -include("jcstress-tests") -include("demo") +// include("jcstress-tests") include("testing") include("bom") +include("demo") diff --git a/src/autogenerated/java/net/minestom/server/featureflag/FeatureFlags.java b/src/autogenerated/java/net/minestom/server/FeatureFlags.java similarity index 73% rename from src/autogenerated/java/net/minestom/server/featureflag/FeatureFlags.java rename to src/autogenerated/java/net/minestom/server/FeatureFlags.java index 1f92c8da724..8122bd62be7 100644 --- a/src/autogenerated/java/net/minestom/server/featureflag/FeatureFlags.java +++ b/src/autogenerated/java/net/minestom/server/FeatureFlags.java @@ -1,12 +1,10 @@ -package net.minestom.server.featureflag; +package net.minestom.server; /** * Code autogenerated, do not edit! */ @SuppressWarnings("unused") interface FeatureFlags { - FeatureFlag UPDATE_1_21 = FeatureFlagImpl.get("minecraft:update_1_21"); - FeatureFlag BUNDLE = FeatureFlagImpl.get("minecraft:bundle"); FeatureFlag VANILLA = FeatureFlagImpl.get("minecraft:vanilla"); diff --git a/src/autogenerated/java/net/minestom/server/MinecraftConstants.java b/src/autogenerated/java/net/minestom/server/MinecraftConstants.java new file mode 100644 index 00000000000..47ac87d07df --- /dev/null +++ b/src/autogenerated/java/net/minestom/server/MinecraftConstants.java @@ -0,0 +1,16 @@ +package net.minestom.server; + +/** + * AUTOGENERATED by ConstantsGenerator + */ +interface MinecraftConstants { + String VERSION_NAME = "1.21"; + + int PROTOCOL_VERSION = 767; + + int DATA_VERSION = 3953; + + int RESOURCE_PACK_VERSION = 34; + + int DATA_PACK_VERSION = 48; +} diff --git a/src/autogenerated/java/net/minestom/server/attribute/Attributes.java b/src/autogenerated/java/net/minestom/server/attribute/Attributes.java deleted file mode 100644 index b0e0b870ac7..00000000000 --- a/src/autogenerated/java/net/minestom/server/attribute/Attributes.java +++ /dev/null @@ -1,35 +0,0 @@ -package net.minestom.server.attribute; - -/** - * Code autogenerated, do not edit! - */ -@SuppressWarnings("unused") -interface Attributes { - Attribute GENERIC_ARMOR = AttributeImpl.get("minecraft:generic.armor"); - - Attribute GENERIC_ARMOR_TOUGHNESS = AttributeImpl.get("minecraft:generic.armor_toughness"); - - Attribute GENERIC_ATTACK_DAMAGE = AttributeImpl.get("minecraft:generic.attack_damage"); - - Attribute GENERIC_ATTACK_KNOCKBACK = AttributeImpl.get("minecraft:generic.attack_knockback"); - - Attribute GENERIC_ATTACK_SPEED = AttributeImpl.get("minecraft:generic.attack_speed"); - - Attribute GENERIC_FLYING_SPEED = AttributeImpl.get("minecraft:generic.flying_speed"); - - Attribute GENERIC_FOLLOW_RANGE = AttributeImpl.get("minecraft:generic.follow_range"); - - Attribute HORSE_JUMP_STRENGTH = AttributeImpl.get("minecraft:horse.jump_strength"); - - Attribute GENERIC_KNOCKBACK_RESISTANCE = AttributeImpl.get("minecraft:generic.knockback_resistance"); - - Attribute GENERIC_LUCK = AttributeImpl.get("minecraft:generic.luck"); - - Attribute GENERIC_MAX_ABSORPTION = AttributeImpl.get("minecraft:generic.max_absorption"); - - Attribute GENERIC_MAX_HEALTH = AttributeImpl.get("minecraft:generic.max_health"); - - Attribute GENERIC_MOVEMENT_SPEED = AttributeImpl.get("minecraft:generic.movement_speed"); - - Attribute ZOMBIE_SPAWN_REINFORCEMENTS = AttributeImpl.get("minecraft:zombie.spawn_reinforcements"); -} diff --git a/src/autogenerated/java/net/minestom/server/color/DyeColor.java b/src/autogenerated/java/net/minestom/server/color/DyeColor.java index 36f5a62db63..b5649ca07bd 100644 --- a/src/autogenerated/java/net/minestom/server/color/DyeColor.java +++ b/src/autogenerated/java/net/minestom/server/color/DyeColor.java @@ -1,6 +1,8 @@ package net.minestom.server.color; import net.kyori.adventure.util.RGBLike; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.nbt.BinaryTagSerializer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,6 +42,10 @@ public enum DyeColor implements RGBLike { BLACK(new Color(0x1d1d21), new Color(0x0), new Color(0x1e1b1b), 29); + public static final NetworkBuffer.Type NETWORK_TYPE = NetworkBuffer.Enum(DyeColor.class); + + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.fromEnumStringable(DyeColor.class); + private static final DyeColor[] VALUES = DyeColor.values(); private final Color textureDiffuseColor; diff --git a/src/autogenerated/java/net/minestom/server/entity/EntityTypes.java b/src/autogenerated/java/net/minestom/server/entity/EntityTypes.java index 9c01f525319..0574ffcf23b 100644 --- a/src/autogenerated/java/net/minestom/server/entity/EntityTypes.java +++ b/src/autogenerated/java/net/minestom/server/entity/EntityTypes.java @@ -9,6 +9,8 @@ interface EntityTypes { EntityType AREA_EFFECT_CLOUD = EntityTypeImpl.get("minecraft:area_effect_cloud"); + EntityType ARMADILLO = EntityTypeImpl.get("minecraft:armadillo"); + EntityType ARMOR_STAND = EntityTypeImpl.get("minecraft:armor_stand"); EntityType ARROW = EntityTypeImpl.get("minecraft:arrow"); @@ -25,8 +27,12 @@ interface EntityTypes { EntityType BOAT = EntityTypeImpl.get("minecraft:boat"); + EntityType BOGGED = EntityTypeImpl.get("minecraft:bogged"); + EntityType BREEZE = EntityTypeImpl.get("minecraft:breeze"); + EntityType BREEZE_WIND_CHARGE = EntityTypeImpl.get("minecraft:breeze_wind_charge"); + EntityType CAMEL = EntityTypeImpl.get("minecraft:camel"); EntityType CAT = EntityTypeImpl.get("minecraft:cat"); @@ -121,6 +127,8 @@ interface EntityTypes { EntityType ITEM_FRAME = EntityTypeImpl.get("minecraft:item_frame"); + EntityType OMINOUS_ITEM_SPAWNER = EntityTypeImpl.get("minecraft:ominous_item_spawner"); + EntityType FIREBALL = EntityTypeImpl.get("minecraft:fireball"); EntityType LEASH_KNOT = EntityTypeImpl.get("minecraft:leash_knot"); diff --git a/src/autogenerated/java/net/minestom/server/entity/attribute/Attributes.java b/src/autogenerated/java/net/minestom/server/entity/attribute/Attributes.java new file mode 100644 index 00000000000..e660d3b2927 --- /dev/null +++ b/src/autogenerated/java/net/minestom/server/entity/attribute/Attributes.java @@ -0,0 +1,69 @@ +package net.minestom.server.entity.attribute; + +/** + * Code autogenerated, do not edit! + */ +@SuppressWarnings("unused") +interface Attributes { + Attribute GENERIC_ARMOR = AttributeImpl.get("minecraft:generic.armor"); + + Attribute GENERIC_ARMOR_TOUGHNESS = AttributeImpl.get("minecraft:generic.armor_toughness"); + + Attribute GENERIC_ATTACK_DAMAGE = AttributeImpl.get("minecraft:generic.attack_damage"); + + Attribute GENERIC_ATTACK_KNOCKBACK = AttributeImpl.get("minecraft:generic.attack_knockback"); + + Attribute GENERIC_ATTACK_SPEED = AttributeImpl.get("minecraft:generic.attack_speed"); + + Attribute PLAYER_BLOCK_BREAK_SPEED = AttributeImpl.get("minecraft:player.block_break_speed"); + + Attribute PLAYER_BLOCK_INTERACTION_RANGE = AttributeImpl.get("minecraft:player.block_interaction_range"); + + Attribute GENERIC_BURNING_TIME = AttributeImpl.get("minecraft:generic.burning_time"); + + Attribute GENERIC_EXPLOSION_KNOCKBACK_RESISTANCE = AttributeImpl.get("minecraft:generic.explosion_knockback_resistance"); + + Attribute PLAYER_ENTITY_INTERACTION_RANGE = AttributeImpl.get("minecraft:player.entity_interaction_range"); + + Attribute GENERIC_FALL_DAMAGE_MULTIPLIER = AttributeImpl.get("minecraft:generic.fall_damage_multiplier"); + + Attribute GENERIC_FLYING_SPEED = AttributeImpl.get("minecraft:generic.flying_speed"); + + Attribute GENERIC_FOLLOW_RANGE = AttributeImpl.get("minecraft:generic.follow_range"); + + Attribute GENERIC_GRAVITY = AttributeImpl.get("minecraft:generic.gravity"); + + Attribute GENERIC_JUMP_STRENGTH = AttributeImpl.get("minecraft:generic.jump_strength"); + + Attribute GENERIC_KNOCKBACK_RESISTANCE = AttributeImpl.get("minecraft:generic.knockback_resistance"); + + Attribute GENERIC_LUCK = AttributeImpl.get("minecraft:generic.luck"); + + Attribute GENERIC_MAX_ABSORPTION = AttributeImpl.get("minecraft:generic.max_absorption"); + + Attribute GENERIC_MAX_HEALTH = AttributeImpl.get("minecraft:generic.max_health"); + + Attribute PLAYER_MINING_EFFICIENCY = AttributeImpl.get("minecraft:player.mining_efficiency"); + + Attribute GENERIC_MOVEMENT_EFFICIENCY = AttributeImpl.get("minecraft:generic.movement_efficiency"); + + Attribute GENERIC_MOVEMENT_SPEED = AttributeImpl.get("minecraft:generic.movement_speed"); + + Attribute GENERIC_OXYGEN_BONUS = AttributeImpl.get("minecraft:generic.oxygen_bonus"); + + Attribute GENERIC_SAFE_FALL_DISTANCE = AttributeImpl.get("minecraft:generic.safe_fall_distance"); + + Attribute GENERIC_SCALE = AttributeImpl.get("minecraft:generic.scale"); + + Attribute PLAYER_SNEAKING_SPEED = AttributeImpl.get("minecraft:player.sneaking_speed"); + + Attribute ZOMBIE_SPAWN_REINFORCEMENTS = AttributeImpl.get("minecraft:zombie.spawn_reinforcements"); + + Attribute GENERIC_STEP_HEIGHT = AttributeImpl.get("minecraft:generic.step_height"); + + Attribute PLAYER_SUBMERGED_MINING_SPEED = AttributeImpl.get("minecraft:player.submerged_mining_speed"); + + Attribute PLAYER_SWEEPING_DAMAGE_RATIO = AttributeImpl.get("minecraft:player.sweeping_damage_ratio"); + + Attribute GENERIC_WATER_MOVEMENT_EFFICIENCY = AttributeImpl.get("minecraft:generic.water_movement_efficiency"); +} diff --git a/src/autogenerated/java/net/minestom/server/entity/damage/DamageTypes.java b/src/autogenerated/java/net/minestom/server/entity/damage/DamageTypes.java index b1b11a6bf4c..806d7a3004f 100644 --- a/src/autogenerated/java/net/minestom/server/entity/damage/DamageTypes.java +++ b/src/autogenerated/java/net/minestom/server/entity/damage/DamageTypes.java @@ -1,95 +1,103 @@ package net.minestom.server.entity.damage; +import net.minestom.server.registry.DynamicRegistry; + /** * Code autogenerated, do not edit! */ @SuppressWarnings("unused") interface DamageTypes { - DamageType WITHER = DamageTypeImpl.get("minecraft:wither"); + DynamicRegistry.Key WITHER = DynamicRegistry.Key.of("minecraft:wither"); + + DynamicRegistry.Key SONIC_BOOM = DynamicRegistry.Key.of("minecraft:sonic_boom"); + + DynamicRegistry.Key WITHER_SKULL = DynamicRegistry.Key.of("minecraft:wither_skull"); + + DynamicRegistry.Key DRY_OUT = DynamicRegistry.Key.of("minecraft:dry_out"); - DamageType SONIC_BOOM = DamageTypeImpl.get("minecraft:sonic_boom"); + DynamicRegistry.Key TRIDENT = DynamicRegistry.Key.of("minecraft:trident"); - DamageType WITHER_SKULL = DamageTypeImpl.get("minecraft:wither_skull"); + DynamicRegistry.Key ON_FIRE = DynamicRegistry.Key.of("minecraft:on_fire"); - DamageType DRY_OUT = DamageTypeImpl.get("minecraft:dry_out"); + DynamicRegistry.Key FALL = DynamicRegistry.Key.of("minecraft:fall"); - DamageType TRIDENT = DamageTypeImpl.get("minecraft:trident"); + DynamicRegistry.Key MOB_ATTACK = DynamicRegistry.Key.of("minecraft:mob_attack"); - DamageType ON_FIRE = DamageTypeImpl.get("minecraft:on_fire"); + DynamicRegistry.Key MOB_PROJECTILE = DynamicRegistry.Key.of("minecraft:mob_projectile"); - DamageType FALL = DamageTypeImpl.get("minecraft:fall"); + DynamicRegistry.Key CAMPFIRE = DynamicRegistry.Key.of("minecraft:campfire"); - DamageType MOB_ATTACK = DamageTypeImpl.get("minecraft:mob_attack"); + DynamicRegistry.Key THROWN = DynamicRegistry.Key.of("minecraft:thrown"); - DamageType MOB_PROJECTILE = DamageTypeImpl.get("minecraft:mob_projectile"); + DynamicRegistry.Key FALLING_STALACTITE = DynamicRegistry.Key.of("minecraft:falling_stalactite"); - DamageType THROWN = DamageTypeImpl.get("minecraft:thrown"); + DynamicRegistry.Key FIREBALL = DynamicRegistry.Key.of("minecraft:fireball"); - DamageType FALLING_STALACTITE = DamageTypeImpl.get("minecraft:falling_stalactite"); + DynamicRegistry.Key FALLING_BLOCK = DynamicRegistry.Key.of("minecraft:falling_block"); - DamageType FIREBALL = DamageTypeImpl.get("minecraft:fireball"); + DynamicRegistry.Key WIND_CHARGE = DynamicRegistry.Key.of("minecraft:wind_charge"); - DamageType FALLING_BLOCK = DamageTypeImpl.get("minecraft:falling_block"); + DynamicRegistry.Key PLAYER_EXPLOSION = DynamicRegistry.Key.of("minecraft:player_explosion"); - DamageType PLAYER_EXPLOSION = DamageTypeImpl.get("minecraft:player_explosion"); + DynamicRegistry.Key SPIT = DynamicRegistry.Key.of("minecraft:spit"); - DamageType STING = DamageTypeImpl.get("minecraft:sting"); + DynamicRegistry.Key STING = DynamicRegistry.Key.of("minecraft:sting"); - DamageType UNATTRIBUTED_FIREBALL = DamageTypeImpl.get("minecraft:unattributed_fireball"); + DynamicRegistry.Key UNATTRIBUTED_FIREBALL = DynamicRegistry.Key.of("minecraft:unattributed_fireball"); - DamageType IN_WALL = DamageTypeImpl.get("minecraft:in_wall"); + DynamicRegistry.Key IN_WALL = DynamicRegistry.Key.of("minecraft:in_wall"); - DamageType IN_FIRE = DamageTypeImpl.get("minecraft:in_fire"); + DynamicRegistry.Key IN_FIRE = DynamicRegistry.Key.of("minecraft:in_fire"); - DamageType ARROW = DamageTypeImpl.get("minecraft:arrow"); + DynamicRegistry.Key ARROW = DynamicRegistry.Key.of("minecraft:arrow"); - DamageType HOT_FLOOR = DamageTypeImpl.get("minecraft:hot_floor"); + DynamicRegistry.Key HOT_FLOOR = DynamicRegistry.Key.of("minecraft:hot_floor"); - DamageType DROWN = DamageTypeImpl.get("minecraft:drown"); + DynamicRegistry.Key DROWN = DynamicRegistry.Key.of("minecraft:drown"); - DamageType STARVE = DamageTypeImpl.get("minecraft:starve"); + DynamicRegistry.Key STARVE = DynamicRegistry.Key.of("minecraft:starve"); - DamageType GENERIC_KILL = DamageTypeImpl.get("minecraft:generic_kill"); + DynamicRegistry.Key GENERIC_KILL = DynamicRegistry.Key.of("minecraft:generic_kill"); - DamageType DRAGON_BREATH = DamageTypeImpl.get("minecraft:dragon_breath"); + DynamicRegistry.Key DRAGON_BREATH = DynamicRegistry.Key.of("minecraft:dragon_breath"); - DamageType MOB_ATTACK_NO_AGGRO = DamageTypeImpl.get("minecraft:mob_attack_no_aggro"); + DynamicRegistry.Key MOB_ATTACK_NO_AGGRO = DynamicRegistry.Key.of("minecraft:mob_attack_no_aggro"); - DamageType LAVA = DamageTypeImpl.get("minecraft:lava"); + DynamicRegistry.Key LAVA = DynamicRegistry.Key.of("minecraft:lava"); - DamageType OUTSIDE_BORDER = DamageTypeImpl.get("minecraft:outside_border"); + DynamicRegistry.Key OUTSIDE_BORDER = DynamicRegistry.Key.of("minecraft:outside_border"); - DamageType FLY_INTO_WALL = DamageTypeImpl.get("minecraft:fly_into_wall"); + DynamicRegistry.Key FLY_INTO_WALL = DynamicRegistry.Key.of("minecraft:fly_into_wall"); - DamageType LIGHTNING_BOLT = DamageTypeImpl.get("minecraft:lightning_bolt"); + DynamicRegistry.Key LIGHTNING_BOLT = DynamicRegistry.Key.of("minecraft:lightning_bolt"); - DamageType PLAYER_ATTACK = DamageTypeImpl.get("minecraft:player_attack"); + DynamicRegistry.Key PLAYER_ATTACK = DynamicRegistry.Key.of("minecraft:player_attack"); - DamageType FREEZE = DamageTypeImpl.get("minecraft:freeze"); + DynamicRegistry.Key FREEZE = DynamicRegistry.Key.of("minecraft:freeze"); - DamageType FALLING_ANVIL = DamageTypeImpl.get("minecraft:falling_anvil"); + DynamicRegistry.Key FALLING_ANVIL = DynamicRegistry.Key.of("minecraft:falling_anvil"); - DamageType OUT_OF_WORLD = DamageTypeImpl.get("minecraft:out_of_world"); + DynamicRegistry.Key OUT_OF_WORLD = DynamicRegistry.Key.of("minecraft:out_of_world"); - DamageType MAGIC = DamageTypeImpl.get("minecraft:magic"); + DynamicRegistry.Key MAGIC = DynamicRegistry.Key.of("minecraft:magic"); - DamageType SWEET_BERRY_BUSH = DamageTypeImpl.get("minecraft:sweet_berry_bush"); + DynamicRegistry.Key SWEET_BERRY_BUSH = DynamicRegistry.Key.of("minecraft:sweet_berry_bush"); - DamageType FIREWORKS = DamageTypeImpl.get("minecraft:fireworks"); + DynamicRegistry.Key FIREWORKS = DynamicRegistry.Key.of("minecraft:fireworks"); - DamageType EXPLOSION = DamageTypeImpl.get("minecraft:explosion"); + DynamicRegistry.Key EXPLOSION = DynamicRegistry.Key.of("minecraft:explosion"); - DamageType BAD_RESPAWN_POINT = DamageTypeImpl.get("minecraft:bad_respawn_point"); + DynamicRegistry.Key BAD_RESPAWN_POINT = DynamicRegistry.Key.of("minecraft:bad_respawn_point"); - DamageType STALAGMITE = DamageTypeImpl.get("minecraft:stalagmite"); + DynamicRegistry.Key STALAGMITE = DynamicRegistry.Key.of("minecraft:stalagmite"); - DamageType THORNS = DamageTypeImpl.get("minecraft:thorns"); + DynamicRegistry.Key THORNS = DynamicRegistry.Key.of("minecraft:thorns"); - DamageType INDIRECT_MAGIC = DamageTypeImpl.get("minecraft:indirect_magic"); + DynamicRegistry.Key INDIRECT_MAGIC = DynamicRegistry.Key.of("minecraft:indirect_magic"); - DamageType CRAMMING = DamageTypeImpl.get("minecraft:cramming"); + DynamicRegistry.Key CRAMMING = DynamicRegistry.Key.of("minecraft:cramming"); - DamageType CACTUS = DamageTypeImpl.get("minecraft:cactus"); + DynamicRegistry.Key CACTUS = DynamicRegistry.Key.of("minecraft:cactus"); - DamageType GENERIC = DamageTypeImpl.get("minecraft:generic"); + DynamicRegistry.Key GENERIC = DynamicRegistry.Key.of("minecraft:generic"); } diff --git a/src/autogenerated/java/net/minestom/server/entity/metadata/animal/tameable/WolfVariants.java b/src/autogenerated/java/net/minestom/server/entity/metadata/animal/tameable/WolfVariants.java new file mode 100644 index 00000000000..63ee18b225a --- /dev/null +++ b/src/autogenerated/java/net/minestom/server/entity/metadata/animal/tameable/WolfVariants.java @@ -0,0 +1,27 @@ +package net.minestom.server.entity.metadata.animal.tameable; + +import net.minestom.server.registry.DynamicRegistry; + +/** + * Code autogenerated, do not edit! + */ +@SuppressWarnings("unused") +interface WolfVariants { + DynamicRegistry.Key BLACK = DynamicRegistry.Key.of("minecraft:black"); + + DynamicRegistry.Key CHESTNUT = DynamicRegistry.Key.of("minecraft:chestnut"); + + DynamicRegistry.Key SNOWY = DynamicRegistry.Key.of("minecraft:snowy"); + + DynamicRegistry.Key STRIPED = DynamicRegistry.Key.of("minecraft:striped"); + + DynamicRegistry.Key ASHEN = DynamicRegistry.Key.of("minecraft:ashen"); + + DynamicRegistry.Key SPOTTED = DynamicRegistry.Key.of("minecraft:spotted"); + + DynamicRegistry.Key RUSTY = DynamicRegistry.Key.of("minecraft:rusty"); + + DynamicRegistry.Key WOODS = DynamicRegistry.Key.of("minecraft:woods"); + + DynamicRegistry.Key PALE = DynamicRegistry.Key.of("minecraft:pale"); +} diff --git a/src/autogenerated/java/net/minestom/server/entity/metadata/other/PaintingVariants.java b/src/autogenerated/java/net/minestom/server/entity/metadata/other/PaintingVariants.java new file mode 100644 index 00000000000..80ceffc3da9 --- /dev/null +++ b/src/autogenerated/java/net/minestom/server/entity/metadata/other/PaintingVariants.java @@ -0,0 +1,109 @@ +package net.minestom.server.entity.metadata.other; + +import net.minestom.server.registry.DynamicRegistry; + +/** + * Code autogenerated, do not edit! + */ +@SuppressWarnings("unused") +interface PaintingVariants { + DynamicRegistry.Key BOUQUET = DynamicRegistry.Key.of("minecraft:bouquet"); + + DynamicRegistry.Key WITHER = DynamicRegistry.Key.of("minecraft:wither"); + + DynamicRegistry.Key ALBAN = DynamicRegistry.Key.of("minecraft:alban"); + + DynamicRegistry.Key UNPACKED = DynamicRegistry.Key.of("minecraft:unpacked"); + + DynamicRegistry.Key AZTEC = DynamicRegistry.Key.of("minecraft:aztec"); + + DynamicRegistry.Key MATCH = DynamicRegistry.Key.of("minecraft:match"); + + DynamicRegistry.Key FINDING = DynamicRegistry.Key.of("minecraft:finding"); + + DynamicRegistry.Key BAROQUE = DynamicRegistry.Key.of("minecraft:baroque"); + + DynamicRegistry.Key ENDBOSS = DynamicRegistry.Key.of("minecraft:endboss"); + + DynamicRegistry.Key ORB = DynamicRegistry.Key.of("minecraft:orb"); + + DynamicRegistry.Key AZTEC2 = DynamicRegistry.Key.of("minecraft:aztec2"); + + DynamicRegistry.Key SUNFLOWERS = DynamicRegistry.Key.of("minecraft:sunflowers"); + + DynamicRegistry.Key CHANGING = DynamicRegistry.Key.of("minecraft:changing"); + + DynamicRegistry.Key WASTELAND = DynamicRegistry.Key.of("minecraft:wasteland"); + + DynamicRegistry.Key DONKEY_KONG = DynamicRegistry.Key.of("minecraft:donkey_kong"); + + DynamicRegistry.Key BUST = DynamicRegistry.Key.of("minecraft:bust"); + + DynamicRegistry.Key POOL = DynamicRegistry.Key.of("minecraft:pool"); + + DynamicRegistry.Key PIGSCENE = DynamicRegistry.Key.of("minecraft:pigscene"); + + DynamicRegistry.Key BURNING_SKULL = DynamicRegistry.Key.of("minecraft:burning_skull"); + + DynamicRegistry.Key FIRE = DynamicRegistry.Key.of("minecraft:fire"); + + DynamicRegistry.Key SEA = DynamicRegistry.Key.of("minecraft:sea"); + + DynamicRegistry.Key SUNSET = DynamicRegistry.Key.of("minecraft:sunset"); + + DynamicRegistry.Key POND = DynamicRegistry.Key.of("minecraft:pond"); + + DynamicRegistry.Key HUMBLE = DynamicRegistry.Key.of("minecraft:humble"); + + DynamicRegistry.Key TIDES = DynamicRegistry.Key.of("minecraft:tides"); + + DynamicRegistry.Key PRAIRIE_RIDE = DynamicRegistry.Key.of("minecraft:prairie_ride"); + + DynamicRegistry.Key FERN = DynamicRegistry.Key.of("minecraft:fern"); + + DynamicRegistry.Key PASSAGE = DynamicRegistry.Key.of("minecraft:passage"); + + DynamicRegistry.Key LOWMIST = DynamicRegistry.Key.of("minecraft:lowmist"); + + DynamicRegistry.Key COURBET = DynamicRegistry.Key.of("minecraft:courbet"); + + DynamicRegistry.Key WANDERER = DynamicRegistry.Key.of("minecraft:wanderer"); + + DynamicRegistry.Key BACKYARD = DynamicRegistry.Key.of("minecraft:backyard"); + + DynamicRegistry.Key EARTH = DynamicRegistry.Key.of("minecraft:earth"); + + DynamicRegistry.Key SKULL_AND_ROSES = DynamicRegistry.Key.of("minecraft:skull_and_roses"); + + DynamicRegistry.Key CAVEBIRD = DynamicRegistry.Key.of("minecraft:cavebird"); + + DynamicRegistry.Key FIGHTERS = DynamicRegistry.Key.of("minecraft:fighters"); + + DynamicRegistry.Key COTAN = DynamicRegistry.Key.of("minecraft:cotan"); + + DynamicRegistry.Key CREEBET = DynamicRegistry.Key.of("minecraft:creebet"); + + DynamicRegistry.Key OWLEMONS = DynamicRegistry.Key.of("minecraft:owlemons"); + + DynamicRegistry.Key GRAHAM = DynamicRegistry.Key.of("minecraft:graham"); + + DynamicRegistry.Key MEDITATIVE = DynamicRegistry.Key.of("minecraft:meditative"); + + DynamicRegistry.Key KEBAB = DynamicRegistry.Key.of("minecraft:kebab"); + + DynamicRegistry.Key STAGE = DynamicRegistry.Key.of("minecraft:stage"); + + DynamicRegistry.Key BOMB = DynamicRegistry.Key.of("minecraft:bomb"); + + DynamicRegistry.Key PLANT = DynamicRegistry.Key.of("minecraft:plant"); + + DynamicRegistry.Key WATER = DynamicRegistry.Key.of("minecraft:water"); + + DynamicRegistry.Key POINTER = DynamicRegistry.Key.of("minecraft:pointer"); + + DynamicRegistry.Key SKELETON = DynamicRegistry.Key.of("minecraft:skeleton"); + + DynamicRegistry.Key VOID = DynamicRegistry.Key.of("minecraft:void"); + + DynamicRegistry.Key WIND = DynamicRegistry.Key.of("minecraft:wind"); +} diff --git a/src/autogenerated/java/net/minestom/server/entity/villager/VillagerProfessions.java b/src/autogenerated/java/net/minestom/server/entity/villager/VillagerProfessions.java deleted file mode 100644 index 0f47b8b5050..00000000000 --- a/src/autogenerated/java/net/minestom/server/entity/villager/VillagerProfessions.java +++ /dev/null @@ -1,37 +0,0 @@ -package net.minestom.server.entity.villager; - -/** - * Code autogenerated, do not edit! - */ -@SuppressWarnings("unused") -interface VillagerProfessions { - VillagerProfession NONE = VillagerProfessionImpl.get("minecraft:none"); - - VillagerProfession ARMORER = VillagerProfessionImpl.get("minecraft:armorer"); - - VillagerProfession BUTCHER = VillagerProfessionImpl.get("minecraft:butcher"); - - VillagerProfession CARTOGRAPHER = VillagerProfessionImpl.get("minecraft:cartographer"); - - VillagerProfession CLERIC = VillagerProfessionImpl.get("minecraft:cleric"); - - VillagerProfession FARMER = VillagerProfessionImpl.get("minecraft:farmer"); - - VillagerProfession FISHERMAN = VillagerProfessionImpl.get("minecraft:fisherman"); - - VillagerProfession FLETCHER = VillagerProfessionImpl.get("minecraft:fletcher"); - - VillagerProfession LEATHERWORKER = VillagerProfessionImpl.get("minecraft:leatherworker"); - - VillagerProfession LIBRARIAN = VillagerProfessionImpl.get("minecraft:librarian"); - - VillagerProfession MASON = VillagerProfessionImpl.get("minecraft:mason"); - - VillagerProfession NITWIT = VillagerProfessionImpl.get("minecraft:nitwit"); - - VillagerProfession SHEPHERD = VillagerProfessionImpl.get("minecraft:shepherd"); - - VillagerProfession TOOLSMITH = VillagerProfessionImpl.get("minecraft:toolsmith"); - - VillagerProfession WEAPONSMITH = VillagerProfessionImpl.get("minecraft:weaponsmith"); -} diff --git a/src/autogenerated/java/net/minestom/server/entity/villager/VillagerTypes.java b/src/autogenerated/java/net/minestom/server/entity/villager/VillagerTypes.java deleted file mode 100644 index 681ce44c1d3..00000000000 --- a/src/autogenerated/java/net/minestom/server/entity/villager/VillagerTypes.java +++ /dev/null @@ -1,21 +0,0 @@ -package net.minestom.server.entity.villager; - -/** - * Code autogenerated, do not edit! - */ -@SuppressWarnings("unused") -interface VillagerTypes { - VillagerType DESERT = VillagerTypeImpl.get("minecraft:desert"); - - VillagerType JUNGLE = VillagerTypeImpl.get("minecraft:jungle"); - - VillagerType PLAINS = VillagerTypeImpl.get("minecraft:plains"); - - VillagerType SAVANNA = VillagerTypeImpl.get("minecraft:savanna"); - - VillagerType SNOW = VillagerTypeImpl.get("minecraft:snow"); - - VillagerType SWAMP = VillagerTypeImpl.get("minecraft:swamp"); - - VillagerType TAIGA = VillagerTypeImpl.get("minecraft:taiga"); -} diff --git a/src/autogenerated/java/net/minestom/server/feature/FeatureFlags.java b/src/autogenerated/java/net/minestom/server/feature/FeatureFlags.java new file mode 100644 index 00000000000..983bd307419 --- /dev/null +++ b/src/autogenerated/java/net/minestom/server/feature/FeatureFlags.java @@ -0,0 +1,35 @@ +package net.minestom.server.feature; + +import net.minestom.server.utils.NamespaceID; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * AUTOGENERATED by FeatureFlagGenerator + */ +public enum FeatureFlags { + UPDATE_1_21(NamespaceID.from("minecraft:update_1_21")), + + BUNDLE(NamespaceID.from("minecraft:bundle")), + + VANILLA(NamespaceID.from("minecraft:vanilla")), + + TRADE_REBALANCE(NamespaceID.from("minecraft:trade_rebalance")); + + private static final FeatureFlags[] VALUES = FeatureFlags.values(); + + private final NamespaceID feature; + + FeatureFlags(@NotNull NamespaceID feature) { + this.feature = feature; + } + + public @NotNull NamespaceID feature() { + return this.feature; + } + + @Nullable + public static FeatureFlags getValue(int id) { + return VALUES[id]; + } +} diff --git a/src/autogenerated/java/net/minestom/server/fluid/Fluid.java b/src/autogenerated/java/net/minestom/server/fluid/Fluid.java new file mode 100644 index 00000000000..e00575f9bbc --- /dev/null +++ b/src/autogenerated/java/net/minestom/server/fluid/Fluid.java @@ -0,0 +1,62 @@ +package net.minestom.server.fluid; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.Keyed; +import net.minestom.server.registry.FluidRegistries; +import net.minestom.server.utils.NamespaceID; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * AUTOGENERATED by FluidGenerator + */ +public enum Fluid implements Keyed { + EMPTY(NamespaceID.from("minecraft:empty")), + + FLOWING_WATER(NamespaceID.from("minecraft:flowing_water")), + + WATER(NamespaceID.from("minecraft:water")), + + FLOWING_LAVA(NamespaceID.from("minecraft:flowing_lava")), + + LAVA(NamespaceID.from("minecraft:lava")); + + private static final Fluid[] VALUES = values(); + + @NotNull + private final NamespaceID id; + + Fluid(@NotNull NamespaceID id) { + this.id = id; + FluidRegistries.fluids.put(id, this); + } + + @Override + @NotNull + public Key key() { + return this.id; + } + + public short getId() { + return (short) ordinal(); + } + + @NotNull + public NamespaceID getNamespaceID() { + return this.id; + } + + @NotNull + @Override + public String toString() { + return "[" + this.id + "]"; + } + + @Nullable + public static Fluid fromId(short id) { + if(id >= 0 && id < VALUES.length) { + return VALUES[id]; + } + return null; + } +} diff --git a/src/autogenerated/java/net/minestom/server/fluid/Fluids.java b/src/autogenerated/java/net/minestom/server/fluid/Fluids.java deleted file mode 100644 index 27157009e9e..00000000000 --- a/src/autogenerated/java/net/minestom/server/fluid/Fluids.java +++ /dev/null @@ -1,17 +0,0 @@ -package net.minestom.server.fluid; - -/** - * Code autogenerated, do not edit! - */ -@SuppressWarnings("unused") -interface Fluids { - Fluid EMPTY = FluidImpl.get("minecraft:empty"); - - Fluid FLOWING_WATER = FluidImpl.get("minecraft:flowing_water"); - - Fluid WATER = FluidImpl.get("minecraft:water"); - - Fluid FLOWING_LAVA = FluidImpl.get("minecraft:flowing_lava"); - - Fluid LAVA = FluidImpl.get("minecraft:lava"); -} diff --git a/src/autogenerated/java/net/minestom/server/instance/block/Blocks.java b/src/autogenerated/java/net/minestom/server/instance/block/Blocks.java index fc2a6033248..974c15bb969 100644 --- a/src/autogenerated/java/net/minestom/server/instance/block/Blocks.java +++ b/src/autogenerated/java/net/minestom/server/instance/block/Blocks.java @@ -2120,4 +2120,8 @@ interface Blocks { Block CRAFTER = BlockImpl.get("minecraft:crafter"); Block TRIAL_SPAWNER = BlockImpl.get("minecraft:trial_spawner"); + + Block VAULT = BlockImpl.get("minecraft:vault"); + + Block HEAVY_CORE = BlockImpl.get("minecraft:heavy_core"); } diff --git a/src/autogenerated/java/net/minestom/server/instance/block/banner/BannerPatterns.java b/src/autogenerated/java/net/minestom/server/instance/block/banner/BannerPatterns.java new file mode 100644 index 00000000000..2a4a509944c --- /dev/null +++ b/src/autogenerated/java/net/minestom/server/instance/block/banner/BannerPatterns.java @@ -0,0 +1,95 @@ +package net.minestom.server.instance.block.banner; + +import net.minestom.server.registry.DynamicRegistry; + +/** + * Code autogenerated, do not edit! + */ +@SuppressWarnings("unused") +interface BannerPatterns { + DynamicRegistry.Key BORDER = DynamicRegistry.Key.of("minecraft:border"); + + DynamicRegistry.Key SQUARE_BOTTOM_RIGHT = DynamicRegistry.Key.of("minecraft:square_bottom_right"); + + DynamicRegistry.Key STRIPE_RIGHT = DynamicRegistry.Key.of("minecraft:stripe_right"); + + DynamicRegistry.Key FLOW = DynamicRegistry.Key.of("minecraft:flow"); + + DynamicRegistry.Key RHOMBUS = DynamicRegistry.Key.of("minecraft:rhombus"); + + DynamicRegistry.Key TRIANGLES_TOP = DynamicRegistry.Key.of("minecraft:triangles_top"); + + DynamicRegistry.Key GRADIENT = DynamicRegistry.Key.of("minecraft:gradient"); + + DynamicRegistry.Key GLOBE = DynamicRegistry.Key.of("minecraft:globe"); + + DynamicRegistry.Key MOJANG = DynamicRegistry.Key.of("minecraft:mojang"); + + DynamicRegistry.Key STRIPE_BOTTOM = DynamicRegistry.Key.of("minecraft:stripe_bottom"); + + DynamicRegistry.Key STRIPE_MIDDLE = DynamicRegistry.Key.of("minecraft:stripe_middle"); + + DynamicRegistry.Key FLOWER = DynamicRegistry.Key.of("minecraft:flower"); + + DynamicRegistry.Key DIAGONAL_UP_RIGHT = DynamicRegistry.Key.of("minecraft:diagonal_up_right"); + + DynamicRegistry.Key CIRCLE = DynamicRegistry.Key.of("minecraft:circle"); + + DynamicRegistry.Key HALF_HORIZONTAL = DynamicRegistry.Key.of("minecraft:half_horizontal"); + + DynamicRegistry.Key BRICKS = DynamicRegistry.Key.of("minecraft:bricks"); + + DynamicRegistry.Key TRIANGLE_BOTTOM = DynamicRegistry.Key.of("minecraft:triangle_bottom"); + + DynamicRegistry.Key CURLY_BORDER = DynamicRegistry.Key.of("minecraft:curly_border"); + + DynamicRegistry.Key BASE = DynamicRegistry.Key.of("minecraft:base"); + + DynamicRegistry.Key PIGLIN = DynamicRegistry.Key.of("minecraft:piglin"); + + DynamicRegistry.Key STRIPE_CENTER = DynamicRegistry.Key.of("minecraft:stripe_center"); + + DynamicRegistry.Key SQUARE_BOTTOM_LEFT = DynamicRegistry.Key.of("minecraft:square_bottom_left"); + + DynamicRegistry.Key SQUARE_TOP_RIGHT = DynamicRegistry.Key.of("minecraft:square_top_right"); + + DynamicRegistry.Key STRIPE_DOWNRIGHT = DynamicRegistry.Key.of("minecraft:stripe_downright"); + + DynamicRegistry.Key GUSTER = DynamicRegistry.Key.of("minecraft:guster"); + + DynamicRegistry.Key GRADIENT_UP = DynamicRegistry.Key.of("minecraft:gradient_up"); + + DynamicRegistry.Key DIAGONAL_RIGHT = DynamicRegistry.Key.of("minecraft:diagonal_right"); + + DynamicRegistry.Key HALF_VERTICAL_RIGHT = DynamicRegistry.Key.of("minecraft:half_vertical_right"); + + DynamicRegistry.Key SMALL_STRIPES = DynamicRegistry.Key.of("minecraft:small_stripes"); + + DynamicRegistry.Key CROSS = DynamicRegistry.Key.of("minecraft:cross"); + + DynamicRegistry.Key DIAGONAL_LEFT = DynamicRegistry.Key.of("minecraft:diagonal_left"); + + DynamicRegistry.Key SKULL = DynamicRegistry.Key.of("minecraft:skull"); + + DynamicRegistry.Key STRIPE_DOWNLEFT = DynamicRegistry.Key.of("minecraft:stripe_downleft"); + + DynamicRegistry.Key DIAGONAL_UP_LEFT = DynamicRegistry.Key.of("minecraft:diagonal_up_left"); + + DynamicRegistry.Key HALF_VERTICAL = DynamicRegistry.Key.of("minecraft:half_vertical"); + + DynamicRegistry.Key TRIANGLE_TOP = DynamicRegistry.Key.of("minecraft:triangle_top"); + + DynamicRegistry.Key HALF_HORIZONTAL_BOTTOM = DynamicRegistry.Key.of("minecraft:half_horizontal_bottom"); + + DynamicRegistry.Key SQUARE_TOP_LEFT = DynamicRegistry.Key.of("minecraft:square_top_left"); + + DynamicRegistry.Key STRIPE_TOP = DynamicRegistry.Key.of("minecraft:stripe_top"); + + DynamicRegistry.Key CREEPER = DynamicRegistry.Key.of("minecraft:creeper"); + + DynamicRegistry.Key STRIPE_LEFT = DynamicRegistry.Key.of("minecraft:stripe_left"); + + DynamicRegistry.Key TRIANGLES_BOTTOM = DynamicRegistry.Key.of("minecraft:triangles_bottom"); + + DynamicRegistry.Key STRAIGHT_CROSS = DynamicRegistry.Key.of("minecraft:straight_cross"); +} diff --git a/src/autogenerated/java/net/minestom/server/instance/block/jukebox/JukeboxSongs.java b/src/autogenerated/java/net/minestom/server/instance/block/jukebox/JukeboxSongs.java new file mode 100644 index 00000000000..3b18fbfd0c7 --- /dev/null +++ b/src/autogenerated/java/net/minestom/server/instance/block/jukebox/JukeboxSongs.java @@ -0,0 +1,47 @@ +package net.minestom.server.instance.block.jukebox; + +import net.minestom.server.registry.DynamicRegistry; + +/** + * Code autogenerated, do not edit! + */ +@SuppressWarnings("unused") +interface JukeboxSongs { + DynamicRegistry.Key PRECIPICE = DynamicRegistry.Key.of("minecraft:precipice"); + + DynamicRegistry.Key STAL = DynamicRegistry.Key.of("minecraft:stal"); + + DynamicRegistry.Key STRAD = DynamicRegistry.Key.of("minecraft:strad"); + + DynamicRegistry.Key CREATOR_MUSIC_BOX = DynamicRegistry.Key.of("minecraft:creator_music_box"); + + DynamicRegistry.Key _13 = DynamicRegistry.Key.of("minecraft:13"); + + DynamicRegistry.Key RELIC = DynamicRegistry.Key.of("minecraft:relic"); + + DynamicRegistry.Key FAR = DynamicRegistry.Key.of("minecraft:far"); + + DynamicRegistry.Key BLOCKS = DynamicRegistry.Key.of("minecraft:blocks"); + + DynamicRegistry.Key _5 = DynamicRegistry.Key.of("minecraft:5"); + + DynamicRegistry.Key OTHERSIDE = DynamicRegistry.Key.of("minecraft:otherside"); + + DynamicRegistry.Key MELLOHI = DynamicRegistry.Key.of("minecraft:mellohi"); + + DynamicRegistry.Key MALL = DynamicRegistry.Key.of("minecraft:mall"); + + DynamicRegistry.Key CHIRP = DynamicRegistry.Key.of("minecraft:chirp"); + + DynamicRegistry.Key CREATOR = DynamicRegistry.Key.of("minecraft:creator"); + + DynamicRegistry.Key PIGSTEP = DynamicRegistry.Key.of("minecraft:pigstep"); + + DynamicRegistry.Key WARD = DynamicRegistry.Key.of("minecraft:ward"); + + DynamicRegistry.Key CAT = DynamicRegistry.Key.of("minecraft:cat"); + + DynamicRegistry.Key WAIT = DynamicRegistry.Key.of("minecraft:wait"); + + DynamicRegistry.Key _11 = DynamicRegistry.Key.of("minecraft:11"); +} diff --git a/src/autogenerated/java/net/minestom/server/item/Enchantments.java b/src/autogenerated/java/net/minestom/server/item/Enchantments.java deleted file mode 100644 index 1de0916b154..00000000000 --- a/src/autogenerated/java/net/minestom/server/item/Enchantments.java +++ /dev/null @@ -1,85 +0,0 @@ -package net.minestom.server.item; - -/** - * Code autogenerated, do not edit! - */ -@SuppressWarnings("unused") -interface Enchantments { - Enchantment PROTECTION = EnchantmentImpl.get("minecraft:protection"); - - Enchantment FIRE_PROTECTION = EnchantmentImpl.get("minecraft:fire_protection"); - - Enchantment FEATHER_FALLING = EnchantmentImpl.get("minecraft:feather_falling"); - - Enchantment BLAST_PROTECTION = EnchantmentImpl.get("minecraft:blast_protection"); - - Enchantment PROJECTILE_PROTECTION = EnchantmentImpl.get("minecraft:projectile_protection"); - - Enchantment RESPIRATION = EnchantmentImpl.get("minecraft:respiration"); - - Enchantment AQUA_AFFINITY = EnchantmentImpl.get("minecraft:aqua_affinity"); - - Enchantment THORNS = EnchantmentImpl.get("minecraft:thorns"); - - Enchantment DEPTH_STRIDER = EnchantmentImpl.get("minecraft:depth_strider"); - - Enchantment FROST_WALKER = EnchantmentImpl.get("minecraft:frost_walker"); - - Enchantment BINDING_CURSE = EnchantmentImpl.get("minecraft:binding_curse"); - - Enchantment SOUL_SPEED = EnchantmentImpl.get("minecraft:soul_speed"); - - Enchantment SWIFT_SNEAK = EnchantmentImpl.get("minecraft:swift_sneak"); - - Enchantment SHARPNESS = EnchantmentImpl.get("minecraft:sharpness"); - - Enchantment SMITE = EnchantmentImpl.get("minecraft:smite"); - - Enchantment BANE_OF_ARTHROPODS = EnchantmentImpl.get("minecraft:bane_of_arthropods"); - - Enchantment KNOCKBACK = EnchantmentImpl.get("minecraft:knockback"); - - Enchantment FIRE_ASPECT = EnchantmentImpl.get("minecraft:fire_aspect"); - - Enchantment LOOTING = EnchantmentImpl.get("minecraft:looting"); - - Enchantment SWEEPING = EnchantmentImpl.get("minecraft:sweeping"); - - Enchantment EFFICIENCY = EnchantmentImpl.get("minecraft:efficiency"); - - Enchantment SILK_TOUCH = EnchantmentImpl.get("minecraft:silk_touch"); - - Enchantment UNBREAKING = EnchantmentImpl.get("minecraft:unbreaking"); - - Enchantment FORTUNE = EnchantmentImpl.get("minecraft:fortune"); - - Enchantment POWER = EnchantmentImpl.get("minecraft:power"); - - Enchantment PUNCH = EnchantmentImpl.get("minecraft:punch"); - - Enchantment FLAME = EnchantmentImpl.get("minecraft:flame"); - - Enchantment INFINITY = EnchantmentImpl.get("minecraft:infinity"); - - Enchantment LUCK_OF_THE_SEA = EnchantmentImpl.get("minecraft:luck_of_the_sea"); - - Enchantment LURE = EnchantmentImpl.get("minecraft:lure"); - - Enchantment LOYALTY = EnchantmentImpl.get("minecraft:loyalty"); - - Enchantment IMPALING = EnchantmentImpl.get("minecraft:impaling"); - - Enchantment RIPTIDE = EnchantmentImpl.get("minecraft:riptide"); - - Enchantment CHANNELING = EnchantmentImpl.get("minecraft:channeling"); - - Enchantment MULTISHOT = EnchantmentImpl.get("minecraft:multishot"); - - Enchantment QUICK_CHARGE = EnchantmentImpl.get("minecraft:quick_charge"); - - Enchantment PIERCING = EnchantmentImpl.get("minecraft:piercing"); - - Enchantment MENDING = EnchantmentImpl.get("minecraft:mending"); - - Enchantment VANISHING_CURSE = EnchantmentImpl.get("minecraft:vanishing_curse"); -} diff --git a/src/autogenerated/java/net/minestom/server/item/Materials.java b/src/autogenerated/java/net/minestom/server/item/Materials.java index 17aab609465..23f5ff7ba35 100644 --- a/src/autogenerated/java/net/minestom/server/item/Materials.java +++ b/src/autogenerated/java/net/minestom/server/item/Materials.java @@ -175,6 +175,8 @@ interface Materials { Material RAW_GOLD_BLOCK = MaterialImpl.get("minecraft:raw_gold_block"); + Material HEAVY_CORE = MaterialImpl.get("minecraft:heavy_core"); + Material AMETHYST_BLOCK = MaterialImpl.get("minecraft:amethyst_block"); Material BUDDING_AMETHYST = MaterialImpl.get("minecraft:budding_amethyst"); @@ -1593,10 +1595,16 @@ interface Materials { Material TURTLE_HELMET = MaterialImpl.get("minecraft:turtle_helmet"); - Material SCUTE = MaterialImpl.get("minecraft:scute"); + Material TURTLE_SCUTE = MaterialImpl.get("minecraft:turtle_scute"); + + Material ARMADILLO_SCUTE = MaterialImpl.get("minecraft:armadillo_scute"); + + Material WOLF_ARMOR = MaterialImpl.get("minecraft:wolf_armor"); Material FLINT_AND_STEEL = MaterialImpl.get("minecraft:flint_and_steel"); + Material BOWL = MaterialImpl.get("minecraft:bowl"); + Material APPLE = MaterialImpl.get("minecraft:apple"); Material BOW = MaterialImpl.get("minecraft:bow"); @@ -1695,8 +1703,6 @@ interface Materials { Material STICK = MaterialImpl.get("minecraft:stick"); - Material BOWL = MaterialImpl.get("minecraft:bowl"); - Material MUSHROOM_STEW = MaterialImpl.get("minecraft:mushroom_stew"); Material STRING = MaterialImpl.get("minecraft:string"); @@ -2015,6 +2021,8 @@ interface Materials { Material GLISTERING_MELON_SLICE = MaterialImpl.get("minecraft:glistering_melon_slice"); + Material ARMADILLO_SPAWN_EGG = MaterialImpl.get("minecraft:armadillo_spawn_egg"); + Material ALLAY_SPAWN_EGG = MaterialImpl.get("minecraft:allay_spawn_egg"); Material AXOLOTL_SPAWN_EGG = MaterialImpl.get("minecraft:axolotl_spawn_egg"); @@ -2025,6 +2033,8 @@ interface Materials { Material BLAZE_SPAWN_EGG = MaterialImpl.get("minecraft:blaze_spawn_egg"); + Material BOGGED_SPAWN_EGG = MaterialImpl.get("minecraft:bogged_spawn_egg"); + Material BREEZE_SPAWN_EGG = MaterialImpl.get("minecraft:breeze_spawn_egg"); Material CAT_SPAWN_EGG = MaterialImpl.get("minecraft:cat_spawn_egg"); @@ -2175,10 +2185,14 @@ interface Materials { Material FIRE_CHARGE = MaterialImpl.get("minecraft:fire_charge"); + Material WIND_CHARGE = MaterialImpl.get("minecraft:wind_charge"); + Material WRITABLE_BOOK = MaterialImpl.get("minecraft:writable_book"); Material WRITTEN_BOOK = MaterialImpl.get("minecraft:written_book"); + Material MACE = MaterialImpl.get("minecraft:mace"); + Material ITEM_FRAME = MaterialImpl.get("minecraft:item_frame"); Material GLOW_ITEM_FRAME = MaterialImpl.get("minecraft:glow_item_frame"); @@ -2335,6 +2349,10 @@ interface Materials { Material MUSIC_DISC_CHIRP = MaterialImpl.get("minecraft:music_disc_chirp"); + Material MUSIC_DISC_CREATOR = MaterialImpl.get("minecraft:music_disc_creator"); + + Material MUSIC_DISC_CREATOR_MUSIC_BOX = MaterialImpl.get("minecraft:music_disc_creator_music_box"); + Material MUSIC_DISC_FAR = MaterialImpl.get("minecraft:music_disc_far"); Material MUSIC_DISC_MALL = MaterialImpl.get("minecraft:music_disc_mall"); @@ -2359,6 +2377,8 @@ interface Materials { Material MUSIC_DISC_PIGSTEP = MaterialImpl.get("minecraft:music_disc_pigstep"); + Material MUSIC_DISC_PRECIPICE = MaterialImpl.get("minecraft:music_disc_precipice"); + Material DISC_FRAGMENT_5 = MaterialImpl.get("minecraft:disc_fragment_5"); Material TRIDENT = MaterialImpl.get("minecraft:trident"); @@ -2387,6 +2407,10 @@ interface Materials { Material PIGLIN_BANNER_PATTERN = MaterialImpl.get("minecraft:piglin_banner_pattern"); + Material FLOW_BANNER_PATTERN = MaterialImpl.get("minecraft:flow_banner_pattern"); + + Material GUSTER_BANNER_PATTERN = MaterialImpl.get("minecraft:guster_banner_pattern"); + Material GOAT_HORN = MaterialImpl.get("minecraft:goat_horn"); Material COMPOSTER = MaterialImpl.get("minecraft:composter"); @@ -2553,6 +2577,10 @@ interface Materials { Material HOST_ARMOR_TRIM_SMITHING_TEMPLATE = MaterialImpl.get("minecraft:host_armor_trim_smithing_template"); + Material FLOW_ARMOR_TRIM_SMITHING_TEMPLATE = MaterialImpl.get("minecraft:flow_armor_trim_smithing_template"); + + Material BOLT_ARMOR_TRIM_SMITHING_TEMPLATE = MaterialImpl.get("minecraft:bolt_armor_trim_smithing_template"); + Material ANGLER_POTTERY_SHERD = MaterialImpl.get("minecraft:angler_pottery_sherd"); Material ARCHER_POTTERY_SHERD = MaterialImpl.get("minecraft:archer_pottery_sherd"); @@ -2569,8 +2597,12 @@ interface Materials { Material EXPLORER_POTTERY_SHERD = MaterialImpl.get("minecraft:explorer_pottery_sherd"); + Material FLOW_POTTERY_SHERD = MaterialImpl.get("minecraft:flow_pottery_sherd"); + Material FRIEND_POTTERY_SHERD = MaterialImpl.get("minecraft:friend_pottery_sherd"); + Material GUSTER_POTTERY_SHERD = MaterialImpl.get("minecraft:guster_pottery_sherd"); + Material HEART_POTTERY_SHERD = MaterialImpl.get("minecraft:heart_pottery_sherd"); Material HEARTBREAK_POTTERY_SHERD = MaterialImpl.get("minecraft:heartbreak_pottery_sherd"); @@ -2585,6 +2617,8 @@ interface Materials { Material PRIZE_POTTERY_SHERD = MaterialImpl.get("minecraft:prize_pottery_sherd"); + Material SCRAPE_POTTERY_SHERD = MaterialImpl.get("minecraft:scrape_pottery_sherd"); + Material SHEAF_POTTERY_SHERD = MaterialImpl.get("minecraft:sheaf_pottery_sherd"); Material SHELTER_POTTERY_SHERD = MaterialImpl.get("minecraft:shelter_pottery_sherd"); @@ -2628,4 +2662,12 @@ interface Materials { Material TRIAL_SPAWNER = MaterialImpl.get("minecraft:trial_spawner"); Material TRIAL_KEY = MaterialImpl.get("minecraft:trial_key"); + + Material OMINOUS_TRIAL_KEY = MaterialImpl.get("minecraft:ominous_trial_key"); + + Material VAULT = MaterialImpl.get("minecraft:vault"); + + Material OMINOUS_BOTTLE = MaterialImpl.get("minecraft:ominous_bottle"); + + Material BREEZE_ROD = MaterialImpl.get("minecraft:breeze_rod"); } diff --git a/src/autogenerated/java/net/minestom/server/item/armor/TrimMaterials.java b/src/autogenerated/java/net/minestom/server/item/armor/TrimMaterials.java index 07e0e19eec5..015d2a50e41 100644 --- a/src/autogenerated/java/net/minestom/server/item/armor/TrimMaterials.java +++ b/src/autogenerated/java/net/minestom/server/item/armor/TrimMaterials.java @@ -1,27 +1,29 @@ package net.minestom.server.item.armor; +import net.minestom.server.registry.DynamicRegistry; + /** * Code autogenerated, do not edit! */ @SuppressWarnings("unused") interface TrimMaterials { - TrimMaterial LAPIS = TrimMaterialImpl.get("minecraft:lapis"); + DynamicRegistry.Key LAPIS = DynamicRegistry.Key.of("minecraft:lapis"); - TrimMaterial IRON = TrimMaterialImpl.get("minecraft:iron"); + DynamicRegistry.Key IRON = DynamicRegistry.Key.of("minecraft:iron"); - TrimMaterial DIAMOND = TrimMaterialImpl.get("minecraft:diamond"); + DynamicRegistry.Key DIAMOND = DynamicRegistry.Key.of("minecraft:diamond"); - TrimMaterial AMETHYST = TrimMaterialImpl.get("minecraft:amethyst"); + DynamicRegistry.Key AMETHYST = DynamicRegistry.Key.of("minecraft:amethyst"); - TrimMaterial COPPER = TrimMaterialImpl.get("minecraft:copper"); + DynamicRegistry.Key COPPER = DynamicRegistry.Key.of("minecraft:copper"); - TrimMaterial QUARTZ = TrimMaterialImpl.get("minecraft:quartz"); + DynamicRegistry.Key QUARTZ = DynamicRegistry.Key.of("minecraft:quartz"); - TrimMaterial EMERALD = TrimMaterialImpl.get("minecraft:emerald"); + DynamicRegistry.Key EMERALD = DynamicRegistry.Key.of("minecraft:emerald"); - TrimMaterial REDSTONE = TrimMaterialImpl.get("minecraft:redstone"); + DynamicRegistry.Key REDSTONE = DynamicRegistry.Key.of("minecraft:redstone"); - TrimMaterial GOLD = TrimMaterialImpl.get("minecraft:gold"); + DynamicRegistry.Key GOLD = DynamicRegistry.Key.of("minecraft:gold"); - TrimMaterial NETHERITE = TrimMaterialImpl.get("minecraft:netherite"); + DynamicRegistry.Key NETHERITE = DynamicRegistry.Key.of("minecraft:netherite"); } diff --git a/src/autogenerated/java/net/minestom/server/item/armor/TrimPatterns.java b/src/autogenerated/java/net/minestom/server/item/armor/TrimPatterns.java index 1233b049764..a11a09285bd 100644 --- a/src/autogenerated/java/net/minestom/server/item/armor/TrimPatterns.java +++ b/src/autogenerated/java/net/minestom/server/item/armor/TrimPatterns.java @@ -1,39 +1,45 @@ package net.minestom.server.item.armor; +import net.minestom.server.registry.DynamicRegistry; + /** * Code autogenerated, do not edit! */ @SuppressWarnings("unused") interface TrimPatterns { - TrimPattern TIDE = TrimPatternImpl.get("minecraft:tide"); + DynamicRegistry.Key TIDE = DynamicRegistry.Key.of("minecraft:tide"); + + DynamicRegistry.Key RIB = DynamicRegistry.Key.of("minecraft:rib"); + + DynamicRegistry.Key BOLT = DynamicRegistry.Key.of("minecraft:bolt"); - TrimPattern RIB = TrimPatternImpl.get("minecraft:rib"); + DynamicRegistry.Key HOST = DynamicRegistry.Key.of("minecraft:host"); - TrimPattern HOST = TrimPatternImpl.get("minecraft:host"); + DynamicRegistry.Key SILENCE = DynamicRegistry.Key.of("minecraft:silence"); - TrimPattern SILENCE = TrimPatternImpl.get("minecraft:silence"); + DynamicRegistry.Key WILD = DynamicRegistry.Key.of("minecraft:wild"); - TrimPattern WILD = TrimPatternImpl.get("minecraft:wild"); + DynamicRegistry.Key WAYFINDER = DynamicRegistry.Key.of("minecraft:wayfinder"); - TrimPattern WAYFINDER = TrimPatternImpl.get("minecraft:wayfinder"); + DynamicRegistry.Key FLOW = DynamicRegistry.Key.of("minecraft:flow"); - TrimPattern DUNE = TrimPatternImpl.get("minecraft:dune"); + DynamicRegistry.Key DUNE = DynamicRegistry.Key.of("minecraft:dune"); - TrimPattern RAISER = TrimPatternImpl.get("minecraft:raiser"); + DynamicRegistry.Key RAISER = DynamicRegistry.Key.of("minecraft:raiser"); - TrimPattern SNOUT = TrimPatternImpl.get("minecraft:snout"); + DynamicRegistry.Key SNOUT = DynamicRegistry.Key.of("minecraft:snout"); - TrimPattern VEX = TrimPatternImpl.get("minecraft:vex"); + DynamicRegistry.Key VEX = DynamicRegistry.Key.of("minecraft:vex"); - TrimPattern SPIRE = TrimPatternImpl.get("minecraft:spire"); + DynamicRegistry.Key SPIRE = DynamicRegistry.Key.of("minecraft:spire"); - TrimPattern SENTRY = TrimPatternImpl.get("minecraft:sentry"); + DynamicRegistry.Key SENTRY = DynamicRegistry.Key.of("minecraft:sentry"); - TrimPattern EYE = TrimPatternImpl.get("minecraft:eye"); + DynamicRegistry.Key EYE = DynamicRegistry.Key.of("minecraft:eye"); - TrimPattern WARD = TrimPatternImpl.get("minecraft:ward"); + DynamicRegistry.Key WARD = DynamicRegistry.Key.of("minecraft:ward"); - TrimPattern COAST = TrimPatternImpl.get("minecraft:coast"); + DynamicRegistry.Key COAST = DynamicRegistry.Key.of("minecraft:coast"); - TrimPattern SHAPER = TrimPatternImpl.get("minecraft:shaper"); + DynamicRegistry.Key SHAPER = DynamicRegistry.Key.of("minecraft:shaper"); } diff --git a/src/autogenerated/java/net/minestom/server/item/enchant/Enchantments.java b/src/autogenerated/java/net/minestom/server/item/enchant/Enchantments.java new file mode 100644 index 00000000000..03f8dad26ae --- /dev/null +++ b/src/autogenerated/java/net/minestom/server/item/enchant/Enchantments.java @@ -0,0 +1,93 @@ +package net.minestom.server.item.enchant; + +import net.minestom.server.registry.DynamicRegistry; + +/** + * Code autogenerated, do not edit! + */ +@SuppressWarnings("unused") +interface Enchantments { + DynamicRegistry.Key DEPTH_STRIDER = DynamicRegistry.Key.of("minecraft:depth_strider"); + + DynamicRegistry.Key VANISHING_CURSE = DynamicRegistry.Key.of("minecraft:vanishing_curse"); + + DynamicRegistry.Key EFFICIENCY = DynamicRegistry.Key.of("minecraft:efficiency"); + + DynamicRegistry.Key IMPALING = DynamicRegistry.Key.of("minecraft:impaling"); + + DynamicRegistry.Key WIND_BURST = DynamicRegistry.Key.of("minecraft:wind_burst"); + + DynamicRegistry.Key BANE_OF_ARTHROPODS = DynamicRegistry.Key.of("minecraft:bane_of_arthropods"); + + DynamicRegistry.Key BINDING_CURSE = DynamicRegistry.Key.of("minecraft:binding_curse"); + + DynamicRegistry.Key PUNCH = DynamicRegistry.Key.of("minecraft:punch"); + + DynamicRegistry.Key FLAME = DynamicRegistry.Key.of("minecraft:flame"); + + DynamicRegistry.Key RIPTIDE = DynamicRegistry.Key.of("minecraft:riptide"); + + DynamicRegistry.Key BLAST_PROTECTION = DynamicRegistry.Key.of("minecraft:blast_protection"); + + DynamicRegistry.Key FROST_WALKER = DynamicRegistry.Key.of("minecraft:frost_walker"); + + DynamicRegistry.Key PROTECTION = DynamicRegistry.Key.of("minecraft:protection"); + + DynamicRegistry.Key FIRE_ASPECT = DynamicRegistry.Key.of("minecraft:fire_aspect"); + + DynamicRegistry.Key LOYALTY = DynamicRegistry.Key.of("minecraft:loyalty"); + + DynamicRegistry.Key SWEEPING_EDGE = DynamicRegistry.Key.of("minecraft:sweeping_edge"); + + DynamicRegistry.Key FIRE_PROTECTION = DynamicRegistry.Key.of("minecraft:fire_protection"); + + DynamicRegistry.Key QUICK_CHARGE = DynamicRegistry.Key.of("minecraft:quick_charge"); + + DynamicRegistry.Key RESPIRATION = DynamicRegistry.Key.of("minecraft:respiration"); + + DynamicRegistry.Key LUCK_OF_THE_SEA = DynamicRegistry.Key.of("minecraft:luck_of_the_sea"); + + DynamicRegistry.Key SOUL_SPEED = DynamicRegistry.Key.of("minecraft:soul_speed"); + + DynamicRegistry.Key DENSITY = DynamicRegistry.Key.of("minecraft:density"); + + DynamicRegistry.Key POWER = DynamicRegistry.Key.of("minecraft:power"); + + DynamicRegistry.Key SILK_TOUCH = DynamicRegistry.Key.of("minecraft:silk_touch"); + + DynamicRegistry.Key CHANNELING = DynamicRegistry.Key.of("minecraft:channeling"); + + DynamicRegistry.Key FORTUNE = DynamicRegistry.Key.of("minecraft:fortune"); + + DynamicRegistry.Key LOOTING = DynamicRegistry.Key.of("minecraft:looting"); + + DynamicRegistry.Key BREACH = DynamicRegistry.Key.of("minecraft:breach"); + + DynamicRegistry.Key PIERCING = DynamicRegistry.Key.of("minecraft:piercing"); + + DynamicRegistry.Key MENDING = DynamicRegistry.Key.of("minecraft:mending"); + + DynamicRegistry.Key FEATHER_FALLING = DynamicRegistry.Key.of("minecraft:feather_falling"); + + DynamicRegistry.Key SHARPNESS = DynamicRegistry.Key.of("minecraft:sharpness"); + + DynamicRegistry.Key KNOCKBACK = DynamicRegistry.Key.of("minecraft:knockback"); + + DynamicRegistry.Key SMITE = DynamicRegistry.Key.of("minecraft:smite"); + + DynamicRegistry.Key INFINITY = DynamicRegistry.Key.of("minecraft:infinity"); + + DynamicRegistry.Key PROJECTILE_PROTECTION = DynamicRegistry.Key.of("minecraft:projectile_protection"); + + DynamicRegistry.Key THORNS = DynamicRegistry.Key.of("minecraft:thorns"); + + DynamicRegistry.Key AQUA_AFFINITY = DynamicRegistry.Key.of("minecraft:aqua_affinity"); + + DynamicRegistry.Key LURE = DynamicRegistry.Key.of("minecraft:lure"); + + DynamicRegistry.Key MULTISHOT = DynamicRegistry.Key.of("minecraft:multishot"); + + DynamicRegistry.Key SWIFT_SNEAK = DynamicRegistry.Key.of("minecraft:swift_sneak"); + + DynamicRegistry.Key UNBREAKING = DynamicRegistry.Key.of("minecraft:unbreaking"); +} diff --git a/src/autogenerated/java/net/minestom/server/message/ChatTypes.java b/src/autogenerated/java/net/minestom/server/message/ChatTypes.java new file mode 100644 index 00000000000..c2b2ad41c06 --- /dev/null +++ b/src/autogenerated/java/net/minestom/server/message/ChatTypes.java @@ -0,0 +1,23 @@ +package net.minestom.server.message; + +import net.minestom.server.registry.DynamicRegistry; + +/** + * Code autogenerated, do not edit! + */ +@SuppressWarnings("unused") +interface ChatTypes { + DynamicRegistry.Key EMOTE_COMMAND = DynamicRegistry.Key.of("minecraft:emote_command"); + + DynamicRegistry.Key TEAM_MSG_COMMAND_INCOMING = DynamicRegistry.Key.of("minecraft:team_msg_command_incoming"); + + DynamicRegistry.Key TEAM_MSG_COMMAND_OUTGOING = DynamicRegistry.Key.of("minecraft:team_msg_command_outgoing"); + + DynamicRegistry.Key CHAT = DynamicRegistry.Key.of("minecraft:chat"); + + DynamicRegistry.Key MSG_COMMAND_INCOMING = DynamicRegistry.Key.of("minecraft:msg_command_incoming"); + + DynamicRegistry.Key MSG_COMMAND_OUTGOING = DynamicRegistry.Key.of("minecraft:msg_command_outgoing"); + + DynamicRegistry.Key SAY_COMMAND = DynamicRegistry.Key.of("minecraft:say_command"); +} diff --git a/src/autogenerated/java/net/minestom/server/particle/Particles.java b/src/autogenerated/java/net/minestom/server/particle/Particles.java index 691280c4356..37ecf045ee9 100644 --- a/src/autogenerated/java/net/minestom/server/particle/Particles.java +++ b/src/autogenerated/java/net/minestom/server/particle/Particles.java @@ -1,17 +1,14 @@ package net.minestom.server.particle; /** - * Code autogenerated, do not edit! + * AUTOGENERATED by ParticleGenerator */ -@SuppressWarnings("unused") interface Particles { - Particle AMBIENT_ENTITY_EFFECT = ParticleImpl.get("minecraft:ambient_entity_effect"); - Particle ANGRY_VILLAGER = ParticleImpl.get("minecraft:angry_villager"); - Particle BLOCK = ParticleImpl.get("minecraft:block"); + Particle.Block BLOCK = (Particle.Block) ParticleImpl.get("minecraft:block"); - Particle BLOCK_MARKER = ParticleImpl.get("minecraft:block_marker"); + Particle.BlockMarker BLOCK_MARKER = (Particle.BlockMarker) ParticleImpl.get("minecraft:block_marker"); Particle BUBBLE = ParticleImpl.get("minecraft:bubble"); @@ -33,9 +30,9 @@ interface Particles { Particle FALLING_WATER = ParticleImpl.get("minecraft:falling_water"); - Particle DUST = ParticleImpl.get("minecraft:dust"); + Particle.Dust DUST = (Particle.Dust) ParticleImpl.get("minecraft:dust"); - Particle DUST_COLOR_TRANSITION = ParticleImpl.get("minecraft:dust_color_transition"); + Particle.DustColorTransition DUST_COLOR_TRANSITION = (Particle.DustColorTransition) ParticleImpl.get("minecraft:dust_color_transition"); Particle EFFECT = ParticleImpl.get("minecraft:effect"); @@ -47,7 +44,7 @@ interface Particles { Particle END_ROD = ParticleImpl.get("minecraft:end_rod"); - Particle ENTITY_EFFECT = ParticleImpl.get("minecraft:entity_effect"); + Particle.EntityEffect ENTITY_EFFECT = (Particle.EntityEffect) ParticleImpl.get("minecraft:entity_effect"); Particle EXPLOSION_EMITTER = ParticleImpl.get("minecraft:explosion_emitter"); @@ -55,11 +52,15 @@ interface Particles { Particle GUST = ParticleImpl.get("minecraft:gust"); - Particle GUST_EMITTER = ParticleImpl.get("minecraft:gust_emitter"); + Particle SMALL_GUST = ParticleImpl.get("minecraft:small_gust"); + + Particle GUST_EMITTER_LARGE = ParticleImpl.get("minecraft:gust_emitter_large"); + + Particle GUST_EMITTER_SMALL = ParticleImpl.get("minecraft:gust_emitter_small"); Particle SONIC_BOOM = ParticleImpl.get("minecraft:sonic_boom"); - Particle FALLING_DUST = ParticleImpl.get("minecraft:falling_dust"); + Particle.FallingDust FALLING_DUST = (Particle.FallingDust) ParticleImpl.get("minecraft:falling_dust"); Particle FIREWORK = ParticleImpl.get("minecraft:firework"); @@ -67,11 +68,13 @@ interface Particles { Particle FLAME = ParticleImpl.get("minecraft:flame"); + Particle INFESTED = ParticleImpl.get("minecraft:infested"); + Particle CHERRY_LEAVES = ParticleImpl.get("minecraft:cherry_leaves"); Particle SCULK_SOUL = ParticleImpl.get("minecraft:sculk_soul"); - Particle SCULK_CHARGE = ParticleImpl.get("minecraft:sculk_charge"); + Particle.SculkCharge SCULK_CHARGE = (Particle.SculkCharge) ParticleImpl.get("minecraft:sculk_charge"); Particle SCULK_CHARGE_POP = ParticleImpl.get("minecraft:sculk_charge_pop"); @@ -89,12 +92,14 @@ interface Particles { Particle INSTANT_EFFECT = ParticleImpl.get("minecraft:instant_effect"); - Particle ITEM = ParticleImpl.get("minecraft:item"); + Particle.Item ITEM = (Particle.Item) ParticleImpl.get("minecraft:item"); - Particle VIBRATION = ParticleImpl.get("minecraft:vibration"); + Particle.Vibration VIBRATION = (Particle.Vibration) ParticleImpl.get("minecraft:vibration"); Particle ITEM_SLIME = ParticleImpl.get("minecraft:item_slime"); + Particle ITEM_COBWEB = ParticleImpl.get("minecraft:item_cobweb"); + Particle ITEM_SNOWBALL = ParticleImpl.get("minecraft:item_snowball"); Particle LARGE_SMOKE = ParticleImpl.get("minecraft:large_smoke"); @@ -197,13 +202,23 @@ interface Particles { Particle SCRAPE = ParticleImpl.get("minecraft:scrape"); - Particle SHRIEK = ParticleImpl.get("minecraft:shriek"); + Particle.Shriek SHRIEK = (Particle.Shriek) ParticleImpl.get("minecraft:shriek"); Particle EGG_CRACK = ParticleImpl.get("minecraft:egg_crack"); Particle DUST_PLUME = ParticleImpl.get("minecraft:dust_plume"); - Particle GUST_DUST = ParticleImpl.get("minecraft:gust_dust"); - Particle TRIAL_SPAWNER_DETECTION = ParticleImpl.get("minecraft:trial_spawner_detection"); + + Particle TRIAL_SPAWNER_DETECTION_OMINOUS = ParticleImpl.get("minecraft:trial_spawner_detection_ominous"); + + Particle VAULT_CONNECTION = ParticleImpl.get("minecraft:vault_connection"); + + Particle.DustPillar DUST_PILLAR = (Particle.DustPillar) ParticleImpl.get("minecraft:dust_pillar"); + + Particle OMINOUS_SPAWNING = ParticleImpl.get("minecraft:ominous_spawning"); + + Particle RAID_OMEN = ParticleImpl.get("minecraft:raid_omen"); + + Particle TRIAL_OMEN = ParticleImpl.get("minecraft:trial_omen"); } diff --git a/src/autogenerated/java/net/minestom/server/potion/PotionEffects.java b/src/autogenerated/java/net/minestom/server/potion/PotionEffects.java index 00189dc4657..6116a8692d8 100644 --- a/src/autogenerated/java/net/minestom/server/potion/PotionEffects.java +++ b/src/autogenerated/java/net/minestom/server/potion/PotionEffects.java @@ -70,4 +70,16 @@ interface PotionEffects { PotionEffect HERO_OF_THE_VILLAGE = PotionEffectImpl.get("minecraft:hero_of_the_village"); PotionEffect DARKNESS = PotionEffectImpl.get("minecraft:darkness"); + + PotionEffect TRIAL_OMEN = PotionEffectImpl.get("minecraft:trial_omen"); + + PotionEffect RAID_OMEN = PotionEffectImpl.get("minecraft:raid_omen"); + + PotionEffect WIND_CHARGED = PotionEffectImpl.get("minecraft:wind_charged"); + + PotionEffect WEAVING = PotionEffectImpl.get("minecraft:weaving"); + + PotionEffect OOZING = PotionEffectImpl.get("minecraft:oozing"); + + PotionEffect INFESTED = PotionEffectImpl.get("minecraft:infested"); } diff --git a/src/autogenerated/java/net/minestom/server/potion/PotionTypes.java b/src/autogenerated/java/net/minestom/server/potion/PotionTypes.java index 91430a1005c..fc078487de8 100644 --- a/src/autogenerated/java/net/minestom/server/potion/PotionTypes.java +++ b/src/autogenerated/java/net/minestom/server/potion/PotionTypes.java @@ -5,8 +5,6 @@ */ @SuppressWarnings("unused") interface PotionTypes { - PotionType EMPTY = PotionTypeImpl.get("minecraft:empty"); - PotionType WATER = PotionTypeImpl.get("minecraft:water"); PotionType MUNDANE = PotionTypeImpl.get("minecraft:mundane"); @@ -90,4 +88,12 @@ interface PotionTypes { PotionType SLOW_FALLING = PotionTypeImpl.get("minecraft:slow_falling"); PotionType LONG_SLOW_FALLING = PotionTypeImpl.get("minecraft:long_slow_falling"); + + PotionType WIND_CHARGED = PotionTypeImpl.get("minecraft:wind_charged"); + + PotionType WEAVING = PotionTypeImpl.get("minecraft:weaving"); + + PotionType OOZING = PotionTypeImpl.get("minecraft:oozing"); + + PotionType INFESTED = PotionTypeImpl.get("minecraft:infested"); } diff --git a/src/autogenerated/java/net/minestom/server/recipe/RecipeType.java b/src/autogenerated/java/net/minestom/server/recipe/RecipeType.java new file mode 100644 index 00000000000..16735c2d46c --- /dev/null +++ b/src/autogenerated/java/net/minestom/server/recipe/RecipeType.java @@ -0,0 +1,76 @@ +package net.minestom.server.recipe; + +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.registry.StaticProtocolObject; +import net.minestom.server.utils.NamespaceID; +import org.jetbrains.annotations.NotNull; + +/** + * AUTOGENERATED by RecipeTypeGenerator + */ +public enum RecipeType implements StaticProtocolObject { + SHAPED(NamespaceID.from("minecraft:crafting_shaped")), + + SHAPELESS(NamespaceID.from("minecraft:crafting_shapeless")), + + SPECIAL_ARMORDYE(NamespaceID.from("minecraft:crafting_special_armordye")), + + SPECIAL_BOOKCLONING(NamespaceID.from("minecraft:crafting_special_bookcloning")), + + SPECIAL_MAPCLONING(NamespaceID.from("minecraft:crafting_special_mapcloning")), + + SPECIAL_MAPEXTENDING(NamespaceID.from("minecraft:crafting_special_mapextending")), + + SPECIAL_FIREWORK_ROCKET(NamespaceID.from("minecraft:crafting_special_firework_rocket")), + + SPECIAL_FIREWORK_STAR(NamespaceID.from("minecraft:crafting_special_firework_star")), + + SPECIAL_FIREWORK_STAR_FADE(NamespaceID.from("minecraft:crafting_special_firework_star_fade")), + + SPECIAL_TIPPEDARROW(NamespaceID.from("minecraft:crafting_special_tippedarrow")), + + SPECIAL_BANNERDUPLICATE(NamespaceID.from("minecraft:crafting_special_bannerduplicate")), + + SPECIAL_SHIELDDECORATION(NamespaceID.from("minecraft:crafting_special_shielddecoration")), + + SPECIAL_SHULKERBOXCOLORING(NamespaceID.from("minecraft:crafting_special_shulkerboxcoloring")), + + SPECIAL_SUSPICIOUSSTEW(NamespaceID.from("minecraft:crafting_special_suspiciousstew")), + + SPECIAL_REPAIRITEM(NamespaceID.from("minecraft:crafting_special_repairitem")), + + SMELTING(NamespaceID.from("minecraft:smelting")), + + BLASTING(NamespaceID.from("minecraft:blasting")), + + SMOKING(NamespaceID.from("minecraft:smoking")), + + CAMPFIRE_COOKING(NamespaceID.from("minecraft:campfire_cooking")), + + STONECUTTING(NamespaceID.from("minecraft:stonecutting")), + + SMITHING_TRANSFORM(NamespaceID.from("minecraft:smithing_transform")), + + SMITHING_TRIM(NamespaceID.from("minecraft:smithing_trim")), + + DECORATED_POT(NamespaceID.from("minecraft:crafting_decorated_pot")); + + public static final NetworkBuffer.Type NETWORK_TYPE = NetworkBuffer.Enum(RecipeType.class); + + private final NamespaceID namespace; + + RecipeType(@NotNull NamespaceID namespace) { + this.namespace = namespace; + } + + @NotNull + @Override + public NamespaceID namespace() { + return this.namespace; + } + + @Override + public int id() { + return this.ordinal(); + } +} diff --git a/src/autogenerated/java/net/minestom/server/registry/FluidRegistries.java b/src/autogenerated/java/net/minestom/server/registry/FluidRegistries.java new file mode 100644 index 00000000000..f6e23118b11 --- /dev/null +++ b/src/autogenerated/java/net/minestom/server/registry/FluidRegistries.java @@ -0,0 +1,45 @@ +// AUTOGENERATED by net.minestom.codegen.RegistriesGenerator +package net.minestom.server.registry; + +import net.kyori.adventure.key.Key; +import net.minestom.server.fluid.Fluid; +import net.minestom.server.utils.NamespaceID; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; + +/** + * AUTOGENERATED + */ +public final class FluidRegistries { + + /** + * Should only be used for internal code, please use the get* methods. + */ + @Deprecated + public static final HashMap fluids = new HashMap<>(); + + /** + * Returns the corresponding Fluid matching the given id. Returns 'EMPTY' if none match. + */ + @NotNull + public static Fluid getFluid(String id) { + return getFluid(NamespaceID.from(id)); + } + + /** + * Returns the corresponding Fluid matching the given id. Returns 'EMPTY' if none match. + */ + @NotNull + public static Fluid getFluid(NamespaceID id) { + return fluids.getOrDefault(id, Fluid.EMPTY); + } + + /** + * Returns the corresponding Fluid matching the given key. Returns 'EMPTY' if none match. + */ + @NotNull + public static Fluid getFluid(Key key) { + return getFluid(NamespaceID.from(key)); + } +} diff --git a/src/autogenerated/java/net/minestom/server/sound/SoundEvents.java b/src/autogenerated/java/net/minestom/server/sound/SoundEvents.java index bd1d0287fa4..f886a7aa7eb 100644 --- a/src/autogenerated/java/net/minestom/server/sound/SoundEvents.java +++ b/src/autogenerated/java/net/minestom/server/sound/SoundEvents.java @@ -5,3081 +5,3225 @@ */ @SuppressWarnings("unused") interface SoundEvents { - SoundEvent ENTITY_ALLAY_AMBIENT_WITH_ITEM = SoundEventImpl.get("minecraft:entity.allay.ambient_with_item"); + SoundEvent ENTITY_ALLAY_AMBIENT_WITH_ITEM = BuiltinSoundEvent.get("minecraft:entity.allay.ambient_with_item"); - SoundEvent ENTITY_ALLAY_AMBIENT_WITHOUT_ITEM = SoundEventImpl.get("minecraft:entity.allay.ambient_without_item"); + SoundEvent ENTITY_ALLAY_AMBIENT_WITHOUT_ITEM = BuiltinSoundEvent.get("minecraft:entity.allay.ambient_without_item"); - SoundEvent ENTITY_ALLAY_DEATH = SoundEventImpl.get("minecraft:entity.allay.death"); + SoundEvent ENTITY_ALLAY_DEATH = BuiltinSoundEvent.get("minecraft:entity.allay.death"); - SoundEvent ENTITY_ALLAY_HURT = SoundEventImpl.get("minecraft:entity.allay.hurt"); + SoundEvent ENTITY_ALLAY_HURT = BuiltinSoundEvent.get("minecraft:entity.allay.hurt"); - SoundEvent ENTITY_ALLAY_ITEM_GIVEN = SoundEventImpl.get("minecraft:entity.allay.item_given"); + SoundEvent ENTITY_ALLAY_ITEM_GIVEN = BuiltinSoundEvent.get("minecraft:entity.allay.item_given"); - SoundEvent ENTITY_ALLAY_ITEM_TAKEN = SoundEventImpl.get("minecraft:entity.allay.item_taken"); + SoundEvent ENTITY_ALLAY_ITEM_TAKEN = BuiltinSoundEvent.get("minecraft:entity.allay.item_taken"); - SoundEvent ENTITY_ALLAY_ITEM_THROWN = SoundEventImpl.get("minecraft:entity.allay.item_thrown"); + SoundEvent ENTITY_ALLAY_ITEM_THROWN = BuiltinSoundEvent.get("minecraft:entity.allay.item_thrown"); - SoundEvent AMBIENT_CAVE = SoundEventImpl.get("minecraft:ambient.cave"); + SoundEvent AMBIENT_CAVE = BuiltinSoundEvent.get("minecraft:ambient.cave"); - SoundEvent AMBIENT_BASALT_DELTAS_ADDITIONS = SoundEventImpl.get("minecraft:ambient.basalt_deltas.additions"); + SoundEvent AMBIENT_BASALT_DELTAS_ADDITIONS = BuiltinSoundEvent.get("minecraft:ambient.basalt_deltas.additions"); - SoundEvent AMBIENT_BASALT_DELTAS_LOOP = SoundEventImpl.get("minecraft:ambient.basalt_deltas.loop"); + SoundEvent AMBIENT_BASALT_DELTAS_LOOP = BuiltinSoundEvent.get("minecraft:ambient.basalt_deltas.loop"); - SoundEvent AMBIENT_BASALT_DELTAS_MOOD = SoundEventImpl.get("minecraft:ambient.basalt_deltas.mood"); + SoundEvent AMBIENT_BASALT_DELTAS_MOOD = BuiltinSoundEvent.get("minecraft:ambient.basalt_deltas.mood"); - SoundEvent AMBIENT_CRIMSON_FOREST_ADDITIONS = SoundEventImpl.get("minecraft:ambient.crimson_forest.additions"); + SoundEvent AMBIENT_CRIMSON_FOREST_ADDITIONS = BuiltinSoundEvent.get("minecraft:ambient.crimson_forest.additions"); - SoundEvent AMBIENT_CRIMSON_FOREST_LOOP = SoundEventImpl.get("minecraft:ambient.crimson_forest.loop"); + SoundEvent AMBIENT_CRIMSON_FOREST_LOOP = BuiltinSoundEvent.get("minecraft:ambient.crimson_forest.loop"); - SoundEvent AMBIENT_CRIMSON_FOREST_MOOD = SoundEventImpl.get("minecraft:ambient.crimson_forest.mood"); + SoundEvent AMBIENT_CRIMSON_FOREST_MOOD = BuiltinSoundEvent.get("minecraft:ambient.crimson_forest.mood"); - SoundEvent AMBIENT_NETHER_WASTES_ADDITIONS = SoundEventImpl.get("minecraft:ambient.nether_wastes.additions"); + SoundEvent AMBIENT_NETHER_WASTES_ADDITIONS = BuiltinSoundEvent.get("minecraft:ambient.nether_wastes.additions"); - SoundEvent AMBIENT_NETHER_WASTES_LOOP = SoundEventImpl.get("minecraft:ambient.nether_wastes.loop"); + SoundEvent AMBIENT_NETHER_WASTES_LOOP = BuiltinSoundEvent.get("minecraft:ambient.nether_wastes.loop"); - SoundEvent AMBIENT_NETHER_WASTES_MOOD = SoundEventImpl.get("minecraft:ambient.nether_wastes.mood"); + SoundEvent AMBIENT_NETHER_WASTES_MOOD = BuiltinSoundEvent.get("minecraft:ambient.nether_wastes.mood"); - SoundEvent AMBIENT_SOUL_SAND_VALLEY_ADDITIONS = SoundEventImpl.get("minecraft:ambient.soul_sand_valley.additions"); + SoundEvent AMBIENT_SOUL_SAND_VALLEY_ADDITIONS = BuiltinSoundEvent.get("minecraft:ambient.soul_sand_valley.additions"); - SoundEvent AMBIENT_SOUL_SAND_VALLEY_LOOP = SoundEventImpl.get("minecraft:ambient.soul_sand_valley.loop"); + SoundEvent AMBIENT_SOUL_SAND_VALLEY_LOOP = BuiltinSoundEvent.get("minecraft:ambient.soul_sand_valley.loop"); - SoundEvent AMBIENT_SOUL_SAND_VALLEY_MOOD = SoundEventImpl.get("minecraft:ambient.soul_sand_valley.mood"); + SoundEvent AMBIENT_SOUL_SAND_VALLEY_MOOD = BuiltinSoundEvent.get("minecraft:ambient.soul_sand_valley.mood"); - SoundEvent AMBIENT_WARPED_FOREST_ADDITIONS = SoundEventImpl.get("minecraft:ambient.warped_forest.additions"); + SoundEvent AMBIENT_WARPED_FOREST_ADDITIONS = BuiltinSoundEvent.get("minecraft:ambient.warped_forest.additions"); - SoundEvent AMBIENT_WARPED_FOREST_LOOP = SoundEventImpl.get("minecraft:ambient.warped_forest.loop"); + SoundEvent AMBIENT_WARPED_FOREST_LOOP = BuiltinSoundEvent.get("minecraft:ambient.warped_forest.loop"); - SoundEvent AMBIENT_WARPED_FOREST_MOOD = SoundEventImpl.get("minecraft:ambient.warped_forest.mood"); + SoundEvent AMBIENT_WARPED_FOREST_MOOD = BuiltinSoundEvent.get("minecraft:ambient.warped_forest.mood"); - SoundEvent AMBIENT_UNDERWATER_ENTER = SoundEventImpl.get("minecraft:ambient.underwater.enter"); + SoundEvent AMBIENT_UNDERWATER_ENTER = BuiltinSoundEvent.get("minecraft:ambient.underwater.enter"); - SoundEvent AMBIENT_UNDERWATER_EXIT = SoundEventImpl.get("minecraft:ambient.underwater.exit"); + SoundEvent AMBIENT_UNDERWATER_EXIT = BuiltinSoundEvent.get("minecraft:ambient.underwater.exit"); - SoundEvent AMBIENT_UNDERWATER_LOOP = SoundEventImpl.get("minecraft:ambient.underwater.loop"); + SoundEvent AMBIENT_UNDERWATER_LOOP = BuiltinSoundEvent.get("minecraft:ambient.underwater.loop"); - SoundEvent AMBIENT_UNDERWATER_LOOP_ADDITIONS = SoundEventImpl.get("minecraft:ambient.underwater.loop.additions"); + SoundEvent AMBIENT_UNDERWATER_LOOP_ADDITIONS = BuiltinSoundEvent.get("minecraft:ambient.underwater.loop.additions"); - SoundEvent AMBIENT_UNDERWATER_LOOP_ADDITIONS_RARE = SoundEventImpl.get("minecraft:ambient.underwater.loop.additions.rare"); + SoundEvent AMBIENT_UNDERWATER_LOOP_ADDITIONS_RARE = BuiltinSoundEvent.get("minecraft:ambient.underwater.loop.additions.rare"); - SoundEvent AMBIENT_UNDERWATER_LOOP_ADDITIONS_ULTRA_RARE = SoundEventImpl.get("minecraft:ambient.underwater.loop.additions.ultra_rare"); + SoundEvent AMBIENT_UNDERWATER_LOOP_ADDITIONS_ULTRA_RARE = BuiltinSoundEvent.get("minecraft:ambient.underwater.loop.additions.ultra_rare"); - SoundEvent BLOCK_AMETHYST_BLOCK_BREAK = SoundEventImpl.get("minecraft:block.amethyst_block.break"); + SoundEvent BLOCK_AMETHYST_BLOCK_BREAK = BuiltinSoundEvent.get("minecraft:block.amethyst_block.break"); - SoundEvent BLOCK_AMETHYST_BLOCK_CHIME = SoundEventImpl.get("minecraft:block.amethyst_block.chime"); + SoundEvent BLOCK_AMETHYST_BLOCK_CHIME = BuiltinSoundEvent.get("minecraft:block.amethyst_block.chime"); - SoundEvent BLOCK_AMETHYST_BLOCK_FALL = SoundEventImpl.get("minecraft:block.amethyst_block.fall"); + SoundEvent BLOCK_AMETHYST_BLOCK_FALL = BuiltinSoundEvent.get("minecraft:block.amethyst_block.fall"); - SoundEvent BLOCK_AMETHYST_BLOCK_HIT = SoundEventImpl.get("minecraft:block.amethyst_block.hit"); + SoundEvent BLOCK_AMETHYST_BLOCK_HIT = BuiltinSoundEvent.get("minecraft:block.amethyst_block.hit"); - SoundEvent BLOCK_AMETHYST_BLOCK_PLACE = SoundEventImpl.get("minecraft:block.amethyst_block.place"); + SoundEvent BLOCK_AMETHYST_BLOCK_PLACE = BuiltinSoundEvent.get("minecraft:block.amethyst_block.place"); - SoundEvent BLOCK_AMETHYST_BLOCK_RESONATE = SoundEventImpl.get("minecraft:block.amethyst_block.resonate"); + SoundEvent BLOCK_AMETHYST_BLOCK_RESONATE = BuiltinSoundEvent.get("minecraft:block.amethyst_block.resonate"); - SoundEvent BLOCK_AMETHYST_BLOCK_STEP = SoundEventImpl.get("minecraft:block.amethyst_block.step"); + SoundEvent BLOCK_AMETHYST_BLOCK_STEP = BuiltinSoundEvent.get("minecraft:block.amethyst_block.step"); - SoundEvent BLOCK_AMETHYST_CLUSTER_BREAK = SoundEventImpl.get("minecraft:block.amethyst_cluster.break"); + SoundEvent BLOCK_AMETHYST_CLUSTER_BREAK = BuiltinSoundEvent.get("minecraft:block.amethyst_cluster.break"); - SoundEvent BLOCK_AMETHYST_CLUSTER_FALL = SoundEventImpl.get("minecraft:block.amethyst_cluster.fall"); + SoundEvent BLOCK_AMETHYST_CLUSTER_FALL = BuiltinSoundEvent.get("minecraft:block.amethyst_cluster.fall"); - SoundEvent BLOCK_AMETHYST_CLUSTER_HIT = SoundEventImpl.get("minecraft:block.amethyst_cluster.hit"); + SoundEvent BLOCK_AMETHYST_CLUSTER_HIT = BuiltinSoundEvent.get("minecraft:block.amethyst_cluster.hit"); - SoundEvent BLOCK_AMETHYST_CLUSTER_PLACE = SoundEventImpl.get("minecraft:block.amethyst_cluster.place"); + SoundEvent BLOCK_AMETHYST_CLUSTER_PLACE = BuiltinSoundEvent.get("minecraft:block.amethyst_cluster.place"); - SoundEvent BLOCK_AMETHYST_CLUSTER_STEP = SoundEventImpl.get("minecraft:block.amethyst_cluster.step"); + SoundEvent BLOCK_AMETHYST_CLUSTER_STEP = BuiltinSoundEvent.get("minecraft:block.amethyst_cluster.step"); - SoundEvent BLOCK_ANCIENT_DEBRIS_BREAK = SoundEventImpl.get("minecraft:block.ancient_debris.break"); + SoundEvent BLOCK_ANCIENT_DEBRIS_BREAK = BuiltinSoundEvent.get("minecraft:block.ancient_debris.break"); - SoundEvent BLOCK_ANCIENT_DEBRIS_STEP = SoundEventImpl.get("minecraft:block.ancient_debris.step"); + SoundEvent BLOCK_ANCIENT_DEBRIS_STEP = BuiltinSoundEvent.get("minecraft:block.ancient_debris.step"); - SoundEvent BLOCK_ANCIENT_DEBRIS_PLACE = SoundEventImpl.get("minecraft:block.ancient_debris.place"); + SoundEvent BLOCK_ANCIENT_DEBRIS_PLACE = BuiltinSoundEvent.get("minecraft:block.ancient_debris.place"); - SoundEvent BLOCK_ANCIENT_DEBRIS_HIT = SoundEventImpl.get("minecraft:block.ancient_debris.hit"); + SoundEvent BLOCK_ANCIENT_DEBRIS_HIT = BuiltinSoundEvent.get("minecraft:block.ancient_debris.hit"); - SoundEvent BLOCK_ANCIENT_DEBRIS_FALL = SoundEventImpl.get("minecraft:block.ancient_debris.fall"); + SoundEvent BLOCK_ANCIENT_DEBRIS_FALL = BuiltinSoundEvent.get("minecraft:block.ancient_debris.fall"); - SoundEvent BLOCK_ANVIL_BREAK = SoundEventImpl.get("minecraft:block.anvil.break"); + SoundEvent BLOCK_ANVIL_BREAK = BuiltinSoundEvent.get("minecraft:block.anvil.break"); - SoundEvent BLOCK_ANVIL_DESTROY = SoundEventImpl.get("minecraft:block.anvil.destroy"); + SoundEvent BLOCK_ANVIL_DESTROY = BuiltinSoundEvent.get("minecraft:block.anvil.destroy"); - SoundEvent BLOCK_ANVIL_FALL = SoundEventImpl.get("minecraft:block.anvil.fall"); + SoundEvent BLOCK_ANVIL_FALL = BuiltinSoundEvent.get("minecraft:block.anvil.fall"); - SoundEvent BLOCK_ANVIL_HIT = SoundEventImpl.get("minecraft:block.anvil.hit"); + SoundEvent BLOCK_ANVIL_HIT = BuiltinSoundEvent.get("minecraft:block.anvil.hit"); - SoundEvent BLOCK_ANVIL_LAND = SoundEventImpl.get("minecraft:block.anvil.land"); + SoundEvent BLOCK_ANVIL_LAND = BuiltinSoundEvent.get("minecraft:block.anvil.land"); - SoundEvent BLOCK_ANVIL_PLACE = SoundEventImpl.get("minecraft:block.anvil.place"); + SoundEvent BLOCK_ANVIL_PLACE = BuiltinSoundEvent.get("minecraft:block.anvil.place"); - SoundEvent BLOCK_ANVIL_STEP = SoundEventImpl.get("minecraft:block.anvil.step"); + SoundEvent BLOCK_ANVIL_STEP = BuiltinSoundEvent.get("minecraft:block.anvil.step"); - SoundEvent BLOCK_ANVIL_USE = SoundEventImpl.get("minecraft:block.anvil.use"); + SoundEvent BLOCK_ANVIL_USE = BuiltinSoundEvent.get("minecraft:block.anvil.use"); - SoundEvent ITEM_ARMOR_EQUIP_CHAIN = SoundEventImpl.get("minecraft:item.armor.equip_chain"); + SoundEvent ENTITY_ARMADILLO_EAT = BuiltinSoundEvent.get("minecraft:entity.armadillo.eat"); - SoundEvent ITEM_ARMOR_EQUIP_DIAMOND = SoundEventImpl.get("minecraft:item.armor.equip_diamond"); + SoundEvent ENTITY_ARMADILLO_HURT = BuiltinSoundEvent.get("minecraft:entity.armadillo.hurt"); - SoundEvent ITEM_ARMOR_EQUIP_ELYTRA = SoundEventImpl.get("minecraft:item.armor.equip_elytra"); + SoundEvent ENTITY_ARMADILLO_HURT_REDUCED = BuiltinSoundEvent.get("minecraft:entity.armadillo.hurt_reduced"); - SoundEvent ITEM_ARMOR_EQUIP_GENERIC = SoundEventImpl.get("minecraft:item.armor.equip_generic"); + SoundEvent ENTITY_ARMADILLO_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.armadillo.ambient"); - SoundEvent ITEM_ARMOR_EQUIP_GOLD = SoundEventImpl.get("minecraft:item.armor.equip_gold"); + SoundEvent ENTITY_ARMADILLO_STEP = BuiltinSoundEvent.get("minecraft:entity.armadillo.step"); - SoundEvent ITEM_ARMOR_EQUIP_IRON = SoundEventImpl.get("minecraft:item.armor.equip_iron"); + SoundEvent ENTITY_ARMADILLO_DEATH = BuiltinSoundEvent.get("minecraft:entity.armadillo.death"); - SoundEvent ITEM_ARMOR_EQUIP_LEATHER = SoundEventImpl.get("minecraft:item.armor.equip_leather"); + SoundEvent ENTITY_ARMADILLO_ROLL = BuiltinSoundEvent.get("minecraft:entity.armadillo.roll"); - SoundEvent ITEM_ARMOR_EQUIP_NETHERITE = SoundEventImpl.get("minecraft:item.armor.equip_netherite"); + SoundEvent ENTITY_ARMADILLO_LAND = BuiltinSoundEvent.get("minecraft:entity.armadillo.land"); - SoundEvent ITEM_ARMOR_EQUIP_TURTLE = SoundEventImpl.get("minecraft:item.armor.equip_turtle"); + SoundEvent ENTITY_ARMADILLO_SCUTE_DROP = BuiltinSoundEvent.get("minecraft:entity.armadillo.scute_drop"); - SoundEvent ENTITY_ARMOR_STAND_BREAK = SoundEventImpl.get("minecraft:entity.armor_stand.break"); + SoundEvent ENTITY_ARMADILLO_UNROLL_FINISH = BuiltinSoundEvent.get("minecraft:entity.armadillo.unroll_finish"); - SoundEvent ENTITY_ARMOR_STAND_FALL = SoundEventImpl.get("minecraft:entity.armor_stand.fall"); + SoundEvent ENTITY_ARMADILLO_PEEK = BuiltinSoundEvent.get("minecraft:entity.armadillo.peek"); - SoundEvent ENTITY_ARMOR_STAND_HIT = SoundEventImpl.get("minecraft:entity.armor_stand.hit"); + SoundEvent ENTITY_ARMADILLO_UNROLL_START = BuiltinSoundEvent.get("minecraft:entity.armadillo.unroll_start"); - SoundEvent ENTITY_ARMOR_STAND_PLACE = SoundEventImpl.get("minecraft:entity.armor_stand.place"); + SoundEvent ENTITY_ARMADILLO_BRUSH = BuiltinSoundEvent.get("minecraft:entity.armadillo.brush"); - SoundEvent ENTITY_ARROW_HIT = SoundEventImpl.get("minecraft:entity.arrow.hit"); + SoundEvent ITEM_ARMOR_EQUIP_CHAIN = BuiltinSoundEvent.get("minecraft:item.armor.equip_chain"); - SoundEvent ENTITY_ARROW_HIT_PLAYER = SoundEventImpl.get("minecraft:entity.arrow.hit_player"); + SoundEvent ITEM_ARMOR_EQUIP_DIAMOND = BuiltinSoundEvent.get("minecraft:item.armor.equip_diamond"); - SoundEvent ENTITY_ARROW_SHOOT = SoundEventImpl.get("minecraft:entity.arrow.shoot"); + SoundEvent ITEM_ARMOR_EQUIP_ELYTRA = BuiltinSoundEvent.get("minecraft:item.armor.equip_elytra"); - SoundEvent ITEM_AXE_STRIP = SoundEventImpl.get("minecraft:item.axe.strip"); + SoundEvent ITEM_ARMOR_EQUIP_GENERIC = BuiltinSoundEvent.get("minecraft:item.armor.equip_generic"); - SoundEvent ITEM_AXE_SCRAPE = SoundEventImpl.get("minecraft:item.axe.scrape"); + SoundEvent ITEM_ARMOR_EQUIP_GOLD = BuiltinSoundEvent.get("minecraft:item.armor.equip_gold"); - SoundEvent ITEM_AXE_WAX_OFF = SoundEventImpl.get("minecraft:item.axe.wax_off"); + SoundEvent ITEM_ARMOR_EQUIP_IRON = BuiltinSoundEvent.get("minecraft:item.armor.equip_iron"); - SoundEvent ENTITY_AXOLOTL_ATTACK = SoundEventImpl.get("minecraft:entity.axolotl.attack"); + SoundEvent ITEM_ARMOR_EQUIP_LEATHER = BuiltinSoundEvent.get("minecraft:item.armor.equip_leather"); - SoundEvent ENTITY_AXOLOTL_DEATH = SoundEventImpl.get("minecraft:entity.axolotl.death"); + SoundEvent ITEM_ARMOR_EQUIP_NETHERITE = BuiltinSoundEvent.get("minecraft:item.armor.equip_netherite"); - SoundEvent ENTITY_AXOLOTL_HURT = SoundEventImpl.get("minecraft:entity.axolotl.hurt"); + SoundEvent ITEM_ARMOR_EQUIP_TURTLE = BuiltinSoundEvent.get("minecraft:item.armor.equip_turtle"); - SoundEvent ENTITY_AXOLOTL_IDLE_AIR = SoundEventImpl.get("minecraft:entity.axolotl.idle_air"); + SoundEvent ITEM_ARMOR_EQUIP_WOLF = BuiltinSoundEvent.get("minecraft:item.armor.equip_wolf"); - SoundEvent ENTITY_AXOLOTL_IDLE_WATER = SoundEventImpl.get("minecraft:entity.axolotl.idle_water"); + SoundEvent ITEM_ARMOR_UNEQUIP_WOLF = BuiltinSoundEvent.get("minecraft:item.armor.unequip_wolf"); - SoundEvent ENTITY_AXOLOTL_SPLASH = SoundEventImpl.get("minecraft:entity.axolotl.splash"); + SoundEvent ENTITY_ARMOR_STAND_BREAK = BuiltinSoundEvent.get("minecraft:entity.armor_stand.break"); - SoundEvent ENTITY_AXOLOTL_SWIM = SoundEventImpl.get("minecraft:entity.axolotl.swim"); + SoundEvent ENTITY_ARMOR_STAND_FALL = BuiltinSoundEvent.get("minecraft:entity.armor_stand.fall"); - SoundEvent BLOCK_AZALEA_BREAK = SoundEventImpl.get("minecraft:block.azalea.break"); + SoundEvent ENTITY_ARMOR_STAND_HIT = BuiltinSoundEvent.get("minecraft:entity.armor_stand.hit"); - SoundEvent BLOCK_AZALEA_FALL = SoundEventImpl.get("minecraft:block.azalea.fall"); + SoundEvent ENTITY_ARMOR_STAND_PLACE = BuiltinSoundEvent.get("minecraft:entity.armor_stand.place"); - SoundEvent BLOCK_AZALEA_HIT = SoundEventImpl.get("minecraft:block.azalea.hit"); + SoundEvent ENTITY_ARROW_HIT = BuiltinSoundEvent.get("minecraft:entity.arrow.hit"); - SoundEvent BLOCK_AZALEA_PLACE = SoundEventImpl.get("minecraft:block.azalea.place"); + SoundEvent ENTITY_ARROW_HIT_PLAYER = BuiltinSoundEvent.get("minecraft:entity.arrow.hit_player"); - SoundEvent BLOCK_AZALEA_STEP = SoundEventImpl.get("minecraft:block.azalea.step"); + SoundEvent ENTITY_ARROW_SHOOT = BuiltinSoundEvent.get("minecraft:entity.arrow.shoot"); - SoundEvent BLOCK_AZALEA_LEAVES_BREAK = SoundEventImpl.get("minecraft:block.azalea_leaves.break"); + SoundEvent ITEM_AXE_STRIP = BuiltinSoundEvent.get("minecraft:item.axe.strip"); - SoundEvent BLOCK_AZALEA_LEAVES_FALL = SoundEventImpl.get("minecraft:block.azalea_leaves.fall"); + SoundEvent ITEM_AXE_SCRAPE = BuiltinSoundEvent.get("minecraft:item.axe.scrape"); - SoundEvent BLOCK_AZALEA_LEAVES_HIT = SoundEventImpl.get("minecraft:block.azalea_leaves.hit"); + SoundEvent ITEM_AXE_WAX_OFF = BuiltinSoundEvent.get("minecraft:item.axe.wax_off"); - SoundEvent BLOCK_AZALEA_LEAVES_PLACE = SoundEventImpl.get("minecraft:block.azalea_leaves.place"); + SoundEvent ENTITY_AXOLOTL_ATTACK = BuiltinSoundEvent.get("minecraft:entity.axolotl.attack"); - SoundEvent BLOCK_AZALEA_LEAVES_STEP = SoundEventImpl.get("minecraft:block.azalea_leaves.step"); + SoundEvent ENTITY_AXOLOTL_DEATH = BuiltinSoundEvent.get("minecraft:entity.axolotl.death"); - SoundEvent BLOCK_BAMBOO_BREAK = SoundEventImpl.get("minecraft:block.bamboo.break"); + SoundEvent ENTITY_AXOLOTL_HURT = BuiltinSoundEvent.get("minecraft:entity.axolotl.hurt"); - SoundEvent BLOCK_BAMBOO_FALL = SoundEventImpl.get("minecraft:block.bamboo.fall"); + SoundEvent ENTITY_AXOLOTL_IDLE_AIR = BuiltinSoundEvent.get("minecraft:entity.axolotl.idle_air"); - SoundEvent BLOCK_BAMBOO_HIT = SoundEventImpl.get("minecraft:block.bamboo.hit"); + SoundEvent ENTITY_AXOLOTL_IDLE_WATER = BuiltinSoundEvent.get("minecraft:entity.axolotl.idle_water"); - SoundEvent BLOCK_BAMBOO_PLACE = SoundEventImpl.get("minecraft:block.bamboo.place"); + SoundEvent ENTITY_AXOLOTL_SPLASH = BuiltinSoundEvent.get("minecraft:entity.axolotl.splash"); - SoundEvent BLOCK_BAMBOO_STEP = SoundEventImpl.get("minecraft:block.bamboo.step"); + SoundEvent ENTITY_AXOLOTL_SWIM = BuiltinSoundEvent.get("minecraft:entity.axolotl.swim"); - SoundEvent BLOCK_BAMBOO_SAPLING_BREAK = SoundEventImpl.get("minecraft:block.bamboo_sapling.break"); + SoundEvent BLOCK_AZALEA_BREAK = BuiltinSoundEvent.get("minecraft:block.azalea.break"); - SoundEvent BLOCK_BAMBOO_SAPLING_HIT = SoundEventImpl.get("minecraft:block.bamboo_sapling.hit"); + SoundEvent BLOCK_AZALEA_FALL = BuiltinSoundEvent.get("minecraft:block.azalea.fall"); - SoundEvent BLOCK_BAMBOO_SAPLING_PLACE = SoundEventImpl.get("minecraft:block.bamboo_sapling.place"); + SoundEvent BLOCK_AZALEA_HIT = BuiltinSoundEvent.get("minecraft:block.azalea.hit"); - SoundEvent BLOCK_BAMBOO_WOOD_BREAK = SoundEventImpl.get("minecraft:block.bamboo_wood.break"); + SoundEvent BLOCK_AZALEA_PLACE = BuiltinSoundEvent.get("minecraft:block.azalea.place"); - SoundEvent BLOCK_BAMBOO_WOOD_FALL = SoundEventImpl.get("minecraft:block.bamboo_wood.fall"); + SoundEvent BLOCK_AZALEA_STEP = BuiltinSoundEvent.get("minecraft:block.azalea.step"); - SoundEvent BLOCK_BAMBOO_WOOD_HIT = SoundEventImpl.get("minecraft:block.bamboo_wood.hit"); + SoundEvent BLOCK_AZALEA_LEAVES_BREAK = BuiltinSoundEvent.get("minecraft:block.azalea_leaves.break"); - SoundEvent BLOCK_BAMBOO_WOOD_PLACE = SoundEventImpl.get("minecraft:block.bamboo_wood.place"); + SoundEvent BLOCK_AZALEA_LEAVES_FALL = BuiltinSoundEvent.get("minecraft:block.azalea_leaves.fall"); - SoundEvent BLOCK_BAMBOO_WOOD_STEP = SoundEventImpl.get("minecraft:block.bamboo_wood.step"); + SoundEvent BLOCK_AZALEA_LEAVES_HIT = BuiltinSoundEvent.get("minecraft:block.azalea_leaves.hit"); - SoundEvent BLOCK_BAMBOO_WOOD_DOOR_CLOSE = SoundEventImpl.get("minecraft:block.bamboo_wood_door.close"); + SoundEvent BLOCK_AZALEA_LEAVES_PLACE = BuiltinSoundEvent.get("minecraft:block.azalea_leaves.place"); - SoundEvent BLOCK_BAMBOO_WOOD_DOOR_OPEN = SoundEventImpl.get("minecraft:block.bamboo_wood_door.open"); + SoundEvent BLOCK_AZALEA_LEAVES_STEP = BuiltinSoundEvent.get("minecraft:block.azalea_leaves.step"); - SoundEvent BLOCK_BAMBOO_WOOD_TRAPDOOR_CLOSE = SoundEventImpl.get("minecraft:block.bamboo_wood_trapdoor.close"); + SoundEvent BLOCK_BAMBOO_BREAK = BuiltinSoundEvent.get("minecraft:block.bamboo.break"); - SoundEvent BLOCK_BAMBOO_WOOD_TRAPDOOR_OPEN = SoundEventImpl.get("minecraft:block.bamboo_wood_trapdoor.open"); + SoundEvent BLOCK_BAMBOO_FALL = BuiltinSoundEvent.get("minecraft:block.bamboo.fall"); - SoundEvent BLOCK_BAMBOO_WOOD_BUTTON_CLICK_OFF = SoundEventImpl.get("minecraft:block.bamboo_wood_button.click_off"); + SoundEvent BLOCK_BAMBOO_HIT = BuiltinSoundEvent.get("minecraft:block.bamboo.hit"); - SoundEvent BLOCK_BAMBOO_WOOD_BUTTON_CLICK_ON = SoundEventImpl.get("minecraft:block.bamboo_wood_button.click_on"); + SoundEvent BLOCK_BAMBOO_PLACE = BuiltinSoundEvent.get("minecraft:block.bamboo.place"); - SoundEvent BLOCK_BAMBOO_WOOD_PRESSURE_PLATE_CLICK_OFF = SoundEventImpl.get("minecraft:block.bamboo_wood_pressure_plate.click_off"); + SoundEvent BLOCK_BAMBOO_STEP = BuiltinSoundEvent.get("minecraft:block.bamboo.step"); - SoundEvent BLOCK_BAMBOO_WOOD_PRESSURE_PLATE_CLICK_ON = SoundEventImpl.get("minecraft:block.bamboo_wood_pressure_plate.click_on"); + SoundEvent BLOCK_BAMBOO_SAPLING_BREAK = BuiltinSoundEvent.get("minecraft:block.bamboo_sapling.break"); - SoundEvent BLOCK_BAMBOO_WOOD_FENCE_GATE_CLOSE = SoundEventImpl.get("minecraft:block.bamboo_wood_fence_gate.close"); + SoundEvent BLOCK_BAMBOO_SAPLING_HIT = BuiltinSoundEvent.get("minecraft:block.bamboo_sapling.hit"); - SoundEvent BLOCK_BAMBOO_WOOD_FENCE_GATE_OPEN = SoundEventImpl.get("minecraft:block.bamboo_wood_fence_gate.open"); + SoundEvent BLOCK_BAMBOO_SAPLING_PLACE = BuiltinSoundEvent.get("minecraft:block.bamboo_sapling.place"); - SoundEvent BLOCK_BARREL_CLOSE = SoundEventImpl.get("minecraft:block.barrel.close"); + SoundEvent BLOCK_BAMBOO_WOOD_BREAK = BuiltinSoundEvent.get("minecraft:block.bamboo_wood.break"); - SoundEvent BLOCK_BARREL_OPEN = SoundEventImpl.get("minecraft:block.barrel.open"); + SoundEvent BLOCK_BAMBOO_WOOD_FALL = BuiltinSoundEvent.get("minecraft:block.bamboo_wood.fall"); - SoundEvent BLOCK_BASALT_BREAK = SoundEventImpl.get("minecraft:block.basalt.break"); + SoundEvent BLOCK_BAMBOO_WOOD_HIT = BuiltinSoundEvent.get("minecraft:block.bamboo_wood.hit"); - SoundEvent BLOCK_BASALT_STEP = SoundEventImpl.get("minecraft:block.basalt.step"); + SoundEvent BLOCK_BAMBOO_WOOD_PLACE = BuiltinSoundEvent.get("minecraft:block.bamboo_wood.place"); - SoundEvent BLOCK_BASALT_PLACE = SoundEventImpl.get("minecraft:block.basalt.place"); + SoundEvent BLOCK_BAMBOO_WOOD_STEP = BuiltinSoundEvent.get("minecraft:block.bamboo_wood.step"); - SoundEvent BLOCK_BASALT_HIT = SoundEventImpl.get("minecraft:block.basalt.hit"); + SoundEvent BLOCK_BAMBOO_WOOD_DOOR_CLOSE = BuiltinSoundEvent.get("minecraft:block.bamboo_wood_door.close"); - SoundEvent BLOCK_BASALT_FALL = SoundEventImpl.get("minecraft:block.basalt.fall"); + SoundEvent BLOCK_BAMBOO_WOOD_DOOR_OPEN = BuiltinSoundEvent.get("minecraft:block.bamboo_wood_door.open"); - SoundEvent ENTITY_BAT_AMBIENT = SoundEventImpl.get("minecraft:entity.bat.ambient"); + SoundEvent BLOCK_BAMBOO_WOOD_TRAPDOOR_CLOSE = BuiltinSoundEvent.get("minecraft:block.bamboo_wood_trapdoor.close"); - SoundEvent ENTITY_BAT_DEATH = SoundEventImpl.get("minecraft:entity.bat.death"); + SoundEvent BLOCK_BAMBOO_WOOD_TRAPDOOR_OPEN = BuiltinSoundEvent.get("minecraft:block.bamboo_wood_trapdoor.open"); - SoundEvent ENTITY_BAT_HURT = SoundEventImpl.get("minecraft:entity.bat.hurt"); + SoundEvent BLOCK_BAMBOO_WOOD_BUTTON_CLICK_OFF = BuiltinSoundEvent.get("minecraft:block.bamboo_wood_button.click_off"); - SoundEvent ENTITY_BAT_LOOP = SoundEventImpl.get("minecraft:entity.bat.loop"); + SoundEvent BLOCK_BAMBOO_WOOD_BUTTON_CLICK_ON = BuiltinSoundEvent.get("minecraft:block.bamboo_wood_button.click_on"); - SoundEvent ENTITY_BAT_TAKEOFF = SoundEventImpl.get("minecraft:entity.bat.takeoff"); + SoundEvent BLOCK_BAMBOO_WOOD_PRESSURE_PLATE_CLICK_OFF = BuiltinSoundEvent.get("minecraft:block.bamboo_wood_pressure_plate.click_off"); - SoundEvent BLOCK_BEACON_ACTIVATE = SoundEventImpl.get("minecraft:block.beacon.activate"); + SoundEvent BLOCK_BAMBOO_WOOD_PRESSURE_PLATE_CLICK_ON = BuiltinSoundEvent.get("minecraft:block.bamboo_wood_pressure_plate.click_on"); - SoundEvent BLOCK_BEACON_AMBIENT = SoundEventImpl.get("minecraft:block.beacon.ambient"); + SoundEvent BLOCK_BAMBOO_WOOD_FENCE_GATE_CLOSE = BuiltinSoundEvent.get("minecraft:block.bamboo_wood_fence_gate.close"); - SoundEvent BLOCK_BEACON_DEACTIVATE = SoundEventImpl.get("minecraft:block.beacon.deactivate"); + SoundEvent BLOCK_BAMBOO_WOOD_FENCE_GATE_OPEN = BuiltinSoundEvent.get("minecraft:block.bamboo_wood_fence_gate.open"); - SoundEvent BLOCK_BEACON_POWER_SELECT = SoundEventImpl.get("minecraft:block.beacon.power_select"); + SoundEvent BLOCK_BARREL_CLOSE = BuiltinSoundEvent.get("minecraft:block.barrel.close"); - SoundEvent ENTITY_BEE_DEATH = SoundEventImpl.get("minecraft:entity.bee.death"); + SoundEvent BLOCK_BARREL_OPEN = BuiltinSoundEvent.get("minecraft:block.barrel.open"); - SoundEvent ENTITY_BEE_HURT = SoundEventImpl.get("minecraft:entity.bee.hurt"); + SoundEvent BLOCK_BASALT_BREAK = BuiltinSoundEvent.get("minecraft:block.basalt.break"); - SoundEvent ENTITY_BEE_LOOP_AGGRESSIVE = SoundEventImpl.get("minecraft:entity.bee.loop_aggressive"); + SoundEvent BLOCK_BASALT_STEP = BuiltinSoundEvent.get("minecraft:block.basalt.step"); - SoundEvent ENTITY_BEE_LOOP = SoundEventImpl.get("minecraft:entity.bee.loop"); + SoundEvent BLOCK_BASALT_PLACE = BuiltinSoundEvent.get("minecraft:block.basalt.place"); - SoundEvent ENTITY_BEE_STING = SoundEventImpl.get("minecraft:entity.bee.sting"); + SoundEvent BLOCK_BASALT_HIT = BuiltinSoundEvent.get("minecraft:block.basalt.hit"); - SoundEvent ENTITY_BEE_POLLINATE = SoundEventImpl.get("minecraft:entity.bee.pollinate"); + SoundEvent BLOCK_BASALT_FALL = BuiltinSoundEvent.get("minecraft:block.basalt.fall"); - SoundEvent BLOCK_BEEHIVE_DRIP = SoundEventImpl.get("minecraft:block.beehive.drip"); + SoundEvent ENTITY_BAT_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.bat.ambient"); - SoundEvent BLOCK_BEEHIVE_ENTER = SoundEventImpl.get("minecraft:block.beehive.enter"); + SoundEvent ENTITY_BAT_DEATH = BuiltinSoundEvent.get("minecraft:entity.bat.death"); - SoundEvent BLOCK_BEEHIVE_EXIT = SoundEventImpl.get("minecraft:block.beehive.exit"); + SoundEvent ENTITY_BAT_HURT = BuiltinSoundEvent.get("minecraft:entity.bat.hurt"); - SoundEvent BLOCK_BEEHIVE_SHEAR = SoundEventImpl.get("minecraft:block.beehive.shear"); + SoundEvent ENTITY_BAT_LOOP = BuiltinSoundEvent.get("minecraft:entity.bat.loop"); - SoundEvent BLOCK_BEEHIVE_WORK = SoundEventImpl.get("minecraft:block.beehive.work"); + SoundEvent ENTITY_BAT_TAKEOFF = BuiltinSoundEvent.get("minecraft:entity.bat.takeoff"); - SoundEvent BLOCK_BELL_USE = SoundEventImpl.get("minecraft:block.bell.use"); + SoundEvent BLOCK_BEACON_ACTIVATE = BuiltinSoundEvent.get("minecraft:block.beacon.activate"); - SoundEvent BLOCK_BELL_RESONATE = SoundEventImpl.get("minecraft:block.bell.resonate"); + SoundEvent BLOCK_BEACON_AMBIENT = BuiltinSoundEvent.get("minecraft:block.beacon.ambient"); - SoundEvent BLOCK_BIG_DRIPLEAF_BREAK = SoundEventImpl.get("minecraft:block.big_dripleaf.break"); + SoundEvent BLOCK_BEACON_DEACTIVATE = BuiltinSoundEvent.get("minecraft:block.beacon.deactivate"); - SoundEvent BLOCK_BIG_DRIPLEAF_FALL = SoundEventImpl.get("minecraft:block.big_dripleaf.fall"); + SoundEvent BLOCK_BEACON_POWER_SELECT = BuiltinSoundEvent.get("minecraft:block.beacon.power_select"); - SoundEvent BLOCK_BIG_DRIPLEAF_HIT = SoundEventImpl.get("minecraft:block.big_dripleaf.hit"); + SoundEvent ENTITY_BEE_DEATH = BuiltinSoundEvent.get("minecraft:entity.bee.death"); - SoundEvent BLOCK_BIG_DRIPLEAF_PLACE = SoundEventImpl.get("minecraft:block.big_dripleaf.place"); + SoundEvent ENTITY_BEE_HURT = BuiltinSoundEvent.get("minecraft:entity.bee.hurt"); - SoundEvent BLOCK_BIG_DRIPLEAF_STEP = SoundEventImpl.get("minecraft:block.big_dripleaf.step"); + SoundEvent ENTITY_BEE_LOOP_AGGRESSIVE = BuiltinSoundEvent.get("minecraft:entity.bee.loop_aggressive"); - SoundEvent ENTITY_BLAZE_AMBIENT = SoundEventImpl.get("minecraft:entity.blaze.ambient"); + SoundEvent ENTITY_BEE_LOOP = BuiltinSoundEvent.get("minecraft:entity.bee.loop"); - SoundEvent ENTITY_BLAZE_BURN = SoundEventImpl.get("minecraft:entity.blaze.burn"); + SoundEvent ENTITY_BEE_STING = BuiltinSoundEvent.get("minecraft:entity.bee.sting"); - SoundEvent ENTITY_BLAZE_DEATH = SoundEventImpl.get("minecraft:entity.blaze.death"); + SoundEvent ENTITY_BEE_POLLINATE = BuiltinSoundEvent.get("minecraft:entity.bee.pollinate"); - SoundEvent ENTITY_BLAZE_HURT = SoundEventImpl.get("minecraft:entity.blaze.hurt"); + SoundEvent BLOCK_BEEHIVE_DRIP = BuiltinSoundEvent.get("minecraft:block.beehive.drip"); - SoundEvent ENTITY_BLAZE_SHOOT = SoundEventImpl.get("minecraft:entity.blaze.shoot"); + SoundEvent BLOCK_BEEHIVE_ENTER = BuiltinSoundEvent.get("minecraft:block.beehive.enter"); - SoundEvent ENTITY_BOAT_PADDLE_LAND = SoundEventImpl.get("minecraft:entity.boat.paddle_land"); + SoundEvent BLOCK_BEEHIVE_EXIT = BuiltinSoundEvent.get("minecraft:block.beehive.exit"); - SoundEvent ENTITY_BOAT_PADDLE_WATER = SoundEventImpl.get("minecraft:entity.boat.paddle_water"); + SoundEvent BLOCK_BEEHIVE_SHEAR = BuiltinSoundEvent.get("minecraft:block.beehive.shear"); - SoundEvent BLOCK_BONE_BLOCK_BREAK = SoundEventImpl.get("minecraft:block.bone_block.break"); + SoundEvent BLOCK_BEEHIVE_WORK = BuiltinSoundEvent.get("minecraft:block.beehive.work"); - SoundEvent BLOCK_BONE_BLOCK_FALL = SoundEventImpl.get("minecraft:block.bone_block.fall"); + SoundEvent BLOCK_BELL_USE = BuiltinSoundEvent.get("minecraft:block.bell.use"); - SoundEvent BLOCK_BONE_BLOCK_HIT = SoundEventImpl.get("minecraft:block.bone_block.hit"); + SoundEvent BLOCK_BELL_RESONATE = BuiltinSoundEvent.get("minecraft:block.bell.resonate"); - SoundEvent BLOCK_BONE_BLOCK_PLACE = SoundEventImpl.get("minecraft:block.bone_block.place"); + SoundEvent BLOCK_BIG_DRIPLEAF_BREAK = BuiltinSoundEvent.get("minecraft:block.big_dripleaf.break"); - SoundEvent BLOCK_BONE_BLOCK_STEP = SoundEventImpl.get("minecraft:block.bone_block.step"); + SoundEvent BLOCK_BIG_DRIPLEAF_FALL = BuiltinSoundEvent.get("minecraft:block.big_dripleaf.fall"); - SoundEvent ITEM_BONE_MEAL_USE = SoundEventImpl.get("minecraft:item.bone_meal.use"); + SoundEvent BLOCK_BIG_DRIPLEAF_HIT = BuiltinSoundEvent.get("minecraft:block.big_dripleaf.hit"); - SoundEvent ITEM_BOOK_PAGE_TURN = SoundEventImpl.get("minecraft:item.book.page_turn"); + SoundEvent BLOCK_BIG_DRIPLEAF_PLACE = BuiltinSoundEvent.get("minecraft:block.big_dripleaf.place"); - SoundEvent ITEM_BOOK_PUT = SoundEventImpl.get("minecraft:item.book.put"); + SoundEvent BLOCK_BIG_DRIPLEAF_STEP = BuiltinSoundEvent.get("minecraft:block.big_dripleaf.step"); - SoundEvent BLOCK_BLASTFURNACE_FIRE_CRACKLE = SoundEventImpl.get("minecraft:block.blastfurnace.fire_crackle"); + SoundEvent ENTITY_BLAZE_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.blaze.ambient"); - SoundEvent ITEM_BOTTLE_EMPTY = SoundEventImpl.get("minecraft:item.bottle.empty"); + SoundEvent ENTITY_BLAZE_BURN = BuiltinSoundEvent.get("minecraft:entity.blaze.burn"); - SoundEvent ITEM_BOTTLE_FILL = SoundEventImpl.get("minecraft:item.bottle.fill"); + SoundEvent ENTITY_BLAZE_DEATH = BuiltinSoundEvent.get("minecraft:entity.blaze.death"); - SoundEvent ITEM_BOTTLE_FILL_DRAGONBREATH = SoundEventImpl.get("minecraft:item.bottle.fill_dragonbreath"); + SoundEvent ENTITY_BLAZE_HURT = BuiltinSoundEvent.get("minecraft:entity.blaze.hurt"); - SoundEvent ENTITY_BREEZE_INHALE = SoundEventImpl.get("minecraft:entity.breeze.inhale"); + SoundEvent ENTITY_BLAZE_SHOOT = BuiltinSoundEvent.get("minecraft:entity.blaze.shoot"); - SoundEvent ENTITY_BREEZE_IDLE_GROUND = SoundEventImpl.get("minecraft:entity.breeze.idle_ground"); + SoundEvent ENTITY_BOAT_PADDLE_LAND = BuiltinSoundEvent.get("minecraft:entity.boat.paddle_land"); - SoundEvent ENTITY_BREEZE_IDLE_AIR = SoundEventImpl.get("minecraft:entity.breeze.idle_air"); + SoundEvent ENTITY_BOAT_PADDLE_WATER = BuiltinSoundEvent.get("minecraft:entity.boat.paddle_water"); - SoundEvent ENTITY_BREEZE_SHOOT = SoundEventImpl.get("minecraft:entity.breeze.shoot"); + SoundEvent ENTITY_BOGGED_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.bogged.ambient"); - SoundEvent ENTITY_BREEZE_JUMP = SoundEventImpl.get("minecraft:entity.breeze.jump"); + SoundEvent ENTITY_BOGGED_DEATH = BuiltinSoundEvent.get("minecraft:entity.bogged.death"); - SoundEvent ENTITY_BREEZE_LAND = SoundEventImpl.get("minecraft:entity.breeze.land"); + SoundEvent ENTITY_BOGGED_HURT = BuiltinSoundEvent.get("minecraft:entity.bogged.hurt"); - SoundEvent ENTITY_BREEZE_SLIDE = SoundEventImpl.get("minecraft:entity.breeze.slide"); + SoundEvent ENTITY_BOGGED_SHEAR = BuiltinSoundEvent.get("minecraft:entity.bogged.shear"); - SoundEvent ENTITY_BREEZE_DEATH = SoundEventImpl.get("minecraft:entity.breeze.death"); + SoundEvent ENTITY_BOGGED_STEP = BuiltinSoundEvent.get("minecraft:entity.bogged.step"); - SoundEvent ENTITY_BREEZE_HURT = SoundEventImpl.get("minecraft:entity.breeze.hurt"); + SoundEvent BLOCK_BONE_BLOCK_BREAK = BuiltinSoundEvent.get("minecraft:block.bone_block.break"); - SoundEvent BLOCK_BREWING_STAND_BREW = SoundEventImpl.get("minecraft:block.brewing_stand.brew"); + SoundEvent BLOCK_BONE_BLOCK_FALL = BuiltinSoundEvent.get("minecraft:block.bone_block.fall"); - SoundEvent ITEM_BRUSH_BRUSHING_GENERIC = SoundEventImpl.get("minecraft:item.brush.brushing.generic"); + SoundEvent BLOCK_BONE_BLOCK_HIT = BuiltinSoundEvent.get("minecraft:block.bone_block.hit"); - SoundEvent ITEM_BRUSH_BRUSHING_SAND = SoundEventImpl.get("minecraft:item.brush.brushing.sand"); + SoundEvent BLOCK_BONE_BLOCK_PLACE = BuiltinSoundEvent.get("minecraft:block.bone_block.place"); - SoundEvent ITEM_BRUSH_BRUSHING_GRAVEL = SoundEventImpl.get("minecraft:item.brush.brushing.gravel"); + SoundEvent BLOCK_BONE_BLOCK_STEP = BuiltinSoundEvent.get("minecraft:block.bone_block.step"); - SoundEvent ITEM_BRUSH_BRUSHING_SAND_COMPLETE = SoundEventImpl.get("minecraft:item.brush.brushing.sand.complete"); + SoundEvent ITEM_BONE_MEAL_USE = BuiltinSoundEvent.get("minecraft:item.bone_meal.use"); - SoundEvent ITEM_BRUSH_BRUSHING_GRAVEL_COMPLETE = SoundEventImpl.get("minecraft:item.brush.brushing.gravel.complete"); + SoundEvent ITEM_BOOK_PAGE_TURN = BuiltinSoundEvent.get("minecraft:item.book.page_turn"); - SoundEvent BLOCK_BUBBLE_COLUMN_BUBBLE_POP = SoundEventImpl.get("minecraft:block.bubble_column.bubble_pop"); + SoundEvent ITEM_BOOK_PUT = BuiltinSoundEvent.get("minecraft:item.book.put"); - SoundEvent BLOCK_BUBBLE_COLUMN_UPWARDS_AMBIENT = SoundEventImpl.get("minecraft:block.bubble_column.upwards_ambient"); + SoundEvent BLOCK_BLASTFURNACE_FIRE_CRACKLE = BuiltinSoundEvent.get("minecraft:block.blastfurnace.fire_crackle"); - SoundEvent BLOCK_BUBBLE_COLUMN_UPWARDS_INSIDE = SoundEventImpl.get("minecraft:block.bubble_column.upwards_inside"); + SoundEvent ITEM_BOTTLE_EMPTY = BuiltinSoundEvent.get("minecraft:item.bottle.empty"); - SoundEvent BLOCK_BUBBLE_COLUMN_WHIRLPOOL_AMBIENT = SoundEventImpl.get("minecraft:block.bubble_column.whirlpool_ambient"); + SoundEvent ITEM_BOTTLE_FILL = BuiltinSoundEvent.get("minecraft:item.bottle.fill"); - SoundEvent BLOCK_BUBBLE_COLUMN_WHIRLPOOL_INSIDE = SoundEventImpl.get("minecraft:block.bubble_column.whirlpool_inside"); + SoundEvent ITEM_BOTTLE_FILL_DRAGONBREATH = BuiltinSoundEvent.get("minecraft:item.bottle.fill_dragonbreath"); - SoundEvent ITEM_BUCKET_EMPTY = SoundEventImpl.get("minecraft:item.bucket.empty"); + SoundEvent ENTITY_BREEZE_CHARGE = BuiltinSoundEvent.get("minecraft:entity.breeze.charge"); - SoundEvent ITEM_BUCKET_EMPTY_AXOLOTL = SoundEventImpl.get("minecraft:item.bucket.empty_axolotl"); + SoundEvent ENTITY_BREEZE_DEFLECT = BuiltinSoundEvent.get("minecraft:entity.breeze.deflect"); - SoundEvent ITEM_BUCKET_EMPTY_FISH = SoundEventImpl.get("minecraft:item.bucket.empty_fish"); + SoundEvent ENTITY_BREEZE_INHALE = BuiltinSoundEvent.get("minecraft:entity.breeze.inhale"); - SoundEvent ITEM_BUCKET_EMPTY_LAVA = SoundEventImpl.get("minecraft:item.bucket.empty_lava"); + SoundEvent ENTITY_BREEZE_IDLE_GROUND = BuiltinSoundEvent.get("minecraft:entity.breeze.idle_ground"); - SoundEvent ITEM_BUCKET_EMPTY_POWDER_SNOW = SoundEventImpl.get("minecraft:item.bucket.empty_powder_snow"); + SoundEvent ENTITY_BREEZE_IDLE_AIR = BuiltinSoundEvent.get("minecraft:entity.breeze.idle_air"); - SoundEvent ITEM_BUCKET_EMPTY_TADPOLE = SoundEventImpl.get("minecraft:item.bucket.empty_tadpole"); + SoundEvent ENTITY_BREEZE_SHOOT = BuiltinSoundEvent.get("minecraft:entity.breeze.shoot"); - SoundEvent ITEM_BUCKET_FILL = SoundEventImpl.get("minecraft:item.bucket.fill"); + SoundEvent ENTITY_BREEZE_JUMP = BuiltinSoundEvent.get("minecraft:entity.breeze.jump"); - SoundEvent ITEM_BUCKET_FILL_AXOLOTL = SoundEventImpl.get("minecraft:item.bucket.fill_axolotl"); + SoundEvent ENTITY_BREEZE_LAND = BuiltinSoundEvent.get("minecraft:entity.breeze.land"); - SoundEvent ITEM_BUCKET_FILL_FISH = SoundEventImpl.get("minecraft:item.bucket.fill_fish"); + SoundEvent ENTITY_BREEZE_SLIDE = BuiltinSoundEvent.get("minecraft:entity.breeze.slide"); - SoundEvent ITEM_BUCKET_FILL_LAVA = SoundEventImpl.get("minecraft:item.bucket.fill_lava"); + SoundEvent ENTITY_BREEZE_DEATH = BuiltinSoundEvent.get("minecraft:entity.breeze.death"); - SoundEvent ITEM_BUCKET_FILL_POWDER_SNOW = SoundEventImpl.get("minecraft:item.bucket.fill_powder_snow"); + SoundEvent ENTITY_BREEZE_HURT = BuiltinSoundEvent.get("minecraft:entity.breeze.hurt"); - SoundEvent ITEM_BUCKET_FILL_TADPOLE = SoundEventImpl.get("minecraft:item.bucket.fill_tadpole"); + SoundEvent ENTITY_BREEZE_WHIRL = BuiltinSoundEvent.get("minecraft:entity.breeze.whirl"); - SoundEvent ITEM_BUNDLE_DROP_CONTENTS = SoundEventImpl.get("minecraft:item.bundle.drop_contents"); + SoundEvent ENTITY_BREEZE_WIND_BURST = BuiltinSoundEvent.get("minecraft:entity.breeze.wind_burst"); - SoundEvent ITEM_BUNDLE_INSERT = SoundEventImpl.get("minecraft:item.bundle.insert"); + SoundEvent BLOCK_BREWING_STAND_BREW = BuiltinSoundEvent.get("minecraft:block.brewing_stand.brew"); - SoundEvent ITEM_BUNDLE_REMOVE_ONE = SoundEventImpl.get("minecraft:item.bundle.remove_one"); + SoundEvent ITEM_BRUSH_BRUSHING_GENERIC = BuiltinSoundEvent.get("minecraft:item.brush.brushing.generic"); - SoundEvent BLOCK_CAKE_ADD_CANDLE = SoundEventImpl.get("minecraft:block.cake.add_candle"); + SoundEvent ITEM_BRUSH_BRUSHING_SAND = BuiltinSoundEvent.get("minecraft:item.brush.brushing.sand"); - SoundEvent BLOCK_CALCITE_BREAK = SoundEventImpl.get("minecraft:block.calcite.break"); + SoundEvent ITEM_BRUSH_BRUSHING_GRAVEL = BuiltinSoundEvent.get("minecraft:item.brush.brushing.gravel"); - SoundEvent BLOCK_CALCITE_STEP = SoundEventImpl.get("minecraft:block.calcite.step"); + SoundEvent ITEM_BRUSH_BRUSHING_SAND_COMPLETE = BuiltinSoundEvent.get("minecraft:item.brush.brushing.sand.complete"); - SoundEvent BLOCK_CALCITE_PLACE = SoundEventImpl.get("minecraft:block.calcite.place"); + SoundEvent ITEM_BRUSH_BRUSHING_GRAVEL_COMPLETE = BuiltinSoundEvent.get("minecraft:item.brush.brushing.gravel.complete"); - SoundEvent BLOCK_CALCITE_HIT = SoundEventImpl.get("minecraft:block.calcite.hit"); + SoundEvent BLOCK_BUBBLE_COLUMN_BUBBLE_POP = BuiltinSoundEvent.get("minecraft:block.bubble_column.bubble_pop"); - SoundEvent BLOCK_CALCITE_FALL = SoundEventImpl.get("minecraft:block.calcite.fall"); + SoundEvent BLOCK_BUBBLE_COLUMN_UPWARDS_AMBIENT = BuiltinSoundEvent.get("minecraft:block.bubble_column.upwards_ambient"); - SoundEvent ENTITY_CAMEL_AMBIENT = SoundEventImpl.get("minecraft:entity.camel.ambient"); + SoundEvent BLOCK_BUBBLE_COLUMN_UPWARDS_INSIDE = BuiltinSoundEvent.get("minecraft:block.bubble_column.upwards_inside"); - SoundEvent ENTITY_CAMEL_DASH = SoundEventImpl.get("minecraft:entity.camel.dash"); + SoundEvent BLOCK_BUBBLE_COLUMN_WHIRLPOOL_AMBIENT = BuiltinSoundEvent.get("minecraft:block.bubble_column.whirlpool_ambient"); - SoundEvent ENTITY_CAMEL_DASH_READY = SoundEventImpl.get("minecraft:entity.camel.dash_ready"); + SoundEvent BLOCK_BUBBLE_COLUMN_WHIRLPOOL_INSIDE = BuiltinSoundEvent.get("minecraft:block.bubble_column.whirlpool_inside"); - SoundEvent ENTITY_CAMEL_DEATH = SoundEventImpl.get("minecraft:entity.camel.death"); + SoundEvent ITEM_BUCKET_EMPTY = BuiltinSoundEvent.get("minecraft:item.bucket.empty"); - SoundEvent ENTITY_CAMEL_EAT = SoundEventImpl.get("minecraft:entity.camel.eat"); + SoundEvent ITEM_BUCKET_EMPTY_AXOLOTL = BuiltinSoundEvent.get("minecraft:item.bucket.empty_axolotl"); - SoundEvent ENTITY_CAMEL_HURT = SoundEventImpl.get("minecraft:entity.camel.hurt"); + SoundEvent ITEM_BUCKET_EMPTY_FISH = BuiltinSoundEvent.get("minecraft:item.bucket.empty_fish"); - SoundEvent ENTITY_CAMEL_SADDLE = SoundEventImpl.get("minecraft:entity.camel.saddle"); + SoundEvent ITEM_BUCKET_EMPTY_LAVA = BuiltinSoundEvent.get("minecraft:item.bucket.empty_lava"); - SoundEvent ENTITY_CAMEL_SIT = SoundEventImpl.get("minecraft:entity.camel.sit"); + SoundEvent ITEM_BUCKET_EMPTY_POWDER_SNOW = BuiltinSoundEvent.get("minecraft:item.bucket.empty_powder_snow"); - SoundEvent ENTITY_CAMEL_STAND = SoundEventImpl.get("minecraft:entity.camel.stand"); + SoundEvent ITEM_BUCKET_EMPTY_TADPOLE = BuiltinSoundEvent.get("minecraft:item.bucket.empty_tadpole"); - SoundEvent ENTITY_CAMEL_STEP = SoundEventImpl.get("minecraft:entity.camel.step"); + SoundEvent ITEM_BUCKET_FILL = BuiltinSoundEvent.get("minecraft:item.bucket.fill"); - SoundEvent ENTITY_CAMEL_STEP_SAND = SoundEventImpl.get("minecraft:entity.camel.step_sand"); + SoundEvent ITEM_BUCKET_FILL_AXOLOTL = BuiltinSoundEvent.get("minecraft:item.bucket.fill_axolotl"); - SoundEvent BLOCK_CAMPFIRE_CRACKLE = SoundEventImpl.get("minecraft:block.campfire.crackle"); + SoundEvent ITEM_BUCKET_FILL_FISH = BuiltinSoundEvent.get("minecraft:item.bucket.fill_fish"); - SoundEvent BLOCK_CANDLE_AMBIENT = SoundEventImpl.get("minecraft:block.candle.ambient"); + SoundEvent ITEM_BUCKET_FILL_LAVA = BuiltinSoundEvent.get("minecraft:item.bucket.fill_lava"); - SoundEvent BLOCK_CANDLE_BREAK = SoundEventImpl.get("minecraft:block.candle.break"); + SoundEvent ITEM_BUCKET_FILL_POWDER_SNOW = BuiltinSoundEvent.get("minecraft:item.bucket.fill_powder_snow"); - SoundEvent BLOCK_CANDLE_EXTINGUISH = SoundEventImpl.get("minecraft:block.candle.extinguish"); + SoundEvent ITEM_BUCKET_FILL_TADPOLE = BuiltinSoundEvent.get("minecraft:item.bucket.fill_tadpole"); - SoundEvent BLOCK_CANDLE_FALL = SoundEventImpl.get("minecraft:block.candle.fall"); + SoundEvent ITEM_BUNDLE_DROP_CONTENTS = BuiltinSoundEvent.get("minecraft:item.bundle.drop_contents"); - SoundEvent BLOCK_CANDLE_HIT = SoundEventImpl.get("minecraft:block.candle.hit"); + SoundEvent ITEM_BUNDLE_INSERT = BuiltinSoundEvent.get("minecraft:item.bundle.insert"); - SoundEvent BLOCK_CANDLE_PLACE = SoundEventImpl.get("minecraft:block.candle.place"); + SoundEvent ITEM_BUNDLE_REMOVE_ONE = BuiltinSoundEvent.get("minecraft:item.bundle.remove_one"); - SoundEvent BLOCK_CANDLE_STEP = SoundEventImpl.get("minecraft:block.candle.step"); + SoundEvent BLOCK_CAKE_ADD_CANDLE = BuiltinSoundEvent.get("minecraft:block.cake.add_candle"); - SoundEvent ENTITY_CAT_AMBIENT = SoundEventImpl.get("minecraft:entity.cat.ambient"); + SoundEvent BLOCK_CALCITE_BREAK = BuiltinSoundEvent.get("minecraft:block.calcite.break"); - SoundEvent ENTITY_CAT_STRAY_AMBIENT = SoundEventImpl.get("minecraft:entity.cat.stray_ambient"); + SoundEvent BLOCK_CALCITE_STEP = BuiltinSoundEvent.get("minecraft:block.calcite.step"); - SoundEvent ENTITY_CAT_DEATH = SoundEventImpl.get("minecraft:entity.cat.death"); + SoundEvent BLOCK_CALCITE_PLACE = BuiltinSoundEvent.get("minecraft:block.calcite.place"); - SoundEvent ENTITY_CAT_EAT = SoundEventImpl.get("minecraft:entity.cat.eat"); + SoundEvent BLOCK_CALCITE_HIT = BuiltinSoundEvent.get("minecraft:block.calcite.hit"); - SoundEvent ENTITY_CAT_HISS = SoundEventImpl.get("minecraft:entity.cat.hiss"); + SoundEvent BLOCK_CALCITE_FALL = BuiltinSoundEvent.get("minecraft:block.calcite.fall"); - SoundEvent ENTITY_CAT_BEG_FOR_FOOD = SoundEventImpl.get("minecraft:entity.cat.beg_for_food"); + SoundEvent ENTITY_CAMEL_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.camel.ambient"); - SoundEvent ENTITY_CAT_HURT = SoundEventImpl.get("minecraft:entity.cat.hurt"); + SoundEvent ENTITY_CAMEL_DASH = BuiltinSoundEvent.get("minecraft:entity.camel.dash"); - SoundEvent ENTITY_CAT_PURR = SoundEventImpl.get("minecraft:entity.cat.purr"); + SoundEvent ENTITY_CAMEL_DASH_READY = BuiltinSoundEvent.get("minecraft:entity.camel.dash_ready"); - SoundEvent ENTITY_CAT_PURREOW = SoundEventImpl.get("minecraft:entity.cat.purreow"); + SoundEvent ENTITY_CAMEL_DEATH = BuiltinSoundEvent.get("minecraft:entity.camel.death"); - SoundEvent BLOCK_CAVE_VINES_BREAK = SoundEventImpl.get("minecraft:block.cave_vines.break"); + SoundEvent ENTITY_CAMEL_EAT = BuiltinSoundEvent.get("minecraft:entity.camel.eat"); - SoundEvent BLOCK_CAVE_VINES_FALL = SoundEventImpl.get("minecraft:block.cave_vines.fall"); + SoundEvent ENTITY_CAMEL_HURT = BuiltinSoundEvent.get("minecraft:entity.camel.hurt"); - SoundEvent BLOCK_CAVE_VINES_HIT = SoundEventImpl.get("minecraft:block.cave_vines.hit"); + SoundEvent ENTITY_CAMEL_SADDLE = BuiltinSoundEvent.get("minecraft:entity.camel.saddle"); - SoundEvent BLOCK_CAVE_VINES_PLACE = SoundEventImpl.get("minecraft:block.cave_vines.place"); + SoundEvent ENTITY_CAMEL_SIT = BuiltinSoundEvent.get("minecraft:entity.camel.sit"); - SoundEvent BLOCK_CAVE_VINES_STEP = SoundEventImpl.get("minecraft:block.cave_vines.step"); + SoundEvent ENTITY_CAMEL_STAND = BuiltinSoundEvent.get("minecraft:entity.camel.stand"); - SoundEvent BLOCK_CAVE_VINES_PICK_BERRIES = SoundEventImpl.get("minecraft:block.cave_vines.pick_berries"); + SoundEvent ENTITY_CAMEL_STEP = BuiltinSoundEvent.get("minecraft:entity.camel.step"); - SoundEvent BLOCK_CHAIN_BREAK = SoundEventImpl.get("minecraft:block.chain.break"); + SoundEvent ENTITY_CAMEL_STEP_SAND = BuiltinSoundEvent.get("minecraft:entity.camel.step_sand"); - SoundEvent BLOCK_CHAIN_FALL = SoundEventImpl.get("minecraft:block.chain.fall"); + SoundEvent BLOCK_CAMPFIRE_CRACKLE = BuiltinSoundEvent.get("minecraft:block.campfire.crackle"); - SoundEvent BLOCK_CHAIN_HIT = SoundEventImpl.get("minecraft:block.chain.hit"); + SoundEvent BLOCK_CANDLE_AMBIENT = BuiltinSoundEvent.get("minecraft:block.candle.ambient"); - SoundEvent BLOCK_CHAIN_PLACE = SoundEventImpl.get("minecraft:block.chain.place"); + SoundEvent BLOCK_CANDLE_BREAK = BuiltinSoundEvent.get("minecraft:block.candle.break"); - SoundEvent BLOCK_CHAIN_STEP = SoundEventImpl.get("minecraft:block.chain.step"); + SoundEvent BLOCK_CANDLE_EXTINGUISH = BuiltinSoundEvent.get("minecraft:block.candle.extinguish"); - SoundEvent BLOCK_CHERRY_WOOD_BREAK = SoundEventImpl.get("minecraft:block.cherry_wood.break"); + SoundEvent BLOCK_CANDLE_FALL = BuiltinSoundEvent.get("minecraft:block.candle.fall"); - SoundEvent BLOCK_CHERRY_WOOD_FALL = SoundEventImpl.get("minecraft:block.cherry_wood.fall"); + SoundEvent BLOCK_CANDLE_HIT = BuiltinSoundEvent.get("minecraft:block.candle.hit"); - SoundEvent BLOCK_CHERRY_WOOD_HIT = SoundEventImpl.get("minecraft:block.cherry_wood.hit"); + SoundEvent BLOCK_CANDLE_PLACE = BuiltinSoundEvent.get("minecraft:block.candle.place"); - SoundEvent BLOCK_CHERRY_WOOD_PLACE = SoundEventImpl.get("minecraft:block.cherry_wood.place"); + SoundEvent BLOCK_CANDLE_STEP = BuiltinSoundEvent.get("minecraft:block.candle.step"); - SoundEvent BLOCK_CHERRY_WOOD_STEP = SoundEventImpl.get("minecraft:block.cherry_wood.step"); + SoundEvent ENTITY_CAT_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.cat.ambient"); - SoundEvent BLOCK_CHERRY_SAPLING_BREAK = SoundEventImpl.get("minecraft:block.cherry_sapling.break"); + SoundEvent ENTITY_CAT_STRAY_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.cat.stray_ambient"); - SoundEvent BLOCK_CHERRY_SAPLING_FALL = SoundEventImpl.get("minecraft:block.cherry_sapling.fall"); + SoundEvent ENTITY_CAT_DEATH = BuiltinSoundEvent.get("minecraft:entity.cat.death"); - SoundEvent BLOCK_CHERRY_SAPLING_HIT = SoundEventImpl.get("minecraft:block.cherry_sapling.hit"); + SoundEvent ENTITY_CAT_EAT = BuiltinSoundEvent.get("minecraft:entity.cat.eat"); - SoundEvent BLOCK_CHERRY_SAPLING_PLACE = SoundEventImpl.get("minecraft:block.cherry_sapling.place"); + SoundEvent ENTITY_CAT_HISS = BuiltinSoundEvent.get("minecraft:entity.cat.hiss"); - SoundEvent BLOCK_CHERRY_SAPLING_STEP = SoundEventImpl.get("minecraft:block.cherry_sapling.step"); + SoundEvent ENTITY_CAT_BEG_FOR_FOOD = BuiltinSoundEvent.get("minecraft:entity.cat.beg_for_food"); - SoundEvent BLOCK_CHERRY_LEAVES_BREAK = SoundEventImpl.get("minecraft:block.cherry_leaves.break"); + SoundEvent ENTITY_CAT_HURT = BuiltinSoundEvent.get("minecraft:entity.cat.hurt"); - SoundEvent BLOCK_CHERRY_LEAVES_FALL = SoundEventImpl.get("minecraft:block.cherry_leaves.fall"); + SoundEvent ENTITY_CAT_PURR = BuiltinSoundEvent.get("minecraft:entity.cat.purr"); - SoundEvent BLOCK_CHERRY_LEAVES_HIT = SoundEventImpl.get("minecraft:block.cherry_leaves.hit"); + SoundEvent ENTITY_CAT_PURREOW = BuiltinSoundEvent.get("minecraft:entity.cat.purreow"); - SoundEvent BLOCK_CHERRY_LEAVES_PLACE = SoundEventImpl.get("minecraft:block.cherry_leaves.place"); + SoundEvent BLOCK_CAVE_VINES_BREAK = BuiltinSoundEvent.get("minecraft:block.cave_vines.break"); - SoundEvent BLOCK_CHERRY_LEAVES_STEP = SoundEventImpl.get("minecraft:block.cherry_leaves.step"); + SoundEvent BLOCK_CAVE_VINES_FALL = BuiltinSoundEvent.get("minecraft:block.cave_vines.fall"); - SoundEvent BLOCK_CHERRY_WOOD_HANGING_SIGN_STEP = SoundEventImpl.get("minecraft:block.cherry_wood_hanging_sign.step"); + SoundEvent BLOCK_CAVE_VINES_HIT = BuiltinSoundEvent.get("minecraft:block.cave_vines.hit"); - SoundEvent BLOCK_CHERRY_WOOD_HANGING_SIGN_BREAK = SoundEventImpl.get("minecraft:block.cherry_wood_hanging_sign.break"); + SoundEvent BLOCK_CAVE_VINES_PLACE = BuiltinSoundEvent.get("minecraft:block.cave_vines.place"); - SoundEvent BLOCK_CHERRY_WOOD_HANGING_SIGN_FALL = SoundEventImpl.get("minecraft:block.cherry_wood_hanging_sign.fall"); + SoundEvent BLOCK_CAVE_VINES_STEP = BuiltinSoundEvent.get("minecraft:block.cave_vines.step"); - SoundEvent BLOCK_CHERRY_WOOD_HANGING_SIGN_HIT = SoundEventImpl.get("minecraft:block.cherry_wood_hanging_sign.hit"); + SoundEvent BLOCK_CAVE_VINES_PICK_BERRIES = BuiltinSoundEvent.get("minecraft:block.cave_vines.pick_berries"); - SoundEvent BLOCK_CHERRY_WOOD_HANGING_SIGN_PLACE = SoundEventImpl.get("minecraft:block.cherry_wood_hanging_sign.place"); + SoundEvent BLOCK_CHAIN_BREAK = BuiltinSoundEvent.get("minecraft:block.chain.break"); - SoundEvent BLOCK_CHERRY_WOOD_DOOR_CLOSE = SoundEventImpl.get("minecraft:block.cherry_wood_door.close"); + SoundEvent BLOCK_CHAIN_FALL = BuiltinSoundEvent.get("minecraft:block.chain.fall"); - SoundEvent BLOCK_CHERRY_WOOD_DOOR_OPEN = SoundEventImpl.get("minecraft:block.cherry_wood_door.open"); + SoundEvent BLOCK_CHAIN_HIT = BuiltinSoundEvent.get("minecraft:block.chain.hit"); - SoundEvent BLOCK_CHERRY_WOOD_TRAPDOOR_CLOSE = SoundEventImpl.get("minecraft:block.cherry_wood_trapdoor.close"); + SoundEvent BLOCK_CHAIN_PLACE = BuiltinSoundEvent.get("minecraft:block.chain.place"); - SoundEvent BLOCK_CHERRY_WOOD_TRAPDOOR_OPEN = SoundEventImpl.get("minecraft:block.cherry_wood_trapdoor.open"); + SoundEvent BLOCK_CHAIN_STEP = BuiltinSoundEvent.get("minecraft:block.chain.step"); - SoundEvent BLOCK_CHERRY_WOOD_BUTTON_CLICK_OFF = SoundEventImpl.get("minecraft:block.cherry_wood_button.click_off"); + SoundEvent BLOCK_CHERRY_WOOD_BREAK = BuiltinSoundEvent.get("minecraft:block.cherry_wood.break"); - SoundEvent BLOCK_CHERRY_WOOD_BUTTON_CLICK_ON = SoundEventImpl.get("minecraft:block.cherry_wood_button.click_on"); + SoundEvent BLOCK_CHERRY_WOOD_FALL = BuiltinSoundEvent.get("minecraft:block.cherry_wood.fall"); - SoundEvent BLOCK_CHERRY_WOOD_PRESSURE_PLATE_CLICK_OFF = SoundEventImpl.get("minecraft:block.cherry_wood_pressure_plate.click_off"); + SoundEvent BLOCK_CHERRY_WOOD_HIT = BuiltinSoundEvent.get("minecraft:block.cherry_wood.hit"); - SoundEvent BLOCK_CHERRY_WOOD_PRESSURE_PLATE_CLICK_ON = SoundEventImpl.get("minecraft:block.cherry_wood_pressure_plate.click_on"); + SoundEvent BLOCK_CHERRY_WOOD_PLACE = BuiltinSoundEvent.get("minecraft:block.cherry_wood.place"); - SoundEvent BLOCK_CHERRY_WOOD_FENCE_GATE_CLOSE = SoundEventImpl.get("minecraft:block.cherry_wood_fence_gate.close"); + SoundEvent BLOCK_CHERRY_WOOD_STEP = BuiltinSoundEvent.get("minecraft:block.cherry_wood.step"); - SoundEvent BLOCK_CHERRY_WOOD_FENCE_GATE_OPEN = SoundEventImpl.get("minecraft:block.cherry_wood_fence_gate.open"); + SoundEvent BLOCK_CHERRY_SAPLING_BREAK = BuiltinSoundEvent.get("minecraft:block.cherry_sapling.break"); - SoundEvent BLOCK_CHEST_CLOSE = SoundEventImpl.get("minecraft:block.chest.close"); + SoundEvent BLOCK_CHERRY_SAPLING_FALL = BuiltinSoundEvent.get("minecraft:block.cherry_sapling.fall"); - SoundEvent BLOCK_CHEST_LOCKED = SoundEventImpl.get("minecraft:block.chest.locked"); + SoundEvent BLOCK_CHERRY_SAPLING_HIT = BuiltinSoundEvent.get("minecraft:block.cherry_sapling.hit"); - SoundEvent BLOCK_CHEST_OPEN = SoundEventImpl.get("minecraft:block.chest.open"); + SoundEvent BLOCK_CHERRY_SAPLING_PLACE = BuiltinSoundEvent.get("minecraft:block.cherry_sapling.place"); - SoundEvent ENTITY_CHICKEN_AMBIENT = SoundEventImpl.get("minecraft:entity.chicken.ambient"); + SoundEvent BLOCK_CHERRY_SAPLING_STEP = BuiltinSoundEvent.get("minecraft:block.cherry_sapling.step"); - SoundEvent ENTITY_CHICKEN_DEATH = SoundEventImpl.get("minecraft:entity.chicken.death"); + SoundEvent BLOCK_CHERRY_LEAVES_BREAK = BuiltinSoundEvent.get("minecraft:block.cherry_leaves.break"); - SoundEvent ENTITY_CHICKEN_EGG = SoundEventImpl.get("minecraft:entity.chicken.egg"); + SoundEvent BLOCK_CHERRY_LEAVES_FALL = BuiltinSoundEvent.get("minecraft:block.cherry_leaves.fall"); - SoundEvent ENTITY_CHICKEN_HURT = SoundEventImpl.get("minecraft:entity.chicken.hurt"); + SoundEvent BLOCK_CHERRY_LEAVES_HIT = BuiltinSoundEvent.get("minecraft:block.cherry_leaves.hit"); - SoundEvent ENTITY_CHICKEN_STEP = SoundEventImpl.get("minecraft:entity.chicken.step"); + SoundEvent BLOCK_CHERRY_LEAVES_PLACE = BuiltinSoundEvent.get("minecraft:block.cherry_leaves.place"); - SoundEvent BLOCK_CHISELED_BOOKSHELF_BREAK = SoundEventImpl.get("minecraft:block.chiseled_bookshelf.break"); + SoundEvent BLOCK_CHERRY_LEAVES_STEP = BuiltinSoundEvent.get("minecraft:block.cherry_leaves.step"); - SoundEvent BLOCK_CHISELED_BOOKSHELF_FALL = SoundEventImpl.get("minecraft:block.chiseled_bookshelf.fall"); + SoundEvent BLOCK_CHERRY_WOOD_HANGING_SIGN_STEP = BuiltinSoundEvent.get("minecraft:block.cherry_wood_hanging_sign.step"); - SoundEvent BLOCK_CHISELED_BOOKSHELF_HIT = SoundEventImpl.get("minecraft:block.chiseled_bookshelf.hit"); + SoundEvent BLOCK_CHERRY_WOOD_HANGING_SIGN_BREAK = BuiltinSoundEvent.get("minecraft:block.cherry_wood_hanging_sign.break"); - SoundEvent BLOCK_CHISELED_BOOKSHELF_INSERT = SoundEventImpl.get("minecraft:block.chiseled_bookshelf.insert"); + SoundEvent BLOCK_CHERRY_WOOD_HANGING_SIGN_FALL = BuiltinSoundEvent.get("minecraft:block.cherry_wood_hanging_sign.fall"); - SoundEvent BLOCK_CHISELED_BOOKSHELF_INSERT_ENCHANTED = SoundEventImpl.get("minecraft:block.chiseled_bookshelf.insert.enchanted"); + SoundEvent BLOCK_CHERRY_WOOD_HANGING_SIGN_HIT = BuiltinSoundEvent.get("minecraft:block.cherry_wood_hanging_sign.hit"); - SoundEvent BLOCK_CHISELED_BOOKSHELF_STEP = SoundEventImpl.get("minecraft:block.chiseled_bookshelf.step"); + SoundEvent BLOCK_CHERRY_WOOD_HANGING_SIGN_PLACE = BuiltinSoundEvent.get("minecraft:block.cherry_wood_hanging_sign.place"); - SoundEvent BLOCK_CHISELED_BOOKSHELF_PICKUP = SoundEventImpl.get("minecraft:block.chiseled_bookshelf.pickup"); + SoundEvent BLOCK_CHERRY_WOOD_DOOR_CLOSE = BuiltinSoundEvent.get("minecraft:block.cherry_wood_door.close"); - SoundEvent BLOCK_CHISELED_BOOKSHELF_PICKUP_ENCHANTED = SoundEventImpl.get("minecraft:block.chiseled_bookshelf.pickup.enchanted"); + SoundEvent BLOCK_CHERRY_WOOD_DOOR_OPEN = BuiltinSoundEvent.get("minecraft:block.cherry_wood_door.open"); - SoundEvent BLOCK_CHISELED_BOOKSHELF_PLACE = SoundEventImpl.get("minecraft:block.chiseled_bookshelf.place"); + SoundEvent BLOCK_CHERRY_WOOD_TRAPDOOR_CLOSE = BuiltinSoundEvent.get("minecraft:block.cherry_wood_trapdoor.close"); - SoundEvent BLOCK_CHORUS_FLOWER_DEATH = SoundEventImpl.get("minecraft:block.chorus_flower.death"); + SoundEvent BLOCK_CHERRY_WOOD_TRAPDOOR_OPEN = BuiltinSoundEvent.get("minecraft:block.cherry_wood_trapdoor.open"); - SoundEvent BLOCK_CHORUS_FLOWER_GROW = SoundEventImpl.get("minecraft:block.chorus_flower.grow"); + SoundEvent BLOCK_CHERRY_WOOD_BUTTON_CLICK_OFF = BuiltinSoundEvent.get("minecraft:block.cherry_wood_button.click_off"); - SoundEvent ITEM_CHORUS_FRUIT_TELEPORT = SoundEventImpl.get("minecraft:item.chorus_fruit.teleport"); + SoundEvent BLOCK_CHERRY_WOOD_BUTTON_CLICK_ON = BuiltinSoundEvent.get("minecraft:block.cherry_wood_button.click_on"); - SoundEvent ENTITY_COD_AMBIENT = SoundEventImpl.get("minecraft:entity.cod.ambient"); + SoundEvent BLOCK_CHERRY_WOOD_PRESSURE_PLATE_CLICK_OFF = BuiltinSoundEvent.get("minecraft:block.cherry_wood_pressure_plate.click_off"); - SoundEvent ENTITY_COD_DEATH = SoundEventImpl.get("minecraft:entity.cod.death"); + SoundEvent BLOCK_CHERRY_WOOD_PRESSURE_PLATE_CLICK_ON = BuiltinSoundEvent.get("minecraft:block.cherry_wood_pressure_plate.click_on"); - SoundEvent ENTITY_COD_FLOP = SoundEventImpl.get("minecraft:entity.cod.flop"); + SoundEvent BLOCK_CHERRY_WOOD_FENCE_GATE_CLOSE = BuiltinSoundEvent.get("minecraft:block.cherry_wood_fence_gate.close"); - SoundEvent ENTITY_COD_HURT = SoundEventImpl.get("minecraft:entity.cod.hurt"); + SoundEvent BLOCK_CHERRY_WOOD_FENCE_GATE_OPEN = BuiltinSoundEvent.get("minecraft:block.cherry_wood_fence_gate.open"); - SoundEvent BLOCK_COMPARATOR_CLICK = SoundEventImpl.get("minecraft:block.comparator.click"); + SoundEvent BLOCK_CHEST_CLOSE = BuiltinSoundEvent.get("minecraft:block.chest.close"); - SoundEvent BLOCK_COMPOSTER_EMPTY = SoundEventImpl.get("minecraft:block.composter.empty"); + SoundEvent BLOCK_CHEST_LOCKED = BuiltinSoundEvent.get("minecraft:block.chest.locked"); - SoundEvent BLOCK_COMPOSTER_FILL = SoundEventImpl.get("minecraft:block.composter.fill"); + SoundEvent BLOCK_CHEST_OPEN = BuiltinSoundEvent.get("minecraft:block.chest.open"); - SoundEvent BLOCK_COMPOSTER_FILL_SUCCESS = SoundEventImpl.get("minecraft:block.composter.fill_success"); + SoundEvent ENTITY_CHICKEN_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.chicken.ambient"); - SoundEvent BLOCK_COMPOSTER_READY = SoundEventImpl.get("minecraft:block.composter.ready"); + SoundEvent ENTITY_CHICKEN_DEATH = BuiltinSoundEvent.get("minecraft:entity.chicken.death"); - SoundEvent BLOCK_CONDUIT_ACTIVATE = SoundEventImpl.get("minecraft:block.conduit.activate"); + SoundEvent ENTITY_CHICKEN_EGG = BuiltinSoundEvent.get("minecraft:entity.chicken.egg"); - SoundEvent BLOCK_CONDUIT_AMBIENT = SoundEventImpl.get("minecraft:block.conduit.ambient"); + SoundEvent ENTITY_CHICKEN_HURT = BuiltinSoundEvent.get("minecraft:entity.chicken.hurt"); - SoundEvent BLOCK_CONDUIT_AMBIENT_SHORT = SoundEventImpl.get("minecraft:block.conduit.ambient.short"); + SoundEvent ENTITY_CHICKEN_STEP = BuiltinSoundEvent.get("minecraft:entity.chicken.step"); - SoundEvent BLOCK_CONDUIT_ATTACK_TARGET = SoundEventImpl.get("minecraft:block.conduit.attack.target"); + SoundEvent BLOCK_CHISELED_BOOKSHELF_BREAK = BuiltinSoundEvent.get("minecraft:block.chiseled_bookshelf.break"); - SoundEvent BLOCK_CONDUIT_DEACTIVATE = SoundEventImpl.get("minecraft:block.conduit.deactivate"); + SoundEvent BLOCK_CHISELED_BOOKSHELF_FALL = BuiltinSoundEvent.get("minecraft:block.chiseled_bookshelf.fall"); - SoundEvent BLOCK_COPPER_BULB_BREAK = SoundEventImpl.get("minecraft:block.copper_bulb.break"); + SoundEvent BLOCK_CHISELED_BOOKSHELF_HIT = BuiltinSoundEvent.get("minecraft:block.chiseled_bookshelf.hit"); - SoundEvent BLOCK_COPPER_BULB_STEP = SoundEventImpl.get("minecraft:block.copper_bulb.step"); + SoundEvent BLOCK_CHISELED_BOOKSHELF_INSERT = BuiltinSoundEvent.get("minecraft:block.chiseled_bookshelf.insert"); - SoundEvent BLOCK_COPPER_BULB_PLACE = SoundEventImpl.get("minecraft:block.copper_bulb.place"); + SoundEvent BLOCK_CHISELED_BOOKSHELF_INSERT_ENCHANTED = BuiltinSoundEvent.get("minecraft:block.chiseled_bookshelf.insert.enchanted"); - SoundEvent BLOCK_COPPER_BULB_HIT = SoundEventImpl.get("minecraft:block.copper_bulb.hit"); + SoundEvent BLOCK_CHISELED_BOOKSHELF_STEP = BuiltinSoundEvent.get("minecraft:block.chiseled_bookshelf.step"); - SoundEvent BLOCK_COPPER_BULB_FALL = SoundEventImpl.get("minecraft:block.copper_bulb.fall"); + SoundEvent BLOCK_CHISELED_BOOKSHELF_PICKUP = BuiltinSoundEvent.get("minecraft:block.chiseled_bookshelf.pickup"); - SoundEvent BLOCK_COPPER_BULB_TURN_ON = SoundEventImpl.get("minecraft:block.copper_bulb.turn_on"); + SoundEvent BLOCK_CHISELED_BOOKSHELF_PICKUP_ENCHANTED = BuiltinSoundEvent.get("minecraft:block.chiseled_bookshelf.pickup.enchanted"); - SoundEvent BLOCK_COPPER_BULB_TURN_OFF = SoundEventImpl.get("minecraft:block.copper_bulb.turn_off"); + SoundEvent BLOCK_CHISELED_BOOKSHELF_PLACE = BuiltinSoundEvent.get("minecraft:block.chiseled_bookshelf.place"); - SoundEvent BLOCK_COPPER_BREAK = SoundEventImpl.get("minecraft:block.copper.break"); + SoundEvent BLOCK_CHORUS_FLOWER_DEATH = BuiltinSoundEvent.get("minecraft:block.chorus_flower.death"); - SoundEvent BLOCK_COPPER_STEP = SoundEventImpl.get("minecraft:block.copper.step"); + SoundEvent BLOCK_CHORUS_FLOWER_GROW = BuiltinSoundEvent.get("minecraft:block.chorus_flower.grow"); - SoundEvent BLOCK_COPPER_PLACE = SoundEventImpl.get("minecraft:block.copper.place"); + SoundEvent ITEM_CHORUS_FRUIT_TELEPORT = BuiltinSoundEvent.get("minecraft:item.chorus_fruit.teleport"); - SoundEvent BLOCK_COPPER_HIT = SoundEventImpl.get("minecraft:block.copper.hit"); + SoundEvent BLOCK_COBWEB_BREAK = BuiltinSoundEvent.get("minecraft:block.cobweb.break"); - SoundEvent BLOCK_COPPER_FALL = SoundEventImpl.get("minecraft:block.copper.fall"); + SoundEvent BLOCK_COBWEB_STEP = BuiltinSoundEvent.get("minecraft:block.cobweb.step"); - SoundEvent BLOCK_COPPER_DOOR_CLOSE = SoundEventImpl.get("minecraft:block.copper_door.close"); + SoundEvent BLOCK_COBWEB_PLACE = BuiltinSoundEvent.get("minecraft:block.cobweb.place"); - SoundEvent BLOCK_COPPER_DOOR_OPEN = SoundEventImpl.get("minecraft:block.copper_door.open"); + SoundEvent BLOCK_COBWEB_HIT = BuiltinSoundEvent.get("minecraft:block.cobweb.hit"); - SoundEvent BLOCK_COPPER_GRATE_BREAK = SoundEventImpl.get("minecraft:block.copper_grate.break"); + SoundEvent BLOCK_COBWEB_FALL = BuiltinSoundEvent.get("minecraft:block.cobweb.fall"); - SoundEvent BLOCK_COPPER_GRATE_STEP = SoundEventImpl.get("minecraft:block.copper_grate.step"); + SoundEvent ENTITY_COD_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.cod.ambient"); - SoundEvent BLOCK_COPPER_GRATE_PLACE = SoundEventImpl.get("minecraft:block.copper_grate.place"); + SoundEvent ENTITY_COD_DEATH = BuiltinSoundEvent.get("minecraft:entity.cod.death"); - SoundEvent BLOCK_COPPER_GRATE_HIT = SoundEventImpl.get("minecraft:block.copper_grate.hit"); + SoundEvent ENTITY_COD_FLOP = BuiltinSoundEvent.get("minecraft:entity.cod.flop"); - SoundEvent BLOCK_COPPER_GRATE_FALL = SoundEventImpl.get("minecraft:block.copper_grate.fall"); + SoundEvent ENTITY_COD_HURT = BuiltinSoundEvent.get("minecraft:entity.cod.hurt"); - SoundEvent BLOCK_COPPER_TRAPDOOR_CLOSE = SoundEventImpl.get("minecraft:block.copper_trapdoor.close"); + SoundEvent BLOCK_COMPARATOR_CLICK = BuiltinSoundEvent.get("minecraft:block.comparator.click"); - SoundEvent BLOCK_COPPER_TRAPDOOR_OPEN = SoundEventImpl.get("minecraft:block.copper_trapdoor.open"); + SoundEvent BLOCK_COMPOSTER_EMPTY = BuiltinSoundEvent.get("minecraft:block.composter.empty"); - SoundEvent BLOCK_CORAL_BLOCK_BREAK = SoundEventImpl.get("minecraft:block.coral_block.break"); + SoundEvent BLOCK_COMPOSTER_FILL = BuiltinSoundEvent.get("minecraft:block.composter.fill"); - SoundEvent BLOCK_CORAL_BLOCK_FALL = SoundEventImpl.get("minecraft:block.coral_block.fall"); + SoundEvent BLOCK_COMPOSTER_FILL_SUCCESS = BuiltinSoundEvent.get("minecraft:block.composter.fill_success"); - SoundEvent BLOCK_CORAL_BLOCK_HIT = SoundEventImpl.get("minecraft:block.coral_block.hit"); + SoundEvent BLOCK_COMPOSTER_READY = BuiltinSoundEvent.get("minecraft:block.composter.ready"); - SoundEvent BLOCK_CORAL_BLOCK_PLACE = SoundEventImpl.get("minecraft:block.coral_block.place"); + SoundEvent BLOCK_CONDUIT_ACTIVATE = BuiltinSoundEvent.get("minecraft:block.conduit.activate"); - SoundEvent BLOCK_CORAL_BLOCK_STEP = SoundEventImpl.get("minecraft:block.coral_block.step"); + SoundEvent BLOCK_CONDUIT_AMBIENT = BuiltinSoundEvent.get("minecraft:block.conduit.ambient"); - SoundEvent ENTITY_COW_AMBIENT = SoundEventImpl.get("minecraft:entity.cow.ambient"); + SoundEvent BLOCK_CONDUIT_AMBIENT_SHORT = BuiltinSoundEvent.get("minecraft:block.conduit.ambient.short"); - SoundEvent ENTITY_COW_DEATH = SoundEventImpl.get("minecraft:entity.cow.death"); + SoundEvent BLOCK_CONDUIT_ATTACK_TARGET = BuiltinSoundEvent.get("minecraft:block.conduit.attack.target"); - SoundEvent ENTITY_COW_HURT = SoundEventImpl.get("minecraft:entity.cow.hurt"); + SoundEvent BLOCK_CONDUIT_DEACTIVATE = BuiltinSoundEvent.get("minecraft:block.conduit.deactivate"); - SoundEvent ENTITY_COW_MILK = SoundEventImpl.get("minecraft:entity.cow.milk"); + SoundEvent BLOCK_COPPER_BULB_BREAK = BuiltinSoundEvent.get("minecraft:block.copper_bulb.break"); - SoundEvent ENTITY_COW_STEP = SoundEventImpl.get("minecraft:entity.cow.step"); + SoundEvent BLOCK_COPPER_BULB_STEP = BuiltinSoundEvent.get("minecraft:block.copper_bulb.step"); - SoundEvent BLOCK_CRAFTER_CRAFT = SoundEventImpl.get("minecraft:block.crafter.craft"); + SoundEvent BLOCK_COPPER_BULB_PLACE = BuiltinSoundEvent.get("minecraft:block.copper_bulb.place"); - SoundEvent BLOCK_CRAFTER_FAIL = SoundEventImpl.get("minecraft:block.crafter.fail"); + SoundEvent BLOCK_COPPER_BULB_HIT = BuiltinSoundEvent.get("minecraft:block.copper_bulb.hit"); - SoundEvent ENTITY_CREEPER_DEATH = SoundEventImpl.get("minecraft:entity.creeper.death"); + SoundEvent BLOCK_COPPER_BULB_FALL = BuiltinSoundEvent.get("minecraft:block.copper_bulb.fall"); - SoundEvent ENTITY_CREEPER_HURT = SoundEventImpl.get("minecraft:entity.creeper.hurt"); + SoundEvent BLOCK_COPPER_BULB_TURN_ON = BuiltinSoundEvent.get("minecraft:block.copper_bulb.turn_on"); - SoundEvent ENTITY_CREEPER_PRIMED = SoundEventImpl.get("minecraft:entity.creeper.primed"); + SoundEvent BLOCK_COPPER_BULB_TURN_OFF = BuiltinSoundEvent.get("minecraft:block.copper_bulb.turn_off"); - SoundEvent BLOCK_CROP_BREAK = SoundEventImpl.get("minecraft:block.crop.break"); + SoundEvent BLOCK_COPPER_BREAK = BuiltinSoundEvent.get("minecraft:block.copper.break"); - SoundEvent ITEM_CROP_PLANT = SoundEventImpl.get("minecraft:item.crop.plant"); + SoundEvent BLOCK_COPPER_STEP = BuiltinSoundEvent.get("minecraft:block.copper.step"); - SoundEvent ITEM_CROSSBOW_HIT = SoundEventImpl.get("minecraft:item.crossbow.hit"); + SoundEvent BLOCK_COPPER_PLACE = BuiltinSoundEvent.get("minecraft:block.copper.place"); - SoundEvent ITEM_CROSSBOW_LOADING_END = SoundEventImpl.get("minecraft:item.crossbow.loading_end"); + SoundEvent BLOCK_COPPER_HIT = BuiltinSoundEvent.get("minecraft:block.copper.hit"); - SoundEvent ITEM_CROSSBOW_LOADING_MIDDLE = SoundEventImpl.get("minecraft:item.crossbow.loading_middle"); + SoundEvent BLOCK_COPPER_FALL = BuiltinSoundEvent.get("minecraft:block.copper.fall"); - SoundEvent ITEM_CROSSBOW_LOADING_START = SoundEventImpl.get("minecraft:item.crossbow.loading_start"); + SoundEvent BLOCK_COPPER_DOOR_CLOSE = BuiltinSoundEvent.get("minecraft:block.copper_door.close"); - SoundEvent ITEM_CROSSBOW_QUICK_CHARGE_1 = SoundEventImpl.get("minecraft:item.crossbow.quick_charge_1"); + SoundEvent BLOCK_COPPER_DOOR_OPEN = BuiltinSoundEvent.get("minecraft:block.copper_door.open"); - SoundEvent ITEM_CROSSBOW_QUICK_CHARGE_2 = SoundEventImpl.get("minecraft:item.crossbow.quick_charge_2"); + SoundEvent BLOCK_COPPER_GRATE_BREAK = BuiltinSoundEvent.get("minecraft:block.copper_grate.break"); - SoundEvent ITEM_CROSSBOW_QUICK_CHARGE_3 = SoundEventImpl.get("minecraft:item.crossbow.quick_charge_3"); + SoundEvent BLOCK_COPPER_GRATE_STEP = BuiltinSoundEvent.get("minecraft:block.copper_grate.step"); - SoundEvent ITEM_CROSSBOW_SHOOT = SoundEventImpl.get("minecraft:item.crossbow.shoot"); + SoundEvent BLOCK_COPPER_GRATE_PLACE = BuiltinSoundEvent.get("minecraft:block.copper_grate.place"); - SoundEvent BLOCK_DECORATED_POT_BREAK = SoundEventImpl.get("minecraft:block.decorated_pot.break"); + SoundEvent BLOCK_COPPER_GRATE_HIT = BuiltinSoundEvent.get("minecraft:block.copper_grate.hit"); - SoundEvent BLOCK_DECORATED_POT_FALL = SoundEventImpl.get("minecraft:block.decorated_pot.fall"); + SoundEvent BLOCK_COPPER_GRATE_FALL = BuiltinSoundEvent.get("minecraft:block.copper_grate.fall"); - SoundEvent BLOCK_DECORATED_POT_HIT = SoundEventImpl.get("minecraft:block.decorated_pot.hit"); + SoundEvent BLOCK_COPPER_TRAPDOOR_CLOSE = BuiltinSoundEvent.get("minecraft:block.copper_trapdoor.close"); - SoundEvent BLOCK_DECORATED_POT_INSERT = SoundEventImpl.get("minecraft:block.decorated_pot.insert"); + SoundEvent BLOCK_COPPER_TRAPDOOR_OPEN = BuiltinSoundEvent.get("minecraft:block.copper_trapdoor.open"); - SoundEvent BLOCK_DECORATED_POT_INSERT_FAIL = SoundEventImpl.get("minecraft:block.decorated_pot.insert_fail"); + SoundEvent BLOCK_CORAL_BLOCK_BREAK = BuiltinSoundEvent.get("minecraft:block.coral_block.break"); - SoundEvent BLOCK_DECORATED_POT_STEP = SoundEventImpl.get("minecraft:block.decorated_pot.step"); + SoundEvent BLOCK_CORAL_BLOCK_FALL = BuiltinSoundEvent.get("minecraft:block.coral_block.fall"); - SoundEvent BLOCK_DECORATED_POT_PLACE = SoundEventImpl.get("minecraft:block.decorated_pot.place"); + SoundEvent BLOCK_CORAL_BLOCK_HIT = BuiltinSoundEvent.get("minecraft:block.coral_block.hit"); - SoundEvent BLOCK_DECORATED_POT_SHATTER = SoundEventImpl.get("minecraft:block.decorated_pot.shatter"); + SoundEvent BLOCK_CORAL_BLOCK_PLACE = BuiltinSoundEvent.get("minecraft:block.coral_block.place"); - SoundEvent BLOCK_DEEPSLATE_BRICKS_BREAK = SoundEventImpl.get("minecraft:block.deepslate_bricks.break"); + SoundEvent BLOCK_CORAL_BLOCK_STEP = BuiltinSoundEvent.get("minecraft:block.coral_block.step"); - SoundEvent BLOCK_DEEPSLATE_BRICKS_FALL = SoundEventImpl.get("minecraft:block.deepslate_bricks.fall"); + SoundEvent ENTITY_COW_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.cow.ambient"); - SoundEvent BLOCK_DEEPSLATE_BRICKS_HIT = SoundEventImpl.get("minecraft:block.deepslate_bricks.hit"); + SoundEvent ENTITY_COW_DEATH = BuiltinSoundEvent.get("minecraft:entity.cow.death"); - SoundEvent BLOCK_DEEPSLATE_BRICKS_PLACE = SoundEventImpl.get("minecraft:block.deepslate_bricks.place"); + SoundEvent ENTITY_COW_HURT = BuiltinSoundEvent.get("minecraft:entity.cow.hurt"); - SoundEvent BLOCK_DEEPSLATE_BRICKS_STEP = SoundEventImpl.get("minecraft:block.deepslate_bricks.step"); + SoundEvent ENTITY_COW_MILK = BuiltinSoundEvent.get("minecraft:entity.cow.milk"); - SoundEvent BLOCK_DEEPSLATE_BREAK = SoundEventImpl.get("minecraft:block.deepslate.break"); + SoundEvent ENTITY_COW_STEP = BuiltinSoundEvent.get("minecraft:entity.cow.step"); - SoundEvent BLOCK_DEEPSLATE_FALL = SoundEventImpl.get("minecraft:block.deepslate.fall"); + SoundEvent BLOCK_CRAFTER_CRAFT = BuiltinSoundEvent.get("minecraft:block.crafter.craft"); - SoundEvent BLOCK_DEEPSLATE_HIT = SoundEventImpl.get("minecraft:block.deepslate.hit"); + SoundEvent BLOCK_CRAFTER_FAIL = BuiltinSoundEvent.get("minecraft:block.crafter.fail"); - SoundEvent BLOCK_DEEPSLATE_PLACE = SoundEventImpl.get("minecraft:block.deepslate.place"); + SoundEvent ENTITY_CREEPER_DEATH = BuiltinSoundEvent.get("minecraft:entity.creeper.death"); - SoundEvent BLOCK_DEEPSLATE_STEP = SoundEventImpl.get("minecraft:block.deepslate.step"); + SoundEvent ENTITY_CREEPER_HURT = BuiltinSoundEvent.get("minecraft:entity.creeper.hurt"); - SoundEvent BLOCK_DEEPSLATE_TILES_BREAK = SoundEventImpl.get("minecraft:block.deepslate_tiles.break"); + SoundEvent ENTITY_CREEPER_PRIMED = BuiltinSoundEvent.get("minecraft:entity.creeper.primed"); - SoundEvent BLOCK_DEEPSLATE_TILES_FALL = SoundEventImpl.get("minecraft:block.deepslate_tiles.fall"); + SoundEvent BLOCK_CROP_BREAK = BuiltinSoundEvent.get("minecraft:block.crop.break"); - SoundEvent BLOCK_DEEPSLATE_TILES_HIT = SoundEventImpl.get("minecraft:block.deepslate_tiles.hit"); + SoundEvent ITEM_CROP_PLANT = BuiltinSoundEvent.get("minecraft:item.crop.plant"); - SoundEvent BLOCK_DEEPSLATE_TILES_PLACE = SoundEventImpl.get("minecraft:block.deepslate_tiles.place"); + SoundEvent ITEM_CROSSBOW_HIT = BuiltinSoundEvent.get("minecraft:item.crossbow.hit"); - SoundEvent BLOCK_DEEPSLATE_TILES_STEP = SoundEventImpl.get("minecraft:block.deepslate_tiles.step"); + SoundEvent ITEM_CROSSBOW_LOADING_END = BuiltinSoundEvent.get("minecraft:item.crossbow.loading_end"); - SoundEvent BLOCK_DISPENSER_DISPENSE = SoundEventImpl.get("minecraft:block.dispenser.dispense"); + SoundEvent ITEM_CROSSBOW_LOADING_MIDDLE = BuiltinSoundEvent.get("minecraft:item.crossbow.loading_middle"); - SoundEvent BLOCK_DISPENSER_FAIL = SoundEventImpl.get("minecraft:block.dispenser.fail"); + SoundEvent ITEM_CROSSBOW_LOADING_START = BuiltinSoundEvent.get("minecraft:item.crossbow.loading_start"); - SoundEvent BLOCK_DISPENSER_LAUNCH = SoundEventImpl.get("minecraft:block.dispenser.launch"); + SoundEvent ITEM_CROSSBOW_QUICK_CHARGE_1 = BuiltinSoundEvent.get("minecraft:item.crossbow.quick_charge_1"); - SoundEvent ENTITY_DOLPHIN_AMBIENT = SoundEventImpl.get("minecraft:entity.dolphin.ambient"); + SoundEvent ITEM_CROSSBOW_QUICK_CHARGE_2 = BuiltinSoundEvent.get("minecraft:item.crossbow.quick_charge_2"); - SoundEvent ENTITY_DOLPHIN_AMBIENT_WATER = SoundEventImpl.get("minecraft:entity.dolphin.ambient_water"); + SoundEvent ITEM_CROSSBOW_QUICK_CHARGE_3 = BuiltinSoundEvent.get("minecraft:item.crossbow.quick_charge_3"); - SoundEvent ENTITY_DOLPHIN_ATTACK = SoundEventImpl.get("minecraft:entity.dolphin.attack"); + SoundEvent ITEM_CROSSBOW_SHOOT = BuiltinSoundEvent.get("minecraft:item.crossbow.shoot"); - SoundEvent ENTITY_DOLPHIN_DEATH = SoundEventImpl.get("minecraft:entity.dolphin.death"); + SoundEvent BLOCK_DECORATED_POT_BREAK = BuiltinSoundEvent.get("minecraft:block.decorated_pot.break"); - SoundEvent ENTITY_DOLPHIN_EAT = SoundEventImpl.get("minecraft:entity.dolphin.eat"); + SoundEvent BLOCK_DECORATED_POT_FALL = BuiltinSoundEvent.get("minecraft:block.decorated_pot.fall"); - SoundEvent ENTITY_DOLPHIN_HURT = SoundEventImpl.get("minecraft:entity.dolphin.hurt"); + SoundEvent BLOCK_DECORATED_POT_HIT = BuiltinSoundEvent.get("minecraft:block.decorated_pot.hit"); - SoundEvent ENTITY_DOLPHIN_JUMP = SoundEventImpl.get("minecraft:entity.dolphin.jump"); + SoundEvent BLOCK_DECORATED_POT_INSERT = BuiltinSoundEvent.get("minecraft:block.decorated_pot.insert"); - SoundEvent ENTITY_DOLPHIN_PLAY = SoundEventImpl.get("minecraft:entity.dolphin.play"); + SoundEvent BLOCK_DECORATED_POT_INSERT_FAIL = BuiltinSoundEvent.get("minecraft:block.decorated_pot.insert_fail"); - SoundEvent ENTITY_DOLPHIN_SPLASH = SoundEventImpl.get("minecraft:entity.dolphin.splash"); + SoundEvent BLOCK_DECORATED_POT_STEP = BuiltinSoundEvent.get("minecraft:block.decorated_pot.step"); - SoundEvent ENTITY_DOLPHIN_SWIM = SoundEventImpl.get("minecraft:entity.dolphin.swim"); + SoundEvent BLOCK_DECORATED_POT_PLACE = BuiltinSoundEvent.get("minecraft:block.decorated_pot.place"); - SoundEvent ENTITY_DONKEY_AMBIENT = SoundEventImpl.get("minecraft:entity.donkey.ambient"); + SoundEvent BLOCK_DECORATED_POT_SHATTER = BuiltinSoundEvent.get("minecraft:block.decorated_pot.shatter"); - SoundEvent ENTITY_DONKEY_ANGRY = SoundEventImpl.get("minecraft:entity.donkey.angry"); + SoundEvent BLOCK_DEEPSLATE_BRICKS_BREAK = BuiltinSoundEvent.get("minecraft:block.deepslate_bricks.break"); - SoundEvent ENTITY_DONKEY_CHEST = SoundEventImpl.get("minecraft:entity.donkey.chest"); + SoundEvent BLOCK_DEEPSLATE_BRICKS_FALL = BuiltinSoundEvent.get("minecraft:block.deepslate_bricks.fall"); - SoundEvent ENTITY_DONKEY_DEATH = SoundEventImpl.get("minecraft:entity.donkey.death"); + SoundEvent BLOCK_DEEPSLATE_BRICKS_HIT = BuiltinSoundEvent.get("minecraft:block.deepslate_bricks.hit"); - SoundEvent ENTITY_DONKEY_EAT = SoundEventImpl.get("minecraft:entity.donkey.eat"); + SoundEvent BLOCK_DEEPSLATE_BRICKS_PLACE = BuiltinSoundEvent.get("minecraft:block.deepslate_bricks.place"); - SoundEvent ENTITY_DONKEY_HURT = SoundEventImpl.get("minecraft:entity.donkey.hurt"); + SoundEvent BLOCK_DEEPSLATE_BRICKS_STEP = BuiltinSoundEvent.get("minecraft:block.deepslate_bricks.step"); - SoundEvent BLOCK_DRIPSTONE_BLOCK_BREAK = SoundEventImpl.get("minecraft:block.dripstone_block.break"); + SoundEvent BLOCK_DEEPSLATE_BREAK = BuiltinSoundEvent.get("minecraft:block.deepslate.break"); - SoundEvent BLOCK_DRIPSTONE_BLOCK_STEP = SoundEventImpl.get("minecraft:block.dripstone_block.step"); + SoundEvent BLOCK_DEEPSLATE_FALL = BuiltinSoundEvent.get("minecraft:block.deepslate.fall"); - SoundEvent BLOCK_DRIPSTONE_BLOCK_PLACE = SoundEventImpl.get("minecraft:block.dripstone_block.place"); + SoundEvent BLOCK_DEEPSLATE_HIT = BuiltinSoundEvent.get("minecraft:block.deepslate.hit"); - SoundEvent BLOCK_DRIPSTONE_BLOCK_HIT = SoundEventImpl.get("minecraft:block.dripstone_block.hit"); + SoundEvent BLOCK_DEEPSLATE_PLACE = BuiltinSoundEvent.get("minecraft:block.deepslate.place"); - SoundEvent BLOCK_DRIPSTONE_BLOCK_FALL = SoundEventImpl.get("minecraft:block.dripstone_block.fall"); + SoundEvent BLOCK_DEEPSLATE_STEP = BuiltinSoundEvent.get("minecraft:block.deepslate.step"); - SoundEvent BLOCK_POINTED_DRIPSTONE_BREAK = SoundEventImpl.get("minecraft:block.pointed_dripstone.break"); + SoundEvent BLOCK_DEEPSLATE_TILES_BREAK = BuiltinSoundEvent.get("minecraft:block.deepslate_tiles.break"); - SoundEvent BLOCK_POINTED_DRIPSTONE_STEP = SoundEventImpl.get("minecraft:block.pointed_dripstone.step"); + SoundEvent BLOCK_DEEPSLATE_TILES_FALL = BuiltinSoundEvent.get("minecraft:block.deepslate_tiles.fall"); - SoundEvent BLOCK_POINTED_DRIPSTONE_PLACE = SoundEventImpl.get("minecraft:block.pointed_dripstone.place"); + SoundEvent BLOCK_DEEPSLATE_TILES_HIT = BuiltinSoundEvent.get("minecraft:block.deepslate_tiles.hit"); - SoundEvent BLOCK_POINTED_DRIPSTONE_HIT = SoundEventImpl.get("minecraft:block.pointed_dripstone.hit"); + SoundEvent BLOCK_DEEPSLATE_TILES_PLACE = BuiltinSoundEvent.get("minecraft:block.deepslate_tiles.place"); - SoundEvent BLOCK_POINTED_DRIPSTONE_FALL = SoundEventImpl.get("minecraft:block.pointed_dripstone.fall"); + SoundEvent BLOCK_DEEPSLATE_TILES_STEP = BuiltinSoundEvent.get("minecraft:block.deepslate_tiles.step"); - SoundEvent BLOCK_POINTED_DRIPSTONE_LAND = SoundEventImpl.get("minecraft:block.pointed_dripstone.land"); + SoundEvent BLOCK_DISPENSER_DISPENSE = BuiltinSoundEvent.get("minecraft:block.dispenser.dispense"); - SoundEvent BLOCK_POINTED_DRIPSTONE_DRIP_LAVA = SoundEventImpl.get("minecraft:block.pointed_dripstone.drip_lava"); + SoundEvent BLOCK_DISPENSER_FAIL = BuiltinSoundEvent.get("minecraft:block.dispenser.fail"); - SoundEvent BLOCK_POINTED_DRIPSTONE_DRIP_WATER = SoundEventImpl.get("minecraft:block.pointed_dripstone.drip_water"); + SoundEvent BLOCK_DISPENSER_LAUNCH = BuiltinSoundEvent.get("minecraft:block.dispenser.launch"); - SoundEvent BLOCK_POINTED_DRIPSTONE_DRIP_LAVA_INTO_CAULDRON = SoundEventImpl.get("minecraft:block.pointed_dripstone.drip_lava_into_cauldron"); + SoundEvent ENTITY_DOLPHIN_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.dolphin.ambient"); - SoundEvent BLOCK_POINTED_DRIPSTONE_DRIP_WATER_INTO_CAULDRON = SoundEventImpl.get("minecraft:block.pointed_dripstone.drip_water_into_cauldron"); + SoundEvent ENTITY_DOLPHIN_AMBIENT_WATER = BuiltinSoundEvent.get("minecraft:entity.dolphin.ambient_water"); - SoundEvent BLOCK_BIG_DRIPLEAF_TILT_DOWN = SoundEventImpl.get("minecraft:block.big_dripleaf.tilt_down"); + SoundEvent ENTITY_DOLPHIN_ATTACK = BuiltinSoundEvent.get("minecraft:entity.dolphin.attack"); - SoundEvent BLOCK_BIG_DRIPLEAF_TILT_UP = SoundEventImpl.get("minecraft:block.big_dripleaf.tilt_up"); + SoundEvent ENTITY_DOLPHIN_DEATH = BuiltinSoundEvent.get("minecraft:entity.dolphin.death"); - SoundEvent ENTITY_DROWNED_AMBIENT = SoundEventImpl.get("minecraft:entity.drowned.ambient"); + SoundEvent ENTITY_DOLPHIN_EAT = BuiltinSoundEvent.get("minecraft:entity.dolphin.eat"); - SoundEvent ENTITY_DROWNED_AMBIENT_WATER = SoundEventImpl.get("minecraft:entity.drowned.ambient_water"); + SoundEvent ENTITY_DOLPHIN_HURT = BuiltinSoundEvent.get("minecraft:entity.dolphin.hurt"); - SoundEvent ENTITY_DROWNED_DEATH = SoundEventImpl.get("minecraft:entity.drowned.death"); + SoundEvent ENTITY_DOLPHIN_JUMP = BuiltinSoundEvent.get("minecraft:entity.dolphin.jump"); - SoundEvent ENTITY_DROWNED_DEATH_WATER = SoundEventImpl.get("minecraft:entity.drowned.death_water"); + SoundEvent ENTITY_DOLPHIN_PLAY = BuiltinSoundEvent.get("minecraft:entity.dolphin.play"); - SoundEvent ENTITY_DROWNED_HURT = SoundEventImpl.get("minecraft:entity.drowned.hurt"); + SoundEvent ENTITY_DOLPHIN_SPLASH = BuiltinSoundEvent.get("minecraft:entity.dolphin.splash"); - SoundEvent ENTITY_DROWNED_HURT_WATER = SoundEventImpl.get("minecraft:entity.drowned.hurt_water"); + SoundEvent ENTITY_DOLPHIN_SWIM = BuiltinSoundEvent.get("minecraft:entity.dolphin.swim"); - SoundEvent ENTITY_DROWNED_SHOOT = SoundEventImpl.get("minecraft:entity.drowned.shoot"); + SoundEvent ENTITY_DONKEY_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.donkey.ambient"); - SoundEvent ENTITY_DROWNED_STEP = SoundEventImpl.get("minecraft:entity.drowned.step"); + SoundEvent ENTITY_DONKEY_ANGRY = BuiltinSoundEvent.get("minecraft:entity.donkey.angry"); - SoundEvent ENTITY_DROWNED_SWIM = SoundEventImpl.get("minecraft:entity.drowned.swim"); + SoundEvent ENTITY_DONKEY_CHEST = BuiltinSoundEvent.get("minecraft:entity.donkey.chest"); - SoundEvent ITEM_DYE_USE = SoundEventImpl.get("minecraft:item.dye.use"); + SoundEvent ENTITY_DONKEY_DEATH = BuiltinSoundEvent.get("minecraft:entity.donkey.death"); - SoundEvent ENTITY_EGG_THROW = SoundEventImpl.get("minecraft:entity.egg.throw"); + SoundEvent ENTITY_DONKEY_EAT = BuiltinSoundEvent.get("minecraft:entity.donkey.eat"); - SoundEvent ENTITY_ELDER_GUARDIAN_AMBIENT = SoundEventImpl.get("minecraft:entity.elder_guardian.ambient"); + SoundEvent ENTITY_DONKEY_HURT = BuiltinSoundEvent.get("minecraft:entity.donkey.hurt"); - SoundEvent ENTITY_ELDER_GUARDIAN_AMBIENT_LAND = SoundEventImpl.get("minecraft:entity.elder_guardian.ambient_land"); + SoundEvent ENTITY_DONKEY_JUMP = BuiltinSoundEvent.get("minecraft:entity.donkey.jump"); - SoundEvent ENTITY_ELDER_GUARDIAN_CURSE = SoundEventImpl.get("minecraft:entity.elder_guardian.curse"); + SoundEvent BLOCK_DRIPSTONE_BLOCK_BREAK = BuiltinSoundEvent.get("minecraft:block.dripstone_block.break"); - SoundEvent ENTITY_ELDER_GUARDIAN_DEATH = SoundEventImpl.get("minecraft:entity.elder_guardian.death"); + SoundEvent BLOCK_DRIPSTONE_BLOCK_STEP = BuiltinSoundEvent.get("minecraft:block.dripstone_block.step"); - SoundEvent ENTITY_ELDER_GUARDIAN_DEATH_LAND = SoundEventImpl.get("minecraft:entity.elder_guardian.death_land"); + SoundEvent BLOCK_DRIPSTONE_BLOCK_PLACE = BuiltinSoundEvent.get("minecraft:block.dripstone_block.place"); - SoundEvent ENTITY_ELDER_GUARDIAN_FLOP = SoundEventImpl.get("minecraft:entity.elder_guardian.flop"); + SoundEvent BLOCK_DRIPSTONE_BLOCK_HIT = BuiltinSoundEvent.get("minecraft:block.dripstone_block.hit"); - SoundEvent ENTITY_ELDER_GUARDIAN_HURT = SoundEventImpl.get("minecraft:entity.elder_guardian.hurt"); + SoundEvent BLOCK_DRIPSTONE_BLOCK_FALL = BuiltinSoundEvent.get("minecraft:block.dripstone_block.fall"); - SoundEvent ENTITY_ELDER_GUARDIAN_HURT_LAND = SoundEventImpl.get("minecraft:entity.elder_guardian.hurt_land"); + SoundEvent BLOCK_POINTED_DRIPSTONE_BREAK = BuiltinSoundEvent.get("minecraft:block.pointed_dripstone.break"); - SoundEvent ITEM_ELYTRA_FLYING = SoundEventImpl.get("minecraft:item.elytra.flying"); + SoundEvent BLOCK_POINTED_DRIPSTONE_STEP = BuiltinSoundEvent.get("minecraft:block.pointed_dripstone.step"); - SoundEvent BLOCK_ENCHANTMENT_TABLE_USE = SoundEventImpl.get("minecraft:block.enchantment_table.use"); + SoundEvent BLOCK_POINTED_DRIPSTONE_PLACE = BuiltinSoundEvent.get("minecraft:block.pointed_dripstone.place"); - SoundEvent BLOCK_ENDER_CHEST_CLOSE = SoundEventImpl.get("minecraft:block.ender_chest.close"); + SoundEvent BLOCK_POINTED_DRIPSTONE_HIT = BuiltinSoundEvent.get("minecraft:block.pointed_dripstone.hit"); - SoundEvent BLOCK_ENDER_CHEST_OPEN = SoundEventImpl.get("minecraft:block.ender_chest.open"); + SoundEvent BLOCK_POINTED_DRIPSTONE_FALL = BuiltinSoundEvent.get("minecraft:block.pointed_dripstone.fall"); - SoundEvent ENTITY_ENDER_DRAGON_AMBIENT = SoundEventImpl.get("minecraft:entity.ender_dragon.ambient"); + SoundEvent BLOCK_POINTED_DRIPSTONE_LAND = BuiltinSoundEvent.get("minecraft:block.pointed_dripstone.land"); - SoundEvent ENTITY_ENDER_DRAGON_DEATH = SoundEventImpl.get("minecraft:entity.ender_dragon.death"); + SoundEvent BLOCK_POINTED_DRIPSTONE_DRIP_LAVA = BuiltinSoundEvent.get("minecraft:block.pointed_dripstone.drip_lava"); - SoundEvent ENTITY_DRAGON_FIREBALL_EXPLODE = SoundEventImpl.get("minecraft:entity.dragon_fireball.explode"); + SoundEvent BLOCK_POINTED_DRIPSTONE_DRIP_WATER = BuiltinSoundEvent.get("minecraft:block.pointed_dripstone.drip_water"); - SoundEvent ENTITY_ENDER_DRAGON_FLAP = SoundEventImpl.get("minecraft:entity.ender_dragon.flap"); + SoundEvent BLOCK_POINTED_DRIPSTONE_DRIP_LAVA_INTO_CAULDRON = BuiltinSoundEvent.get("minecraft:block.pointed_dripstone.drip_lava_into_cauldron"); - SoundEvent ENTITY_ENDER_DRAGON_GROWL = SoundEventImpl.get("minecraft:entity.ender_dragon.growl"); + SoundEvent BLOCK_POINTED_DRIPSTONE_DRIP_WATER_INTO_CAULDRON = BuiltinSoundEvent.get("minecraft:block.pointed_dripstone.drip_water_into_cauldron"); - SoundEvent ENTITY_ENDER_DRAGON_HURT = SoundEventImpl.get("minecraft:entity.ender_dragon.hurt"); + SoundEvent BLOCK_BIG_DRIPLEAF_TILT_DOWN = BuiltinSoundEvent.get("minecraft:block.big_dripleaf.tilt_down"); - SoundEvent ENTITY_ENDER_DRAGON_SHOOT = SoundEventImpl.get("minecraft:entity.ender_dragon.shoot"); + SoundEvent BLOCK_BIG_DRIPLEAF_TILT_UP = BuiltinSoundEvent.get("minecraft:block.big_dripleaf.tilt_up"); - SoundEvent ENTITY_ENDER_EYE_DEATH = SoundEventImpl.get("minecraft:entity.ender_eye.death"); + SoundEvent ENTITY_DROWNED_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.drowned.ambient"); - SoundEvent ENTITY_ENDER_EYE_LAUNCH = SoundEventImpl.get("minecraft:entity.ender_eye.launch"); + SoundEvent ENTITY_DROWNED_AMBIENT_WATER = BuiltinSoundEvent.get("minecraft:entity.drowned.ambient_water"); - SoundEvent ENTITY_ENDERMAN_AMBIENT = SoundEventImpl.get("minecraft:entity.enderman.ambient"); + SoundEvent ENTITY_DROWNED_DEATH = BuiltinSoundEvent.get("minecraft:entity.drowned.death"); - SoundEvent ENTITY_ENDERMAN_DEATH = SoundEventImpl.get("minecraft:entity.enderman.death"); + SoundEvent ENTITY_DROWNED_DEATH_WATER = BuiltinSoundEvent.get("minecraft:entity.drowned.death_water"); - SoundEvent ENTITY_ENDERMAN_HURT = SoundEventImpl.get("minecraft:entity.enderman.hurt"); + SoundEvent ENTITY_DROWNED_HURT = BuiltinSoundEvent.get("minecraft:entity.drowned.hurt"); - SoundEvent ENTITY_ENDERMAN_SCREAM = SoundEventImpl.get("minecraft:entity.enderman.scream"); + SoundEvent ENTITY_DROWNED_HURT_WATER = BuiltinSoundEvent.get("minecraft:entity.drowned.hurt_water"); - SoundEvent ENTITY_ENDERMAN_STARE = SoundEventImpl.get("minecraft:entity.enderman.stare"); + SoundEvent ENTITY_DROWNED_SHOOT = BuiltinSoundEvent.get("minecraft:entity.drowned.shoot"); - SoundEvent ENTITY_ENDERMAN_TELEPORT = SoundEventImpl.get("minecraft:entity.enderman.teleport"); + SoundEvent ENTITY_DROWNED_STEP = BuiltinSoundEvent.get("minecraft:entity.drowned.step"); - SoundEvent ENTITY_ENDERMITE_AMBIENT = SoundEventImpl.get("minecraft:entity.endermite.ambient"); + SoundEvent ENTITY_DROWNED_SWIM = BuiltinSoundEvent.get("minecraft:entity.drowned.swim"); - SoundEvent ENTITY_ENDERMITE_DEATH = SoundEventImpl.get("minecraft:entity.endermite.death"); + SoundEvent ITEM_DYE_USE = BuiltinSoundEvent.get("minecraft:item.dye.use"); - SoundEvent ENTITY_ENDERMITE_HURT = SoundEventImpl.get("minecraft:entity.endermite.hurt"); + SoundEvent ENTITY_EGG_THROW = BuiltinSoundEvent.get("minecraft:entity.egg.throw"); - SoundEvent ENTITY_ENDERMITE_STEP = SoundEventImpl.get("minecraft:entity.endermite.step"); + SoundEvent ENTITY_ELDER_GUARDIAN_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.elder_guardian.ambient"); - SoundEvent ENTITY_ENDER_PEARL_THROW = SoundEventImpl.get("minecraft:entity.ender_pearl.throw"); + SoundEvent ENTITY_ELDER_GUARDIAN_AMBIENT_LAND = BuiltinSoundEvent.get("minecraft:entity.elder_guardian.ambient_land"); - SoundEvent BLOCK_END_GATEWAY_SPAWN = SoundEventImpl.get("minecraft:block.end_gateway.spawn"); + SoundEvent ENTITY_ELDER_GUARDIAN_CURSE = BuiltinSoundEvent.get("minecraft:entity.elder_guardian.curse"); - SoundEvent BLOCK_END_PORTAL_FRAME_FILL = SoundEventImpl.get("minecraft:block.end_portal_frame.fill"); + SoundEvent ENTITY_ELDER_GUARDIAN_DEATH = BuiltinSoundEvent.get("minecraft:entity.elder_guardian.death"); - SoundEvent BLOCK_END_PORTAL_SPAWN = SoundEventImpl.get("minecraft:block.end_portal.spawn"); + SoundEvent ENTITY_ELDER_GUARDIAN_DEATH_LAND = BuiltinSoundEvent.get("minecraft:entity.elder_guardian.death_land"); - SoundEvent ENTITY_EVOKER_AMBIENT = SoundEventImpl.get("minecraft:entity.evoker.ambient"); + SoundEvent ENTITY_ELDER_GUARDIAN_FLOP = BuiltinSoundEvent.get("minecraft:entity.elder_guardian.flop"); - SoundEvent ENTITY_EVOKER_CAST_SPELL = SoundEventImpl.get("minecraft:entity.evoker.cast_spell"); + SoundEvent ENTITY_ELDER_GUARDIAN_HURT = BuiltinSoundEvent.get("minecraft:entity.elder_guardian.hurt"); - SoundEvent ENTITY_EVOKER_CELEBRATE = SoundEventImpl.get("minecraft:entity.evoker.celebrate"); + SoundEvent ENTITY_ELDER_GUARDIAN_HURT_LAND = BuiltinSoundEvent.get("minecraft:entity.elder_guardian.hurt_land"); - SoundEvent ENTITY_EVOKER_DEATH = SoundEventImpl.get("minecraft:entity.evoker.death"); + SoundEvent ITEM_ELYTRA_FLYING = BuiltinSoundEvent.get("minecraft:item.elytra.flying"); - SoundEvent ENTITY_EVOKER_FANGS_ATTACK = SoundEventImpl.get("minecraft:entity.evoker_fangs.attack"); + SoundEvent BLOCK_ENCHANTMENT_TABLE_USE = BuiltinSoundEvent.get("minecraft:block.enchantment_table.use"); - SoundEvent ENTITY_EVOKER_HURT = SoundEventImpl.get("minecraft:entity.evoker.hurt"); + SoundEvent BLOCK_ENDER_CHEST_CLOSE = BuiltinSoundEvent.get("minecraft:block.ender_chest.close"); - SoundEvent ENTITY_EVOKER_PREPARE_ATTACK = SoundEventImpl.get("minecraft:entity.evoker.prepare_attack"); + SoundEvent BLOCK_ENDER_CHEST_OPEN = BuiltinSoundEvent.get("minecraft:block.ender_chest.open"); - SoundEvent ENTITY_EVOKER_PREPARE_SUMMON = SoundEventImpl.get("minecraft:entity.evoker.prepare_summon"); + SoundEvent ENTITY_ENDER_DRAGON_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.ender_dragon.ambient"); - SoundEvent ENTITY_EVOKER_PREPARE_WOLOLO = SoundEventImpl.get("minecraft:entity.evoker.prepare_wololo"); + SoundEvent ENTITY_ENDER_DRAGON_DEATH = BuiltinSoundEvent.get("minecraft:entity.ender_dragon.death"); - SoundEvent ENTITY_EXPERIENCE_BOTTLE_THROW = SoundEventImpl.get("minecraft:entity.experience_bottle.throw"); + SoundEvent ENTITY_DRAGON_FIREBALL_EXPLODE = BuiltinSoundEvent.get("minecraft:entity.dragon_fireball.explode"); - SoundEvent ENTITY_EXPERIENCE_ORB_PICKUP = SoundEventImpl.get("minecraft:entity.experience_orb.pickup"); + SoundEvent ENTITY_ENDER_DRAGON_FLAP = BuiltinSoundEvent.get("minecraft:entity.ender_dragon.flap"); - SoundEvent BLOCK_FENCE_GATE_CLOSE = SoundEventImpl.get("minecraft:block.fence_gate.close"); + SoundEvent ENTITY_ENDER_DRAGON_GROWL = BuiltinSoundEvent.get("minecraft:entity.ender_dragon.growl"); - SoundEvent BLOCK_FENCE_GATE_OPEN = SoundEventImpl.get("minecraft:block.fence_gate.open"); + SoundEvent ENTITY_ENDER_DRAGON_HURT = BuiltinSoundEvent.get("minecraft:entity.ender_dragon.hurt"); - SoundEvent ITEM_FIRECHARGE_USE = SoundEventImpl.get("minecraft:item.firecharge.use"); + SoundEvent ENTITY_ENDER_DRAGON_SHOOT = BuiltinSoundEvent.get("minecraft:entity.ender_dragon.shoot"); - SoundEvent ENTITY_FIREWORK_ROCKET_BLAST = SoundEventImpl.get("minecraft:entity.firework_rocket.blast"); + SoundEvent ENTITY_ENDER_EYE_DEATH = BuiltinSoundEvent.get("minecraft:entity.ender_eye.death"); - SoundEvent ENTITY_FIREWORK_ROCKET_BLAST_FAR = SoundEventImpl.get("minecraft:entity.firework_rocket.blast_far"); + SoundEvent ENTITY_ENDER_EYE_LAUNCH = BuiltinSoundEvent.get("minecraft:entity.ender_eye.launch"); - SoundEvent ENTITY_FIREWORK_ROCKET_LARGE_BLAST = SoundEventImpl.get("minecraft:entity.firework_rocket.large_blast"); + SoundEvent ENTITY_ENDERMAN_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.enderman.ambient"); - SoundEvent ENTITY_FIREWORK_ROCKET_LARGE_BLAST_FAR = SoundEventImpl.get("minecraft:entity.firework_rocket.large_blast_far"); + SoundEvent ENTITY_ENDERMAN_DEATH = BuiltinSoundEvent.get("minecraft:entity.enderman.death"); - SoundEvent ENTITY_FIREWORK_ROCKET_LAUNCH = SoundEventImpl.get("minecraft:entity.firework_rocket.launch"); + SoundEvent ENTITY_ENDERMAN_HURT = BuiltinSoundEvent.get("minecraft:entity.enderman.hurt"); - SoundEvent ENTITY_FIREWORK_ROCKET_SHOOT = SoundEventImpl.get("minecraft:entity.firework_rocket.shoot"); + SoundEvent ENTITY_ENDERMAN_SCREAM = BuiltinSoundEvent.get("minecraft:entity.enderman.scream"); - SoundEvent ENTITY_FIREWORK_ROCKET_TWINKLE = SoundEventImpl.get("minecraft:entity.firework_rocket.twinkle"); + SoundEvent ENTITY_ENDERMAN_STARE = BuiltinSoundEvent.get("minecraft:entity.enderman.stare"); - SoundEvent ENTITY_FIREWORK_ROCKET_TWINKLE_FAR = SoundEventImpl.get("minecraft:entity.firework_rocket.twinkle_far"); + SoundEvent ENTITY_ENDERMAN_TELEPORT = BuiltinSoundEvent.get("minecraft:entity.enderman.teleport"); - SoundEvent BLOCK_FIRE_AMBIENT = SoundEventImpl.get("minecraft:block.fire.ambient"); + SoundEvent ENTITY_ENDERMITE_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.endermite.ambient"); - SoundEvent BLOCK_FIRE_EXTINGUISH = SoundEventImpl.get("minecraft:block.fire.extinguish"); + SoundEvent ENTITY_ENDERMITE_DEATH = BuiltinSoundEvent.get("minecraft:entity.endermite.death"); - SoundEvent ENTITY_FISH_SWIM = SoundEventImpl.get("minecraft:entity.fish.swim"); + SoundEvent ENTITY_ENDERMITE_HURT = BuiltinSoundEvent.get("minecraft:entity.endermite.hurt"); - SoundEvent ENTITY_FISHING_BOBBER_RETRIEVE = SoundEventImpl.get("minecraft:entity.fishing_bobber.retrieve"); + SoundEvent ENTITY_ENDERMITE_STEP = BuiltinSoundEvent.get("minecraft:entity.endermite.step"); - SoundEvent ENTITY_FISHING_BOBBER_SPLASH = SoundEventImpl.get("minecraft:entity.fishing_bobber.splash"); + SoundEvent ENTITY_ENDER_PEARL_THROW = BuiltinSoundEvent.get("minecraft:entity.ender_pearl.throw"); - SoundEvent ENTITY_FISHING_BOBBER_THROW = SoundEventImpl.get("minecraft:entity.fishing_bobber.throw"); + SoundEvent BLOCK_END_GATEWAY_SPAWN = BuiltinSoundEvent.get("minecraft:block.end_gateway.spawn"); - SoundEvent ITEM_FLINTANDSTEEL_USE = SoundEventImpl.get("minecraft:item.flintandsteel.use"); + SoundEvent BLOCK_END_PORTAL_FRAME_FILL = BuiltinSoundEvent.get("minecraft:block.end_portal_frame.fill"); - SoundEvent BLOCK_FLOWERING_AZALEA_BREAK = SoundEventImpl.get("minecraft:block.flowering_azalea.break"); + SoundEvent BLOCK_END_PORTAL_SPAWN = BuiltinSoundEvent.get("minecraft:block.end_portal.spawn"); - SoundEvent BLOCK_FLOWERING_AZALEA_FALL = SoundEventImpl.get("minecraft:block.flowering_azalea.fall"); + SoundEvent ENTITY_EVOKER_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.evoker.ambient"); - SoundEvent BLOCK_FLOWERING_AZALEA_HIT = SoundEventImpl.get("minecraft:block.flowering_azalea.hit"); + SoundEvent ENTITY_EVOKER_CAST_SPELL = BuiltinSoundEvent.get("minecraft:entity.evoker.cast_spell"); - SoundEvent BLOCK_FLOWERING_AZALEA_PLACE = SoundEventImpl.get("minecraft:block.flowering_azalea.place"); + SoundEvent ENTITY_EVOKER_CELEBRATE = BuiltinSoundEvent.get("minecraft:entity.evoker.celebrate"); - SoundEvent BLOCK_FLOWERING_AZALEA_STEP = SoundEventImpl.get("minecraft:block.flowering_azalea.step"); + SoundEvent ENTITY_EVOKER_DEATH = BuiltinSoundEvent.get("minecraft:entity.evoker.death"); - SoundEvent ENTITY_FOX_AGGRO = SoundEventImpl.get("minecraft:entity.fox.aggro"); + SoundEvent ENTITY_EVOKER_FANGS_ATTACK = BuiltinSoundEvent.get("minecraft:entity.evoker_fangs.attack"); - SoundEvent ENTITY_FOX_AMBIENT = SoundEventImpl.get("minecraft:entity.fox.ambient"); + SoundEvent ENTITY_EVOKER_HURT = BuiltinSoundEvent.get("minecraft:entity.evoker.hurt"); - SoundEvent ENTITY_FOX_BITE = SoundEventImpl.get("minecraft:entity.fox.bite"); + SoundEvent ENTITY_EVOKER_PREPARE_ATTACK = BuiltinSoundEvent.get("minecraft:entity.evoker.prepare_attack"); - SoundEvent ENTITY_FOX_DEATH = SoundEventImpl.get("minecraft:entity.fox.death"); + SoundEvent ENTITY_EVOKER_PREPARE_SUMMON = BuiltinSoundEvent.get("minecraft:entity.evoker.prepare_summon"); - SoundEvent ENTITY_FOX_EAT = SoundEventImpl.get("minecraft:entity.fox.eat"); + SoundEvent ENTITY_EVOKER_PREPARE_WOLOLO = BuiltinSoundEvent.get("minecraft:entity.evoker.prepare_wololo"); - SoundEvent ENTITY_FOX_HURT = SoundEventImpl.get("minecraft:entity.fox.hurt"); + SoundEvent ENTITY_EXPERIENCE_BOTTLE_THROW = BuiltinSoundEvent.get("minecraft:entity.experience_bottle.throw"); - SoundEvent ENTITY_FOX_SCREECH = SoundEventImpl.get("minecraft:entity.fox.screech"); + SoundEvent ENTITY_EXPERIENCE_ORB_PICKUP = BuiltinSoundEvent.get("minecraft:entity.experience_orb.pickup"); - SoundEvent ENTITY_FOX_SLEEP = SoundEventImpl.get("minecraft:entity.fox.sleep"); + SoundEvent BLOCK_FENCE_GATE_CLOSE = BuiltinSoundEvent.get("minecraft:block.fence_gate.close"); - SoundEvent ENTITY_FOX_SNIFF = SoundEventImpl.get("minecraft:entity.fox.sniff"); + SoundEvent BLOCK_FENCE_GATE_OPEN = BuiltinSoundEvent.get("minecraft:block.fence_gate.open"); - SoundEvent ENTITY_FOX_SPIT = SoundEventImpl.get("minecraft:entity.fox.spit"); + SoundEvent ITEM_FIRECHARGE_USE = BuiltinSoundEvent.get("minecraft:item.firecharge.use"); - SoundEvent ENTITY_FOX_TELEPORT = SoundEventImpl.get("minecraft:entity.fox.teleport"); + SoundEvent ENTITY_FIREWORK_ROCKET_BLAST = BuiltinSoundEvent.get("minecraft:entity.firework_rocket.blast"); - SoundEvent BLOCK_SUSPICIOUS_SAND_BREAK = SoundEventImpl.get("minecraft:block.suspicious_sand.break"); + SoundEvent ENTITY_FIREWORK_ROCKET_BLAST_FAR = BuiltinSoundEvent.get("minecraft:entity.firework_rocket.blast_far"); - SoundEvent BLOCK_SUSPICIOUS_SAND_STEP = SoundEventImpl.get("minecraft:block.suspicious_sand.step"); + SoundEvent ENTITY_FIREWORK_ROCKET_LARGE_BLAST = BuiltinSoundEvent.get("minecraft:entity.firework_rocket.large_blast"); - SoundEvent BLOCK_SUSPICIOUS_SAND_PLACE = SoundEventImpl.get("minecraft:block.suspicious_sand.place"); + SoundEvent ENTITY_FIREWORK_ROCKET_LARGE_BLAST_FAR = BuiltinSoundEvent.get("minecraft:entity.firework_rocket.large_blast_far"); - SoundEvent BLOCK_SUSPICIOUS_SAND_HIT = SoundEventImpl.get("minecraft:block.suspicious_sand.hit"); + SoundEvent ENTITY_FIREWORK_ROCKET_LAUNCH = BuiltinSoundEvent.get("minecraft:entity.firework_rocket.launch"); - SoundEvent BLOCK_SUSPICIOUS_SAND_FALL = SoundEventImpl.get("minecraft:block.suspicious_sand.fall"); + SoundEvent ENTITY_FIREWORK_ROCKET_SHOOT = BuiltinSoundEvent.get("minecraft:entity.firework_rocket.shoot"); - SoundEvent BLOCK_SUSPICIOUS_GRAVEL_BREAK = SoundEventImpl.get("minecraft:block.suspicious_gravel.break"); + SoundEvent ENTITY_FIREWORK_ROCKET_TWINKLE = BuiltinSoundEvent.get("minecraft:entity.firework_rocket.twinkle"); - SoundEvent BLOCK_SUSPICIOUS_GRAVEL_STEP = SoundEventImpl.get("minecraft:block.suspicious_gravel.step"); + SoundEvent ENTITY_FIREWORK_ROCKET_TWINKLE_FAR = BuiltinSoundEvent.get("minecraft:entity.firework_rocket.twinkle_far"); - SoundEvent BLOCK_SUSPICIOUS_GRAVEL_PLACE = SoundEventImpl.get("minecraft:block.suspicious_gravel.place"); + SoundEvent BLOCK_FIRE_AMBIENT = BuiltinSoundEvent.get("minecraft:block.fire.ambient"); - SoundEvent BLOCK_SUSPICIOUS_GRAVEL_HIT = SoundEventImpl.get("minecraft:block.suspicious_gravel.hit"); + SoundEvent BLOCK_FIRE_EXTINGUISH = BuiltinSoundEvent.get("minecraft:block.fire.extinguish"); - SoundEvent BLOCK_SUSPICIOUS_GRAVEL_FALL = SoundEventImpl.get("minecraft:block.suspicious_gravel.fall"); + SoundEvent ENTITY_FISH_SWIM = BuiltinSoundEvent.get("minecraft:entity.fish.swim"); - SoundEvent BLOCK_FROGLIGHT_BREAK = SoundEventImpl.get("minecraft:block.froglight.break"); + SoundEvent ENTITY_FISHING_BOBBER_RETRIEVE = BuiltinSoundEvent.get("minecraft:entity.fishing_bobber.retrieve"); - SoundEvent BLOCK_FROGLIGHT_FALL = SoundEventImpl.get("minecraft:block.froglight.fall"); + SoundEvent ENTITY_FISHING_BOBBER_SPLASH = BuiltinSoundEvent.get("minecraft:entity.fishing_bobber.splash"); - SoundEvent BLOCK_FROGLIGHT_HIT = SoundEventImpl.get("minecraft:block.froglight.hit"); + SoundEvent ENTITY_FISHING_BOBBER_THROW = BuiltinSoundEvent.get("minecraft:entity.fishing_bobber.throw"); - SoundEvent BLOCK_FROGLIGHT_PLACE = SoundEventImpl.get("minecraft:block.froglight.place"); + SoundEvent ITEM_FLINTANDSTEEL_USE = BuiltinSoundEvent.get("minecraft:item.flintandsteel.use"); - SoundEvent BLOCK_FROGLIGHT_STEP = SoundEventImpl.get("minecraft:block.froglight.step"); + SoundEvent BLOCK_FLOWERING_AZALEA_BREAK = BuiltinSoundEvent.get("minecraft:block.flowering_azalea.break"); - SoundEvent BLOCK_FROGSPAWN_STEP = SoundEventImpl.get("minecraft:block.frogspawn.step"); + SoundEvent BLOCK_FLOWERING_AZALEA_FALL = BuiltinSoundEvent.get("minecraft:block.flowering_azalea.fall"); - SoundEvent BLOCK_FROGSPAWN_BREAK = SoundEventImpl.get("minecraft:block.frogspawn.break"); + SoundEvent BLOCK_FLOWERING_AZALEA_HIT = BuiltinSoundEvent.get("minecraft:block.flowering_azalea.hit"); - SoundEvent BLOCK_FROGSPAWN_FALL = SoundEventImpl.get("minecraft:block.frogspawn.fall"); + SoundEvent BLOCK_FLOWERING_AZALEA_PLACE = BuiltinSoundEvent.get("minecraft:block.flowering_azalea.place"); - SoundEvent BLOCK_FROGSPAWN_HATCH = SoundEventImpl.get("minecraft:block.frogspawn.hatch"); + SoundEvent BLOCK_FLOWERING_AZALEA_STEP = BuiltinSoundEvent.get("minecraft:block.flowering_azalea.step"); - SoundEvent BLOCK_FROGSPAWN_HIT = SoundEventImpl.get("minecraft:block.frogspawn.hit"); + SoundEvent ENTITY_FOX_AGGRO = BuiltinSoundEvent.get("minecraft:entity.fox.aggro"); - SoundEvent BLOCK_FROGSPAWN_PLACE = SoundEventImpl.get("minecraft:block.frogspawn.place"); + SoundEvent ENTITY_FOX_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.fox.ambient"); - SoundEvent ENTITY_FROG_AMBIENT = SoundEventImpl.get("minecraft:entity.frog.ambient"); + SoundEvent ENTITY_FOX_BITE = BuiltinSoundEvent.get("minecraft:entity.fox.bite"); - SoundEvent ENTITY_FROG_DEATH = SoundEventImpl.get("minecraft:entity.frog.death"); + SoundEvent ENTITY_FOX_DEATH = BuiltinSoundEvent.get("minecraft:entity.fox.death"); - SoundEvent ENTITY_FROG_EAT = SoundEventImpl.get("minecraft:entity.frog.eat"); + SoundEvent ENTITY_FOX_EAT = BuiltinSoundEvent.get("minecraft:entity.fox.eat"); - SoundEvent ENTITY_FROG_HURT = SoundEventImpl.get("minecraft:entity.frog.hurt"); + SoundEvent ENTITY_FOX_HURT = BuiltinSoundEvent.get("minecraft:entity.fox.hurt"); - SoundEvent ENTITY_FROG_LAY_SPAWN = SoundEventImpl.get("minecraft:entity.frog.lay_spawn"); + SoundEvent ENTITY_FOX_SCREECH = BuiltinSoundEvent.get("minecraft:entity.fox.screech"); - SoundEvent ENTITY_FROG_LONG_JUMP = SoundEventImpl.get("minecraft:entity.frog.long_jump"); + SoundEvent ENTITY_FOX_SLEEP = BuiltinSoundEvent.get("minecraft:entity.fox.sleep"); - SoundEvent ENTITY_FROG_STEP = SoundEventImpl.get("minecraft:entity.frog.step"); + SoundEvent ENTITY_FOX_SNIFF = BuiltinSoundEvent.get("minecraft:entity.fox.sniff"); - SoundEvent ENTITY_FROG_TONGUE = SoundEventImpl.get("minecraft:entity.frog.tongue"); + SoundEvent ENTITY_FOX_SPIT = BuiltinSoundEvent.get("minecraft:entity.fox.spit"); - SoundEvent BLOCK_ROOTS_BREAK = SoundEventImpl.get("minecraft:block.roots.break"); + SoundEvent ENTITY_FOX_TELEPORT = BuiltinSoundEvent.get("minecraft:entity.fox.teleport"); - SoundEvent BLOCK_ROOTS_STEP = SoundEventImpl.get("minecraft:block.roots.step"); + SoundEvent BLOCK_SUSPICIOUS_SAND_BREAK = BuiltinSoundEvent.get("minecraft:block.suspicious_sand.break"); - SoundEvent BLOCK_ROOTS_PLACE = SoundEventImpl.get("minecraft:block.roots.place"); + SoundEvent BLOCK_SUSPICIOUS_SAND_STEP = BuiltinSoundEvent.get("minecraft:block.suspicious_sand.step"); - SoundEvent BLOCK_ROOTS_HIT = SoundEventImpl.get("minecraft:block.roots.hit"); + SoundEvent BLOCK_SUSPICIOUS_SAND_PLACE = BuiltinSoundEvent.get("minecraft:block.suspicious_sand.place"); - SoundEvent BLOCK_ROOTS_FALL = SoundEventImpl.get("minecraft:block.roots.fall"); + SoundEvent BLOCK_SUSPICIOUS_SAND_HIT = BuiltinSoundEvent.get("minecraft:block.suspicious_sand.hit"); - SoundEvent BLOCK_FURNACE_FIRE_CRACKLE = SoundEventImpl.get("minecraft:block.furnace.fire_crackle"); + SoundEvent BLOCK_SUSPICIOUS_SAND_FALL = BuiltinSoundEvent.get("minecraft:block.suspicious_sand.fall"); - SoundEvent ENTITY_GENERIC_BIG_FALL = SoundEventImpl.get("minecraft:entity.generic.big_fall"); + SoundEvent BLOCK_SUSPICIOUS_GRAVEL_BREAK = BuiltinSoundEvent.get("minecraft:block.suspicious_gravel.break"); - SoundEvent ENTITY_GENERIC_BURN = SoundEventImpl.get("minecraft:entity.generic.burn"); + SoundEvent BLOCK_SUSPICIOUS_GRAVEL_STEP = BuiltinSoundEvent.get("minecraft:block.suspicious_gravel.step"); - SoundEvent ENTITY_GENERIC_DEATH = SoundEventImpl.get("minecraft:entity.generic.death"); + SoundEvent BLOCK_SUSPICIOUS_GRAVEL_PLACE = BuiltinSoundEvent.get("minecraft:block.suspicious_gravel.place"); - SoundEvent ENTITY_GENERIC_DRINK = SoundEventImpl.get("minecraft:entity.generic.drink"); + SoundEvent BLOCK_SUSPICIOUS_GRAVEL_HIT = BuiltinSoundEvent.get("minecraft:block.suspicious_gravel.hit"); - SoundEvent ENTITY_GENERIC_EAT = SoundEventImpl.get("minecraft:entity.generic.eat"); + SoundEvent BLOCK_SUSPICIOUS_GRAVEL_FALL = BuiltinSoundEvent.get("minecraft:block.suspicious_gravel.fall"); - SoundEvent ENTITY_GENERIC_EXPLODE = SoundEventImpl.get("minecraft:entity.generic.explode"); + SoundEvent BLOCK_FROGLIGHT_BREAK = BuiltinSoundEvent.get("minecraft:block.froglight.break"); - SoundEvent ENTITY_GENERIC_EXTINGUISH_FIRE = SoundEventImpl.get("minecraft:entity.generic.extinguish_fire"); + SoundEvent BLOCK_FROGLIGHT_FALL = BuiltinSoundEvent.get("minecraft:block.froglight.fall"); - SoundEvent ENTITY_GENERIC_HURT = SoundEventImpl.get("minecraft:entity.generic.hurt"); + SoundEvent BLOCK_FROGLIGHT_HIT = BuiltinSoundEvent.get("minecraft:block.froglight.hit"); - SoundEvent ENTITY_GENERIC_SMALL_FALL = SoundEventImpl.get("minecraft:entity.generic.small_fall"); + SoundEvent BLOCK_FROGLIGHT_PLACE = BuiltinSoundEvent.get("minecraft:block.froglight.place"); - SoundEvent ENTITY_GENERIC_SPLASH = SoundEventImpl.get("minecraft:entity.generic.splash"); + SoundEvent BLOCK_FROGLIGHT_STEP = BuiltinSoundEvent.get("minecraft:block.froglight.step"); - SoundEvent ENTITY_GENERIC_SWIM = SoundEventImpl.get("minecraft:entity.generic.swim"); + SoundEvent BLOCK_FROGSPAWN_STEP = BuiltinSoundEvent.get("minecraft:block.frogspawn.step"); - SoundEvent ENTITY_GHAST_AMBIENT = SoundEventImpl.get("minecraft:entity.ghast.ambient"); + SoundEvent BLOCK_FROGSPAWN_BREAK = BuiltinSoundEvent.get("minecraft:block.frogspawn.break"); - SoundEvent ENTITY_GHAST_DEATH = SoundEventImpl.get("minecraft:entity.ghast.death"); + SoundEvent BLOCK_FROGSPAWN_FALL = BuiltinSoundEvent.get("minecraft:block.frogspawn.fall"); - SoundEvent ENTITY_GHAST_HURT = SoundEventImpl.get("minecraft:entity.ghast.hurt"); + SoundEvent BLOCK_FROGSPAWN_HATCH = BuiltinSoundEvent.get("minecraft:block.frogspawn.hatch"); - SoundEvent ENTITY_GHAST_SCREAM = SoundEventImpl.get("minecraft:entity.ghast.scream"); + SoundEvent BLOCK_FROGSPAWN_HIT = BuiltinSoundEvent.get("minecraft:block.frogspawn.hit"); - SoundEvent ENTITY_GHAST_SHOOT = SoundEventImpl.get("minecraft:entity.ghast.shoot"); + SoundEvent BLOCK_FROGSPAWN_PLACE = BuiltinSoundEvent.get("minecraft:block.frogspawn.place"); - SoundEvent ENTITY_GHAST_WARN = SoundEventImpl.get("minecraft:entity.ghast.warn"); + SoundEvent ENTITY_FROG_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.frog.ambient"); - SoundEvent BLOCK_GILDED_BLACKSTONE_BREAK = SoundEventImpl.get("minecraft:block.gilded_blackstone.break"); + SoundEvent ENTITY_FROG_DEATH = BuiltinSoundEvent.get("minecraft:entity.frog.death"); - SoundEvent BLOCK_GILDED_BLACKSTONE_FALL = SoundEventImpl.get("minecraft:block.gilded_blackstone.fall"); + SoundEvent ENTITY_FROG_EAT = BuiltinSoundEvent.get("minecraft:entity.frog.eat"); - SoundEvent BLOCK_GILDED_BLACKSTONE_HIT = SoundEventImpl.get("minecraft:block.gilded_blackstone.hit"); + SoundEvent ENTITY_FROG_HURT = BuiltinSoundEvent.get("minecraft:entity.frog.hurt"); - SoundEvent BLOCK_GILDED_BLACKSTONE_PLACE = SoundEventImpl.get("minecraft:block.gilded_blackstone.place"); + SoundEvent ENTITY_FROG_LAY_SPAWN = BuiltinSoundEvent.get("minecraft:entity.frog.lay_spawn"); - SoundEvent BLOCK_GILDED_BLACKSTONE_STEP = SoundEventImpl.get("minecraft:block.gilded_blackstone.step"); + SoundEvent ENTITY_FROG_LONG_JUMP = BuiltinSoundEvent.get("minecraft:entity.frog.long_jump"); - SoundEvent BLOCK_GLASS_BREAK = SoundEventImpl.get("minecraft:block.glass.break"); + SoundEvent ENTITY_FROG_STEP = BuiltinSoundEvent.get("minecraft:entity.frog.step"); - SoundEvent BLOCK_GLASS_FALL = SoundEventImpl.get("minecraft:block.glass.fall"); + SoundEvent ENTITY_FROG_TONGUE = BuiltinSoundEvent.get("minecraft:entity.frog.tongue"); - SoundEvent BLOCK_GLASS_HIT = SoundEventImpl.get("minecraft:block.glass.hit"); + SoundEvent BLOCK_ROOTS_BREAK = BuiltinSoundEvent.get("minecraft:block.roots.break"); - SoundEvent BLOCK_GLASS_PLACE = SoundEventImpl.get("minecraft:block.glass.place"); + SoundEvent BLOCK_ROOTS_STEP = BuiltinSoundEvent.get("minecraft:block.roots.step"); - SoundEvent BLOCK_GLASS_STEP = SoundEventImpl.get("minecraft:block.glass.step"); + SoundEvent BLOCK_ROOTS_PLACE = BuiltinSoundEvent.get("minecraft:block.roots.place"); - SoundEvent ITEM_GLOW_INK_SAC_USE = SoundEventImpl.get("minecraft:item.glow_ink_sac.use"); + SoundEvent BLOCK_ROOTS_HIT = BuiltinSoundEvent.get("minecraft:block.roots.hit"); - SoundEvent ENTITY_GLOW_ITEM_FRAME_ADD_ITEM = SoundEventImpl.get("minecraft:entity.glow_item_frame.add_item"); + SoundEvent BLOCK_ROOTS_FALL = BuiltinSoundEvent.get("minecraft:block.roots.fall"); - SoundEvent ENTITY_GLOW_ITEM_FRAME_BREAK = SoundEventImpl.get("minecraft:entity.glow_item_frame.break"); + SoundEvent BLOCK_FURNACE_FIRE_CRACKLE = BuiltinSoundEvent.get("minecraft:block.furnace.fire_crackle"); - SoundEvent ENTITY_GLOW_ITEM_FRAME_PLACE = SoundEventImpl.get("minecraft:entity.glow_item_frame.place"); + SoundEvent ENTITY_GENERIC_BIG_FALL = BuiltinSoundEvent.get("minecraft:entity.generic.big_fall"); - SoundEvent ENTITY_GLOW_ITEM_FRAME_REMOVE_ITEM = SoundEventImpl.get("minecraft:entity.glow_item_frame.remove_item"); + SoundEvent ENTITY_GENERIC_BURN = BuiltinSoundEvent.get("minecraft:entity.generic.burn"); - SoundEvent ENTITY_GLOW_ITEM_FRAME_ROTATE_ITEM = SoundEventImpl.get("minecraft:entity.glow_item_frame.rotate_item"); + SoundEvent ENTITY_GENERIC_DEATH = BuiltinSoundEvent.get("minecraft:entity.generic.death"); - SoundEvent ENTITY_GLOW_SQUID_AMBIENT = SoundEventImpl.get("minecraft:entity.glow_squid.ambient"); + SoundEvent ENTITY_GENERIC_DRINK = BuiltinSoundEvent.get("minecraft:entity.generic.drink"); - SoundEvent ENTITY_GLOW_SQUID_DEATH = SoundEventImpl.get("minecraft:entity.glow_squid.death"); + SoundEvent ENTITY_GENERIC_EAT = BuiltinSoundEvent.get("minecraft:entity.generic.eat"); - SoundEvent ENTITY_GLOW_SQUID_HURT = SoundEventImpl.get("minecraft:entity.glow_squid.hurt"); + SoundEvent ENTITY_GENERIC_EXPLODE = BuiltinSoundEvent.get("minecraft:entity.generic.explode"); - SoundEvent ENTITY_GLOW_SQUID_SQUIRT = SoundEventImpl.get("minecraft:entity.glow_squid.squirt"); + SoundEvent ENTITY_GENERIC_EXTINGUISH_FIRE = BuiltinSoundEvent.get("minecraft:entity.generic.extinguish_fire"); - SoundEvent ENTITY_GOAT_AMBIENT = SoundEventImpl.get("minecraft:entity.goat.ambient"); + SoundEvent ENTITY_GENERIC_HURT = BuiltinSoundEvent.get("minecraft:entity.generic.hurt"); - SoundEvent ENTITY_GOAT_DEATH = SoundEventImpl.get("minecraft:entity.goat.death"); + SoundEvent ENTITY_GENERIC_SMALL_FALL = BuiltinSoundEvent.get("minecraft:entity.generic.small_fall"); - SoundEvent ENTITY_GOAT_EAT = SoundEventImpl.get("minecraft:entity.goat.eat"); + SoundEvent ENTITY_GENERIC_SPLASH = BuiltinSoundEvent.get("minecraft:entity.generic.splash"); - SoundEvent ENTITY_GOAT_HURT = SoundEventImpl.get("minecraft:entity.goat.hurt"); + SoundEvent ENTITY_GENERIC_SWIM = BuiltinSoundEvent.get("minecraft:entity.generic.swim"); - SoundEvent ENTITY_GOAT_LONG_JUMP = SoundEventImpl.get("minecraft:entity.goat.long_jump"); + SoundEvent ENTITY_GHAST_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.ghast.ambient"); - SoundEvent ENTITY_GOAT_MILK = SoundEventImpl.get("minecraft:entity.goat.milk"); + SoundEvent ENTITY_GHAST_DEATH = BuiltinSoundEvent.get("minecraft:entity.ghast.death"); - SoundEvent ENTITY_GOAT_PREPARE_RAM = SoundEventImpl.get("minecraft:entity.goat.prepare_ram"); + SoundEvent ENTITY_GHAST_HURT = BuiltinSoundEvent.get("minecraft:entity.ghast.hurt"); - SoundEvent ENTITY_GOAT_RAM_IMPACT = SoundEventImpl.get("minecraft:entity.goat.ram_impact"); + SoundEvent ENTITY_GHAST_SCREAM = BuiltinSoundEvent.get("minecraft:entity.ghast.scream"); - SoundEvent ENTITY_GOAT_HORN_BREAK = SoundEventImpl.get("minecraft:entity.goat.horn_break"); + SoundEvent ENTITY_GHAST_SHOOT = BuiltinSoundEvent.get("minecraft:entity.ghast.shoot"); - SoundEvent ITEM_GOAT_HORN_PLAY = SoundEventImpl.get("minecraft:item.goat_horn.play"); + SoundEvent ENTITY_GHAST_WARN = BuiltinSoundEvent.get("minecraft:entity.ghast.warn"); - SoundEvent ENTITY_GOAT_SCREAMING_AMBIENT = SoundEventImpl.get("minecraft:entity.goat.screaming.ambient"); + SoundEvent BLOCK_GILDED_BLACKSTONE_BREAK = BuiltinSoundEvent.get("minecraft:block.gilded_blackstone.break"); - SoundEvent ENTITY_GOAT_SCREAMING_DEATH = SoundEventImpl.get("minecraft:entity.goat.screaming.death"); + SoundEvent BLOCK_GILDED_BLACKSTONE_FALL = BuiltinSoundEvent.get("minecraft:block.gilded_blackstone.fall"); - SoundEvent ENTITY_GOAT_SCREAMING_EAT = SoundEventImpl.get("minecraft:entity.goat.screaming.eat"); + SoundEvent BLOCK_GILDED_BLACKSTONE_HIT = BuiltinSoundEvent.get("minecraft:block.gilded_blackstone.hit"); - SoundEvent ENTITY_GOAT_SCREAMING_HURT = SoundEventImpl.get("minecraft:entity.goat.screaming.hurt"); + SoundEvent BLOCK_GILDED_BLACKSTONE_PLACE = BuiltinSoundEvent.get("minecraft:block.gilded_blackstone.place"); - SoundEvent ENTITY_GOAT_SCREAMING_LONG_JUMP = SoundEventImpl.get("minecraft:entity.goat.screaming.long_jump"); + SoundEvent BLOCK_GILDED_BLACKSTONE_STEP = BuiltinSoundEvent.get("minecraft:block.gilded_blackstone.step"); - SoundEvent ENTITY_GOAT_SCREAMING_MILK = SoundEventImpl.get("minecraft:entity.goat.screaming.milk"); + SoundEvent BLOCK_GLASS_BREAK = BuiltinSoundEvent.get("minecraft:block.glass.break"); - SoundEvent ENTITY_GOAT_SCREAMING_PREPARE_RAM = SoundEventImpl.get("minecraft:entity.goat.screaming.prepare_ram"); + SoundEvent BLOCK_GLASS_FALL = BuiltinSoundEvent.get("minecraft:block.glass.fall"); - SoundEvent ENTITY_GOAT_SCREAMING_RAM_IMPACT = SoundEventImpl.get("minecraft:entity.goat.screaming.ram_impact"); + SoundEvent BLOCK_GLASS_HIT = BuiltinSoundEvent.get("minecraft:block.glass.hit"); - SoundEvent ENTITY_GOAT_SCREAMING_HORN_BREAK = SoundEventImpl.get("minecraft:entity.goat.screaming.horn_break"); + SoundEvent BLOCK_GLASS_PLACE = BuiltinSoundEvent.get("minecraft:block.glass.place"); - SoundEvent ENTITY_GOAT_STEP = SoundEventImpl.get("minecraft:entity.goat.step"); + SoundEvent BLOCK_GLASS_STEP = BuiltinSoundEvent.get("minecraft:block.glass.step"); - SoundEvent BLOCK_GRASS_BREAK = SoundEventImpl.get("minecraft:block.grass.break"); + SoundEvent ITEM_GLOW_INK_SAC_USE = BuiltinSoundEvent.get("minecraft:item.glow_ink_sac.use"); - SoundEvent BLOCK_GRASS_FALL = SoundEventImpl.get("minecraft:block.grass.fall"); + SoundEvent ENTITY_GLOW_ITEM_FRAME_ADD_ITEM = BuiltinSoundEvent.get("minecraft:entity.glow_item_frame.add_item"); - SoundEvent BLOCK_GRASS_HIT = SoundEventImpl.get("minecraft:block.grass.hit"); + SoundEvent ENTITY_GLOW_ITEM_FRAME_BREAK = BuiltinSoundEvent.get("minecraft:entity.glow_item_frame.break"); - SoundEvent BLOCK_GRASS_PLACE = SoundEventImpl.get("minecraft:block.grass.place"); + SoundEvent ENTITY_GLOW_ITEM_FRAME_PLACE = BuiltinSoundEvent.get("minecraft:entity.glow_item_frame.place"); - SoundEvent BLOCK_GRASS_STEP = SoundEventImpl.get("minecraft:block.grass.step"); + SoundEvent ENTITY_GLOW_ITEM_FRAME_REMOVE_ITEM = BuiltinSoundEvent.get("minecraft:entity.glow_item_frame.remove_item"); - SoundEvent BLOCK_GRAVEL_BREAK = SoundEventImpl.get("minecraft:block.gravel.break"); + SoundEvent ENTITY_GLOW_ITEM_FRAME_ROTATE_ITEM = BuiltinSoundEvent.get("minecraft:entity.glow_item_frame.rotate_item"); - SoundEvent BLOCK_GRAVEL_FALL = SoundEventImpl.get("minecraft:block.gravel.fall"); + SoundEvent ENTITY_GLOW_SQUID_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.glow_squid.ambient"); - SoundEvent BLOCK_GRAVEL_HIT = SoundEventImpl.get("minecraft:block.gravel.hit"); + SoundEvent ENTITY_GLOW_SQUID_DEATH = BuiltinSoundEvent.get("minecraft:entity.glow_squid.death"); - SoundEvent BLOCK_GRAVEL_PLACE = SoundEventImpl.get("minecraft:block.gravel.place"); + SoundEvent ENTITY_GLOW_SQUID_HURT = BuiltinSoundEvent.get("minecraft:entity.glow_squid.hurt"); - SoundEvent BLOCK_GRAVEL_STEP = SoundEventImpl.get("minecraft:block.gravel.step"); + SoundEvent ENTITY_GLOW_SQUID_SQUIRT = BuiltinSoundEvent.get("minecraft:entity.glow_squid.squirt"); - SoundEvent BLOCK_GRINDSTONE_USE = SoundEventImpl.get("minecraft:block.grindstone.use"); + SoundEvent ENTITY_GOAT_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.goat.ambient"); - SoundEvent BLOCK_GROWING_PLANT_CROP = SoundEventImpl.get("minecraft:block.growing_plant.crop"); + SoundEvent ENTITY_GOAT_DEATH = BuiltinSoundEvent.get("minecraft:entity.goat.death"); - SoundEvent ENTITY_GUARDIAN_AMBIENT = SoundEventImpl.get("minecraft:entity.guardian.ambient"); + SoundEvent ENTITY_GOAT_EAT = BuiltinSoundEvent.get("minecraft:entity.goat.eat"); - SoundEvent ENTITY_GUARDIAN_AMBIENT_LAND = SoundEventImpl.get("minecraft:entity.guardian.ambient_land"); + SoundEvent ENTITY_GOAT_HURT = BuiltinSoundEvent.get("minecraft:entity.goat.hurt"); - SoundEvent ENTITY_GUARDIAN_ATTACK = SoundEventImpl.get("minecraft:entity.guardian.attack"); + SoundEvent ENTITY_GOAT_LONG_JUMP = BuiltinSoundEvent.get("minecraft:entity.goat.long_jump"); - SoundEvent ENTITY_GUARDIAN_DEATH = SoundEventImpl.get("minecraft:entity.guardian.death"); + SoundEvent ENTITY_GOAT_MILK = BuiltinSoundEvent.get("minecraft:entity.goat.milk"); - SoundEvent ENTITY_GUARDIAN_DEATH_LAND = SoundEventImpl.get("minecraft:entity.guardian.death_land"); + SoundEvent ENTITY_GOAT_PREPARE_RAM = BuiltinSoundEvent.get("minecraft:entity.goat.prepare_ram"); - SoundEvent ENTITY_GUARDIAN_FLOP = SoundEventImpl.get("minecraft:entity.guardian.flop"); + SoundEvent ENTITY_GOAT_RAM_IMPACT = BuiltinSoundEvent.get("minecraft:entity.goat.ram_impact"); - SoundEvent ENTITY_GUARDIAN_HURT = SoundEventImpl.get("minecraft:entity.guardian.hurt"); + SoundEvent ENTITY_GOAT_HORN_BREAK = BuiltinSoundEvent.get("minecraft:entity.goat.horn_break"); - SoundEvent ENTITY_GUARDIAN_HURT_LAND = SoundEventImpl.get("minecraft:entity.guardian.hurt_land"); + SoundEvent ITEM_GOAT_HORN_PLAY = BuiltinSoundEvent.get("minecraft:item.goat_horn.play"); - SoundEvent BLOCK_HANGING_ROOTS_BREAK = SoundEventImpl.get("minecraft:block.hanging_roots.break"); + SoundEvent ENTITY_GOAT_SCREAMING_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.goat.screaming.ambient"); - SoundEvent BLOCK_HANGING_ROOTS_FALL = SoundEventImpl.get("minecraft:block.hanging_roots.fall"); + SoundEvent ENTITY_GOAT_SCREAMING_DEATH = BuiltinSoundEvent.get("minecraft:entity.goat.screaming.death"); - SoundEvent BLOCK_HANGING_ROOTS_HIT = SoundEventImpl.get("minecraft:block.hanging_roots.hit"); + SoundEvent ENTITY_GOAT_SCREAMING_EAT = BuiltinSoundEvent.get("minecraft:entity.goat.screaming.eat"); - SoundEvent BLOCK_HANGING_ROOTS_PLACE = SoundEventImpl.get("minecraft:block.hanging_roots.place"); + SoundEvent ENTITY_GOAT_SCREAMING_HURT = BuiltinSoundEvent.get("minecraft:entity.goat.screaming.hurt"); - SoundEvent BLOCK_HANGING_ROOTS_STEP = SoundEventImpl.get("minecraft:block.hanging_roots.step"); + SoundEvent ENTITY_GOAT_SCREAMING_LONG_JUMP = BuiltinSoundEvent.get("minecraft:entity.goat.screaming.long_jump"); - SoundEvent BLOCK_HANGING_SIGN_STEP = SoundEventImpl.get("minecraft:block.hanging_sign.step"); + SoundEvent ENTITY_GOAT_SCREAMING_MILK = BuiltinSoundEvent.get("minecraft:entity.goat.screaming.milk"); - SoundEvent BLOCK_HANGING_SIGN_BREAK = SoundEventImpl.get("minecraft:block.hanging_sign.break"); + SoundEvent ENTITY_GOAT_SCREAMING_PREPARE_RAM = BuiltinSoundEvent.get("minecraft:entity.goat.screaming.prepare_ram"); - SoundEvent BLOCK_HANGING_SIGN_FALL = SoundEventImpl.get("minecraft:block.hanging_sign.fall"); + SoundEvent ENTITY_GOAT_SCREAMING_RAM_IMPACT = BuiltinSoundEvent.get("minecraft:entity.goat.screaming.ram_impact"); - SoundEvent BLOCK_HANGING_SIGN_HIT = SoundEventImpl.get("minecraft:block.hanging_sign.hit"); + SoundEvent ENTITY_GOAT_SCREAMING_HORN_BREAK = BuiltinSoundEvent.get("minecraft:entity.goat.screaming.horn_break"); - SoundEvent BLOCK_HANGING_SIGN_PLACE = SoundEventImpl.get("minecraft:block.hanging_sign.place"); + SoundEvent ENTITY_GOAT_STEP = BuiltinSoundEvent.get("minecraft:entity.goat.step"); - SoundEvent BLOCK_NETHER_WOOD_HANGING_SIGN_STEP = SoundEventImpl.get("minecraft:block.nether_wood_hanging_sign.step"); + SoundEvent BLOCK_GRASS_BREAK = BuiltinSoundEvent.get("minecraft:block.grass.break"); - SoundEvent BLOCK_NETHER_WOOD_HANGING_SIGN_BREAK = SoundEventImpl.get("minecraft:block.nether_wood_hanging_sign.break"); + SoundEvent BLOCK_GRASS_FALL = BuiltinSoundEvent.get("minecraft:block.grass.fall"); - SoundEvent BLOCK_NETHER_WOOD_HANGING_SIGN_FALL = SoundEventImpl.get("minecraft:block.nether_wood_hanging_sign.fall"); + SoundEvent BLOCK_GRASS_HIT = BuiltinSoundEvent.get("minecraft:block.grass.hit"); - SoundEvent BLOCK_NETHER_WOOD_HANGING_SIGN_HIT = SoundEventImpl.get("minecraft:block.nether_wood_hanging_sign.hit"); + SoundEvent BLOCK_GRASS_PLACE = BuiltinSoundEvent.get("minecraft:block.grass.place"); - SoundEvent BLOCK_NETHER_WOOD_HANGING_SIGN_PLACE = SoundEventImpl.get("minecraft:block.nether_wood_hanging_sign.place"); + SoundEvent BLOCK_GRASS_STEP = BuiltinSoundEvent.get("minecraft:block.grass.step"); - SoundEvent BLOCK_BAMBOO_WOOD_HANGING_SIGN_STEP = SoundEventImpl.get("minecraft:block.bamboo_wood_hanging_sign.step"); + SoundEvent BLOCK_GRAVEL_BREAK = BuiltinSoundEvent.get("minecraft:block.gravel.break"); - SoundEvent BLOCK_BAMBOO_WOOD_HANGING_SIGN_BREAK = SoundEventImpl.get("minecraft:block.bamboo_wood_hanging_sign.break"); + SoundEvent BLOCK_GRAVEL_FALL = BuiltinSoundEvent.get("minecraft:block.gravel.fall"); - SoundEvent BLOCK_BAMBOO_WOOD_HANGING_SIGN_FALL = SoundEventImpl.get("minecraft:block.bamboo_wood_hanging_sign.fall"); + SoundEvent BLOCK_GRAVEL_HIT = BuiltinSoundEvent.get("minecraft:block.gravel.hit"); - SoundEvent BLOCK_BAMBOO_WOOD_HANGING_SIGN_HIT = SoundEventImpl.get("minecraft:block.bamboo_wood_hanging_sign.hit"); + SoundEvent BLOCK_GRAVEL_PLACE = BuiltinSoundEvent.get("minecraft:block.gravel.place"); - SoundEvent BLOCK_BAMBOO_WOOD_HANGING_SIGN_PLACE = SoundEventImpl.get("minecraft:block.bamboo_wood_hanging_sign.place"); + SoundEvent BLOCK_GRAVEL_STEP = BuiltinSoundEvent.get("minecraft:block.gravel.step"); - SoundEvent BLOCK_TRIAL_SPAWNER_BREAK = SoundEventImpl.get("minecraft:block.trial_spawner.break"); + SoundEvent BLOCK_GRINDSTONE_USE = BuiltinSoundEvent.get("minecraft:block.grindstone.use"); - SoundEvent BLOCK_TRIAL_SPAWNER_STEP = SoundEventImpl.get("minecraft:block.trial_spawner.step"); + SoundEvent BLOCK_GROWING_PLANT_CROP = BuiltinSoundEvent.get("minecraft:block.growing_plant.crop"); - SoundEvent BLOCK_TRIAL_SPAWNER_PLACE = SoundEventImpl.get("minecraft:block.trial_spawner.place"); + SoundEvent ENTITY_GUARDIAN_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.guardian.ambient"); - SoundEvent BLOCK_TRIAL_SPAWNER_HIT = SoundEventImpl.get("minecraft:block.trial_spawner.hit"); + SoundEvent ENTITY_GUARDIAN_AMBIENT_LAND = BuiltinSoundEvent.get("minecraft:entity.guardian.ambient_land"); - SoundEvent BLOCK_TRIAL_SPAWNER_FALL = SoundEventImpl.get("minecraft:block.trial_spawner.fall"); + SoundEvent ENTITY_GUARDIAN_ATTACK = BuiltinSoundEvent.get("minecraft:entity.guardian.attack"); - SoundEvent BLOCK_TRIAL_SPAWNER_SPAWN_MOB = SoundEventImpl.get("minecraft:block.trial_spawner.spawn_mob"); + SoundEvent ENTITY_GUARDIAN_DEATH = BuiltinSoundEvent.get("minecraft:entity.guardian.death"); - SoundEvent BLOCK_TRIAL_SPAWNER_DETECT_PLAYER = SoundEventImpl.get("minecraft:block.trial_spawner.detect_player"); + SoundEvent ENTITY_GUARDIAN_DEATH_LAND = BuiltinSoundEvent.get("minecraft:entity.guardian.death_land"); - SoundEvent BLOCK_TRIAL_SPAWNER_AMBIENT = SoundEventImpl.get("minecraft:block.trial_spawner.ambient"); + SoundEvent ENTITY_GUARDIAN_FLOP = BuiltinSoundEvent.get("minecraft:entity.guardian.flop"); - SoundEvent BLOCK_TRIAL_SPAWNER_OPEN_SHUTTER = SoundEventImpl.get("minecraft:block.trial_spawner.open_shutter"); + SoundEvent ENTITY_GUARDIAN_HURT = BuiltinSoundEvent.get("minecraft:entity.guardian.hurt"); - SoundEvent BLOCK_TRIAL_SPAWNER_CLOSE_SHUTTER = SoundEventImpl.get("minecraft:block.trial_spawner.close_shutter"); + SoundEvent ENTITY_GUARDIAN_HURT_LAND = BuiltinSoundEvent.get("minecraft:entity.guardian.hurt_land"); - SoundEvent BLOCK_TRIAL_SPAWNER_EJECT_ITEM = SoundEventImpl.get("minecraft:block.trial_spawner.eject_item"); + SoundEvent BLOCK_HANGING_ROOTS_BREAK = BuiltinSoundEvent.get("minecraft:block.hanging_roots.break"); - SoundEvent ITEM_HOE_TILL = SoundEventImpl.get("minecraft:item.hoe.till"); + SoundEvent BLOCK_HANGING_ROOTS_FALL = BuiltinSoundEvent.get("minecraft:block.hanging_roots.fall"); - SoundEvent ENTITY_HOGLIN_AMBIENT = SoundEventImpl.get("minecraft:entity.hoglin.ambient"); + SoundEvent BLOCK_HANGING_ROOTS_HIT = BuiltinSoundEvent.get("minecraft:block.hanging_roots.hit"); - SoundEvent ENTITY_HOGLIN_ANGRY = SoundEventImpl.get("minecraft:entity.hoglin.angry"); + SoundEvent BLOCK_HANGING_ROOTS_PLACE = BuiltinSoundEvent.get("minecraft:block.hanging_roots.place"); - SoundEvent ENTITY_HOGLIN_ATTACK = SoundEventImpl.get("minecraft:entity.hoglin.attack"); + SoundEvent BLOCK_HANGING_ROOTS_STEP = BuiltinSoundEvent.get("minecraft:block.hanging_roots.step"); - SoundEvent ENTITY_HOGLIN_CONVERTED_TO_ZOMBIFIED = SoundEventImpl.get("minecraft:entity.hoglin.converted_to_zombified"); + SoundEvent BLOCK_HANGING_SIGN_STEP = BuiltinSoundEvent.get("minecraft:block.hanging_sign.step"); - SoundEvent ENTITY_HOGLIN_DEATH = SoundEventImpl.get("minecraft:entity.hoglin.death"); + SoundEvent BLOCK_HANGING_SIGN_BREAK = BuiltinSoundEvent.get("minecraft:block.hanging_sign.break"); - SoundEvent ENTITY_HOGLIN_HURT = SoundEventImpl.get("minecraft:entity.hoglin.hurt"); + SoundEvent BLOCK_HANGING_SIGN_FALL = BuiltinSoundEvent.get("minecraft:block.hanging_sign.fall"); - SoundEvent ENTITY_HOGLIN_RETREAT = SoundEventImpl.get("minecraft:entity.hoglin.retreat"); + SoundEvent BLOCK_HANGING_SIGN_HIT = BuiltinSoundEvent.get("minecraft:block.hanging_sign.hit"); - SoundEvent ENTITY_HOGLIN_STEP = SoundEventImpl.get("minecraft:entity.hoglin.step"); + SoundEvent BLOCK_HANGING_SIGN_PLACE = BuiltinSoundEvent.get("minecraft:block.hanging_sign.place"); - SoundEvent BLOCK_HONEY_BLOCK_BREAK = SoundEventImpl.get("minecraft:block.honey_block.break"); + SoundEvent BLOCK_HEAVY_CORE_BREAK = BuiltinSoundEvent.get("minecraft:block.heavy_core.break"); - SoundEvent BLOCK_HONEY_BLOCK_FALL = SoundEventImpl.get("minecraft:block.honey_block.fall"); + SoundEvent BLOCK_HEAVY_CORE_FALL = BuiltinSoundEvent.get("minecraft:block.heavy_core.fall"); - SoundEvent BLOCK_HONEY_BLOCK_HIT = SoundEventImpl.get("minecraft:block.honey_block.hit"); + SoundEvent BLOCK_HEAVY_CORE_HIT = BuiltinSoundEvent.get("minecraft:block.heavy_core.hit"); - SoundEvent BLOCK_HONEY_BLOCK_PLACE = SoundEventImpl.get("minecraft:block.honey_block.place"); + SoundEvent BLOCK_HEAVY_CORE_PLACE = BuiltinSoundEvent.get("minecraft:block.heavy_core.place"); - SoundEvent BLOCK_HONEY_BLOCK_SLIDE = SoundEventImpl.get("minecraft:block.honey_block.slide"); + SoundEvent BLOCK_HEAVY_CORE_STEP = BuiltinSoundEvent.get("minecraft:block.heavy_core.step"); - SoundEvent BLOCK_HONEY_BLOCK_STEP = SoundEventImpl.get("minecraft:block.honey_block.step"); + SoundEvent BLOCK_NETHER_WOOD_HANGING_SIGN_STEP = BuiltinSoundEvent.get("minecraft:block.nether_wood_hanging_sign.step"); - SoundEvent ITEM_HONEYCOMB_WAX_ON = SoundEventImpl.get("minecraft:item.honeycomb.wax_on"); + SoundEvent BLOCK_NETHER_WOOD_HANGING_SIGN_BREAK = BuiltinSoundEvent.get("minecraft:block.nether_wood_hanging_sign.break"); - SoundEvent ITEM_HONEY_BOTTLE_DRINK = SoundEventImpl.get("minecraft:item.honey_bottle.drink"); + SoundEvent BLOCK_NETHER_WOOD_HANGING_SIGN_FALL = BuiltinSoundEvent.get("minecraft:block.nether_wood_hanging_sign.fall"); - SoundEvent ITEM_GOAT_HORN_SOUND_0 = SoundEventImpl.get("minecraft:item.goat_horn.sound.0"); + SoundEvent BLOCK_NETHER_WOOD_HANGING_SIGN_HIT = BuiltinSoundEvent.get("minecraft:block.nether_wood_hanging_sign.hit"); - SoundEvent ITEM_GOAT_HORN_SOUND_1 = SoundEventImpl.get("minecraft:item.goat_horn.sound.1"); + SoundEvent BLOCK_NETHER_WOOD_HANGING_SIGN_PLACE = BuiltinSoundEvent.get("minecraft:block.nether_wood_hanging_sign.place"); - SoundEvent ITEM_GOAT_HORN_SOUND_2 = SoundEventImpl.get("minecraft:item.goat_horn.sound.2"); + SoundEvent BLOCK_BAMBOO_WOOD_HANGING_SIGN_STEP = BuiltinSoundEvent.get("minecraft:block.bamboo_wood_hanging_sign.step"); - SoundEvent ITEM_GOAT_HORN_SOUND_3 = SoundEventImpl.get("minecraft:item.goat_horn.sound.3"); + SoundEvent BLOCK_BAMBOO_WOOD_HANGING_SIGN_BREAK = BuiltinSoundEvent.get("minecraft:block.bamboo_wood_hanging_sign.break"); - SoundEvent ITEM_GOAT_HORN_SOUND_4 = SoundEventImpl.get("minecraft:item.goat_horn.sound.4"); + SoundEvent BLOCK_BAMBOO_WOOD_HANGING_SIGN_FALL = BuiltinSoundEvent.get("minecraft:block.bamboo_wood_hanging_sign.fall"); - SoundEvent ITEM_GOAT_HORN_SOUND_5 = SoundEventImpl.get("minecraft:item.goat_horn.sound.5"); + SoundEvent BLOCK_BAMBOO_WOOD_HANGING_SIGN_HIT = BuiltinSoundEvent.get("minecraft:block.bamboo_wood_hanging_sign.hit"); - SoundEvent ITEM_GOAT_HORN_SOUND_6 = SoundEventImpl.get("minecraft:item.goat_horn.sound.6"); + SoundEvent BLOCK_BAMBOO_WOOD_HANGING_SIGN_PLACE = BuiltinSoundEvent.get("minecraft:block.bamboo_wood_hanging_sign.place"); - SoundEvent ITEM_GOAT_HORN_SOUND_7 = SoundEventImpl.get("minecraft:item.goat_horn.sound.7"); + SoundEvent BLOCK_TRIAL_SPAWNER_BREAK = BuiltinSoundEvent.get("minecraft:block.trial_spawner.break"); - SoundEvent ENTITY_HORSE_AMBIENT = SoundEventImpl.get("minecraft:entity.horse.ambient"); + SoundEvent BLOCK_TRIAL_SPAWNER_STEP = BuiltinSoundEvent.get("minecraft:block.trial_spawner.step"); - SoundEvent ENTITY_HORSE_ANGRY = SoundEventImpl.get("minecraft:entity.horse.angry"); + SoundEvent BLOCK_TRIAL_SPAWNER_PLACE = BuiltinSoundEvent.get("minecraft:block.trial_spawner.place"); - SoundEvent ENTITY_HORSE_ARMOR = SoundEventImpl.get("minecraft:entity.horse.armor"); + SoundEvent BLOCK_TRIAL_SPAWNER_HIT = BuiltinSoundEvent.get("minecraft:block.trial_spawner.hit"); - SoundEvent ENTITY_HORSE_BREATHE = SoundEventImpl.get("minecraft:entity.horse.breathe"); + SoundEvent BLOCK_TRIAL_SPAWNER_FALL = BuiltinSoundEvent.get("minecraft:block.trial_spawner.fall"); - SoundEvent ENTITY_HORSE_DEATH = SoundEventImpl.get("minecraft:entity.horse.death"); + SoundEvent BLOCK_TRIAL_SPAWNER_SPAWN_MOB = BuiltinSoundEvent.get("minecraft:block.trial_spawner.spawn_mob"); - SoundEvent ENTITY_HORSE_EAT = SoundEventImpl.get("minecraft:entity.horse.eat"); + SoundEvent BLOCK_TRIAL_SPAWNER_ABOUT_TO_SPAWN_ITEM = BuiltinSoundEvent.get("minecraft:block.trial_spawner.about_to_spawn_item"); - SoundEvent ENTITY_HORSE_GALLOP = SoundEventImpl.get("minecraft:entity.horse.gallop"); + SoundEvent BLOCK_TRIAL_SPAWNER_SPAWN_ITEM = BuiltinSoundEvent.get("minecraft:block.trial_spawner.spawn_item"); - SoundEvent ENTITY_HORSE_HURT = SoundEventImpl.get("minecraft:entity.horse.hurt"); + SoundEvent BLOCK_TRIAL_SPAWNER_SPAWN_ITEM_BEGIN = BuiltinSoundEvent.get("minecraft:block.trial_spawner.spawn_item_begin"); - SoundEvent ENTITY_HORSE_JUMP = SoundEventImpl.get("minecraft:entity.horse.jump"); + SoundEvent BLOCK_TRIAL_SPAWNER_DETECT_PLAYER = BuiltinSoundEvent.get("minecraft:block.trial_spawner.detect_player"); - SoundEvent ENTITY_HORSE_LAND = SoundEventImpl.get("minecraft:entity.horse.land"); + SoundEvent BLOCK_TRIAL_SPAWNER_OMINOUS_ACTIVATE = BuiltinSoundEvent.get("minecraft:block.trial_spawner.ominous_activate"); - SoundEvent ENTITY_HORSE_SADDLE = SoundEventImpl.get("minecraft:entity.horse.saddle"); + SoundEvent BLOCK_TRIAL_SPAWNER_AMBIENT = BuiltinSoundEvent.get("minecraft:block.trial_spawner.ambient"); - SoundEvent ENTITY_HORSE_STEP = SoundEventImpl.get("minecraft:entity.horse.step"); + SoundEvent BLOCK_TRIAL_SPAWNER_AMBIENT_OMINOUS = BuiltinSoundEvent.get("minecraft:block.trial_spawner.ambient_ominous"); - SoundEvent ENTITY_HORSE_STEP_WOOD = SoundEventImpl.get("minecraft:entity.horse.step_wood"); + SoundEvent BLOCK_TRIAL_SPAWNER_OPEN_SHUTTER = BuiltinSoundEvent.get("minecraft:block.trial_spawner.open_shutter"); - SoundEvent ENTITY_HOSTILE_BIG_FALL = SoundEventImpl.get("minecraft:entity.hostile.big_fall"); + SoundEvent BLOCK_TRIAL_SPAWNER_CLOSE_SHUTTER = BuiltinSoundEvent.get("minecraft:block.trial_spawner.close_shutter"); - SoundEvent ENTITY_HOSTILE_DEATH = SoundEventImpl.get("minecraft:entity.hostile.death"); + SoundEvent BLOCK_TRIAL_SPAWNER_EJECT_ITEM = BuiltinSoundEvent.get("minecraft:block.trial_spawner.eject_item"); - SoundEvent ENTITY_HOSTILE_HURT = SoundEventImpl.get("minecraft:entity.hostile.hurt"); + SoundEvent ITEM_HOE_TILL = BuiltinSoundEvent.get("minecraft:item.hoe.till"); - SoundEvent ENTITY_HOSTILE_SMALL_FALL = SoundEventImpl.get("minecraft:entity.hostile.small_fall"); + SoundEvent ENTITY_HOGLIN_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.hoglin.ambient"); - SoundEvent ENTITY_HOSTILE_SPLASH = SoundEventImpl.get("minecraft:entity.hostile.splash"); + SoundEvent ENTITY_HOGLIN_ANGRY = BuiltinSoundEvent.get("minecraft:entity.hoglin.angry"); - SoundEvent ENTITY_HOSTILE_SWIM = SoundEventImpl.get("minecraft:entity.hostile.swim"); + SoundEvent ENTITY_HOGLIN_ATTACK = BuiltinSoundEvent.get("minecraft:entity.hoglin.attack"); - SoundEvent ENTITY_HUSK_AMBIENT = SoundEventImpl.get("minecraft:entity.husk.ambient"); + SoundEvent ENTITY_HOGLIN_CONVERTED_TO_ZOMBIFIED = BuiltinSoundEvent.get("minecraft:entity.hoglin.converted_to_zombified"); - SoundEvent ENTITY_HUSK_CONVERTED_TO_ZOMBIE = SoundEventImpl.get("minecraft:entity.husk.converted_to_zombie"); + SoundEvent ENTITY_HOGLIN_DEATH = BuiltinSoundEvent.get("minecraft:entity.hoglin.death"); - SoundEvent ENTITY_HUSK_DEATH = SoundEventImpl.get("minecraft:entity.husk.death"); + SoundEvent ENTITY_HOGLIN_HURT = BuiltinSoundEvent.get("minecraft:entity.hoglin.hurt"); - SoundEvent ENTITY_HUSK_HURT = SoundEventImpl.get("minecraft:entity.husk.hurt"); + SoundEvent ENTITY_HOGLIN_RETREAT = BuiltinSoundEvent.get("minecraft:entity.hoglin.retreat"); - SoundEvent ENTITY_HUSK_STEP = SoundEventImpl.get("minecraft:entity.husk.step"); + SoundEvent ENTITY_HOGLIN_STEP = BuiltinSoundEvent.get("minecraft:entity.hoglin.step"); - SoundEvent ENTITY_ILLUSIONER_AMBIENT = SoundEventImpl.get("minecraft:entity.illusioner.ambient"); + SoundEvent BLOCK_HONEY_BLOCK_BREAK = BuiltinSoundEvent.get("minecraft:block.honey_block.break"); - SoundEvent ENTITY_ILLUSIONER_CAST_SPELL = SoundEventImpl.get("minecraft:entity.illusioner.cast_spell"); + SoundEvent BLOCK_HONEY_BLOCK_FALL = BuiltinSoundEvent.get("minecraft:block.honey_block.fall"); - SoundEvent ENTITY_ILLUSIONER_DEATH = SoundEventImpl.get("minecraft:entity.illusioner.death"); + SoundEvent BLOCK_HONEY_BLOCK_HIT = BuiltinSoundEvent.get("minecraft:block.honey_block.hit"); - SoundEvent ENTITY_ILLUSIONER_HURT = SoundEventImpl.get("minecraft:entity.illusioner.hurt"); + SoundEvent BLOCK_HONEY_BLOCK_PLACE = BuiltinSoundEvent.get("minecraft:block.honey_block.place"); - SoundEvent ENTITY_ILLUSIONER_MIRROR_MOVE = SoundEventImpl.get("minecraft:entity.illusioner.mirror_move"); + SoundEvent BLOCK_HONEY_BLOCK_SLIDE = BuiltinSoundEvent.get("minecraft:block.honey_block.slide"); - SoundEvent ENTITY_ILLUSIONER_PREPARE_BLINDNESS = SoundEventImpl.get("minecraft:entity.illusioner.prepare_blindness"); + SoundEvent BLOCK_HONEY_BLOCK_STEP = BuiltinSoundEvent.get("minecraft:block.honey_block.step"); - SoundEvent ENTITY_ILLUSIONER_PREPARE_MIRROR = SoundEventImpl.get("minecraft:entity.illusioner.prepare_mirror"); + SoundEvent ITEM_HONEYCOMB_WAX_ON = BuiltinSoundEvent.get("minecraft:item.honeycomb.wax_on"); - SoundEvent ITEM_INK_SAC_USE = SoundEventImpl.get("minecraft:item.ink_sac.use"); + SoundEvent ITEM_HONEY_BOTTLE_DRINK = BuiltinSoundEvent.get("minecraft:item.honey_bottle.drink"); - SoundEvent BLOCK_IRON_DOOR_CLOSE = SoundEventImpl.get("minecraft:block.iron_door.close"); + SoundEvent ITEM_GOAT_HORN_SOUND_0 = BuiltinSoundEvent.get("minecraft:item.goat_horn.sound.0"); - SoundEvent BLOCK_IRON_DOOR_OPEN = SoundEventImpl.get("minecraft:block.iron_door.open"); + SoundEvent ITEM_GOAT_HORN_SOUND_1 = BuiltinSoundEvent.get("minecraft:item.goat_horn.sound.1"); - SoundEvent ENTITY_IRON_GOLEM_ATTACK = SoundEventImpl.get("minecraft:entity.iron_golem.attack"); + SoundEvent ITEM_GOAT_HORN_SOUND_2 = BuiltinSoundEvent.get("minecraft:item.goat_horn.sound.2"); - SoundEvent ENTITY_IRON_GOLEM_DAMAGE = SoundEventImpl.get("minecraft:entity.iron_golem.damage"); + SoundEvent ITEM_GOAT_HORN_SOUND_3 = BuiltinSoundEvent.get("minecraft:item.goat_horn.sound.3"); - SoundEvent ENTITY_IRON_GOLEM_DEATH = SoundEventImpl.get("minecraft:entity.iron_golem.death"); + SoundEvent ITEM_GOAT_HORN_SOUND_4 = BuiltinSoundEvent.get("minecraft:item.goat_horn.sound.4"); - SoundEvent ENTITY_IRON_GOLEM_HURT = SoundEventImpl.get("minecraft:entity.iron_golem.hurt"); + SoundEvent ITEM_GOAT_HORN_SOUND_5 = BuiltinSoundEvent.get("minecraft:item.goat_horn.sound.5"); - SoundEvent ENTITY_IRON_GOLEM_REPAIR = SoundEventImpl.get("minecraft:entity.iron_golem.repair"); + SoundEvent ITEM_GOAT_HORN_SOUND_6 = BuiltinSoundEvent.get("minecraft:item.goat_horn.sound.6"); - SoundEvent ENTITY_IRON_GOLEM_STEP = SoundEventImpl.get("minecraft:entity.iron_golem.step"); + SoundEvent ITEM_GOAT_HORN_SOUND_7 = BuiltinSoundEvent.get("minecraft:item.goat_horn.sound.7"); - SoundEvent BLOCK_IRON_TRAPDOOR_CLOSE = SoundEventImpl.get("minecraft:block.iron_trapdoor.close"); + SoundEvent ENTITY_HORSE_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.horse.ambient"); - SoundEvent BLOCK_IRON_TRAPDOOR_OPEN = SoundEventImpl.get("minecraft:block.iron_trapdoor.open"); + SoundEvent ENTITY_HORSE_ANGRY = BuiltinSoundEvent.get("minecraft:entity.horse.angry"); - SoundEvent ENTITY_ITEM_FRAME_ADD_ITEM = SoundEventImpl.get("minecraft:entity.item_frame.add_item"); + SoundEvent ENTITY_HORSE_ARMOR = BuiltinSoundEvent.get("minecraft:entity.horse.armor"); - SoundEvent ENTITY_ITEM_FRAME_BREAK = SoundEventImpl.get("minecraft:entity.item_frame.break"); + SoundEvent ENTITY_HORSE_BREATHE = BuiltinSoundEvent.get("minecraft:entity.horse.breathe"); - SoundEvent ENTITY_ITEM_FRAME_PLACE = SoundEventImpl.get("minecraft:entity.item_frame.place"); + SoundEvent ENTITY_HORSE_DEATH = BuiltinSoundEvent.get("minecraft:entity.horse.death"); - SoundEvent ENTITY_ITEM_FRAME_REMOVE_ITEM = SoundEventImpl.get("minecraft:entity.item_frame.remove_item"); + SoundEvent ENTITY_HORSE_EAT = BuiltinSoundEvent.get("minecraft:entity.horse.eat"); - SoundEvent ENTITY_ITEM_FRAME_ROTATE_ITEM = SoundEventImpl.get("minecraft:entity.item_frame.rotate_item"); + SoundEvent ENTITY_HORSE_GALLOP = BuiltinSoundEvent.get("minecraft:entity.horse.gallop"); - SoundEvent ENTITY_ITEM_BREAK = SoundEventImpl.get("minecraft:entity.item.break"); + SoundEvent ENTITY_HORSE_HURT = BuiltinSoundEvent.get("minecraft:entity.horse.hurt"); - SoundEvent ENTITY_ITEM_PICKUP = SoundEventImpl.get("minecraft:entity.item.pickup"); + SoundEvent ENTITY_HORSE_JUMP = BuiltinSoundEvent.get("minecraft:entity.horse.jump"); - SoundEvent BLOCK_LADDER_BREAK = SoundEventImpl.get("minecraft:block.ladder.break"); + SoundEvent ENTITY_HORSE_LAND = BuiltinSoundEvent.get("minecraft:entity.horse.land"); - SoundEvent BLOCK_LADDER_FALL = SoundEventImpl.get("minecraft:block.ladder.fall"); + SoundEvent ENTITY_HORSE_SADDLE = BuiltinSoundEvent.get("minecraft:entity.horse.saddle"); - SoundEvent BLOCK_LADDER_HIT = SoundEventImpl.get("minecraft:block.ladder.hit"); + SoundEvent ENTITY_HORSE_STEP = BuiltinSoundEvent.get("minecraft:entity.horse.step"); - SoundEvent BLOCK_LADDER_PLACE = SoundEventImpl.get("minecraft:block.ladder.place"); + SoundEvent ENTITY_HORSE_STEP_WOOD = BuiltinSoundEvent.get("minecraft:entity.horse.step_wood"); - SoundEvent BLOCK_LADDER_STEP = SoundEventImpl.get("minecraft:block.ladder.step"); + SoundEvent ENTITY_HOSTILE_BIG_FALL = BuiltinSoundEvent.get("minecraft:entity.hostile.big_fall"); - SoundEvent BLOCK_LANTERN_BREAK = SoundEventImpl.get("minecraft:block.lantern.break"); + SoundEvent ENTITY_HOSTILE_DEATH = BuiltinSoundEvent.get("minecraft:entity.hostile.death"); - SoundEvent BLOCK_LANTERN_FALL = SoundEventImpl.get("minecraft:block.lantern.fall"); + SoundEvent ENTITY_HOSTILE_HURT = BuiltinSoundEvent.get("minecraft:entity.hostile.hurt"); - SoundEvent BLOCK_LANTERN_HIT = SoundEventImpl.get("minecraft:block.lantern.hit"); + SoundEvent ENTITY_HOSTILE_SMALL_FALL = BuiltinSoundEvent.get("minecraft:entity.hostile.small_fall"); - SoundEvent BLOCK_LANTERN_PLACE = SoundEventImpl.get("minecraft:block.lantern.place"); + SoundEvent ENTITY_HOSTILE_SPLASH = BuiltinSoundEvent.get("minecraft:entity.hostile.splash"); - SoundEvent BLOCK_LANTERN_STEP = SoundEventImpl.get("minecraft:block.lantern.step"); + SoundEvent ENTITY_HOSTILE_SWIM = BuiltinSoundEvent.get("minecraft:entity.hostile.swim"); - SoundEvent BLOCK_LARGE_AMETHYST_BUD_BREAK = SoundEventImpl.get("minecraft:block.large_amethyst_bud.break"); + SoundEvent ENTITY_HUSK_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.husk.ambient"); - SoundEvent BLOCK_LARGE_AMETHYST_BUD_PLACE = SoundEventImpl.get("minecraft:block.large_amethyst_bud.place"); + SoundEvent ENTITY_HUSK_CONVERTED_TO_ZOMBIE = BuiltinSoundEvent.get("minecraft:entity.husk.converted_to_zombie"); - SoundEvent BLOCK_LAVA_AMBIENT = SoundEventImpl.get("minecraft:block.lava.ambient"); + SoundEvent ENTITY_HUSK_DEATH = BuiltinSoundEvent.get("minecraft:entity.husk.death"); - SoundEvent BLOCK_LAVA_EXTINGUISH = SoundEventImpl.get("minecraft:block.lava.extinguish"); + SoundEvent ENTITY_HUSK_HURT = BuiltinSoundEvent.get("minecraft:entity.husk.hurt"); - SoundEvent BLOCK_LAVA_POP = SoundEventImpl.get("minecraft:block.lava.pop"); + SoundEvent ENTITY_HUSK_STEP = BuiltinSoundEvent.get("minecraft:entity.husk.step"); - SoundEvent ENTITY_LEASH_KNOT_BREAK = SoundEventImpl.get("minecraft:entity.leash_knot.break"); + SoundEvent ENTITY_ILLUSIONER_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.illusioner.ambient"); - SoundEvent ENTITY_LEASH_KNOT_PLACE = SoundEventImpl.get("minecraft:entity.leash_knot.place"); + SoundEvent ENTITY_ILLUSIONER_CAST_SPELL = BuiltinSoundEvent.get("minecraft:entity.illusioner.cast_spell"); - SoundEvent BLOCK_LEVER_CLICK = SoundEventImpl.get("minecraft:block.lever.click"); + SoundEvent ENTITY_ILLUSIONER_DEATH = BuiltinSoundEvent.get("minecraft:entity.illusioner.death"); - SoundEvent ENTITY_LIGHTNING_BOLT_IMPACT = SoundEventImpl.get("minecraft:entity.lightning_bolt.impact"); + SoundEvent ENTITY_ILLUSIONER_HURT = BuiltinSoundEvent.get("minecraft:entity.illusioner.hurt"); - SoundEvent ENTITY_LIGHTNING_BOLT_THUNDER = SoundEventImpl.get("minecraft:entity.lightning_bolt.thunder"); + SoundEvent ENTITY_ILLUSIONER_MIRROR_MOVE = BuiltinSoundEvent.get("minecraft:entity.illusioner.mirror_move"); - SoundEvent ENTITY_LINGERING_POTION_THROW = SoundEventImpl.get("minecraft:entity.lingering_potion.throw"); + SoundEvent ENTITY_ILLUSIONER_PREPARE_BLINDNESS = BuiltinSoundEvent.get("minecraft:entity.illusioner.prepare_blindness"); - SoundEvent ENTITY_LLAMA_AMBIENT = SoundEventImpl.get("minecraft:entity.llama.ambient"); + SoundEvent ENTITY_ILLUSIONER_PREPARE_MIRROR = BuiltinSoundEvent.get("minecraft:entity.illusioner.prepare_mirror"); - SoundEvent ENTITY_LLAMA_ANGRY = SoundEventImpl.get("minecraft:entity.llama.angry"); + SoundEvent ITEM_INK_SAC_USE = BuiltinSoundEvent.get("minecraft:item.ink_sac.use"); - SoundEvent ENTITY_LLAMA_CHEST = SoundEventImpl.get("minecraft:entity.llama.chest"); + SoundEvent BLOCK_IRON_DOOR_CLOSE = BuiltinSoundEvent.get("minecraft:block.iron_door.close"); - SoundEvent ENTITY_LLAMA_DEATH = SoundEventImpl.get("minecraft:entity.llama.death"); + SoundEvent BLOCK_IRON_DOOR_OPEN = BuiltinSoundEvent.get("minecraft:block.iron_door.open"); - SoundEvent ENTITY_LLAMA_EAT = SoundEventImpl.get("minecraft:entity.llama.eat"); + SoundEvent ENTITY_IRON_GOLEM_ATTACK = BuiltinSoundEvent.get("minecraft:entity.iron_golem.attack"); - SoundEvent ENTITY_LLAMA_HURT = SoundEventImpl.get("minecraft:entity.llama.hurt"); + SoundEvent ENTITY_IRON_GOLEM_DAMAGE = BuiltinSoundEvent.get("minecraft:entity.iron_golem.damage"); - SoundEvent ENTITY_LLAMA_SPIT = SoundEventImpl.get("minecraft:entity.llama.spit"); + SoundEvent ENTITY_IRON_GOLEM_DEATH = BuiltinSoundEvent.get("minecraft:entity.iron_golem.death"); - SoundEvent ENTITY_LLAMA_STEP = SoundEventImpl.get("minecraft:entity.llama.step"); + SoundEvent ENTITY_IRON_GOLEM_HURT = BuiltinSoundEvent.get("minecraft:entity.iron_golem.hurt"); - SoundEvent ENTITY_LLAMA_SWAG = SoundEventImpl.get("minecraft:entity.llama.swag"); + SoundEvent ENTITY_IRON_GOLEM_REPAIR = BuiltinSoundEvent.get("minecraft:entity.iron_golem.repair"); - SoundEvent ENTITY_MAGMA_CUBE_DEATH_SMALL = SoundEventImpl.get("minecraft:entity.magma_cube.death_small"); + SoundEvent ENTITY_IRON_GOLEM_STEP = BuiltinSoundEvent.get("minecraft:entity.iron_golem.step"); - SoundEvent BLOCK_LODESTONE_BREAK = SoundEventImpl.get("minecraft:block.lodestone.break"); + SoundEvent BLOCK_IRON_TRAPDOOR_CLOSE = BuiltinSoundEvent.get("minecraft:block.iron_trapdoor.close"); - SoundEvent BLOCK_LODESTONE_STEP = SoundEventImpl.get("minecraft:block.lodestone.step"); + SoundEvent BLOCK_IRON_TRAPDOOR_OPEN = BuiltinSoundEvent.get("minecraft:block.iron_trapdoor.open"); - SoundEvent BLOCK_LODESTONE_PLACE = SoundEventImpl.get("minecraft:block.lodestone.place"); + SoundEvent ENTITY_ITEM_FRAME_ADD_ITEM = BuiltinSoundEvent.get("minecraft:entity.item_frame.add_item"); - SoundEvent BLOCK_LODESTONE_HIT = SoundEventImpl.get("minecraft:block.lodestone.hit"); + SoundEvent ENTITY_ITEM_FRAME_BREAK = BuiltinSoundEvent.get("minecraft:entity.item_frame.break"); - SoundEvent BLOCK_LODESTONE_FALL = SoundEventImpl.get("minecraft:block.lodestone.fall"); + SoundEvent ENTITY_ITEM_FRAME_PLACE = BuiltinSoundEvent.get("minecraft:entity.item_frame.place"); - SoundEvent ITEM_LODESTONE_COMPASS_LOCK = SoundEventImpl.get("minecraft:item.lodestone_compass.lock"); + SoundEvent ENTITY_ITEM_FRAME_REMOVE_ITEM = BuiltinSoundEvent.get("minecraft:entity.item_frame.remove_item"); - SoundEvent ENTITY_MAGMA_CUBE_DEATH = SoundEventImpl.get("minecraft:entity.magma_cube.death"); + SoundEvent ENTITY_ITEM_FRAME_ROTATE_ITEM = BuiltinSoundEvent.get("minecraft:entity.item_frame.rotate_item"); - SoundEvent ENTITY_MAGMA_CUBE_HURT = SoundEventImpl.get("minecraft:entity.magma_cube.hurt"); + SoundEvent ENTITY_ITEM_BREAK = BuiltinSoundEvent.get("minecraft:entity.item.break"); - SoundEvent ENTITY_MAGMA_CUBE_HURT_SMALL = SoundEventImpl.get("minecraft:entity.magma_cube.hurt_small"); + SoundEvent ENTITY_ITEM_PICKUP = BuiltinSoundEvent.get("minecraft:entity.item.pickup"); - SoundEvent ENTITY_MAGMA_CUBE_JUMP = SoundEventImpl.get("minecraft:entity.magma_cube.jump"); + SoundEvent BLOCK_LADDER_BREAK = BuiltinSoundEvent.get("minecraft:block.ladder.break"); - SoundEvent ENTITY_MAGMA_CUBE_SQUISH = SoundEventImpl.get("minecraft:entity.magma_cube.squish"); + SoundEvent BLOCK_LADDER_FALL = BuiltinSoundEvent.get("minecraft:block.ladder.fall"); - SoundEvent ENTITY_MAGMA_CUBE_SQUISH_SMALL = SoundEventImpl.get("minecraft:entity.magma_cube.squish_small"); + SoundEvent BLOCK_LADDER_HIT = BuiltinSoundEvent.get("minecraft:block.ladder.hit"); - SoundEvent BLOCK_MANGROVE_ROOTS_BREAK = SoundEventImpl.get("minecraft:block.mangrove_roots.break"); + SoundEvent BLOCK_LADDER_PLACE = BuiltinSoundEvent.get("minecraft:block.ladder.place"); - SoundEvent BLOCK_MANGROVE_ROOTS_FALL = SoundEventImpl.get("minecraft:block.mangrove_roots.fall"); + SoundEvent BLOCK_LADDER_STEP = BuiltinSoundEvent.get("minecraft:block.ladder.step"); - SoundEvent BLOCK_MANGROVE_ROOTS_HIT = SoundEventImpl.get("minecraft:block.mangrove_roots.hit"); + SoundEvent BLOCK_LANTERN_BREAK = BuiltinSoundEvent.get("minecraft:block.lantern.break"); - SoundEvent BLOCK_MANGROVE_ROOTS_PLACE = SoundEventImpl.get("minecraft:block.mangrove_roots.place"); + SoundEvent BLOCK_LANTERN_FALL = BuiltinSoundEvent.get("minecraft:block.lantern.fall"); - SoundEvent BLOCK_MANGROVE_ROOTS_STEP = SoundEventImpl.get("minecraft:block.mangrove_roots.step"); + SoundEvent BLOCK_LANTERN_HIT = BuiltinSoundEvent.get("minecraft:block.lantern.hit"); - SoundEvent BLOCK_MEDIUM_AMETHYST_BUD_BREAK = SoundEventImpl.get("minecraft:block.medium_amethyst_bud.break"); + SoundEvent BLOCK_LANTERN_PLACE = BuiltinSoundEvent.get("minecraft:block.lantern.place"); - SoundEvent BLOCK_MEDIUM_AMETHYST_BUD_PLACE = SoundEventImpl.get("minecraft:block.medium_amethyst_bud.place"); + SoundEvent BLOCK_LANTERN_STEP = BuiltinSoundEvent.get("minecraft:block.lantern.step"); - SoundEvent BLOCK_METAL_BREAK = SoundEventImpl.get("minecraft:block.metal.break"); + SoundEvent BLOCK_LARGE_AMETHYST_BUD_BREAK = BuiltinSoundEvent.get("minecraft:block.large_amethyst_bud.break"); - SoundEvent BLOCK_METAL_FALL = SoundEventImpl.get("minecraft:block.metal.fall"); + SoundEvent BLOCK_LARGE_AMETHYST_BUD_PLACE = BuiltinSoundEvent.get("minecraft:block.large_amethyst_bud.place"); - SoundEvent BLOCK_METAL_HIT = SoundEventImpl.get("minecraft:block.metal.hit"); + SoundEvent BLOCK_LAVA_AMBIENT = BuiltinSoundEvent.get("minecraft:block.lava.ambient"); - SoundEvent BLOCK_METAL_PLACE = SoundEventImpl.get("minecraft:block.metal.place"); + SoundEvent BLOCK_LAVA_EXTINGUISH = BuiltinSoundEvent.get("minecraft:block.lava.extinguish"); - SoundEvent BLOCK_METAL_PRESSURE_PLATE_CLICK_OFF = SoundEventImpl.get("minecraft:block.metal_pressure_plate.click_off"); + SoundEvent BLOCK_LAVA_POP = BuiltinSoundEvent.get("minecraft:block.lava.pop"); - SoundEvent BLOCK_METAL_PRESSURE_PLATE_CLICK_ON = SoundEventImpl.get("minecraft:block.metal_pressure_plate.click_on"); + SoundEvent ENTITY_LEASH_KNOT_BREAK = BuiltinSoundEvent.get("minecraft:entity.leash_knot.break"); - SoundEvent BLOCK_METAL_STEP = SoundEventImpl.get("minecraft:block.metal.step"); + SoundEvent ENTITY_LEASH_KNOT_PLACE = BuiltinSoundEvent.get("minecraft:entity.leash_knot.place"); - SoundEvent ENTITY_MINECART_INSIDE_UNDERWATER = SoundEventImpl.get("minecraft:entity.minecart.inside.underwater"); + SoundEvent BLOCK_LEVER_CLICK = BuiltinSoundEvent.get("minecraft:block.lever.click"); - SoundEvent ENTITY_MINECART_INSIDE = SoundEventImpl.get("minecraft:entity.minecart.inside"); + SoundEvent ENTITY_LIGHTNING_BOLT_IMPACT = BuiltinSoundEvent.get("minecraft:entity.lightning_bolt.impact"); - SoundEvent ENTITY_MINECART_RIDING = SoundEventImpl.get("minecraft:entity.minecart.riding"); + SoundEvent ENTITY_LIGHTNING_BOLT_THUNDER = BuiltinSoundEvent.get("minecraft:entity.lightning_bolt.thunder"); - SoundEvent ENTITY_MOOSHROOM_CONVERT = SoundEventImpl.get("minecraft:entity.mooshroom.convert"); + SoundEvent ENTITY_LINGERING_POTION_THROW = BuiltinSoundEvent.get("minecraft:entity.lingering_potion.throw"); - SoundEvent ENTITY_MOOSHROOM_EAT = SoundEventImpl.get("minecraft:entity.mooshroom.eat"); + SoundEvent ENTITY_LLAMA_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.llama.ambient"); - SoundEvent ENTITY_MOOSHROOM_MILK = SoundEventImpl.get("minecraft:entity.mooshroom.milk"); + SoundEvent ENTITY_LLAMA_ANGRY = BuiltinSoundEvent.get("minecraft:entity.llama.angry"); - SoundEvent ENTITY_MOOSHROOM_SUSPICIOUS_MILK = SoundEventImpl.get("minecraft:entity.mooshroom.suspicious_milk"); + SoundEvent ENTITY_LLAMA_CHEST = BuiltinSoundEvent.get("minecraft:entity.llama.chest"); - SoundEvent ENTITY_MOOSHROOM_SHEAR = SoundEventImpl.get("minecraft:entity.mooshroom.shear"); + SoundEvent ENTITY_LLAMA_DEATH = BuiltinSoundEvent.get("minecraft:entity.llama.death"); - SoundEvent BLOCK_MOSS_CARPET_BREAK = SoundEventImpl.get("minecraft:block.moss_carpet.break"); + SoundEvent ENTITY_LLAMA_EAT = BuiltinSoundEvent.get("minecraft:entity.llama.eat"); - SoundEvent BLOCK_MOSS_CARPET_FALL = SoundEventImpl.get("minecraft:block.moss_carpet.fall"); + SoundEvent ENTITY_LLAMA_HURT = BuiltinSoundEvent.get("minecraft:entity.llama.hurt"); - SoundEvent BLOCK_MOSS_CARPET_HIT = SoundEventImpl.get("minecraft:block.moss_carpet.hit"); + SoundEvent ENTITY_LLAMA_SPIT = BuiltinSoundEvent.get("minecraft:entity.llama.spit"); - SoundEvent BLOCK_MOSS_CARPET_PLACE = SoundEventImpl.get("minecraft:block.moss_carpet.place"); + SoundEvent ENTITY_LLAMA_STEP = BuiltinSoundEvent.get("minecraft:entity.llama.step"); - SoundEvent BLOCK_MOSS_CARPET_STEP = SoundEventImpl.get("minecraft:block.moss_carpet.step"); + SoundEvent ENTITY_LLAMA_SWAG = BuiltinSoundEvent.get("minecraft:entity.llama.swag"); - SoundEvent BLOCK_PINK_PETALS_BREAK = SoundEventImpl.get("minecraft:block.pink_petals.break"); + SoundEvent ENTITY_MAGMA_CUBE_DEATH_SMALL = BuiltinSoundEvent.get("minecraft:entity.magma_cube.death_small"); - SoundEvent BLOCK_PINK_PETALS_FALL = SoundEventImpl.get("minecraft:block.pink_petals.fall"); + SoundEvent BLOCK_LODESTONE_BREAK = BuiltinSoundEvent.get("minecraft:block.lodestone.break"); - SoundEvent BLOCK_PINK_PETALS_HIT = SoundEventImpl.get("minecraft:block.pink_petals.hit"); + SoundEvent BLOCK_LODESTONE_STEP = BuiltinSoundEvent.get("minecraft:block.lodestone.step"); - SoundEvent BLOCK_PINK_PETALS_PLACE = SoundEventImpl.get("minecraft:block.pink_petals.place"); + SoundEvent BLOCK_LODESTONE_PLACE = BuiltinSoundEvent.get("minecraft:block.lodestone.place"); - SoundEvent BLOCK_PINK_PETALS_STEP = SoundEventImpl.get("minecraft:block.pink_petals.step"); + SoundEvent BLOCK_LODESTONE_HIT = BuiltinSoundEvent.get("minecraft:block.lodestone.hit"); - SoundEvent BLOCK_MOSS_BREAK = SoundEventImpl.get("minecraft:block.moss.break"); + SoundEvent BLOCK_LODESTONE_FALL = BuiltinSoundEvent.get("minecraft:block.lodestone.fall"); - SoundEvent BLOCK_MOSS_FALL = SoundEventImpl.get("minecraft:block.moss.fall"); + SoundEvent ITEM_LODESTONE_COMPASS_LOCK = BuiltinSoundEvent.get("minecraft:item.lodestone_compass.lock"); - SoundEvent BLOCK_MOSS_HIT = SoundEventImpl.get("minecraft:block.moss.hit"); + SoundEvent ITEM_MACE_SMASH_AIR = BuiltinSoundEvent.get("minecraft:item.mace.smash_air"); - SoundEvent BLOCK_MOSS_PLACE = SoundEventImpl.get("minecraft:block.moss.place"); + SoundEvent ITEM_MACE_SMASH_GROUND = BuiltinSoundEvent.get("minecraft:item.mace.smash_ground"); - SoundEvent BLOCK_MOSS_STEP = SoundEventImpl.get("minecraft:block.moss.step"); + SoundEvent ITEM_MACE_SMASH_GROUND_HEAVY = BuiltinSoundEvent.get("minecraft:item.mace.smash_ground_heavy"); - SoundEvent BLOCK_MUD_BREAK = SoundEventImpl.get("minecraft:block.mud.break"); + SoundEvent ENTITY_MAGMA_CUBE_DEATH = BuiltinSoundEvent.get("minecraft:entity.magma_cube.death"); - SoundEvent BLOCK_MUD_FALL = SoundEventImpl.get("minecraft:block.mud.fall"); + SoundEvent ENTITY_MAGMA_CUBE_HURT = BuiltinSoundEvent.get("minecraft:entity.magma_cube.hurt"); - SoundEvent BLOCK_MUD_HIT = SoundEventImpl.get("minecraft:block.mud.hit"); + SoundEvent ENTITY_MAGMA_CUBE_HURT_SMALL = BuiltinSoundEvent.get("minecraft:entity.magma_cube.hurt_small"); - SoundEvent BLOCK_MUD_PLACE = SoundEventImpl.get("minecraft:block.mud.place"); + SoundEvent ENTITY_MAGMA_CUBE_JUMP = BuiltinSoundEvent.get("minecraft:entity.magma_cube.jump"); - SoundEvent BLOCK_MUD_STEP = SoundEventImpl.get("minecraft:block.mud.step"); + SoundEvent ENTITY_MAGMA_CUBE_SQUISH = BuiltinSoundEvent.get("minecraft:entity.magma_cube.squish"); - SoundEvent BLOCK_MUD_BRICKS_BREAK = SoundEventImpl.get("minecraft:block.mud_bricks.break"); + SoundEvent ENTITY_MAGMA_CUBE_SQUISH_SMALL = BuiltinSoundEvent.get("minecraft:entity.magma_cube.squish_small"); - SoundEvent BLOCK_MUD_BRICKS_FALL = SoundEventImpl.get("minecraft:block.mud_bricks.fall"); + SoundEvent BLOCK_MANGROVE_ROOTS_BREAK = BuiltinSoundEvent.get("minecraft:block.mangrove_roots.break"); - SoundEvent BLOCK_MUD_BRICKS_HIT = SoundEventImpl.get("minecraft:block.mud_bricks.hit"); + SoundEvent BLOCK_MANGROVE_ROOTS_FALL = BuiltinSoundEvent.get("minecraft:block.mangrove_roots.fall"); - SoundEvent BLOCK_MUD_BRICKS_PLACE = SoundEventImpl.get("minecraft:block.mud_bricks.place"); + SoundEvent BLOCK_MANGROVE_ROOTS_HIT = BuiltinSoundEvent.get("minecraft:block.mangrove_roots.hit"); - SoundEvent BLOCK_MUD_BRICKS_STEP = SoundEventImpl.get("minecraft:block.mud_bricks.step"); + SoundEvent BLOCK_MANGROVE_ROOTS_PLACE = BuiltinSoundEvent.get("minecraft:block.mangrove_roots.place"); - SoundEvent BLOCK_MUDDY_MANGROVE_ROOTS_BREAK = SoundEventImpl.get("minecraft:block.muddy_mangrove_roots.break"); + SoundEvent BLOCK_MANGROVE_ROOTS_STEP = BuiltinSoundEvent.get("minecraft:block.mangrove_roots.step"); - SoundEvent BLOCK_MUDDY_MANGROVE_ROOTS_FALL = SoundEventImpl.get("minecraft:block.muddy_mangrove_roots.fall"); + SoundEvent BLOCK_MEDIUM_AMETHYST_BUD_BREAK = BuiltinSoundEvent.get("minecraft:block.medium_amethyst_bud.break"); - SoundEvent BLOCK_MUDDY_MANGROVE_ROOTS_HIT = SoundEventImpl.get("minecraft:block.muddy_mangrove_roots.hit"); + SoundEvent BLOCK_MEDIUM_AMETHYST_BUD_PLACE = BuiltinSoundEvent.get("minecraft:block.medium_amethyst_bud.place"); - SoundEvent BLOCK_MUDDY_MANGROVE_ROOTS_PLACE = SoundEventImpl.get("minecraft:block.muddy_mangrove_roots.place"); + SoundEvent BLOCK_METAL_BREAK = BuiltinSoundEvent.get("minecraft:block.metal.break"); - SoundEvent BLOCK_MUDDY_MANGROVE_ROOTS_STEP = SoundEventImpl.get("minecraft:block.muddy_mangrove_roots.step"); + SoundEvent BLOCK_METAL_FALL = BuiltinSoundEvent.get("minecraft:block.metal.fall"); - SoundEvent ENTITY_MULE_AMBIENT = SoundEventImpl.get("minecraft:entity.mule.ambient"); + SoundEvent BLOCK_METAL_HIT = BuiltinSoundEvent.get("minecraft:block.metal.hit"); - SoundEvent ENTITY_MULE_ANGRY = SoundEventImpl.get("minecraft:entity.mule.angry"); + SoundEvent BLOCK_METAL_PLACE = BuiltinSoundEvent.get("minecraft:block.metal.place"); - SoundEvent ENTITY_MULE_CHEST = SoundEventImpl.get("minecraft:entity.mule.chest"); + SoundEvent BLOCK_METAL_PRESSURE_PLATE_CLICK_OFF = BuiltinSoundEvent.get("minecraft:block.metal_pressure_plate.click_off"); - SoundEvent ENTITY_MULE_DEATH = SoundEventImpl.get("minecraft:entity.mule.death"); + SoundEvent BLOCK_METAL_PRESSURE_PLATE_CLICK_ON = BuiltinSoundEvent.get("minecraft:block.metal_pressure_plate.click_on"); - SoundEvent ENTITY_MULE_EAT = SoundEventImpl.get("minecraft:entity.mule.eat"); + SoundEvent BLOCK_METAL_STEP = BuiltinSoundEvent.get("minecraft:block.metal.step"); - SoundEvent ENTITY_MULE_HURT = SoundEventImpl.get("minecraft:entity.mule.hurt"); + SoundEvent ENTITY_MINECART_INSIDE_UNDERWATER = BuiltinSoundEvent.get("minecraft:entity.minecart.inside.underwater"); - SoundEvent MUSIC_CREATIVE = SoundEventImpl.get("minecraft:music.creative"); + SoundEvent ENTITY_MINECART_INSIDE = BuiltinSoundEvent.get("minecraft:entity.minecart.inside"); - SoundEvent MUSIC_CREDITS = SoundEventImpl.get("minecraft:music.credits"); + SoundEvent ENTITY_MINECART_RIDING = BuiltinSoundEvent.get("minecraft:entity.minecart.riding"); - SoundEvent MUSIC_DISC_5 = SoundEventImpl.get("minecraft:music_disc.5"); + SoundEvent ENTITY_MOOSHROOM_CONVERT = BuiltinSoundEvent.get("minecraft:entity.mooshroom.convert"); - SoundEvent MUSIC_DISC_11 = SoundEventImpl.get("minecraft:music_disc.11"); + SoundEvent ENTITY_MOOSHROOM_EAT = BuiltinSoundEvent.get("minecraft:entity.mooshroom.eat"); - SoundEvent MUSIC_DISC_13 = SoundEventImpl.get("minecraft:music_disc.13"); + SoundEvent ENTITY_MOOSHROOM_MILK = BuiltinSoundEvent.get("minecraft:entity.mooshroom.milk"); - SoundEvent MUSIC_DISC_BLOCKS = SoundEventImpl.get("minecraft:music_disc.blocks"); + SoundEvent ENTITY_MOOSHROOM_SUSPICIOUS_MILK = BuiltinSoundEvent.get("minecraft:entity.mooshroom.suspicious_milk"); - SoundEvent MUSIC_DISC_CAT = SoundEventImpl.get("minecraft:music_disc.cat"); + SoundEvent ENTITY_MOOSHROOM_SHEAR = BuiltinSoundEvent.get("minecraft:entity.mooshroom.shear"); - SoundEvent MUSIC_DISC_CHIRP = SoundEventImpl.get("minecraft:music_disc.chirp"); + SoundEvent BLOCK_MOSS_CARPET_BREAK = BuiltinSoundEvent.get("minecraft:block.moss_carpet.break"); - SoundEvent MUSIC_DISC_FAR = SoundEventImpl.get("minecraft:music_disc.far"); + SoundEvent BLOCK_MOSS_CARPET_FALL = BuiltinSoundEvent.get("minecraft:block.moss_carpet.fall"); - SoundEvent MUSIC_DISC_MALL = SoundEventImpl.get("minecraft:music_disc.mall"); + SoundEvent BLOCK_MOSS_CARPET_HIT = BuiltinSoundEvent.get("minecraft:block.moss_carpet.hit"); - SoundEvent MUSIC_DISC_MELLOHI = SoundEventImpl.get("minecraft:music_disc.mellohi"); + SoundEvent BLOCK_MOSS_CARPET_PLACE = BuiltinSoundEvent.get("minecraft:block.moss_carpet.place"); - SoundEvent MUSIC_DISC_PIGSTEP = SoundEventImpl.get("minecraft:music_disc.pigstep"); + SoundEvent BLOCK_MOSS_CARPET_STEP = BuiltinSoundEvent.get("minecraft:block.moss_carpet.step"); - SoundEvent MUSIC_DISC_STAL = SoundEventImpl.get("minecraft:music_disc.stal"); + SoundEvent BLOCK_PINK_PETALS_BREAK = BuiltinSoundEvent.get("minecraft:block.pink_petals.break"); - SoundEvent MUSIC_DISC_STRAD = SoundEventImpl.get("minecraft:music_disc.strad"); + SoundEvent BLOCK_PINK_PETALS_FALL = BuiltinSoundEvent.get("minecraft:block.pink_petals.fall"); - SoundEvent MUSIC_DISC_WAIT = SoundEventImpl.get("minecraft:music_disc.wait"); + SoundEvent BLOCK_PINK_PETALS_HIT = BuiltinSoundEvent.get("minecraft:block.pink_petals.hit"); - SoundEvent MUSIC_DISC_WARD = SoundEventImpl.get("minecraft:music_disc.ward"); + SoundEvent BLOCK_PINK_PETALS_PLACE = BuiltinSoundEvent.get("minecraft:block.pink_petals.place"); - SoundEvent MUSIC_DISC_OTHERSIDE = SoundEventImpl.get("minecraft:music_disc.otherside"); + SoundEvent BLOCK_PINK_PETALS_STEP = BuiltinSoundEvent.get("minecraft:block.pink_petals.step"); - SoundEvent MUSIC_DISC_RELIC = SoundEventImpl.get("minecraft:music_disc.relic"); + SoundEvent BLOCK_MOSS_BREAK = BuiltinSoundEvent.get("minecraft:block.moss.break"); - SoundEvent MUSIC_DRAGON = SoundEventImpl.get("minecraft:music.dragon"); + SoundEvent BLOCK_MOSS_FALL = BuiltinSoundEvent.get("minecraft:block.moss.fall"); - SoundEvent MUSIC_END = SoundEventImpl.get("minecraft:music.end"); + SoundEvent BLOCK_MOSS_HIT = BuiltinSoundEvent.get("minecraft:block.moss.hit"); - SoundEvent MUSIC_GAME = SoundEventImpl.get("minecraft:music.game"); + SoundEvent BLOCK_MOSS_PLACE = BuiltinSoundEvent.get("minecraft:block.moss.place"); - SoundEvent MUSIC_MENU = SoundEventImpl.get("minecraft:music.menu"); + SoundEvent BLOCK_MOSS_STEP = BuiltinSoundEvent.get("minecraft:block.moss.step"); - SoundEvent MUSIC_NETHER_BASALT_DELTAS = SoundEventImpl.get("minecraft:music.nether.basalt_deltas"); + SoundEvent BLOCK_MUD_BREAK = BuiltinSoundEvent.get("minecraft:block.mud.break"); - SoundEvent MUSIC_NETHER_CRIMSON_FOREST = SoundEventImpl.get("minecraft:music.nether.crimson_forest"); + SoundEvent BLOCK_MUD_FALL = BuiltinSoundEvent.get("minecraft:block.mud.fall"); - SoundEvent MUSIC_OVERWORLD_DEEP_DARK = SoundEventImpl.get("minecraft:music.overworld.deep_dark"); + SoundEvent BLOCK_MUD_HIT = BuiltinSoundEvent.get("minecraft:block.mud.hit"); - SoundEvent MUSIC_OVERWORLD_DRIPSTONE_CAVES = SoundEventImpl.get("minecraft:music.overworld.dripstone_caves"); + SoundEvent BLOCK_MUD_PLACE = BuiltinSoundEvent.get("minecraft:block.mud.place"); - SoundEvent MUSIC_OVERWORLD_GROVE = SoundEventImpl.get("minecraft:music.overworld.grove"); + SoundEvent BLOCK_MUD_STEP = BuiltinSoundEvent.get("minecraft:block.mud.step"); - SoundEvent MUSIC_OVERWORLD_JAGGED_PEAKS = SoundEventImpl.get("minecraft:music.overworld.jagged_peaks"); + SoundEvent BLOCK_MUD_BRICKS_BREAK = BuiltinSoundEvent.get("minecraft:block.mud_bricks.break"); - SoundEvent MUSIC_OVERWORLD_LUSH_CAVES = SoundEventImpl.get("minecraft:music.overworld.lush_caves"); + SoundEvent BLOCK_MUD_BRICKS_FALL = BuiltinSoundEvent.get("minecraft:block.mud_bricks.fall"); - SoundEvent MUSIC_OVERWORLD_SWAMP = SoundEventImpl.get("minecraft:music.overworld.swamp"); + SoundEvent BLOCK_MUD_BRICKS_HIT = BuiltinSoundEvent.get("minecraft:block.mud_bricks.hit"); - SoundEvent MUSIC_OVERWORLD_FOREST = SoundEventImpl.get("minecraft:music.overworld.forest"); + SoundEvent BLOCK_MUD_BRICKS_PLACE = BuiltinSoundEvent.get("minecraft:block.mud_bricks.place"); - SoundEvent MUSIC_OVERWORLD_OLD_GROWTH_TAIGA = SoundEventImpl.get("minecraft:music.overworld.old_growth_taiga"); + SoundEvent BLOCK_MUD_BRICKS_STEP = BuiltinSoundEvent.get("minecraft:block.mud_bricks.step"); - SoundEvent MUSIC_OVERWORLD_MEADOW = SoundEventImpl.get("minecraft:music.overworld.meadow"); + SoundEvent BLOCK_MUDDY_MANGROVE_ROOTS_BREAK = BuiltinSoundEvent.get("minecraft:block.muddy_mangrove_roots.break"); - SoundEvent MUSIC_OVERWORLD_CHERRY_GROVE = SoundEventImpl.get("minecraft:music.overworld.cherry_grove"); + SoundEvent BLOCK_MUDDY_MANGROVE_ROOTS_FALL = BuiltinSoundEvent.get("minecraft:block.muddy_mangrove_roots.fall"); - SoundEvent MUSIC_NETHER_NETHER_WASTES = SoundEventImpl.get("minecraft:music.nether.nether_wastes"); + SoundEvent BLOCK_MUDDY_MANGROVE_ROOTS_HIT = BuiltinSoundEvent.get("minecraft:block.muddy_mangrove_roots.hit"); - SoundEvent MUSIC_OVERWORLD_FROZEN_PEAKS = SoundEventImpl.get("minecraft:music.overworld.frozen_peaks"); + SoundEvent BLOCK_MUDDY_MANGROVE_ROOTS_PLACE = BuiltinSoundEvent.get("minecraft:block.muddy_mangrove_roots.place"); - SoundEvent MUSIC_OVERWORLD_SNOWY_SLOPES = SoundEventImpl.get("minecraft:music.overworld.snowy_slopes"); + SoundEvent BLOCK_MUDDY_MANGROVE_ROOTS_STEP = BuiltinSoundEvent.get("minecraft:block.muddy_mangrove_roots.step"); - SoundEvent MUSIC_NETHER_SOUL_SAND_VALLEY = SoundEventImpl.get("minecraft:music.nether.soul_sand_valley"); + SoundEvent ENTITY_MULE_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.mule.ambient"); - SoundEvent MUSIC_OVERWORLD_STONY_PEAKS = SoundEventImpl.get("minecraft:music.overworld.stony_peaks"); + SoundEvent ENTITY_MULE_ANGRY = BuiltinSoundEvent.get("minecraft:entity.mule.angry"); - SoundEvent MUSIC_NETHER_WARPED_FOREST = SoundEventImpl.get("minecraft:music.nether.warped_forest"); + SoundEvent ENTITY_MULE_CHEST = BuiltinSoundEvent.get("minecraft:entity.mule.chest"); - SoundEvent MUSIC_OVERWORLD_FLOWER_FOREST = SoundEventImpl.get("minecraft:music.overworld.flower_forest"); + SoundEvent ENTITY_MULE_DEATH = BuiltinSoundEvent.get("minecraft:entity.mule.death"); - SoundEvent MUSIC_OVERWORLD_DESERT = SoundEventImpl.get("minecraft:music.overworld.desert"); + SoundEvent ENTITY_MULE_EAT = BuiltinSoundEvent.get("minecraft:entity.mule.eat"); - SoundEvent MUSIC_OVERWORLD_BADLANDS = SoundEventImpl.get("minecraft:music.overworld.badlands"); + SoundEvent ENTITY_MULE_HURT = BuiltinSoundEvent.get("minecraft:entity.mule.hurt"); - SoundEvent MUSIC_OVERWORLD_JUNGLE = SoundEventImpl.get("minecraft:music.overworld.jungle"); + SoundEvent ENTITY_MULE_JUMP = BuiltinSoundEvent.get("minecraft:entity.mule.jump"); - SoundEvent MUSIC_OVERWORLD_SPARSE_JUNGLE = SoundEventImpl.get("minecraft:music.overworld.sparse_jungle"); + SoundEvent MUSIC_CREATIVE = BuiltinSoundEvent.get("minecraft:music.creative"); - SoundEvent MUSIC_OVERWORLD_BAMBOO_JUNGLE = SoundEventImpl.get("minecraft:music.overworld.bamboo_jungle"); + SoundEvent MUSIC_CREDITS = BuiltinSoundEvent.get("minecraft:music.credits"); - SoundEvent MUSIC_UNDER_WATER = SoundEventImpl.get("minecraft:music.under_water"); + SoundEvent MUSIC_DISC_5 = BuiltinSoundEvent.get("minecraft:music_disc.5"); - SoundEvent BLOCK_NETHER_BRICKS_BREAK = SoundEventImpl.get("minecraft:block.nether_bricks.break"); + SoundEvent MUSIC_DISC_11 = BuiltinSoundEvent.get("minecraft:music_disc.11"); - SoundEvent BLOCK_NETHER_BRICKS_STEP = SoundEventImpl.get("minecraft:block.nether_bricks.step"); + SoundEvent MUSIC_DISC_13 = BuiltinSoundEvent.get("minecraft:music_disc.13"); - SoundEvent BLOCK_NETHER_BRICKS_PLACE = SoundEventImpl.get("minecraft:block.nether_bricks.place"); + SoundEvent MUSIC_DISC_BLOCKS = BuiltinSoundEvent.get("minecraft:music_disc.blocks"); - SoundEvent BLOCK_NETHER_BRICKS_HIT = SoundEventImpl.get("minecraft:block.nether_bricks.hit"); + SoundEvent MUSIC_DISC_CAT = BuiltinSoundEvent.get("minecraft:music_disc.cat"); - SoundEvent BLOCK_NETHER_BRICKS_FALL = SoundEventImpl.get("minecraft:block.nether_bricks.fall"); + SoundEvent MUSIC_DISC_CHIRP = BuiltinSoundEvent.get("minecraft:music_disc.chirp"); - SoundEvent BLOCK_NETHER_WART_BREAK = SoundEventImpl.get("minecraft:block.nether_wart.break"); + SoundEvent MUSIC_DISC_FAR = BuiltinSoundEvent.get("minecraft:music_disc.far"); - SoundEvent ITEM_NETHER_WART_PLANT = SoundEventImpl.get("minecraft:item.nether_wart.plant"); + SoundEvent MUSIC_DISC_MALL = BuiltinSoundEvent.get("minecraft:music_disc.mall"); - SoundEvent BLOCK_NETHER_WOOD_BREAK = SoundEventImpl.get("minecraft:block.nether_wood.break"); + SoundEvent MUSIC_DISC_MELLOHI = BuiltinSoundEvent.get("minecraft:music_disc.mellohi"); - SoundEvent BLOCK_NETHER_WOOD_FALL = SoundEventImpl.get("minecraft:block.nether_wood.fall"); + SoundEvent MUSIC_DISC_PIGSTEP = BuiltinSoundEvent.get("minecraft:music_disc.pigstep"); - SoundEvent BLOCK_NETHER_WOOD_HIT = SoundEventImpl.get("minecraft:block.nether_wood.hit"); + SoundEvent MUSIC_DISC_STAL = BuiltinSoundEvent.get("minecraft:music_disc.stal"); - SoundEvent BLOCK_NETHER_WOOD_PLACE = SoundEventImpl.get("minecraft:block.nether_wood.place"); + SoundEvent MUSIC_DISC_STRAD = BuiltinSoundEvent.get("minecraft:music_disc.strad"); - SoundEvent BLOCK_NETHER_WOOD_STEP = SoundEventImpl.get("minecraft:block.nether_wood.step"); + SoundEvent MUSIC_DISC_WAIT = BuiltinSoundEvent.get("minecraft:music_disc.wait"); - SoundEvent BLOCK_NETHER_WOOD_DOOR_CLOSE = SoundEventImpl.get("minecraft:block.nether_wood_door.close"); + SoundEvent MUSIC_DISC_WARD = BuiltinSoundEvent.get("minecraft:music_disc.ward"); - SoundEvent BLOCK_NETHER_WOOD_DOOR_OPEN = SoundEventImpl.get("minecraft:block.nether_wood_door.open"); + SoundEvent MUSIC_DISC_OTHERSIDE = BuiltinSoundEvent.get("minecraft:music_disc.otherside"); - SoundEvent BLOCK_NETHER_WOOD_TRAPDOOR_CLOSE = SoundEventImpl.get("minecraft:block.nether_wood_trapdoor.close"); + SoundEvent MUSIC_DISC_RELIC = BuiltinSoundEvent.get("minecraft:music_disc.relic"); - SoundEvent BLOCK_NETHER_WOOD_TRAPDOOR_OPEN = SoundEventImpl.get("minecraft:block.nether_wood_trapdoor.open"); + SoundEvent MUSIC_DISC_CREATOR = BuiltinSoundEvent.get("minecraft:music_disc.creator"); - SoundEvent BLOCK_NETHER_WOOD_BUTTON_CLICK_OFF = SoundEventImpl.get("minecraft:block.nether_wood_button.click_off"); + SoundEvent MUSIC_DISC_CREATOR_MUSIC_BOX = BuiltinSoundEvent.get("minecraft:music_disc.creator_music_box"); - SoundEvent BLOCK_NETHER_WOOD_BUTTON_CLICK_ON = SoundEventImpl.get("minecraft:block.nether_wood_button.click_on"); + SoundEvent MUSIC_DISC_PRECIPICE = BuiltinSoundEvent.get("minecraft:music_disc.precipice"); - SoundEvent BLOCK_NETHER_WOOD_PRESSURE_PLATE_CLICK_OFF = SoundEventImpl.get("minecraft:block.nether_wood_pressure_plate.click_off"); + SoundEvent MUSIC_DRAGON = BuiltinSoundEvent.get("minecraft:music.dragon"); - SoundEvent BLOCK_NETHER_WOOD_PRESSURE_PLATE_CLICK_ON = SoundEventImpl.get("minecraft:block.nether_wood_pressure_plate.click_on"); + SoundEvent MUSIC_END = BuiltinSoundEvent.get("minecraft:music.end"); - SoundEvent BLOCK_NETHER_WOOD_FENCE_GATE_CLOSE = SoundEventImpl.get("minecraft:block.nether_wood_fence_gate.close"); + SoundEvent MUSIC_GAME = BuiltinSoundEvent.get("minecraft:music.game"); - SoundEvent BLOCK_NETHER_WOOD_FENCE_GATE_OPEN = SoundEventImpl.get("minecraft:block.nether_wood_fence_gate.open"); + SoundEvent MUSIC_MENU = BuiltinSoundEvent.get("minecraft:music.menu"); - SoundEvent INTENTIONALLY_EMPTY = SoundEventImpl.get("minecraft:intentionally_empty"); + SoundEvent MUSIC_NETHER_BASALT_DELTAS = BuiltinSoundEvent.get("minecraft:music.nether.basalt_deltas"); - SoundEvent BLOCK_PACKED_MUD_BREAK = SoundEventImpl.get("minecraft:block.packed_mud.break"); + SoundEvent MUSIC_NETHER_CRIMSON_FOREST = BuiltinSoundEvent.get("minecraft:music.nether.crimson_forest"); - SoundEvent BLOCK_PACKED_MUD_FALL = SoundEventImpl.get("minecraft:block.packed_mud.fall"); + SoundEvent MUSIC_OVERWORLD_DEEP_DARK = BuiltinSoundEvent.get("minecraft:music.overworld.deep_dark"); - SoundEvent BLOCK_PACKED_MUD_HIT = SoundEventImpl.get("minecraft:block.packed_mud.hit"); + SoundEvent MUSIC_OVERWORLD_DRIPSTONE_CAVES = BuiltinSoundEvent.get("minecraft:music.overworld.dripstone_caves"); - SoundEvent BLOCK_PACKED_MUD_PLACE = SoundEventImpl.get("minecraft:block.packed_mud.place"); + SoundEvent MUSIC_OVERWORLD_GROVE = BuiltinSoundEvent.get("minecraft:music.overworld.grove"); - SoundEvent BLOCK_PACKED_MUD_STEP = SoundEventImpl.get("minecraft:block.packed_mud.step"); + SoundEvent MUSIC_OVERWORLD_JAGGED_PEAKS = BuiltinSoundEvent.get("minecraft:music.overworld.jagged_peaks"); - SoundEvent BLOCK_STEM_BREAK = SoundEventImpl.get("minecraft:block.stem.break"); + SoundEvent MUSIC_OVERWORLD_LUSH_CAVES = BuiltinSoundEvent.get("minecraft:music.overworld.lush_caves"); - SoundEvent BLOCK_STEM_STEP = SoundEventImpl.get("minecraft:block.stem.step"); + SoundEvent MUSIC_OVERWORLD_SWAMP = BuiltinSoundEvent.get("minecraft:music.overworld.swamp"); - SoundEvent BLOCK_STEM_PLACE = SoundEventImpl.get("minecraft:block.stem.place"); + SoundEvent MUSIC_OVERWORLD_FOREST = BuiltinSoundEvent.get("minecraft:music.overworld.forest"); - SoundEvent BLOCK_STEM_HIT = SoundEventImpl.get("minecraft:block.stem.hit"); + SoundEvent MUSIC_OVERWORLD_OLD_GROWTH_TAIGA = BuiltinSoundEvent.get("minecraft:music.overworld.old_growth_taiga"); - SoundEvent BLOCK_STEM_FALL = SoundEventImpl.get("minecraft:block.stem.fall"); + SoundEvent MUSIC_OVERWORLD_MEADOW = BuiltinSoundEvent.get("minecraft:music.overworld.meadow"); - SoundEvent BLOCK_NYLIUM_BREAK = SoundEventImpl.get("minecraft:block.nylium.break"); + SoundEvent MUSIC_OVERWORLD_CHERRY_GROVE = BuiltinSoundEvent.get("minecraft:music.overworld.cherry_grove"); - SoundEvent BLOCK_NYLIUM_STEP = SoundEventImpl.get("minecraft:block.nylium.step"); + SoundEvent MUSIC_NETHER_NETHER_WASTES = BuiltinSoundEvent.get("minecraft:music.nether.nether_wastes"); - SoundEvent BLOCK_NYLIUM_PLACE = SoundEventImpl.get("minecraft:block.nylium.place"); + SoundEvent MUSIC_OVERWORLD_FROZEN_PEAKS = BuiltinSoundEvent.get("minecraft:music.overworld.frozen_peaks"); - SoundEvent BLOCK_NYLIUM_HIT = SoundEventImpl.get("minecraft:block.nylium.hit"); + SoundEvent MUSIC_OVERWORLD_SNOWY_SLOPES = BuiltinSoundEvent.get("minecraft:music.overworld.snowy_slopes"); - SoundEvent BLOCK_NYLIUM_FALL = SoundEventImpl.get("minecraft:block.nylium.fall"); + SoundEvent MUSIC_NETHER_SOUL_SAND_VALLEY = BuiltinSoundEvent.get("minecraft:music.nether.soul_sand_valley"); - SoundEvent BLOCK_NETHER_SPROUTS_BREAK = SoundEventImpl.get("minecraft:block.nether_sprouts.break"); + SoundEvent MUSIC_OVERWORLD_STONY_PEAKS = BuiltinSoundEvent.get("minecraft:music.overworld.stony_peaks"); - SoundEvent BLOCK_NETHER_SPROUTS_STEP = SoundEventImpl.get("minecraft:block.nether_sprouts.step"); + SoundEvent MUSIC_NETHER_WARPED_FOREST = BuiltinSoundEvent.get("minecraft:music.nether.warped_forest"); - SoundEvent BLOCK_NETHER_SPROUTS_PLACE = SoundEventImpl.get("minecraft:block.nether_sprouts.place"); + SoundEvent MUSIC_OVERWORLD_FLOWER_FOREST = BuiltinSoundEvent.get("minecraft:music.overworld.flower_forest"); - SoundEvent BLOCK_NETHER_SPROUTS_HIT = SoundEventImpl.get("minecraft:block.nether_sprouts.hit"); + SoundEvent MUSIC_OVERWORLD_DESERT = BuiltinSoundEvent.get("minecraft:music.overworld.desert"); - SoundEvent BLOCK_NETHER_SPROUTS_FALL = SoundEventImpl.get("minecraft:block.nether_sprouts.fall"); + SoundEvent MUSIC_OVERWORLD_BADLANDS = BuiltinSoundEvent.get("minecraft:music.overworld.badlands"); - SoundEvent BLOCK_FUNGUS_BREAK = SoundEventImpl.get("minecraft:block.fungus.break"); + SoundEvent MUSIC_OVERWORLD_JUNGLE = BuiltinSoundEvent.get("minecraft:music.overworld.jungle"); - SoundEvent BLOCK_FUNGUS_STEP = SoundEventImpl.get("minecraft:block.fungus.step"); + SoundEvent MUSIC_OVERWORLD_SPARSE_JUNGLE = BuiltinSoundEvent.get("minecraft:music.overworld.sparse_jungle"); - SoundEvent BLOCK_FUNGUS_PLACE = SoundEventImpl.get("minecraft:block.fungus.place"); + SoundEvent MUSIC_OVERWORLD_BAMBOO_JUNGLE = BuiltinSoundEvent.get("minecraft:music.overworld.bamboo_jungle"); - SoundEvent BLOCK_FUNGUS_HIT = SoundEventImpl.get("minecraft:block.fungus.hit"); + SoundEvent MUSIC_UNDER_WATER = BuiltinSoundEvent.get("minecraft:music.under_water"); - SoundEvent BLOCK_FUNGUS_FALL = SoundEventImpl.get("minecraft:block.fungus.fall"); + SoundEvent BLOCK_NETHER_BRICKS_BREAK = BuiltinSoundEvent.get("minecraft:block.nether_bricks.break"); - SoundEvent BLOCK_WEEPING_VINES_BREAK = SoundEventImpl.get("minecraft:block.weeping_vines.break"); + SoundEvent BLOCK_NETHER_BRICKS_STEP = BuiltinSoundEvent.get("minecraft:block.nether_bricks.step"); - SoundEvent BLOCK_WEEPING_VINES_STEP = SoundEventImpl.get("minecraft:block.weeping_vines.step"); + SoundEvent BLOCK_NETHER_BRICKS_PLACE = BuiltinSoundEvent.get("minecraft:block.nether_bricks.place"); - SoundEvent BLOCK_WEEPING_VINES_PLACE = SoundEventImpl.get("minecraft:block.weeping_vines.place"); + SoundEvent BLOCK_NETHER_BRICKS_HIT = BuiltinSoundEvent.get("minecraft:block.nether_bricks.hit"); - SoundEvent BLOCK_WEEPING_VINES_HIT = SoundEventImpl.get("minecraft:block.weeping_vines.hit"); + SoundEvent BLOCK_NETHER_BRICKS_FALL = BuiltinSoundEvent.get("minecraft:block.nether_bricks.fall"); - SoundEvent BLOCK_WEEPING_VINES_FALL = SoundEventImpl.get("minecraft:block.weeping_vines.fall"); + SoundEvent BLOCK_NETHER_WART_BREAK = BuiltinSoundEvent.get("minecraft:block.nether_wart.break"); - SoundEvent BLOCK_WART_BLOCK_BREAK = SoundEventImpl.get("minecraft:block.wart_block.break"); + SoundEvent ITEM_NETHER_WART_PLANT = BuiltinSoundEvent.get("minecraft:item.nether_wart.plant"); - SoundEvent BLOCK_WART_BLOCK_STEP = SoundEventImpl.get("minecraft:block.wart_block.step"); + SoundEvent BLOCK_NETHER_WOOD_BREAK = BuiltinSoundEvent.get("minecraft:block.nether_wood.break"); - SoundEvent BLOCK_WART_BLOCK_PLACE = SoundEventImpl.get("minecraft:block.wart_block.place"); + SoundEvent BLOCK_NETHER_WOOD_FALL = BuiltinSoundEvent.get("minecraft:block.nether_wood.fall"); - SoundEvent BLOCK_WART_BLOCK_HIT = SoundEventImpl.get("minecraft:block.wart_block.hit"); + SoundEvent BLOCK_NETHER_WOOD_HIT = BuiltinSoundEvent.get("minecraft:block.nether_wood.hit"); - SoundEvent BLOCK_WART_BLOCK_FALL = SoundEventImpl.get("minecraft:block.wart_block.fall"); + SoundEvent BLOCK_NETHER_WOOD_PLACE = BuiltinSoundEvent.get("minecraft:block.nether_wood.place"); - SoundEvent BLOCK_NETHERITE_BLOCK_BREAK = SoundEventImpl.get("minecraft:block.netherite_block.break"); + SoundEvent BLOCK_NETHER_WOOD_STEP = BuiltinSoundEvent.get("minecraft:block.nether_wood.step"); - SoundEvent BLOCK_NETHERITE_BLOCK_STEP = SoundEventImpl.get("minecraft:block.netherite_block.step"); + SoundEvent BLOCK_NETHER_WOOD_DOOR_CLOSE = BuiltinSoundEvent.get("minecraft:block.nether_wood_door.close"); - SoundEvent BLOCK_NETHERITE_BLOCK_PLACE = SoundEventImpl.get("minecraft:block.netherite_block.place"); + SoundEvent BLOCK_NETHER_WOOD_DOOR_OPEN = BuiltinSoundEvent.get("minecraft:block.nether_wood_door.open"); - SoundEvent BLOCK_NETHERITE_BLOCK_HIT = SoundEventImpl.get("minecraft:block.netherite_block.hit"); + SoundEvent BLOCK_NETHER_WOOD_TRAPDOOR_CLOSE = BuiltinSoundEvent.get("minecraft:block.nether_wood_trapdoor.close"); - SoundEvent BLOCK_NETHERITE_BLOCK_FALL = SoundEventImpl.get("minecraft:block.netherite_block.fall"); + SoundEvent BLOCK_NETHER_WOOD_TRAPDOOR_OPEN = BuiltinSoundEvent.get("minecraft:block.nether_wood_trapdoor.open"); - SoundEvent BLOCK_NETHERRACK_BREAK = SoundEventImpl.get("minecraft:block.netherrack.break"); + SoundEvent BLOCK_NETHER_WOOD_BUTTON_CLICK_OFF = BuiltinSoundEvent.get("minecraft:block.nether_wood_button.click_off"); - SoundEvent BLOCK_NETHERRACK_STEP = SoundEventImpl.get("minecraft:block.netherrack.step"); + SoundEvent BLOCK_NETHER_WOOD_BUTTON_CLICK_ON = BuiltinSoundEvent.get("minecraft:block.nether_wood_button.click_on"); - SoundEvent BLOCK_NETHERRACK_PLACE = SoundEventImpl.get("minecraft:block.netherrack.place"); + SoundEvent BLOCK_NETHER_WOOD_PRESSURE_PLATE_CLICK_OFF = BuiltinSoundEvent.get("minecraft:block.nether_wood_pressure_plate.click_off"); - SoundEvent BLOCK_NETHERRACK_HIT = SoundEventImpl.get("minecraft:block.netherrack.hit"); + SoundEvent BLOCK_NETHER_WOOD_PRESSURE_PLATE_CLICK_ON = BuiltinSoundEvent.get("minecraft:block.nether_wood_pressure_plate.click_on"); - SoundEvent BLOCK_NETHERRACK_FALL = SoundEventImpl.get("minecraft:block.netherrack.fall"); + SoundEvent BLOCK_NETHER_WOOD_FENCE_GATE_CLOSE = BuiltinSoundEvent.get("minecraft:block.nether_wood_fence_gate.close"); - SoundEvent BLOCK_NOTE_BLOCK_BASEDRUM = SoundEventImpl.get("minecraft:block.note_block.basedrum"); + SoundEvent BLOCK_NETHER_WOOD_FENCE_GATE_OPEN = BuiltinSoundEvent.get("minecraft:block.nether_wood_fence_gate.open"); - SoundEvent BLOCK_NOTE_BLOCK_BASS = SoundEventImpl.get("minecraft:block.note_block.bass"); + SoundEvent INTENTIONALLY_EMPTY = BuiltinSoundEvent.get("minecraft:intentionally_empty"); - SoundEvent BLOCK_NOTE_BLOCK_BELL = SoundEventImpl.get("minecraft:block.note_block.bell"); + SoundEvent BLOCK_PACKED_MUD_BREAK = BuiltinSoundEvent.get("minecraft:block.packed_mud.break"); - SoundEvent BLOCK_NOTE_BLOCK_CHIME = SoundEventImpl.get("minecraft:block.note_block.chime"); + SoundEvent BLOCK_PACKED_MUD_FALL = BuiltinSoundEvent.get("minecraft:block.packed_mud.fall"); - SoundEvent BLOCK_NOTE_BLOCK_FLUTE = SoundEventImpl.get("minecraft:block.note_block.flute"); + SoundEvent BLOCK_PACKED_MUD_HIT = BuiltinSoundEvent.get("minecraft:block.packed_mud.hit"); - SoundEvent BLOCK_NOTE_BLOCK_GUITAR = SoundEventImpl.get("minecraft:block.note_block.guitar"); + SoundEvent BLOCK_PACKED_MUD_PLACE = BuiltinSoundEvent.get("minecraft:block.packed_mud.place"); - SoundEvent BLOCK_NOTE_BLOCK_HARP = SoundEventImpl.get("minecraft:block.note_block.harp"); + SoundEvent BLOCK_PACKED_MUD_STEP = BuiltinSoundEvent.get("minecraft:block.packed_mud.step"); - SoundEvent BLOCK_NOTE_BLOCK_HAT = SoundEventImpl.get("minecraft:block.note_block.hat"); + SoundEvent BLOCK_STEM_BREAK = BuiltinSoundEvent.get("minecraft:block.stem.break"); - SoundEvent BLOCK_NOTE_BLOCK_PLING = SoundEventImpl.get("minecraft:block.note_block.pling"); + SoundEvent BLOCK_STEM_STEP = BuiltinSoundEvent.get("minecraft:block.stem.step"); - SoundEvent BLOCK_NOTE_BLOCK_SNARE = SoundEventImpl.get("minecraft:block.note_block.snare"); + SoundEvent BLOCK_STEM_PLACE = BuiltinSoundEvent.get("minecraft:block.stem.place"); - SoundEvent BLOCK_NOTE_BLOCK_XYLOPHONE = SoundEventImpl.get("minecraft:block.note_block.xylophone"); + SoundEvent BLOCK_STEM_HIT = BuiltinSoundEvent.get("minecraft:block.stem.hit"); - SoundEvent BLOCK_NOTE_BLOCK_IRON_XYLOPHONE = SoundEventImpl.get("minecraft:block.note_block.iron_xylophone"); + SoundEvent BLOCK_STEM_FALL = BuiltinSoundEvent.get("minecraft:block.stem.fall"); - SoundEvent BLOCK_NOTE_BLOCK_COW_BELL = SoundEventImpl.get("minecraft:block.note_block.cow_bell"); + SoundEvent BLOCK_NYLIUM_BREAK = BuiltinSoundEvent.get("minecraft:block.nylium.break"); - SoundEvent BLOCK_NOTE_BLOCK_DIDGERIDOO = SoundEventImpl.get("minecraft:block.note_block.didgeridoo"); + SoundEvent BLOCK_NYLIUM_STEP = BuiltinSoundEvent.get("minecraft:block.nylium.step"); - SoundEvent BLOCK_NOTE_BLOCK_BIT = SoundEventImpl.get("minecraft:block.note_block.bit"); + SoundEvent BLOCK_NYLIUM_PLACE = BuiltinSoundEvent.get("minecraft:block.nylium.place"); - SoundEvent BLOCK_NOTE_BLOCK_BANJO = SoundEventImpl.get("minecraft:block.note_block.banjo"); + SoundEvent BLOCK_NYLIUM_HIT = BuiltinSoundEvent.get("minecraft:block.nylium.hit"); - SoundEvent BLOCK_NOTE_BLOCK_IMITATE_ZOMBIE = SoundEventImpl.get("minecraft:block.note_block.imitate.zombie"); + SoundEvent BLOCK_NYLIUM_FALL = BuiltinSoundEvent.get("minecraft:block.nylium.fall"); - SoundEvent BLOCK_NOTE_BLOCK_IMITATE_SKELETON = SoundEventImpl.get("minecraft:block.note_block.imitate.skeleton"); + SoundEvent BLOCK_NETHER_SPROUTS_BREAK = BuiltinSoundEvent.get("minecraft:block.nether_sprouts.break"); - SoundEvent BLOCK_NOTE_BLOCK_IMITATE_CREEPER = SoundEventImpl.get("minecraft:block.note_block.imitate.creeper"); + SoundEvent BLOCK_NETHER_SPROUTS_STEP = BuiltinSoundEvent.get("minecraft:block.nether_sprouts.step"); - SoundEvent BLOCK_NOTE_BLOCK_IMITATE_ENDER_DRAGON = SoundEventImpl.get("minecraft:block.note_block.imitate.ender_dragon"); + SoundEvent BLOCK_NETHER_SPROUTS_PLACE = BuiltinSoundEvent.get("minecraft:block.nether_sprouts.place"); - SoundEvent BLOCK_NOTE_BLOCK_IMITATE_WITHER_SKELETON = SoundEventImpl.get("minecraft:block.note_block.imitate.wither_skeleton"); + SoundEvent BLOCK_NETHER_SPROUTS_HIT = BuiltinSoundEvent.get("minecraft:block.nether_sprouts.hit"); - SoundEvent BLOCK_NOTE_BLOCK_IMITATE_PIGLIN = SoundEventImpl.get("minecraft:block.note_block.imitate.piglin"); + SoundEvent BLOCK_NETHER_SPROUTS_FALL = BuiltinSoundEvent.get("minecraft:block.nether_sprouts.fall"); - SoundEvent ENTITY_OCELOT_HURT = SoundEventImpl.get("minecraft:entity.ocelot.hurt"); + SoundEvent BLOCK_FUNGUS_BREAK = BuiltinSoundEvent.get("minecraft:block.fungus.break"); - SoundEvent ENTITY_OCELOT_AMBIENT = SoundEventImpl.get("minecraft:entity.ocelot.ambient"); + SoundEvent BLOCK_FUNGUS_STEP = BuiltinSoundEvent.get("minecraft:block.fungus.step"); - SoundEvent ENTITY_OCELOT_DEATH = SoundEventImpl.get("minecraft:entity.ocelot.death"); + SoundEvent BLOCK_FUNGUS_PLACE = BuiltinSoundEvent.get("minecraft:block.fungus.place"); - SoundEvent ENTITY_PAINTING_BREAK = SoundEventImpl.get("minecraft:entity.painting.break"); + SoundEvent BLOCK_FUNGUS_HIT = BuiltinSoundEvent.get("minecraft:block.fungus.hit"); - SoundEvent ENTITY_PAINTING_PLACE = SoundEventImpl.get("minecraft:entity.painting.place"); + SoundEvent BLOCK_FUNGUS_FALL = BuiltinSoundEvent.get("minecraft:block.fungus.fall"); - SoundEvent ENTITY_PANDA_PRE_SNEEZE = SoundEventImpl.get("minecraft:entity.panda.pre_sneeze"); + SoundEvent BLOCK_WEEPING_VINES_BREAK = BuiltinSoundEvent.get("minecraft:block.weeping_vines.break"); - SoundEvent ENTITY_PANDA_SNEEZE = SoundEventImpl.get("minecraft:entity.panda.sneeze"); + SoundEvent BLOCK_WEEPING_VINES_STEP = BuiltinSoundEvent.get("minecraft:block.weeping_vines.step"); - SoundEvent ENTITY_PANDA_AMBIENT = SoundEventImpl.get("minecraft:entity.panda.ambient"); + SoundEvent BLOCK_WEEPING_VINES_PLACE = BuiltinSoundEvent.get("minecraft:block.weeping_vines.place"); - SoundEvent ENTITY_PANDA_DEATH = SoundEventImpl.get("minecraft:entity.panda.death"); + SoundEvent BLOCK_WEEPING_VINES_HIT = BuiltinSoundEvent.get("minecraft:block.weeping_vines.hit"); - SoundEvent ENTITY_PANDA_EAT = SoundEventImpl.get("minecraft:entity.panda.eat"); + SoundEvent BLOCK_WEEPING_VINES_FALL = BuiltinSoundEvent.get("minecraft:block.weeping_vines.fall"); - SoundEvent ENTITY_PANDA_STEP = SoundEventImpl.get("minecraft:entity.panda.step"); + SoundEvent BLOCK_WART_BLOCK_BREAK = BuiltinSoundEvent.get("minecraft:block.wart_block.break"); - SoundEvent ENTITY_PANDA_CANT_BREED = SoundEventImpl.get("minecraft:entity.panda.cant_breed"); + SoundEvent BLOCK_WART_BLOCK_STEP = BuiltinSoundEvent.get("minecraft:block.wart_block.step"); - SoundEvent ENTITY_PANDA_AGGRESSIVE_AMBIENT = SoundEventImpl.get("minecraft:entity.panda.aggressive_ambient"); + SoundEvent BLOCK_WART_BLOCK_PLACE = BuiltinSoundEvent.get("minecraft:block.wart_block.place"); - SoundEvent ENTITY_PANDA_WORRIED_AMBIENT = SoundEventImpl.get("minecraft:entity.panda.worried_ambient"); + SoundEvent BLOCK_WART_BLOCK_HIT = BuiltinSoundEvent.get("minecraft:block.wart_block.hit"); - SoundEvent ENTITY_PANDA_HURT = SoundEventImpl.get("minecraft:entity.panda.hurt"); + SoundEvent BLOCK_WART_BLOCK_FALL = BuiltinSoundEvent.get("minecraft:block.wart_block.fall"); - SoundEvent ENTITY_PANDA_BITE = SoundEventImpl.get("minecraft:entity.panda.bite"); + SoundEvent BLOCK_NETHERITE_BLOCK_BREAK = BuiltinSoundEvent.get("minecraft:block.netherite_block.break"); - SoundEvent ENTITY_PARROT_AMBIENT = SoundEventImpl.get("minecraft:entity.parrot.ambient"); + SoundEvent BLOCK_NETHERITE_BLOCK_STEP = BuiltinSoundEvent.get("minecraft:block.netherite_block.step"); - SoundEvent ENTITY_PARROT_DEATH = SoundEventImpl.get("minecraft:entity.parrot.death"); + SoundEvent BLOCK_NETHERITE_BLOCK_PLACE = BuiltinSoundEvent.get("minecraft:block.netherite_block.place"); - SoundEvent ENTITY_PARROT_EAT = SoundEventImpl.get("minecraft:entity.parrot.eat"); + SoundEvent BLOCK_NETHERITE_BLOCK_HIT = BuiltinSoundEvent.get("minecraft:block.netherite_block.hit"); - SoundEvent ENTITY_PARROT_FLY = SoundEventImpl.get("minecraft:entity.parrot.fly"); + SoundEvent BLOCK_NETHERITE_BLOCK_FALL = BuiltinSoundEvent.get("minecraft:block.netherite_block.fall"); - SoundEvent ENTITY_PARROT_HURT = SoundEventImpl.get("minecraft:entity.parrot.hurt"); + SoundEvent BLOCK_NETHERRACK_BREAK = BuiltinSoundEvent.get("minecraft:block.netherrack.break"); - SoundEvent ENTITY_PARROT_IMITATE_BLAZE = SoundEventImpl.get("minecraft:entity.parrot.imitate.blaze"); + SoundEvent BLOCK_NETHERRACK_STEP = BuiltinSoundEvent.get("minecraft:block.netherrack.step"); - SoundEvent ENTITY_PARROT_IMITATE_BREEZE = SoundEventImpl.get("minecraft:entity.parrot.imitate.breeze"); + SoundEvent BLOCK_NETHERRACK_PLACE = BuiltinSoundEvent.get("minecraft:block.netherrack.place"); - SoundEvent ENTITY_PARROT_IMITATE_CREEPER = SoundEventImpl.get("minecraft:entity.parrot.imitate.creeper"); + SoundEvent BLOCK_NETHERRACK_HIT = BuiltinSoundEvent.get("minecraft:block.netherrack.hit"); - SoundEvent ENTITY_PARROT_IMITATE_DROWNED = SoundEventImpl.get("minecraft:entity.parrot.imitate.drowned"); + SoundEvent BLOCK_NETHERRACK_FALL = BuiltinSoundEvent.get("minecraft:block.netherrack.fall"); - SoundEvent ENTITY_PARROT_IMITATE_ELDER_GUARDIAN = SoundEventImpl.get("minecraft:entity.parrot.imitate.elder_guardian"); + SoundEvent BLOCK_NOTE_BLOCK_BASEDRUM = BuiltinSoundEvent.get("minecraft:block.note_block.basedrum"); - SoundEvent ENTITY_PARROT_IMITATE_ENDER_DRAGON = SoundEventImpl.get("minecraft:entity.parrot.imitate.ender_dragon"); + SoundEvent BLOCK_NOTE_BLOCK_BASS = BuiltinSoundEvent.get("minecraft:block.note_block.bass"); - SoundEvent ENTITY_PARROT_IMITATE_ENDERMITE = SoundEventImpl.get("minecraft:entity.parrot.imitate.endermite"); + SoundEvent BLOCK_NOTE_BLOCK_BELL = BuiltinSoundEvent.get("minecraft:block.note_block.bell"); - SoundEvent ENTITY_PARROT_IMITATE_EVOKER = SoundEventImpl.get("minecraft:entity.parrot.imitate.evoker"); + SoundEvent BLOCK_NOTE_BLOCK_CHIME = BuiltinSoundEvent.get("minecraft:block.note_block.chime"); - SoundEvent ENTITY_PARROT_IMITATE_GHAST = SoundEventImpl.get("minecraft:entity.parrot.imitate.ghast"); + SoundEvent BLOCK_NOTE_BLOCK_FLUTE = BuiltinSoundEvent.get("minecraft:block.note_block.flute"); - SoundEvent ENTITY_PARROT_IMITATE_GUARDIAN = SoundEventImpl.get("minecraft:entity.parrot.imitate.guardian"); + SoundEvent BLOCK_NOTE_BLOCK_GUITAR = BuiltinSoundEvent.get("minecraft:block.note_block.guitar"); - SoundEvent ENTITY_PARROT_IMITATE_HOGLIN = SoundEventImpl.get("minecraft:entity.parrot.imitate.hoglin"); + SoundEvent BLOCK_NOTE_BLOCK_HARP = BuiltinSoundEvent.get("minecraft:block.note_block.harp"); - SoundEvent ENTITY_PARROT_IMITATE_HUSK = SoundEventImpl.get("minecraft:entity.parrot.imitate.husk"); + SoundEvent BLOCK_NOTE_BLOCK_HAT = BuiltinSoundEvent.get("minecraft:block.note_block.hat"); - SoundEvent ENTITY_PARROT_IMITATE_ILLUSIONER = SoundEventImpl.get("minecraft:entity.parrot.imitate.illusioner"); + SoundEvent BLOCK_NOTE_BLOCK_PLING = BuiltinSoundEvent.get("minecraft:block.note_block.pling"); - SoundEvent ENTITY_PARROT_IMITATE_MAGMA_CUBE = SoundEventImpl.get("minecraft:entity.parrot.imitate.magma_cube"); + SoundEvent BLOCK_NOTE_BLOCK_SNARE = BuiltinSoundEvent.get("minecraft:block.note_block.snare"); - SoundEvent ENTITY_PARROT_IMITATE_PHANTOM = SoundEventImpl.get("minecraft:entity.parrot.imitate.phantom"); + SoundEvent BLOCK_NOTE_BLOCK_XYLOPHONE = BuiltinSoundEvent.get("minecraft:block.note_block.xylophone"); - SoundEvent ENTITY_PARROT_IMITATE_PIGLIN = SoundEventImpl.get("minecraft:entity.parrot.imitate.piglin"); + SoundEvent BLOCK_NOTE_BLOCK_IRON_XYLOPHONE = BuiltinSoundEvent.get("minecraft:block.note_block.iron_xylophone"); - SoundEvent ENTITY_PARROT_IMITATE_PIGLIN_BRUTE = SoundEventImpl.get("minecraft:entity.parrot.imitate.piglin_brute"); + SoundEvent BLOCK_NOTE_BLOCK_COW_BELL = BuiltinSoundEvent.get("minecraft:block.note_block.cow_bell"); - SoundEvent ENTITY_PARROT_IMITATE_PILLAGER = SoundEventImpl.get("minecraft:entity.parrot.imitate.pillager"); + SoundEvent BLOCK_NOTE_BLOCK_DIDGERIDOO = BuiltinSoundEvent.get("minecraft:block.note_block.didgeridoo"); - SoundEvent ENTITY_PARROT_IMITATE_RAVAGER = SoundEventImpl.get("minecraft:entity.parrot.imitate.ravager"); + SoundEvent BLOCK_NOTE_BLOCK_BIT = BuiltinSoundEvent.get("minecraft:block.note_block.bit"); - SoundEvent ENTITY_PARROT_IMITATE_SHULKER = SoundEventImpl.get("minecraft:entity.parrot.imitate.shulker"); + SoundEvent BLOCK_NOTE_BLOCK_BANJO = BuiltinSoundEvent.get("minecraft:block.note_block.banjo"); - SoundEvent ENTITY_PARROT_IMITATE_SILVERFISH = SoundEventImpl.get("minecraft:entity.parrot.imitate.silverfish"); + SoundEvent BLOCK_NOTE_BLOCK_IMITATE_ZOMBIE = BuiltinSoundEvent.get("minecraft:block.note_block.imitate.zombie"); - SoundEvent ENTITY_PARROT_IMITATE_SKELETON = SoundEventImpl.get("minecraft:entity.parrot.imitate.skeleton"); + SoundEvent BLOCK_NOTE_BLOCK_IMITATE_SKELETON = BuiltinSoundEvent.get("minecraft:block.note_block.imitate.skeleton"); - SoundEvent ENTITY_PARROT_IMITATE_SLIME = SoundEventImpl.get("minecraft:entity.parrot.imitate.slime"); + SoundEvent BLOCK_NOTE_BLOCK_IMITATE_CREEPER = BuiltinSoundEvent.get("minecraft:block.note_block.imitate.creeper"); - SoundEvent ENTITY_PARROT_IMITATE_SPIDER = SoundEventImpl.get("minecraft:entity.parrot.imitate.spider"); + SoundEvent BLOCK_NOTE_BLOCK_IMITATE_ENDER_DRAGON = BuiltinSoundEvent.get("minecraft:block.note_block.imitate.ender_dragon"); - SoundEvent ENTITY_PARROT_IMITATE_STRAY = SoundEventImpl.get("minecraft:entity.parrot.imitate.stray"); + SoundEvent BLOCK_NOTE_BLOCK_IMITATE_WITHER_SKELETON = BuiltinSoundEvent.get("minecraft:block.note_block.imitate.wither_skeleton"); - SoundEvent ENTITY_PARROT_IMITATE_VEX = SoundEventImpl.get("minecraft:entity.parrot.imitate.vex"); + SoundEvent BLOCK_NOTE_BLOCK_IMITATE_PIGLIN = BuiltinSoundEvent.get("minecraft:block.note_block.imitate.piglin"); - SoundEvent ENTITY_PARROT_IMITATE_VINDICATOR = SoundEventImpl.get("minecraft:entity.parrot.imitate.vindicator"); + SoundEvent ENTITY_OCELOT_HURT = BuiltinSoundEvent.get("minecraft:entity.ocelot.hurt"); - SoundEvent ENTITY_PARROT_IMITATE_WARDEN = SoundEventImpl.get("minecraft:entity.parrot.imitate.warden"); + SoundEvent ENTITY_OCELOT_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.ocelot.ambient"); - SoundEvent ENTITY_PARROT_IMITATE_WITCH = SoundEventImpl.get("minecraft:entity.parrot.imitate.witch"); + SoundEvent ENTITY_OCELOT_DEATH = BuiltinSoundEvent.get("minecraft:entity.ocelot.death"); - SoundEvent ENTITY_PARROT_IMITATE_WITHER = SoundEventImpl.get("minecraft:entity.parrot.imitate.wither"); + SoundEvent ITEM_OMINOUS_BOTTLE_DISPOSE = BuiltinSoundEvent.get("minecraft:item.ominous_bottle.dispose"); - SoundEvent ENTITY_PARROT_IMITATE_WITHER_SKELETON = SoundEventImpl.get("minecraft:entity.parrot.imitate.wither_skeleton"); + SoundEvent ENTITY_PAINTING_BREAK = BuiltinSoundEvent.get("minecraft:entity.painting.break"); - SoundEvent ENTITY_PARROT_IMITATE_ZOGLIN = SoundEventImpl.get("minecraft:entity.parrot.imitate.zoglin"); + SoundEvent ENTITY_PAINTING_PLACE = BuiltinSoundEvent.get("minecraft:entity.painting.place"); - SoundEvent ENTITY_PARROT_IMITATE_ZOMBIE = SoundEventImpl.get("minecraft:entity.parrot.imitate.zombie"); + SoundEvent ENTITY_PANDA_PRE_SNEEZE = BuiltinSoundEvent.get("minecraft:entity.panda.pre_sneeze"); - SoundEvent ENTITY_PARROT_IMITATE_ZOMBIE_VILLAGER = SoundEventImpl.get("minecraft:entity.parrot.imitate.zombie_villager"); + SoundEvent ENTITY_PANDA_SNEEZE = BuiltinSoundEvent.get("minecraft:entity.panda.sneeze"); - SoundEvent ENTITY_PARROT_STEP = SoundEventImpl.get("minecraft:entity.parrot.step"); + SoundEvent ENTITY_PANDA_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.panda.ambient"); - SoundEvent ENTITY_PHANTOM_AMBIENT = SoundEventImpl.get("minecraft:entity.phantom.ambient"); + SoundEvent ENTITY_PANDA_DEATH = BuiltinSoundEvent.get("minecraft:entity.panda.death"); - SoundEvent ENTITY_PHANTOM_BITE = SoundEventImpl.get("minecraft:entity.phantom.bite"); + SoundEvent ENTITY_PANDA_EAT = BuiltinSoundEvent.get("minecraft:entity.panda.eat"); - SoundEvent ENTITY_PHANTOM_DEATH = SoundEventImpl.get("minecraft:entity.phantom.death"); + SoundEvent ENTITY_PANDA_STEP = BuiltinSoundEvent.get("minecraft:entity.panda.step"); - SoundEvent ENTITY_PHANTOM_FLAP = SoundEventImpl.get("minecraft:entity.phantom.flap"); + SoundEvent ENTITY_PANDA_CANT_BREED = BuiltinSoundEvent.get("minecraft:entity.panda.cant_breed"); - SoundEvent ENTITY_PHANTOM_HURT = SoundEventImpl.get("minecraft:entity.phantom.hurt"); + SoundEvent ENTITY_PANDA_AGGRESSIVE_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.panda.aggressive_ambient"); - SoundEvent ENTITY_PHANTOM_SWOOP = SoundEventImpl.get("minecraft:entity.phantom.swoop"); + SoundEvent ENTITY_PANDA_WORRIED_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.panda.worried_ambient"); - SoundEvent ENTITY_PIG_AMBIENT = SoundEventImpl.get("minecraft:entity.pig.ambient"); + SoundEvent ENTITY_PANDA_HURT = BuiltinSoundEvent.get("minecraft:entity.panda.hurt"); - SoundEvent ENTITY_PIG_DEATH = SoundEventImpl.get("minecraft:entity.pig.death"); + SoundEvent ENTITY_PANDA_BITE = BuiltinSoundEvent.get("minecraft:entity.panda.bite"); - SoundEvent ENTITY_PIG_HURT = SoundEventImpl.get("minecraft:entity.pig.hurt"); + SoundEvent ENTITY_PARROT_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.parrot.ambient"); - SoundEvent ENTITY_PIG_SADDLE = SoundEventImpl.get("minecraft:entity.pig.saddle"); + SoundEvent ENTITY_PARROT_DEATH = BuiltinSoundEvent.get("minecraft:entity.parrot.death"); - SoundEvent ENTITY_PIG_STEP = SoundEventImpl.get("minecraft:entity.pig.step"); + SoundEvent ENTITY_PARROT_EAT = BuiltinSoundEvent.get("minecraft:entity.parrot.eat"); - SoundEvent ENTITY_PIGLIN_ADMIRING_ITEM = SoundEventImpl.get("minecraft:entity.piglin.admiring_item"); + SoundEvent ENTITY_PARROT_FLY = BuiltinSoundEvent.get("minecraft:entity.parrot.fly"); - SoundEvent ENTITY_PIGLIN_AMBIENT = SoundEventImpl.get("minecraft:entity.piglin.ambient"); + SoundEvent ENTITY_PARROT_HURT = BuiltinSoundEvent.get("minecraft:entity.parrot.hurt"); - SoundEvent ENTITY_PIGLIN_ANGRY = SoundEventImpl.get("minecraft:entity.piglin.angry"); + SoundEvent ENTITY_PARROT_IMITATE_BLAZE = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.blaze"); - SoundEvent ENTITY_PIGLIN_CELEBRATE = SoundEventImpl.get("minecraft:entity.piglin.celebrate"); + SoundEvent ENTITY_PARROT_IMITATE_BOGGED = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.bogged"); - SoundEvent ENTITY_PIGLIN_DEATH = SoundEventImpl.get("minecraft:entity.piglin.death"); + SoundEvent ENTITY_PARROT_IMITATE_BREEZE = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.breeze"); - SoundEvent ENTITY_PIGLIN_JEALOUS = SoundEventImpl.get("minecraft:entity.piglin.jealous"); + SoundEvent ENTITY_PARROT_IMITATE_CREEPER = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.creeper"); - SoundEvent ENTITY_PIGLIN_HURT = SoundEventImpl.get("minecraft:entity.piglin.hurt"); + SoundEvent ENTITY_PARROT_IMITATE_DROWNED = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.drowned"); - SoundEvent ENTITY_PIGLIN_RETREAT = SoundEventImpl.get("minecraft:entity.piglin.retreat"); + SoundEvent ENTITY_PARROT_IMITATE_ELDER_GUARDIAN = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.elder_guardian"); - SoundEvent ENTITY_PIGLIN_STEP = SoundEventImpl.get("minecraft:entity.piglin.step"); + SoundEvent ENTITY_PARROT_IMITATE_ENDER_DRAGON = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.ender_dragon"); - SoundEvent ENTITY_PIGLIN_CONVERTED_TO_ZOMBIFIED = SoundEventImpl.get("minecraft:entity.piglin.converted_to_zombified"); + SoundEvent ENTITY_PARROT_IMITATE_ENDERMITE = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.endermite"); - SoundEvent ENTITY_PIGLIN_BRUTE_AMBIENT = SoundEventImpl.get("minecraft:entity.piglin_brute.ambient"); + SoundEvent ENTITY_PARROT_IMITATE_EVOKER = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.evoker"); - SoundEvent ENTITY_PIGLIN_BRUTE_ANGRY = SoundEventImpl.get("minecraft:entity.piglin_brute.angry"); + SoundEvent ENTITY_PARROT_IMITATE_GHAST = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.ghast"); - SoundEvent ENTITY_PIGLIN_BRUTE_DEATH = SoundEventImpl.get("minecraft:entity.piglin_brute.death"); + SoundEvent ENTITY_PARROT_IMITATE_GUARDIAN = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.guardian"); - SoundEvent ENTITY_PIGLIN_BRUTE_HURT = SoundEventImpl.get("minecraft:entity.piglin_brute.hurt"); + SoundEvent ENTITY_PARROT_IMITATE_HOGLIN = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.hoglin"); - SoundEvent ENTITY_PIGLIN_BRUTE_STEP = SoundEventImpl.get("minecraft:entity.piglin_brute.step"); + SoundEvent ENTITY_PARROT_IMITATE_HUSK = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.husk"); - SoundEvent ENTITY_PIGLIN_BRUTE_CONVERTED_TO_ZOMBIFIED = SoundEventImpl.get("minecraft:entity.piglin_brute.converted_to_zombified"); + SoundEvent ENTITY_PARROT_IMITATE_ILLUSIONER = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.illusioner"); - SoundEvent ENTITY_PILLAGER_AMBIENT = SoundEventImpl.get("minecraft:entity.pillager.ambient"); + SoundEvent ENTITY_PARROT_IMITATE_MAGMA_CUBE = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.magma_cube"); - SoundEvent ENTITY_PILLAGER_CELEBRATE = SoundEventImpl.get("minecraft:entity.pillager.celebrate"); + SoundEvent ENTITY_PARROT_IMITATE_PHANTOM = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.phantom"); - SoundEvent ENTITY_PILLAGER_DEATH = SoundEventImpl.get("minecraft:entity.pillager.death"); + SoundEvent ENTITY_PARROT_IMITATE_PIGLIN = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.piglin"); - SoundEvent ENTITY_PILLAGER_HURT = SoundEventImpl.get("minecraft:entity.pillager.hurt"); + SoundEvent ENTITY_PARROT_IMITATE_PIGLIN_BRUTE = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.piglin_brute"); - SoundEvent BLOCK_PISTON_CONTRACT = SoundEventImpl.get("minecraft:block.piston.contract"); + SoundEvent ENTITY_PARROT_IMITATE_PILLAGER = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.pillager"); - SoundEvent BLOCK_PISTON_EXTEND = SoundEventImpl.get("minecraft:block.piston.extend"); + SoundEvent ENTITY_PARROT_IMITATE_RAVAGER = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.ravager"); - SoundEvent ENTITY_PLAYER_ATTACK_CRIT = SoundEventImpl.get("minecraft:entity.player.attack.crit"); + SoundEvent ENTITY_PARROT_IMITATE_SHULKER = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.shulker"); - SoundEvent ENTITY_PLAYER_ATTACK_KNOCKBACK = SoundEventImpl.get("minecraft:entity.player.attack.knockback"); + SoundEvent ENTITY_PARROT_IMITATE_SILVERFISH = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.silverfish"); - SoundEvent ENTITY_PLAYER_ATTACK_NODAMAGE = SoundEventImpl.get("minecraft:entity.player.attack.nodamage"); + SoundEvent ENTITY_PARROT_IMITATE_SKELETON = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.skeleton"); - SoundEvent ENTITY_PLAYER_ATTACK_STRONG = SoundEventImpl.get("minecraft:entity.player.attack.strong"); + SoundEvent ENTITY_PARROT_IMITATE_SLIME = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.slime"); - SoundEvent ENTITY_PLAYER_ATTACK_SWEEP = SoundEventImpl.get("minecraft:entity.player.attack.sweep"); + SoundEvent ENTITY_PARROT_IMITATE_SPIDER = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.spider"); - SoundEvent ENTITY_PLAYER_ATTACK_WEAK = SoundEventImpl.get("minecraft:entity.player.attack.weak"); + SoundEvent ENTITY_PARROT_IMITATE_STRAY = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.stray"); - SoundEvent ENTITY_PLAYER_BIG_FALL = SoundEventImpl.get("minecraft:entity.player.big_fall"); + SoundEvent ENTITY_PARROT_IMITATE_VEX = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.vex"); - SoundEvent ENTITY_PLAYER_BREATH = SoundEventImpl.get("minecraft:entity.player.breath"); + SoundEvent ENTITY_PARROT_IMITATE_VINDICATOR = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.vindicator"); - SoundEvent ENTITY_PLAYER_BURP = SoundEventImpl.get("minecraft:entity.player.burp"); + SoundEvent ENTITY_PARROT_IMITATE_WARDEN = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.warden"); - SoundEvent ENTITY_PLAYER_DEATH = SoundEventImpl.get("minecraft:entity.player.death"); + SoundEvent ENTITY_PARROT_IMITATE_WITCH = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.witch"); - SoundEvent ENTITY_PLAYER_HURT = SoundEventImpl.get("minecraft:entity.player.hurt"); + SoundEvent ENTITY_PARROT_IMITATE_WITHER = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.wither"); - SoundEvent ENTITY_PLAYER_HURT_DROWN = SoundEventImpl.get("minecraft:entity.player.hurt_drown"); + SoundEvent ENTITY_PARROT_IMITATE_WITHER_SKELETON = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.wither_skeleton"); - SoundEvent ENTITY_PLAYER_HURT_FREEZE = SoundEventImpl.get("minecraft:entity.player.hurt_freeze"); + SoundEvent ENTITY_PARROT_IMITATE_ZOGLIN = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.zoglin"); - SoundEvent ENTITY_PLAYER_HURT_ON_FIRE = SoundEventImpl.get("minecraft:entity.player.hurt_on_fire"); + SoundEvent ENTITY_PARROT_IMITATE_ZOMBIE = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.zombie"); - SoundEvent ENTITY_PLAYER_HURT_SWEET_BERRY_BUSH = SoundEventImpl.get("minecraft:entity.player.hurt_sweet_berry_bush"); + SoundEvent ENTITY_PARROT_IMITATE_ZOMBIE_VILLAGER = BuiltinSoundEvent.get("minecraft:entity.parrot.imitate.zombie_villager"); - SoundEvent ENTITY_PLAYER_LEVELUP = SoundEventImpl.get("minecraft:entity.player.levelup"); + SoundEvent ENTITY_PARROT_STEP = BuiltinSoundEvent.get("minecraft:entity.parrot.step"); - SoundEvent ENTITY_PLAYER_SMALL_FALL = SoundEventImpl.get("minecraft:entity.player.small_fall"); + SoundEvent ENTITY_PHANTOM_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.phantom.ambient"); - SoundEvent ENTITY_PLAYER_SPLASH = SoundEventImpl.get("minecraft:entity.player.splash"); + SoundEvent ENTITY_PHANTOM_BITE = BuiltinSoundEvent.get("minecraft:entity.phantom.bite"); - SoundEvent ENTITY_PLAYER_SPLASH_HIGH_SPEED = SoundEventImpl.get("minecraft:entity.player.splash.high_speed"); + SoundEvent ENTITY_PHANTOM_DEATH = BuiltinSoundEvent.get("minecraft:entity.phantom.death"); - SoundEvent ENTITY_PLAYER_SWIM = SoundEventImpl.get("minecraft:entity.player.swim"); + SoundEvent ENTITY_PHANTOM_FLAP = BuiltinSoundEvent.get("minecraft:entity.phantom.flap"); - SoundEvent ENTITY_PLAYER_TELEPORT = SoundEventImpl.get("minecraft:entity.player.teleport"); + SoundEvent ENTITY_PHANTOM_HURT = BuiltinSoundEvent.get("minecraft:entity.phantom.hurt"); - SoundEvent ENTITY_POLAR_BEAR_AMBIENT = SoundEventImpl.get("minecraft:entity.polar_bear.ambient"); + SoundEvent ENTITY_PHANTOM_SWOOP = BuiltinSoundEvent.get("minecraft:entity.phantom.swoop"); - SoundEvent ENTITY_POLAR_BEAR_AMBIENT_BABY = SoundEventImpl.get("minecraft:entity.polar_bear.ambient_baby"); + SoundEvent ENTITY_PIG_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.pig.ambient"); - SoundEvent ENTITY_POLAR_BEAR_DEATH = SoundEventImpl.get("minecraft:entity.polar_bear.death"); + SoundEvent ENTITY_PIG_DEATH = BuiltinSoundEvent.get("minecraft:entity.pig.death"); - SoundEvent ENTITY_POLAR_BEAR_HURT = SoundEventImpl.get("minecraft:entity.polar_bear.hurt"); + SoundEvent ENTITY_PIG_HURT = BuiltinSoundEvent.get("minecraft:entity.pig.hurt"); - SoundEvent ENTITY_POLAR_BEAR_STEP = SoundEventImpl.get("minecraft:entity.polar_bear.step"); + SoundEvent ENTITY_PIG_SADDLE = BuiltinSoundEvent.get("minecraft:entity.pig.saddle"); - SoundEvent ENTITY_POLAR_BEAR_WARNING = SoundEventImpl.get("minecraft:entity.polar_bear.warning"); + SoundEvent ENTITY_PIG_STEP = BuiltinSoundEvent.get("minecraft:entity.pig.step"); - SoundEvent BLOCK_POLISHED_DEEPSLATE_BREAK = SoundEventImpl.get("minecraft:block.polished_deepslate.break"); + SoundEvent ENTITY_PIGLIN_ADMIRING_ITEM = BuiltinSoundEvent.get("minecraft:entity.piglin.admiring_item"); - SoundEvent BLOCK_POLISHED_DEEPSLATE_FALL = SoundEventImpl.get("minecraft:block.polished_deepslate.fall"); + SoundEvent ENTITY_PIGLIN_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.piglin.ambient"); - SoundEvent BLOCK_POLISHED_DEEPSLATE_HIT = SoundEventImpl.get("minecraft:block.polished_deepslate.hit"); + SoundEvent ENTITY_PIGLIN_ANGRY = BuiltinSoundEvent.get("minecraft:entity.piglin.angry"); - SoundEvent BLOCK_POLISHED_DEEPSLATE_PLACE = SoundEventImpl.get("minecraft:block.polished_deepslate.place"); + SoundEvent ENTITY_PIGLIN_CELEBRATE = BuiltinSoundEvent.get("minecraft:entity.piglin.celebrate"); - SoundEvent BLOCK_POLISHED_DEEPSLATE_STEP = SoundEventImpl.get("minecraft:block.polished_deepslate.step"); + SoundEvent ENTITY_PIGLIN_DEATH = BuiltinSoundEvent.get("minecraft:entity.piglin.death"); - SoundEvent BLOCK_PORTAL_AMBIENT = SoundEventImpl.get("minecraft:block.portal.ambient"); + SoundEvent ENTITY_PIGLIN_JEALOUS = BuiltinSoundEvent.get("minecraft:entity.piglin.jealous"); - SoundEvent BLOCK_PORTAL_TRAVEL = SoundEventImpl.get("minecraft:block.portal.travel"); + SoundEvent ENTITY_PIGLIN_HURT = BuiltinSoundEvent.get("minecraft:entity.piglin.hurt"); - SoundEvent BLOCK_PORTAL_TRIGGER = SoundEventImpl.get("minecraft:block.portal.trigger"); + SoundEvent ENTITY_PIGLIN_RETREAT = BuiltinSoundEvent.get("minecraft:entity.piglin.retreat"); - SoundEvent BLOCK_POWDER_SNOW_BREAK = SoundEventImpl.get("minecraft:block.powder_snow.break"); + SoundEvent ENTITY_PIGLIN_STEP = BuiltinSoundEvent.get("minecraft:entity.piglin.step"); - SoundEvent BLOCK_POWDER_SNOW_FALL = SoundEventImpl.get("minecraft:block.powder_snow.fall"); + SoundEvent ENTITY_PIGLIN_CONVERTED_TO_ZOMBIFIED = BuiltinSoundEvent.get("minecraft:entity.piglin.converted_to_zombified"); - SoundEvent BLOCK_POWDER_SNOW_HIT = SoundEventImpl.get("minecraft:block.powder_snow.hit"); + SoundEvent ENTITY_PIGLIN_BRUTE_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.piglin_brute.ambient"); - SoundEvent BLOCK_POWDER_SNOW_PLACE = SoundEventImpl.get("minecraft:block.powder_snow.place"); + SoundEvent ENTITY_PIGLIN_BRUTE_ANGRY = BuiltinSoundEvent.get("minecraft:entity.piglin_brute.angry"); - SoundEvent BLOCK_POWDER_SNOW_STEP = SoundEventImpl.get("minecraft:block.powder_snow.step"); + SoundEvent ENTITY_PIGLIN_BRUTE_DEATH = BuiltinSoundEvent.get("minecraft:entity.piglin_brute.death"); - SoundEvent ENTITY_PUFFER_FISH_AMBIENT = SoundEventImpl.get("minecraft:entity.puffer_fish.ambient"); + SoundEvent ENTITY_PIGLIN_BRUTE_HURT = BuiltinSoundEvent.get("minecraft:entity.piglin_brute.hurt"); - SoundEvent ENTITY_PUFFER_FISH_BLOW_OUT = SoundEventImpl.get("minecraft:entity.puffer_fish.blow_out"); + SoundEvent ENTITY_PIGLIN_BRUTE_STEP = BuiltinSoundEvent.get("minecraft:entity.piglin_brute.step"); - SoundEvent ENTITY_PUFFER_FISH_BLOW_UP = SoundEventImpl.get("minecraft:entity.puffer_fish.blow_up"); + SoundEvent ENTITY_PIGLIN_BRUTE_CONVERTED_TO_ZOMBIFIED = BuiltinSoundEvent.get("minecraft:entity.piglin_brute.converted_to_zombified"); - SoundEvent ENTITY_PUFFER_FISH_DEATH = SoundEventImpl.get("minecraft:entity.puffer_fish.death"); + SoundEvent ENTITY_PILLAGER_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.pillager.ambient"); - SoundEvent ENTITY_PUFFER_FISH_FLOP = SoundEventImpl.get("minecraft:entity.puffer_fish.flop"); + SoundEvent ENTITY_PILLAGER_CELEBRATE = BuiltinSoundEvent.get("minecraft:entity.pillager.celebrate"); - SoundEvent ENTITY_PUFFER_FISH_HURT = SoundEventImpl.get("minecraft:entity.puffer_fish.hurt"); + SoundEvent ENTITY_PILLAGER_DEATH = BuiltinSoundEvent.get("minecraft:entity.pillager.death"); - SoundEvent ENTITY_PUFFER_FISH_STING = SoundEventImpl.get("minecraft:entity.puffer_fish.sting"); + SoundEvent ENTITY_PILLAGER_HURT = BuiltinSoundEvent.get("minecraft:entity.pillager.hurt"); - SoundEvent BLOCK_PUMPKIN_CARVE = SoundEventImpl.get("minecraft:block.pumpkin.carve"); + SoundEvent BLOCK_PISTON_CONTRACT = BuiltinSoundEvent.get("minecraft:block.piston.contract"); - SoundEvent ENTITY_RABBIT_AMBIENT = SoundEventImpl.get("minecraft:entity.rabbit.ambient"); + SoundEvent BLOCK_PISTON_EXTEND = BuiltinSoundEvent.get("minecraft:block.piston.extend"); - SoundEvent ENTITY_RABBIT_ATTACK = SoundEventImpl.get("minecraft:entity.rabbit.attack"); + SoundEvent ENTITY_PLAYER_ATTACK_CRIT = BuiltinSoundEvent.get("minecraft:entity.player.attack.crit"); - SoundEvent ENTITY_RABBIT_DEATH = SoundEventImpl.get("minecraft:entity.rabbit.death"); + SoundEvent ENTITY_PLAYER_ATTACK_KNOCKBACK = BuiltinSoundEvent.get("minecraft:entity.player.attack.knockback"); - SoundEvent ENTITY_RABBIT_HURT = SoundEventImpl.get("minecraft:entity.rabbit.hurt"); + SoundEvent ENTITY_PLAYER_ATTACK_NODAMAGE = BuiltinSoundEvent.get("minecraft:entity.player.attack.nodamage"); - SoundEvent ENTITY_RABBIT_JUMP = SoundEventImpl.get("minecraft:entity.rabbit.jump"); + SoundEvent ENTITY_PLAYER_ATTACK_STRONG = BuiltinSoundEvent.get("minecraft:entity.player.attack.strong"); - SoundEvent EVENT_RAID_HORN = SoundEventImpl.get("minecraft:event.raid.horn"); + SoundEvent ENTITY_PLAYER_ATTACK_SWEEP = BuiltinSoundEvent.get("minecraft:entity.player.attack.sweep"); - SoundEvent ENTITY_RAVAGER_AMBIENT = SoundEventImpl.get("minecraft:entity.ravager.ambient"); + SoundEvent ENTITY_PLAYER_ATTACK_WEAK = BuiltinSoundEvent.get("minecraft:entity.player.attack.weak"); - SoundEvent ENTITY_RAVAGER_ATTACK = SoundEventImpl.get("minecraft:entity.ravager.attack"); + SoundEvent ENTITY_PLAYER_BIG_FALL = BuiltinSoundEvent.get("minecraft:entity.player.big_fall"); - SoundEvent ENTITY_RAVAGER_CELEBRATE = SoundEventImpl.get("minecraft:entity.ravager.celebrate"); + SoundEvent ENTITY_PLAYER_BREATH = BuiltinSoundEvent.get("minecraft:entity.player.breath"); - SoundEvent ENTITY_RAVAGER_DEATH = SoundEventImpl.get("minecraft:entity.ravager.death"); + SoundEvent ENTITY_PLAYER_BURP = BuiltinSoundEvent.get("minecraft:entity.player.burp"); - SoundEvent ENTITY_RAVAGER_HURT = SoundEventImpl.get("minecraft:entity.ravager.hurt"); + SoundEvent ENTITY_PLAYER_DEATH = BuiltinSoundEvent.get("minecraft:entity.player.death"); - SoundEvent ENTITY_RAVAGER_STEP = SoundEventImpl.get("minecraft:entity.ravager.step"); + SoundEvent ENTITY_PLAYER_HURT = BuiltinSoundEvent.get("minecraft:entity.player.hurt"); - SoundEvent ENTITY_RAVAGER_STUNNED = SoundEventImpl.get("minecraft:entity.ravager.stunned"); + SoundEvent ENTITY_PLAYER_HURT_DROWN = BuiltinSoundEvent.get("minecraft:entity.player.hurt_drown"); - SoundEvent ENTITY_RAVAGER_ROAR = SoundEventImpl.get("minecraft:entity.ravager.roar"); + SoundEvent ENTITY_PLAYER_HURT_FREEZE = BuiltinSoundEvent.get("minecraft:entity.player.hurt_freeze"); - SoundEvent BLOCK_NETHER_GOLD_ORE_BREAK = SoundEventImpl.get("minecraft:block.nether_gold_ore.break"); + SoundEvent ENTITY_PLAYER_HURT_ON_FIRE = BuiltinSoundEvent.get("minecraft:entity.player.hurt_on_fire"); - SoundEvent BLOCK_NETHER_GOLD_ORE_FALL = SoundEventImpl.get("minecraft:block.nether_gold_ore.fall"); + SoundEvent ENTITY_PLAYER_HURT_SWEET_BERRY_BUSH = BuiltinSoundEvent.get("minecraft:entity.player.hurt_sweet_berry_bush"); - SoundEvent BLOCK_NETHER_GOLD_ORE_HIT = SoundEventImpl.get("minecraft:block.nether_gold_ore.hit"); + SoundEvent ENTITY_PLAYER_LEVELUP = BuiltinSoundEvent.get("minecraft:entity.player.levelup"); - SoundEvent BLOCK_NETHER_GOLD_ORE_PLACE = SoundEventImpl.get("minecraft:block.nether_gold_ore.place"); + SoundEvent ENTITY_PLAYER_SMALL_FALL = BuiltinSoundEvent.get("minecraft:entity.player.small_fall"); - SoundEvent BLOCK_NETHER_GOLD_ORE_STEP = SoundEventImpl.get("minecraft:block.nether_gold_ore.step"); + SoundEvent ENTITY_PLAYER_SPLASH = BuiltinSoundEvent.get("minecraft:entity.player.splash"); - SoundEvent BLOCK_NETHER_ORE_BREAK = SoundEventImpl.get("minecraft:block.nether_ore.break"); + SoundEvent ENTITY_PLAYER_SPLASH_HIGH_SPEED = BuiltinSoundEvent.get("minecraft:entity.player.splash.high_speed"); - SoundEvent BLOCK_NETHER_ORE_FALL = SoundEventImpl.get("minecraft:block.nether_ore.fall"); + SoundEvent ENTITY_PLAYER_SWIM = BuiltinSoundEvent.get("minecraft:entity.player.swim"); - SoundEvent BLOCK_NETHER_ORE_HIT = SoundEventImpl.get("minecraft:block.nether_ore.hit"); + SoundEvent ENTITY_PLAYER_TELEPORT = BuiltinSoundEvent.get("minecraft:entity.player.teleport"); - SoundEvent BLOCK_NETHER_ORE_PLACE = SoundEventImpl.get("minecraft:block.nether_ore.place"); + SoundEvent ENTITY_POLAR_BEAR_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.polar_bear.ambient"); - SoundEvent BLOCK_NETHER_ORE_STEP = SoundEventImpl.get("minecraft:block.nether_ore.step"); + SoundEvent ENTITY_POLAR_BEAR_AMBIENT_BABY = BuiltinSoundEvent.get("minecraft:entity.polar_bear.ambient_baby"); - SoundEvent BLOCK_REDSTONE_TORCH_BURNOUT = SoundEventImpl.get("minecraft:block.redstone_torch.burnout"); + SoundEvent ENTITY_POLAR_BEAR_DEATH = BuiltinSoundEvent.get("minecraft:entity.polar_bear.death"); - SoundEvent BLOCK_RESPAWN_ANCHOR_AMBIENT = SoundEventImpl.get("minecraft:block.respawn_anchor.ambient"); + SoundEvent ENTITY_POLAR_BEAR_HURT = BuiltinSoundEvent.get("minecraft:entity.polar_bear.hurt"); - SoundEvent BLOCK_RESPAWN_ANCHOR_CHARGE = SoundEventImpl.get("minecraft:block.respawn_anchor.charge"); + SoundEvent ENTITY_POLAR_BEAR_STEP = BuiltinSoundEvent.get("minecraft:entity.polar_bear.step"); - SoundEvent BLOCK_RESPAWN_ANCHOR_DEPLETE = SoundEventImpl.get("minecraft:block.respawn_anchor.deplete"); + SoundEvent ENTITY_POLAR_BEAR_WARNING = BuiltinSoundEvent.get("minecraft:entity.polar_bear.warning"); - SoundEvent BLOCK_RESPAWN_ANCHOR_SET_SPAWN = SoundEventImpl.get("minecraft:block.respawn_anchor.set_spawn"); + SoundEvent BLOCK_POLISHED_DEEPSLATE_BREAK = BuiltinSoundEvent.get("minecraft:block.polished_deepslate.break"); - SoundEvent BLOCK_ROOTED_DIRT_BREAK = SoundEventImpl.get("minecraft:block.rooted_dirt.break"); + SoundEvent BLOCK_POLISHED_DEEPSLATE_FALL = BuiltinSoundEvent.get("minecraft:block.polished_deepslate.fall"); - SoundEvent BLOCK_ROOTED_DIRT_FALL = SoundEventImpl.get("minecraft:block.rooted_dirt.fall"); + SoundEvent BLOCK_POLISHED_DEEPSLATE_HIT = BuiltinSoundEvent.get("minecraft:block.polished_deepslate.hit"); - SoundEvent BLOCK_ROOTED_DIRT_HIT = SoundEventImpl.get("minecraft:block.rooted_dirt.hit"); + SoundEvent BLOCK_POLISHED_DEEPSLATE_PLACE = BuiltinSoundEvent.get("minecraft:block.polished_deepslate.place"); - SoundEvent BLOCK_ROOTED_DIRT_PLACE = SoundEventImpl.get("minecraft:block.rooted_dirt.place"); + SoundEvent BLOCK_POLISHED_DEEPSLATE_STEP = BuiltinSoundEvent.get("minecraft:block.polished_deepslate.step"); - SoundEvent BLOCK_ROOTED_DIRT_STEP = SoundEventImpl.get("minecraft:block.rooted_dirt.step"); + SoundEvent BLOCK_PORTAL_AMBIENT = BuiltinSoundEvent.get("minecraft:block.portal.ambient"); - SoundEvent ENTITY_SALMON_AMBIENT = SoundEventImpl.get("minecraft:entity.salmon.ambient"); + SoundEvent BLOCK_PORTAL_TRAVEL = BuiltinSoundEvent.get("minecraft:block.portal.travel"); - SoundEvent ENTITY_SALMON_DEATH = SoundEventImpl.get("minecraft:entity.salmon.death"); + SoundEvent BLOCK_PORTAL_TRIGGER = BuiltinSoundEvent.get("minecraft:block.portal.trigger"); - SoundEvent ENTITY_SALMON_FLOP = SoundEventImpl.get("minecraft:entity.salmon.flop"); + SoundEvent BLOCK_POWDER_SNOW_BREAK = BuiltinSoundEvent.get("minecraft:block.powder_snow.break"); - SoundEvent ENTITY_SALMON_HURT = SoundEventImpl.get("minecraft:entity.salmon.hurt"); + SoundEvent BLOCK_POWDER_SNOW_FALL = BuiltinSoundEvent.get("minecraft:block.powder_snow.fall"); - SoundEvent BLOCK_SAND_BREAK = SoundEventImpl.get("minecraft:block.sand.break"); + SoundEvent BLOCK_POWDER_SNOW_HIT = BuiltinSoundEvent.get("minecraft:block.powder_snow.hit"); - SoundEvent BLOCK_SAND_FALL = SoundEventImpl.get("minecraft:block.sand.fall"); + SoundEvent BLOCK_POWDER_SNOW_PLACE = BuiltinSoundEvent.get("minecraft:block.powder_snow.place"); - SoundEvent BLOCK_SAND_HIT = SoundEventImpl.get("minecraft:block.sand.hit"); + SoundEvent BLOCK_POWDER_SNOW_STEP = BuiltinSoundEvent.get("minecraft:block.powder_snow.step"); - SoundEvent BLOCK_SAND_PLACE = SoundEventImpl.get("minecraft:block.sand.place"); + SoundEvent ENTITY_PUFFER_FISH_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.puffer_fish.ambient"); - SoundEvent BLOCK_SAND_STEP = SoundEventImpl.get("minecraft:block.sand.step"); + SoundEvent ENTITY_PUFFER_FISH_BLOW_OUT = BuiltinSoundEvent.get("minecraft:entity.puffer_fish.blow_out"); - SoundEvent BLOCK_SCAFFOLDING_BREAK = SoundEventImpl.get("minecraft:block.scaffolding.break"); + SoundEvent ENTITY_PUFFER_FISH_BLOW_UP = BuiltinSoundEvent.get("minecraft:entity.puffer_fish.blow_up"); - SoundEvent BLOCK_SCAFFOLDING_FALL = SoundEventImpl.get("minecraft:block.scaffolding.fall"); + SoundEvent ENTITY_PUFFER_FISH_DEATH = BuiltinSoundEvent.get("minecraft:entity.puffer_fish.death"); - SoundEvent BLOCK_SCAFFOLDING_HIT = SoundEventImpl.get("minecraft:block.scaffolding.hit"); + SoundEvent ENTITY_PUFFER_FISH_FLOP = BuiltinSoundEvent.get("minecraft:entity.puffer_fish.flop"); - SoundEvent BLOCK_SCAFFOLDING_PLACE = SoundEventImpl.get("minecraft:block.scaffolding.place"); + SoundEvent ENTITY_PUFFER_FISH_HURT = BuiltinSoundEvent.get("minecraft:entity.puffer_fish.hurt"); - SoundEvent BLOCK_SCAFFOLDING_STEP = SoundEventImpl.get("minecraft:block.scaffolding.step"); + SoundEvent ENTITY_PUFFER_FISH_STING = BuiltinSoundEvent.get("minecraft:entity.puffer_fish.sting"); - SoundEvent BLOCK_SCULK_SPREAD = SoundEventImpl.get("minecraft:block.sculk.spread"); + SoundEvent BLOCK_PUMPKIN_CARVE = BuiltinSoundEvent.get("minecraft:block.pumpkin.carve"); - SoundEvent BLOCK_SCULK_CHARGE = SoundEventImpl.get("minecraft:block.sculk.charge"); + SoundEvent ENTITY_RABBIT_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.rabbit.ambient"); - SoundEvent BLOCK_SCULK_BREAK = SoundEventImpl.get("minecraft:block.sculk.break"); + SoundEvent ENTITY_RABBIT_ATTACK = BuiltinSoundEvent.get("minecraft:entity.rabbit.attack"); - SoundEvent BLOCK_SCULK_FALL = SoundEventImpl.get("minecraft:block.sculk.fall"); + SoundEvent ENTITY_RABBIT_DEATH = BuiltinSoundEvent.get("minecraft:entity.rabbit.death"); - SoundEvent BLOCK_SCULK_HIT = SoundEventImpl.get("minecraft:block.sculk.hit"); + SoundEvent ENTITY_RABBIT_HURT = BuiltinSoundEvent.get("minecraft:entity.rabbit.hurt"); - SoundEvent BLOCK_SCULK_PLACE = SoundEventImpl.get("minecraft:block.sculk.place"); + SoundEvent ENTITY_RABBIT_JUMP = BuiltinSoundEvent.get("minecraft:entity.rabbit.jump"); - SoundEvent BLOCK_SCULK_STEP = SoundEventImpl.get("minecraft:block.sculk.step"); + SoundEvent EVENT_RAID_HORN = BuiltinSoundEvent.get("minecraft:event.raid.horn"); - SoundEvent BLOCK_SCULK_CATALYST_BLOOM = SoundEventImpl.get("minecraft:block.sculk_catalyst.bloom"); + SoundEvent ENTITY_RAVAGER_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.ravager.ambient"); - SoundEvent BLOCK_SCULK_CATALYST_BREAK = SoundEventImpl.get("minecraft:block.sculk_catalyst.break"); + SoundEvent ENTITY_RAVAGER_ATTACK = BuiltinSoundEvent.get("minecraft:entity.ravager.attack"); - SoundEvent BLOCK_SCULK_CATALYST_FALL = SoundEventImpl.get("minecraft:block.sculk_catalyst.fall"); + SoundEvent ENTITY_RAVAGER_CELEBRATE = BuiltinSoundEvent.get("minecraft:entity.ravager.celebrate"); - SoundEvent BLOCK_SCULK_CATALYST_HIT = SoundEventImpl.get("minecraft:block.sculk_catalyst.hit"); + SoundEvent ENTITY_RAVAGER_DEATH = BuiltinSoundEvent.get("minecraft:entity.ravager.death"); - SoundEvent BLOCK_SCULK_CATALYST_PLACE = SoundEventImpl.get("minecraft:block.sculk_catalyst.place"); + SoundEvent ENTITY_RAVAGER_HURT = BuiltinSoundEvent.get("minecraft:entity.ravager.hurt"); - SoundEvent BLOCK_SCULK_CATALYST_STEP = SoundEventImpl.get("minecraft:block.sculk_catalyst.step"); + SoundEvent ENTITY_RAVAGER_STEP = BuiltinSoundEvent.get("minecraft:entity.ravager.step"); - SoundEvent BLOCK_SCULK_SENSOR_CLICKING = SoundEventImpl.get("minecraft:block.sculk_sensor.clicking"); + SoundEvent ENTITY_RAVAGER_STUNNED = BuiltinSoundEvent.get("minecraft:entity.ravager.stunned"); - SoundEvent BLOCK_SCULK_SENSOR_CLICKING_STOP = SoundEventImpl.get("minecraft:block.sculk_sensor.clicking_stop"); + SoundEvent ENTITY_RAVAGER_ROAR = BuiltinSoundEvent.get("minecraft:entity.ravager.roar"); - SoundEvent BLOCK_SCULK_SENSOR_BREAK = SoundEventImpl.get("minecraft:block.sculk_sensor.break"); + SoundEvent BLOCK_NETHER_GOLD_ORE_BREAK = BuiltinSoundEvent.get("minecraft:block.nether_gold_ore.break"); - SoundEvent BLOCK_SCULK_SENSOR_FALL = SoundEventImpl.get("minecraft:block.sculk_sensor.fall"); + SoundEvent BLOCK_NETHER_GOLD_ORE_FALL = BuiltinSoundEvent.get("minecraft:block.nether_gold_ore.fall"); - SoundEvent BLOCK_SCULK_SENSOR_HIT = SoundEventImpl.get("minecraft:block.sculk_sensor.hit"); + SoundEvent BLOCK_NETHER_GOLD_ORE_HIT = BuiltinSoundEvent.get("minecraft:block.nether_gold_ore.hit"); - SoundEvent BLOCK_SCULK_SENSOR_PLACE = SoundEventImpl.get("minecraft:block.sculk_sensor.place"); + SoundEvent BLOCK_NETHER_GOLD_ORE_PLACE = BuiltinSoundEvent.get("minecraft:block.nether_gold_ore.place"); - SoundEvent BLOCK_SCULK_SENSOR_STEP = SoundEventImpl.get("minecraft:block.sculk_sensor.step"); + SoundEvent BLOCK_NETHER_GOLD_ORE_STEP = BuiltinSoundEvent.get("minecraft:block.nether_gold_ore.step"); - SoundEvent BLOCK_SCULK_SHRIEKER_BREAK = SoundEventImpl.get("minecraft:block.sculk_shrieker.break"); + SoundEvent BLOCK_NETHER_ORE_BREAK = BuiltinSoundEvent.get("minecraft:block.nether_ore.break"); - SoundEvent BLOCK_SCULK_SHRIEKER_FALL = SoundEventImpl.get("minecraft:block.sculk_shrieker.fall"); + SoundEvent BLOCK_NETHER_ORE_FALL = BuiltinSoundEvent.get("minecraft:block.nether_ore.fall"); - SoundEvent BLOCK_SCULK_SHRIEKER_HIT = SoundEventImpl.get("minecraft:block.sculk_shrieker.hit"); + SoundEvent BLOCK_NETHER_ORE_HIT = BuiltinSoundEvent.get("minecraft:block.nether_ore.hit"); - SoundEvent BLOCK_SCULK_SHRIEKER_PLACE = SoundEventImpl.get("minecraft:block.sculk_shrieker.place"); + SoundEvent BLOCK_NETHER_ORE_PLACE = BuiltinSoundEvent.get("minecraft:block.nether_ore.place"); - SoundEvent BLOCK_SCULK_SHRIEKER_SHRIEK = SoundEventImpl.get("minecraft:block.sculk_shrieker.shriek"); + SoundEvent BLOCK_NETHER_ORE_STEP = BuiltinSoundEvent.get("minecraft:block.nether_ore.step"); - SoundEvent BLOCK_SCULK_SHRIEKER_STEP = SoundEventImpl.get("minecraft:block.sculk_shrieker.step"); + SoundEvent BLOCK_REDSTONE_TORCH_BURNOUT = BuiltinSoundEvent.get("minecraft:block.redstone_torch.burnout"); - SoundEvent BLOCK_SCULK_VEIN_BREAK = SoundEventImpl.get("minecraft:block.sculk_vein.break"); + SoundEvent BLOCK_RESPAWN_ANCHOR_AMBIENT = BuiltinSoundEvent.get("minecraft:block.respawn_anchor.ambient"); - SoundEvent BLOCK_SCULK_VEIN_FALL = SoundEventImpl.get("minecraft:block.sculk_vein.fall"); + SoundEvent BLOCK_RESPAWN_ANCHOR_CHARGE = BuiltinSoundEvent.get("minecraft:block.respawn_anchor.charge"); - SoundEvent BLOCK_SCULK_VEIN_HIT = SoundEventImpl.get("minecraft:block.sculk_vein.hit"); + SoundEvent BLOCK_RESPAWN_ANCHOR_DEPLETE = BuiltinSoundEvent.get("minecraft:block.respawn_anchor.deplete"); - SoundEvent BLOCK_SCULK_VEIN_PLACE = SoundEventImpl.get("minecraft:block.sculk_vein.place"); + SoundEvent BLOCK_RESPAWN_ANCHOR_SET_SPAWN = BuiltinSoundEvent.get("minecraft:block.respawn_anchor.set_spawn"); - SoundEvent BLOCK_SCULK_VEIN_STEP = SoundEventImpl.get("minecraft:block.sculk_vein.step"); + SoundEvent BLOCK_ROOTED_DIRT_BREAK = BuiltinSoundEvent.get("minecraft:block.rooted_dirt.break"); - SoundEvent ENTITY_SHEEP_AMBIENT = SoundEventImpl.get("minecraft:entity.sheep.ambient"); + SoundEvent BLOCK_ROOTED_DIRT_FALL = BuiltinSoundEvent.get("minecraft:block.rooted_dirt.fall"); - SoundEvent ENTITY_SHEEP_DEATH = SoundEventImpl.get("minecraft:entity.sheep.death"); + SoundEvent BLOCK_ROOTED_DIRT_HIT = BuiltinSoundEvent.get("minecraft:block.rooted_dirt.hit"); - SoundEvent ENTITY_SHEEP_HURT = SoundEventImpl.get("minecraft:entity.sheep.hurt"); + SoundEvent BLOCK_ROOTED_DIRT_PLACE = BuiltinSoundEvent.get("minecraft:block.rooted_dirt.place"); - SoundEvent ENTITY_SHEEP_SHEAR = SoundEventImpl.get("minecraft:entity.sheep.shear"); + SoundEvent BLOCK_ROOTED_DIRT_STEP = BuiltinSoundEvent.get("minecraft:block.rooted_dirt.step"); - SoundEvent ENTITY_SHEEP_STEP = SoundEventImpl.get("minecraft:entity.sheep.step"); + SoundEvent ENTITY_SALMON_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.salmon.ambient"); - SoundEvent ITEM_SHIELD_BLOCK = SoundEventImpl.get("minecraft:item.shield.block"); + SoundEvent ENTITY_SALMON_DEATH = BuiltinSoundEvent.get("minecraft:entity.salmon.death"); - SoundEvent ITEM_SHIELD_BREAK = SoundEventImpl.get("minecraft:item.shield.break"); + SoundEvent ENTITY_SALMON_FLOP = BuiltinSoundEvent.get("minecraft:entity.salmon.flop"); - SoundEvent BLOCK_SHROOMLIGHT_BREAK = SoundEventImpl.get("minecraft:block.shroomlight.break"); + SoundEvent ENTITY_SALMON_HURT = BuiltinSoundEvent.get("minecraft:entity.salmon.hurt"); - SoundEvent BLOCK_SHROOMLIGHT_STEP = SoundEventImpl.get("minecraft:block.shroomlight.step"); + SoundEvent BLOCK_SAND_BREAK = BuiltinSoundEvent.get("minecraft:block.sand.break"); - SoundEvent BLOCK_SHROOMLIGHT_PLACE = SoundEventImpl.get("minecraft:block.shroomlight.place"); + SoundEvent BLOCK_SAND_FALL = BuiltinSoundEvent.get("minecraft:block.sand.fall"); - SoundEvent BLOCK_SHROOMLIGHT_HIT = SoundEventImpl.get("minecraft:block.shroomlight.hit"); + SoundEvent BLOCK_SAND_HIT = BuiltinSoundEvent.get("minecraft:block.sand.hit"); - SoundEvent BLOCK_SHROOMLIGHT_FALL = SoundEventImpl.get("minecraft:block.shroomlight.fall"); + SoundEvent BLOCK_SAND_PLACE = BuiltinSoundEvent.get("minecraft:block.sand.place"); - SoundEvent ITEM_SHOVEL_FLATTEN = SoundEventImpl.get("minecraft:item.shovel.flatten"); + SoundEvent BLOCK_SAND_STEP = BuiltinSoundEvent.get("minecraft:block.sand.step"); - SoundEvent ENTITY_SHULKER_AMBIENT = SoundEventImpl.get("minecraft:entity.shulker.ambient"); + SoundEvent BLOCK_SCAFFOLDING_BREAK = BuiltinSoundEvent.get("minecraft:block.scaffolding.break"); - SoundEvent BLOCK_SHULKER_BOX_CLOSE = SoundEventImpl.get("minecraft:block.shulker_box.close"); + SoundEvent BLOCK_SCAFFOLDING_FALL = BuiltinSoundEvent.get("minecraft:block.scaffolding.fall"); - SoundEvent BLOCK_SHULKER_BOX_OPEN = SoundEventImpl.get("minecraft:block.shulker_box.open"); + SoundEvent BLOCK_SCAFFOLDING_HIT = BuiltinSoundEvent.get("minecraft:block.scaffolding.hit"); - SoundEvent ENTITY_SHULKER_BULLET_HIT = SoundEventImpl.get("minecraft:entity.shulker_bullet.hit"); + SoundEvent BLOCK_SCAFFOLDING_PLACE = BuiltinSoundEvent.get("minecraft:block.scaffolding.place"); - SoundEvent ENTITY_SHULKER_BULLET_HURT = SoundEventImpl.get("minecraft:entity.shulker_bullet.hurt"); + SoundEvent BLOCK_SCAFFOLDING_STEP = BuiltinSoundEvent.get("minecraft:block.scaffolding.step"); - SoundEvent ENTITY_SHULKER_CLOSE = SoundEventImpl.get("minecraft:entity.shulker.close"); + SoundEvent BLOCK_SCULK_SPREAD = BuiltinSoundEvent.get("minecraft:block.sculk.spread"); - SoundEvent ENTITY_SHULKER_DEATH = SoundEventImpl.get("minecraft:entity.shulker.death"); + SoundEvent BLOCK_SCULK_CHARGE = BuiltinSoundEvent.get("minecraft:block.sculk.charge"); - SoundEvent ENTITY_SHULKER_HURT = SoundEventImpl.get("minecraft:entity.shulker.hurt"); + SoundEvent BLOCK_SCULK_BREAK = BuiltinSoundEvent.get("minecraft:block.sculk.break"); - SoundEvent ENTITY_SHULKER_HURT_CLOSED = SoundEventImpl.get("minecraft:entity.shulker.hurt_closed"); + SoundEvent BLOCK_SCULK_FALL = BuiltinSoundEvent.get("minecraft:block.sculk.fall"); - SoundEvent ENTITY_SHULKER_OPEN = SoundEventImpl.get("minecraft:entity.shulker.open"); + SoundEvent BLOCK_SCULK_HIT = BuiltinSoundEvent.get("minecraft:block.sculk.hit"); - SoundEvent ENTITY_SHULKER_SHOOT = SoundEventImpl.get("minecraft:entity.shulker.shoot"); + SoundEvent BLOCK_SCULK_PLACE = BuiltinSoundEvent.get("minecraft:block.sculk.place"); - SoundEvent ENTITY_SHULKER_TELEPORT = SoundEventImpl.get("minecraft:entity.shulker.teleport"); + SoundEvent BLOCK_SCULK_STEP = BuiltinSoundEvent.get("minecraft:block.sculk.step"); - SoundEvent ENTITY_SILVERFISH_AMBIENT = SoundEventImpl.get("minecraft:entity.silverfish.ambient"); + SoundEvent BLOCK_SCULK_CATALYST_BLOOM = BuiltinSoundEvent.get("minecraft:block.sculk_catalyst.bloom"); - SoundEvent ENTITY_SILVERFISH_DEATH = SoundEventImpl.get("minecraft:entity.silverfish.death"); + SoundEvent BLOCK_SCULK_CATALYST_BREAK = BuiltinSoundEvent.get("minecraft:block.sculk_catalyst.break"); - SoundEvent ENTITY_SILVERFISH_HURT = SoundEventImpl.get("minecraft:entity.silverfish.hurt"); + SoundEvent BLOCK_SCULK_CATALYST_FALL = BuiltinSoundEvent.get("minecraft:block.sculk_catalyst.fall"); - SoundEvent ENTITY_SILVERFISH_STEP = SoundEventImpl.get("minecraft:entity.silverfish.step"); + SoundEvent BLOCK_SCULK_CATALYST_HIT = BuiltinSoundEvent.get("minecraft:block.sculk_catalyst.hit"); - SoundEvent ENTITY_SKELETON_AMBIENT = SoundEventImpl.get("minecraft:entity.skeleton.ambient"); + SoundEvent BLOCK_SCULK_CATALYST_PLACE = BuiltinSoundEvent.get("minecraft:block.sculk_catalyst.place"); - SoundEvent ENTITY_SKELETON_CONVERTED_TO_STRAY = SoundEventImpl.get("minecraft:entity.skeleton.converted_to_stray"); + SoundEvent BLOCK_SCULK_CATALYST_STEP = BuiltinSoundEvent.get("minecraft:block.sculk_catalyst.step"); - SoundEvent ENTITY_SKELETON_DEATH = SoundEventImpl.get("minecraft:entity.skeleton.death"); + SoundEvent BLOCK_SCULK_SENSOR_CLICKING = BuiltinSoundEvent.get("minecraft:block.sculk_sensor.clicking"); - SoundEvent ENTITY_SKELETON_HORSE_AMBIENT = SoundEventImpl.get("minecraft:entity.skeleton_horse.ambient"); + SoundEvent BLOCK_SCULK_SENSOR_CLICKING_STOP = BuiltinSoundEvent.get("minecraft:block.sculk_sensor.clicking_stop"); - SoundEvent ENTITY_SKELETON_HORSE_DEATH = SoundEventImpl.get("minecraft:entity.skeleton_horse.death"); + SoundEvent BLOCK_SCULK_SENSOR_BREAK = BuiltinSoundEvent.get("minecraft:block.sculk_sensor.break"); - SoundEvent ENTITY_SKELETON_HORSE_HURT = SoundEventImpl.get("minecraft:entity.skeleton_horse.hurt"); + SoundEvent BLOCK_SCULK_SENSOR_FALL = BuiltinSoundEvent.get("minecraft:block.sculk_sensor.fall"); - SoundEvent ENTITY_SKELETON_HORSE_SWIM = SoundEventImpl.get("minecraft:entity.skeleton_horse.swim"); + SoundEvent BLOCK_SCULK_SENSOR_HIT = BuiltinSoundEvent.get("minecraft:block.sculk_sensor.hit"); - SoundEvent ENTITY_SKELETON_HORSE_AMBIENT_WATER = SoundEventImpl.get("minecraft:entity.skeleton_horse.ambient_water"); + SoundEvent BLOCK_SCULK_SENSOR_PLACE = BuiltinSoundEvent.get("minecraft:block.sculk_sensor.place"); - SoundEvent ENTITY_SKELETON_HORSE_GALLOP_WATER = SoundEventImpl.get("minecraft:entity.skeleton_horse.gallop_water"); + SoundEvent BLOCK_SCULK_SENSOR_STEP = BuiltinSoundEvent.get("minecraft:block.sculk_sensor.step"); - SoundEvent ENTITY_SKELETON_HORSE_JUMP_WATER = SoundEventImpl.get("minecraft:entity.skeleton_horse.jump_water"); + SoundEvent BLOCK_SCULK_SHRIEKER_BREAK = BuiltinSoundEvent.get("minecraft:block.sculk_shrieker.break"); - SoundEvent ENTITY_SKELETON_HORSE_STEP_WATER = SoundEventImpl.get("minecraft:entity.skeleton_horse.step_water"); + SoundEvent BLOCK_SCULK_SHRIEKER_FALL = BuiltinSoundEvent.get("minecraft:block.sculk_shrieker.fall"); - SoundEvent ENTITY_SKELETON_HURT = SoundEventImpl.get("minecraft:entity.skeleton.hurt"); + SoundEvent BLOCK_SCULK_SHRIEKER_HIT = BuiltinSoundEvent.get("minecraft:block.sculk_shrieker.hit"); - SoundEvent ENTITY_SKELETON_SHOOT = SoundEventImpl.get("minecraft:entity.skeleton.shoot"); + SoundEvent BLOCK_SCULK_SHRIEKER_PLACE = BuiltinSoundEvent.get("minecraft:block.sculk_shrieker.place"); - SoundEvent ENTITY_SKELETON_STEP = SoundEventImpl.get("minecraft:entity.skeleton.step"); + SoundEvent BLOCK_SCULK_SHRIEKER_SHRIEK = BuiltinSoundEvent.get("minecraft:block.sculk_shrieker.shriek"); - SoundEvent ENTITY_SLIME_ATTACK = SoundEventImpl.get("minecraft:entity.slime.attack"); + SoundEvent BLOCK_SCULK_SHRIEKER_STEP = BuiltinSoundEvent.get("minecraft:block.sculk_shrieker.step"); - SoundEvent ENTITY_SLIME_DEATH = SoundEventImpl.get("minecraft:entity.slime.death"); + SoundEvent BLOCK_SCULK_VEIN_BREAK = BuiltinSoundEvent.get("minecraft:block.sculk_vein.break"); - SoundEvent ENTITY_SLIME_HURT = SoundEventImpl.get("minecraft:entity.slime.hurt"); + SoundEvent BLOCK_SCULK_VEIN_FALL = BuiltinSoundEvent.get("minecraft:block.sculk_vein.fall"); - SoundEvent ENTITY_SLIME_JUMP = SoundEventImpl.get("minecraft:entity.slime.jump"); + SoundEvent BLOCK_SCULK_VEIN_HIT = BuiltinSoundEvent.get("minecraft:block.sculk_vein.hit"); - SoundEvent ENTITY_SLIME_SQUISH = SoundEventImpl.get("minecraft:entity.slime.squish"); + SoundEvent BLOCK_SCULK_VEIN_PLACE = BuiltinSoundEvent.get("minecraft:block.sculk_vein.place"); - SoundEvent BLOCK_SLIME_BLOCK_BREAK = SoundEventImpl.get("minecraft:block.slime_block.break"); + SoundEvent BLOCK_SCULK_VEIN_STEP = BuiltinSoundEvent.get("minecraft:block.sculk_vein.step"); - SoundEvent BLOCK_SLIME_BLOCK_FALL = SoundEventImpl.get("minecraft:block.slime_block.fall"); + SoundEvent ENTITY_SHEEP_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.sheep.ambient"); - SoundEvent BLOCK_SLIME_BLOCK_HIT = SoundEventImpl.get("minecraft:block.slime_block.hit"); + SoundEvent ENTITY_SHEEP_DEATH = BuiltinSoundEvent.get("minecraft:entity.sheep.death"); - SoundEvent BLOCK_SLIME_BLOCK_PLACE = SoundEventImpl.get("minecraft:block.slime_block.place"); + SoundEvent ENTITY_SHEEP_HURT = BuiltinSoundEvent.get("minecraft:entity.sheep.hurt"); - SoundEvent BLOCK_SLIME_BLOCK_STEP = SoundEventImpl.get("minecraft:block.slime_block.step"); + SoundEvent ENTITY_SHEEP_SHEAR = BuiltinSoundEvent.get("minecraft:entity.sheep.shear"); - SoundEvent BLOCK_SMALL_AMETHYST_BUD_BREAK = SoundEventImpl.get("minecraft:block.small_amethyst_bud.break"); + SoundEvent ENTITY_SHEEP_STEP = BuiltinSoundEvent.get("minecraft:entity.sheep.step"); - SoundEvent BLOCK_SMALL_AMETHYST_BUD_PLACE = SoundEventImpl.get("minecraft:block.small_amethyst_bud.place"); + SoundEvent ITEM_SHIELD_BLOCK = BuiltinSoundEvent.get("minecraft:item.shield.block"); - SoundEvent BLOCK_SMALL_DRIPLEAF_BREAK = SoundEventImpl.get("minecraft:block.small_dripleaf.break"); + SoundEvent ITEM_SHIELD_BREAK = BuiltinSoundEvent.get("minecraft:item.shield.break"); - SoundEvent BLOCK_SMALL_DRIPLEAF_FALL = SoundEventImpl.get("minecraft:block.small_dripleaf.fall"); + SoundEvent BLOCK_SHROOMLIGHT_BREAK = BuiltinSoundEvent.get("minecraft:block.shroomlight.break"); - SoundEvent BLOCK_SMALL_DRIPLEAF_HIT = SoundEventImpl.get("minecraft:block.small_dripleaf.hit"); + SoundEvent BLOCK_SHROOMLIGHT_STEP = BuiltinSoundEvent.get("minecraft:block.shroomlight.step"); - SoundEvent BLOCK_SMALL_DRIPLEAF_PLACE = SoundEventImpl.get("minecraft:block.small_dripleaf.place"); + SoundEvent BLOCK_SHROOMLIGHT_PLACE = BuiltinSoundEvent.get("minecraft:block.shroomlight.place"); - SoundEvent BLOCK_SMALL_DRIPLEAF_STEP = SoundEventImpl.get("minecraft:block.small_dripleaf.step"); + SoundEvent BLOCK_SHROOMLIGHT_HIT = BuiltinSoundEvent.get("minecraft:block.shroomlight.hit"); - SoundEvent BLOCK_SOUL_SAND_BREAK = SoundEventImpl.get("minecraft:block.soul_sand.break"); + SoundEvent BLOCK_SHROOMLIGHT_FALL = BuiltinSoundEvent.get("minecraft:block.shroomlight.fall"); - SoundEvent BLOCK_SOUL_SAND_STEP = SoundEventImpl.get("minecraft:block.soul_sand.step"); + SoundEvent ITEM_SHOVEL_FLATTEN = BuiltinSoundEvent.get("minecraft:item.shovel.flatten"); - SoundEvent BLOCK_SOUL_SAND_PLACE = SoundEventImpl.get("minecraft:block.soul_sand.place"); + SoundEvent ENTITY_SHULKER_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.shulker.ambient"); - SoundEvent BLOCK_SOUL_SAND_HIT = SoundEventImpl.get("minecraft:block.soul_sand.hit"); + SoundEvent BLOCK_SHULKER_BOX_CLOSE = BuiltinSoundEvent.get("minecraft:block.shulker_box.close"); - SoundEvent BLOCK_SOUL_SAND_FALL = SoundEventImpl.get("minecraft:block.soul_sand.fall"); + SoundEvent BLOCK_SHULKER_BOX_OPEN = BuiltinSoundEvent.get("minecraft:block.shulker_box.open"); - SoundEvent BLOCK_SOUL_SOIL_BREAK = SoundEventImpl.get("minecraft:block.soul_soil.break"); + SoundEvent ENTITY_SHULKER_BULLET_HIT = BuiltinSoundEvent.get("minecraft:entity.shulker_bullet.hit"); - SoundEvent BLOCK_SOUL_SOIL_STEP = SoundEventImpl.get("minecraft:block.soul_soil.step"); + SoundEvent ENTITY_SHULKER_BULLET_HURT = BuiltinSoundEvent.get("minecraft:entity.shulker_bullet.hurt"); - SoundEvent BLOCK_SOUL_SOIL_PLACE = SoundEventImpl.get("minecraft:block.soul_soil.place"); + SoundEvent ENTITY_SHULKER_CLOSE = BuiltinSoundEvent.get("minecraft:entity.shulker.close"); - SoundEvent BLOCK_SOUL_SOIL_HIT = SoundEventImpl.get("minecraft:block.soul_soil.hit"); + SoundEvent ENTITY_SHULKER_DEATH = BuiltinSoundEvent.get("minecraft:entity.shulker.death"); - SoundEvent BLOCK_SOUL_SOIL_FALL = SoundEventImpl.get("minecraft:block.soul_soil.fall"); + SoundEvent ENTITY_SHULKER_HURT = BuiltinSoundEvent.get("minecraft:entity.shulker.hurt"); - SoundEvent PARTICLE_SOUL_ESCAPE = SoundEventImpl.get("minecraft:particle.soul_escape"); + SoundEvent ENTITY_SHULKER_HURT_CLOSED = BuiltinSoundEvent.get("minecraft:entity.shulker.hurt_closed"); - SoundEvent BLOCK_SPORE_BLOSSOM_BREAK = SoundEventImpl.get("minecraft:block.spore_blossom.break"); + SoundEvent ENTITY_SHULKER_OPEN = BuiltinSoundEvent.get("minecraft:entity.shulker.open"); - SoundEvent BLOCK_SPORE_BLOSSOM_FALL = SoundEventImpl.get("minecraft:block.spore_blossom.fall"); + SoundEvent ENTITY_SHULKER_SHOOT = BuiltinSoundEvent.get("minecraft:entity.shulker.shoot"); - SoundEvent BLOCK_SPORE_BLOSSOM_HIT = SoundEventImpl.get("minecraft:block.spore_blossom.hit"); + SoundEvent ENTITY_SHULKER_TELEPORT = BuiltinSoundEvent.get("minecraft:entity.shulker.teleport"); - SoundEvent BLOCK_SPORE_BLOSSOM_PLACE = SoundEventImpl.get("minecraft:block.spore_blossom.place"); + SoundEvent ENTITY_SILVERFISH_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.silverfish.ambient"); - SoundEvent BLOCK_SPORE_BLOSSOM_STEP = SoundEventImpl.get("minecraft:block.spore_blossom.step"); + SoundEvent ENTITY_SILVERFISH_DEATH = BuiltinSoundEvent.get("minecraft:entity.silverfish.death"); - SoundEvent ENTITY_STRIDER_AMBIENT = SoundEventImpl.get("minecraft:entity.strider.ambient"); + SoundEvent ENTITY_SILVERFISH_HURT = BuiltinSoundEvent.get("minecraft:entity.silverfish.hurt"); - SoundEvent ENTITY_STRIDER_HAPPY = SoundEventImpl.get("minecraft:entity.strider.happy"); + SoundEvent ENTITY_SILVERFISH_STEP = BuiltinSoundEvent.get("minecraft:entity.silverfish.step"); - SoundEvent ENTITY_STRIDER_RETREAT = SoundEventImpl.get("minecraft:entity.strider.retreat"); + SoundEvent ENTITY_SKELETON_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.skeleton.ambient"); - SoundEvent ENTITY_STRIDER_DEATH = SoundEventImpl.get("minecraft:entity.strider.death"); + SoundEvent ENTITY_SKELETON_CONVERTED_TO_STRAY = BuiltinSoundEvent.get("minecraft:entity.skeleton.converted_to_stray"); - SoundEvent ENTITY_STRIDER_HURT = SoundEventImpl.get("minecraft:entity.strider.hurt"); + SoundEvent ENTITY_SKELETON_DEATH = BuiltinSoundEvent.get("minecraft:entity.skeleton.death"); - SoundEvent ENTITY_STRIDER_STEP = SoundEventImpl.get("minecraft:entity.strider.step"); + SoundEvent ENTITY_SKELETON_HORSE_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.skeleton_horse.ambient"); - SoundEvent ENTITY_STRIDER_STEP_LAVA = SoundEventImpl.get("minecraft:entity.strider.step_lava"); + SoundEvent ENTITY_SKELETON_HORSE_DEATH = BuiltinSoundEvent.get("minecraft:entity.skeleton_horse.death"); - SoundEvent ENTITY_STRIDER_EAT = SoundEventImpl.get("minecraft:entity.strider.eat"); + SoundEvent ENTITY_SKELETON_HORSE_HURT = BuiltinSoundEvent.get("minecraft:entity.skeleton_horse.hurt"); - SoundEvent ENTITY_STRIDER_SADDLE = SoundEventImpl.get("minecraft:entity.strider.saddle"); + SoundEvent ENTITY_SKELETON_HORSE_SWIM = BuiltinSoundEvent.get("minecraft:entity.skeleton_horse.swim"); - SoundEvent ENTITY_SLIME_DEATH_SMALL = SoundEventImpl.get("minecraft:entity.slime.death_small"); + SoundEvent ENTITY_SKELETON_HORSE_AMBIENT_WATER = BuiltinSoundEvent.get("minecraft:entity.skeleton_horse.ambient_water"); - SoundEvent ENTITY_SLIME_HURT_SMALL = SoundEventImpl.get("minecraft:entity.slime.hurt_small"); + SoundEvent ENTITY_SKELETON_HORSE_GALLOP_WATER = BuiltinSoundEvent.get("minecraft:entity.skeleton_horse.gallop_water"); - SoundEvent ENTITY_SLIME_JUMP_SMALL = SoundEventImpl.get("minecraft:entity.slime.jump_small"); + SoundEvent ENTITY_SKELETON_HORSE_JUMP_WATER = BuiltinSoundEvent.get("minecraft:entity.skeleton_horse.jump_water"); - SoundEvent ENTITY_SLIME_SQUISH_SMALL = SoundEventImpl.get("minecraft:entity.slime.squish_small"); + SoundEvent ENTITY_SKELETON_HORSE_STEP_WATER = BuiltinSoundEvent.get("minecraft:entity.skeleton_horse.step_water"); - SoundEvent BLOCK_SMITHING_TABLE_USE = SoundEventImpl.get("minecraft:block.smithing_table.use"); + SoundEvent ENTITY_SKELETON_HURT = BuiltinSoundEvent.get("minecraft:entity.skeleton.hurt"); - SoundEvent BLOCK_SMOKER_SMOKE = SoundEventImpl.get("minecraft:block.smoker.smoke"); + SoundEvent ENTITY_SKELETON_SHOOT = BuiltinSoundEvent.get("minecraft:entity.skeleton.shoot"); - SoundEvent ENTITY_SNIFFER_STEP = SoundEventImpl.get("minecraft:entity.sniffer.step"); + SoundEvent ENTITY_SKELETON_STEP = BuiltinSoundEvent.get("minecraft:entity.skeleton.step"); - SoundEvent ENTITY_SNIFFER_EAT = SoundEventImpl.get("minecraft:entity.sniffer.eat"); + SoundEvent ENTITY_SLIME_ATTACK = BuiltinSoundEvent.get("minecraft:entity.slime.attack"); - SoundEvent ENTITY_SNIFFER_IDLE = SoundEventImpl.get("minecraft:entity.sniffer.idle"); + SoundEvent ENTITY_SLIME_DEATH = BuiltinSoundEvent.get("minecraft:entity.slime.death"); - SoundEvent ENTITY_SNIFFER_HURT = SoundEventImpl.get("minecraft:entity.sniffer.hurt"); + SoundEvent ENTITY_SLIME_HURT = BuiltinSoundEvent.get("minecraft:entity.slime.hurt"); - SoundEvent ENTITY_SNIFFER_DEATH = SoundEventImpl.get("minecraft:entity.sniffer.death"); + SoundEvent ENTITY_SLIME_JUMP = BuiltinSoundEvent.get("minecraft:entity.slime.jump"); - SoundEvent ENTITY_SNIFFER_DROP_SEED = SoundEventImpl.get("minecraft:entity.sniffer.drop_seed"); + SoundEvent ENTITY_SLIME_SQUISH = BuiltinSoundEvent.get("minecraft:entity.slime.squish"); - SoundEvent ENTITY_SNIFFER_SCENTING = SoundEventImpl.get("minecraft:entity.sniffer.scenting"); + SoundEvent BLOCK_SLIME_BLOCK_BREAK = BuiltinSoundEvent.get("minecraft:block.slime_block.break"); - SoundEvent ENTITY_SNIFFER_SNIFFING = SoundEventImpl.get("minecraft:entity.sniffer.sniffing"); + SoundEvent BLOCK_SLIME_BLOCK_FALL = BuiltinSoundEvent.get("minecraft:block.slime_block.fall"); - SoundEvent ENTITY_SNIFFER_SEARCHING = SoundEventImpl.get("minecraft:entity.sniffer.searching"); + SoundEvent BLOCK_SLIME_BLOCK_HIT = BuiltinSoundEvent.get("minecraft:block.slime_block.hit"); - SoundEvent ENTITY_SNIFFER_DIGGING = SoundEventImpl.get("minecraft:entity.sniffer.digging"); + SoundEvent BLOCK_SLIME_BLOCK_PLACE = BuiltinSoundEvent.get("minecraft:block.slime_block.place"); - SoundEvent ENTITY_SNIFFER_DIGGING_STOP = SoundEventImpl.get("minecraft:entity.sniffer.digging_stop"); + SoundEvent BLOCK_SLIME_BLOCK_STEP = BuiltinSoundEvent.get("minecraft:block.slime_block.step"); - SoundEvent ENTITY_SNIFFER_HAPPY = SoundEventImpl.get("minecraft:entity.sniffer.happy"); + SoundEvent BLOCK_SMALL_AMETHYST_BUD_BREAK = BuiltinSoundEvent.get("minecraft:block.small_amethyst_bud.break"); - SoundEvent BLOCK_SNIFFER_EGG_PLOP = SoundEventImpl.get("minecraft:block.sniffer_egg.plop"); + SoundEvent BLOCK_SMALL_AMETHYST_BUD_PLACE = BuiltinSoundEvent.get("minecraft:block.small_amethyst_bud.place"); - SoundEvent BLOCK_SNIFFER_EGG_CRACK = SoundEventImpl.get("minecraft:block.sniffer_egg.crack"); + SoundEvent BLOCK_SMALL_DRIPLEAF_BREAK = BuiltinSoundEvent.get("minecraft:block.small_dripleaf.break"); - SoundEvent BLOCK_SNIFFER_EGG_HATCH = SoundEventImpl.get("minecraft:block.sniffer_egg.hatch"); + SoundEvent BLOCK_SMALL_DRIPLEAF_FALL = BuiltinSoundEvent.get("minecraft:block.small_dripleaf.fall"); - SoundEvent ENTITY_SNOWBALL_THROW = SoundEventImpl.get("minecraft:entity.snowball.throw"); + SoundEvent BLOCK_SMALL_DRIPLEAF_HIT = BuiltinSoundEvent.get("minecraft:block.small_dripleaf.hit"); - SoundEvent BLOCK_SNOW_BREAK = SoundEventImpl.get("minecraft:block.snow.break"); + SoundEvent BLOCK_SMALL_DRIPLEAF_PLACE = BuiltinSoundEvent.get("minecraft:block.small_dripleaf.place"); - SoundEvent BLOCK_SNOW_FALL = SoundEventImpl.get("minecraft:block.snow.fall"); + SoundEvent BLOCK_SMALL_DRIPLEAF_STEP = BuiltinSoundEvent.get("minecraft:block.small_dripleaf.step"); - SoundEvent ENTITY_SNOW_GOLEM_AMBIENT = SoundEventImpl.get("minecraft:entity.snow_golem.ambient"); + SoundEvent BLOCK_SOUL_SAND_BREAK = BuiltinSoundEvent.get("minecraft:block.soul_sand.break"); - SoundEvent ENTITY_SNOW_GOLEM_DEATH = SoundEventImpl.get("minecraft:entity.snow_golem.death"); + SoundEvent BLOCK_SOUL_SAND_STEP = BuiltinSoundEvent.get("minecraft:block.soul_sand.step"); - SoundEvent ENTITY_SNOW_GOLEM_HURT = SoundEventImpl.get("minecraft:entity.snow_golem.hurt"); + SoundEvent BLOCK_SOUL_SAND_PLACE = BuiltinSoundEvent.get("minecraft:block.soul_sand.place"); - SoundEvent ENTITY_SNOW_GOLEM_SHOOT = SoundEventImpl.get("minecraft:entity.snow_golem.shoot"); + SoundEvent BLOCK_SOUL_SAND_HIT = BuiltinSoundEvent.get("minecraft:block.soul_sand.hit"); - SoundEvent ENTITY_SNOW_GOLEM_SHEAR = SoundEventImpl.get("minecraft:entity.snow_golem.shear"); + SoundEvent BLOCK_SOUL_SAND_FALL = BuiltinSoundEvent.get("minecraft:block.soul_sand.fall"); - SoundEvent BLOCK_SNOW_HIT = SoundEventImpl.get("minecraft:block.snow.hit"); + SoundEvent BLOCK_SOUL_SOIL_BREAK = BuiltinSoundEvent.get("minecraft:block.soul_soil.break"); - SoundEvent BLOCK_SNOW_PLACE = SoundEventImpl.get("minecraft:block.snow.place"); + SoundEvent BLOCK_SOUL_SOIL_STEP = BuiltinSoundEvent.get("minecraft:block.soul_soil.step"); - SoundEvent BLOCK_SNOW_STEP = SoundEventImpl.get("minecraft:block.snow.step"); + SoundEvent BLOCK_SOUL_SOIL_PLACE = BuiltinSoundEvent.get("minecraft:block.soul_soil.place"); - SoundEvent ENTITY_SPIDER_AMBIENT = SoundEventImpl.get("minecraft:entity.spider.ambient"); + SoundEvent BLOCK_SOUL_SOIL_HIT = BuiltinSoundEvent.get("minecraft:block.soul_soil.hit"); - SoundEvent ENTITY_SPIDER_DEATH = SoundEventImpl.get("minecraft:entity.spider.death"); + SoundEvent BLOCK_SOUL_SOIL_FALL = BuiltinSoundEvent.get("minecraft:block.soul_soil.fall"); - SoundEvent ENTITY_SPIDER_HURT = SoundEventImpl.get("minecraft:entity.spider.hurt"); + SoundEvent PARTICLE_SOUL_ESCAPE = BuiltinSoundEvent.get("minecraft:particle.soul_escape"); - SoundEvent ENTITY_SPIDER_STEP = SoundEventImpl.get("minecraft:entity.spider.step"); + SoundEvent BLOCK_SPORE_BLOSSOM_BREAK = BuiltinSoundEvent.get("minecraft:block.spore_blossom.break"); - SoundEvent ENTITY_SPLASH_POTION_BREAK = SoundEventImpl.get("minecraft:entity.splash_potion.break"); + SoundEvent BLOCK_SPORE_BLOSSOM_FALL = BuiltinSoundEvent.get("minecraft:block.spore_blossom.fall"); - SoundEvent ENTITY_SPLASH_POTION_THROW = SoundEventImpl.get("minecraft:entity.splash_potion.throw"); + SoundEvent BLOCK_SPORE_BLOSSOM_HIT = BuiltinSoundEvent.get("minecraft:block.spore_blossom.hit"); - SoundEvent BLOCK_SPONGE_BREAK = SoundEventImpl.get("minecraft:block.sponge.break"); + SoundEvent BLOCK_SPORE_BLOSSOM_PLACE = BuiltinSoundEvent.get("minecraft:block.spore_blossom.place"); - SoundEvent BLOCK_SPONGE_FALL = SoundEventImpl.get("minecraft:block.sponge.fall"); + SoundEvent BLOCK_SPORE_BLOSSOM_STEP = BuiltinSoundEvent.get("minecraft:block.spore_blossom.step"); - SoundEvent BLOCK_SPONGE_HIT = SoundEventImpl.get("minecraft:block.sponge.hit"); + SoundEvent ENTITY_STRIDER_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.strider.ambient"); - SoundEvent BLOCK_SPONGE_PLACE = SoundEventImpl.get("minecraft:block.sponge.place"); + SoundEvent ENTITY_STRIDER_HAPPY = BuiltinSoundEvent.get("minecraft:entity.strider.happy"); - SoundEvent BLOCK_SPONGE_STEP = SoundEventImpl.get("minecraft:block.sponge.step"); + SoundEvent ENTITY_STRIDER_RETREAT = BuiltinSoundEvent.get("minecraft:entity.strider.retreat"); - SoundEvent BLOCK_SPONGE_ABSORB = SoundEventImpl.get("minecraft:block.sponge.absorb"); + SoundEvent ENTITY_STRIDER_DEATH = BuiltinSoundEvent.get("minecraft:entity.strider.death"); - SoundEvent ITEM_SPYGLASS_USE = SoundEventImpl.get("minecraft:item.spyglass.use"); + SoundEvent ENTITY_STRIDER_HURT = BuiltinSoundEvent.get("minecraft:entity.strider.hurt"); - SoundEvent ITEM_SPYGLASS_STOP_USING = SoundEventImpl.get("minecraft:item.spyglass.stop_using"); + SoundEvent ENTITY_STRIDER_STEP = BuiltinSoundEvent.get("minecraft:entity.strider.step"); - SoundEvent ENTITY_SQUID_AMBIENT = SoundEventImpl.get("minecraft:entity.squid.ambient"); + SoundEvent ENTITY_STRIDER_STEP_LAVA = BuiltinSoundEvent.get("minecraft:entity.strider.step_lava"); - SoundEvent ENTITY_SQUID_DEATH = SoundEventImpl.get("minecraft:entity.squid.death"); + SoundEvent ENTITY_STRIDER_EAT = BuiltinSoundEvent.get("minecraft:entity.strider.eat"); - SoundEvent ENTITY_SQUID_HURT = SoundEventImpl.get("minecraft:entity.squid.hurt"); + SoundEvent ENTITY_STRIDER_SADDLE = BuiltinSoundEvent.get("minecraft:entity.strider.saddle"); - SoundEvent ENTITY_SQUID_SQUIRT = SoundEventImpl.get("minecraft:entity.squid.squirt"); + SoundEvent ENTITY_SLIME_DEATH_SMALL = BuiltinSoundEvent.get("minecraft:entity.slime.death_small"); - SoundEvent BLOCK_STONE_BREAK = SoundEventImpl.get("minecraft:block.stone.break"); + SoundEvent ENTITY_SLIME_HURT_SMALL = BuiltinSoundEvent.get("minecraft:entity.slime.hurt_small"); - SoundEvent BLOCK_STONE_BUTTON_CLICK_OFF = SoundEventImpl.get("minecraft:block.stone_button.click_off"); + SoundEvent ENTITY_SLIME_JUMP_SMALL = BuiltinSoundEvent.get("minecraft:entity.slime.jump_small"); - SoundEvent BLOCK_STONE_BUTTON_CLICK_ON = SoundEventImpl.get("minecraft:block.stone_button.click_on"); + SoundEvent ENTITY_SLIME_SQUISH_SMALL = BuiltinSoundEvent.get("minecraft:entity.slime.squish_small"); - SoundEvent BLOCK_STONE_FALL = SoundEventImpl.get("minecraft:block.stone.fall"); + SoundEvent BLOCK_SMITHING_TABLE_USE = BuiltinSoundEvent.get("minecraft:block.smithing_table.use"); - SoundEvent BLOCK_STONE_HIT = SoundEventImpl.get("minecraft:block.stone.hit"); + SoundEvent BLOCK_SMOKER_SMOKE = BuiltinSoundEvent.get("minecraft:block.smoker.smoke"); - SoundEvent BLOCK_STONE_PLACE = SoundEventImpl.get("minecraft:block.stone.place"); + SoundEvent ENTITY_SNIFFER_STEP = BuiltinSoundEvent.get("minecraft:entity.sniffer.step"); - SoundEvent BLOCK_STONE_PRESSURE_PLATE_CLICK_OFF = SoundEventImpl.get("minecraft:block.stone_pressure_plate.click_off"); + SoundEvent ENTITY_SNIFFER_EAT = BuiltinSoundEvent.get("minecraft:entity.sniffer.eat"); - SoundEvent BLOCK_STONE_PRESSURE_PLATE_CLICK_ON = SoundEventImpl.get("minecraft:block.stone_pressure_plate.click_on"); + SoundEvent ENTITY_SNIFFER_IDLE = BuiltinSoundEvent.get("minecraft:entity.sniffer.idle"); - SoundEvent BLOCK_STONE_STEP = SoundEventImpl.get("minecraft:block.stone.step"); + SoundEvent ENTITY_SNIFFER_HURT = BuiltinSoundEvent.get("minecraft:entity.sniffer.hurt"); - SoundEvent ENTITY_STRAY_AMBIENT = SoundEventImpl.get("minecraft:entity.stray.ambient"); + SoundEvent ENTITY_SNIFFER_DEATH = BuiltinSoundEvent.get("minecraft:entity.sniffer.death"); - SoundEvent ENTITY_STRAY_DEATH = SoundEventImpl.get("minecraft:entity.stray.death"); + SoundEvent ENTITY_SNIFFER_DROP_SEED = BuiltinSoundEvent.get("minecraft:entity.sniffer.drop_seed"); - SoundEvent ENTITY_STRAY_HURT = SoundEventImpl.get("minecraft:entity.stray.hurt"); + SoundEvent ENTITY_SNIFFER_SCENTING = BuiltinSoundEvent.get("minecraft:entity.sniffer.scenting"); - SoundEvent ENTITY_STRAY_STEP = SoundEventImpl.get("minecraft:entity.stray.step"); + SoundEvent ENTITY_SNIFFER_SNIFFING = BuiltinSoundEvent.get("minecraft:entity.sniffer.sniffing"); - SoundEvent BLOCK_SWEET_BERRY_BUSH_BREAK = SoundEventImpl.get("minecraft:block.sweet_berry_bush.break"); + SoundEvent ENTITY_SNIFFER_SEARCHING = BuiltinSoundEvent.get("minecraft:entity.sniffer.searching"); - SoundEvent BLOCK_SWEET_BERRY_BUSH_PLACE = SoundEventImpl.get("minecraft:block.sweet_berry_bush.place"); + SoundEvent ENTITY_SNIFFER_DIGGING = BuiltinSoundEvent.get("minecraft:entity.sniffer.digging"); - SoundEvent BLOCK_SWEET_BERRY_BUSH_PICK_BERRIES = SoundEventImpl.get("minecraft:block.sweet_berry_bush.pick_berries"); + SoundEvent ENTITY_SNIFFER_DIGGING_STOP = BuiltinSoundEvent.get("minecraft:entity.sniffer.digging_stop"); - SoundEvent ENTITY_TADPOLE_DEATH = SoundEventImpl.get("minecraft:entity.tadpole.death"); + SoundEvent ENTITY_SNIFFER_HAPPY = BuiltinSoundEvent.get("minecraft:entity.sniffer.happy"); - SoundEvent ENTITY_TADPOLE_FLOP = SoundEventImpl.get("minecraft:entity.tadpole.flop"); + SoundEvent BLOCK_SNIFFER_EGG_PLOP = BuiltinSoundEvent.get("minecraft:block.sniffer_egg.plop"); - SoundEvent ENTITY_TADPOLE_GROW_UP = SoundEventImpl.get("minecraft:entity.tadpole.grow_up"); + SoundEvent BLOCK_SNIFFER_EGG_CRACK = BuiltinSoundEvent.get("minecraft:block.sniffer_egg.crack"); - SoundEvent ENTITY_TADPOLE_HURT = SoundEventImpl.get("minecraft:entity.tadpole.hurt"); + SoundEvent BLOCK_SNIFFER_EGG_HATCH = BuiltinSoundEvent.get("minecraft:block.sniffer_egg.hatch"); - SoundEvent ENCHANT_THORNS_HIT = SoundEventImpl.get("minecraft:enchant.thorns.hit"); + SoundEvent ENTITY_SNOWBALL_THROW = BuiltinSoundEvent.get("minecraft:entity.snowball.throw"); - SoundEvent ENTITY_TNT_PRIMED = SoundEventImpl.get("minecraft:entity.tnt.primed"); + SoundEvent BLOCK_SNOW_BREAK = BuiltinSoundEvent.get("minecraft:block.snow.break"); - SoundEvent ITEM_TOTEM_USE = SoundEventImpl.get("minecraft:item.totem.use"); + SoundEvent BLOCK_SNOW_FALL = BuiltinSoundEvent.get("minecraft:block.snow.fall"); - SoundEvent ITEM_TRIDENT_HIT = SoundEventImpl.get("minecraft:item.trident.hit"); + SoundEvent ENTITY_SNOW_GOLEM_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.snow_golem.ambient"); - SoundEvent ITEM_TRIDENT_HIT_GROUND = SoundEventImpl.get("minecraft:item.trident.hit_ground"); + SoundEvent ENTITY_SNOW_GOLEM_DEATH = BuiltinSoundEvent.get("minecraft:entity.snow_golem.death"); - SoundEvent ITEM_TRIDENT_RETURN = SoundEventImpl.get("minecraft:item.trident.return"); + SoundEvent ENTITY_SNOW_GOLEM_HURT = BuiltinSoundEvent.get("minecraft:entity.snow_golem.hurt"); - SoundEvent ITEM_TRIDENT_RIPTIDE_1 = SoundEventImpl.get("minecraft:item.trident.riptide_1"); + SoundEvent ENTITY_SNOW_GOLEM_SHOOT = BuiltinSoundEvent.get("minecraft:entity.snow_golem.shoot"); - SoundEvent ITEM_TRIDENT_RIPTIDE_2 = SoundEventImpl.get("minecraft:item.trident.riptide_2"); + SoundEvent ENTITY_SNOW_GOLEM_SHEAR = BuiltinSoundEvent.get("minecraft:entity.snow_golem.shear"); - SoundEvent ITEM_TRIDENT_RIPTIDE_3 = SoundEventImpl.get("minecraft:item.trident.riptide_3"); + SoundEvent BLOCK_SNOW_HIT = BuiltinSoundEvent.get("minecraft:block.snow.hit"); - SoundEvent ITEM_TRIDENT_THROW = SoundEventImpl.get("minecraft:item.trident.throw"); + SoundEvent BLOCK_SNOW_PLACE = BuiltinSoundEvent.get("minecraft:block.snow.place"); - SoundEvent ITEM_TRIDENT_THUNDER = SoundEventImpl.get("minecraft:item.trident.thunder"); + SoundEvent BLOCK_SNOW_STEP = BuiltinSoundEvent.get("minecraft:block.snow.step"); - SoundEvent BLOCK_TRIPWIRE_ATTACH = SoundEventImpl.get("minecraft:block.tripwire.attach"); + SoundEvent ENTITY_SPIDER_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.spider.ambient"); - SoundEvent BLOCK_TRIPWIRE_CLICK_OFF = SoundEventImpl.get("minecraft:block.tripwire.click_off"); + SoundEvent ENTITY_SPIDER_DEATH = BuiltinSoundEvent.get("minecraft:entity.spider.death"); - SoundEvent BLOCK_TRIPWIRE_CLICK_ON = SoundEventImpl.get("minecraft:block.tripwire.click_on"); + SoundEvent ENTITY_SPIDER_HURT = BuiltinSoundEvent.get("minecraft:entity.spider.hurt"); - SoundEvent BLOCK_TRIPWIRE_DETACH = SoundEventImpl.get("minecraft:block.tripwire.detach"); + SoundEvent ENTITY_SPIDER_STEP = BuiltinSoundEvent.get("minecraft:entity.spider.step"); - SoundEvent ENTITY_TROPICAL_FISH_AMBIENT = SoundEventImpl.get("minecraft:entity.tropical_fish.ambient"); + SoundEvent ENTITY_SPLASH_POTION_BREAK = BuiltinSoundEvent.get("minecraft:entity.splash_potion.break"); - SoundEvent ENTITY_TROPICAL_FISH_DEATH = SoundEventImpl.get("minecraft:entity.tropical_fish.death"); + SoundEvent ENTITY_SPLASH_POTION_THROW = BuiltinSoundEvent.get("minecraft:entity.splash_potion.throw"); - SoundEvent ENTITY_TROPICAL_FISH_FLOP = SoundEventImpl.get("minecraft:entity.tropical_fish.flop"); + SoundEvent BLOCK_SPONGE_BREAK = BuiltinSoundEvent.get("minecraft:block.sponge.break"); - SoundEvent ENTITY_TROPICAL_FISH_HURT = SoundEventImpl.get("minecraft:entity.tropical_fish.hurt"); + SoundEvent BLOCK_SPONGE_FALL = BuiltinSoundEvent.get("minecraft:block.sponge.fall"); - SoundEvent BLOCK_TUFF_BREAK = SoundEventImpl.get("minecraft:block.tuff.break"); + SoundEvent BLOCK_SPONGE_HIT = BuiltinSoundEvent.get("minecraft:block.sponge.hit"); - SoundEvent BLOCK_TUFF_STEP = SoundEventImpl.get("minecraft:block.tuff.step"); + SoundEvent BLOCK_SPONGE_PLACE = BuiltinSoundEvent.get("minecraft:block.sponge.place"); - SoundEvent BLOCK_TUFF_PLACE = SoundEventImpl.get("minecraft:block.tuff.place"); + SoundEvent BLOCK_SPONGE_STEP = BuiltinSoundEvent.get("minecraft:block.sponge.step"); - SoundEvent BLOCK_TUFF_HIT = SoundEventImpl.get("minecraft:block.tuff.hit"); + SoundEvent BLOCK_SPONGE_ABSORB = BuiltinSoundEvent.get("minecraft:block.sponge.absorb"); - SoundEvent BLOCK_TUFF_FALL = SoundEventImpl.get("minecraft:block.tuff.fall"); + SoundEvent ITEM_SPYGLASS_USE = BuiltinSoundEvent.get("minecraft:item.spyglass.use"); - SoundEvent BLOCK_TUFF_BRICKS_BREAK = SoundEventImpl.get("minecraft:block.tuff_bricks.break"); + SoundEvent ITEM_SPYGLASS_STOP_USING = BuiltinSoundEvent.get("minecraft:item.spyglass.stop_using"); - SoundEvent BLOCK_TUFF_BRICKS_FALL = SoundEventImpl.get("minecraft:block.tuff_bricks.fall"); + SoundEvent ENTITY_SQUID_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.squid.ambient"); - SoundEvent BLOCK_TUFF_BRICKS_HIT = SoundEventImpl.get("minecraft:block.tuff_bricks.hit"); + SoundEvent ENTITY_SQUID_DEATH = BuiltinSoundEvent.get("minecraft:entity.squid.death"); - SoundEvent BLOCK_TUFF_BRICKS_PLACE = SoundEventImpl.get("minecraft:block.tuff_bricks.place"); + SoundEvent ENTITY_SQUID_HURT = BuiltinSoundEvent.get("minecraft:entity.squid.hurt"); - SoundEvent BLOCK_TUFF_BRICKS_STEP = SoundEventImpl.get("minecraft:block.tuff_bricks.step"); + SoundEvent ENTITY_SQUID_SQUIRT = BuiltinSoundEvent.get("minecraft:entity.squid.squirt"); - SoundEvent BLOCK_POLISHED_TUFF_BREAK = SoundEventImpl.get("minecraft:block.polished_tuff.break"); + SoundEvent BLOCK_STONE_BREAK = BuiltinSoundEvent.get("minecraft:block.stone.break"); - SoundEvent BLOCK_POLISHED_TUFF_FALL = SoundEventImpl.get("minecraft:block.polished_tuff.fall"); + SoundEvent BLOCK_STONE_BUTTON_CLICK_OFF = BuiltinSoundEvent.get("minecraft:block.stone_button.click_off"); - SoundEvent BLOCK_POLISHED_TUFF_HIT = SoundEventImpl.get("minecraft:block.polished_tuff.hit"); + SoundEvent BLOCK_STONE_BUTTON_CLICK_ON = BuiltinSoundEvent.get("minecraft:block.stone_button.click_on"); - SoundEvent BLOCK_POLISHED_TUFF_PLACE = SoundEventImpl.get("minecraft:block.polished_tuff.place"); + SoundEvent BLOCK_STONE_FALL = BuiltinSoundEvent.get("minecraft:block.stone.fall"); - SoundEvent BLOCK_POLISHED_TUFF_STEP = SoundEventImpl.get("minecraft:block.polished_tuff.step"); + SoundEvent BLOCK_STONE_HIT = BuiltinSoundEvent.get("minecraft:block.stone.hit"); - SoundEvent ENTITY_TURTLE_AMBIENT_LAND = SoundEventImpl.get("minecraft:entity.turtle.ambient_land"); + SoundEvent BLOCK_STONE_PLACE = BuiltinSoundEvent.get("minecraft:block.stone.place"); - SoundEvent ENTITY_TURTLE_DEATH = SoundEventImpl.get("minecraft:entity.turtle.death"); + SoundEvent BLOCK_STONE_PRESSURE_PLATE_CLICK_OFF = BuiltinSoundEvent.get("minecraft:block.stone_pressure_plate.click_off"); - SoundEvent ENTITY_TURTLE_DEATH_BABY = SoundEventImpl.get("minecraft:entity.turtle.death_baby"); + SoundEvent BLOCK_STONE_PRESSURE_PLATE_CLICK_ON = BuiltinSoundEvent.get("minecraft:block.stone_pressure_plate.click_on"); - SoundEvent ENTITY_TURTLE_EGG_BREAK = SoundEventImpl.get("minecraft:entity.turtle.egg_break"); + SoundEvent BLOCK_STONE_STEP = BuiltinSoundEvent.get("minecraft:block.stone.step"); - SoundEvent ENTITY_TURTLE_EGG_CRACK = SoundEventImpl.get("minecraft:entity.turtle.egg_crack"); + SoundEvent ENTITY_STRAY_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.stray.ambient"); - SoundEvent ENTITY_TURTLE_EGG_HATCH = SoundEventImpl.get("minecraft:entity.turtle.egg_hatch"); + SoundEvent ENTITY_STRAY_DEATH = BuiltinSoundEvent.get("minecraft:entity.stray.death"); - SoundEvent ENTITY_TURTLE_HURT = SoundEventImpl.get("minecraft:entity.turtle.hurt"); + SoundEvent ENTITY_STRAY_HURT = BuiltinSoundEvent.get("minecraft:entity.stray.hurt"); - SoundEvent ENTITY_TURTLE_HURT_BABY = SoundEventImpl.get("minecraft:entity.turtle.hurt_baby"); + SoundEvent ENTITY_STRAY_STEP = BuiltinSoundEvent.get("minecraft:entity.stray.step"); - SoundEvent ENTITY_TURTLE_LAY_EGG = SoundEventImpl.get("minecraft:entity.turtle.lay_egg"); + SoundEvent BLOCK_SWEET_BERRY_BUSH_BREAK = BuiltinSoundEvent.get("minecraft:block.sweet_berry_bush.break"); - SoundEvent ENTITY_TURTLE_SHAMBLE = SoundEventImpl.get("minecraft:entity.turtle.shamble"); + SoundEvent BLOCK_SWEET_BERRY_BUSH_PLACE = BuiltinSoundEvent.get("minecraft:block.sweet_berry_bush.place"); - SoundEvent ENTITY_TURTLE_SHAMBLE_BABY = SoundEventImpl.get("minecraft:entity.turtle.shamble_baby"); + SoundEvent BLOCK_SWEET_BERRY_BUSH_PICK_BERRIES = BuiltinSoundEvent.get("minecraft:block.sweet_berry_bush.pick_berries"); - SoundEvent ENTITY_TURTLE_SWIM = SoundEventImpl.get("minecraft:entity.turtle.swim"); + SoundEvent ENTITY_TADPOLE_DEATH = BuiltinSoundEvent.get("minecraft:entity.tadpole.death"); - SoundEvent UI_BUTTON_CLICK = SoundEventImpl.get("minecraft:ui.button.click"); + SoundEvent ENTITY_TADPOLE_FLOP = BuiltinSoundEvent.get("minecraft:entity.tadpole.flop"); - SoundEvent UI_LOOM_SELECT_PATTERN = SoundEventImpl.get("minecraft:ui.loom.select_pattern"); + SoundEvent ENTITY_TADPOLE_GROW_UP = BuiltinSoundEvent.get("minecraft:entity.tadpole.grow_up"); - SoundEvent UI_LOOM_TAKE_RESULT = SoundEventImpl.get("minecraft:ui.loom.take_result"); + SoundEvent ENTITY_TADPOLE_HURT = BuiltinSoundEvent.get("minecraft:entity.tadpole.hurt"); - SoundEvent UI_CARTOGRAPHY_TABLE_TAKE_RESULT = SoundEventImpl.get("minecraft:ui.cartography_table.take_result"); + SoundEvent ENCHANT_THORNS_HIT = BuiltinSoundEvent.get("minecraft:enchant.thorns.hit"); - SoundEvent UI_STONECUTTER_TAKE_RESULT = SoundEventImpl.get("minecraft:ui.stonecutter.take_result"); + SoundEvent ENTITY_TNT_PRIMED = BuiltinSoundEvent.get("minecraft:entity.tnt.primed"); - SoundEvent UI_STONECUTTER_SELECT_RECIPE = SoundEventImpl.get("minecraft:ui.stonecutter.select_recipe"); + SoundEvent ITEM_TOTEM_USE = BuiltinSoundEvent.get("minecraft:item.totem.use"); - SoundEvent UI_TOAST_CHALLENGE_COMPLETE = SoundEventImpl.get("minecraft:ui.toast.challenge_complete"); + SoundEvent ITEM_TRIDENT_HIT = BuiltinSoundEvent.get("minecraft:item.trident.hit"); - SoundEvent UI_TOAST_IN = SoundEventImpl.get("minecraft:ui.toast.in"); + SoundEvent ITEM_TRIDENT_HIT_GROUND = BuiltinSoundEvent.get("minecraft:item.trident.hit_ground"); - SoundEvent UI_TOAST_OUT = SoundEventImpl.get("minecraft:ui.toast.out"); + SoundEvent ITEM_TRIDENT_RETURN = BuiltinSoundEvent.get("minecraft:item.trident.return"); - SoundEvent ENTITY_VEX_AMBIENT = SoundEventImpl.get("minecraft:entity.vex.ambient"); + SoundEvent ITEM_TRIDENT_RIPTIDE_1 = BuiltinSoundEvent.get("minecraft:item.trident.riptide_1"); - SoundEvent ENTITY_VEX_CHARGE = SoundEventImpl.get("minecraft:entity.vex.charge"); + SoundEvent ITEM_TRIDENT_RIPTIDE_2 = BuiltinSoundEvent.get("minecraft:item.trident.riptide_2"); - SoundEvent ENTITY_VEX_DEATH = SoundEventImpl.get("minecraft:entity.vex.death"); + SoundEvent ITEM_TRIDENT_RIPTIDE_3 = BuiltinSoundEvent.get("minecraft:item.trident.riptide_3"); - SoundEvent ENTITY_VEX_HURT = SoundEventImpl.get("minecraft:entity.vex.hurt"); + SoundEvent ITEM_TRIDENT_THROW = BuiltinSoundEvent.get("minecraft:item.trident.throw"); - SoundEvent ENTITY_VILLAGER_AMBIENT = SoundEventImpl.get("minecraft:entity.villager.ambient"); + SoundEvent ITEM_TRIDENT_THUNDER = BuiltinSoundEvent.get("minecraft:item.trident.thunder"); - SoundEvent ENTITY_VILLAGER_CELEBRATE = SoundEventImpl.get("minecraft:entity.villager.celebrate"); + SoundEvent BLOCK_TRIPWIRE_ATTACH = BuiltinSoundEvent.get("minecraft:block.tripwire.attach"); - SoundEvent ENTITY_VILLAGER_DEATH = SoundEventImpl.get("minecraft:entity.villager.death"); + SoundEvent BLOCK_TRIPWIRE_CLICK_OFF = BuiltinSoundEvent.get("minecraft:block.tripwire.click_off"); - SoundEvent ENTITY_VILLAGER_HURT = SoundEventImpl.get("minecraft:entity.villager.hurt"); + SoundEvent BLOCK_TRIPWIRE_CLICK_ON = BuiltinSoundEvent.get("minecraft:block.tripwire.click_on"); - SoundEvent ENTITY_VILLAGER_NO = SoundEventImpl.get("minecraft:entity.villager.no"); + SoundEvent BLOCK_TRIPWIRE_DETACH = BuiltinSoundEvent.get("minecraft:block.tripwire.detach"); - SoundEvent ENTITY_VILLAGER_TRADE = SoundEventImpl.get("minecraft:entity.villager.trade"); + SoundEvent ENTITY_TROPICAL_FISH_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.tropical_fish.ambient"); - SoundEvent ENTITY_VILLAGER_YES = SoundEventImpl.get("minecraft:entity.villager.yes"); + SoundEvent ENTITY_TROPICAL_FISH_DEATH = BuiltinSoundEvent.get("minecraft:entity.tropical_fish.death"); - SoundEvent ENTITY_VILLAGER_WORK_ARMORER = SoundEventImpl.get("minecraft:entity.villager.work_armorer"); + SoundEvent ENTITY_TROPICAL_FISH_FLOP = BuiltinSoundEvent.get("minecraft:entity.tropical_fish.flop"); - SoundEvent ENTITY_VILLAGER_WORK_BUTCHER = SoundEventImpl.get("minecraft:entity.villager.work_butcher"); + SoundEvent ENTITY_TROPICAL_FISH_HURT = BuiltinSoundEvent.get("minecraft:entity.tropical_fish.hurt"); - SoundEvent ENTITY_VILLAGER_WORK_CARTOGRAPHER = SoundEventImpl.get("minecraft:entity.villager.work_cartographer"); + SoundEvent BLOCK_TUFF_BREAK = BuiltinSoundEvent.get("minecraft:block.tuff.break"); - SoundEvent ENTITY_VILLAGER_WORK_CLERIC = SoundEventImpl.get("minecraft:entity.villager.work_cleric"); + SoundEvent BLOCK_TUFF_STEP = BuiltinSoundEvent.get("minecraft:block.tuff.step"); - SoundEvent ENTITY_VILLAGER_WORK_FARMER = SoundEventImpl.get("minecraft:entity.villager.work_farmer"); + SoundEvent BLOCK_TUFF_PLACE = BuiltinSoundEvent.get("minecraft:block.tuff.place"); - SoundEvent ENTITY_VILLAGER_WORK_FISHERMAN = SoundEventImpl.get("minecraft:entity.villager.work_fisherman"); + SoundEvent BLOCK_TUFF_HIT = BuiltinSoundEvent.get("minecraft:block.tuff.hit"); - SoundEvent ENTITY_VILLAGER_WORK_FLETCHER = SoundEventImpl.get("minecraft:entity.villager.work_fletcher"); + SoundEvent BLOCK_TUFF_FALL = BuiltinSoundEvent.get("minecraft:block.tuff.fall"); - SoundEvent ENTITY_VILLAGER_WORK_LEATHERWORKER = SoundEventImpl.get("minecraft:entity.villager.work_leatherworker"); + SoundEvent BLOCK_TUFF_BRICKS_BREAK = BuiltinSoundEvent.get("minecraft:block.tuff_bricks.break"); - SoundEvent ENTITY_VILLAGER_WORK_LIBRARIAN = SoundEventImpl.get("minecraft:entity.villager.work_librarian"); + SoundEvent BLOCK_TUFF_BRICKS_FALL = BuiltinSoundEvent.get("minecraft:block.tuff_bricks.fall"); - SoundEvent ENTITY_VILLAGER_WORK_MASON = SoundEventImpl.get("minecraft:entity.villager.work_mason"); + SoundEvent BLOCK_TUFF_BRICKS_HIT = BuiltinSoundEvent.get("minecraft:block.tuff_bricks.hit"); - SoundEvent ENTITY_VILLAGER_WORK_SHEPHERD = SoundEventImpl.get("minecraft:entity.villager.work_shepherd"); + SoundEvent BLOCK_TUFF_BRICKS_PLACE = BuiltinSoundEvent.get("minecraft:block.tuff_bricks.place"); - SoundEvent ENTITY_VILLAGER_WORK_TOOLSMITH = SoundEventImpl.get("minecraft:entity.villager.work_toolsmith"); + SoundEvent BLOCK_TUFF_BRICKS_STEP = BuiltinSoundEvent.get("minecraft:block.tuff_bricks.step"); - SoundEvent ENTITY_VILLAGER_WORK_WEAPONSMITH = SoundEventImpl.get("minecraft:entity.villager.work_weaponsmith"); + SoundEvent BLOCK_POLISHED_TUFF_BREAK = BuiltinSoundEvent.get("minecraft:block.polished_tuff.break"); - SoundEvent ENTITY_VINDICATOR_AMBIENT = SoundEventImpl.get("minecraft:entity.vindicator.ambient"); + SoundEvent BLOCK_POLISHED_TUFF_FALL = BuiltinSoundEvent.get("minecraft:block.polished_tuff.fall"); - SoundEvent ENTITY_VINDICATOR_CELEBRATE = SoundEventImpl.get("minecraft:entity.vindicator.celebrate"); + SoundEvent BLOCK_POLISHED_TUFF_HIT = BuiltinSoundEvent.get("minecraft:block.polished_tuff.hit"); - SoundEvent ENTITY_VINDICATOR_DEATH = SoundEventImpl.get("minecraft:entity.vindicator.death"); + SoundEvent BLOCK_POLISHED_TUFF_PLACE = BuiltinSoundEvent.get("minecraft:block.polished_tuff.place"); - SoundEvent ENTITY_VINDICATOR_HURT = SoundEventImpl.get("minecraft:entity.vindicator.hurt"); + SoundEvent BLOCK_POLISHED_TUFF_STEP = BuiltinSoundEvent.get("minecraft:block.polished_tuff.step"); - SoundEvent BLOCK_VINE_BREAK = SoundEventImpl.get("minecraft:block.vine.break"); + SoundEvent ENTITY_TURTLE_AMBIENT_LAND = BuiltinSoundEvent.get("minecraft:entity.turtle.ambient_land"); - SoundEvent BLOCK_VINE_FALL = SoundEventImpl.get("minecraft:block.vine.fall"); + SoundEvent ENTITY_TURTLE_DEATH = BuiltinSoundEvent.get("minecraft:entity.turtle.death"); - SoundEvent BLOCK_VINE_HIT = SoundEventImpl.get("minecraft:block.vine.hit"); + SoundEvent ENTITY_TURTLE_DEATH_BABY = BuiltinSoundEvent.get("minecraft:entity.turtle.death_baby"); - SoundEvent BLOCK_VINE_PLACE = SoundEventImpl.get("minecraft:block.vine.place"); + SoundEvent ENTITY_TURTLE_EGG_BREAK = BuiltinSoundEvent.get("minecraft:entity.turtle.egg_break"); - SoundEvent BLOCK_VINE_STEP = SoundEventImpl.get("minecraft:block.vine.step"); + SoundEvent ENTITY_TURTLE_EGG_CRACK = BuiltinSoundEvent.get("minecraft:entity.turtle.egg_crack"); - SoundEvent BLOCK_LILY_PAD_PLACE = SoundEventImpl.get("minecraft:block.lily_pad.place"); + SoundEvent ENTITY_TURTLE_EGG_HATCH = BuiltinSoundEvent.get("minecraft:entity.turtle.egg_hatch"); - SoundEvent ENTITY_WANDERING_TRADER_AMBIENT = SoundEventImpl.get("minecraft:entity.wandering_trader.ambient"); + SoundEvent ENTITY_TURTLE_HURT = BuiltinSoundEvent.get("minecraft:entity.turtle.hurt"); - SoundEvent ENTITY_WANDERING_TRADER_DEATH = SoundEventImpl.get("minecraft:entity.wandering_trader.death"); + SoundEvent ENTITY_TURTLE_HURT_BABY = BuiltinSoundEvent.get("minecraft:entity.turtle.hurt_baby"); - SoundEvent ENTITY_WANDERING_TRADER_DISAPPEARED = SoundEventImpl.get("minecraft:entity.wandering_trader.disappeared"); + SoundEvent ENTITY_TURTLE_LAY_EGG = BuiltinSoundEvent.get("minecraft:entity.turtle.lay_egg"); - SoundEvent ENTITY_WANDERING_TRADER_DRINK_MILK = SoundEventImpl.get("minecraft:entity.wandering_trader.drink_milk"); + SoundEvent ENTITY_TURTLE_SHAMBLE = BuiltinSoundEvent.get("minecraft:entity.turtle.shamble"); - SoundEvent ENTITY_WANDERING_TRADER_DRINK_POTION = SoundEventImpl.get("minecraft:entity.wandering_trader.drink_potion"); + SoundEvent ENTITY_TURTLE_SHAMBLE_BABY = BuiltinSoundEvent.get("minecraft:entity.turtle.shamble_baby"); - SoundEvent ENTITY_WANDERING_TRADER_HURT = SoundEventImpl.get("minecraft:entity.wandering_trader.hurt"); + SoundEvent ENTITY_TURTLE_SWIM = BuiltinSoundEvent.get("minecraft:entity.turtle.swim"); - SoundEvent ENTITY_WANDERING_TRADER_NO = SoundEventImpl.get("minecraft:entity.wandering_trader.no"); + SoundEvent UI_BUTTON_CLICK = BuiltinSoundEvent.get("minecraft:ui.button.click"); - SoundEvent ENTITY_WANDERING_TRADER_REAPPEARED = SoundEventImpl.get("minecraft:entity.wandering_trader.reappeared"); + SoundEvent UI_LOOM_SELECT_PATTERN = BuiltinSoundEvent.get("minecraft:ui.loom.select_pattern"); - SoundEvent ENTITY_WANDERING_TRADER_TRADE = SoundEventImpl.get("minecraft:entity.wandering_trader.trade"); + SoundEvent UI_LOOM_TAKE_RESULT = BuiltinSoundEvent.get("minecraft:ui.loom.take_result"); - SoundEvent ENTITY_WANDERING_TRADER_YES = SoundEventImpl.get("minecraft:entity.wandering_trader.yes"); + SoundEvent UI_CARTOGRAPHY_TABLE_TAKE_RESULT = BuiltinSoundEvent.get("minecraft:ui.cartography_table.take_result"); - SoundEvent ENTITY_WARDEN_AGITATED = SoundEventImpl.get("minecraft:entity.warden.agitated"); + SoundEvent UI_STONECUTTER_TAKE_RESULT = BuiltinSoundEvent.get("minecraft:ui.stonecutter.take_result"); - SoundEvent ENTITY_WARDEN_AMBIENT = SoundEventImpl.get("minecraft:entity.warden.ambient"); + SoundEvent UI_STONECUTTER_SELECT_RECIPE = BuiltinSoundEvent.get("minecraft:ui.stonecutter.select_recipe"); - SoundEvent ENTITY_WARDEN_ANGRY = SoundEventImpl.get("minecraft:entity.warden.angry"); + SoundEvent UI_TOAST_CHALLENGE_COMPLETE = BuiltinSoundEvent.get("minecraft:ui.toast.challenge_complete"); - SoundEvent ENTITY_WARDEN_ATTACK_IMPACT = SoundEventImpl.get("minecraft:entity.warden.attack_impact"); + SoundEvent UI_TOAST_IN = BuiltinSoundEvent.get("minecraft:ui.toast.in"); - SoundEvent ENTITY_WARDEN_DEATH = SoundEventImpl.get("minecraft:entity.warden.death"); + SoundEvent UI_TOAST_OUT = BuiltinSoundEvent.get("minecraft:ui.toast.out"); - SoundEvent ENTITY_WARDEN_DIG = SoundEventImpl.get("minecraft:entity.warden.dig"); + SoundEvent BLOCK_VAULT_ACTIVATE = BuiltinSoundEvent.get("minecraft:block.vault.activate"); - SoundEvent ENTITY_WARDEN_EMERGE = SoundEventImpl.get("minecraft:entity.warden.emerge"); + SoundEvent BLOCK_VAULT_AMBIENT = BuiltinSoundEvent.get("minecraft:block.vault.ambient"); - SoundEvent ENTITY_WARDEN_HEARTBEAT = SoundEventImpl.get("minecraft:entity.warden.heartbeat"); + SoundEvent BLOCK_VAULT_BREAK = BuiltinSoundEvent.get("minecraft:block.vault.break"); - SoundEvent ENTITY_WARDEN_HURT = SoundEventImpl.get("minecraft:entity.warden.hurt"); + SoundEvent BLOCK_VAULT_CLOSE_SHUTTER = BuiltinSoundEvent.get("minecraft:block.vault.close_shutter"); - SoundEvent ENTITY_WARDEN_LISTENING = SoundEventImpl.get("minecraft:entity.warden.listening"); + SoundEvent BLOCK_VAULT_DEACTIVATE = BuiltinSoundEvent.get("minecraft:block.vault.deactivate"); - SoundEvent ENTITY_WARDEN_LISTENING_ANGRY = SoundEventImpl.get("minecraft:entity.warden.listening_angry"); + SoundEvent BLOCK_VAULT_EJECT_ITEM = BuiltinSoundEvent.get("minecraft:block.vault.eject_item"); - SoundEvent ENTITY_WARDEN_NEARBY_CLOSE = SoundEventImpl.get("minecraft:entity.warden.nearby_close"); + SoundEvent BLOCK_VAULT_REJECT_REWARDED_PLAYER = BuiltinSoundEvent.get("minecraft:block.vault.reject_rewarded_player"); - SoundEvent ENTITY_WARDEN_NEARBY_CLOSER = SoundEventImpl.get("minecraft:entity.warden.nearby_closer"); + SoundEvent BLOCK_VAULT_FALL = BuiltinSoundEvent.get("minecraft:block.vault.fall"); - SoundEvent ENTITY_WARDEN_NEARBY_CLOSEST = SoundEventImpl.get("minecraft:entity.warden.nearby_closest"); + SoundEvent BLOCK_VAULT_HIT = BuiltinSoundEvent.get("minecraft:block.vault.hit"); - SoundEvent ENTITY_WARDEN_ROAR = SoundEventImpl.get("minecraft:entity.warden.roar"); + SoundEvent BLOCK_VAULT_INSERT_ITEM = BuiltinSoundEvent.get("minecraft:block.vault.insert_item"); - SoundEvent ENTITY_WARDEN_SNIFF = SoundEventImpl.get("minecraft:entity.warden.sniff"); + SoundEvent BLOCK_VAULT_INSERT_ITEM_FAIL = BuiltinSoundEvent.get("minecraft:block.vault.insert_item_fail"); - SoundEvent ENTITY_WARDEN_SONIC_BOOM = SoundEventImpl.get("minecraft:entity.warden.sonic_boom"); + SoundEvent BLOCK_VAULT_OPEN_SHUTTER = BuiltinSoundEvent.get("minecraft:block.vault.open_shutter"); - SoundEvent ENTITY_WARDEN_SONIC_CHARGE = SoundEventImpl.get("minecraft:entity.warden.sonic_charge"); + SoundEvent BLOCK_VAULT_PLACE = BuiltinSoundEvent.get("minecraft:block.vault.place"); - SoundEvent ENTITY_WARDEN_STEP = SoundEventImpl.get("minecraft:entity.warden.step"); + SoundEvent BLOCK_VAULT_STEP = BuiltinSoundEvent.get("minecraft:block.vault.step"); - SoundEvent ENTITY_WARDEN_TENDRIL_CLICKS = SoundEventImpl.get("minecraft:entity.warden.tendril_clicks"); + SoundEvent ENTITY_VEX_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.vex.ambient"); - SoundEvent BLOCK_HANGING_SIGN_WAXED_INTERACT_FAIL = SoundEventImpl.get("minecraft:block.hanging_sign.waxed_interact_fail"); + SoundEvent ENTITY_VEX_CHARGE = BuiltinSoundEvent.get("minecraft:entity.vex.charge"); - SoundEvent BLOCK_SIGN_WAXED_INTERACT_FAIL = SoundEventImpl.get("minecraft:block.sign.waxed_interact_fail"); + SoundEvent ENTITY_VEX_DEATH = BuiltinSoundEvent.get("minecraft:entity.vex.death"); - SoundEvent BLOCK_WATER_AMBIENT = SoundEventImpl.get("minecraft:block.water.ambient"); + SoundEvent ENTITY_VEX_HURT = BuiltinSoundEvent.get("minecraft:entity.vex.hurt"); - SoundEvent WEATHER_RAIN = SoundEventImpl.get("minecraft:weather.rain"); + SoundEvent ENTITY_VILLAGER_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.villager.ambient"); - SoundEvent WEATHER_RAIN_ABOVE = SoundEventImpl.get("minecraft:weather.rain.above"); + SoundEvent ENTITY_VILLAGER_CELEBRATE = BuiltinSoundEvent.get("minecraft:entity.villager.celebrate"); - SoundEvent BLOCK_WET_GRASS_BREAK = SoundEventImpl.get("minecraft:block.wet_grass.break"); + SoundEvent ENTITY_VILLAGER_DEATH = BuiltinSoundEvent.get("minecraft:entity.villager.death"); - SoundEvent BLOCK_WET_GRASS_FALL = SoundEventImpl.get("minecraft:block.wet_grass.fall"); + SoundEvent ENTITY_VILLAGER_HURT = BuiltinSoundEvent.get("minecraft:entity.villager.hurt"); - SoundEvent BLOCK_WET_GRASS_HIT = SoundEventImpl.get("minecraft:block.wet_grass.hit"); + SoundEvent ENTITY_VILLAGER_NO = BuiltinSoundEvent.get("minecraft:entity.villager.no"); - SoundEvent BLOCK_WET_GRASS_PLACE = SoundEventImpl.get("minecraft:block.wet_grass.place"); + SoundEvent ENTITY_VILLAGER_TRADE = BuiltinSoundEvent.get("minecraft:entity.villager.trade"); - SoundEvent BLOCK_WET_GRASS_STEP = SoundEventImpl.get("minecraft:block.wet_grass.step"); + SoundEvent ENTITY_VILLAGER_YES = BuiltinSoundEvent.get("minecraft:entity.villager.yes"); - SoundEvent BLOCK_WET_SPONGE_BREAK = SoundEventImpl.get("minecraft:block.wet_sponge.break"); + SoundEvent ENTITY_VILLAGER_WORK_ARMORER = BuiltinSoundEvent.get("minecraft:entity.villager.work_armorer"); - SoundEvent BLOCK_WET_SPONGE_FALL = SoundEventImpl.get("minecraft:block.wet_sponge.fall"); + SoundEvent ENTITY_VILLAGER_WORK_BUTCHER = BuiltinSoundEvent.get("minecraft:entity.villager.work_butcher"); - SoundEvent BLOCK_WET_SPONGE_HIT = SoundEventImpl.get("minecraft:block.wet_sponge.hit"); + SoundEvent ENTITY_VILLAGER_WORK_CARTOGRAPHER = BuiltinSoundEvent.get("minecraft:entity.villager.work_cartographer"); - SoundEvent BLOCK_WET_SPONGE_PLACE = SoundEventImpl.get("minecraft:block.wet_sponge.place"); + SoundEvent ENTITY_VILLAGER_WORK_CLERIC = BuiltinSoundEvent.get("minecraft:entity.villager.work_cleric"); - SoundEvent BLOCK_WET_SPONGE_STEP = SoundEventImpl.get("minecraft:block.wet_sponge.step"); + SoundEvent ENTITY_VILLAGER_WORK_FARMER = BuiltinSoundEvent.get("minecraft:entity.villager.work_farmer"); - SoundEvent ENTITY_GENERIC_WIND_BURST = SoundEventImpl.get("minecraft:entity.generic.wind_burst"); + SoundEvent ENTITY_VILLAGER_WORK_FISHERMAN = BuiltinSoundEvent.get("minecraft:entity.villager.work_fisherman"); - SoundEvent ENTITY_WITCH_AMBIENT = SoundEventImpl.get("minecraft:entity.witch.ambient"); + SoundEvent ENTITY_VILLAGER_WORK_FLETCHER = BuiltinSoundEvent.get("minecraft:entity.villager.work_fletcher"); - SoundEvent ENTITY_WITCH_CELEBRATE = SoundEventImpl.get("minecraft:entity.witch.celebrate"); + SoundEvent ENTITY_VILLAGER_WORK_LEATHERWORKER = BuiltinSoundEvent.get("minecraft:entity.villager.work_leatherworker"); - SoundEvent ENTITY_WITCH_DEATH = SoundEventImpl.get("minecraft:entity.witch.death"); + SoundEvent ENTITY_VILLAGER_WORK_LIBRARIAN = BuiltinSoundEvent.get("minecraft:entity.villager.work_librarian"); - SoundEvent ENTITY_WITCH_DRINK = SoundEventImpl.get("minecraft:entity.witch.drink"); + SoundEvent ENTITY_VILLAGER_WORK_MASON = BuiltinSoundEvent.get("minecraft:entity.villager.work_mason"); - SoundEvent ENTITY_WITCH_HURT = SoundEventImpl.get("minecraft:entity.witch.hurt"); + SoundEvent ENTITY_VILLAGER_WORK_SHEPHERD = BuiltinSoundEvent.get("minecraft:entity.villager.work_shepherd"); - SoundEvent ENTITY_WITCH_THROW = SoundEventImpl.get("minecraft:entity.witch.throw"); + SoundEvent ENTITY_VILLAGER_WORK_TOOLSMITH = BuiltinSoundEvent.get("minecraft:entity.villager.work_toolsmith"); - SoundEvent ENTITY_WITHER_AMBIENT = SoundEventImpl.get("minecraft:entity.wither.ambient"); + SoundEvent ENTITY_VILLAGER_WORK_WEAPONSMITH = BuiltinSoundEvent.get("minecraft:entity.villager.work_weaponsmith"); - SoundEvent ENTITY_WITHER_BREAK_BLOCK = SoundEventImpl.get("minecraft:entity.wither.break_block"); + SoundEvent ENTITY_VINDICATOR_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.vindicator.ambient"); - SoundEvent ENTITY_WITHER_DEATH = SoundEventImpl.get("minecraft:entity.wither.death"); + SoundEvent ENTITY_VINDICATOR_CELEBRATE = BuiltinSoundEvent.get("minecraft:entity.vindicator.celebrate"); - SoundEvent ENTITY_WITHER_HURT = SoundEventImpl.get("minecraft:entity.wither.hurt"); + SoundEvent ENTITY_VINDICATOR_DEATH = BuiltinSoundEvent.get("minecraft:entity.vindicator.death"); - SoundEvent ENTITY_WITHER_SHOOT = SoundEventImpl.get("minecraft:entity.wither.shoot"); + SoundEvent ENTITY_VINDICATOR_HURT = BuiltinSoundEvent.get("minecraft:entity.vindicator.hurt"); - SoundEvent ENTITY_WITHER_SKELETON_AMBIENT = SoundEventImpl.get("minecraft:entity.wither_skeleton.ambient"); + SoundEvent BLOCK_VINE_BREAK = BuiltinSoundEvent.get("minecraft:block.vine.break"); - SoundEvent ENTITY_WITHER_SKELETON_DEATH = SoundEventImpl.get("minecraft:entity.wither_skeleton.death"); + SoundEvent BLOCK_VINE_FALL = BuiltinSoundEvent.get("minecraft:block.vine.fall"); - SoundEvent ENTITY_WITHER_SKELETON_HURT = SoundEventImpl.get("minecraft:entity.wither_skeleton.hurt"); + SoundEvent BLOCK_VINE_HIT = BuiltinSoundEvent.get("minecraft:block.vine.hit"); - SoundEvent ENTITY_WITHER_SKELETON_STEP = SoundEventImpl.get("minecraft:entity.wither_skeleton.step"); + SoundEvent BLOCK_VINE_PLACE = BuiltinSoundEvent.get("minecraft:block.vine.place"); - SoundEvent ENTITY_WITHER_SPAWN = SoundEventImpl.get("minecraft:entity.wither.spawn"); + SoundEvent BLOCK_VINE_STEP = BuiltinSoundEvent.get("minecraft:block.vine.step"); - SoundEvent ENTITY_WOLF_AMBIENT = SoundEventImpl.get("minecraft:entity.wolf.ambient"); + SoundEvent BLOCK_LILY_PAD_PLACE = BuiltinSoundEvent.get("minecraft:block.lily_pad.place"); - SoundEvent ENTITY_WOLF_DEATH = SoundEventImpl.get("minecraft:entity.wolf.death"); + SoundEvent ENTITY_WANDERING_TRADER_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.wandering_trader.ambient"); - SoundEvent ENTITY_WOLF_GROWL = SoundEventImpl.get("minecraft:entity.wolf.growl"); + SoundEvent ENTITY_WANDERING_TRADER_DEATH = BuiltinSoundEvent.get("minecraft:entity.wandering_trader.death"); - SoundEvent ENTITY_WOLF_HOWL = SoundEventImpl.get("minecraft:entity.wolf.howl"); + SoundEvent ENTITY_WANDERING_TRADER_DISAPPEARED = BuiltinSoundEvent.get("minecraft:entity.wandering_trader.disappeared"); - SoundEvent ENTITY_WOLF_HURT = SoundEventImpl.get("minecraft:entity.wolf.hurt"); + SoundEvent ENTITY_WANDERING_TRADER_DRINK_MILK = BuiltinSoundEvent.get("minecraft:entity.wandering_trader.drink_milk"); - SoundEvent ENTITY_WOLF_PANT = SoundEventImpl.get("minecraft:entity.wolf.pant"); + SoundEvent ENTITY_WANDERING_TRADER_DRINK_POTION = BuiltinSoundEvent.get("minecraft:entity.wandering_trader.drink_potion"); - SoundEvent ENTITY_WOLF_SHAKE = SoundEventImpl.get("minecraft:entity.wolf.shake"); + SoundEvent ENTITY_WANDERING_TRADER_HURT = BuiltinSoundEvent.get("minecraft:entity.wandering_trader.hurt"); - SoundEvent ENTITY_WOLF_STEP = SoundEventImpl.get("minecraft:entity.wolf.step"); + SoundEvent ENTITY_WANDERING_TRADER_NO = BuiltinSoundEvent.get("minecraft:entity.wandering_trader.no"); - SoundEvent ENTITY_WOLF_WHINE = SoundEventImpl.get("minecraft:entity.wolf.whine"); + SoundEvent ENTITY_WANDERING_TRADER_REAPPEARED = BuiltinSoundEvent.get("minecraft:entity.wandering_trader.reappeared"); - SoundEvent BLOCK_WOODEN_DOOR_CLOSE = SoundEventImpl.get("minecraft:block.wooden_door.close"); + SoundEvent ENTITY_WANDERING_TRADER_TRADE = BuiltinSoundEvent.get("minecraft:entity.wandering_trader.trade"); - SoundEvent BLOCK_WOODEN_DOOR_OPEN = SoundEventImpl.get("minecraft:block.wooden_door.open"); + SoundEvent ENTITY_WANDERING_TRADER_YES = BuiltinSoundEvent.get("minecraft:entity.wandering_trader.yes"); - SoundEvent BLOCK_WOODEN_TRAPDOOR_CLOSE = SoundEventImpl.get("minecraft:block.wooden_trapdoor.close"); + SoundEvent ENTITY_WARDEN_AGITATED = BuiltinSoundEvent.get("minecraft:entity.warden.agitated"); - SoundEvent BLOCK_WOODEN_TRAPDOOR_OPEN = SoundEventImpl.get("minecraft:block.wooden_trapdoor.open"); + SoundEvent ENTITY_WARDEN_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.warden.ambient"); - SoundEvent BLOCK_WOODEN_BUTTON_CLICK_OFF = SoundEventImpl.get("minecraft:block.wooden_button.click_off"); + SoundEvent ENTITY_WARDEN_ANGRY = BuiltinSoundEvent.get("minecraft:entity.warden.angry"); - SoundEvent BLOCK_WOODEN_BUTTON_CLICK_ON = SoundEventImpl.get("minecraft:block.wooden_button.click_on"); + SoundEvent ENTITY_WARDEN_ATTACK_IMPACT = BuiltinSoundEvent.get("minecraft:entity.warden.attack_impact"); - SoundEvent BLOCK_WOODEN_PRESSURE_PLATE_CLICK_OFF = SoundEventImpl.get("minecraft:block.wooden_pressure_plate.click_off"); + SoundEvent ENTITY_WARDEN_DEATH = BuiltinSoundEvent.get("minecraft:entity.warden.death"); - SoundEvent BLOCK_WOODEN_PRESSURE_PLATE_CLICK_ON = SoundEventImpl.get("minecraft:block.wooden_pressure_plate.click_on"); + SoundEvent ENTITY_WARDEN_DIG = BuiltinSoundEvent.get("minecraft:entity.warden.dig"); - SoundEvent BLOCK_WOOD_BREAK = SoundEventImpl.get("minecraft:block.wood.break"); + SoundEvent ENTITY_WARDEN_EMERGE = BuiltinSoundEvent.get("minecraft:entity.warden.emerge"); - SoundEvent BLOCK_WOOD_FALL = SoundEventImpl.get("minecraft:block.wood.fall"); + SoundEvent ENTITY_WARDEN_HEARTBEAT = BuiltinSoundEvent.get("minecraft:entity.warden.heartbeat"); - SoundEvent BLOCK_WOOD_HIT = SoundEventImpl.get("minecraft:block.wood.hit"); + SoundEvent ENTITY_WARDEN_HURT = BuiltinSoundEvent.get("minecraft:entity.warden.hurt"); - SoundEvent BLOCK_WOOD_PLACE = SoundEventImpl.get("minecraft:block.wood.place"); + SoundEvent ENTITY_WARDEN_LISTENING = BuiltinSoundEvent.get("minecraft:entity.warden.listening"); - SoundEvent BLOCK_WOOD_STEP = SoundEventImpl.get("minecraft:block.wood.step"); + SoundEvent ENTITY_WARDEN_LISTENING_ANGRY = BuiltinSoundEvent.get("minecraft:entity.warden.listening_angry"); - SoundEvent BLOCK_WOOL_BREAK = SoundEventImpl.get("minecraft:block.wool.break"); + SoundEvent ENTITY_WARDEN_NEARBY_CLOSE = BuiltinSoundEvent.get("minecraft:entity.warden.nearby_close"); - SoundEvent BLOCK_WOOL_FALL = SoundEventImpl.get("minecraft:block.wool.fall"); + SoundEvent ENTITY_WARDEN_NEARBY_CLOSER = BuiltinSoundEvent.get("minecraft:entity.warden.nearby_closer"); - SoundEvent BLOCK_WOOL_HIT = SoundEventImpl.get("minecraft:block.wool.hit"); + SoundEvent ENTITY_WARDEN_NEARBY_CLOSEST = BuiltinSoundEvent.get("minecraft:entity.warden.nearby_closest"); - SoundEvent BLOCK_WOOL_PLACE = SoundEventImpl.get("minecraft:block.wool.place"); + SoundEvent ENTITY_WARDEN_ROAR = BuiltinSoundEvent.get("minecraft:entity.warden.roar"); - SoundEvent BLOCK_WOOL_STEP = SoundEventImpl.get("minecraft:block.wool.step"); + SoundEvent ENTITY_WARDEN_SNIFF = BuiltinSoundEvent.get("minecraft:entity.warden.sniff"); - SoundEvent ENTITY_ZOGLIN_AMBIENT = SoundEventImpl.get("minecraft:entity.zoglin.ambient"); + SoundEvent ENTITY_WARDEN_SONIC_BOOM = BuiltinSoundEvent.get("minecraft:entity.warden.sonic_boom"); - SoundEvent ENTITY_ZOGLIN_ANGRY = SoundEventImpl.get("minecraft:entity.zoglin.angry"); + SoundEvent ENTITY_WARDEN_SONIC_CHARGE = BuiltinSoundEvent.get("minecraft:entity.warden.sonic_charge"); - SoundEvent ENTITY_ZOGLIN_ATTACK = SoundEventImpl.get("minecraft:entity.zoglin.attack"); + SoundEvent ENTITY_WARDEN_STEP = BuiltinSoundEvent.get("minecraft:entity.warden.step"); - SoundEvent ENTITY_ZOGLIN_DEATH = SoundEventImpl.get("minecraft:entity.zoglin.death"); + SoundEvent ENTITY_WARDEN_TENDRIL_CLICKS = BuiltinSoundEvent.get("minecraft:entity.warden.tendril_clicks"); - SoundEvent ENTITY_ZOGLIN_HURT = SoundEventImpl.get("minecraft:entity.zoglin.hurt"); + SoundEvent BLOCK_HANGING_SIGN_WAXED_INTERACT_FAIL = BuiltinSoundEvent.get("minecraft:block.hanging_sign.waxed_interact_fail"); - SoundEvent ENTITY_ZOGLIN_STEP = SoundEventImpl.get("minecraft:entity.zoglin.step"); + SoundEvent BLOCK_SIGN_WAXED_INTERACT_FAIL = BuiltinSoundEvent.get("minecraft:block.sign.waxed_interact_fail"); - SoundEvent ENTITY_ZOMBIE_AMBIENT = SoundEventImpl.get("minecraft:entity.zombie.ambient"); + SoundEvent BLOCK_WATER_AMBIENT = BuiltinSoundEvent.get("minecraft:block.water.ambient"); - SoundEvent ENTITY_ZOMBIE_ATTACK_WOODEN_DOOR = SoundEventImpl.get("minecraft:entity.zombie.attack_wooden_door"); + SoundEvent WEATHER_RAIN = BuiltinSoundEvent.get("minecraft:weather.rain"); - SoundEvent ENTITY_ZOMBIE_ATTACK_IRON_DOOR = SoundEventImpl.get("minecraft:entity.zombie.attack_iron_door"); + SoundEvent WEATHER_RAIN_ABOVE = BuiltinSoundEvent.get("minecraft:weather.rain.above"); - SoundEvent ENTITY_ZOMBIE_BREAK_WOODEN_DOOR = SoundEventImpl.get("minecraft:entity.zombie.break_wooden_door"); + SoundEvent BLOCK_WET_GRASS_BREAK = BuiltinSoundEvent.get("minecraft:block.wet_grass.break"); - SoundEvent ENTITY_ZOMBIE_CONVERTED_TO_DROWNED = SoundEventImpl.get("minecraft:entity.zombie.converted_to_drowned"); + SoundEvent BLOCK_WET_GRASS_FALL = BuiltinSoundEvent.get("minecraft:block.wet_grass.fall"); - SoundEvent ENTITY_ZOMBIE_DEATH = SoundEventImpl.get("minecraft:entity.zombie.death"); + SoundEvent BLOCK_WET_GRASS_HIT = BuiltinSoundEvent.get("minecraft:block.wet_grass.hit"); - SoundEvent ENTITY_ZOMBIE_DESTROY_EGG = SoundEventImpl.get("minecraft:entity.zombie.destroy_egg"); + SoundEvent BLOCK_WET_GRASS_PLACE = BuiltinSoundEvent.get("minecraft:block.wet_grass.place"); - SoundEvent ENTITY_ZOMBIE_HORSE_AMBIENT = SoundEventImpl.get("minecraft:entity.zombie_horse.ambient"); + SoundEvent BLOCK_WET_GRASS_STEP = BuiltinSoundEvent.get("minecraft:block.wet_grass.step"); - SoundEvent ENTITY_ZOMBIE_HORSE_DEATH = SoundEventImpl.get("minecraft:entity.zombie_horse.death"); + SoundEvent BLOCK_WET_SPONGE_BREAK = BuiltinSoundEvent.get("minecraft:block.wet_sponge.break"); - SoundEvent ENTITY_ZOMBIE_HORSE_HURT = SoundEventImpl.get("minecraft:entity.zombie_horse.hurt"); + SoundEvent BLOCK_WET_SPONGE_DRIES = BuiltinSoundEvent.get("minecraft:block.wet_sponge.dries"); - SoundEvent ENTITY_ZOMBIE_HURT = SoundEventImpl.get("minecraft:entity.zombie.hurt"); + SoundEvent BLOCK_WET_SPONGE_FALL = BuiltinSoundEvent.get("minecraft:block.wet_sponge.fall"); - SoundEvent ENTITY_ZOMBIE_INFECT = SoundEventImpl.get("minecraft:entity.zombie.infect"); + SoundEvent BLOCK_WET_SPONGE_HIT = BuiltinSoundEvent.get("minecraft:block.wet_sponge.hit"); - SoundEvent ENTITY_ZOMBIFIED_PIGLIN_AMBIENT = SoundEventImpl.get("minecraft:entity.zombified_piglin.ambient"); + SoundEvent BLOCK_WET_SPONGE_PLACE = BuiltinSoundEvent.get("minecraft:block.wet_sponge.place"); - SoundEvent ENTITY_ZOMBIFIED_PIGLIN_ANGRY = SoundEventImpl.get("minecraft:entity.zombified_piglin.angry"); + SoundEvent BLOCK_WET_SPONGE_STEP = BuiltinSoundEvent.get("minecraft:block.wet_sponge.step"); - SoundEvent ENTITY_ZOMBIFIED_PIGLIN_DEATH = SoundEventImpl.get("minecraft:entity.zombified_piglin.death"); + SoundEvent ENTITY_WIND_CHARGE_WIND_BURST = BuiltinSoundEvent.get("minecraft:entity.wind_charge.wind_burst"); - SoundEvent ENTITY_ZOMBIFIED_PIGLIN_HURT = SoundEventImpl.get("minecraft:entity.zombified_piglin.hurt"); + SoundEvent ENTITY_WIND_CHARGE_THROW = BuiltinSoundEvent.get("minecraft:entity.wind_charge.throw"); - SoundEvent ENTITY_ZOMBIE_STEP = SoundEventImpl.get("minecraft:entity.zombie.step"); + SoundEvent ENTITY_WITCH_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.witch.ambient"); - SoundEvent ENTITY_ZOMBIE_VILLAGER_AMBIENT = SoundEventImpl.get("minecraft:entity.zombie_villager.ambient"); + SoundEvent ENTITY_WITCH_CELEBRATE = BuiltinSoundEvent.get("minecraft:entity.witch.celebrate"); - SoundEvent ENTITY_ZOMBIE_VILLAGER_CONVERTED = SoundEventImpl.get("minecraft:entity.zombie_villager.converted"); + SoundEvent ENTITY_WITCH_DEATH = BuiltinSoundEvent.get("minecraft:entity.witch.death"); - SoundEvent ENTITY_ZOMBIE_VILLAGER_CURE = SoundEventImpl.get("minecraft:entity.zombie_villager.cure"); + SoundEvent ENTITY_WITCH_DRINK = BuiltinSoundEvent.get("minecraft:entity.witch.drink"); - SoundEvent ENTITY_ZOMBIE_VILLAGER_DEATH = SoundEventImpl.get("minecraft:entity.zombie_villager.death"); + SoundEvent ENTITY_WITCH_HURT = BuiltinSoundEvent.get("minecraft:entity.witch.hurt"); - SoundEvent ENTITY_ZOMBIE_VILLAGER_HURT = SoundEventImpl.get("minecraft:entity.zombie_villager.hurt"); + SoundEvent ENTITY_WITCH_THROW = BuiltinSoundEvent.get("minecraft:entity.witch.throw"); - SoundEvent ENTITY_ZOMBIE_VILLAGER_STEP = SoundEventImpl.get("minecraft:entity.zombie_villager.step"); + SoundEvent ENTITY_WITHER_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.wither.ambient"); + + SoundEvent ENTITY_WITHER_BREAK_BLOCK = BuiltinSoundEvent.get("minecraft:entity.wither.break_block"); + + SoundEvent ENTITY_WITHER_DEATH = BuiltinSoundEvent.get("minecraft:entity.wither.death"); + + SoundEvent ENTITY_WITHER_HURT = BuiltinSoundEvent.get("minecraft:entity.wither.hurt"); + + SoundEvent ENTITY_WITHER_SHOOT = BuiltinSoundEvent.get("minecraft:entity.wither.shoot"); + + SoundEvent ENTITY_WITHER_SKELETON_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.wither_skeleton.ambient"); + + SoundEvent ENTITY_WITHER_SKELETON_DEATH = BuiltinSoundEvent.get("minecraft:entity.wither_skeleton.death"); + + SoundEvent ENTITY_WITHER_SKELETON_HURT = BuiltinSoundEvent.get("minecraft:entity.wither_skeleton.hurt"); + + SoundEvent ENTITY_WITHER_SKELETON_STEP = BuiltinSoundEvent.get("minecraft:entity.wither_skeleton.step"); + + SoundEvent ENTITY_WITHER_SPAWN = BuiltinSoundEvent.get("minecraft:entity.wither.spawn"); + + SoundEvent ITEM_WOLF_ARMOR_BREAK = BuiltinSoundEvent.get("minecraft:item.wolf_armor.break"); + + SoundEvent ITEM_WOLF_ARMOR_CRACK = BuiltinSoundEvent.get("minecraft:item.wolf_armor.crack"); + + SoundEvent ITEM_WOLF_ARMOR_DAMAGE = BuiltinSoundEvent.get("minecraft:item.wolf_armor.damage"); + + SoundEvent ITEM_WOLF_ARMOR_REPAIR = BuiltinSoundEvent.get("minecraft:item.wolf_armor.repair"); + + SoundEvent ENTITY_WOLF_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.wolf.ambient"); + + SoundEvent ENTITY_WOLF_DEATH = BuiltinSoundEvent.get("minecraft:entity.wolf.death"); + + SoundEvent ENTITY_WOLF_GROWL = BuiltinSoundEvent.get("minecraft:entity.wolf.growl"); + + SoundEvent ENTITY_WOLF_HOWL = BuiltinSoundEvent.get("minecraft:entity.wolf.howl"); + + SoundEvent ENTITY_WOLF_HURT = BuiltinSoundEvent.get("minecraft:entity.wolf.hurt"); + + SoundEvent ENTITY_WOLF_PANT = BuiltinSoundEvent.get("minecraft:entity.wolf.pant"); + + SoundEvent ENTITY_WOLF_SHAKE = BuiltinSoundEvent.get("minecraft:entity.wolf.shake"); + + SoundEvent ENTITY_WOLF_STEP = BuiltinSoundEvent.get("minecraft:entity.wolf.step"); + + SoundEvent ENTITY_WOLF_WHINE = BuiltinSoundEvent.get("minecraft:entity.wolf.whine"); + + SoundEvent BLOCK_WOODEN_DOOR_CLOSE = BuiltinSoundEvent.get("minecraft:block.wooden_door.close"); + + SoundEvent BLOCK_WOODEN_DOOR_OPEN = BuiltinSoundEvent.get("minecraft:block.wooden_door.open"); + + SoundEvent BLOCK_WOODEN_TRAPDOOR_CLOSE = BuiltinSoundEvent.get("minecraft:block.wooden_trapdoor.close"); + + SoundEvent BLOCK_WOODEN_TRAPDOOR_OPEN = BuiltinSoundEvent.get("minecraft:block.wooden_trapdoor.open"); + + SoundEvent BLOCK_WOODEN_BUTTON_CLICK_OFF = BuiltinSoundEvent.get("minecraft:block.wooden_button.click_off"); + + SoundEvent BLOCK_WOODEN_BUTTON_CLICK_ON = BuiltinSoundEvent.get("minecraft:block.wooden_button.click_on"); + + SoundEvent BLOCK_WOODEN_PRESSURE_PLATE_CLICK_OFF = BuiltinSoundEvent.get("minecraft:block.wooden_pressure_plate.click_off"); + + SoundEvent BLOCK_WOODEN_PRESSURE_PLATE_CLICK_ON = BuiltinSoundEvent.get("minecraft:block.wooden_pressure_plate.click_on"); + + SoundEvent BLOCK_WOOD_BREAK = BuiltinSoundEvent.get("minecraft:block.wood.break"); + + SoundEvent BLOCK_WOOD_FALL = BuiltinSoundEvent.get("minecraft:block.wood.fall"); + + SoundEvent BLOCK_WOOD_HIT = BuiltinSoundEvent.get("minecraft:block.wood.hit"); + + SoundEvent BLOCK_WOOD_PLACE = BuiltinSoundEvent.get("minecraft:block.wood.place"); + + SoundEvent BLOCK_WOOD_STEP = BuiltinSoundEvent.get("minecraft:block.wood.step"); + + SoundEvent BLOCK_WOOL_BREAK = BuiltinSoundEvent.get("minecraft:block.wool.break"); + + SoundEvent BLOCK_WOOL_FALL = BuiltinSoundEvent.get("minecraft:block.wool.fall"); + + SoundEvent BLOCK_WOOL_HIT = BuiltinSoundEvent.get("minecraft:block.wool.hit"); + + SoundEvent BLOCK_WOOL_PLACE = BuiltinSoundEvent.get("minecraft:block.wool.place"); + + SoundEvent BLOCK_WOOL_STEP = BuiltinSoundEvent.get("minecraft:block.wool.step"); + + SoundEvent ENTITY_ZOGLIN_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.zoglin.ambient"); + + SoundEvent ENTITY_ZOGLIN_ANGRY = BuiltinSoundEvent.get("minecraft:entity.zoglin.angry"); + + SoundEvent ENTITY_ZOGLIN_ATTACK = BuiltinSoundEvent.get("minecraft:entity.zoglin.attack"); + + SoundEvent ENTITY_ZOGLIN_DEATH = BuiltinSoundEvent.get("minecraft:entity.zoglin.death"); + + SoundEvent ENTITY_ZOGLIN_HURT = BuiltinSoundEvent.get("minecraft:entity.zoglin.hurt"); + + SoundEvent ENTITY_ZOGLIN_STEP = BuiltinSoundEvent.get("minecraft:entity.zoglin.step"); + + SoundEvent ENTITY_ZOMBIE_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.zombie.ambient"); + + SoundEvent ENTITY_ZOMBIE_ATTACK_WOODEN_DOOR = BuiltinSoundEvent.get("minecraft:entity.zombie.attack_wooden_door"); + + SoundEvent ENTITY_ZOMBIE_ATTACK_IRON_DOOR = BuiltinSoundEvent.get("minecraft:entity.zombie.attack_iron_door"); + + SoundEvent ENTITY_ZOMBIE_BREAK_WOODEN_DOOR = BuiltinSoundEvent.get("minecraft:entity.zombie.break_wooden_door"); + + SoundEvent ENTITY_ZOMBIE_CONVERTED_TO_DROWNED = BuiltinSoundEvent.get("minecraft:entity.zombie.converted_to_drowned"); + + SoundEvent ENTITY_ZOMBIE_DEATH = BuiltinSoundEvent.get("minecraft:entity.zombie.death"); + + SoundEvent ENTITY_ZOMBIE_DESTROY_EGG = BuiltinSoundEvent.get("minecraft:entity.zombie.destroy_egg"); + + SoundEvent ENTITY_ZOMBIE_HORSE_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.zombie_horse.ambient"); + + SoundEvent ENTITY_ZOMBIE_HORSE_DEATH = BuiltinSoundEvent.get("minecraft:entity.zombie_horse.death"); + + SoundEvent ENTITY_ZOMBIE_HORSE_HURT = BuiltinSoundEvent.get("minecraft:entity.zombie_horse.hurt"); + + SoundEvent ENTITY_ZOMBIE_HURT = BuiltinSoundEvent.get("minecraft:entity.zombie.hurt"); + + SoundEvent ENTITY_ZOMBIE_INFECT = BuiltinSoundEvent.get("minecraft:entity.zombie.infect"); + + SoundEvent ENTITY_ZOMBIFIED_PIGLIN_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.zombified_piglin.ambient"); + + SoundEvent ENTITY_ZOMBIFIED_PIGLIN_ANGRY = BuiltinSoundEvent.get("minecraft:entity.zombified_piglin.angry"); + + SoundEvent ENTITY_ZOMBIFIED_PIGLIN_DEATH = BuiltinSoundEvent.get("minecraft:entity.zombified_piglin.death"); + + SoundEvent ENTITY_ZOMBIFIED_PIGLIN_HURT = BuiltinSoundEvent.get("minecraft:entity.zombified_piglin.hurt"); + + SoundEvent ENTITY_ZOMBIE_STEP = BuiltinSoundEvent.get("minecraft:entity.zombie.step"); + + SoundEvent ENTITY_ZOMBIE_VILLAGER_AMBIENT = BuiltinSoundEvent.get("minecraft:entity.zombie_villager.ambient"); + + SoundEvent ENTITY_ZOMBIE_VILLAGER_CONVERTED = BuiltinSoundEvent.get("minecraft:entity.zombie_villager.converted"); + + SoundEvent ENTITY_ZOMBIE_VILLAGER_CURE = BuiltinSoundEvent.get("minecraft:entity.zombie_villager.cure"); + + SoundEvent ENTITY_ZOMBIE_VILLAGER_DEATH = BuiltinSoundEvent.get("minecraft:entity.zombie_villager.death"); + + SoundEvent ENTITY_ZOMBIE_VILLAGER_HURT = BuiltinSoundEvent.get("minecraft:entity.zombie_villager.hurt"); + + SoundEvent ENTITY_ZOMBIE_VILLAGER_STEP = BuiltinSoundEvent.get("minecraft:entity.zombie_villager.step"); + + SoundEvent EVENT_MOB_EFFECT_BAD_OMEN = BuiltinSoundEvent.get("minecraft:event.mob_effect.bad_omen"); + + SoundEvent EVENT_MOB_EFFECT_TRIAL_OMEN = BuiltinSoundEvent.get("minecraft:event.mob_effect.trial_omen"); + + SoundEvent EVENT_MOB_EFFECT_RAID_OMEN = BuiltinSoundEvent.get("minecraft:event.mob_effect.raid_omen"); } diff --git a/src/autogenerated/java/net/minestom/server/world/DimensionTypes.java b/src/autogenerated/java/net/minestom/server/world/DimensionTypes.java new file mode 100644 index 00000000000..578a366186b --- /dev/null +++ b/src/autogenerated/java/net/minestom/server/world/DimensionTypes.java @@ -0,0 +1,17 @@ +package net.minestom.server.world; + +import net.minestom.server.registry.DynamicRegistry; + +/** + * Code autogenerated, do not edit! + */ +@SuppressWarnings("unused") +interface DimensionTypes { + DynamicRegistry.Key THE_END = DynamicRegistry.Key.of("minecraft:the_end"); + + DynamicRegistry.Key OVERWORLD_CAVES = DynamicRegistry.Key.of("minecraft:overworld_caves"); + + DynamicRegistry.Key THE_NETHER = DynamicRegistry.Key.of("minecraft:the_nether"); + + DynamicRegistry.Key OVERWORLD = DynamicRegistry.Key.of("minecraft:overworld"); +} diff --git a/src/autogenerated/java/net/minestom/server/world/biome/Biomes.java b/src/autogenerated/java/net/minestom/server/world/biome/Biomes.java new file mode 100644 index 00000000000..5d6459608df --- /dev/null +++ b/src/autogenerated/java/net/minestom/server/world/biome/Biomes.java @@ -0,0 +1,137 @@ +package net.minestom.server.world.biome; + +import net.minestom.server.registry.DynamicRegistry; + +/** + * Code autogenerated, do not edit! + */ +@SuppressWarnings("unused") +interface Biomes { + DynamicRegistry.Key SNOWY_SLOPES = DynamicRegistry.Key.of("minecraft:snowy_slopes"); + + DynamicRegistry.Key OLD_GROWTH_PINE_TAIGA = DynamicRegistry.Key.of("minecraft:old_growth_pine_taiga"); + + DynamicRegistry.Key MUSHROOM_FIELDS = DynamicRegistry.Key.of("minecraft:mushroom_fields"); + + DynamicRegistry.Key TAIGA = DynamicRegistry.Key.of("minecraft:taiga"); + + DynamicRegistry.Key DEEP_OCEAN = DynamicRegistry.Key.of("minecraft:deep_ocean"); + + DynamicRegistry.Key ERODED_BADLANDS = DynamicRegistry.Key.of("minecraft:eroded_badlands"); + + DynamicRegistry.Key FROZEN_RIVER = DynamicRegistry.Key.of("minecraft:frozen_river"); + + DynamicRegistry.Key END_HIGHLANDS = DynamicRegistry.Key.of("minecraft:end_highlands"); + + DynamicRegistry.Key CHERRY_GROVE = DynamicRegistry.Key.of("minecraft:cherry_grove"); + + DynamicRegistry.Key SUNFLOWER_PLAINS = DynamicRegistry.Key.of("minecraft:sunflower_plains"); + + DynamicRegistry.Key BIRCH_FOREST = DynamicRegistry.Key.of("minecraft:birch_forest"); + + DynamicRegistry.Key WINDSWEPT_HILLS = DynamicRegistry.Key.of("minecraft:windswept_hills"); + + DynamicRegistry.Key BAMBOO_JUNGLE = DynamicRegistry.Key.of("minecraft:bamboo_jungle"); + + DynamicRegistry.Key WOODED_BADLANDS = DynamicRegistry.Key.of("minecraft:wooded_badlands"); + + DynamicRegistry.Key BADLANDS = DynamicRegistry.Key.of("minecraft:badlands"); + + DynamicRegistry.Key SAVANNA_PLATEAU = DynamicRegistry.Key.of("minecraft:savanna_plateau"); + + DynamicRegistry.Key BEACH = DynamicRegistry.Key.of("minecraft:beach"); + + DynamicRegistry.Key DARK_FOREST = DynamicRegistry.Key.of("minecraft:dark_forest"); + + DynamicRegistry.Key STONY_PEAKS = DynamicRegistry.Key.of("minecraft:stony_peaks"); + + DynamicRegistry.Key MANGROVE_SWAMP = DynamicRegistry.Key.of("minecraft:mangrove_swamp"); + + DynamicRegistry.Key SPARSE_JUNGLE = DynamicRegistry.Key.of("minecraft:sparse_jungle"); + + DynamicRegistry.Key LUKEWARM_OCEAN = DynamicRegistry.Key.of("minecraft:lukewarm_ocean"); + + DynamicRegistry.Key RIVER = DynamicRegistry.Key.of("minecraft:river"); + + DynamicRegistry.Key STONY_SHORE = DynamicRegistry.Key.of("minecraft:stony_shore"); + + DynamicRegistry.Key WARPED_FOREST = DynamicRegistry.Key.of("minecraft:warped_forest"); + + DynamicRegistry.Key SNOWY_PLAINS = DynamicRegistry.Key.of("minecraft:snowy_plains"); + + DynamicRegistry.Key DRIPSTONE_CAVES = DynamicRegistry.Key.of("minecraft:dripstone_caves"); + + DynamicRegistry.Key SNOWY_TAIGA = DynamicRegistry.Key.of("minecraft:snowy_taiga"); + + DynamicRegistry.Key GROVE = DynamicRegistry.Key.of("minecraft:grove"); + + DynamicRegistry.Key SWAMP = DynamicRegistry.Key.of("minecraft:swamp"); + + DynamicRegistry.Key JAGGED_PEAKS = DynamicRegistry.Key.of("minecraft:jagged_peaks"); + + DynamicRegistry.Key COLD_OCEAN = DynamicRegistry.Key.of("minecraft:cold_ocean"); + + DynamicRegistry.Key FOREST = DynamicRegistry.Key.of("minecraft:forest"); + + DynamicRegistry.Key LUSH_CAVES = DynamicRegistry.Key.of("minecraft:lush_caves"); + + DynamicRegistry.Key BASALT_DELTAS = DynamicRegistry.Key.of("minecraft:basalt_deltas"); + + DynamicRegistry.Key DEEP_COLD_OCEAN = DynamicRegistry.Key.of("minecraft:deep_cold_ocean"); + + DynamicRegistry.Key ICE_SPIKES = DynamicRegistry.Key.of("minecraft:ice_spikes"); + + DynamicRegistry.Key END_MIDLANDS = DynamicRegistry.Key.of("minecraft:end_midlands"); + + DynamicRegistry.Key FROZEN_OCEAN = DynamicRegistry.Key.of("minecraft:frozen_ocean"); + + DynamicRegistry.Key DESERT = DynamicRegistry.Key.of("minecraft:desert"); + + DynamicRegistry.Key DEEP_FROZEN_OCEAN = DynamicRegistry.Key.of("minecraft:deep_frozen_ocean"); + + DynamicRegistry.Key WINDSWEPT_FOREST = DynamicRegistry.Key.of("minecraft:windswept_forest"); + + DynamicRegistry.Key JUNGLE = DynamicRegistry.Key.of("minecraft:jungle"); + + DynamicRegistry.Key OCEAN = DynamicRegistry.Key.of("minecraft:ocean"); + + DynamicRegistry.Key OLD_GROWTH_SPRUCE_TAIGA = DynamicRegistry.Key.of("minecraft:old_growth_spruce_taiga"); + + DynamicRegistry.Key SNOWY_BEACH = DynamicRegistry.Key.of("minecraft:snowy_beach"); + + DynamicRegistry.Key WINDSWEPT_SAVANNA = DynamicRegistry.Key.of("minecraft:windswept_savanna"); + + DynamicRegistry.Key END_BARRENS = DynamicRegistry.Key.of("minecraft:end_barrens"); + + DynamicRegistry.Key WARM_OCEAN = DynamicRegistry.Key.of("minecraft:warm_ocean"); + + DynamicRegistry.Key DEEP_LUKEWARM_OCEAN = DynamicRegistry.Key.of("minecraft:deep_lukewarm_ocean"); + + DynamicRegistry.Key FLOWER_FOREST = DynamicRegistry.Key.of("minecraft:flower_forest"); + + DynamicRegistry.Key SOUL_SAND_VALLEY = DynamicRegistry.Key.of("minecraft:soul_sand_valley"); + + DynamicRegistry.Key NETHER_WASTES = DynamicRegistry.Key.of("minecraft:nether_wastes"); + + DynamicRegistry.Key FROZEN_PEAKS = DynamicRegistry.Key.of("minecraft:frozen_peaks"); + + DynamicRegistry.Key THE_END = DynamicRegistry.Key.of("minecraft:the_end"); + + DynamicRegistry.Key SMALL_END_ISLANDS = DynamicRegistry.Key.of("minecraft:small_end_islands"); + + DynamicRegistry.Key OLD_GROWTH_BIRCH_FOREST = DynamicRegistry.Key.of("minecraft:old_growth_birch_forest"); + + DynamicRegistry.Key CRIMSON_FOREST = DynamicRegistry.Key.of("minecraft:crimson_forest"); + + DynamicRegistry.Key THE_VOID = DynamicRegistry.Key.of("minecraft:the_void"); + + DynamicRegistry.Key DEEP_DARK = DynamicRegistry.Key.of("minecraft:deep_dark"); + + DynamicRegistry.Key MEADOW = DynamicRegistry.Key.of("minecraft:meadow"); + + DynamicRegistry.Key WINDSWEPT_GRAVELLY_HILLS = DynamicRegistry.Key.of("minecraft:windswept_gravelly_hills"); + + DynamicRegistry.Key SAVANNA = DynamicRegistry.Key.of("minecraft:savanna"); + + DynamicRegistry.Key PLAINS = DynamicRegistry.Key.of("minecraft:plains"); +} diff --git a/src/autogenerated/java/net/minestom/server/world/biomes/Biomes.java b/src/autogenerated/java/net/minestom/server/world/biomes/Biomes.java deleted file mode 100644 index 245a19d9fc5..00000000000 --- a/src/autogenerated/java/net/minestom/server/world/biomes/Biomes.java +++ /dev/null @@ -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"); -} diff --git a/src/main/java/net/kyori/adventure/nbt/TagStringIOExt.java b/src/main/java/net/kyori/adventure/nbt/TagStringIOExt.java new file mode 100644 index 00000000000..814093dad7c --- /dev/null +++ b/src/main/java/net/kyori/adventure/nbt/TagStringIOExt.java @@ -0,0 +1,64 @@ +package net.kyori.adventure.nbt; + +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.util.Map; + +// Based on net.kyori.adventure.nbt.TagStringIO licensed under the MIT license. +// https://github.com/KyoriPowered/adventure/blob/main/4/nbt/src/main/java/net/kyori/adventure/nbt/TagStringIO.java +public final class TagStringIOExt { + + public static @NotNull String writeTag(@NotNull BinaryTag tag) { + return writeTag(tag, ""); + } + + public static @NotNull String writeTag(@NotNull BinaryTag input, @NotNull String indent) { + final StringBuilder sb = new StringBuilder(); + try (final TagStringWriter emit = new TagStringWriter(sb, indent)) { + emit.writeTag(input); + } catch (IOException e) { + // The IOException comes from Writer#close(), but we are passing a StringBuilder which + // is not a writer and does not need to be closed so will not throw. + throw new RuntimeException(e); + } + return sb.toString(); + } + + public static @NotNull BinaryTag readTag(@NotNull String input) throws IOException { + try { + final CharBuffer buffer = new CharBuffer(input); + final TagStringReader parser = new TagStringReader(buffer); + final BinaryTag tag = parser.tag(); + if (buffer.skipWhitespace().hasMore()) { + throw new IOException("Document had trailing content after first tag"); + } + return tag; + } catch (final StringTagParseException ex) { + throw new IOException(ex); + } + } + + /** + * Reads a tag and returns the remainder of the input buffer. + */ + public static Map.Entry<@NotNull BinaryTag, @NotNull String> readTagEmbedded(@NotNull String input) throws IOException { + try { + final CharBuffer buffer = new CharBuffer(input); + final TagStringReader parser = new TagStringReader(buffer); + final BinaryTag tag = parser.tag(); + + // Collect remaining (todo figure out a better way, probably need to just write an snbt parser) + final StringBuilder remainder = new StringBuilder(); + while (buffer.hasMore()) { + remainder.append(buffer.take()); + } + + return Map.entry(tag, remainder.toString()); + } catch (final StringTagParseException ex) { + throw new IOException(ex); + } + } + + private TagStringIOExt() {} +} diff --git a/src/main/java/net/minestom/server/FeatureFlag.java b/src/main/java/net/minestom/server/FeatureFlag.java new file mode 100644 index 00000000000..811a545fe1b --- /dev/null +++ b/src/main/java/net/minestom/server/FeatureFlag.java @@ -0,0 +1,6 @@ +package net.minestom.server; + +import net.minestom.server.registry.StaticProtocolObject; + +public sealed interface FeatureFlag extends StaticProtocolObject, FeatureFlags permits FeatureFlagImpl { +} diff --git a/src/main/java/net/minestom/server/FeatureFlagImpl.java b/src/main/java/net/minestom/server/FeatureFlagImpl.java new file mode 100644 index 00000000000..94fa2ac6d19 --- /dev/null +++ b/src/main/java/net/minestom/server/FeatureFlagImpl.java @@ -0,0 +1,33 @@ +package net.minestom.server; + +import net.minestom.server.registry.Registry; +import net.minestom.server.utils.NamespaceID; +import org.jetbrains.annotations.NotNull; + +record FeatureFlagImpl(@NotNull Registry.FeatureFlagEntry registry) implements FeatureFlag { + + private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.FEATURE_FLAGS, + (namespace, properties) -> new FeatureFlagImpl(Registry.featureFlag(namespace, properties))); + + static FeatureFlag get(@NotNull String namespace) { + return CONTAINER.get(namespace); + } + + static FeatureFlag getSafe(@NotNull String namespace) { + return CONTAINER.getSafe(namespace); + } + + static FeatureFlag getId(int id) { + return CONTAINER.getId(id); + } + + @Override + public @NotNull NamespaceID namespace() { + return registry.namespace(); + } + + @Override + public int id() { + return registry.id(); + } +} diff --git a/src/main/java/net/minestom/server/Metrics.java b/src/main/java/net/minestom/server/Metrics.java index 2d221da02ef..56cb113053d 100644 --- a/src/main/java/net/minestom/server/Metrics.java +++ b/src/main/java/net/minestom/server/Metrics.java @@ -11,7 +11,6 @@ import net.minestom.server.extras.MojangAuth; import net.minestom.server.extras.bungee.BungeeCordProxy; import net.minestom.server.extras.velocity.VelocityProxy; -import net.minestom.server.utils.debug.DebugUtils; import org.bstats.MetricsBase; import org.bstats.charts.DrilldownPie; import org.bstats.charts.SimplePie; @@ -51,7 +50,7 @@ public void start() { } System.setProperty("bstats.relocatecheck", "false"); metrics = new MetricsBase("server-implementation", serverUUID, SERVICE_ID,true, this::getServerData, jsonObjectBuilder -> {}, null, () -> true, MinecraftServer.LOGGER::error,MinecraftServer.LOGGER::info, - DebugUtils.INSIDE_TEST, DebugUtils.INSIDE_TEST,DebugUtils.INSIDE_TEST); + ServerFlag.INSIDE_TEST, ServerFlag.INSIDE_TEST,ServerFlag.INSIDE_TEST); metrics.addCustomChart(new SimplePie("minecraft_version", () -> { String minecraftVersion = MinecraftServer.VERSION_NAME; diff --git a/src/main/java/net/minestom/server/MinecraftServer.java b/src/main/java/net/minestom/server/MinecraftServer.java index e19a6cfca2a..65ac14695b0 100644 --- a/src/main/java/net/minestom/server/MinecraftServer.java +++ b/src/main/java/net/minestom/server/MinecraftServer.java @@ -3,16 +3,23 @@ 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.attribute.AttributeManager; 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.entity.metadata.other.PaintingMeta; import net.minestom.server.event.GlobalEventHandler; import net.minestom.server.exception.ExceptionManager; import net.minestom.server.extensions.ExtensionManager; 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.instance.block.jukebox.JukeboxSong; +import net.minestom.server.item.armor.TrimMaterial; +import net.minestom.server.item.armor.TrimPattern; +import net.minestom.server.item.enchant.*; 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; @@ -20,15 +27,17 @@ 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.MathUtils; import net.minestom.server.utils.PacketUtils; +import net.minestom.server.utils.nbt.BinaryTagSerializer; 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.Nullable; @@ -44,13 +53,10 @@ * The server needs to be initialized with {@link #init()} and started with {@link #start(String, int)}. * You should register all of your dimensions, biomes, commands, events, etc... in-between. */ -public final class MinecraftServer { +public final class MinecraftServer implements MinecraftConstants { public static final ComponentLogger LOGGER = ComponentLogger.logger(MinecraftServer.class); - public static final String VERSION_NAME = "1.20.4"; - public static final int PROTOCOL_VERSION = 765; - // Threads public static final String THREAD_NAME_BENCHMARK = "Ms-Benchmark"; @@ -128,7 +134,6 @@ public static void setDifficulty(@NotNull Difficulty difficulty) { PacketUtils.broadcastPlayPacket(new ServerDifficultyPacket(difficulty, true)); } - @ApiStatus.Experimental public static @UnknownNullability ServerProcess process() { return serverProcess; } @@ -244,32 +249,76 @@ public static void setCompressionThreshold(int compressionThreshold) { MinecraftServer.compressionThreshold = compressionThreshold; } - public static DimensionTypeManager getDimensionTypeManager() { - return serverProcess.dimension(); + public static AdvancementManager getAdvancementManager() { + return serverProcess.advancement(); + } + + public static @Nullable ExtensionManager getExtensionManager() { + return serverProcess.extension(); + } + + public static TagManager getTagManager() { + return serverProcess.tag(); } - public static BiomeManager getBiomeManager() { + public static @NotNull DynamicRegistry getChatTypeRegistry() { + return serverProcess.chatType(); + } + + public static @NotNull DynamicRegistry getDimensionTypeRegistry() { + return serverProcess.dimensionType(); + } + + public static @NotNull DynamicRegistry getBiomeRegistry() { return serverProcess.biome(); } - public static AttributeManager getAttributeManager() { - return serverProcess.attribute(); + public static @NotNull DynamicRegistry getDamageTypeRegistry() { + return serverProcess.damageType(); } - public static AdvancementManager getAdvancementManager() { - return serverProcess.advancement(); + public static @NotNull DynamicRegistry getTrimMaterialRegistry() { + return serverProcess.trimMaterial(); } - public static @Nullable ExtensionManager getExtensionManager() { - return serverProcess.extension(); + public static @NotNull DynamicRegistry getTrimPatternRegistry() { + return serverProcess.trimPattern(); } - public static TagManager getTagManager() { - return serverProcess.tag(); + public static @NotNull DynamicRegistry getBannerPatternRegistry() { + return serverProcess.bannerPattern(); + } + + public static @NotNull DynamicRegistry getWolfVariantRegistry() { + return serverProcess.wolfVariant(); + } + + public static @NotNull DynamicRegistry getEnchantmentRegistry() { + return serverProcess.enchantment(); + } + + public static @NotNull DynamicRegistry getPaintingVariantRegistry() { + return serverProcess.paintingVariant(); + } + + public static @NotNull DynamicRegistry getJukeboxSongRegistry() { + return serverProcess.jukeboxSong(); + } + + public static @NotNull DynamicRegistry> enchantmentLevelBasedValues() { + return process().enchantmentLevelBasedValues(); + } + + public static @NotNull DynamicRegistry> enchantmentValueEffects() { + return process().enchantmentValueEffects(); + } + + public static @NotNull DynamicRegistry> enchantmentEntityEffects() { + return process().enchantmentEntityEffects(); } - public static TrimManager getTrimManager() { - return serverProcess.trim(); + public static @NotNull DynamicRegistry> enchantmentLocationEffects() { + return process().enchantmentLocationEffects(); } public static Server getServer() { diff --git a/src/main/java/net/minestom/server/ServerFlag.java b/src/main/java/net/minestom/server/ServerFlag.java index 71e3ddca600..93765323c5b 100644 --- a/src/main/java/net/minestom/server/ServerFlag.java +++ b/src/main/java/net/minestom/server/ServerFlag.java @@ -1,6 +1,6 @@ package net.minestom.server; -import net.minestom.server.utils.PropertyUtils; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -12,62 +12,109 @@ public final class ServerFlag { // Server Behavior - public static final int SERVER_TICKS_PER_SECOND = Integer.getInteger("minestom.tps", 20); - public static final int SERVER_MAX_TICK_CATCH_UP = Integer.getInteger("minestom.max-tick-catch-up", 5); - public static final int CHUNK_VIEW_DISTANCE = Integer.getInteger("minestom.chunk-view-distance", 8); - public static final int ENTITY_VIEW_DISTANCE = Integer.getInteger("minestom.entity-view-distance", 5); - public static final int ENTITY_SYNCHRONIZATION_TICKS = Integer.getInteger("minestom.entity-synchronization-ticks", 20); - public static final int WORKER_COUNT = Integer.getInteger("minestom.workers", Runtime.getRuntime().availableProcessors()); - public static final int MAX_PACKET_SIZE = Integer.getInteger("minestom.max-packet-size", 2_097_151); // 3 bytes var-int - public static final int SOCKET_SEND_BUFFER_SIZE = Integer.getInteger("minestom.send-buffer-size", 262_143); - public static final int SOCKET_RECEIVE_BUFFER_SIZE = Integer.getInteger("minestom.receive-buffer-size", 32_767); - public static final int POOLED_BUFFER_SIZE = Integer.getInteger("minestom.pooled-buffer-size", 262_143); - public static final int PLAYER_PACKET_PER_TICK = Integer.getInteger("minestom.packet-per-tick", 20); - public static final int PLAYER_PACKET_QUEUE_SIZE = Integer.getInteger("minestom.packet-queue-size", 1000); - public static final int SEND_LIGHT_AFTER_BLOCK_PLACEMENT_DELAY = Integer.getInteger("minestom.send-light-after-block-placement-delay", 100); - public static final long KEEP_ALIVE_DELAY = Long.getLong("minestom.keep-alive-delay", 10_000); - public static final long KEEP_ALIVE_KICK = Long.getLong("minestom.keep-alive-kick", 30_000); - public static final long LOGIN_PLUGIN_MESSAGE_TIMEOUT = Long.getLong("minestom.login-plugin-message-timeout", 5_000); + public static final Boolean SHUTDOWN_ON_SIGNAL = booleanProperty("minestom.shutdown-on-signal", true); + public static final int SERVER_TICKS_PER_SECOND = intProperty("minestom.tps", 20); + public static final int SERVER_MAX_TICK_CATCH_UP = intProperty("minestom.max-tick-catch-up", 5); + public static final int CHUNK_VIEW_DISTANCE = intProperty("minestom.chunk-view-distance", 8); + public static final int ENTITY_VIEW_DISTANCE = intProperty("minestom.entity-view-distance", 5); + public static final int ENTITY_SYNCHRONIZATION_TICKS = intProperty("minestom.entity-synchronization-ticks", 20); + public static final int WORKER_COUNT = intProperty("minestom.workers", Runtime.getRuntime().availableProcessors()); + public static final int DISPATCHER_THREADS = intProperty("minestom.dispatcher-threads", 1); + public static final int MAX_PACKET_SIZE = intProperty("minestom.max-packet-size", 2_097_151); // 3 bytes var-int + public static final int SOCKET_SEND_BUFFER_SIZE = intProperty("minestom.send-buffer-size", 262_143); + public static final int SOCKET_RECEIVE_BUFFER_SIZE = intProperty("minestom.receive-buffer-size", 32_767); + public static final int POOLED_BUFFER_SIZE = intProperty("minestom.pooled-buffer-size", 262_143); + public static final int PLAYER_PACKET_PER_TICK = intProperty("minestom.packet-per-tick", 20); + public static final int PLAYER_PACKET_QUEUE_SIZE = intProperty("minestom.packet-queue-size", 1000); + public static final int SEND_LIGHT_AFTER_BLOCK_PLACEMENT_DELAY = intProperty("minestom.send-light-after-block-placement-delay", 100); + public static final long KEEP_ALIVE_DELAY = longProperty("minestom.keep-alive-delay", 10_000); + public static final long KEEP_ALIVE_KICK = longProperty("minestom.keep-alive-kick", 30_000); + public static final long LOGIN_PLUGIN_MESSAGE_TIMEOUT = longProperty("minestom.login-plugin-message-timeout", 5_000); + + // Chunk update + public static final float MIN_CHUNKS_PER_TICK = floatProperty("minestom.chunk-queue.min-per-tick", 0.01f); + public static final float MAX_CHUNKS_PER_TICK = floatProperty("minestom.chunk-queue.max-per-tick", 64.0f); + public static final float CHUNKS_PER_TICK_MULTIPLIER = floatProperty("minestom.chunk-queue.multiplier", 1f); // Packet sending optimizations - public static final boolean GROUPED_PACKET = PropertyUtils.getBoolean("minestom.grouped-packet", true); - public static final boolean CACHED_PACKET = PropertyUtils.getBoolean("minestom.cached-packet", true); - public static final boolean VIEWABLE_PACKET = PropertyUtils.getBoolean("minestom.viewable-packet", true); + public static final boolean GROUPED_PACKET = booleanProperty("minestom.grouped-packet", true); + public static final boolean CACHED_PACKET = booleanProperty("minestom.cached-packet", true); + public static final boolean VIEWABLE_PACKET = booleanProperty("minestom.viewable-packet", true); // Tags - public static final boolean TAG_HANDLER_CACHE_ENABLED = PropertyUtils.getBoolean("minestom.tag-handler-cache", true); - public static final boolean SERIALIZE_EMPTY_COMPOUND = PropertyUtils.getBoolean("minestom.serialization.serialize-empty-nbt-compound", false); + public static final boolean TAG_HANDLER_CACHE_ENABLED = booleanProperty("minestom.tag-handler-cache", true); + public static final boolean SERIALIZE_EMPTY_COMPOUND = booleanProperty("minestom.serialization.serialize-empty-nbt-compound", false); // Online Mode - public static final @NotNull String AUTH_URL = System.getProperty("minestom.auth.url", "https://sessionserver.mojang.com/session/minecraft/hasJoined"); + public static final @NotNull String AUTH_URL = stringProperty("minestom.auth.url", "https://sessionserver.mojang.com/session/minecraft/hasJoined"); // World - public static final @Nullable String STACKING_RULE = System.getProperty("minestom.stacking-rule"); - public static final int WORLD_BORDER_SIZE = Integer.getInteger("minestom.world-border-size", 29999984); - - // Biomes - public static final boolean BIOMES_ENABLED = System.getProperty("minestom.attributes.disabled") == null; - - // Attributes - public static final boolean ATTRIBUTES_ENABLED = System.getProperty("minestom.attributes.disabled") == null; + public static final int WORLD_BORDER_SIZE = intProperty("minestom.world-border-size", 29999984); // Terminal public static final boolean TERMINAL_ENABLED = System.getProperty("minestom.terminal.disabled") == null; - public static final boolean TERMINAL_SUPPORT_HEX_COLOR = PropertyUtils.getBoolean("minestom.terminal.support-hex-color", true); - public static final boolean TERMINAL_SUPPORT_COLOR = PropertyUtils.getBoolean("minestom.terminal.support-color", true); + public static final boolean TERMINAL_SUPPORT_HEX_COLOR = Boolean.getBoolean("minestom.terminal.support-hex-color"); + public static final boolean TERMINAL_SUPPORT_COLOR = Boolean.getBoolean("minestom.terminal.support-color"); // Extensions todo use enabled flag - public static final boolean EXTENSIONS_ENABLED = PropertyUtils.getBoolean("minestom.extension.enabled", false); + public static final boolean EXTENSIONS_ENABLED = Boolean.getBoolean("minestom.extension.enabled"); public static final @NotNull String EXTENSIONS_FOLDER = System.getProperty("minestom.extension.folder", "extensions"); public static final @Nullable String EXTENSIONS_DEV_CLASSES = System.getProperty("minestom.extension.indevfolder.classes"); public static final @Nullable String EXTENSIONS_DEV_RESOURCES = System.getProperty("minestom.extension.indevfolder.resources"); + // Attributes + public static final boolean ATTRIBUTES_ENABLED = System.getProperty("minestom.attributes.disabled") == null; // Maps - public static final @NotNull String MAP_RGB_MAPPING = System.getProperty("minestom.map.rgbmapping", "lazy"); - public static final @Nullable String MAP_RGB_REDUCTION = System.getProperty("minestom.map.rgbreduction"); // Only used if rgb mapping is "approximate" + public static final @NotNull String MAP_RGB_MAPPING = stringProperty("minestom.map.rgbmapping", "lazy"); + public static final @Nullable String MAP_RGB_REDUCTION = stringProperty("minestom.map.rgbreduction"); // Only used if rgb mapping is "approximate" // Experimental/Unstable - public static final boolean EVENT_NODE_ALLOW_MULTIPLE_PARENTS = Boolean.getBoolean("minestom.event.multiple-parents"); + public static final boolean REGISTRY_LATE_REGISTER = booleanProperty("minestom.registry.late-register"); + public static final boolean REGISTRY_UNSAFE_OPS = booleanProperty("minestom.registry.unsafe-ops"); + public static final boolean EVENT_NODE_ALLOW_MULTIPLE_PARENTS = booleanProperty("minestom.event.multiple-parents"); + + public static boolean INSIDE_TEST = booleanProperty("minestom.inside-test", false); + + private ServerFlag() { + } + + private static boolean booleanProperty(String name) { + return Boolean.getBoolean(name); + } + + private static boolean booleanProperty(String name, boolean defaultValue) { + boolean result = defaultValue; + try { + final String value = System.getProperty(name); + if (value != null) result = Boolean.parseBoolean(value); + } catch (IllegalArgumentException | NullPointerException ignored) { + } + return result; + } + + @Contract("_, null -> null; _, !null -> !null") + private static String stringProperty(@NotNull String name, @Nullable String defaultValue) { + return System.getProperty(name, defaultValue); + } + + private static String stringProperty(@NotNull String name) { + return System.getProperty(name); + } + + private static int intProperty(String name, int defaultValue) { + return Integer.getInteger(name, defaultValue); + } - private ServerFlag() {} + private static long longProperty(String name, long defaultValue) { + return Long.getLong(name, defaultValue); + } + private static Float floatProperty(String name, Float defaultValue) { + Float result = defaultValue; + try { + final String value = System.getProperty(name); + if (value != null) result = Float.parseFloat(value); + } catch (IllegalArgumentException | NullPointerException ignored) { + } + return result; + } } diff --git a/src/main/java/net/minestom/server/ServerProcess.java b/src/main/java/net/minestom/server/ServerProcess.java index 401745f5d08..c700f316410 100644 --- a/src/main/java/net/minestom/server/ServerProcess.java +++ b/src/main/java/net/minestom/server/ServerProcess.java @@ -2,7 +2,6 @@ import net.minestom.server.advancements.AdvancementManager; import net.minestom.server.adventure.bossbar.BossBarManager; -import net.minestom.server.attribute.AttributeManager; import net.minestom.server.command.CommandManager; import net.minestom.server.event.GlobalEventHandler; import net.minestom.server.exception.ExceptionManager; @@ -12,28 +11,25 @@ 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; import org.jetbrains.annotations.Nullable; import java.net.SocketAddress; -@ApiStatus.Experimental @ApiStatus.NonExtendable -public interface ServerProcess extends Snapshotable { +public interface ServerProcess extends Registries, Snapshotable { /** * Handles incoming connections/players. */ @@ -79,21 +75,6 @@ public interface ServerProcess extends Snapshotable { @NotNull BenchmarkManager benchmark(); - /** - * Handles registered dimensions. - */ - @NotNull DimensionTypeManager dimension(); - - /** - * Handles registered biomes. - */ - @NotNull BiomeManager biome(); - - /** - * Handles registered attributes - */ - @NotNull AttributeManager attribute(); - /** * Handles registered advancements. */ @@ -114,8 +95,6 @@ public interface ServerProcess extends Snapshotable { */ @NotNull TagManager tag(); - @NotNull TrimManager trim(); - /** * Handles all thrown exceptions from the server. */ diff --git a/src/main/java/net/minestom/server/ServerProcessImpl.java b/src/main/java/net/minestom/server/ServerProcessImpl.java index 5eff5ac8daf..9040c041827 100644 --- a/src/main/java/net/minestom/server/ServerProcessImpl.java +++ b/src/main/java/net/minestom/server/ServerProcessImpl.java @@ -9,9 +9,11 @@ import java.util.concurrent.atomic.AtomicReference; import net.minestom.server.advancements.AdvancementManager; import net.minestom.server.adventure.bossbar.BossBarManager; -import net.minestom.server.attribute.AttributeManager; 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.entity.metadata.other.PaintingMeta; import net.minestom.server.event.EventDispatcher; import net.minestom.server.event.GlobalEventHandler; import net.minestom.server.event.server.ServerTickMonitorEvent; @@ -22,38 +24,58 @@ 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.instance.block.jukebox.JukeboxSong; +import net.minestom.server.item.armor.TrimMaterial; +import net.minestom.server.item.armor.TrimPattern; +import net.minestom.server.item.enchant.*; 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.EntitySnapshot; -import net.minestom.server.snapshot.InstanceSnapshot; -import net.minestom.server.snapshot.ServerSnapshot; -import net.minestom.server.snapshot.SnapshotImpl; -import net.minestom.server.snapshot.SnapshotUpdater; +import net.minestom.server.snapshot.*; import net.minestom.server.terminal.MinestomTerminal; import net.minestom.server.thread.Acquirable; import net.minestom.server.thread.ThreadDispatcher; +import net.minestom.server.thread.ThreadProvider; 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.utils.nbt.BinaryTagSerializer; +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; final class ServerProcessImpl implements ServerProcess { private static final Logger LOGGER = LoggerFactory.getLogger(ServerProcessImpl.class); - private static final Boolean SHUTDOWN_ON_SIGNAL = PropertyUtils.getBoolean("minestom.shutdown-on-signal", true); private final ExceptionManager exception; + + private final DynamicRegistry> enchantmentLevelBasedValues; + private final DynamicRegistry> enchantmentValueEffects; + private final DynamicRegistry> enchantmentEntityEffects; + private final DynamicRegistry> enchantmentLocationEffects; + + private final DynamicRegistry chatType; + private final DynamicRegistry dimensionType; + private final DynamicRegistry biome; + private final DynamicRegistry damageType; + private final DynamicRegistry trimMaterial; + private final DynamicRegistry trimPattern; + private final DynamicRegistry bannerPattern; + private final DynamicRegistry wolfVariant; + private final DynamicRegistry enchantment; + private final DynamicRegistry paintingVariant; + private final DynamicRegistry jukeboxSong; + private final ExtensionManager extension; private final ConnectionManager connection; private final PacketListenerManager packetListener; @@ -66,13 +88,10 @@ 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 AttributeManager attribute; private final AdvancementManager advancement; private final BossBarManager bossBar; private final TagManager tag; - private final TrimManager trim; + private final Server server; private final Metrics metrics; @@ -88,10 +107,28 @@ final class ServerProcessImpl implements ServerProcess { public ServerProcessImpl() throws IOException { this.exception = new ExceptionManager(); this.extension = new ExtensionManager(this); + // The order of initialization here is relevant, we must load the enchantment util registries before the vanilla data is loaded. + this.enchantmentLevelBasedValues = LevelBasedValue.createDefaultRegistry(); + this.enchantmentValueEffects = ValueEffect.createDefaultRegistry(); + this.enchantmentEntityEffects = EntityEffect.createDefaultRegistry(); + this.enchantmentLocationEffects = LocationEffect.createDefaultRegistry(); + + 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.enchantment = Enchantment.createDefaultRegistry(this); + this.paintingVariant = PaintingMeta.Variant.createDefaultRegistry(); + this.jukeboxSong = JukeboxSong.createDefaultRegistry(); + this.connection = new ConnectionManager(); this.packetListener = new PacketListenerManager(); this.packetProcessor = new PacketProcessor(packetListener); - this.instance = new InstanceManager(); + this.instance = new InstanceManager(this); this.block = new BlockManager(); this.command = new CommandManager(); this.recipe = new RecipeManager(); @@ -99,20 +136,82 @@ public ServerProcessImpl() throws IOException { this.eventHandler = new GlobalEventHandler(); this.scheduler = new SchedulerManager(); this.benchmark = new BenchmarkManager(); - this.dimension = new DimensionTypeManager(); - this.biome = new BiomeManager(); - this.attribute = new AttributeManager(); this.advancement = new AdvancementManager(); this.bossBar = new BossBarManager(); this.tag = new TagManager(); - this.trim = new TrimManager(); + this.server = new Server(packetProcessor); - this.dispatcher = ThreadDispatcher.singleThread(); + this.dispatcher = ThreadDispatcher.of(ThreadProvider.counter(), ServerFlag.DISPATCHER_THREADS); this.ticker = new TickerImpl(); this.metrics = new Metrics(); } + @Override + public @NotNull ExceptionManager exception() { + return exception; + } + + @Override + public @NotNull DynamicRegistry damageType() { + return damageType; + } + + @Override + public @NotNull DynamicRegistry trimMaterial() { + return trimMaterial; + } + + @Override + public @NotNull DynamicRegistry trimPattern() { + return trimPattern; + } + + @Override + public @NotNull DynamicRegistry bannerPattern() { + return bannerPattern; + } + + @Override + public @NotNull DynamicRegistry wolfVariant() { + return wolfVariant; + } + + @Override + public @NotNull DynamicRegistry enchantment() { + return enchantment; + } + + @Override + public @NotNull DynamicRegistry paintingVariant() { + return paintingVariant; + } + + @Override + public @NotNull DynamicRegistry jukeboxSong() { + return jukeboxSong; + } + + @Override + public @NotNull DynamicRegistry> enchantmentLevelBasedValues() { + return enchantmentLevelBasedValues; + } + + @Override + public @NotNull DynamicRegistry> enchantmentValueEffects() { + return enchantmentValueEffects; + } + + @Override + public @NotNull DynamicRegistry> enchantmentEntityEffects() { + return enchantmentEntityEffects; + } + + @Override + public @NotNull DynamicRegistry> enchantmentLocationEffects() { + return enchantmentLocationEffects; + } + @Override public @NotNull ConnectionManager connection() { return connection; @@ -158,16 +257,6 @@ public ServerProcessImpl() throws IOException { return benchmark; } - @Override - public @NotNull DimensionTypeManager dimension() { - return dimension; - } - - @Override - public @NotNull BiomeManager biome() { - return biome; - } - @Override public @NotNull AdvancementManager advancement() { return advancement; @@ -189,13 +278,18 @@ public ServerProcessImpl() throws IOException { } @Override - public @NotNull TrimManager trim() { - return trim; + public @NotNull DynamicRegistry chatType() { + return chatType; } @Override - public @NotNull ExceptionManager exception() { - return exception; + public @NotNull DynamicRegistry dimensionType() { + return dimensionType; + } + + @Override + public @NotNull DynamicRegistry biome() { + return biome; } @Override @@ -223,11 +317,6 @@ public ServerProcessImpl() throws IOException { return ticker; } - @Override - public @NotNull AttributeManager attribute() { - return attribute; - } - @Override public void start(@NotNull SocketAddress socketAddress) { if (!started.compareAndSet(false, true)) { @@ -256,24 +345,15 @@ public void start(@NotNull SocketAddress socketAddress) { LOGGER.info(MinecraftServer.getBrandName() + " server started successfully."); - if (ServerFlag.ATTRIBUTES_ENABLED) { - attribute.loadVanillaAttributes(); - } - LOGGER.info("Register Attributes({})", attribute.unmodifiableCollection().size()); - - if (ServerFlag.BIOMES_ENABLED) { - biome.loadVanillaBiomes(); - } - LOGGER.info("Register Biomes({})", biome.unmodifiableCollection().size()); - if (ServerFlag.TERMINAL_ENABLED) { MinestomTerminal.start(); } if (bstatsEnabled) { this.metrics.start(); } + // Stop the server on SIGINT - if (SHUTDOWN_ON_SIGNAL) Runtime.getRuntime().addShutdownHook(new Thread(this::stop)); + if (ServerFlag.SHUTDOWN_ON_SIGNAL) Runtime.getRuntime().addShutdownHook(new Thread(this::stop)); } @Override diff --git a/src/main/java/net/minestom/server/adventure/AdventurePacketConvertor.java b/src/main/java/net/minestom/server/adventure/AdventurePacketConvertor.java index 2cf3005dfff..4acc71e5ade 100644 --- a/src/main/java/net/minestom/server/adventure/AdventurePacketConvertor.java +++ b/src/main/java/net/minestom/server/adventure/AdventurePacketConvertor.java @@ -10,11 +10,11 @@ import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.title.Title; import net.kyori.adventure.title.TitlePart; -import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.Entity; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.play.*; import net.minestom.server.sound.SoundEvent; +import net.minestom.server.utils.NamespaceID; import net.minestom.server.utils.TickUtils; import org.jetbrains.annotations.NotNull; @@ -113,15 +113,12 @@ public static int getNamedTextColorValue(@NotNull NamedTextColor color) { * @return the sound packet */ public static @NotNull ServerPacket createSoundPacket(@NotNull Sound sound, double x, double y, double z) { - final SoundEvent minestomSound = SoundEvent.fromNamespaceId(sound.name().asString()); + final NamespaceID soundName = NamespaceID.from(sound.name().asString()); + SoundEvent minestomSound = SoundEvent.fromNamespaceId(soundName); + if (minestomSound == null) minestomSound = SoundEvent.of(soundName, null); + final long seed = sound.seed().orElse(ThreadLocalRandom.current().nextLong()); - if (minestomSound == null) { - return new SoundEffectPacket(sound.name().asString(), null, sound.source(), - new Vec(x, y, z), sound.volume(), sound.pitch(), seed); - } else { - return new SoundEffectPacket(minestomSound, null, sound.source(), - new Vec(x, y, z), sound.volume(), sound.pitch(), seed); - } + return new SoundEffectPacket(minestomSound, sound.source(), (int) x, (int) y, (int) z, sound.volume(), sound.pitch(), seed); } /** @@ -138,14 +135,12 @@ public static int getNamedTextColorValue(@NotNull NamedTextColor color) { if (!(emitter instanceof Entity entity)) throw new IllegalArgumentException("you can only call this method with entities"); - final SoundEvent minestomSound = SoundEvent.fromNamespaceId(sound.name().asString()); - final long seed = sound.seed().orElse(ThreadLocalRandom.current().nextLong()); + final NamespaceID soundName = NamespaceID.from(sound.name().asString()); + SoundEvent minestomSound = SoundEvent.fromNamespaceId(soundName); + if (minestomSound == null) minestomSound = SoundEvent.of(soundName, null); - if (minestomSound != null) { - return new EntitySoundEffectPacket(minestomSound, null, sound.source(), entity.getEntityId(), sound.volume(), sound.pitch(), seed); - } else { - return new EntitySoundEffectPacket(sound.name().asString(), null, sound.source(), entity.getEntityId(), sound.volume(), sound.pitch(), seed); - } + final long seed = sound.seed().orElse(ThreadLocalRandom.current().nextLong()); + return new EntitySoundEffectPacket(minestomSound, sound.source(), entity.getEntityId(), sound.volume(), sound.pitch(), seed); } /** diff --git a/src/main/java/net/minestom/server/adventure/MinestomAdventure.java b/src/main/java/net/minestom/server/adventure/MinestomAdventure.java index fe62f85e675..10ffcdfa7fb 100644 --- a/src/main/java/net/minestom/server/adventure/MinestomAdventure.java +++ b/src/main/java/net/minestom/server/adventure/MinestomAdventure.java @@ -1,21 +1,18 @@ package net.minestom.server.adventure; -import java.io.StringReader; - +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.TagStringIO; import net.kyori.adventure.text.Component; import net.kyori.adventure.translation.GlobalTranslator; import net.kyori.adventure.util.Codec; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.IOException; import java.util.Locale; import java.util.Objects; import java.util.function.BiFunction; -import org.jglrxavpok.hephaistos.nbt.NBT; -import org.jglrxavpok.hephaistos.nbt.NBTException; -import org.jglrxavpok.hephaistos.parser.SNBTParser; - /** * Adventure related constants, etc. */ @@ -23,8 +20,8 @@ public final class MinestomAdventure { /** * A codec to convert between strings and NBT. */ - public static final Codec NBT_CODEC - = Codec.codec(encoded -> new SNBTParser(new StringReader(encoded)).parse(), NBT::toSNBT); + public static final Codec NBT_CODEC + = Codec.codec(TagStringIO.get()::asCompound, TagStringIO.get()::asString); /** * If components should be automatically translated in outgoing packets. diff --git a/src/main/java/net/minestom/server/adventure/provider/NBTLegacyHoverEventSerializer.java b/src/main/java/net/minestom/server/adventure/provider/NBTLegacyHoverEventSerializer.java index 690592629d9..12770ffe67a 100644 --- a/src/main/java/net/minestom/server/adventure/provider/NBTLegacyHoverEventSerializer.java +++ b/src/main/java/net/minestom/server/adventure/provider/NBTLegacyHoverEventSerializer.java @@ -1,6 +1,7 @@ package net.minestom.server.adventure.provider; import net.kyori.adventure.key.Key; +import net.kyori.adventure.nbt.CompoundBinaryTag; import net.kyori.adventure.nbt.api.BinaryTagHolder; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.HoverEvent; @@ -9,14 +10,10 @@ import net.kyori.adventure.util.Codec; import net.minestom.server.adventure.MinestomAdventure; import org.jetbrains.annotations.NotNull; -import org.jglrxavpok.hephaistos.nbt.NBT; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; -import org.jglrxavpok.hephaistos.nbt.NBTException; import java.io.IOException; import java.util.Objects; import java.util.UUID; -import java.util.concurrent.atomic.AtomicReference; final class NBTLegacyHoverEventSerializer implements LegacyHoverEventSerializer { static final NBTLegacyHoverEventSerializer INSTANCE = new NBTLegacyHoverEventSerializer(); @@ -30,76 +27,46 @@ private NBTLegacyHoverEventSerializer() { @Override public HoverEvent.@NotNull ShowItem deserializeShowItem(@NotNull Component input) throws IOException { final String raw = PlainTextComponentSerializer.plainText().serialize(input); - try { - // attempt the parse - final NBT nbt = MinestomAdventure.NBT_CODEC.decode(raw); - if (!(nbt instanceof NBTCompound contents)) throw new IOException("contents were not a compound"); - final NBTCompound tag = contents.getCompound(ITEM_TAG); - - // create the event - return HoverEvent.ShowItem.showItem( - Key.key(Objects.requireNonNullElse(contents.getString(ITEM_TYPE), "")), - Objects.requireNonNullElse(contents.getByte(ITEM_COUNT), (byte) 1), - tag == null ? null : BinaryTagHolder.encode(tag, MinestomAdventure.NBT_CODEC) - ); - } catch (final NBTException e) { - throw new IOException(e); - } + // attempt the parse + final CompoundBinaryTag contents = MinestomAdventure.NBT_CODEC.decode(raw); + final CompoundBinaryTag tag = contents.getCompound(ITEM_TAG); + + // create the event + return HoverEvent.ShowItem.showItem( + Key.key(contents.getString(ITEM_TYPE, "")), + contents.getByte(ITEM_COUNT, (byte) 1), + tag.size() == 0 ? null : BinaryTagHolder.encode(tag, MinestomAdventure.NBT_CODEC) + ); } @Override public HoverEvent.@NotNull ShowEntity deserializeShowEntity(@NotNull Component input, Codec.Decoder componentDecoder) throws IOException { final String raw = PlainTextComponentSerializer.plainText().serialize(input); - try { - final NBT nbt = MinestomAdventure.NBT_CODEC.decode(raw); - if (!(nbt instanceof NBTCompound contents)) throw new IOException("contents were not a compound"); - - return HoverEvent.ShowEntity.showEntity( - Key.key(Objects.requireNonNullElse(contents.getString(ENTITY_TYPE), "")), - UUID.fromString(Objects.requireNonNullElse(contents.getString(ENTITY_ID), "")), - componentDecoder.decode(Objects.requireNonNullElse(contents.getString(ENTITY_NAME), "")) - ); - } catch (NBTException e) { - throw new IOException(e); - } + final CompoundBinaryTag contents = MinestomAdventure.NBT_CODEC.decode(raw); + return HoverEvent.ShowEntity.showEntity( + Key.key(contents.getString(ENTITY_TYPE, "")), + UUID.fromString(Objects.requireNonNullElse(contents.getString(ENTITY_ID), "")), + componentDecoder.decode(Objects.requireNonNullElse(contents.getString(ENTITY_NAME), "")) + ); } @Override public @NotNull Component serializeShowItem(HoverEvent.@NotNull ShowItem input) throws IOException { - AtomicReference exception = new AtomicReference<>(null); - final NBTCompound tag = NBT.Compound(t -> { - t.setString(ITEM_TYPE, input.item().asString()); - t.setByte(ITEM_COUNT, (byte) input.count()); - - final BinaryTagHolder nbt = input.nbt(); - if (nbt != null) { - try { - t.set(ITEM_TAG, nbt.get(MinestomAdventure.NBT_CODEC)); - } catch (NBTException e) { - exception.set(e); - } - } - }); - - if (exception.get() != null) { - throw new IOException(exception.get()); - } - - return Component.text(MinestomAdventure.NBT_CODEC.encode(tag)); + CompoundBinaryTag.Builder tag = CompoundBinaryTag.builder(); + tag.putString(ITEM_TYPE, input.item().asString()); + tag.putByte(ITEM_COUNT, (byte) input.count()); + final BinaryTagHolder nbt = input.nbt(); + if (nbt != null) tag.put(ITEM_TAG, nbt.get(MinestomAdventure.NBT_CODEC)); + return Component.text(MinestomAdventure.NBT_CODEC.encode(tag.build())); } @Override - public @NotNull Component serializeShowEntity(HoverEvent.@NotNull ShowEntity input, Codec.Encoder componentEncoder) { - final NBTCompound tag = NBT.Compound(t -> { - t.setString(ENTITY_ID, input.id().toString()); - t.setString(ENTITY_TYPE, input.type().asString()); - - final Component name = input.name(); - if (name != null) { - t.setString(ENTITY_NAME, componentEncoder.encode(name)); - } - }); - - return Component.text(MinestomAdventure.NBT_CODEC.encode(tag)); + public @NotNull Component serializeShowEntity(HoverEvent.@NotNull ShowEntity input, Codec.Encoder componentEncoder) throws IOException { + CompoundBinaryTag.Builder tag = CompoundBinaryTag.builder(); + tag.putString(ENTITY_ID, input.id().toString()); + tag.putString(ENTITY_TYPE, input.type().asString()); + final Component name = input.name(); + if (name != null) tag.putString(ENTITY_NAME, componentEncoder.encode(name)); + return Component.text(MinestomAdventure.NBT_CODEC.encode(tag.build())); } } diff --git a/src/main/java/net/minestom/server/adventure/serializer/nbt/NbtComponentSerializer.java b/src/main/java/net/minestom/server/adventure/serializer/nbt/NbtComponentSerializer.java index a397352e9f7..174ab0064c8 100644 --- a/src/main/java/net/minestom/server/adventure/serializer/nbt/NbtComponentSerializer.java +++ b/src/main/java/net/minestom/server/adventure/serializer/nbt/NbtComponentSerializer.java @@ -1,12 +1,18 @@ 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; -import org.jglrxavpok.hephaistos.nbt.NBT; -public interface NbtComponentSerializer extends ComponentSerializer { +public interface NbtComponentSerializer extends ComponentSerializer { static @NotNull NbtComponentSerializer nbt() { return NbtComponentSerializerImpl.INSTANCE; } + + @NotNull Style deserializeStyle(@NotNull BinaryTag tag); + + @NotNull CompoundBinaryTag serializeStyle(@NotNull Style style); } diff --git a/src/main/java/net/minestom/server/adventure/serializer/nbt/NbtComponentSerializerImpl.java b/src/main/java/net/minestom/server/adventure/serializer/nbt/NbtComponentSerializerImpl.java index 5920920c503..69790f92079 100644 --- a/src/main/java/net/minestom/server/adventure/serializer/nbt/NbtComponentSerializerImpl.java +++ b/src/main/java/net/minestom/server/adventure/serializer/nbt/NbtComponentSerializerImpl.java @@ -1,6 +1,7 @@ package net.minestom.server.adventure.serializer.nbt; import net.kyori.adventure.key.Key; +import net.kyori.adventure.nbt.*; import net.kyori.adventure.nbt.api.BinaryTagHolder; import net.kyori.adventure.text.*; import net.kyori.adventure.text.event.ClickEvent; @@ -12,14 +13,10 @@ import net.minestom.server.utils.validate.Check; import org.intellij.lang.annotations.Subst; import org.jetbrains.annotations.NotNull; -import org.jglrxavpok.hephaistos.nbt.NBT; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; -import org.jglrxavpok.hephaistos.nbt.NBTList; -import org.jglrxavpok.hephaistos.nbt.NBTType; -import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound; import java.util.ArrayList; import java.util.Collection; +import java.util.Set; import java.util.UUID; //todo write tests for me!! @@ -27,32 +24,38 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer { static final NbtComponentSerializer INSTANCE = new NbtComponentSerializerImpl(); @Override - public @NotNull Component deserialize(@NotNull NBT input) { + public @NotNull Component deserialize(@NotNull BinaryTag input) { return deserializeAnyComponent(input); } @Override - public @NotNull NBT serialize(@NotNull Component component) { + public @NotNull BinaryTag serialize(@NotNull Component component) { return serializeComponent(component); } // DESERIALIZATION - private @NotNull Component deserializeAnyComponent(@NotNull NBT nbt) { - if (nbt instanceof NBTCompound compound) { - return deserializeComponent(compound); - } else { - //todo raw string + list - throw new UnsupportedOperationException("Unknown NBT type: " + nbt.getClass().getName()); - } + private @NotNull Component deserializeAnyComponent(@NotNull BinaryTag nbt) { + return switch (nbt) { + case CompoundBinaryTag compound -> deserializeComponent(compound); + case StringBinaryTag string -> Component.text(string.value()); + case ListBinaryTag list -> { + var builder = Component.text(); + for (var element : list) { + builder.append(deserializeAnyComponent(element)); + } + yield builder.build(); + } + default -> throw new UnsupportedOperationException("Unknown NBT type: " + nbt.getClass().getName()); + }; } - private @NotNull Component deserializeComponent(@NotNull NBTCompound compound) { + private @NotNull Component deserializeComponent(@NotNull CompoundBinaryTag compound) { ComponentBuilder builder; - var type = compound.getString("type"); - if (type != null) { + var type = compound.get("type"); + if (type instanceof StringBinaryTag sType) { // If type is specified, use that - builder = switch (type) { + builder = switch (sType.value()) { case "text" -> deserializeTextComponent(compound); case "translatable" -> deserializeTranslatableComponent(compound); case "score" -> deserializeScoreComponent(compound); @@ -63,35 +66,50 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer { }; } else { // Try to infer the type from the fields present. - if (compound.containsKey("text")) { + Set keys = compound.keySet(); + if (keys.isEmpty()) { + return Component.empty(); + } else if (keys.contains("text")) { builder = deserializeTextComponent(compound); - } else if (compound.containsKey("translate")) { + } else if (keys.contains("translate")) { builder = deserializeTranslatableComponent(compound); - } else if (compound.containsKey("score")) { + } else if (keys.contains("score")) { builder = deserializeScoreComponent(compound); - } else if (compound.containsKey("selector")) { + } else if (keys.contains("selector")) { builder = deserializeSelectorComponent(compound); - } else if (compound.containsKey("keybind")) { + } else if (keys.contains("keybind")) { builder = deserializeKeybindComponent(compound); - } else if (compound.containsKey("nbt")) { + } else if (keys.contains("nbt")) { builder = deserializeNbtComponent(compound); + } else if (keys.contains("")) { + //todo This feels like a bug, im not sure why this is created. + builder = Component.text().content(compound.getString("")); } else throw new UnsupportedOperationException("Unable to infer component type"); } // Children var extra = compound.getList("extra"); - Check.argCondition(extra != null && !extra.getSubtagType().equals(NBTType.TAG_Compound), - "Extra field must be a list of compounds"); - if (extra != null) { + if (extra.size() > 0) { var list = new ArrayList(); for (var child : extra) list.add(deserializeAnyComponent(child)); builder.append(list); } // 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 != null) { + if (!color.isEmpty()) { var hexColor = TextColor.fromHexString(color); if (hexColor != null) { style.color(hexColor); @@ -105,57 +123,59 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer { } } @Subst("minecraft:default") var font = compound.getString("font"); - if (font != null) style.font(Key.key(font)); - var bold = compound.getByte("bold"); - if (bold != null) style.decoration(TextDecoration.BOLD, bold == 1 ? TextDecoration.State.TRUE : TextDecoration.State.FALSE); - var italic = compound.getByte("italic"); - if (italic != null) style.decoration(TextDecoration.ITALIC, italic == 1 ? TextDecoration.State.TRUE : TextDecoration.State.FALSE); - var underlined = compound.getByte("underlined"); - if (underlined != null) style.decoration(TextDecoration.UNDERLINED, underlined == 1 ? TextDecoration.State.TRUE : TextDecoration.State.FALSE); - var strikethrough = compound.getByte("strikethrough"); - if (strikethrough != null) style.decoration(TextDecoration.STRIKETHROUGH, strikethrough == 1 ? TextDecoration.State.TRUE : TextDecoration.State.FALSE); - var obfuscated = compound.getByte("obfuscated"); - if (obfuscated != null) style.decoration(TextDecoration.OBFUSCATED, obfuscated == 1 ? TextDecoration.State.TRUE : TextDecoration.State.FALSE); - builder.style(style.build()); + if (!font.isEmpty()) style.font(Key.key(font)); + BinaryTag bold = compound.get("bold"); + if (bold instanceof ByteBinaryTag b) + style.decoration(TextDecoration.BOLD, b.value() == 1 ? TextDecoration.State.TRUE : TextDecoration.State.FALSE); + BinaryTag italic = compound.get("italic"); + if (italic instanceof ByteBinaryTag b) + style.decoration(TextDecoration.ITALIC, b.value() == 1 ? TextDecoration.State.TRUE : TextDecoration.State.FALSE); + BinaryTag underlined = compound.get("underlined"); + if (underlined instanceof ByteBinaryTag b) + style.decoration(TextDecoration.UNDERLINED, b.value() == 1 ? TextDecoration.State.TRUE : TextDecoration.State.FALSE); + BinaryTag strikethrough = compound.get("strikethrough"); + if (strikethrough instanceof ByteBinaryTag b) + style.decoration(TextDecoration.STRIKETHROUGH, b.value() == 1 ? TextDecoration.State.TRUE : TextDecoration.State.FALSE); + BinaryTag obfuscated = compound.get("obfuscated"); + if (obfuscated instanceof ByteBinaryTag b) + style.decoration(TextDecoration.OBFUSCATED, b.value() == 1 ? TextDecoration.State.TRUE : TextDecoration.State.FALSE); // Interactivity var insertion = compound.getString("insertion"); - if (insertion != null) builder.insertion(insertion); + if (!insertion.isEmpty()) style.insertion(insertion); var clickEvent = compound.getCompound("clickEvent"); - if (clickEvent != null) builder.clickEvent(deserializeClickEvent(clickEvent)); + if (clickEvent.size() > 0) style.clickEvent(deserializeClickEvent(clickEvent)); var hoverEvent = compound.getCompound("hoverEvent"); - if (hoverEvent != null) builder.hoverEvent(deserializeHoverEvent(hoverEvent)); + if (hoverEvent.size() > 0) style.hoverEvent(deserializeHoverEvent(hoverEvent)); - return builder.build(); + return style.build(); } - private @NotNull ComponentBuilder deserializeTextComponent(@NotNull NBTCompound compound) { + private @NotNull ComponentBuilder deserializeTextComponent(@NotNull CompoundBinaryTag compound) { var text = compound.getString("text"); Check.notNull(text, "Text component must have a text field"); return Component.text().content(text); } - private @NotNull ComponentBuilder deserializeTranslatableComponent(@NotNull NBTCompound compound) { + private @NotNull ComponentBuilder deserializeTranslatableComponent(@NotNull CompoundBinaryTag compound) { var key = compound.getString("translate"); Check.notNull(key, "Translatable component must have a translate field"); var builder = Component.translatable().key(key); - var fallback = compound.getString("fallback"); - if (fallback != null) builder.fallback(fallback); + var fallback = compound.get("fallback"); + if (fallback instanceof StringBinaryTag s) builder.fallback(s.value()); - NBTList args = compound.getList("with"); - Check.argCondition(args != null && !args.getSubtagType().equals(NBTType.TAG_Compound), - "Translatable component with field must be a list of compounds"); - if (args != null) { + ListBinaryTag args = compound.getList("with", BinaryTagTypes.COMPOUND); + if (args.size() > 0) { var list = new ArrayList(); - for (var arg : args) list.add(deserializeComponent(arg)); + for (var arg : args) list.add(deserializeComponent((CompoundBinaryTag) arg)); builder.arguments(list); } return builder; } - private @NotNull ComponentBuilder deserializeScoreComponent(@NotNull NBTCompound compound) { + private @NotNull ComponentBuilder deserializeScoreComponent(@NotNull CompoundBinaryTag compound) { var scoreCompound = compound.getCompound("score"); Check.notNull(scoreCompound, "Score component must have a score field"); var name = scoreCompound.getString("name"); @@ -165,14 +185,14 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer { var builder = Component.score().name(name).objective(objective); var value = scoreCompound.getString("value"); - if (value != null) + if (!value.isEmpty()) //noinspection deprecation builder.value(value); return builder; } - private @NotNull ComponentBuilder deserializeSelectorComponent(@NotNull NBTCompound compound) { + private @NotNull ComponentBuilder deserializeSelectorComponent(@NotNull CompoundBinaryTag compound) { var selector = compound.getString("selector"); Check.notNull(selector, "Selector component must have a selector field"); var builder = Component.selector().pattern(selector); @@ -183,17 +203,17 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer { return builder; } - private @NotNull ComponentBuilder deserializeKeybindComponent(@NotNull NBTCompound compound) { + private @NotNull ComponentBuilder deserializeKeybindComponent(@NotNull CompoundBinaryTag compound) { var keybind = compound.getString("keybind"); Check.notNull(keybind, "Keybind component must have a keybind field"); return Component.keybind().keybind(keybind); } - private @NotNull ComponentBuilder deserializeNbtComponent(@NotNull NBTCompound compound) { + private @NotNull ComponentBuilder deserializeNbtComponent(@NotNull CompoundBinaryTag compound) { throw new UnsupportedOperationException("NBTComponent is not implemented yet"); } - private @NotNull ClickEvent deserializeClickEvent(@NotNull NBTCompound compound) { + private @NotNull ClickEvent deserializeClickEvent(@NotNull CompoundBinaryTag compound) { var actionName = compound.getString("action"); Check.notNull(actionName, "Click event must have an action field"); var action = ClickEvent.Action.NAMES.value(actionName); @@ -203,7 +223,7 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer { return ClickEvent.clickEvent(action, value); } - private @NotNull HoverEvent deserializeHoverEvent(@NotNull NBTCompound compound) { + private @NotNull HoverEvent deserializeHoverEvent(@NotNull CompoundBinaryTag compound) { var actionName = compound.getString("action"); Check.notNull(actionName, "Hover event must have an action field"); var contents = compound.getCompound("contents"); @@ -216,13 +236,12 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer { @Subst("minecraft:stick") var id = contents.getString("id"); Check.notNull(id, "Show item hover event must have an id field"); var count = contents.getInt("count"); - var countInt = count == null ? 1 : count; var tag = contents.getString("tag"); - var binaryTag = tag == null ? null : BinaryTagHolder.binaryTagHolder(tag); - return HoverEvent.showItem(Key.key(id), countInt, binaryTag); + var binaryTag = tag.isEmpty() ? null : BinaryTagHolder.binaryTagHolder(tag); + return HoverEvent.showItem(Key.key(id), count, binaryTag); } else if (action == HoverEvent.Action.SHOW_ENTITY) { var name = contents.getCompound("name"); - var nameComponent = name == null ? null : deserializeComponent(name); + var nameComponent = name.size() == 0 ? null : deserializeComponent(name); @Subst("minecraft:pig") var type = contents.getString("type"); Check.notNull(type, "Show entity hover event must have a type field"); var id = contents.getString("id"); @@ -235,36 +254,36 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer { // SERIALIZATION - private @NotNull NBT serializeComponent(@NotNull Component component) { - MutableNBTCompound compound = new MutableNBTCompound(); + private @NotNull CompoundBinaryTag serializeComponent(@NotNull Component component) { + CompoundBinaryTag.Builder compound = CompoundBinaryTag.builder(); // Base component types if (component instanceof TextComponent text) { - compound.setString("type", "text"); - compound.setString("text", text.content()); + compound.putString("type", "text"); + compound.putString("text", text.content()); } else if (component instanceof TranslatableComponent translatable) { - compound.setString("type", "translatable"); - compound.setString("translate", translatable.key()); + compound.putString("type", "translatable"); + compound.putString("translate", translatable.key()); var fallback = translatable.fallback(); - if (fallback != null) compound.setString("fallback", fallback); + if (fallback != null) compound.putString("fallback", fallback); var args = translatable.arguments(); - if (!args.isEmpty()) compound.set("with", serializeTranslationArgs(args)); + if (!args.isEmpty()) compound.put("with", serializeTranslationArgs(args)); } else if (component instanceof ScoreComponent score) { - compound.setString("type", "score"); - var scoreCompound = new MutableNBTCompound(); - scoreCompound.setString("name", score.name()); - scoreCompound.setString("objective", score.objective()); + compound.putString("type", "score"); + CompoundBinaryTag.Builder scoreCompound = CompoundBinaryTag.builder(); + scoreCompound.putString("name", score.name()); + scoreCompound.putString("objective", score.objective()); @SuppressWarnings("deprecation") var value = score.value(); - if (value != null) scoreCompound.setString("value", value); - compound.set("score", scoreCompound.toCompound()); + if (value != null) scoreCompound.putString("value", value); + compound.put("score", scoreCompound.build()); } else if (component instanceof SelectorComponent selector) { - compound.setString("type", "selector"); - compound.setString("selector", selector.pattern()); + compound.putString("type", "selector"); + compound.putString("selector", selector.pattern()); var separator = selector.separator(); - if (separator != null) compound.set("separator", serializeComponent(separator)); + if (separator != null) compound.put("separator", serializeComponent(separator)); } else if (component instanceof KeybindComponent keybind) { - compound.setString("type", "keybind"); - compound.setString("keybind", keybind.keybind()); + compound.putString("type", "keybind"); + compound.putString("keybind", keybind.keybind()); } else if (component instanceof NBTComponent nbt) { //todo throw new UnsupportedOperationException("NBTComponent is not implemented yet"); @@ -274,105 +293,107 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer { // Children if (!component.children().isEmpty()) { - var children = new ArrayList(); + ListBinaryTag.Builder children = ListBinaryTag.builder(BinaryTagTypes.COMPOUND); for (var child : component.children()) children.add(serializeComponent(child)); - compound.set("extra", new NBTList<>(NBTType.TAG_Compound, children)); + 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) { - compound.setString("color", named.toString()); + compound.putString("color", named.toString()); } else { - compound.setString("color", color.asHexString()); + compound.putString("color", color.asHexString()); } } var font = style.font(); if (font != null) - compound.setString("font", font.toString()); + compound.putString("font", font.toString()); var bold = style.decoration(TextDecoration.BOLD); if (bold != TextDecoration.State.NOT_SET) - setBool(compound, "bold", bold == TextDecoration.State.TRUE); + compound.putBoolean("bold", bold == TextDecoration.State.TRUE); var italic = style.decoration(TextDecoration.ITALIC); if (italic != TextDecoration.State.NOT_SET) - setBool(compound, "italic", italic == TextDecoration.State.TRUE); + compound.putBoolean("italic", italic == TextDecoration.State.TRUE); var underlined = style.decoration(TextDecoration.UNDERLINED); if (underlined != TextDecoration.State.NOT_SET) - setBool(compound, "underlined", underlined == TextDecoration.State.TRUE); + compound.putBoolean("underlined", underlined == TextDecoration.State.TRUE); var strikethrough = style.decoration(TextDecoration.STRIKETHROUGH); if (strikethrough != TextDecoration.State.NOT_SET) - setBool(compound, "strikethrough", strikethrough == TextDecoration.State.TRUE); + compound.putBoolean("strikethrough", strikethrough == TextDecoration.State.TRUE); var obfuscated = style.decoration(TextDecoration.OBFUSCATED); if (obfuscated != TextDecoration.State.NOT_SET) - setBool(compound, "obfuscated", obfuscated == TextDecoration.State.TRUE); + compound.putBoolean("obfuscated", obfuscated == TextDecoration.State.TRUE); - // Interactivity - var insertion = component.insertion(); - if (insertion != null) compound.setString("insertion", insertion); - var clickEvent = component.clickEvent(); - if (clickEvent != null) compound.set("clickEvent", serializeClickEvent(clickEvent)); - var hoverEvent = component.hoverEvent(); - if (hoverEvent != null) compound.set("hoverEvent", serializeHoverEvent(hoverEvent)); - - return compound.toCompound(); + var insertion = style.insertion(); + if (insertion != null) compound.putString("insertion", insertion); + var clickEvent = style.clickEvent(); + if (clickEvent != null) compound.put("clickEvent", serializeClickEvent(clickEvent)); + var hoverEvent = style.hoverEvent(); + if (hoverEvent != null) compound.put("hoverEvent", serializeHoverEvent(hoverEvent)); + + return compound.build(); } - private @NotNull NBT serializeTranslationArgs(@NotNull Collection args) { - var list = new ArrayList(); + private @NotNull BinaryTag serializeTranslationArgs(@NotNull Collection args) { + ListBinaryTag.Builder argList = ListBinaryTag.builder(BinaryTagTypes.COMPOUND); for (var arg : args) - list.add(serializeComponent(arg.asComponent())); - return new NBTList<>(NBTType.TAG_Compound, list); + argList.add(serializeComponent(arg.asComponent())); + return argList.build(); } - private @NotNull NBT serializeClickEvent(@NotNull ClickEvent event) { - var compound = new MutableNBTCompound(); - compound.setString("action", event.action().toString()); - compound.setString("value", event.value()); - return compound.toCompound(); + private @NotNull BinaryTag serializeClickEvent(@NotNull ClickEvent event) { + return CompoundBinaryTag.builder() + .putString("action", event.action().toString()) + .putString("value", event.value()) + .build(); } @SuppressWarnings("unchecked") - private @NotNull NBT serializeHoverEvent(@NotNull HoverEvent event) { - var compound = new MutableNBTCompound(); + private @NotNull BinaryTag serializeHoverEvent(@NotNull HoverEvent event) { + CompoundBinaryTag.Builder compound = CompoundBinaryTag.builder(); //todo surely there is a better way to do this? - compound.setString("action", event.action().toString()); + compound.putString("action", event.action().toString()); if (event.action() == HoverEvent.Action.SHOW_TEXT) { var value = ((HoverEvent) event).value(); - compound.set("contents", serializeComponent(value)); + compound.put("contents", serializeComponent(value)); } else if (event.action() == HoverEvent.Action.SHOW_ITEM) { var value = ((HoverEvent) event).value(); - var itemCompound = new MutableNBTCompound(); - itemCompound.setString("id", value.item().asString()); - if (value.count() != 1) itemCompound.setInt("count", value.count()); + CompoundBinaryTag.Builder itemCompound = CompoundBinaryTag.builder(); + itemCompound.putString("id", value.item().asString()); + if (value.count() != 1) itemCompound.putInt("count", value.count()); var tag = value.nbt(); - if (tag != null) itemCompound.setString("tag", tag.string()); + if (tag != null) itemCompound.putString("tag", tag.string()); - compound.set("contents", itemCompound.toCompound()); + compound.put("contents", itemCompound.build()); } else if (event.action() == HoverEvent.Action.SHOW_ENTITY) { var value = ((HoverEvent) event).value(); - var entityCompound = new MutableNBTCompound(); + CompoundBinaryTag.Builder entityCompound = CompoundBinaryTag.builder(); var name = value.name(); - if (name != null) entityCompound.set("name", serializeComponent(name)); - entityCompound.setString("type", value.type().asString()); - entityCompound.setString("id", value.id().toString()); + if (name != null) entityCompound.put("name", serializeComponent(name)); + entityCompound.putString("type", value.type().asString()); + entityCompound.putString("id", value.id().toString()); - compound.set("contents", entityCompound.toCompound()); + compound.put("contents", entityCompound.build()); } else { throw new UnsupportedOperationException("Unknown hover event action: " + event.action()); } - return compound.toCompound(); + return compound.build(); } - private void setBool(@NotNull MutableNBTCompound compound, @NotNull String key, boolean value) { - compound.setByte(key, value ? (byte) 1 : 0); - } - - } diff --git a/src/main/java/net/minestom/server/attribute/Attribute.java b/src/main/java/net/minestom/server/attribute/Attribute.java deleted file mode 100644 index 81982b81a8d..00000000000 --- a/src/main/java/net/minestom/server/attribute/Attribute.java +++ /dev/null @@ -1,25 +0,0 @@ -package net.minestom.server.attribute; - -import net.minestom.server.registry.ProtocolObject; -import net.minestom.server.registry.Registry; -import net.minestom.server.utils.NamespaceID; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public sealed interface Attribute extends ProtocolObject permits AttributeImpl { - @Contract(pure = true) - @Nullable - Registry.AttributeEntry registry(); - - @Override - @NotNull - NamespaceID namespace(); - - float defaultValue(); - float maxValue(); - float mineValue(); - boolean clientSync(); - @NotNull - String translationKey(); -} diff --git a/src/main/java/net/minestom/server/attribute/AttributeImpl.java b/src/main/java/net/minestom/server/attribute/AttributeImpl.java deleted file mode 100644 index 791862c8479..00000000000 --- a/src/main/java/net/minestom/server/attribute/AttributeImpl.java +++ /dev/null @@ -1,64 +0,0 @@ -package net.minestom.server.attribute; - -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; - -/** - * Represents a {@link net.minestom.server.entity.LivingEntity living entity} attribute. - */ -public record AttributeImpl(Registry.AttributeEntry registry, int id) implements Attribute, ProtocolObject { - - private static final Registry.DynamicContainer CONTAINER = Registry.createDynamicContainer(Registry.Resource.ATTRIBUTES, AttributeImpl::createImpl); - - private static Attribute createImpl(String namespace, Registry.Properties properties) { - Registry.AttributeEntry attributeEntry = Registry.attribute(namespace, properties); - return new AttributeImpl(attributeEntry, attributeEntry.id()); - } - - static Collection values() { - return CONTAINER.values(); - } - - static Attribute get(@NotNull String namespace) { - return CONTAINER.get(namespace); - } - - static Attribute getSafe(@NotNull String namespace) { - return CONTAINER.getSafe(namespace); - } - - @Override - public @NotNull NamespaceID namespace() { - return this.registry.namespace(); - } - - @Override - public float defaultValue() { - return this.registry.defaultValue(); - } - - @Override - public float maxValue() { - return this.registry.maxValue(); - } - - @Override - public float mineValue() { - return this.registry.maxValue(); - } - - @Override - public boolean clientSync() { - return this.registry.clientSync(); - } - - @Override - public @NotNull String translationKey() { - return this.registry.translationKey(); - } -} diff --git a/src/main/java/net/minestom/server/attribute/AttributeInstance.java b/src/main/java/net/minestom/server/attribute/AttributeInstance.java deleted file mode 100644 index e123792f687..00000000000 --- a/src/main/java/net/minestom/server/attribute/AttributeInstance.java +++ /dev/null @@ -1,138 +0,0 @@ -package net.minestom.server.attribute; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import java.util.function.Consumer; - -/** - * Represents an instance of an attribute and its modifiers. - */ -public final class AttributeInstance { - private final Attribute attribute; - private final Map modifiers = new HashMap<>(); - private final Consumer propertyChangeListener; - private float baseValue; - private float cachedValue = 0.0f; - - public AttributeInstance(@NotNull Attribute attribute, @Nullable Consumer listener) { - this.attribute = attribute; - this.propertyChangeListener = listener; - this.baseValue = attribute.defaultValue(); - refreshCachedValue(); - } - - /** - * Gets the attribute associated to this instance. - * - * @return the associated attribute - */ - public @NotNull Attribute getAttribute() { - return attribute; - } - - /** - * The base value of this instance without modifiers - * - * @return the instance base value - * @see #setBaseValue(float) - */ - public float getBaseValue() { - return baseValue; - } - - /** - * Sets the base value of this instance. - * - * @param baseValue the new base value - * @see #getBaseValue() - */ - public void setBaseValue(float baseValue) { - if (this.baseValue != baseValue) { - this.baseValue = baseValue; - refreshCachedValue(); - } - } - - /** - * Add a modifier to this instance. - * - * @param modifier the modifier to add - */ - public void addModifier(@NotNull AttributeModifier modifier) { - if (modifiers.putIfAbsent(modifier.getId(), modifier) == null) { - refreshCachedValue(); - } - } - - /** - * Remove a modifier from this instance. - * - * @param modifier the modifier to remove - */ - public void removeModifier(@NotNull AttributeModifier modifier) { - removeModifier(modifier.getId()); - } - - /** - * Remove a modifier from this instance. - * - * @param uuid The UUID of the modifier to remove - */ - public void removeModifier(@NotNull UUID uuid) { - if (modifiers.remove(uuid) != null) { - refreshCachedValue(); - } - } - - /** - * Get the modifiers applied to this instance. - * - * @return the modifiers. - */ - @NotNull - public Collection getModifiers() { - return modifiers.values(); - } - - /** - * Gets the value of this instance calculated with modifiers applied. - * - * @return the attribute value - */ - public float getValue() { - return cachedValue; - } - - /** - * Recalculate the value of this attribute instance using the modifiers. - */ - private void refreshCachedValue() { - final Collection modifiers = getModifiers(); - float base = getBaseValue(); - - for (var modifier : modifiers.stream().filter(mod -> mod.getOperation() == AttributeOperation.ADDITION).toArray(AttributeModifier[]::new)) { - base += modifier.getAmount(); - } - - float result = base; - - for (var modifier : modifiers.stream().filter(mod -> mod.getOperation() == AttributeOperation.MULTIPLY_BASE).toArray(AttributeModifier[]::new)) { - result += (base * modifier.getAmount()); - } - for (var modifier : modifiers.stream().filter(mod -> mod.getOperation() == AttributeOperation.MULTIPLY_TOTAL).toArray(AttributeModifier[]::new)) { - result *= (1.0f + modifier.getAmount()); - } - - this.cachedValue = Math.min(result, getAttribute().maxValue()); - - // Signal entity - if (propertyChangeListener != null) { - propertyChangeListener.accept(this); - } - } -} diff --git a/src/main/java/net/minestom/server/attribute/AttributeManager.java b/src/main/java/net/minestom/server/attribute/AttributeManager.java deleted file mode 100644 index 703194cb476..00000000000 --- a/src/main/java/net/minestom/server/attribute/AttributeManager.java +++ /dev/null @@ -1,89 +0,0 @@ -package net.minestom.server.attribute; - -import net.minestom.server.utils.NamespaceID; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -public final class AttributeManager { - - private final List attributes = new ArrayList<>(); - private final Map attributesByName = new ConcurrentHashMap<>(); - private final Map idMappings = new ConcurrentHashMap<>(); - - - /** - * Adds a new Attribute. This does NOT send the new list to players. - * - * @param attribute the attribute to add - */ - public synchronized void register(Attribute attribute) { - this.attributesByName.put(attribute.namespace(), attribute); - this.idMappings.put(attribute.namespace(), attribute.registry().id()); - this.attributes.add(attribute); - } - - public void loadVanillaAttributes() { - for (Attribute attribute : AttributeImpl.values()) { - if (getByName(attribute.namespace()) == null) - register(attribute); - } - } - - /** - * Removes a attribute. This does NOT send the new list to players. - * - * @param attribute the attribute to remove - */ - public void removeBiome(@NotNull Attribute attribute) { - var id = idMappings.get(attribute.namespace()); - if (id != null) { - attributes.remove(id); - attributesByName.remove(attribute.namespace()); - idMappings.remove(attribute.namespace()); - } - } - - /** - * Returns an immutable copy of the attribute already registered. - * - * @return an immutable copy of the attributes already registered - */ - public synchronized Collection unmodifiableCollection() { - return Collections.unmodifiableCollection(attributes); - } - - /** - * Gets a attribute by its id. - * - * @param id the id of the attribute - * @return the {@link Attribute} linked to this id - */ - @Nullable - public synchronized Attribute getById(int id) { - return attributes.get(id); - } - - @Nullable - public Attribute getByName(@NotNull NamespaceID namespaceID) { - return attributesByName.get(namespaceID); - } - - @Nullable - public Attribute getByName(@NotNull String namespaceID) { - NamespaceID namespace = NamespaceID.from(namespaceID); - return getByName(namespace); - } - - /** - * Gets the id of a attribute. - *` - * @param attribute - * @return the id of the attribute, or -1 if the attribute is not registered - */ - public int getId(Attribute attribute) { - return idMappings.getOrDefault(attribute.namespace(), -1); - } -} diff --git a/src/main/java/net/minestom/server/attribute/AttributeModifier.java b/src/main/java/net/minestom/server/attribute/AttributeModifier.java deleted file mode 100644 index 143b5ef583e..00000000000 --- a/src/main/java/net/minestom/server/attribute/AttributeModifier.java +++ /dev/null @@ -1,81 +0,0 @@ -package net.minestom.server.attribute; - -import org.jetbrains.annotations.NotNull; - -import java.util.UUID; - -/** - * Represent an attribute modifier. - */ -public class AttributeModifier { - - private final double amount; - private final String name; - private final AttributeOperation operation; - private final UUID id; - - /** - * Creates a new modifier with a random id. - * - * @param name the name of this modifier - * @param amount the value of this modifier - * @param operation the operation to apply this modifier with - */ - public AttributeModifier(@NotNull String name, double amount, @NotNull AttributeOperation operation) { - this(UUID.randomUUID(), name, amount, operation); - } - - /** - * Creates a new modifier. - * - * @param id the id of this modifier - * @param name the name of this modifier - * @param amount the value of this modifier - * @param operation the operation to apply this modifier with - */ - public AttributeModifier(@NotNull UUID id, @NotNull String name, double amount, @NotNull AttributeOperation operation) { - this.id = id; - this.name = name; - this.amount = amount; - this.operation = operation; - } - - /** - * Gets the id of this modifier. - * - * @return the id of this modifier - */ - @NotNull - public UUID getId() { - return id; - } - - /** - * Gets the name of this modifier. - * - * @return the name of this modifier - */ - @NotNull - public String getName() { - return name; - } - - /** - * Gets the value of this modifier. - * - * @return the value of this modifier - */ - public double getAmount() { - return amount; - } - - /** - * Gets the operation of this modifier. - * - * @return the operation of this modifier - */ - @NotNull - public AttributeOperation getOperation() { - return operation; - } -} diff --git a/src/main/java/net/minestom/server/attribute/VanillaAttribute.java b/src/main/java/net/minestom/server/attribute/VanillaAttribute.java deleted file mode 100644 index 69868b9115b..00000000000 --- a/src/main/java/net/minestom/server/attribute/VanillaAttribute.java +++ /dev/null @@ -1,4 +0,0 @@ -package net.minestom.server.attribute; - -public final class VanillaAttribute implements Attributes { -} diff --git a/src/main/java/net/minestom/server/collision/BlockCollision.java b/src/main/java/net/minestom/server/collision/BlockCollision.java index 3d3595421fe..698c7ff72c2 100644 --- a/src/main/java/net/minestom/server/collision/BlockCollision.java +++ b/src/main/java/net/minestom/server/collision/BlockCollision.java @@ -43,7 +43,7 @@ static PhysicsResult handlePhysics(@NotNull BoundingBox boundingBox, 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()) @@ -90,7 +90,7 @@ private static PhysicsResult stepPhysics(@NotNull BoundingBox boundingBox, @NotNull Vec velocity, @NotNull Pos entityPosition, @NotNull Block.Getter getter, boolean singleCollision) { // Allocate once and update values - SweepResult finalResult = new SweepResult(1 - Vec.EPSILON, 0, 0, 0, null, null); + SweepResult finalResult = new SweepResult(1 - Vec.EPSILON, 0, 0, 0, null, 0, 0, 0); boolean foundCollisionX = false, foundCollisionY = false, foundCollisionZ = false; @@ -114,19 +114,19 @@ private static PhysicsResult stepPhysics(@NotNull BoundingBox boundingBox, if (result.collisionX()) { foundCollisionX = true; collisionShapes[0] = finalResult.collidedShape; - collidedPoints[0] = finalResult.collidedPosition; + collidedPoints[0] = new Vec(finalResult.collidedPositionX, finalResult.collidedPositionY, finalResult.collidedPositionZ); hasCollided = true; if (singleCollision) break; } else if (result.collisionZ()) { foundCollisionZ = true; collisionShapes[2] = finalResult.collidedShape; - collidedPoints[2] = finalResult.collidedPosition; + collidedPoints[2] = new Vec(finalResult.collidedPositionX, finalResult.collidedPositionY, finalResult.collidedPositionZ); hasCollided = true; if (singleCollision) break; } else if (result.collisionY()) { foundCollisionY = true; collisionShapes[1] = finalResult.collidedShape; - collidedPoints[1] = finalResult.collidedPosition; + collidedPoints[1] = new Vec(finalResult.collidedPositionX, finalResult.collidedPositionY, finalResult.collidedPositionZ); hasCollided = true; if (singleCollision) break; } @@ -158,7 +158,8 @@ private static PhysicsResult computePhysics(@NotNull BoundingBox boundingBox, @NotNull SweepResult finalResult) { // If the movement is small we don't need to run the expensive ray casting. // Positions of move less than one can have hardcoded blocks to check for every direction - if (velocity.length() < 1) { + // Diagonals are a special case which will work with fast physics + if (velocity.length() <= 1 || isDiagonal(velocity)) { fastPhysics(boundingBox, velocity, entityPosition, getter, allFaces, finalResult); } else { slowPhysics(boundingBox, velocity, entityPosition, getter, allFaces, finalResult); @@ -187,14 +188,19 @@ private static PhysicsResult computePhysics(@NotNull BoundingBox boundingBox, Vec.ZERO, null, null, false, finalResult); } + private static boolean isDiagonal(Vec velocity) { + return Math.abs(velocity.x()) == 1 && Math.abs(velocity.z()) == 1; + } + private static void slowPhysics(@NotNull BoundingBox boundingBox, @NotNull Vec velocity, Pos entityPosition, @NotNull Block.Getter getter, @NotNull Vec[] allFaces, @NotNull SweepResult finalResult) { + BlockIterator iterator = new BlockIterator(); // When large moves are done we need to ray-cast to find all blocks that could intersect with the movement for (Vec point : allFaces) { - BlockIterator iterator = new BlockIterator(Vec.fromPoint(point.add(entityPosition)), velocity, 0, velocity.length()); + iterator.reset(Vec.fromPoint(point.add(entityPosition)), velocity, 0, velocity.length(), false); int timer = -1; while (iterator.hasNext() && timer != 0) { @@ -298,7 +304,7 @@ static boolean checkBoundingBox(int blockX, int blockY, int blockZ, // don't fall out of if statement, we could end up redundantly grabbing a block, and we only need to // collision check against the current shape since the below shape isn't tall if (belowShape.relativeEnd().y() > 1) { - // we should always check both shapes, so no short-circuit here, to handle cases where the bounding box + // we should always check both shapes, so no short-circuit here, to handle properties where the bounding box // hits the current solid but misses the tall solid return belowShape.intersectBoxSwept(entityPosition, entityVelocity, belowPos, boundingBox, finalResult) | (currentCollidable && currentShape.intersectBoxSwept(entityPosition, entityVelocity, currentPos, boundingBox, finalResult)); diff --git a/src/main/java/net/minestom/server/collision/BoundingBox.java b/src/main/java/net/minestom/server/collision/BoundingBox.java index 0c714422375..5500a2290d9 100644 --- a/src/main/java/net/minestom/server/collision/BoundingBox.java +++ b/src/main/java/net/minestom/server/collision/BoundingBox.java @@ -5,7 +5,6 @@ import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.Entity; import net.minestom.server.instance.block.BlockFace; -import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -42,7 +41,6 @@ public boolean isOccluded(@NotNull Shape shape, @NotNull BlockFace face) { } @Override - @ApiStatus.Experimental public boolean intersectBox(@NotNull Point positionRelative, @NotNull BoundingBox boundingBox) { return (minX() + positionRelative.x() <= boundingBox.maxX() - Vec.EPSILON / 2 && maxX() + positionRelative.x() >= boundingBox.minX() + Vec.EPSILON / 2) && (minY() + positionRelative.y() <= boundingBox.maxY() - Vec.EPSILON / 2 && maxY() + positionRelative.y() >= boundingBox.minY() + Vec.EPSILON / 2) && @@ -50,10 +48,11 @@ public boolean intersectBox(@NotNull Point positionRelative, @NotNull BoundingBo } @Override - @ApiStatus.Experimental public boolean intersectBoxSwept(@NotNull Point rayStart, @NotNull Point rayDirection, @NotNull Point shapePos, @NotNull BoundingBox moving, @NotNull SweepResult finalResult) { if (RayUtils.BoundingBoxIntersectionCheck(moving, rayStart, rayDirection, this, shapePos, finalResult) ) { - finalResult.collidedPosition = rayStart.add(rayDirection.mul(finalResult.res)); + finalResult.collidedPositionX = rayStart.x() + rayDirection.x() * finalResult.res; + finalResult.collidedPositionY = rayStart.y() + rayDirection.y() * finalResult.res; + finalResult.collidedPositionZ = rayStart.z() + rayDirection.z() * finalResult.res; finalResult.collidedShape = this; return true; } @@ -61,7 +60,6 @@ public boolean intersectBoxSwept(@NotNull Point rayStart, @NotNull Point rayDire return false; } - @ApiStatus.Experimental public boolean boundingBoxRayIntersectionCheck(Vec start, Vec direction, Pos position) { return RayUtils.BoundingBoxRayIntersectionCheck(start, direction, this, position); } @@ -167,58 +165,106 @@ public enum AxisMask { NONE } - public Iterator getBlocks(Point point) { + public PointIterator getBlocks(Point point) { return new PointIterator(this, point, AxisMask.NONE, 0); } - public Iterator getBlocks(Point point, AxisMask axisMask, double axis) { + public PointIterator getBlocks(Point point, AxisMask axisMask, double axis) { return new PointIterator(this, point, axisMask, axis); } - static class PointIterator implements Iterator { - private final double sx, sy, sz; + public static class MutablePoint { + double x, y, z; + + public void set(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + + public double x() { + return x; + } + + public double y() { + return y; + } + + public double z() { + return z; + } + + public int blockX() { + return (int) Math.floor(x); + } + + public int blockY() { + return (int) Math.floor(y); + } + + public int blockZ() { + return (int) Math.floor(z); + } + } + + public static class PointIterator implements Iterator { + private double sx, sy, sz; double x, y, z; private double minX, minY, minZ, maxX, maxY, maxZ; + private final MutablePoint point = new MutablePoint(); + public PointIterator() {} public PointIterator(BoundingBox boundingBox, Point p, AxisMask axisMask, double axis) { - minX = (int) Math.floor(boundingBox.minX() + p.x()); - minY = (int) Math.floor(boundingBox.minY() + p.y()); - minZ = (int) Math.floor(boundingBox.minZ() + p.z()); - maxX = (int) Math.floor(boundingBox.maxX() + p.x()); - maxY = (int) Math.floor(boundingBox.maxY() + p.y()); - maxZ = (int) Math.floor(boundingBox.maxZ() + p.z()); + reset(boundingBox, p, axisMask, axis); + } + + public void reset(BoundingBox boundingBox, double pointX, double pointY, double pointZ, AxisMask axisMask, int axis) { + minX = (int) Math.floor(boundingBox.minX() + pointX); + minY = (int) Math.floor(boundingBox.minY() + pointY); + minZ = (int) Math.floor(boundingBox.minZ() + pointZ); + maxX = (int) Math.floor(boundingBox.maxX() + pointX); + maxY = (int) Math.floor(boundingBox.maxY() + pointY); + maxZ = (int) Math.floor(boundingBox.maxZ() + pointZ); x = minX; y = minY; z = minZ; - sx = boundingBox.minX() + p.x() - minX; - sy = boundingBox.minY() + p.y() - minY; - sz = boundingBox.minZ() + p.z() - minZ; + sx = boundingBox.minX() + pointX - minX; + sy = boundingBox.minY() + pointY - minY; + sz = boundingBox.minZ() + pointZ - minZ; if (axisMask == AxisMask.X) { - x = axis + p.x(); + x = axis + pointX; minX = x; maxX = x; } else if (axisMask == AxisMask.Y) { - y = axis + p.y(); + y = axis + pointY; minY = y; maxY = y; } else if (axisMask == AxisMask.Z) { - z = axis + p.z(); + z = axis + pointZ; minZ = z; maxZ = z; } } + public void reset(BoundingBox boundingBox, Point p, AxisMask axisMask, double axis) { + reset(boundingBox, p.x(), p.y(), p.z(), axisMask, (int) axis); + } + + public void reset(BoundingBox boundingBox, double x, double y, double z, AxisMask axisMask, double axis) { + reset(boundingBox, x, y, z, axisMask, (int) axis); + } + @Override public boolean hasNext() { return x <= maxX && y <= maxY && z <= maxZ; } @Override - public Point next() { - var res = new Vec(x + sx, y + sy, z + sz); + public MutablePoint next() { + point.set(x + sx, y + sy, z + sz); x++; if (x > maxX) { @@ -229,7 +275,7 @@ public Point next() { z++; } } - return res; + return point; } } diff --git a/src/main/java/net/minestom/server/collision/CollisionUtils.java b/src/main/java/net/minestom/server/collision/CollisionUtils.java index 00533def455..61e2c62cf98 100644 --- a/src/main/java/net/minestom/server/collision/CollisionUtils.java +++ b/src/main/java/net/minestom/server/collision/CollisionUtils.java @@ -17,7 +17,6 @@ import java.util.function.Function; @ApiStatus.Internal -@ApiStatus.Experimental public final class CollisionUtils { /** @@ -162,23 +161,17 @@ public static Entity canPlaceBlockAt(Instance instance, Point blockPos, Block b) * @param newPosition the future target position * @return the position with the world border collision applied (can be {@code newPosition} if not changed) */ - public static @NotNull Pos applyWorldBorder(@NotNull WorldBorder worldBorder, - @NotNull Pos currentPosition, @NotNull Pos newPosition) { - final WorldBorder.CollisionAxis collisionAxis = worldBorder.getCollisionAxis(newPosition); - return switch (collisionAxis) { - case NONE -> - // Apply velocity + gravity - newPosition; - case BOTH -> - // Apply Y velocity/gravity - new Pos(currentPosition.x(), newPosition.y(), currentPosition.z()); - case X -> - // Apply Y/Z velocity/gravity - new Pos(currentPosition.x(), newPosition.y(), newPosition.z()); - case Z -> - // Apply X/Y velocity/gravity - new Pos(newPosition.x(), newPosition.y(), currentPosition.z()); - }; + public static @NotNull Pos applyWorldBorder(@NotNull WorldBorder worldBorder, @NotNull Pos currentPosition, @NotNull Pos newPosition) { + double radius = worldBorder.diameter() / 2; + // If there is a collision on a given axis prevent the entity + // from moving forward by supplying their previous position's value + boolean xCollision = newPosition.x() > worldBorder.centerX() + radius || newPosition.x() < worldBorder.centerX() - radius; + boolean zCollision = newPosition.z() > worldBorder.centerZ() + radius || newPosition.z() < worldBorder.centerZ() - radius; + if (xCollision || zCollision) { + return newPosition.withCoord(xCollision ? currentPosition.x() : newPosition.x(), newPosition.y(), + zCollision ? currentPosition.z() : newPosition.z()); + } + return newPosition; } public static Shape parseBlockShape(String collision, String occlusion, Registry.BlockEntry blockEntry) { diff --git a/src/main/java/net/minestom/server/collision/EntityCollision.java b/src/main/java/net/minestom/server/collision/EntityCollision.java index 7391e1dc03b..dff967b2a8f 100644 --- a/src/main/java/net/minestom/server/collision/EntityCollision.java +++ b/src/main/java/net/minestom/server/collision/EntityCollision.java @@ -12,7 +12,7 @@ public static PhysicsResult checkCollision(Instance instance, BoundingBox boundi double minimumRes = res != null ? res.res().res : Double.MAX_VALUE; if (instance == null) return null; - SweepResult sweepResult = new SweepResult(minimumRes, 0, 0, 0, null, null); + SweepResult sweepResult = new SweepResult(minimumRes, 0, 0, 0, null, 0, 0, 0); double closestDistance = minimumRes; Entity closestEntity = null; diff --git a/src/main/java/net/minestom/server/collision/RayUtils.java b/src/main/java/net/minestom/server/collision/RayUtils.java index 3ea648c361f..d766f3082b3 100644 --- a/src/main/java/net/minestom/server/collision/RayUtils.java +++ b/src/main/java/net/minestom/server/collision/RayUtils.java @@ -174,6 +174,6 @@ private static double epsilon(double value) { } public static boolean BoundingBoxRayIntersectionCheck(Vec start, Vec direction, BoundingBox boundingBox, Pos position) { - return BoundingBoxIntersectionCheck(BoundingBox.ZERO, start, direction, boundingBox, position, new SweepResult(Double.MAX_VALUE, 0, 0, 0, null, null)); + return BoundingBoxIntersectionCheck(BoundingBox.ZERO, start, direction, boundingBox, position, new SweepResult(Double.MAX_VALUE, 0, 0, 0, null, 0, 0, 0)); } } diff --git a/src/main/java/net/minestom/server/collision/Shape.java b/src/main/java/net/minestom/server/collision/Shape.java index c6238de8310..dfb2625bc6d 100644 --- a/src/main/java/net/minestom/server/collision/Shape.java +++ b/src/main/java/net/minestom/server/collision/Shape.java @@ -3,10 +3,8 @@ import net.minestom.server.coordinate.Point; import net.minestom.server.entity.Entity; import net.minestom.server.instance.block.BlockFace; -import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; -@ApiStatus.Experimental public interface Shape { boolean isOccluded(@NotNull Shape shape, @NotNull BlockFace face); @@ -47,7 +45,6 @@ boolean intersectBoxSwept(@NotNull Point rayStart, @NotNull Point rayDirection, * @param entity the entity to check the bounding box * @return true if this bounding box intersects with the entity, false otherwise */ - @ApiStatus.Experimental default boolean intersectEntity(@NotNull Point src, @NotNull Entity entity) { return intersectBox(src.sub(entity.getPosition()), entity.getBoundingBox()); } diff --git a/src/main/java/net/minestom/server/collision/ShapeImpl.java b/src/main/java/net/minestom/server/collision/ShapeImpl.java index ac0e83b0278..e528797fa4f 100644 --- a/src/main/java/net/minestom/server/collision/ShapeImpl.java +++ b/src/main/java/net/minestom/server/collision/ShapeImpl.java @@ -8,6 +8,7 @@ import net.minestom.server.instance.block.BlockFace; import net.minestom.server.registry.Registry; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Unmodifiable; import java.util.ArrayList; import java.util.List; @@ -16,11 +17,11 @@ public final class ShapeImpl implements Shape { private static final Pattern PATTERN = Pattern.compile("\\d.\\d+", Pattern.MULTILINE); - private final BoundingBox[] collisionBoundingBoxes; + private final List collisionBoundingBoxes; private final Point relativeStart, relativeEnd; private final byte fullFaces; - private final BoundingBox[] occlusionBoundingBoxes; + private final List occlusionBoundingBoxes; private final byte blockOcclusion; private final byte airOcclusion; @@ -28,12 +29,12 @@ public final class ShapeImpl implements Shape { private Block block; private ShapeImpl(BoundingBox[] boundingBoxes, BoundingBox[] occlusionBoundingBoxes, Registry.BlockEntry blockEntry) { - this.collisionBoundingBoxes = boundingBoxes; - this.occlusionBoundingBoxes = occlusionBoundingBoxes; + this.collisionBoundingBoxes = List.of(boundingBoxes); + this.occlusionBoundingBoxes = List.of(occlusionBoundingBoxes); this.blockEntry = blockEntry; // Find bounds of collision - if (collisionBoundingBoxes.length > 0) { + if (!collisionBoundingBoxes.isEmpty()) { double minX = 1, minY = 1, minZ = 1; double maxX = 0, maxY = 0, maxZ = 0; for (BoundingBox blockSection : collisionBoundingBoxes) { @@ -63,7 +64,7 @@ private ShapeImpl(BoundingBox[] boundingBoxes, BoundingBox[] occlusionBoundingBo byte airFaces = 0; byte fullFaces = 0; for (BlockFace f : BlockFace.values()) { - final byte res = isFaceCovered(computeOcclusionSet(f, occlusionBoundingBoxes)); + final byte res = isFaceCovered(computeOcclusionSet(f, this.occlusionBoundingBoxes)); airFaces |= ((res == 0) ? 0b1 : 0b0) << (byte) f.ordinal(); fullFaces |= ((res == 2) ? 0b1 : 0b0) << (byte) f.ordinal(); } @@ -181,7 +182,10 @@ public boolean intersectBoxSwept(@NotNull Point rayStart, @NotNull Point rayDire for (BoundingBox blockSection : collisionBoundingBoxes) { // Update final result if the temp result collision is sooner than the current final result if (RayUtils.BoundingBoxIntersectionCheck(moving, rayStart, rayDirection, blockSection, shapePos, finalResult)) { - finalResult.collidedPosition = rayStart.add(rayDirection.mul(finalResult.res)); + finalResult.collidedPositionX = rayStart.x() + rayDirection.x() * finalResult.res; + finalResult.collidedPositionY = rayStart.y() + rayDirection.y() * finalResult.res; + finalResult.collidedPositionZ = rayStart.z() + rayDirection.z() * finalResult.res; + finalResult.collidedShape = this; hitBlock = true; } @@ -195,7 +199,26 @@ public Block block() { return block; } - private static @NotNull List computeOcclusionSet(BlockFace face, BoundingBox[] boundingBoxes) { + /** + * Gets the collision bounding boxes for this block. There will be more than one bounds for more complex shapes e.g. + * stairs. + * + * @return the collision bounding boxes for this block + */ + public @NotNull @Unmodifiable List collisionBoundingBoxes() { + return collisionBoundingBoxes; + } + + /** + * Gets the occlusion bounding boxes for this block. + * + * @return the occlusion bounding boxes for this block + */ + public @NotNull @Unmodifiable List occlusionBoundingBoxes() { + return occlusionBoundingBoxes; + } + + private static @NotNull List computeOcclusionSet(BlockFace face, List boundingBoxes) { List rSet = new ArrayList<>(); for (BoundingBox boundingBox : boundingBoxes) { switch (face) { diff --git a/src/main/java/net/minestom/server/collision/SweepResult.java b/src/main/java/net/minestom/server/collision/SweepResult.java index 56060c55c89..348e41238ff 100644 --- a/src/main/java/net/minestom/server/collision/SweepResult.java +++ b/src/main/java/net/minestom/server/collision/SweepResult.java @@ -1,15 +1,11 @@ package net.minestom.server.collision; -import net.minestom.server.coordinate.Point; -import net.minestom.server.coordinate.Pos; - public final class SweepResult { - public static SweepResult NO_COLLISION = new SweepResult(Double.MAX_VALUE, 0, 0, 0, null, Pos.ZERO); + public static SweepResult NO_COLLISION = new SweepResult(Double.MAX_VALUE, 0, 0, 0, null, 0, 0, 0); double res; double normalX, normalY, normalZ; - Point collidedPosition; - Point collidedPos; + double collidedPositionX, collidedPositionY, collidedPositionZ; Shape collidedShape; /** @@ -20,12 +16,14 @@ public final class SweepResult { * @param normalY -1 if intersected on bottom, 1 if intersected on top * @param normalZ -1 if intersected on front, 1 if intersected on back */ - public SweepResult(double res, double normalX, double normalY, double normalZ, Shape collidedShape, Point collidedPos) { + public SweepResult(double res, double normalX, double normalY, double normalZ, Shape collidedShape, double collidedPosX, double collidedPosY, double collidedPosZ) { this.res = res; this.normalX = normalX; this.normalY = normalY; this.normalZ = normalZ; this.collidedShape = collidedShape; - this.collidedPos = collidedPos; + this.collidedPositionX = collidedPosX; + this.collidedPositionY = collidedPosY; + this.collidedPositionZ = collidedPosZ; } } diff --git a/src/main/java/net/minestom/server/color/AlphaColor.java b/src/main/java/net/minestom/server/color/AlphaColor.java new file mode 100644 index 00000000000..10138425ac6 --- /dev/null +++ b/src/main/java/net/minestom/server/color/AlphaColor.java @@ -0,0 +1,115 @@ +package net.minestom.server.color; + +import net.kyori.adventure.util.RGBLike; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.MathUtils; +import net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * A general purpose class for representing colors. + *

+ * Colors must be in the range of 0-255. + */ +public final class AlphaColor extends Color { + private static final int BIT_MASK = 0xff; + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type() { + @Override + public void write(@NotNull NetworkBuffer buffer, AlphaColor value) { + buffer.write(NetworkBuffer.INT, value.asARGB()); + } + + @Override + public AlphaColor read(@NotNull NetworkBuffer buffer) { + return new AlphaColor(buffer.read(NetworkBuffer.INT)); + } + }; + private final int alpha; + + public AlphaColor(int alpha, int red, int green, int blue) { + super(red, green, blue); + Check.argCondition(!MathUtils.isBetween(alpha, 0, 255), "Alpha is not between 0-255: {0}", alpha); + this.alpha = alpha; + } + + /** + * Creates an alpha color from an integer. This is done by reading each color component + * from the lowest order 32 bits of the integer, and creating a color from those + * components. + * + * @param argb the integer + */ + public AlphaColor(int argb) { + this((argb >> 24) & BIT_MASK, (argb >> 16) & BIT_MASK, (argb >> 8) & BIT_MASK, argb & BIT_MASK); + } + + /** + * Creates a color from an RGB-like color. + * + * @param rgbLike the color + */ + public AlphaColor(int alpha, @NotNull RGBLike rgbLike) { + this(alpha, rgbLike.red(), rgbLike.green(), rgbLike.blue()); + } + + @Override + public @NotNull AlphaColor withRed(int red) { + return new AlphaColor(alpha(), red, green(), blue()); + } + + @Override + public @NotNull AlphaColor withGreen(int green) { + return new AlphaColor(alpha(), red(), green, blue()); + } + + @Override + public @NotNull AlphaColor withBlue(int blue) { + return new AlphaColor(alpha(), red(), green(), blue); + } + + public @NotNull AlphaColor withAlpha(int alpha) { + return new AlphaColor(alpha, red(), green(), blue()); + } + + /** + * Gets the color as an RGB integer. + * + * @return An integer representation of this color, as 0xRRGGBB + */ + public int asARGB() { + return (alpha << 24) + asRGB(); + } + + public int alpha() { + return alpha; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (AlphaColor) obj; + return this.alpha == that.alpha && + red() == that.red() && + this.green() == that.green() && + this.blue() == that.blue(); + } + + @Override + public int hashCode() { + return Objects.hash(alpha, red(), green(), blue()); + } + + @Override + public String toString() { + return "AlphaColor[" + + "alpha=" + alpha + ", " + + "red=" + red() + ", " + + "green=" + green() + ", " + + "blue=" + blue() + ']'; + } + +} diff --git a/src/main/java/net/minestom/server/color/Color.java b/src/main/java/net/minestom/server/color/Color.java index cb756a5396b..a825be1ae79 100644 --- a/src/main/java/net/minestom/server/color/Color.java +++ b/src/main/java/net/minestom/server/color/Color.java @@ -1,22 +1,46 @@ package net.minestom.server.color; import net.kyori.adventure.util.RGBLike; +import net.minestom.server.network.NetworkBuffer; import net.minestom.server.utils.MathUtils; +import net.minestom.server.utils.nbt.BinaryTagSerializer; import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.NotNull; +import java.util.Objects; + /** * A general purpose class for representing colors. *

* Colors must be in the range of 0-255. */ -public record Color(int red, int green, int blue) implements RGBLike { +public class Color implements RGBLike { private static final int BIT_MASK = 0xff; - public Color { + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type() { + @Override + public void write(@NotNull NetworkBuffer buffer, RGBLike value) { + buffer.write(NetworkBuffer.INT, Color.fromRGBLike(value).asRGB()); + } + + @Override + public RGBLike read(@NotNull NetworkBuffer buffer) { + return new Color(buffer.read(NetworkBuffer.INT)); + } + }; + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.INT + .map(Color::new, color -> Color.fromRGBLike(color).asRGB()); + private final int red; + private final int green; + private final int blue; + + public Color(int red, int green, int blue) { Check.argCondition(!MathUtils.isBetween(red, 0, 255), "Red is not between 0-255: {0}", red); Check.argCondition(!MathUtils.isBetween(green, 0, 255), "Green is not between 0-255: {0}", green); Check.argCondition(!MathUtils.isBetween(blue, 0, 255), "Blue is not between 0-255: {0}", blue); + this.red = red; + this.green = green; + this.blue = blue; } /** @@ -39,6 +63,11 @@ public Color(@NotNull RGBLike rgbLike) { this(rgbLike.red(), rgbLike.green(), rgbLike.blue()); } + public static @NotNull Color fromRGBLike(@NotNull RGBLike rgbLike) { + if (rgbLike instanceof Color color) return color; + return new Color(rgbLike.red(), rgbLike.green(), rgbLike.blue()); + } + public @NotNull Color withRed(int red) { return new Color(red, green, blue); } @@ -51,6 +80,10 @@ public Color(@NotNull RGBLike rgbLike) { return new Color(red, green, blue); } + public @NotNull AlphaColor withAlpha(int alpha) { + return new AlphaColor(alpha, red, green, blue); + } + /** * Gets the color as an RGB integer. * @@ -102,4 +135,57 @@ public int asRGB() { b = Math.round(averageBlue * gainFactor); return new Color(r, g, b); } + + @Deprecated + public int getRed() { + return this.red; + } + + @Deprecated + public int getGreen() { + return this.green; + } + + @Deprecated + public int getBlue() { + return this.blue; + } + + @Override + public int red() { + return red; + } + + @Override + public int green() { + return green; + } + + @Override + public int blue() { + return blue; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (Color) obj; + return this.red == that.red && + this.green == that.green && + this.blue == that.blue; + } + + @Override + public int hashCode() { + return Objects.hash(red, green, blue); + } + + @Override + public String toString() { + return "Color[" + + "red=" + red + ", " + + "green=" + green + ", " + + "blue=" + blue + ']'; + } } diff --git a/src/main/java/net/minestom/server/command/CommandParser.java b/src/main/java/net/minestom/server/command/CommandParser.java index e77145dbf0f..07505023568 100644 --- a/src/main/java/net/minestom/server/command/CommandParser.java +++ b/src/main/java/net/minestom/server/command/CommandParser.java @@ -10,7 +10,6 @@ import java.util.List; @ApiStatus.Internal -@ApiStatus.Experimental public interface CommandParser { static @NotNull CommandParser parser() { return CommandParserImpl.PARSER; diff --git a/src/main/java/net/minestom/server/command/ExecutableCommand.java b/src/main/java/net/minestom/server/command/ExecutableCommand.java index c0a487d808b..4a6457f1da1 100644 --- a/src/main/java/net/minestom/server/command/ExecutableCommand.java +++ b/src/main/java/net/minestom/server/command/ExecutableCommand.java @@ -7,7 +7,6 @@ import org.jetbrains.annotations.NotNull; @ApiStatus.Internal -@ApiStatus.Experimental public interface ExecutableCommand { @NotNull Result execute(@NotNull CommandSender sender); diff --git a/src/main/java/net/minestom/server/command/builder/arguments/Argument.java b/src/main/java/net/minestom/server/command/builder/arguments/Argument.java index a9608f9edb2..f6d705585aa 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/Argument.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/Argument.java @@ -7,8 +7,8 @@ import net.minestom.server.command.builder.arguments.minecraft.SuggestionType; import net.minestom.server.command.builder.exception.ArgumentSyntaxException; import net.minestom.server.command.builder.suggestion.SuggestionCallback; -import net.minestom.server.registry.StaticProtocolObject; import net.minestom.server.registry.Registry; +import net.minestom.server.registry.StaticProtocolObject; import net.minestom.server.utils.NamespaceID; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -94,7 +94,6 @@ public Argument(@NotNull String id) { * @return the parsed result * @throws ArgumentSyntaxException if the argument cannot be parsed due to a fault input (argument id) */ - @ApiStatus.Experimental public static @NotNull T parse(@NotNull CommandSender sender, @NotNull Argument argument) throws ArgumentSyntaxException { return argument.parse(sender, argument.getId()); } @@ -270,12 +269,10 @@ public boolean hasSuggestion() { * @param The type of output expected. * @return A new ArgumentMap that can get this complex object type. */ - @ApiStatus.Experimental public @NotNull Argument map(@NotNull Function mapper) { return new ArgumentMap<>(this, (p, i) -> mapper.apply(i)); } - @ApiStatus.Experimental public @NotNull Argument map(@NotNull BiFunction mapper) { return new ArgumentMap<>(this, mapper); } diff --git a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java index 6722e6a72c9..8a896076ef8 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java @@ -1,7 +1,6 @@ package net.minestom.server.command.builder.arguments; import net.minestom.server.command.builder.arguments.minecraft.*; -import net.minestom.server.command.builder.arguments.minecraft.registry.ArgumentEnchantment; import net.minestom.server.command.builder.arguments.minecraft.registry.ArgumentEntityType; import net.minestom.server.command.builder.arguments.minecraft.registry.ArgumentParticle; import net.minestom.server.command.builder.arguments.number.ArgumentDouble; @@ -124,13 +123,6 @@ public static ArgumentTime Time(@NotNull String id) { return new ArgumentTime(id); } - /** - * @see ArgumentEnchantment - */ - public static ArgumentEnchantment Enchantment(@NotNull String id) { - return new ArgumentEnchantment(id); - } - /** * @see ArgumentParticle */ diff --git a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentItemStack.java b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentItemStack.java index 1b684111152..b1ef8fa48ba 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentItemStack.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentItemStack.java @@ -1,16 +1,23 @@ package net.minestom.server.command.builder.arguments.minecraft; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.TagStringIOExt; +import net.minestom.server.MinecraftServer; import net.minestom.server.command.CommandSender; import net.minestom.server.command.builder.arguments.Argument; import net.minestom.server.command.builder.exception.ArgumentSyntaxException; +import net.minestom.server.component.DataComponent; +import net.minestom.server.component.DataComponentMap; +import net.minestom.server.item.ItemComponent; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; +import net.minestom.server.item.component.CustomData; +import net.minestom.server.utils.NamespaceID; +import net.minestom.server.utils.nbt.BinaryTagSerializer; import org.jetbrains.annotations.NotNull; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; -import org.jglrxavpok.hephaistos.nbt.NBTException; -import org.jglrxavpok.hephaistos.parser.SNBTParser; -import java.io.StringReader; +import java.io.IOException; /** * Argument which can be used to retrieve an {@link ItemStack} from its material and with NBT data. @@ -24,6 +31,7 @@ public class ArgumentItemStack extends Argument { public static final int NO_MATERIAL = 1; public static final int INVALID_NBT = 2; public static final int INVALID_MATERIAL = 3; + public static final int INVALID_COMPONENT = 4; public ArgumentItemStack(String id) { super(id, true); @@ -45,39 +53,110 @@ public String parser() { */ @Deprecated public static ItemStack staticParse(@NotNull String input) throws ArgumentSyntaxException { - final int nbtIndex = input.indexOf("{"); - - if (nbtIndex == 0) - throw new ArgumentSyntaxException("The item needs a material", input, NO_MATERIAL); - - if (nbtIndex == -1) { - // Only material name - final Material material = Material.fromNamespaceId(input); - if (material == null) - throw new ArgumentSyntaxException("Material is invalid", input, INVALID_MATERIAL); - return ItemStack.of(material); - } else { - // Material plus additional NBT - final String materialName = input.substring(0, nbtIndex); - final Material material = Material.fromNamespaceId(materialName); - if (material == null) - throw new ArgumentSyntaxException("Material is invalid", input, INVALID_MATERIAL); - - final String sNBT = input.substring(nbtIndex).replace("\\\"", "\""); - - NBTCompound compound; - try { - compound = (NBTCompound) new SNBTParser(new StringReader(sNBT)).parse(); - } catch (NBTException e) { - throw new ArgumentSyntaxException("Item NBT is invalid", input, INVALID_NBT); - } + var reader = new StringReader(input); - return ItemStack.fromNBT(material, compound); + final Material material = Material.fromNamespaceId(reader.readNamespaceId()); + if (material == null) + throw new ArgumentSyntaxException("Material is invalid", input, INVALID_MATERIAL); + if (!reader.hasMore()) { + return ItemStack.of(material); // Nothing else, we have our item } + + DataComponentMap.Builder components = DataComponentMap.builder(); + + // Parse the declared components + if (reader.peek() == '[') { + reader.consume('['); + do { + final NamespaceID componentId = reader.readNamespaceId(); + final DataComponent component = ItemComponent.fromNamespaceId(componentId); + if (component == null) + throw new ArgumentSyntaxException("Unknown item component", input, INVALID_COMPONENT); + + reader.consume('='); + + final BinaryTag nbt = reader.readTag(); + BinaryTagSerializer.Context context = new BinaryTagSerializer.ContextWithRegistries(MinecraftServer.process(), false); + //noinspection unchecked + components.set((DataComponent) component, component.read(context, nbt)); + + if (reader.peek() != ']') + reader.consume(','); + } while (reader.peek() != ']'); + reader.consume(']'); + } + + // Parse the NBT + if (reader.hasMore() && reader.peek() == '{') { + final BinaryTag nbt = reader.readTag(); + if (!(nbt instanceof CompoundBinaryTag compound)) + throw new ArgumentSyntaxException("Item NBT must be compound", input, INVALID_NBT); + + final CompoundBinaryTag customData = CompoundBinaryTag.builder() + .put(components.get(ItemComponent.CUSTOM_DATA, CustomData.EMPTY).nbt()) + .put(compound) + .build(); + components.set(ItemComponent.CUSTOM_DATA, new CustomData(customData)); + } + + if (reader.hasMore()) + throw new ArgumentSyntaxException("Unexpected remaining input", input, INVALID_NBT); + + return ItemStack.of(material, components.build()); } @Override public String toString() { return String.format("ItemStack<%s>", getId()); } + + private static class StringReader { + private String input; + private int index = 0; + + public StringReader(@NotNull String input) { + this.input = input; + } + + public boolean hasMore() { + return index < input.length(); + } + + public char peek() { + if (!hasMore()) { + throw new ArgumentSyntaxException("Unexpected end of input", input, INVALID_NBT); + } + + return input.charAt(index); + } + + public void consume(char c) { + char next = peek(); + if (next != c) { + throw new ArgumentSyntaxException("Expected '" + c + "', got '" + next + "'", input, INVALID_NBT); + } + index++; + } + + public @NotNull NamespaceID readNamespaceId() { + char c; + int start = index; + while (hasMore() && (c = peek()) != '{' && c != '[' && c != '=') { + index++; + } + return NamespaceID.from(input.substring(start, index)); + } + + public @NotNull BinaryTag readTag() { + try { + var result = TagStringIOExt.readTagEmbedded(input.substring(index)); + this.input = result.getValue(); + this.index = 0; + + return result.getKey(); + } catch (IOException e) { + throw new ArgumentSyntaxException("Invalid NBT", input, INVALID_NBT); + } + } + } } diff --git a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentNbtCompoundTag.java b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentNbtCompoundTag.java index 92fb5b3e91c..54633a46b3e 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentNbtCompoundTag.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentNbtCompoundTag.java @@ -1,22 +1,21 @@ package net.minestom.server.command.builder.arguments.minecraft; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.TagStringIO; import net.minestom.server.command.CommandSender; import net.minestom.server.command.builder.arguments.Argument; import net.minestom.server.command.builder.exception.ArgumentSyntaxException; import org.jetbrains.annotations.NotNull; -import org.jglrxavpok.hephaistos.nbt.NBT; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; -import org.jglrxavpok.hephaistos.nbt.NBTException; -import org.jglrxavpok.hephaistos.parser.SNBTParser; -import java.io.StringReader; +import java.io.IOException; /** - * Argument used to retrieve a {@link NBTCompound} if you need key-value data. + * Argument used to retrieve a {@link CompoundBinaryTag} if you need key-value data. *

* Example: {display:{Name:"{\"text\":\"Sword of Power\"}"}} */ -public class ArgumentNbtCompoundTag extends Argument { +public class ArgumentNbtCompoundTag extends Argument { public static final int INVALID_NBT = 1; @@ -26,15 +25,15 @@ public ArgumentNbtCompoundTag(String id) { @NotNull @Override - public NBTCompound parse(@NotNull CommandSender sender, @NotNull String input) throws ArgumentSyntaxException { + public CompoundBinaryTag parse(@NotNull CommandSender sender, @NotNull String input) throws ArgumentSyntaxException { try { - NBT nbt = new SNBTParser(new StringReader(input)).parse(); + BinaryTag nbt = TagStringIO.get().asCompound(input); - if (!(nbt instanceof NBTCompound)) + if (!(nbt instanceof CompoundBinaryTag compound)) throw new ArgumentSyntaxException("NBTCompound is invalid", input, INVALID_NBT); - return (NBTCompound) nbt; - } catch (NBTException e) { + return compound; + } catch (IOException e) { throw new ArgumentSyntaxException("NBTCompound is invalid", input, INVALID_NBT); } } diff --git a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentNbtTag.java b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentNbtTag.java index 86dac51ef75..a51e39ff6a7 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentNbtTag.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentNbtTag.java @@ -1,23 +1,22 @@ package net.minestom.server.command.builder.arguments.minecraft; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.TagStringIOExt; import net.minestom.server.command.CommandSender; import net.minestom.server.command.builder.arguments.Argument; import net.minestom.server.command.builder.exception.ArgumentSyntaxException; import org.jetbrains.annotations.NotNull; -import org.jglrxavpok.hephaistos.nbt.NBT; -import org.jglrxavpok.hephaistos.nbt.NBTException; -import org.jglrxavpok.hephaistos.parser.SNBTParser; -import java.io.StringReader; +import java.io.IOException; /** - * Argument used to retrieve a {@link NBT} based object, can be any kind of tag like - * {@link org.jglrxavpok.hephaistos.nbt.NBTCompound}, {@link org.jglrxavpok.hephaistos.nbt.NBTList}, - * {@link org.jglrxavpok.hephaistos.nbt.NBTInt}, etc... + * Argument used to retrieve a {@link BinaryTag} based object, can be any kind of tag like + * {@link net.kyori.adventure.nbt.CompoundBinaryTag}, {@link net.kyori.adventure.nbt.ListBinaryTag}, + * {@link net.kyori.adventure.nbt.IntBinaryTag}, etc... *

* Example: {display:{Name:"{\"text\":\"Sword of Power\"}"}} or [{display:{Name:"{\"text\":\"Sword of Power\"}"}}] */ -public class ArgumentNbtTag extends Argument { +public class ArgumentNbtTag extends Argument { public static final int INVALID_NBT = 1; @@ -27,10 +26,10 @@ public ArgumentNbtTag(String id) { @NotNull @Override - public NBT parse(@NotNull CommandSender sender, @NotNull String input) throws ArgumentSyntaxException { + public BinaryTag parse(@NotNull CommandSender sender, @NotNull String input) throws ArgumentSyntaxException { try { - return new SNBTParser(new StringReader(input)).parse(); - } catch (NBTException e) { + return TagStringIOExt.readTag(input); + } catch (IOException e) { throw new ArgumentSyntaxException("Invalid NBT", input, INVALID_NBT); } } diff --git a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/registry/ArgumentEnchantment.java b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/registry/ArgumentEnchantment.java deleted file mode 100644 index 0ca7404c1ae..00000000000 --- a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/registry/ArgumentEnchantment.java +++ /dev/null @@ -1,29 +0,0 @@ -package net.minestom.server.command.builder.arguments.minecraft.registry; - -import net.minestom.server.item.Enchantment; -import org.jetbrains.annotations.NotNull; - -/** - * Represents an argument giving an {@link Enchantment}. - */ -public class ArgumentEnchantment extends ArgumentRegistry { - - public ArgumentEnchantment(String id) { - super(id); - } - - @Override - public String parser() { - return "minecraft:item_enchantment"; - } - - @Override - public Enchantment getRegistry(@NotNull String value) { - return Enchantment.fromNamespaceId(value); - } - - @Override - public String toString() { - return String.format("Enchantment<%s>", getId()); - } -} diff --git a/src/main/java/net/minestom/server/command/builder/condition/Conditions.java b/src/main/java/net/minestom/server/command/builder/condition/Conditions.java index f185ed0bff6..14de416cc30 100644 --- a/src/main/java/net/minestom/server/command/builder/condition/Conditions.java +++ b/src/main/java/net/minestom/server/command/builder/condition/Conditions.java @@ -1,7 +1,5 @@ package net.minestom.server.command.builder.condition; - -import net.kyori.adventure.text.Component; import net.minestom.server.command.CommandSender; import net.minestom.server.command.ConsoleSender; import net.minestom.server.entity.Player; @@ -10,19 +8,47 @@ * Common command conditions */ public class Conditions { - public static boolean playerOnly(CommandSender sender, String commandString) { - if (!(sender instanceof Player)) { - sender.sendMessage(Component.text("The command is only available for players")); + /** + * Will only execute if all command conditions succeed. + */ + public static CommandCondition all(CommandCondition... conditions) { + return (sender, commandString) -> { + for (CommandCondition condition : conditions) { + if (!condition.canUse(sender, commandString)) { + return false; + } + } + + return true; + }; + } + + /** + * Will execute if one or more command conditions succeed. + */ + public static CommandCondition any(CommandCondition... conditions) { + return (sender, commandString) -> { + for (CommandCondition condition : conditions) { + if (condition.canUse(sender, commandString)) { + return true; + } + } + return false; - } - return true; + }; } + /** + * Will succeed if the command sender is a player. + */ + public static boolean playerOnly(CommandSender sender, String commandString) { + return sender instanceof Player; + } + + /** + * Will succeed if the command sender is the server console. + */ public static boolean consoleOnly(CommandSender sender, String commandString) { - if (!(sender instanceof ConsoleSender)) { - sender.sendMessage(Component.text("The command is only available from the console")); - return false; - } - return true; + return sender instanceof ConsoleSender; } } diff --git a/src/main/java/net/minestom/server/command/builder/parser/ArgumentParser.java b/src/main/java/net/minestom/server/command/builder/parser/ArgumentParser.java index a419ef2e14e..a05f4ebb84c 100644 --- a/src/main/java/net/minestom/server/command/builder/parser/ArgumentParser.java +++ b/src/main/java/net/minestom/server/command/builder/parser/ArgumentParser.java @@ -3,7 +3,6 @@ import net.minestom.server.command.CommandSender; import net.minestom.server.command.builder.arguments.*; import net.minestom.server.command.builder.arguments.minecraft.*; -import net.minestom.server.command.builder.arguments.minecraft.registry.ArgumentEnchantment; import net.minestom.server.command.builder.arguments.minecraft.registry.ArgumentEntityType; import net.minestom.server.command.builder.arguments.minecraft.registry.ArgumentParticle; import net.minestom.server.command.builder.arguments.number.ArgumentDouble; @@ -42,7 +41,6 @@ public class ArgumentParser { // TODO enum ARGUMENT_FUNCTION_MAP.put("color", ArgumentColor::new); ARGUMENT_FUNCTION_MAP.put("time", ArgumentTime::new); - ARGUMENT_FUNCTION_MAP.put("enchantment", ArgumentEnchantment::new); ARGUMENT_FUNCTION_MAP.put("particle", ArgumentParticle::new); ARGUMENT_FUNCTION_MAP.put("resourcelocation", ArgumentResourceLocation::new); ARGUMENT_FUNCTION_MAP.put("entitytype", ArgumentEntityType::new); diff --git a/src/main/java/net/minestom/server/component/DataComponent.java b/src/main/java/net/minestom/server/component/DataComponent.java new file mode 100644 index 00000000000..a3a05ca65d1 --- /dev/null +++ b/src/main/java/net/minestom/server/component/DataComponent.java @@ -0,0 +1,57 @@ +package net.minestom.server.component; + +import net.kyori.adventure.nbt.BinaryTag; +import net.minestom.server.item.enchant.EffectComponent; +import net.minestom.server.network.NetworkBuffer; +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.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A common type to represent all forms of component in the game. Each group of component types has its own declaration + * file (see {@link net.minestom.server.item.ItemComponent} for example). + * + * @param The value type of the component + * + * @see net.minestom.server.item.ItemComponent + * @see EffectComponent + */ +public sealed interface DataComponent extends StaticProtocolObject permits DataComponentImpl { + + /** + * Represents any type which can hold data components. Represents a finalized view of a component, that is to say + * an implementation may represent a patch on top of another Holder, however the return values of this type + * will always represent the merged view. + */ + interface Holder { + boolean has(@NotNull DataComponent component); + + @Nullable T get(@NotNull DataComponent component); + + default @NotNull T get(@NotNull DataComponent component, @NotNull T defaultValue) { + final T value = get(component); + return value != null ? value : defaultValue; + } + } + + boolean isSynced(); + boolean isSerialized(); + + @NotNull T read(@NotNull BinaryTagSerializer.Context context, @NotNull BinaryTag tag); + @NotNull BinaryTag write(@NotNull BinaryTagSerializer.Context context, @NotNull T value); + + @NotNull T read(@NotNull NetworkBuffer reader); + void write(@NotNull NetworkBuffer writer, @NotNull T value); + + @ApiStatus.Internal + static DataComponent createHeadless( + int id, @NotNull NamespaceID namespace, + @Nullable NetworkBuffer.Type network, + @Nullable BinaryTagSerializer nbt + ) { + return new DataComponentImpl<>(id, namespace, network, nbt); + } +} diff --git a/src/main/java/net/minestom/server/component/DataComponentImpl.java b/src/main/java/net/minestom/server/component/DataComponentImpl.java new file mode 100644 index 00000000000..3b333249872 --- /dev/null +++ b/src/main/java/net/minestom/server/component/DataComponentImpl.java @@ -0,0 +1,57 @@ +package net.minestom.server.component; + +import net.kyori.adventure.nbt.BinaryTag; +import net.minestom.server.network.NetworkBuffer; +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 DataComponentImpl( + int id, + @NotNull NamespaceID namespace, + @Nullable NetworkBuffer.Type network, + @Nullable BinaryTagSerializer nbt +) implements DataComponent { + + @Override + public boolean isSynced() { + return network != null; + } + + @Override + public boolean isSerialized() { + return nbt != null; + } + + @Override + public @NotNull T read(@NotNull BinaryTagSerializer.Context context, @NotNull BinaryTag tag) { + Check.notNull(nbt, "{0} cannot be deserialized from NBT", this); + return nbt.read(context, tag); + } + + @Override + public @NotNull BinaryTag write(@NotNull BinaryTagSerializer.Context context, @NotNull T value) { + Check.notNull(nbt, "{0} cannot be serialized to NBT", this); + return nbt.write(context, value); + } + + @Override + public @NotNull T read(@NotNull NetworkBuffer reader) { + Check.notNull(network, "{0} cannot be deserialized from network", this); + return network.read(reader); + } + + @Override + public void write(@NotNull NetworkBuffer writer, @NotNull T value) { + Check.notNull(network, "{0} cannot be serialized to network", this); + network.write(writer, value); + } + + @Override + public String toString() { + return name(); + } + +} diff --git a/src/main/java/net/minestom/server/component/DataComponentMap.java b/src/main/java/net/minestom/server/component/DataComponentMap.java new file mode 100644 index 00000000000..6bcdc1fddad --- /dev/null +++ b/src/main/java/net/minestom/server/component/DataComponentMap.java @@ -0,0 +1,157 @@ +package net.minestom.server.component; + +import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.Unit; +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.function.Function; +import java.util.function.IntFunction; + +/** + *

Represents any container of {@link DataComponent}s.

+ * + *

This type is capable of storing a patch of added and removed components on top of a 'prototype', or existing + * set of components. See {@link #diff(DataComponentMap, DataComponentMap)}.

+ */ +@ApiStatus.Experimental +public sealed interface DataComponentMap extends DataComponent.Holder permits DataComponentMapImpl { + @NotNull DataComponentMap EMPTY = new DataComponentMapImpl(new Int2ObjectArrayMap<>(0)); + + static @NotNull DataComponentMap.Builder builder() { + return new DataComponentMapImpl.BuilderImpl(new Int2ObjectArrayMap<>()); + } + + static @NotNull DataComponentMap.PatchBuilder patchBuilder() { + return new DataComponentMapImpl.PatchBuilderImpl(new Int2ObjectArrayMap<>()); + } + + /** + * Creates a network type for the given component type. For internal use only, get the value from the target component class. + */ + @ApiStatus.Internal + static @NotNull BinaryTagSerializer nbtType( + @NotNull IntFunction> idToType, + @NotNull Function> nameToType + ) { + return new DataComponentMapImpl.NbtType(idToType, nameToType, false); + } + + /** + * Creates a network type for the given component type. For internal use only, get the value from the target component class. + */ + @ApiStatus.Internal + static @NotNull NetworkBuffer.Type patchNetworkType(@NotNull IntFunction> idToType) { + return new DataComponentMapImpl.PatchNetworkType(idToType); + } + + /** + * Creates a network type for the given component type. For internal use only, get the value from the target component class. + */ + @ApiStatus.Internal + static @NotNull BinaryTagSerializer patchNbtType( + @NotNull IntFunction> idToType, + @NotNull Function> nameToType + ) { + return new DataComponentMapImpl.NbtType(idToType, nameToType, true); + } + + static @NotNull DataComponentMap diff(@NotNull DataComponentMap prototype, @NotNull DataComponentMap patch) { + final DataComponentMapImpl patchImpl = (DataComponentMapImpl) patch; + if (patchImpl.components().isEmpty()) return EMPTY; + + final DataComponentMapImpl protoImpl = (DataComponentMapImpl) prototype; + + final Int2ObjectArrayMap diff = new Int2ObjectArrayMap<>(patchImpl.components()); + var iter = diff.int2ObjectEntrySet().fastIterator(); + while (iter.hasNext()) { + final var entry = iter.next(); // Entry in patch + final var protoComp = protoImpl.components().get(entry.getIntKey()); // Entry in prototype + if (entry.getValue() == null) { + // If the component is removed, remove it from the diff if it is not in the prototype + if (protoImpl.components().containsKey(entry.getIntKey())) { + iter.remove(); + } + } else if (protoComp != null && protoComp.equals(entry.getValue())) { + // If the component is the same as in the prototype, remove it from the diff + iter.remove(); + } + } + + return new DataComponentMapImpl(diff); + } + + /** + * Does a 'patch'ed has against the given prototype. That is, this map is treated as the primary source, but if + * unspecified, the given prototype is used as a fallback. + * + * @param prototype The prototype to fall back to + * @param component The component to check + * @return True if the component is present (taking into account the prototype). + */ + boolean has(@NotNull DataComponentMap prototype, @NotNull DataComponent component); + + /** + * Does a 'patch'ed get against the given prototype. That is, this map is treated as the primary source, but if + * unspecified, the given prototype is used as a fallback. + * + * @param prototype The prototype to fall back to + * @param component The component to get + * @return The value of the component, or null if not present (taking into account the prototype). + * @param The type of the component + */ + @Nullable T get(@NotNull DataComponentMap prototype, @NotNull DataComponent component); + + /** + * Adds the component, overwriting any prior value if present. + * + * @return A new map with the component set to the value + */ + @NotNull DataComponentMap set(@NotNull DataComponent component, @NotNull T value); + + default @NotNull DataComponentMap set(@NotNull DataComponent component) { + return set(component, Unit.INSTANCE); + } + + /** + * Removes the component from the map (or patch). + * + * @param component The component to remove + * @return A new map with the component removed + */ + @NotNull DataComponentMap remove(@NotNull DataComponent component); + + @NotNull Builder toBuilder(); + + @NotNull PatchBuilder toPatchBuilder(); + + sealed interface Builder extends DataComponent.Holder permits DataComponentMapImpl.BuilderImpl { + + @NotNull Builder set(@NotNull DataComponent component, @NotNull T value); + + default @NotNull Builder set(@NotNull DataComponent component) { + return set(component, Unit.INSTANCE); + } + + @NotNull DataComponentMap build(); + + } + + sealed interface PatchBuilder extends DataComponent.Holder permits DataComponentMapImpl.PatchBuilderImpl { + + @NotNull PatchBuilder set(@NotNull DataComponent component, @NotNull T value); + + default @NotNull PatchBuilder set(@NotNull DataComponent component) { + return set(component, Unit.INSTANCE); + } + + @NotNull PatchBuilder remove(@NotNull DataComponent component); + + @NotNull DataComponentMap build(); + + } + +} diff --git a/src/main/java/net/minestom/server/component/DataComponentMapImpl.java b/src/main/java/net/minestom/server/component/DataComponentMapImpl.java new file mode 100644 index 00000000000..c2b72572bf1 --- /dev/null +++ b/src/main/java/net/minestom/server/component/DataComponentMapImpl.java @@ -0,0 +1,236 @@ +package net.minestom.server.component; + +import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minestom.server.network.NetworkBuffer; +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.Map; +import java.util.function.Function; +import java.util.function.IntFunction; + +/** + *

A component list, always stored as a patch of added and removed components (even if none are removed).

+ * + *

The inner map contains the value for added components, null for removed components, and no entry for unmodified components.

+ * + * @param components The component patch. + */ +record DataComponentMapImpl(@NotNull Int2ObjectMap components) implements DataComponentMap { + private static final char REMOVAL_PREFIX = '!'; + + @Override + public boolean has(@NotNull DataComponent component) { + return components.containsKey(component.id()) && components.get(component.id()) != null; + } + + @Override + public @Nullable T get(@NotNull DataComponent component) { + //noinspection unchecked + return (T) components.get(component.id()); + } + + @Override + public boolean has(@NotNull DataComponentMap prototype, @NotNull DataComponent component) { + if (components.containsKey(component.id())) { + return components.get(component.id()) != null; + } else { + return prototype.has(component); + } + } + + @Override + public @Nullable T get(@NotNull DataComponentMap prototype, @NotNull DataComponent component) { + if (components.containsKey(component.id())) { + //noinspection unchecked + return (T) components.get(component.id()); + } else { + return prototype.get(component); + } + } + + @Override + public @NotNull DataComponentMap set(@NotNull DataComponent component, @NotNull T value) { + Int2ObjectMap newComponents = new Int2ObjectArrayMap<>(components); + newComponents.put(component.id(), value); + return new DataComponentMapImpl(newComponents); + } + + @Override + public @NotNull DataComponentMap remove(@NotNull DataComponent component) { + Int2ObjectMap newComponents = new Int2ObjectArrayMap<>(components); + newComponents.put(component.id(), null); + return new DataComponentMapImpl(newComponents); + } + + @Override + public @NotNull Builder toBuilder() { + return new BuilderImpl(new Int2ObjectArrayMap<>(components)); + } + + @Override + public @NotNull PatchBuilder toPatchBuilder() { + return new PatchBuilderImpl(new Int2ObjectArrayMap<>(components)); + } + + record BuilderImpl(@NotNull Int2ObjectMap components) implements DataComponentMap.Builder { + + @Override + public boolean has(@NotNull DataComponent component) { + return components.get(component.id()) != null; + } + + @Override + public @Nullable T get(@NotNull DataComponent component) { + //noinspection unchecked + return (T) components.get(component.id()); + } + + @Override + public @NotNull Builder set(@NotNull DataComponent component, @NotNull T value) { + components.put(component.id(), value); + return this; + } + + @Override + public @NotNull DataComponentMap build() { + return new DataComponentMapImpl(new Int2ObjectArrayMap<>(components)); + } + } + + record PatchBuilderImpl(@NotNull Int2ObjectMap components) implements DataComponentMap.PatchBuilder { + + @Override + public boolean has(@NotNull DataComponent component) { + return components.get(component.id()) != null; + } + + @Override + public @Nullable T get(@NotNull DataComponent component) { + //noinspection unchecked + return (T) components.get(component.id()); + } + + @Override + public @NotNull PatchBuilder set(@NotNull DataComponent component, @NotNull T value) { + components.put(component.id(), value); + return this; + } + + @Override + public @NotNull PatchBuilder remove(@NotNull DataComponent component) { + components.put(component.id(), null); + return this; + } + + @Override + public @NotNull DataComponentMap build() { + return new DataComponentMapImpl(new Int2ObjectArrayMap<>(components)); + } + } + + record PatchNetworkType(@NotNull IntFunction> idToType) implements NetworkBuffer.Type { + @Override + public void write(@NotNull NetworkBuffer buffer, DataComponentMap value) { + final DataComponentMapImpl patch = (DataComponentMapImpl) value; + int added = 0; + for (Object o : patch.components.values()) { + if (o != null) added++; + } + + buffer.write(NetworkBuffer.VAR_INT, added); + buffer.write(NetworkBuffer.VAR_INT, patch.components.size() - added); + for (Int2ObjectMap.Entry entry : patch.components.int2ObjectEntrySet()) { + if (entry.getValue() != null) { + buffer.write(NetworkBuffer.VAR_INT, entry.getIntKey()); + //noinspection unchecked + DataComponent type = (DataComponent) this.idToType.apply(entry.getIntKey()); + assert type != null; + type.write(buffer, entry.getValue()); + } + } + for (Int2ObjectMap.Entry entry : patch.components.int2ObjectEntrySet()) { + if (entry.getValue() == null) { + buffer.write(NetworkBuffer.VAR_INT, entry.getIntKey()); + } + } + } + + @Override + public DataComponentMap read(@NotNull NetworkBuffer buffer) { + int added = buffer.read(NetworkBuffer.VAR_INT); + int removed = buffer.read(NetworkBuffer.VAR_INT); + Check.stateCondition(added + removed > 256, "Item component patch too large: {0}", added + removed); + Int2ObjectMap patch = new Int2ObjectArrayMap<>(added + removed); + for (int i = 0; i < added; i++) { + int id = buffer.read(NetworkBuffer.VAR_INT); + //noinspection unchecked + DataComponent type = (DataComponent) this.idToType.apply(id); + Check.notNull(type, "Unknown component: {0}", id); + patch.put(type.id(), type.read(buffer)); + } + for (int i = 0; i < removed; i++) { + int id = buffer.read(NetworkBuffer.VAR_INT); + patch.put(id, null); + } + return new DataComponentMapImpl(patch); + } + } + record NbtType( + @NotNull IntFunction> idToType, + @NotNull Function> nameToType, + boolean isPatch + ) implements BinaryTagSerializer { + + @Override + public @NotNull BinaryTag write(@NotNull Context context, @NotNull DataComponentMap value) { + final DataComponentMapImpl patch = (DataComponentMapImpl) value; + if (patch.components.isEmpty()) return CompoundBinaryTag.empty(); + CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder(); + for (Int2ObjectMap.Entry entry : patch.components.int2ObjectEntrySet()) { + //noinspection unchecked + DataComponent type = (DataComponent) this.idToType.apply(entry.getIntKey()); + Check.notNull(type, "Unknown item component id: {0}", entry.getIntKey()); + if (entry.getValue() == null) { + if (isPatch) builder.put(REMOVAL_PREFIX + type.name(), CompoundBinaryTag.empty()); + // Removing a component in an absolute (non-patch) builder is a noop because it is not yet present. + } else { + builder.put(type.name(), type.write(context, entry.getValue())); + } + } + return builder.build(); + } + + @Override + public @NotNull DataComponentMap read(@NotNull Context context, @NotNull BinaryTag anyTag) { + if (!(anyTag instanceof CompoundBinaryTag tag)) + throw new IllegalArgumentException("Component patch must be a compound tag, was: " + anyTag.type()); + if (tag.size() == 0) return EMPTY; + Int2ObjectMap patch = new Int2ObjectArrayMap<>(tag.size()); + for (Map.Entry entry : tag) { + String key = entry.getKey(); + boolean remove = false; + if (!key.isEmpty() && key.charAt(0) == REMOVAL_PREFIX) { + key = key.substring(1); + remove = true; + } + DataComponent type = this.nameToType.apply(key); + Check.notNull(type, "Unknown item component: {0}", key); + if (remove) { + if (isPatch) patch.put(type.id(), null); + // Removing a component in an absolute (non-patch) builder is a noop because it is not yet present. + } else { + Object value = type.read(context, entry.getValue()); + patch.put(type.id(), value); + } + } + return new DataComponentMapImpl(patch); + } + } + +} diff --git a/src/main/java/net/minestom/server/condition/DataPredicate.java b/src/main/java/net/minestom/server/condition/DataPredicate.java new file mode 100644 index 00000000000..90b7d36c118 --- /dev/null +++ b/src/main/java/net/minestom/server/condition/DataPredicate.java @@ -0,0 +1,23 @@ +package net.minestom.server.condition; + +import net.kyori.adventure.nbt.BinaryTag; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +public interface DataPredicate { + record Noop(BinaryTag content) implements DataPredicate { + + } + + public static final BinaryTagSerializer NBT_TYPE = new BinaryTagSerializer() { + @Override + public @NotNull BinaryTag write(@NotNull DataPredicate value) { + return ((Noop) value).content; + } + + @Override + public @NotNull DataPredicate read(@NotNull BinaryTag tag) { + return new Noop(tag); + } + }; +} diff --git a/src/main/java/net/minestom/server/coordinate/BlockVec.java b/src/main/java/net/minestom/server/coordinate/BlockVec.java index c168bacb622..f8df8214b4b 100644 --- a/src/main/java/net/minestom/server/coordinate/BlockVec.java +++ b/src/main/java/net/minestom/server/coordinate/BlockVec.java @@ -150,7 +150,7 @@ public BlockVec(@NotNull Point point) { @Override public @NotNull Point div(double x, double y, double z) { - return div(this.x / x, this.y / y, this.z / z); + return new Vec(this.x / x, this.y / y, this.z / z); } @Override diff --git a/src/main/java/net/minestom/server/coordinate/Point.java b/src/main/java/net/minestom/server/coordinate/Point.java index 0d792c9b1f0..cf8b378f18a 100644 --- a/src/main/java/net/minestom/server/coordinate/Point.java +++ b/src/main/java/net/minestom/server/coordinate/Point.java @@ -3,7 +3,6 @@ import net.minestom.server.instance.block.BlockFace; import net.minestom.server.utils.MathUtils; import net.minestom.server.utils.chunk.ChunkUtils; -import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -74,7 +73,6 @@ default int chunkX() { } @Contract(pure = true) - @ApiStatus.Experimental default int section() { return ChunkUtils.getChunkCoordinate(y()); } diff --git a/src/main/java/net/minestom/server/coordinate/Vec.java b/src/main/java/net/minestom/server/coordinate/Vec.java index ae34febd1ca..d3ba7ce6b89 100644 --- a/src/main/java/net/minestom/server/coordinate/Vec.java +++ b/src/main/java/net/minestom/server/coordinate/Vec.java @@ -487,6 +487,12 @@ public interface Operator { Math.floor(z) ); + Operator SIGNUM = (x, y, z) -> new Vec( + Math.signum(x), + Math.signum(y), + Math.signum(z) + ); + @NotNull Vec apply(double x, double y, double z); } diff --git a/src/main/java/net/minestom/server/entity/Entity.java b/src/main/java/net/minestom/server/entity/Entity.java index eb3c5c5216a..bce75c63dfd 100644 --- a/src/main/java/net/minestom/server/entity/Entity.java +++ b/src/main/java/net/minestom/server/entity/Entity.java @@ -41,6 +41,7 @@ import net.minestom.server.tag.TagHandler; import net.minestom.server.tag.Taggable; import net.minestom.server.thread.Acquirable; +import net.minestom.server.thread.AcquirableSource; import net.minestom.server.timer.Schedulable; import net.minestom.server.timer.Scheduler; import net.minestom.server.timer.TaskSchedule; @@ -51,7 +52,6 @@ import net.minestom.server.utils.chunk.ChunkCache; import net.minestom.server.utils.chunk.ChunkUtils; import net.minestom.server.utils.entity.EntityUtils; -import net.minestom.server.utils.player.PlayerUtils; import net.minestom.server.utils.position.PositionUtils; import net.minestom.server.utils.time.TimeUnit; import net.minestom.server.utils.validate.Check; @@ -60,13 +60,11 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.UnknownNullability; -import space.vectrix.flare.fastutil.Int2ObjectSyncMap; import java.time.Duration; import java.time.temporal.TemporalUnit; import java.util.*; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicInteger; @@ -80,9 +78,7 @@ * To create your own entity you probably want to extend {@link LivingEntity} or {@link EntityCreature} instead. */ public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, EventHandler, Taggable, - PermissionHandler, HoverEventSource, Sound.Emitter, Shape { - private static final Int2ObjectSyncMap ENTITY_BY_ID = Int2ObjectSyncMap.hashmap(); - private static final Map ENTITY_BY_UUID = new ConcurrentHashMap<>(); + PermissionHandler, HoverEventSource, Sound.Emitter, Shape, AcquirableSource { private static final AtomicInteger LAST_ENTITY_ID = new AtomicInteger(); // Certain entities should only have their position packets sent during synchronization @@ -147,7 +143,7 @@ public void referenceUpdate(@NotNull Point point, @Nullable EntityTracker tracke private final EventNode eventNode; private final Set permissions = new CopyOnWriteArraySet<>(); - protected UUID uuid; + private final UUID uuid; private boolean isActive; // False if entity has only been instanced without being added somewhere protected boolean removed; @@ -162,7 +158,7 @@ public void referenceUpdate(@NotNull Point point, @Nullable EntityTracker tracke private long synchronizationTicks = ServerFlag.ENTITY_SYNCHRONIZATION_TICKS; private long nextSynchronizationTick = synchronizationTicks; - protected Metadata metadata = new Metadata(this); + protected MetadataHolder metadata = new MetadataHolder(this); protected EntityMeta entityMeta; private final List effects = new CopyOnWriteArrayList<>(); @@ -180,13 +176,10 @@ public Entity(@NotNull EntityType entityType, @NotNull UUID uuid) { this.previousPosition = Pos.ZERO; this.lastSyncedPosition = Pos.ZERO; - this.entityMeta = EntityTypeImpl.createMeta(entityType, this, this.metadata); + this.entityMeta = MetadataHolder.createMeta(entityType, this, this.metadata); setBoundingBox(entityType.registry().boundingBox()); - Entity.ENTITY_BY_ID.put(id, this); - Entity.ENTITY_BY_UUID.put(uuid, this); - EntitySpawnType type = entityType.registry().spawnType(); this.aerodynamics = new Aerodynamics(entityType.registry().acceleration(), type == EntitySpawnType.LIVING || type == EntitySpawnType.PLAYER ? 0.91 : 0.98, 1 - entityType.registry().drag()); @@ -213,29 +206,6 @@ public void scheduleNextTick(@NotNull Consumer callback) { this.scheduler.scheduleNextTick(() -> callback.accept(this)); } - /** - * Gets an entity based on its id (from {@link #getEntityId()}). - *

- * Entity id are unique server-wide. - * - * @param id the entity unique id - * @return the entity having the specified id, null if not found - */ - public static @Nullable Entity getEntity(int id) { - return Entity.ENTITY_BY_ID.get(id); - } - - /** - * Gets an entity based on its UUID (from {@link #getUuid()}). - * - * @param uuid the entity UUID - * @return the entity having the specified uuid, null if not found - */ - public static @Nullable Entity getEntity(@NotNull UUID uuid) { - return Entity.ENTITY_BY_UUID.getOrDefault(uuid, null); - } - - /** * Generate and return a new unique entity id. *

@@ -303,23 +273,25 @@ public void editEntityMeta(Class metaClass, Co * Teleports the entity only if the chunk at {@code position} is loaded or if * {@link Instance#hasEnabledAutoChunkLoad()} returns true. * - * @param position the teleport position - * @param chunks the chunk indexes to load before teleporting the entity, - * indexes are from {@link ChunkUtils#getChunkIndex(int, int)}, - * can be null or empty to only load the chunk at {@code position} - * @param flags flags used to teleport the entity relatively rather than absolutely - * use {@link RelativeFlags} to see available flags + * @param position the teleport position + * @param chunks the chunk indexes to load before teleporting the entity, + * indexes are from {@link ChunkUtils#getChunkIndex(int, int)}, + * can be null or empty to only load the chunk at {@code position} + * @param flags flags used to teleport the entity relatively rather than absolutely + * use {@link RelativeFlags} to see available flags + * @param shouldConfirm if false, the teleportation will be done without confirmation * @throws IllegalStateException if you try to teleport an entity before settings its instance */ public @NotNull CompletableFuture teleport(@NotNull Pos position, long @Nullable [] chunks, - @MagicConstant(flagsFromClass = RelativeFlags.class) int flags) { + @MagicConstant(flagsFromClass = RelativeFlags.class) int flags, + boolean shouldConfirm) { Check.stateCondition(instance == null, "You need to use Entity#setInstance before teleporting an entity!"); final Pos globalPosition = PositionUtils.getPositionWithRelativeFlags(this.position, position, flags); final Runnable endCallback = () -> { this.previousPosition = this.position; this.position = globalPosition; refreshCoordinate(globalPosition); - if (this instanceof Player player) player.synchronizePositionAfterTeleport(position, flags); + if (this instanceof Player player) player.synchronizePositionAfterTeleport(position, flags, shouldConfirm); else synchronizePosition(); }; @@ -338,6 +310,11 @@ public void editEntityMeta(Class metaClass, Co } } + public @NotNull CompletableFuture teleport(@NotNull Pos position, long @Nullable [] chunks, + @MagicConstant(flagsFromClass = RelativeFlags.class) int flags) { + return teleport(position, chunks, flags, true); + } + public @NotNull CompletableFuture teleport(@NotNull Pos position) { return teleport(position, null, RelativeFlags.NONE); } @@ -397,12 +374,10 @@ public void setAutoViewable(boolean autoViewable) { this.viewEngine.viewableOption.updateAuto(autoViewable); } - @ApiStatus.Experimental public void updateViewableRule(@Nullable Predicate predicate) { this.viewEngine.viewableOption.updateRule(predicate); } - @ApiStatus.Experimental public void updateViewableRule() { this.viewEngine.viewableOption.updateRule(); } @@ -413,7 +388,6 @@ public void updateViewableRule() { * * @return true if surrounding entities are visible by this */ - @ApiStatus.Experimental public boolean autoViewEntities() { return viewEngine.viewerOption.isAuto(); } @@ -423,17 +397,14 @@ public boolean autoViewEntities() { * * @param autoViewer true to add view surrounding entities, false to remove */ - @ApiStatus.Experimental public void setAutoViewEntities(boolean autoViewer) { this.viewEngine.viewerOption.updateAuto(autoViewer); } - @ApiStatus.Experimental public void updateViewerRule(@Nullable Predicate predicate) { this.viewEngine.viewerOption.updateRule(predicate); } - @ApiStatus.Experimental public void updateViewerRule() { this.viewEngine.viewerOption.updateRule(); } @@ -480,7 +451,7 @@ public void updateNewViewer(@NotNull Player player) { if (entity.isViewer(player)) { player.sendPacket(entity.getAttachEntityPacket()); } - }; + } // Head position player.sendPacket(new EntityHeadLookPacket(getEntityId(), position.yaw())); } @@ -528,8 +499,8 @@ public boolean hasPredictableViewers() { */ public synchronized void switchEntityType(@NotNull EntityType entityType) { this.entityType = entityType; - this.metadata = new Metadata(this); - this.entityMeta = EntityTypeImpl.createMeta(entityType, this, this.metadata); + this.metadata = new MetadataHolder(this); + this.entityMeta = MetadataHolder.createMeta(entityType, this, this.metadata); EntitySpawnType type = entityType.registry().spawnType(); this.aerodynamics = aerodynamics.withAirResistance(type == EntitySpawnType.LIVING || type == EntitySpawnType.PLAYER ? 0.91 : 0.98, 1 - entityType.registry().drag()); @@ -602,7 +573,7 @@ protected void movementTick() { if (!ChunkUtils.isLoaded(finalChunk)) return; velocity = physicsResult.newVelocity().mul(ServerFlag.SERVER_TICKS_PER_SECOND); - if (!PlayerUtils.isSocketClient(this)) { + if (!(this instanceof Player)) { onGround = physicsResult.isOnGround(); refreshPosition(physicsResult.newPosition(), true, !SYNCHRONIZE_ONLY_ENTITIES.contains(entityType)); } @@ -672,7 +643,7 @@ public long getAliveTicks() { * Each entity has an unique id (server-wide) which will change after a restart. * * @return the unique entity id - * @see Entity#getEntity(int) to retrive an entity based on its id + * @see Instance#getEntityById(int) to retrieve an entity based on its id */ public int getEntityId() { return id; @@ -696,18 +667,6 @@ public int getEntityId() { return uuid; } - /** - * Changes the internal entity UUID, mostly unsafe. - * - * @param uuid the new entity uuid - */ - public void setUuid(@NotNull UUID uuid) { - // Refresh internal map - Entity.ENTITY_BY_UUID.remove(this.uuid); - Entity.ENTITY_BY_UUID.put(uuid, this); - this.uuid = uuid; - } - /** * Returns false just after instantiation, set to true after calling {@link #setInstance(Instance)}. * @@ -811,7 +770,7 @@ public CompletableFuture setInstance(@NotNull Instance instance, @NotNull Check.notNull(chunk, "Entity has been placed in an unloaded chunk!"); refreshCurrentChunk(chunk); if (this instanceof Player player) { - instance.getWorldBorder().init(player); + player.sendPacket(instance.createInitializeWorldBorderPacket()); player.sendPacket(instance.createTimePacket()); player.sendPackets(instance.getWeather().createWeatherPackets()); } @@ -1062,18 +1021,6 @@ public boolean isOnFire() { return this.entityMeta.isOnFire(); } - /** - * Sets the entity in fire visually. - *

- * WARNING: if you want to apply damage or specify a duration, - * see {@link LivingEntity#setFireForDuration(int, TemporalUnit)}. - * - * @param fire should the entity be set in fire - */ - public void setOnFire(boolean fire) { - this.entityMeta.setOnFire(fire); - } - /** * Gets if the entity is sneaking. *

@@ -1384,13 +1331,11 @@ protected void refreshCoordinate(Point newPosition) { /** * Gets the entity eye height. - *

- * Default to {@link BoundingBox#height()}x0.85 * * @return the entity eye height */ public double getEyeHeight() { - return getPose() == Pose.SLEEPING ? 0.2 : (boundingBox.height() * 0.85); + return getPose() == Pose.SLEEPING ? 0.2 : entityType.registry().eyeHeight(); } /** @@ -1453,11 +1398,11 @@ public boolean hasEffect(@NotNull PotionEffect effect) { * Gets the level of the specified effect. * * @param effect the effect type - * @return the effect level, 0 if not found + * @return the effect level, -1 if not found */ public int getEffectLevel(@NotNull PotionEffect effect) { TimedPotion timedPotion = getEffect(effect); - return timedPotion == null ? 0 : timedPotion.potion().amplifier(); + return timedPotion == null ? -1 : timedPotion.potion().amplifier(); } /** @@ -1500,11 +1445,8 @@ protected void remove(boolean permanent) { MinecraftServer.process().dispatcher().removeElement(this); this.removed = true; - if (permanent) { - Entity.ENTITY_BY_ID.remove(id); - Entity.ENTITY_BY_UUID.remove(uuid); - } else { - // Reset some other state + if (!permanent) { + // Reset some state to be ready for re-use this.position = Pos.ZERO; this.previousPosition = Pos.ZERO; this.lastSyncedPosition = Pos.ZERO; @@ -1514,7 +1456,6 @@ protected void remove(boolean permanent) { removeFromInstance(currentInstance); this.instance = null; } - } /** @@ -1622,11 +1563,6 @@ public void setSynchronizationTicks(long ticks) { return HoverEvent.showEntity(ShowEntity.showEntity(this.entityType, this.uuid)); } - @ApiStatus.Experimental - public @NotNull Acquirable getAcquirable() { - return (Acquirable) acquirable; - } - @Override public @NotNull TagHandler tagHandler() { return tagHandler; @@ -1650,7 +1586,6 @@ public void setSynchronizationTicks(long ticks) { } @Override - @ApiStatus.Experimental public @NotNull EventNode eventNode() { return eventNode; } @@ -1786,6 +1721,26 @@ public boolean hasCollision() { return hasCollision; } + /** + * Acquires this entity. + * + * @param the type of object to be acquired + * @return the acquirable for this entity + * @deprecated It's preferred to use {@link AcquirableSource#acquirable()} instead, as it is overridden by + * subclasses + */ + @Deprecated + @ApiStatus.Experimental + public @NotNull Acquirable getAcquirable() { + return (Acquirable) acquirable; + } + + @ApiStatus.Experimental + @Override + public @NotNull Acquirable acquirable() { + return acquirable; + } + public enum Pose { STANDING, FALL_FLYING, diff --git a/src/main/java/net/minestom/server/entity/EntityCreature.java b/src/main/java/net/minestom/server/entity/EntityCreature.java index fdbf64d3d27..89927d0f15a 100644 --- a/src/main/java/net/minestom/server/entity/EntityCreature.java +++ b/src/main/java/net/minestom/server/entity/EntityCreature.java @@ -1,6 +1,5 @@ package net.minestom.server.entity; -import com.extollit.gaming.ai.path.HydrazinePathFinder; import net.minestom.server.coordinate.Pos; import net.minestom.server.entity.ai.EntityAI; import net.minestom.server.entity.ai.EntityAIGroup; @@ -9,7 +8,9 @@ import net.minestom.server.event.EventDispatcher; import net.minestom.server.event.entity.EntityAttackEvent; import net.minestom.server.instance.Instance; +import net.minestom.server.thread.Acquirable; import net.minestom.server.utils.time.TimeUnit; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -56,8 +57,7 @@ public void update(long time) { @Override public CompletableFuture setInstance(@NotNull Instance instance, @NotNull Pos spawnPosition) { - this.navigator.setPathFinder(new HydrazinePathFinder(navigator.getPathingEntity(), instance.getInstanceSpace())); - + this.navigator.reset(); return super.setInstance(instance, spawnPosition); } @@ -148,4 +148,10 @@ public void attack(@NotNull Entity target) { attack(target, false); } + @SuppressWarnings("unchecked") + @ApiStatus.Experimental + @Override + public @NotNull Acquirable acquirable() { + return (Acquirable) super.acquirable(); + } } diff --git a/src/main/java/net/minestom/server/entity/EntityProjectile.java b/src/main/java/net/minestom/server/entity/EntityProjectile.java index bf681cbc6cd..f5548ce9d45 100644 --- a/src/main/java/net/minestom/server/entity/EntityProjectile.java +++ b/src/main/java/net/minestom/server/entity/EntityProjectile.java @@ -13,6 +13,8 @@ import net.minestom.server.instance.Chunk; import net.minestom.server.instance.Instance; import net.minestom.server.instance.block.Block; +import net.minestom.server.thread.Acquirable; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -91,6 +93,8 @@ private void shoot(@NotNull Point from, @NotNull Point to, double power, double public void tick(long time) { final Pos posBefore = getPosition(); super.tick(time); + if (super.isRemoved()) return; + final Pos posNow = getPosition(); if (isStuck(posBefore, posNow)) { if (super.onGround) { @@ -184,4 +188,11 @@ private boolean isStuck(Pos pos, Pos posNow) { } return false; } + + @ApiStatus.Experimental + @SuppressWarnings("unchecked") + @Override + public @NotNull Acquirable acquirable() { + return (Acquirable) super.acquirable(); + } } diff --git a/src/main/java/net/minestom/server/entity/EntityTypeImpl.java b/src/main/java/net/minestom/server/entity/EntityTypeImpl.java index 9a2d0ee48da..9800e93e646 100644 --- a/src/main/java/net/minestom/server/entity/EntityTypeImpl.java +++ b/src/main/java/net/minestom/server/entity/EntityTypeImpl.java @@ -1,53 +1,13 @@ package net.minestom.server.entity; -import net.minestom.server.entity.metadata.EntityMeta; -import net.minestom.server.entity.metadata.PlayerMeta; -import net.minestom.server.entity.metadata.ambient.BatMeta; -import net.minestom.server.entity.metadata.animal.*; -import net.minestom.server.entity.metadata.animal.tameable.CatMeta; -import net.minestom.server.entity.metadata.animal.tameable.ParrotMeta; -import net.minestom.server.entity.metadata.animal.tameable.WolfMeta; -import net.minestom.server.entity.metadata.projectile.*; -import net.minestom.server.entity.metadata.display.BlockDisplayMeta; -import net.minestom.server.entity.metadata.display.ItemDisplayMeta; -import net.minestom.server.entity.metadata.display.TextDisplayMeta; -import net.minestom.server.entity.metadata.flying.GhastMeta; -import net.minestom.server.entity.metadata.flying.PhantomMeta; -import net.minestom.server.entity.metadata.golem.IronGolemMeta; -import net.minestom.server.entity.metadata.golem.ShulkerMeta; -import net.minestom.server.entity.metadata.golem.SnowGolemMeta; -import net.minestom.server.entity.metadata.item.*; -import net.minestom.server.entity.metadata.minecart.*; -import net.minestom.server.entity.metadata.monster.*; -import net.minestom.server.entity.metadata.monster.raider.*; -import net.minestom.server.entity.metadata.monster.skeleton.SkeletonMeta; -import net.minestom.server.entity.metadata.monster.skeleton.StrayMeta; -import net.minestom.server.entity.metadata.monster.skeleton.WitherSkeletonMeta; -import net.minestom.server.entity.metadata.monster.zombie.*; -import net.minestom.server.entity.metadata.other.*; -import net.minestom.server.entity.metadata.villager.VillagerMeta; -import net.minestom.server.entity.metadata.villager.WanderingTraderMeta; -import net.minestom.server.entity.metadata.water.AxolotlMeta; -import net.minestom.server.entity.metadata.water.DolphinMeta; -import net.minestom.server.entity.metadata.water.GlowSquidMeta; -import net.minestom.server.entity.metadata.water.SquidMeta; -import net.minestom.server.entity.metadata.water.fish.*; import net.minestom.server.registry.Registry; import org.jetbrains.annotations.NotNull; import java.util.Collection; -import java.util.Map; -import java.util.function.BiFunction; - -import static java.util.Map.entry; record EntityTypeImpl(Registry.EntityEntry registry) implements EntityType { - private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.ENTITIES, EntityTypeImpl::createImpl); - - private static EntityType createImpl(String namespace, Registry.Properties properties) { - return new EntityTypeImpl(Registry.entity(namespace, properties)); - } - static final Map> ENTITY_META_SUPPLIER = createMetaMap(); + private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.ENTITIES, + (namespace, properties) -> new EntityTypeImpl(Registry.entity(namespace, properties))); static EntityType get(@NotNull String namespace) { return CONTAINER.get(namespace); @@ -65,143 +25,8 @@ static Collection values() { return CONTAINER.values(); } - static EntityMeta createMeta(EntityType entityType, Entity entity, Metadata metadata) { - return ENTITY_META_SUPPLIER.get(entityType.name()).apply(entity, metadata); - } - @Override public String toString() { return name(); } - - private static Map> createMetaMap() { - return Map.>ofEntries( - entry("minecraft:allay", AllayMeta::new), - entry("minecraft:area_effect_cloud", AreaEffectCloudMeta::new), - entry("minecraft:armor_stand", ArmorStandMeta::new), - entry("minecraft:arrow", ArrowMeta::new), - entry("minecraft:axolotl", AxolotlMeta::new), - entry("minecraft:bat", BatMeta::new), - entry("minecraft:bee", BeeMeta::new), - entry("minecraft:blaze", BlazeMeta::new), - entry("minecraft:block_display", BlockDisplayMeta::new), - entry("minecraft:boat", BoatMeta::new), - entry("minecraft:breeze", BreezeMeta::new), - entry("minecraft:chest_boat", BoatMeta::new), - entry("minecraft:camel", CamelMeta::new), - entry("minecraft:cat", CatMeta::new), - entry("minecraft:cave_spider", CaveSpiderMeta::new), - entry("minecraft:chicken", ChickenMeta::new), - entry("minecraft:cod", CodMeta::new), - entry("minecraft:cow", CowMeta::new), - entry("minecraft:creeper", CreeperMeta::new), - entry("minecraft:dolphin", DolphinMeta::new), - entry("minecraft:donkey", DonkeyMeta::new), - entry("minecraft:dragon_fireball", DragonFireballMeta::new), - entry("minecraft:drowned", DrownedMeta::new), - entry("minecraft:elder_guardian", ElderGuardianMeta::new), - entry("minecraft:end_crystal", EndCrystalMeta::new), - entry("minecraft:ender_dragon", EnderDragonMeta::new), - entry("minecraft:enderman", EndermanMeta::new), - entry("minecraft:endermite", EndermiteMeta::new), - entry("minecraft:evoker", EvokerMeta::new), - entry("minecraft:evoker_fangs", EvokerFangsMeta::new), - entry("minecraft:experience_orb", ExperienceOrbMeta::new), - entry("minecraft:eye_of_ender", EyeOfEnderMeta::new), - entry("minecraft:falling_block", FallingBlockMeta::new), - entry("minecraft:firework_rocket", FireworkRocketMeta::new), - entry("minecraft:fox", FoxMeta::new), - entry("minecraft:frog", FrogMeta::new), - entry("minecraft:ghast", GhastMeta::new), - entry("minecraft:giant", GiantMeta::new), - entry("minecraft:glow_item_frame", GlowItemFrameMeta::new), - entry("minecraft:glow_squid", GlowSquidMeta::new), - entry("minecraft:goat", GoatMeta::new), - entry("minecraft:guardian", GuardianMeta::new), - entry("minecraft:hoglin", HoglinMeta::new), - entry("minecraft:horse", HorseMeta::new), - entry("minecraft:husk", HuskMeta::new), - entry("minecraft:illusioner", IllusionerMeta::new), - entry("minecraft:interaction", InteractionMeta::new), - entry("minecraft:iron_golem", IronGolemMeta::new), - entry("minecraft:item", ItemEntityMeta::new), - entry("minecraft:item_display", ItemDisplayMeta::new), - entry("minecraft:item_frame", ItemFrameMeta::new), - entry("minecraft:fireball", FireballMeta::new), - entry("minecraft:leash_knot", LeashKnotMeta::new), - entry("minecraft:lightning_bolt", LightningBoltMeta::new), - entry("minecraft:llama", LlamaMeta::new), - entry("minecraft:llama_spit", LlamaSpitMeta::new), - entry("minecraft:magma_cube", MagmaCubeMeta::new), - entry("minecraft:marker", MarkerMeta::new), - entry("minecraft:minecart", MinecartMeta::new), - entry("minecraft:chest_minecart", ChestMinecartMeta::new), - entry("minecraft:command_block_minecart", CommandBlockMinecartMeta::new), - entry("minecraft:furnace_minecart", FurnaceMinecartMeta::new), - entry("minecraft:hopper_minecart", HopperMinecartMeta::new), - entry("minecraft:spawner_minecart", SpawnerMinecartMeta::new), - entry("minecraft:text_display", TextDisplayMeta::new), - entry("minecraft:tnt_minecart", TntMinecartMeta::new), - entry("minecraft:mule", MuleMeta::new), - entry("minecraft:mooshroom", MooshroomMeta::new), - entry("minecraft:ocelot", OcelotMeta::new), - entry("minecraft:painting", PaintingMeta::new), - entry("minecraft:panda", PandaMeta::new), - entry("minecraft:parrot", ParrotMeta::new), - entry("minecraft:phantom", PhantomMeta::new), - entry("minecraft:pig", PigMeta::new), - entry("minecraft:piglin", PiglinMeta::new), - entry("minecraft:piglin_brute", PiglinBruteMeta::new), - entry("minecraft:pillager", PillagerMeta::new), - entry("minecraft:polar_bear", PolarBearMeta::new), - entry("minecraft:tnt", PrimedTntMeta::new), - entry("minecraft:pufferfish", PufferfishMeta::new), - entry("minecraft:rabbit", RabbitMeta::new), - entry("minecraft:ravager", RavagerMeta::new), - entry("minecraft:salmon", SalmonMeta::new), - entry("minecraft:sheep", SheepMeta::new), - entry("minecraft:shulker", ShulkerMeta::new), - entry("minecraft:shulker_bullet", ShulkerBulletMeta::new), - entry("minecraft:silverfish", SilverfishMeta::new), - entry("minecraft:skeleton", SkeletonMeta::new), - entry("minecraft:skeleton_horse", SkeletonHorseMeta::new), - entry("minecraft:slime", SlimeMeta::new), - entry("minecraft:small_fireball", SmallFireballMeta::new), - entry("minecraft:sniffer", SnifferMeta::new), - entry("minecraft:snow_golem", SnowGolemMeta::new), - entry("minecraft:snowball", SnowballMeta::new), - entry("minecraft:spectral_arrow", SpectralArrowMeta::new), - entry("minecraft:spider", SpiderMeta::new), - entry("minecraft:squid", SquidMeta::new), - entry("minecraft:stray", StrayMeta::new), - entry("minecraft:strider", StriderMeta::new), - entry("minecraft:tadpole", TadpoleMeta::new), - entry("minecraft:egg", ThrownEggMeta::new), - entry("minecraft:ender_pearl", ThrownEnderPearlMeta::new), - entry("minecraft:experience_bottle", ThrownExperienceBottleMeta::new), - entry("minecraft:potion", ThrownPotionMeta::new), - entry("minecraft:trident", ThrownTridentMeta::new), - entry("minecraft:trader_llama", TraderLlamaMeta::new), - entry("minecraft:tropical_fish", TropicalFishMeta::new), - entry("minecraft:turtle", TurtleMeta::new), - entry("minecraft:vex", VexMeta::new), - entry("minecraft:villager", VillagerMeta::new), - entry("minecraft:vindicator", VindicatorMeta::new), - entry("minecraft:wandering_trader", WanderingTraderMeta::new), - entry("minecraft:warden", WardenMeta::new), - entry("minecraft:wind_charge", WindChargeMeta::new), - entry("minecraft:witch", WitchMeta::new), - entry("minecraft:wither", WitherMeta::new), - entry("minecraft:wither_skeleton", WitherSkeletonMeta::new), - entry("minecraft:wither_skull", WitherSkullMeta::new), - entry("minecraft:wolf", WolfMeta::new), - entry("minecraft:zoglin", ZoglinMeta::new), - entry("minecraft:zombie", ZombieMeta::new), - entry("minecraft:zombie_horse", ZombieHorseMeta::new), - entry("minecraft:zombie_villager", ZombieVillagerMeta::new), - entry("minecraft:zombified_piglin", ZombifiedPiglinMeta::new), - entry("minecraft:player", PlayerMeta::new), - entry("minecraft:fishing_bobber", FishingHookMeta::new) - ); - } } diff --git a/src/main/java/net/minestom/server/entity/EntityView.java b/src/main/java/net/minestom/server/entity/EntityView.java index 8e3b768ddf9..736122755ea 100644 --- a/src/main/java/net/minestom/server/entity/EntityView.java +++ b/src/main/java/net/minestom/server/entity/EntityView.java @@ -4,7 +4,6 @@ import it.unimi.dsi.fastutil.ints.IntIterator; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; -import net.minestom.server.MinecraftServer; import net.minestom.server.ServerFlag; import net.minestom.server.coordinate.Point; import net.minestom.server.instance.EntityTracker; @@ -167,7 +166,7 @@ public boolean isRegistered(T entity) { } public void register(T entity) { - assert Entity.getEntity(entity.getEntityId()) == entity : "Unregistered entity shouldn't be registered as viewer"; + assert entity.getInstance() != null : "Instance-less entity shouldn't be registered as viewer"; this.bitSet.add(entity.getEntityId()); } @@ -247,10 +246,12 @@ final class SetImpl extends AbstractSet { synchronized (mutex) { var bitSet = viewableOption.bitSet; if (bitSet.isEmpty()) return Collections.emptyIterator(); + Instance instance = entity.getInstance(); + if (instance == null) return Collections.emptyIterator(); players = new ArrayList<>(bitSet.size()); for (IntIterator it = bitSet.intIterator(); it.hasNext(); ) { final int id = it.nextInt(); - final Player player = (Player) Entity.getEntity(id); + final Player player = (Player) instance.getEntityById(id); if (player != null) players.add(player); } } diff --git a/src/main/java/net/minestom/server/entity/EquipmentSlot.java b/src/main/java/net/minestom/server/entity/EquipmentSlot.java index 2fa5bc18952..79bd82afe8f 100644 --- a/src/main/java/net/minestom/server/entity/EquipmentSlot.java +++ b/src/main/java/net/minestom/server/entity/EquipmentSlot.java @@ -1,6 +1,5 @@ package net.minestom.server.entity; -import net.minestom.server.item.attribute.AttributeSlot; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -41,14 +40,4 @@ public int armorSlot() { return ARMORS; } - public static @NotNull EquipmentSlot fromAttributeSlot(@NotNull AttributeSlot attributeSlot) { - return switch (attributeSlot) { - case MAINHAND -> MAIN_HAND; - case OFFHAND -> OFF_HAND; - case FEET -> BOOTS; - case LEGS -> LEGGINGS; - case CHEST -> CHESTPLATE; - case HEAD -> HELMET; - }; - } } diff --git a/src/main/java/net/minestom/server/entity/EquipmentSlotGroup.java b/src/main/java/net/minestom/server/entity/EquipmentSlotGroup.java new file mode 100644 index 00000000000..6f2bc2bff12 --- /dev/null +++ b/src/main/java/net/minestom/server/entity/EquipmentSlotGroup.java @@ -0,0 +1,43 @@ +package net.minestom.server.entity; + +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public enum EquipmentSlotGroup { + ANY(EquipmentSlot.values()), + MAIN_HAND(EquipmentSlot.MAIN_HAND), + OFF_HAND(EquipmentSlot.OFF_HAND), + HAND(EquipmentSlot.MAIN_HAND, EquipmentSlot.OFF_HAND), + FEET(EquipmentSlot.BOOTS), + LEGS(EquipmentSlot.LEGGINGS), + CHEST(EquipmentSlot.CHESTPLATE), + HEAD(EquipmentSlot.HELMET), + ARMOR(EquipmentSlot.CHESTPLATE, EquipmentSlot.LEGGINGS, EquipmentSlot.BOOTS, EquipmentSlot.HELMET), + BODY(EquipmentSlot.CHESTPLATE, EquipmentSlot.LEGGINGS); + + public static final NetworkBuffer.Type NETWORK_TYPE = NetworkBuffer.Enum(EquipmentSlotGroup.class); + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.fromEnumStringable(EquipmentSlotGroup.class); + + private final List equipmentSlots; + + EquipmentSlotGroup(@NotNull EquipmentSlot... equipmentSlots) { + this.equipmentSlots = List.of(equipmentSlots); + } + + /** + * Returns the (potentially multiple) equipment slots associated with this attribute slot. + */ + public @NotNull List equipmentSlots() { + return this.equipmentSlots; + } + + /** + * Returns true if this attribute slot has an effect on the given {@link EquipmentSlot}, false otherwise. + */ + public boolean contains(@NotNull EquipmentSlot equipmentSlot) { + return this.equipmentSlots.contains(equipmentSlot); + } +} diff --git a/src/main/java/net/minestom/server/entity/ExperienceOrb.java b/src/main/java/net/minestom/server/entity/ExperienceOrb.java index 87dec2a436a..0c8b2457720 100644 --- a/src/main/java/net/minestom/server/entity/ExperienceOrb.java +++ b/src/main/java/net/minestom/server/entity/ExperienceOrb.java @@ -1,6 +1,9 @@ package net.minestom.server.entity; import net.minestom.server.coordinate.Vec; +import net.minestom.server.thread.Acquirable; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; import java.util.Comparator; @@ -105,4 +108,11 @@ private Player getClosestPlayer(Entity entity, float maxDistance) { if (closest.getDistanceSquared(entity) > maxDistance * maxDistance) return null; return closest; } + + @ApiStatus.Experimental + @SuppressWarnings("unchecked") + @Override + public @NotNull Acquirable acquirable() { + return (Acquirable) super.acquirable(); + } } diff --git a/src/main/java/net/minestom/server/entity/ItemEntity.java b/src/main/java/net/minestom/server/entity/ItemEntity.java index f0ff33d7bde..17800771f3f 100644 --- a/src/main/java/net/minestom/server/entity/ItemEntity.java +++ b/src/main/java/net/minestom/server/entity/ItemEntity.java @@ -5,9 +5,11 @@ import net.minestom.server.event.entity.EntityItemMergeEvent; import net.minestom.server.instance.EntityTracker; import net.minestom.server.item.ItemStack; -import net.minestom.server.item.StackingRule; +import net.minestom.server.thread.Acquirable; +import net.minestom.server.utils.MathUtils; import net.minestom.server.utils.time.Cooldown; import net.minestom.server.utils.time.TimeUnit; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -77,13 +79,12 @@ public void update(long time) { if (getDistanceSquared(itemEntity) > mergeRange * mergeRange) return; final ItemStack itemStackEntity = itemEntity.getItemStack(); - final StackingRule stackingRule = StackingRule.get(); - final boolean canStack = stackingRule.canBeStacked(itemStack, itemStackEntity); + final boolean canStack = itemStack.isSimilar(itemStackEntity); if (!canStack) return; - final int totalAmount = stackingRule.getAmount(itemStack) + stackingRule.getAmount(itemStackEntity); - if (!stackingRule.canApply(itemStack, totalAmount)) return; - final ItemStack result = stackingRule.apply(itemStack, totalAmount); + final int totalAmount = itemStack.amount() + itemStackEntity.amount(); + if (!MathUtils.isBetween(totalAmount, 0, itemStack.maxStackSize())) return; + final ItemStack result = itemStack.withAmount(totalAmount); EntityItemMergeEvent entityItemMergeEvent = new EntityItemMergeEvent(this, itemEntity, result); EventDispatcher.callCancellable(entityItemMergeEvent, () -> { setItemStack(entityItemMergeEvent.getResult()); @@ -217,4 +218,11 @@ public void setPickupDelay(Duration delay) { public long getSpawnTime() { return spawnTime; } + + @ApiStatus.Experimental + @SuppressWarnings("unchecked") + @Override + public @NotNull Acquirable acquirable() { + return (Acquirable) super.acquirable(); + } } diff --git a/src/main/java/net/minestom/server/entity/LivingEntity.java b/src/main/java/net/minestom/server/entity/LivingEntity.java index 251ec7d3d23..ae54fea9901 100644 --- a/src/main/java/net/minestom/server/entity/LivingEntity.java +++ b/src/main/java/net/minestom/server/entity/LivingEntity.java @@ -1,48 +1,59 @@ package net.minestom.server.entity; import net.kyori.adventure.sound.Sound.Source; -import net.minestom.server.attribute.*; import net.minestom.server.collision.BoundingBox; import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Vec; +import net.minestom.server.entity.attribute.Attribute; +import net.minestom.server.entity.attribute.AttributeInstance; +import net.minestom.server.entity.attribute.AttributeModifier; +import net.minestom.server.entity.attribute.AttributeOperation; import net.minestom.server.entity.damage.Damage; import net.minestom.server.entity.damage.DamageType; import net.minestom.server.entity.metadata.LivingEntityMeta; import net.minestom.server.event.EventDispatcher; import net.minestom.server.event.entity.EntityDamageEvent; import net.minestom.server.event.entity.EntityDeathEvent; -import net.minestom.server.event.entity.EntityFireEvent; +import net.minestom.server.event.entity.EntityFireExtinguishEvent; +import net.minestom.server.event.entity.EntitySetFireEvent; import net.minestom.server.event.item.EntityEquipEvent; import net.minestom.server.event.item.PickupItemEvent; import net.minestom.server.instance.EntityTracker; import net.minestom.server.inventory.EquipmentHandler; +import net.minestom.server.item.ItemComponent; import net.minestom.server.item.ItemStack; +import net.minestom.server.item.component.AttributeList; import net.minestom.server.network.ConnectionState; import net.minestom.server.network.packet.server.LazyPacket; -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.EntityPropertiesPacket; -import net.minestom.server.network.packet.server.play.SoundEffectPacket; +import net.minestom.server.network.packet.server.play.*; import net.minestom.server.network.player.PlayerConnection; +import net.minestom.server.registry.DynamicRegistry; import net.minestom.server.scoreboard.Team; import net.minestom.server.sound.SoundEvent; +import net.minestom.server.thread.Acquirable; import net.minestom.server.utils.NamespaceID; import net.minestom.server.utils.block.BlockIterator; import net.minestom.server.utils.time.Cooldown; import net.minestom.server.utils.time.TimeUnit; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnmodifiableView; import java.time.Duration; -import java.time.temporal.TemporalUnit; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; public class LivingEntity extends Entity implements EquipmentHandler { + private static final AttributeModifier SPRINTING_SPEED_MODIFIER = new AttributeModifier(NamespaceID.from("minecraft:sprinting"), 0.3, AttributeOperation.MULTIPLY_TOTAL); + + /** + * IDs of modifiers that are protected from removal by methods like {@link AttributeInstance#clearModifiers()}. + */ + @ApiStatus.Internal + public static final Set PROTECTED_MODIFIERS = Set.of(SPRINTING_SPEED_MODIFIER.id()); + // ItemStack pickup protected boolean canPickupItem; protected Cooldown itemPickupCooldown = new Cooldown(Duration.of(5, TimeUnit.SERVER_TICK)); @@ -54,20 +65,17 @@ public class LivingEntity extends Entity implements EquipmentHandler { // Bounding box used for items' pickup (see LivingEntity#setBoundingBox) protected BoundingBox expandedBoundingBox; - private final Map attributeModifiers = new ConcurrentHashMap<>(); + private final Map attributeModifiers = new ConcurrentHashMap<>(); + private final Collection unmodifiableModifiers = + Collections.unmodifiableCollection(attributeModifiers.values()); // Abilities protected boolean invulnerable; /** - * Time at which this entity must be extinguished - */ - private long fireExtinguishTime; - - /** - * Period, in ms, between two fire damage applications + * Ticks until this entity must be extinguished */ - private long fireDamagePeriod = 1000L; + private int remainingFireTicks; private Team team; @@ -105,6 +113,18 @@ private void initEquipments() { this.boots = ItemStack.AIR; } + @Override + public void setSprinting(boolean sprinting) { + super.setSprinting(sprinting); + + // We must set the sprinting attribute serverside because when we resend modifiers it overwrites what + // the client has, meaning if they are sprinting and we send no modifiers, they will no longer be + // getting the speed boost of sprinting. + final AttributeInstance speed = getAttribute(Attribute.GENERIC_MOVEMENT_SPEED); + if (sprinting) speed.addModifier(SPRINTING_SPEED_MODIFIER); + else speed.removeModifier(SPRINTING_SPEED_MODIFIER); + } + @NotNull @Override public ItemStack getItemInMainHand() { @@ -113,8 +133,10 @@ public ItemStack getItemInMainHand() { @Override public void setItemInMainHand(@NotNull ItemStack itemStack) { + ItemStack oldItem = this.mainHandItem; this.mainHandItem = getEquipmentItem(itemStack, EquipmentSlot.MAIN_HAND); syncEquipment(EquipmentSlot.MAIN_HAND); + updateEquipmentAttributes(oldItem, this.mainHandItem, EquipmentSlot.MAIN_HAND); } @NotNull @@ -125,8 +147,10 @@ public ItemStack getItemInOffHand() { @Override public void setItemInOffHand(@NotNull ItemStack itemStack) { + ItemStack oldItem = this.offHandItem; this.offHandItem = getEquipmentItem(itemStack, EquipmentSlot.OFF_HAND); syncEquipment(EquipmentSlot.OFF_HAND); + updateEquipmentAttributes(oldItem, this.offHandItem, EquipmentSlot.OFF_HAND); } @NotNull @@ -137,8 +161,10 @@ public ItemStack getHelmet() { @Override public void setHelmet(@NotNull ItemStack itemStack) { + ItemStack oldItem = this.helmet; this.helmet = getEquipmentItem(itemStack, EquipmentSlot.HELMET); syncEquipment(EquipmentSlot.HELMET); + updateEquipmentAttributes(oldItem, this.helmet, EquipmentSlot.HELMET); } @NotNull @@ -149,8 +175,10 @@ public ItemStack getChestplate() { @Override public void setChestplate(@NotNull ItemStack itemStack) { + ItemStack oldItem = this.chestplate; this.chestplate = getEquipmentItem(itemStack, EquipmentSlot.CHESTPLATE); syncEquipment(EquipmentSlot.CHESTPLATE); + updateEquipmentAttributes(oldItem, this.chestplate, EquipmentSlot.CHESTPLATE); } @NotNull @@ -161,8 +189,10 @@ public ItemStack getLeggings() { @Override public void setLeggings(@NotNull ItemStack itemStack) { + ItemStack oldItem = this.leggings; this.leggings = getEquipmentItem(itemStack, EquipmentSlot.LEGGINGS); syncEquipment(EquipmentSlot.LEGGINGS); + updateEquipmentAttributes(oldItem, this.leggings, EquipmentSlot.LEGGINGS); } @NotNull @@ -173,8 +203,10 @@ public ItemStack getBoots() { @Override public void setBoots(@NotNull ItemStack itemStack) { + ItemStack oldItem = this.boots; this.boots = getEquipmentItem(itemStack, EquipmentSlot.BOOTS); syncEquipment(EquipmentSlot.BOOTS); + updateEquipmentAttributes(oldItem, this.boots, EquipmentSlot.BOOTS); } private ItemStack getEquipmentItem(@NotNull ItemStack itemStack, @NotNull EquipmentSlot slot) { @@ -183,10 +215,45 @@ private ItemStack getEquipmentItem(@NotNull ItemStack itemStack, @NotNull Equipm return entityEquipEvent.getEquippedItem(); } + + /** + * Updates the current attributes of the living entity based on + * + * @param oldItemStack The ItemStack that has been removed, modifiers on this stack will be removed from the entity + * @param newItemStack The ItemStack that has been added, modifiers on this stack will be added to the entity + * @param slot The slot that changed, this will determine what modifiers are actually changed + */ + @ApiStatus.Internal + public void updateEquipmentAttributes(@NotNull ItemStack oldItemStack, @NotNull ItemStack newItemStack, @NotNull EquipmentSlot slot) { + AttributeList oldAttributes = oldItemStack.get(ItemComponent.ATTRIBUTE_MODIFIERS); + // Remove old attributes + if (oldAttributes != null) { + for (AttributeList.Modifier modifier : oldAttributes.modifiers()) { + // If the modifier currently modifies the slot we are updating + if (modifier.slot().contains(slot)) { + AttributeInstance attributeInstance = getAttribute(modifier.attribute()); + attributeInstance.removeModifier(modifier.modifier().id()); + } + } + } + AttributeList newAttributes = newItemStack.get(ItemComponent.ATTRIBUTE_MODIFIERS); + // Add new attributes + if (newAttributes != null) { + for (AttributeList.Modifier modifier : newAttributes.modifiers()) { + // If the modifier currently modifies the slot we are updating + if (modifier.slot().contains(slot)) { + AttributeInstance attributeInstance = getAttribute(modifier.attribute()); + attributeInstance.addModifier(modifier.modifier()); + } + } + } + } + @Override public void update(long time) { - if (isOnFire() && time > fireExtinguishTime) { - setOnFire(false); + // Fire + if (remainingFireTicks > 0 && --remainingFireTicks == 0) { + EventDispatcher.callCancellable(new EntityFireExtinguishEvent(this, true), () -> entityMeta.setOnFire(false)); } // Items picking @@ -271,54 +338,50 @@ public void kill() { } /** - * Sets fire to this entity for a given duration. + * Gets the amount of ticks this entity is on fire for. * - * @param duration duration in ticks of the effect + * @return the remaining duration of fire in ticks, 0 if not on fire */ - public void setFireForDuration(int duration) { - setFireForDuration(duration, TimeUnit.SERVER_TICK); + public int getFireTicks() { + return remainingFireTicks; } /** - * Sets fire to this entity for a given duration. + * Sets this entity on fire for the given ticks. * - * @param duration duration of the effect - * @param temporalUnit unit used to express the duration - * @see #setOnFire(boolean) if you want it to be permanent without any event callback - */ - public void setFireForDuration(int duration, TemporalUnit temporalUnit) { - setFireForDuration(Duration.of(duration, temporalUnit)); - } + * @param ticks duration of fire in ticks + */ + public void setFireTicks(int ticks) { + int fireTicks = Math.max(0, ticks); + if (fireTicks > 0) { + EntitySetFireEvent entitySetFireEvent = new EntitySetFireEvent(this, ticks); + EventDispatcher.call(entitySetFireEvent); + if (entitySetFireEvent.isCancelled()) return; + + fireTicks = Math.max(0, entitySetFireEvent.getFireTicks()); + if (fireTicks > 0) { + remainingFireTicks = fireTicks; + entityMeta.setOnFire(true); + return; + } + } - /** - * Sets fire to this entity for a given duration. - * - * @param duration duration of the effect - * @see #setOnFire(boolean) if you want it to be permanent without any event callback - */ - public void setFireForDuration(Duration duration) { - EntityFireEvent entityFireEvent = new EntityFireEvent(this, duration); - - // Do not start fire event if the fire needs to be removed (< 0 duration) - if (duration.toMillis() > 0) { - EventDispatcher.callCancellable(entityFireEvent, () -> { - final long fireTime = entityFireEvent.getFireTime(TimeUnit.MILLISECOND); - setOnFire(true); - fireExtinguishTime = System.currentTimeMillis() + fireTime; - }); - } else { - fireExtinguishTime = System.currentTimeMillis(); + if (remainingFireTicks != 0) { + EntityFireExtinguishEvent entityFireExtinguishEvent = new EntityFireExtinguishEvent(this, false); + EventDispatcher.callCancellable(entityFireExtinguishEvent, () -> entityMeta.setOnFire(false)); } + + remainingFireTicks = fireTicks; } - public boolean damage(@NotNull DamageType type, float amount) { + public boolean damage(@NotNull DynamicRegistry.Key type, float amount) { return damage(new Damage(type, null, null, null, amount)); } /** * Damages the entity by a value, the type of the damage also has to be specified. * - * @param damage the damage to be applied + * @param damage the damage to be applied * @return true if damage has been applied, false if it didn't */ public boolean damage(@NotNull Damage damage) { @@ -339,6 +402,8 @@ public boolean damage(@NotNull Damage damage) { sendPacketToViewersAndSelf(new EntityAnimationPacket(getEntityId(), EntityAnimationPacket.Animation.TAKE_DAMAGE)); } + sendPacketToViewersAndSelf(new DamageEventPacket(getEntityId(), damage.getTypeId(), damage.getAttacker() == null ? 0 : damage.getAttacker().getEntityId() + 1, damage.getSource() == null ? 0 : damage.getSource().getEntityId() + 1, damage.getSourcePosition())); + // Additional hearts support if (this instanceof Player player) { final float additionalHearts = player.getAdditionalHearts(); @@ -366,8 +431,7 @@ public boolean damage(@NotNull Damage damage) { // TODO: separate living entity categories soundCategory = Source.HOSTILE; } - sendPacketToViewersAndSelf(new SoundEffectPacket(sound, null, soundCategory, - getPosition(), 1.0f, 1.0f, 0)); + sendPacketToViewersAndSelf(new SoundEffectPacket(sound, soundCategory, getPosition(), 1.0f, 1.0f, 0)); } }); @@ -380,7 +444,7 @@ public boolean damage(@NotNull Damage damage) { * @param type the type of damage * @return true if this entity is immune to the given type of damage */ - public boolean isImmune(@NotNull DamageType type) { + public boolean isImmune(@NotNull DynamicRegistry.Key type) { return false; } @@ -399,7 +463,7 @@ public float getHealth() { * @param health the new entity health */ public void setHealth(float health) { - this.health = Math.min(health, getMaxHealth()); + this.health = Math.min(health, (float) getAttributeValue(Attribute.GENERIC_MAX_HEALTH)); if (this.health <= 0 && !isDead) { kill(); } @@ -418,22 +482,13 @@ public void setHealth(float health) { return lastDamage; } - /** - * Gets the entity max health from {@link #getAttributeValue(Attribute)} {@link VanillaAttribute#GENERIC_MAX_HEALTH}. - * - * @return the entity max health - */ - public float getMaxHealth() { - return getAttributeValue(VanillaAttribute.GENERIC_MAX_HEALTH); - } - /** * Sets the heal of the entity as its max health. *

- * Retrieved from {@link #getAttributeValue(Attribute)} with the attribute {@link VanillaAttribute#GENERIC_MAX_HEALTH}. + * Retrieved from {@link #getAttributeValue(Attribute)} with the attribute {@link Attribute#GENERIC_MAX_HEALTH}. */ public void heal() { - setHealth(getAttributeValue(VanillaAttribute.GENERIC_MAX_HEALTH)); + setHealth((float) getAttributeValue(Attribute.GENERIC_MAX_HEALTH)); } /** @@ -443,10 +498,19 @@ public void heal() { * @return the attribute instance */ public @NotNull AttributeInstance getAttribute(@NotNull Attribute attribute) { - return attributeModifiers.computeIfAbsent(attribute.namespace(), + return attributeModifiers.computeIfAbsent(attribute.name(), s -> new AttributeInstance(attribute, this::onAttributeChanged)); } + /** + * Retrieves all {@link AttributeInstance}s on this entity. + * + * @return a collection of all attribute instances on this entity + */ + public @NotNull @UnmodifiableView Collection getAttributes() { + return unmodifiableModifiers; + } + /** * Callback used when an attribute instance has been modified. * @@ -459,7 +523,12 @@ protected void onAttributeChanged(@NotNull AttributeInstance attributeInstance) // connection null during Player initialization (due to #super call) self = playerConnection != null && playerConnection.getConnectionState() == ConnectionState.PLAY; } - EntityPropertiesPacket propertiesPacket = new EntityPropertiesPacket(getEntityId(), List.of(attributeInstance)); + EntityAttributesPacket propertiesPacket = new EntityAttributesPacket(getEntityId(), List.of( + new EntityAttributesPacket.Property( + attributeInstance.attribute(), + attributeInstance.getBaseValue(), + attributeInstance.getModifiers()) + )); if (self) { sendPacketToViewersAndSelf(propertiesPacket); } else { @@ -473,8 +542,8 @@ protected void onAttributeChanged(@NotNull AttributeInstance attributeInstance) * @param attribute the attribute value to get * @return the attribute value */ - public float getAttributeValue(@NotNull Attribute attribute) { - AttributeInstance instance = attributeModifiers.get(attribute.namespace()); + public double getAttributeValue(@NotNull Attribute attribute) { + AttributeInstance instance = attributeModifiers.get(attribute.name()); return (instance != null) ? instance.getValue() : attribute.defaultValue(); } @@ -523,7 +592,7 @@ public void setBoundingBox(BoundingBox boundingBox) { * (can be used for attack animation). */ public void swingMainHand() { - sendPacketToViewers(new EntityAnimationPacket(getEntityId(), EntityAnimationPacket.Animation.SWING_MAIN_ARM)); + swingMainHand(false); } /** @@ -531,7 +600,38 @@ public void swingMainHand() { * (can be used for attack animation). */ public void swingOffHand() { - sendPacketToViewers(new EntityAnimationPacket(getEntityId(), EntityAnimationPacket.Animation.SWING_OFF_HAND)); + swingOffHand(false); + } + + /** + * Sends a {@link EntityAnimationPacket} to swing the main hand + * (can be used for attack animation). + * + * @param fromClient if true, broadcast only to viewers + */ + @ApiStatus.Internal + public void swingMainHand(boolean fromClient) { + swingHand(fromClient, EntityAnimationPacket.Animation.SWING_MAIN_ARM); + } + + /** + * Sends a {@link EntityAnimationPacket} to swing the off hand + * (can be used for attack animation). + * + * @param fromClient if true, broadcast only to viewers + */ + @ApiStatus.Internal + public void swingOffHand(boolean fromClient) { + swingHand(fromClient, EntityAnimationPacket.Animation.SWING_OFF_HAND); + } + + private void swingHand(boolean fromClient, EntityAnimationPacket.Animation animation) { + EntityAnimationPacket packet = new EntityAnimationPacket(getEntityId(), animation); + if (fromClient) { + sendPacketToViewers(packet); + } else { + sendPacketToViewersAndSelf(packet); + } } public void refreshActiveHand(boolean isHandActive, boolean offHand, boolean riptideSpinAttack) { @@ -566,41 +666,16 @@ protected void refreshIsDead(boolean isDead) { } /** - * Gets an {@link EntityPropertiesPacket} for this entity with all of its attributes values. + * Gets an {@link EntityAttributesPacket} for this entity with all of its attributes values. * - * @return an {@link EntityPropertiesPacket} linked to this entity + * @return an {@link EntityAttributesPacket} linked to this entity */ - protected @NotNull EntityPropertiesPacket getPropertiesPacket() { - return new EntityPropertiesPacket(getEntityId(), List.copyOf(attributeModifiers.values())); - } - - /** - * Gets the time in ms between two fire damage applications. - * - * @return the time in ms - * @see #setFireDamagePeriod(Duration) - */ - public long getFireDamagePeriod() { - return fireDamagePeriod; - } - - /** - * Changes the delay between two fire damage applications. - * - * @param fireDamagePeriod the delay - * @param temporalUnit the time unit - */ - public void setFireDamagePeriod(long fireDamagePeriod, @NotNull TemporalUnit temporalUnit) { - setFireDamagePeriod(Duration.of(fireDamagePeriod, temporalUnit)); - } - - /** - * Changes the delay between two fire damage applications. - * - * @param fireDamagePeriod the delay - */ - public void setFireDamagePeriod(Duration fireDamagePeriod) { - this.fireDamagePeriod = fireDamagePeriod.toMillis(); + protected @NotNull EntityAttributesPacket getPropertiesPacket() { + List properties = new ArrayList<>(); + for (AttributeInstance instance : attributeModifiers.values()) { + properties.add(new EntityAttributesPacket.Property(instance.attribute(), instance.getBaseValue(), instance.getModifiers())); + } + return new EntityAttributesPacket(getEntityId(), properties); } /** @@ -610,7 +685,7 @@ public void setFireDamagePeriod(Duration fireDamagePeriod) { */ public void setTeam(@Nullable Team team) { if (this.team == team) return; - String member = this instanceof Player player ? player.getUsername() : uuid.toString(); + String member = this instanceof Player player ? player.getUsername() : getUuid().toString(); if (this.team != null) { this.team.removeMember(member); } @@ -667,7 +742,14 @@ public void setTeam(@Nullable Team team) { */ @Override public void takeKnockback(float strength, final double x, final double z) { - strength *= 1 - getAttributeValue(VanillaAttribute.GENERIC_KNOCKBACK_RESISTANCE); + strength *= (float) (1 - getAttributeValue(Attribute.GENERIC_KNOCKBACK_RESISTANCE)); super.takeKnockback(strength, x, z); } + + @SuppressWarnings("unchecked") + @ApiStatus.Experimental + @Override + public @NotNull Acquirable acquirable() { + return (Acquirable) super.acquirable(); + } } diff --git a/src/main/java/net/minestom/server/entity/Metadata.java b/src/main/java/net/minestom/server/entity/Metadata.java index fbceea35927..7dd983e6a50 100644 --- a/src/main/java/net/minestom/server/entity/Metadata.java +++ b/src/main/java/net/minestom/server/entity/Metadata.java @@ -1,28 +1,29 @@ package net.minestom.server.entity; +import net.kyori.adventure.nbt.BinaryTag; import net.kyori.adventure.text.Component; import net.minestom.server.coordinate.Point; +import net.minestom.server.entity.metadata.animal.ArmadilloMeta; import net.minestom.server.entity.metadata.animal.FrogMeta; import net.minestom.server.entity.metadata.animal.SnifferMeta; import net.minestom.server.entity.metadata.animal.tameable.CatMeta; +import net.minestom.server.entity.metadata.animal.tameable.WolfMeta; import net.minestom.server.entity.metadata.other.PaintingMeta; +import net.minestom.server.instance.block.Block; import net.minestom.server.item.ItemStack; import net.minestom.server.network.NetworkBuffer; -import net.minestom.server.network.packet.server.play.EntityMetaDataPacket; import net.minestom.server.particle.Particle; +import net.minestom.server.registry.DynamicRegistry; import net.minestom.server.utils.Direction; +import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.UnknownNullability; -import org.jglrxavpok.hephaistos.nbt.NBT; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.VarHandle; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; +import java.util.List; import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; public final class Metadata { public static Entry Byte(byte value) { @@ -33,7 +34,7 @@ public static Entry VarInt(int value) { return new MetadataImpl.EntryImpl<>(TYPE_VARINT, value, NetworkBuffer.VAR_INT); } - public static Entry Long(long value) { + public static Entry VarLong(long value) { return new MetadataImpl.EntryImpl<>(TYPE_LONG, value, NetworkBuffer.VAR_LONG); } @@ -50,11 +51,11 @@ public static Entry Chat(@NotNull Component value) { } public static Entry OptChat(@Nullable Component value) { - return new MetadataImpl.EntryImpl<>(TYPE_OPTCHAT, value, NetworkBuffer.OPT_CHAT); + return new MetadataImpl.EntryImpl<>(TYPE_OPT_CHAT, value, NetworkBuffer.OPT_CHAT); } - public static Entry Slot(@NotNull ItemStack value) { - return new MetadataImpl.EntryImpl<>(TYPE_SLOT, value, NetworkBuffer.ITEM); + public static Entry ItemStack(@NotNull ItemStack value) { + return new MetadataImpl.EntryImpl<>(TYPE_ITEM_STACK, value, ItemStack.NETWORK_TYPE); } public static Entry Boolean(boolean value) { @@ -65,12 +66,12 @@ public static Entry Rotation(@NotNull Point value) { return new MetadataImpl.EntryImpl<>(TYPE_ROTATION, value, NetworkBuffer.VECTOR3); } - public static Entry Position(@NotNull Point value) { - return new MetadataImpl.EntryImpl<>(TYPE_POSITION, value, NetworkBuffer.BLOCK_POSITION); + public static Entry BlockPosition(@NotNull Point value) { + return new MetadataImpl.EntryImpl<>(TYPE_BLOCK_POSITION, value, NetworkBuffer.BLOCK_POSITION); } - public static Entry OptPosition(@Nullable Point value) { - return new MetadataImpl.EntryImpl<>(TYPE_OPTPOSITION, value, NetworkBuffer.OPT_BLOCK_POSITION); + public static Entry OptBlockPosition(@Nullable Point value) { + return new MetadataImpl.EntryImpl<>(TYPE_OPT_BLOCK_POSITION, value, NetworkBuffer.OPT_BLOCK_POSITION); } public static Entry Direction(@NotNull Direction value) { @@ -78,15 +79,15 @@ public static Entry Direction(@NotNull Direction value) { } public static Entry OptUUID(@Nullable UUID value) { - return new MetadataImpl.EntryImpl<>(TYPE_OPTUUID, value, NetworkBuffer.OPT_UUID); + return new MetadataImpl.EntryImpl<>(TYPE_OPT_UUID, value, NetworkBuffer.OPT_UUID); } - public static Entry BlockState(@NotNull Integer value) { - return new MetadataImpl.EntryImpl<>(TYPE_BLOCKSTATE, value, NetworkBuffer.BLOCK_STATE); + public static Entry BlockState(@NotNull Block value) { + return new MetadataImpl.EntryImpl<>(TYPE_BLOCKSTATE, value, Block.NETWORK_TYPE); } public static Entry OptBlockState(@Nullable Integer value) { - return new MetadataImpl.EntryImpl<>(TYPE_OPTBLOCKSTATE, value, new NetworkBuffer.Type<>() { + return new MetadataImpl.EntryImpl<>(TYPE_OPT_BLOCKSTATE, value, new NetworkBuffer.Type<>() { @Override public void write(@NotNull NetworkBuffer buffer, Integer value) { buffer.write(NetworkBuffer.VAR_INT, value == null ? 0 : value); @@ -100,19 +101,29 @@ public Integer read(@NotNull NetworkBuffer buffer) { }); } - public static Entry NBT(@NotNull NBT nbt) { + public static Entry NBT(@NotNull BinaryTag nbt) { return new MetadataImpl.EntryImpl<>(TYPE_NBT, nbt, NetworkBuffer.NBT); } - public static Entry VillagerData(int villagerType, - int villagerProfession, - int level) { - return new MetadataImpl.EntryImpl<>(TYPE_VILLAGERDATA, new int[]{villagerType, villagerProfession, level}, - NetworkBuffer.VILLAGER_DATA); + public static Entry Particle(@NotNull Particle particle) { + return new MetadataImpl.EntryImpl<>(TYPE_PARTICLE, particle, Particle.NETWORK_TYPE); + } + + public static Entry> ParticleList(@NotNull List particles) { + return new MetadataImpl.EntryImpl<>(TYPE_PARTICLE_LIST, particles, Particle.NETWORK_TYPE.list(Short.MAX_VALUE)); + } + + public static Entry VillagerData(int[] data) { + Check.argCondition(data.length != 3, "Villager data array must have a length of 3"); + return new MetadataImpl.EntryImpl<>(TYPE_VILLAGERDATA, data, NetworkBuffer.VILLAGER_DATA); + } + + public static Entry VillagerData(int villagerType, int villagerProfession, int level) { + return VillagerData(new int[]{villagerType, villagerProfession, level}); } public static Entry OptVarInt(@Nullable Integer value) { - return new MetadataImpl.EntryImpl<>(TYPE_OPTVARINT, value, new NetworkBuffer.Type<>() { + return new MetadataImpl.EntryImpl<>(TYPE_OPT_VARINT, value, new NetworkBuffer.Type<>() { @Override public void write(@NotNull NetworkBuffer buffer, Integer value) { buffer.write(NetworkBuffer.VAR_INT, value == null ? 0 : value + 1); @@ -126,24 +137,32 @@ public Integer read(@NotNull NetworkBuffer buffer) { }); } - public static Entry Pose(@NotNull Entity.Pose value) { + public static Entry Pose(Entity.@NotNull Pose value) { return new MetadataImpl.EntryImpl<>(TYPE_POSE, value, NetworkBuffer.POSE); } public static Entry CatVariant(@NotNull CatMeta.Variant value) { - return new MetadataImpl.EntryImpl<>(TYPE_CAT_VARIANT, value, NetworkBuffer.CAT_VARIANT); + return new MetadataImpl.EntryImpl<>(TYPE_CAT_VARIANT, value, CatMeta.Variant.NETWORK_TYPE); + } + + public static Entry> WolfVariant(@NotNull DynamicRegistry.Key value) { + return new MetadataImpl.EntryImpl<>(TYPE_WOLF_VARIANT, value, WolfMeta.Variant.NETWORK_TYPE); } public static Entry FrogVariant(@NotNull FrogMeta.Variant value) { - return new MetadataImpl.EntryImpl<>(TYPE_FROG_VARIANT, value, NetworkBuffer.FROG_VARIANT); + return new MetadataImpl.EntryImpl<>(TYPE_FROG_VARIANT, value, FrogMeta.Variant.NETWORK_TYPE); } - public static Entry PaintingVariant(@NotNull PaintingMeta.Variant value) { - return new MetadataImpl.EntryImpl<>(TYPE_PAINTINGVARIANT, value, NetworkBuffer.PAINTING_VARIANT); + public static Entry> PaintingVariant(@NotNull DynamicRegistry.Key value) { + return new MetadataImpl.EntryImpl<>(TYPE_PAINTING_VARIANT, value, PaintingMeta.Variant.NETWORK_TYPE); } public static Entry SnifferState(@NotNull SnifferMeta.State value) { - return new MetadataImpl.EntryImpl<>(TYPE_SNIFFER_STATE, value, NetworkBuffer.SNIFFER_STATE); + return new MetadataImpl.EntryImpl<>(TYPE_SNIFFER_STATE, value, SnifferMeta.State.NETWORK_TYPE); + } + + public static Entry ArmadilloState(@NotNull ArmadilloMeta.State value) { + return new MetadataImpl.EntryImpl<>(TYPE_ARMADILLO_STATE, value, ArmadilloMeta.State.NETWORK_TYPE); } public static Entry Vector3(@NotNull Point value) { @@ -154,124 +173,44 @@ public static Entry Quaternion(float @NotNull [] value) { return new MetadataImpl.EntryImpl<>(TYPE_QUATERNION, value, NetworkBuffer.QUATERNION); } - public static Entry Particle(@NotNull Particle particle) { - return new MetadataImpl.EntryImpl<>(TYPE_PARTICLE, particle, NetworkBuffer.PARTICLE); - } - - public static final byte TYPE_BYTE = 0; - public static final byte TYPE_VARINT = 1; - public static final byte TYPE_LONG = 2; - public static final byte TYPE_FLOAT = 3; - public static final byte TYPE_STRING = 4; - public static final byte TYPE_CHAT = 5; - public static final byte TYPE_OPTCHAT = 6; - public static final byte TYPE_SLOT = 7; - public static final byte TYPE_BOOLEAN = 8; - public static final byte TYPE_ROTATION = 9; - public static final byte TYPE_POSITION = 10; - public static final byte TYPE_OPTPOSITION = 11; - public static final byte TYPE_DIRECTION = 12; - public static final byte TYPE_OPTUUID = 13; - public static final byte TYPE_BLOCKSTATE = 14; - public static final byte TYPE_OPTBLOCKSTATE = 15; - public static final byte TYPE_NBT = 16; - public static final byte TYPE_PARTICLE = 17; - public static final byte TYPE_VILLAGERDATA = 18; - public static final byte TYPE_OPTVARINT = 19; - public static final byte TYPE_POSE = 20; - public static final byte TYPE_CAT_VARIANT = 21; - public static final byte TYPE_FROG_VARIANT = 22; - public static final byte TYPE_OPTGLOBALPOS = 23; - public static final byte TYPE_PAINTINGVARIANT = 24; - public static final byte TYPE_SNIFFER_STATE = 25; - public static final byte TYPE_VECTOR3 = 26; - public static final byte TYPE_QUATERNION = 27; + private static final AtomicInteger NEXT_ID = new AtomicInteger(0); + + public static final byte TYPE_BYTE = nextId(); + public static final byte TYPE_VARINT = nextId(); + public static final byte TYPE_LONG = nextId(); + public static final byte TYPE_FLOAT = nextId(); + public static final byte TYPE_STRING = nextId(); + public static final byte TYPE_CHAT = nextId(); + public static final byte TYPE_OPT_CHAT = nextId(); + public static final byte TYPE_ITEM_STACK = nextId(); + public static final byte TYPE_BOOLEAN = nextId(); + public static final byte TYPE_ROTATION = nextId(); + public static final byte TYPE_BLOCK_POSITION = nextId(); + public static final byte TYPE_OPT_BLOCK_POSITION = nextId(); + public static final byte TYPE_DIRECTION = nextId(); + public static final byte TYPE_OPT_UUID = nextId(); + public static final byte TYPE_BLOCKSTATE = nextId(); + public static final byte TYPE_OPT_BLOCKSTATE = nextId(); + public static final byte TYPE_NBT = nextId(); + public static final byte TYPE_PARTICLE = nextId(); + public static final byte TYPE_PARTICLE_LIST = nextId(); + public static final byte TYPE_VILLAGERDATA = nextId(); + public static final byte TYPE_OPT_VARINT = nextId(); + public static final byte TYPE_POSE = nextId(); + public static final byte TYPE_CAT_VARIANT = nextId(); + public static final byte TYPE_WOLF_VARIANT = nextId(); + public static final byte TYPE_FROG_VARIANT = nextId(); + public static final byte TYPE_OPT_GLOBAL_POSITION = nextId(); // Unused by protocol it seems + public static final byte TYPE_PAINTING_VARIANT = nextId(); + public static final byte TYPE_SNIFFER_STATE = nextId(); + public static final byte TYPE_ARMADILLO_STATE = nextId(); + public static final byte TYPE_VECTOR3 = nextId(); + public static final byte TYPE_QUATERNION = nextId(); // Impl Note: Adding an entry here requires that a default value entry is added in MetadataImpl.EMPTY_VALUES - private static final VarHandle NOTIFIED_CHANGES; - - static { - try { - NOTIFIED_CHANGES = MethodHandles.lookup().findVarHandle(Metadata.class, "notifyAboutChanges", boolean.class); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new IllegalStateException(e); - } - } - - private final Entity entity; - private volatile Entry[] entries = new Entry[0]; - private volatile Map> entryMap = null; - - @SuppressWarnings("FieldMayBeFinal") - private volatile boolean notifyAboutChanges = true; - private final Map> notNotifiedChanges = new HashMap<>(); - - public Metadata(@Nullable Entity entity) { - this.entity = entity; - } - - @SuppressWarnings("unchecked") - public T getIndex(int index, @Nullable T defaultValue) { - final Entry[] entries = this.entries; - if (index < 0 || index >= entries.length) return defaultValue; - final Entry entry = entries[index]; - return entry != null ? (T) entry.value() : defaultValue; - } - - public void setIndex(int index, @NotNull Entry entry) { - Entry[] entries = this.entries; - // Resize array if necessary - if (index >= entries.length) { - final int newLength = Math.max(entries.length * 2, index + 1); - this.entries = entries = Arrays.copyOf(entries, newLength); - } - entries[index] = entry; - this.entryMap = null; - // Send metadata packet to update viewers and self - final Entity entity = this.entity; - if (entity != null && entity.isActive()) { - if (!this.notifyAboutChanges) { - synchronized (this.notNotifiedChanges) { - this.notNotifiedChanges.put(index, entry); - } - } else { - entity.sendPacketToViewersAndSelf(new EntityMetaDataPacket(entity.getEntityId(), Map.of(index, entry))); - } - } - } - - public void setNotifyAboutChanges(boolean notifyAboutChanges) { - if (!NOTIFIED_CHANGES.compareAndSet(this, !notifyAboutChanges, notifyAboutChanges)) - return; - if (!notifyAboutChanges) { - // Ask future metadata changes to be cached - return; - } - final Entity entity = this.entity; - if (entity == null || !entity.isActive()) return; - Map> entries; - synchronized (this.notNotifiedChanges) { - Map> awaitingChanges = this.notNotifiedChanges; - if (awaitingChanges.isEmpty()) return; - entries = Map.copyOf(awaitingChanges); - awaitingChanges.clear(); - } - entity.sendPacketToViewersAndSelf(new EntityMetaDataPacket(entity.getEntityId(), entries)); - } - - public @NotNull Map> getEntries() { - Map> map = entryMap; - if (map == null) { - map = new HashMap<>(); - final Entry[] entries = this.entries; - for (int i = 0; i < entries.length; i++) { - final Entry entry = entries[i]; - if (entry != null) map.put(i, entry); - } - this.entryMap = Map.copyOf(map); - } - return map; + private static byte nextId() { + return (byte) NEXT_ID.getAndIncrement(); } public sealed interface Entry extends NetworkBuffer.Writer diff --git a/src/main/java/net/minestom/server/entity/MetadataHolder.java b/src/main/java/net/minestom/server/entity/MetadataHolder.java new file mode 100644 index 00000000000..03391408b8b --- /dev/null +++ b/src/main/java/net/minestom/server/entity/MetadataHolder.java @@ -0,0 +1,273 @@ +package net.minestom.server.entity; + +import net.minestom.server.entity.metadata.EntityMeta; +import net.minestom.server.entity.metadata.PlayerMeta; +import net.minestom.server.entity.metadata.ambient.BatMeta; +import net.minestom.server.entity.metadata.animal.*; +import net.minestom.server.entity.metadata.animal.tameable.CatMeta; +import net.minestom.server.entity.metadata.animal.tameable.ParrotMeta; +import net.minestom.server.entity.metadata.animal.tameable.WolfMeta; +import net.minestom.server.entity.metadata.display.BlockDisplayMeta; +import net.minestom.server.entity.metadata.display.ItemDisplayMeta; +import net.minestom.server.entity.metadata.display.TextDisplayMeta; +import net.minestom.server.entity.metadata.flying.GhastMeta; +import net.minestom.server.entity.metadata.flying.PhantomMeta; +import net.minestom.server.entity.metadata.golem.IronGolemMeta; +import net.minestom.server.entity.metadata.golem.ShulkerMeta; +import net.minestom.server.entity.metadata.golem.SnowGolemMeta; +import net.minestom.server.entity.metadata.item.*; +import net.minestom.server.entity.metadata.minecart.*; +import net.minestom.server.entity.metadata.monster.*; +import net.minestom.server.entity.metadata.monster.raider.*; +import net.minestom.server.entity.metadata.monster.skeleton.BoggedMeta; +import net.minestom.server.entity.metadata.monster.skeleton.SkeletonMeta; +import net.minestom.server.entity.metadata.monster.skeleton.StrayMeta; +import net.minestom.server.entity.metadata.monster.skeleton.WitherSkeletonMeta; +import net.minestom.server.entity.metadata.monster.zombie.*; +import net.minestom.server.entity.metadata.other.*; +import net.minestom.server.entity.metadata.projectile.*; +import net.minestom.server.entity.metadata.villager.VillagerMeta; +import net.minestom.server.entity.metadata.villager.WanderingTraderMeta; +import net.minestom.server.entity.metadata.water.AxolotlMeta; +import net.minestom.server.entity.metadata.water.DolphinMeta; +import net.minestom.server.entity.metadata.water.GlowSquidMeta; +import net.minestom.server.entity.metadata.water.SquidMeta; +import net.minestom.server.entity.metadata.water.fish.*; +import net.minestom.server.network.packet.server.play.EntityMetaDataPacket; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiFunction; + +public final class MetadataHolder { + private static final VarHandle NOTIFIED_CHANGES; + + static { + try { + NOTIFIED_CHANGES = MethodHandles.lookup().findVarHandle(MetadataHolder.class, "notifyAboutChanges", boolean.class); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + + private final Entity entity; + private volatile Metadata.Entry[] entries = new Metadata.Entry[0]; + private volatile Map> entryMap = null; + + @SuppressWarnings("FieldMayBeFinal") + private volatile boolean notifyAboutChanges = true; + private final Map> notNotifiedChanges = new HashMap<>(); + + public MetadataHolder(@Nullable Entity entity) { + this.entity = entity; + } + + @SuppressWarnings("unchecked") + public T getIndex(int index, @Nullable T defaultValue) { + final Metadata.Entry[] entries = this.entries; + if (index < 0 || index >= entries.length) return defaultValue; + final Metadata.Entry entry = entries[index]; + return entry != null ? (T) entry.value() : defaultValue; + } + + public void setIndex(int index, @NotNull Metadata.Entry entry) { + Metadata.Entry[] entries = this.entries; + // Resize array if necessary + if (index >= entries.length) { + final int newLength = Math.max(entries.length * 2, index + 1); + this.entries = entries = Arrays.copyOf(entries, newLength); + } + entries[index] = entry; + this.entryMap = null; + // Send metadata packet to update viewers and self + final Entity entity = this.entity; + if (entity != null && entity.isActive()) { + if (!this.notifyAboutChanges) { + synchronized (this.notNotifiedChanges) { + this.notNotifiedChanges.put(index, entry); + } + } else { + entity.sendPacketToViewersAndSelf(new EntityMetaDataPacket(entity.getEntityId(), Map.of(index, entry))); + } + } + } + + public void setNotifyAboutChanges(boolean notifyAboutChanges) { + if (!NOTIFIED_CHANGES.compareAndSet(this, !notifyAboutChanges, notifyAboutChanges)) + return; + if (!notifyAboutChanges) { + // Ask future metadata changes to be cached + return; + } + final Entity entity = this.entity; + if (entity == null || !entity.isActive()) return; + Map> entries; + synchronized (this.notNotifiedChanges) { + Map> awaitingChanges = this.notNotifiedChanges; + if (awaitingChanges.isEmpty()) return; + entries = Map.copyOf(awaitingChanges); + awaitingChanges.clear(); + } + entity.sendPacketToViewersAndSelf(new EntityMetaDataPacket(entity.getEntityId(), entries)); + } + + public @NotNull Map> getEntries() { + Map> map = entryMap; + if (map == null) { + map = new HashMap<>(); + final Metadata.Entry[] entries = this.entries; + for (int i = 0; i < entries.length; i++) { + final Metadata.Entry entry = entries[i]; + if (entry != null) map.put(i, entry); + } + this.entryMap = Map.copyOf(map); + } + return map; + } + + static final Map> ENTITY_META_SUPPLIER = createMetaMap(); + + static EntityMeta createMeta(EntityType entityType, Entity entity, MetadataHolder metadata) { + return ENTITY_META_SUPPLIER.get(entityType.name()).apply(entity, metadata); + } + + private static Map> createMetaMap() { + return Map.>ofEntries( + Map.entry("minecraft:allay", AllayMeta::new), + Map.entry("minecraft:area_effect_cloud", AreaEffectCloudMeta::new), + Map.entry("minecraft:armadillo", ArmadilloMeta::new), + Map.entry("minecraft:armor_stand", ArmorStandMeta::new), + Map.entry("minecraft:arrow", ArrowMeta::new), + Map.entry("minecraft:axolotl", AxolotlMeta::new), + Map.entry("minecraft:bat", BatMeta::new), + Map.entry("minecraft:bee", BeeMeta::new), + Map.entry("minecraft:blaze", BlazeMeta::new), + Map.entry("minecraft:block_display", BlockDisplayMeta::new), + Map.entry("minecraft:boat", BoatMeta::new), + Map.entry("minecraft:bogged", BoggedMeta::new), + Map.entry("minecraft:breeze", BreezeMeta::new), + Map.entry("minecraft:breeze_wind_charge", BreezeWindChargeMeta::new), + Map.entry("minecraft:chest_boat", BoatMeta::new), + Map.entry("minecraft:camel", CamelMeta::new), + Map.entry("minecraft:cat", CatMeta::new), + Map.entry("minecraft:cave_spider", CaveSpiderMeta::new), + Map.entry("minecraft:chicken", ChickenMeta::new), + Map.entry("minecraft:cod", CodMeta::new), + Map.entry("minecraft:cow", CowMeta::new), + Map.entry("minecraft:creeper", CreeperMeta::new), + Map.entry("minecraft:dolphin", DolphinMeta::new), + Map.entry("minecraft:donkey", DonkeyMeta::new), + Map.entry("minecraft:dragon_fireball", DragonFireballMeta::new), + Map.entry("minecraft:drowned", DrownedMeta::new), + Map.entry("minecraft:elder_guardian", ElderGuardianMeta::new), + Map.entry("minecraft:end_crystal", EndCrystalMeta::new), + Map.entry("minecraft:ender_dragon", EnderDragonMeta::new), + Map.entry("minecraft:enderman", EndermanMeta::new), + Map.entry("minecraft:endermite", EndermiteMeta::new), + Map.entry("minecraft:evoker", EvokerMeta::new), + Map.entry("minecraft:evoker_fangs", EvokerFangsMeta::new), + Map.entry("minecraft:experience_orb", ExperienceOrbMeta::new), + Map.entry("minecraft:eye_of_ender", EyeOfEnderMeta::new), + Map.entry("minecraft:falling_block", FallingBlockMeta::new), + Map.entry("minecraft:firework_rocket", FireworkRocketMeta::new), + Map.entry("minecraft:fox", FoxMeta::new), + Map.entry("minecraft:frog", FrogMeta::new), + Map.entry("minecraft:ghast", GhastMeta::new), + Map.entry("minecraft:giant", GiantMeta::new), + Map.entry("minecraft:glow_item_frame", GlowItemFrameMeta::new), + Map.entry("minecraft:glow_squid", GlowSquidMeta::new), + Map.entry("minecraft:goat", GoatMeta::new), + Map.entry("minecraft:guardian", GuardianMeta::new), + Map.entry("minecraft:hoglin", HoglinMeta::new), + Map.entry("minecraft:horse", HorseMeta::new), + Map.entry("minecraft:husk", HuskMeta::new), + Map.entry("minecraft:illusioner", IllusionerMeta::new), + Map.entry("minecraft:interaction", InteractionMeta::new), + Map.entry("minecraft:iron_golem", IronGolemMeta::new), + Map.entry("minecraft:item", ItemEntityMeta::new), + Map.entry("minecraft:item_display", ItemDisplayMeta::new), + Map.entry("minecraft:item_frame", ItemFrameMeta::new), + Map.entry("minecraft:fireball", FireballMeta::new), + Map.entry("minecraft:leash_knot", LeashKnotMeta::new), + Map.entry("minecraft:lightning_bolt", LightningBoltMeta::new), + Map.entry("minecraft:llama", LlamaMeta::new), + Map.entry("minecraft:llama_spit", LlamaSpitMeta::new), + Map.entry("minecraft:magma_cube", MagmaCubeMeta::new), + Map.entry("minecraft:marker", MarkerMeta::new), + Map.entry("minecraft:minecart", MinecartMeta::new), + Map.entry("minecraft:chest_minecart", ChestMinecartMeta::new), + Map.entry("minecraft:command_block_minecart", CommandBlockMinecartMeta::new), + Map.entry("minecraft:furnace_minecart", FurnaceMinecartMeta::new), + Map.entry("minecraft:hopper_minecart", HopperMinecartMeta::new), + Map.entry("minecraft:spawner_minecart", SpawnerMinecartMeta::new), + Map.entry("minecraft:text_display", TextDisplayMeta::new), + Map.entry("minecraft:tnt_minecart", TntMinecartMeta::new), + Map.entry("minecraft:mule", MuleMeta::new), + Map.entry("minecraft:mooshroom", MooshroomMeta::new), + Map.entry("minecraft:ocelot", OcelotMeta::new), + Map.entry("minecraft:ominous_item_spawner", OminousItemSpawnerMeta::new), + Map.entry("minecraft:painting", PaintingMeta::new), + Map.entry("minecraft:panda", PandaMeta::new), + Map.entry("minecraft:parrot", ParrotMeta::new), + Map.entry("minecraft:phantom", PhantomMeta::new), + Map.entry("minecraft:pig", PigMeta::new), + Map.entry("minecraft:piglin", PiglinMeta::new), + Map.entry("minecraft:piglin_brute", PiglinBruteMeta::new), + Map.entry("minecraft:pillager", PillagerMeta::new), + Map.entry("minecraft:polar_bear", PolarBearMeta::new), + Map.entry("minecraft:tnt", PrimedTntMeta::new), + Map.entry("minecraft:pufferfish", PufferfishMeta::new), + Map.entry("minecraft:rabbit", RabbitMeta::new), + Map.entry("minecraft:ravager", RavagerMeta::new), + Map.entry("minecraft:salmon", SalmonMeta::new), + Map.entry("minecraft:sheep", SheepMeta::new), + Map.entry("minecraft:shulker", ShulkerMeta::new), + Map.entry("minecraft:shulker_bullet", ShulkerBulletMeta::new), + Map.entry("minecraft:silverfish", SilverfishMeta::new), + Map.entry("minecraft:skeleton", SkeletonMeta::new), + Map.entry("minecraft:skeleton_horse", SkeletonHorseMeta::new), + Map.entry("minecraft:slime", SlimeMeta::new), + Map.entry("minecraft:small_fireball", SmallFireballMeta::new), + Map.entry("minecraft:sniffer", SnifferMeta::new), + Map.entry("minecraft:snow_golem", SnowGolemMeta::new), + Map.entry("minecraft:snowball", SnowballMeta::new), + Map.entry("minecraft:spectral_arrow", SpectralArrowMeta::new), + Map.entry("minecraft:spider", SpiderMeta::new), + Map.entry("minecraft:squid", SquidMeta::new), + Map.entry("minecraft:stray", StrayMeta::new), + Map.entry("minecraft:strider", StriderMeta::new), + Map.entry("minecraft:tadpole", TadpoleMeta::new), + Map.entry("minecraft:egg", ThrownEggMeta::new), + Map.entry("minecraft:ender_pearl", ThrownEnderPearlMeta::new), + Map.entry("minecraft:experience_bottle", ThrownExperienceBottleMeta::new), + Map.entry("minecraft:potion", ThrownPotionMeta::new), + Map.entry("minecraft:trident", ThrownTridentMeta::new), + Map.entry("minecraft:trader_llama", TraderLlamaMeta::new), + Map.entry("minecraft:tropical_fish", TropicalFishMeta::new), + Map.entry("minecraft:turtle", TurtleMeta::new), + Map.entry("minecraft:vex", VexMeta::new), + Map.entry("minecraft:villager", VillagerMeta::new), + Map.entry("minecraft:vindicator", VindicatorMeta::new), + Map.entry("minecraft:wandering_trader", WanderingTraderMeta::new), + Map.entry("minecraft:warden", WardenMeta::new), + Map.entry("minecraft:wind_charge", WindChargeMeta::new), + Map.entry("minecraft:witch", WitchMeta::new), + Map.entry("minecraft:wither", WitherMeta::new), + Map.entry("minecraft:wither_skeleton", WitherSkeletonMeta::new), + Map.entry("minecraft:wither_skull", WitherSkullMeta::new), + Map.entry("minecraft:wolf", WolfMeta::new), + Map.entry("minecraft:zoglin", ZoglinMeta::new), + Map.entry("minecraft:zombie", ZombieMeta::new), + Map.entry("minecraft:zombie_horse", ZombieHorseMeta::new), + Map.entry("minecraft:zombie_villager", ZombieVillagerMeta::new), + Map.entry("minecraft:zombified_piglin", ZombifiedPiglinMeta::new), + Map.entry("minecraft:player", PlayerMeta::new), + Map.entry("minecraft:fishing_bobber", FishingHookMeta::new) + ); + } +} diff --git a/src/main/java/net/minestom/server/entity/MetadataImpl.java b/src/main/java/net/minestom/server/entity/MetadataImpl.java index d2d89cb92bf..239f870f6e9 100644 --- a/src/main/java/net/minestom/server/entity/MetadataImpl.java +++ b/src/main/java/net/minestom/server/entity/MetadataImpl.java @@ -1,19 +1,24 @@ package net.minestom.server.entity; +import net.kyori.adventure.nbt.EndBinaryTag; import net.kyori.adventure.text.Component; import net.minestom.server.coordinate.Vec; +import net.minestom.server.entity.metadata.animal.ArmadilloMeta; import net.minestom.server.entity.metadata.animal.FrogMeta; import net.minestom.server.entity.metadata.animal.SnifferMeta; import net.minestom.server.entity.metadata.animal.tameable.CatMeta; +import net.minestom.server.entity.metadata.animal.tameable.WolfMeta; import net.minestom.server.entity.metadata.other.PaintingMeta; import net.minestom.server.instance.block.Block; import net.minestom.server.item.ItemStack; import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.particle.Particle; import net.minestom.server.utils.Direction; import net.minestom.server.utils.collection.ObjectArray; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.UnknownNullability; -import org.jglrxavpok.hephaistos.nbt.NBTEnd; + +import java.util.List; import static net.minestom.server.entity.Metadata.*; import static net.minestom.server.network.NetworkBuffer.VAR_INT; @@ -24,30 +29,33 @@ final class MetadataImpl { static { EMPTY_VALUES.set(TYPE_BYTE, Byte((byte) 0)); EMPTY_VALUES.set(TYPE_VARINT, VarInt(0)); - EMPTY_VALUES.set(TYPE_LONG, Long(0L)); + EMPTY_VALUES.set(TYPE_LONG, VarLong(0L)); EMPTY_VALUES.set(TYPE_FLOAT, Float(0f)); EMPTY_VALUES.set(TYPE_STRING, String("")); EMPTY_VALUES.set(TYPE_CHAT, Chat(Component.empty())); - EMPTY_VALUES.set(TYPE_OPTCHAT, OptChat(null)); - EMPTY_VALUES.set(TYPE_SLOT, Slot(ItemStack.AIR)); + EMPTY_VALUES.set(TYPE_OPT_CHAT, OptChat(null)); + EMPTY_VALUES.set(TYPE_ITEM_STACK, ItemStack(ItemStack.AIR)); EMPTY_VALUES.set(TYPE_BOOLEAN, Boolean(false)); EMPTY_VALUES.set(TYPE_ROTATION, Rotation(Vec.ZERO)); - EMPTY_VALUES.set(TYPE_POSITION, Position(Vec.ZERO)); - EMPTY_VALUES.set(TYPE_OPTPOSITION, OptPosition(null)); + EMPTY_VALUES.set(TYPE_BLOCK_POSITION, BlockPosition(Vec.ZERO)); + EMPTY_VALUES.set(TYPE_OPT_BLOCK_POSITION, OptBlockPosition(null)); EMPTY_VALUES.set(TYPE_DIRECTION, Direction(Direction.DOWN)); - EMPTY_VALUES.set(TYPE_OPTUUID, OptUUID(null)); - EMPTY_VALUES.set(TYPE_BLOCKSTATE, BlockState(Block.AIR.id())); - EMPTY_VALUES.set(TYPE_OPTBLOCKSTATE, OptBlockState(null)); - EMPTY_VALUES.set(TYPE_NBT, NBT(NBTEnd.INSTANCE)); - //EMPTY_VALUES.set(TYPE_PARTICLE -> throw new UnsupportedOperationException(); + EMPTY_VALUES.set(TYPE_OPT_UUID, OptUUID(null)); + EMPTY_VALUES.set(TYPE_BLOCKSTATE, BlockState(Block.AIR)); + EMPTY_VALUES.set(TYPE_OPT_BLOCKSTATE, OptBlockState(null)); + EMPTY_VALUES.set(TYPE_NBT, NBT(EndBinaryTag.endBinaryTag())); + EMPTY_VALUES.set(TYPE_PARTICLE, Particle(Particle.DUST)); + EMPTY_VALUES.set(TYPE_PARTICLE_LIST, ParticleList(List.of())); EMPTY_VALUES.set(TYPE_VILLAGERDATA, VillagerData(0, 0, 0)); - EMPTY_VALUES.set(TYPE_OPTVARINT, OptVarInt(null)); + EMPTY_VALUES.set(TYPE_OPT_VARINT, OptVarInt(null)); EMPTY_VALUES.set(TYPE_POSE, Pose(Entity.Pose.STANDING)); EMPTY_VALUES.set(TYPE_CAT_VARIANT, CatVariant(CatMeta.Variant.TABBY)); + EMPTY_VALUES.set(TYPE_WOLF_VARIANT, WolfVariant(WolfMeta.Variant.PALE)); EMPTY_VALUES.set(TYPE_FROG_VARIANT, FrogVariant(FrogMeta.Variant.TEMPERATE)); // OptGlobalPos - EMPTY_VALUES.set(TYPE_PAINTINGVARIANT, PaintingVariant(PaintingMeta.Variant.KEBAB)); + EMPTY_VALUES.set(TYPE_PAINTING_VARIANT, PaintingVariant(PaintingMeta.Variant.KEBAB)); EMPTY_VALUES.set(TYPE_SNIFFER_STATE, SnifferState(SnifferMeta.State.IDLING)); + EMPTY_VALUES.set(TYPE_ARMADILLO_STATE, ArmadilloState(ArmadilloMeta.State.IDLE)); EMPTY_VALUES.set(TYPE_VECTOR3, Vector3(Vec.ZERO)); EMPTY_VALUES.set(TYPE_QUATERNION, Quaternion(new float[]{0, 0, 0, 0})); EMPTY_VALUES.trim(); diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 4bb77627151..e88d90de368 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -9,6 +9,7 @@ import net.kyori.adventure.inventory.Book; import net.kyori.adventure.pointer.Pointers; import net.kyori.adventure.resource.ResourcePackCallback; +import net.kyori.adventure.resource.ResourcePackInfo; import net.kyori.adventure.resource.ResourcePackRequest; import net.kyori.adventure.resource.ResourcePackStatus; import net.kyori.adventure.sound.Sound; @@ -18,6 +19,7 @@ import net.kyori.adventure.text.event.HoverEvent.ShowEntity; import net.kyori.adventure.text.event.HoverEventSource; import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import net.kyori.adventure.title.TitlePart; import net.minestom.server.MinecraftServer; import net.minestom.server.ServerFlag; @@ -25,13 +27,13 @@ import net.minestom.server.adventure.AdventurePacketConvertor; import net.minestom.server.adventure.Localizable; import net.minestom.server.adventure.audience.Audiences; -import net.minestom.server.attribute.VanillaAttribute; import net.minestom.server.collision.BoundingBox; import net.minestom.server.command.CommandSender; import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Pos; import net.minestom.server.coordinate.Vec; import net.minestom.server.effects.Effects; +import net.minestom.server.entity.attribute.Attribute; import net.minestom.server.entity.damage.DamageType; import net.minestom.server.entity.metadata.LivingEntityMeta; import net.minestom.server.entity.metadata.PlayerMeta; @@ -40,17 +42,20 @@ import net.minestom.server.event.inventory.InventoryOpenEvent; import net.minestom.server.event.item.ItemDropEvent; import net.minestom.server.event.item.ItemUpdateStateEvent; +import net.minestom.server.event.item.ItemUsageCompleteEvent; import net.minestom.server.event.item.PickupExperienceEvent; import net.minestom.server.event.player.*; import net.minestom.server.instance.Chunk; import net.minestom.server.instance.EntityTracker; import net.minestom.server.instance.Instance; +import net.minestom.server.instance.SharedInstance; import net.minestom.server.instance.block.Block; import net.minestom.server.inventory.Inventory; import net.minestom.server.inventory.PlayerInventory; +import net.minestom.server.item.ItemComponent; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; -import net.minestom.server.item.metadata.WrittenBookMeta; +import net.minestom.server.item.component.WrittenBookContent; import net.minestom.server.listener.manager.PacketListenerManager; import net.minestom.server.message.ChatMessageType; import net.minestom.server.message.ChatPosition; @@ -70,6 +75,7 @@ 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; @@ -77,16 +83,15 @@ import net.minestom.server.snapshot.SnapshotImpl; import net.minestom.server.snapshot.SnapshotUpdater; import net.minestom.server.statistic.PlayerStatistic; +import net.minestom.server.thread.Acquirable; import net.minestom.server.timer.Scheduler; import net.minestom.server.utils.MathUtils; import net.minestom.server.utils.PacketUtils; -import net.minestom.server.utils.PropertyUtils; import net.minestom.server.utils.async.AsyncUtils; import net.minestom.server.utils.chunk.ChunkUpdateLimitChecker; import net.minestom.server.utils.chunk.ChunkUtils; import net.minestom.server.utils.function.IntegerBiConsumer; import net.minestom.server.utils.identity.NamedAndIdentified; -import net.minestom.server.utils.instance.InstanceUtils; import net.minestom.server.utils.inventory.PlayerInventoryUtils; import net.minestom.server.utils.time.Cooldown; import net.minestom.server.utils.time.TimeUnit; @@ -118,13 +123,11 @@ public class Player extends LivingEntity implements CommandSender, Localizable, HoverEventSource, Identified, NamedAndIdentified { private static final Logger logger = LoggerFactory.getLogger(Player.class); - private static final Component REMOVE_MESSAGE = Component.text("You have been removed from the server without reason.", NamedTextColor.RED); + private static final DynamicRegistry DIMENSION_TYPE_REGISTRY = MinecraftServer.getDimensionTypeRegistry(); - private static final float MIN_CHUNKS_PER_TICK = PropertyUtils.getFloat("minestom.chunk-queue.min-per-tick", 0.01f); - private static final float MAX_CHUNKS_PER_TICK = PropertyUtils.getFloat("minestom.chunk-queue.max-per-tick", 64.0f); - private static final float CHUNKS_PER_TICK_MULTIPLIER = PropertyUtils.getFloat("minestom.chunk-queue.multiplier", 1f); + private static final Component REMOVE_MESSAGE = Component.text("You have been removed from the server without reason.", NamedTextColor.RED); + private static final Component MISSING_REQUIRED_RESOURCE_PACK = Component.text("Required resource pack was not loaded.", NamedTextColor.RED); - public static final boolean EXPERIMENT_PERFORM_POSE_UPDATES = Boolean.getBoolean("minestom.experiment.pose-updates"); // Magic values: https://wiki.vg/Entity_statuses#Player private static final int STATUS_ENABLE_REDUCED_DEBUG_INFO = 22; private static final int STATUS_DISABLE_REDUCED_DEBUG_INFO = 23; @@ -137,12 +140,12 @@ public class Player extends LivingEntity implements CommandSender, Localizable, private Component usernameComponent; protected final PlayerConnection playerConnection; - private int latency; + private volatile int latency; private Component displayName; private PlayerSkin skin; private Instance pendingInstance = null; - private DimensionType dimensionType; + private int dimensionTypeId; private GameMode gameMode; private WorldPos deathLocation; @@ -153,6 +156,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable, private Vec chunksLoadedByClient = Vec.ZERO; private final ReentrantLock chunkQueueLock = new ReentrantLock(); private final LongPriorityQueue chunkQueue = new LongArrayPriorityQueue(this::compareChunkDistance); + private boolean needsChunkPositionSync = true; private float targetChunksPerTick = 9f; // Always send 9 chunks immediately private float pendingChunkCount = 0f; // Number of chunks to send on the current tick (ie 0.5 means we cannot send a chunk yet, 1.5 would send a single chunk with a 0.5 remainder) private int maxChunkBatchLead = 1; // Maximum number of batches to send before waiting for a reply @@ -189,10 +193,10 @@ public class Player extends LivingEntity implements CommandSender, Localizable, private int food; private float foodSaturation; - private long startEatingTime; - private long defaultEatingTime = 1000L; - private long eatingTime; - private Hand eatingHand; + + private long startItemUseTime; + private long itemUseTime; + private Hand itemUseHand; // Game state (https://wiki.vg/Protocol#Change_Game_State) private boolean enableRespawnScreen; @@ -222,11 +226,14 @@ public class Player extends LivingEntity implements CommandSender, Localizable, private final PlayerVehicleInformation vehicleInformation = new PlayerVehicleInformation(); // Adventure - private Identity identity; + private final Identity identity; private final Pointers pointers; // Resource packs - private final Map resourcePackCallbacks = new HashMap<>(); + record PendingResourcePack(boolean required, @NotNull ResourcePackCallback callback) { + } + + private final Map pendingResourcePacks = new HashMap<>(); // The future is non-null when a resource pack is in-flight, and completed when all statuses have been received. private CompletableFuture resourcePackFuture = null; @@ -247,9 +254,9 @@ public Player(@NotNull UUID uuid, @NotNull String username, @NotNull PlayerConne 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(VanillaAttribute.GENERIC_MOVEMENT_SPEED).setBaseValue(0.1f); + getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).setBaseValue(0.1); // FakePlayer init its connection there playerConnectionInit(); @@ -286,13 +293,13 @@ public CompletableFuture UNSAFE_init() { 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.toString(), spawnInstance.getDimensionName(), - 0, gameMode, null, false, levelFlat, deathLocation, portalCooldown); + false, true, false, dimensionTypeId, spawnInstance.getDimensionName(), + 0, gameMode, null, false, levelFlat, deathLocation, portalCooldown, true); sendPacket(joinGamePacket); // Difficulty @@ -350,7 +357,7 @@ public CompletableFuture UNSAFE_init() { for (Recipe recipe : recipeManager.getRecipes()) { if (!recipe.shouldShow(this)) continue; - recipesIdentifier.add(recipe.getRecipeId()); + recipesIdentifier.add(recipe.id()); } if (!recipesIdentifier.isEmpty()) { UnlockRecipesPacket unlockRecipesPacket = new UnlockRecipesPacket(0, @@ -429,30 +436,30 @@ public void update(long time) { } // Eating animation - if (isEating()) { - if (time - startEatingTime >= eatingTime) { + if (isUsingItem()) { + if (itemUseTime > 0 && getCurrentItemUseTime() >= itemUseTime) { triggerStatus((byte) 9); // Mark item use as finished - ItemUpdateStateEvent itemUpdateStateEvent = callItemUpdateStateEvent(eatingHand); - - Check.notNull(itemUpdateStateEvent, "#callItemUpdateStateEvent returned null."); + ItemUpdateStateEvent itemUpdateStateEvent = callItemUpdateStateEvent(itemUseHand); // Refresh hand final boolean isOffHand = itemUpdateStateEvent.getHand() == Player.Hand.OFF; refreshActiveHand(false, isOffHand, false); - final ItemStack foodItem = itemUpdateStateEvent.getItemStack(); - final boolean isFood = foodItem.material().isFood(); - - if (isFood) { - PlayerEatEvent playerEatEvent = new PlayerEatEvent(this, foodItem, eatingHand); + final ItemStack item = itemUpdateStateEvent.getItemStack(); + final boolean isFood = item.has(ItemComponent.FOOD); + if (isFood || item.material() == Material.POTION) { + PlayerEatEvent playerEatEvent = new PlayerEatEvent(this, item, itemUseHand); EventDispatcher.call(playerEatEvent); } - refreshEating(null); + var itemUsageCompleteEvent = new ItemUsageCompleteEvent(this, itemUseHand, item); + EventDispatcher.call(itemUsageCompleteEvent); + + clearItemUse(); } } - if (EXPERIMENT_PERFORM_POSE_UPDATES) updatePose(); + updatePose(); // Tick event EventDispatcher.call(new PlayerTickEvent(this)); @@ -502,7 +509,7 @@ public void kill() { // Set death location if (getInstance() != null) - setDeathLocation(getInstance().getDimensionType(), getPosition()); + setDeathLocation(getInstance().getDimensionName(), getPosition()); } super.kill(); } @@ -515,12 +522,12 @@ public void respawn() { if (!isDead()) return; - setFireForDuration(0); - setOnFire(false); + setFireTicks(0); + entityMeta.setOnFire(false); refreshHealth(); - sendPacket(new RespawnPacket(getDimensionType().toString(), instance.getDimensionName(), - 0, gameMode, gameMode, false, levelFlat, deathLocation, portalCooldown, RespawnPacket.COPY_ALL)); + sendPacket(new RespawnPacket(dimensionTypeId, instance.getDimensionName(), 0, gameMode, gameMode, + false, levelFlat, deathLocation, portalCooldown, RespawnPacket.COPY_ALL)); refreshClientStateAfterRespawn(); PlayerRespawnEvent respawnEvent = new PlayerRespawnEvent(this); @@ -537,7 +544,7 @@ public void respawn() { this.instance.getEntityTracker().nearbyEntitiesByChunkRange(respawnPosition, settings.getEffectiveViewDistance(), EntityTracker.Target.ENTITIES, entity -> { // Skip refreshing self with a new viewer - if (!entity.getUuid().equals(uuid) && entity.isViewer(this)) { + if (!entity.getUuid().equals(getUuid()) && entity.isViewer(this)) { entity.updateNewViewer(this); } }); @@ -635,7 +642,7 @@ public void sendPacketToViewersAndSelf(@NotNull SendablePacket packet) { public CompletableFuture setInstance(@NotNull Instance instance, @NotNull Pos spawnPosition) { final Instance currentInstance = this.instance; Check.argCondition(currentInstance == instance, "Instance should be different than the current one"); - if (InstanceUtils.areLinked(currentInstance, instance) && spawnPosition.sameChunk(this.position)) { + if (SharedInstance.areLinked(currentInstance, instance) && spawnPosition.sameChunk(this.position)) { // The player already has the good version of all the chunks. // We just need to refresh his entity viewing list and add him to the instance spawnPlayer(instance, spawnPosition, false, false, false); @@ -647,6 +654,11 @@ public CompletableFuture setInstance(@NotNull Instance instance, @NotNull final Consumer runnable = (i) -> spawnPlayer(i, spawnPosition, currentInstance == null, dimensionChange, true); + // Reset chunk queue state + needsChunkPositionSync = true; + targetChunksPerTick = 9f; + pendingChunkCount = 0f; + // Ensure that surrounding chunks are loaded List> futures = new ArrayList<>(); ChunkUtils.forChunksInRange(spawnPosition, settings.getEffectiveViewDistance(), (chunkX, chunkZ) -> { @@ -738,13 +750,14 @@ private void spawnPlayer(@NotNull Instance instance, @NotNull Pos spawnPosition, // Load the nearby chunks and queue them to be sent to them ChunkUtils.forChunksInRange(spawnPosition, settings.getEffectiveViewDistance(), chunkAdder); + sendPendingChunks(); // Send available first chunk immediately to prevent falling through the floor } - synchronizePositionAfterTeleport(spawnPosition, 0); // So the player doesn't get stuck + synchronizePositionAfterTeleport(spawnPosition, 0, true); // So the player doesn't get stuck if (dimensionChange) { sendPacket(new SpawnPositionPacket(spawnPosition, 0)); - instance.getWorldBorder().init(this); + sendPacket(instance.createInitializeWorldBorderPacket()); sendPacket(new TimeUpdatePacket(instance.getWorldAge(), instance.getTime())); } @@ -763,8 +776,8 @@ private void spawnPlayer(@NotNull Instance instance, @NotNull Pos spawnPosition, public void onChunkBatchReceived(float newTargetChunksPerTick) { // logger.debug("chunk batch received player={} chunks/tick={} lead={}", username, newTargetChunksPerTick, chunkBatchLead); chunkBatchLead -= 1; - targetChunksPerTick = Float.isNaN(newTargetChunksPerTick) ? MIN_CHUNKS_PER_TICK : MathUtils.clamp( - newTargetChunksPerTick * CHUNKS_PER_TICK_MULTIPLIER, MIN_CHUNKS_PER_TICK, MAX_CHUNKS_PER_TICK); + targetChunksPerTick = Float.isNaN(newTargetChunksPerTick) ? ServerFlag.MIN_CHUNKS_PER_TICK : MathUtils.clamp( + newTargetChunksPerTick * ServerFlag.CHUNKS_PER_TICK_MULTIPLIER, ServerFlag.MIN_CHUNKS_PER_TICK, ServerFlag.MAX_CHUNKS_PER_TICK); // Beyond the first batch we can preemptively send up to 10 (matching mojang server) if (maxChunkBatchLead == 1) maxChunkBatchLead = 10; @@ -790,7 +803,7 @@ private void sendPendingChunks() { if (chunkQueue.isEmpty() || chunkBatchLead >= maxChunkBatchLead) return; // Increment the pending chunk count by the target chunks per tick - pendingChunkCount = Math.min(pendingChunkCount + targetChunksPerTick, MAX_CHUNKS_PER_TICK); + pendingChunkCount = Math.min(pendingChunkCount + targetChunksPerTick, ServerFlag.MAX_CHUNKS_PER_TICK); if (pendingChunkCount < 1) return; // Cant send anything chunkQueueLock.lock(); @@ -812,6 +825,15 @@ private void sendPendingChunks() { sendPacket(new ChunkBatchFinishedPacket(batchSize)); chunkBatchLead += 1; // logger.debug("chunk batch sent player={} chunks={} lead={}", username, batchSize, chunkBatchLead); + + // After sending the first chunk we always send a synchronize position to the client. This is to prevent + // cases where the client falls through the floor slightly while loading the first chunk. + // In the vanilla server they have an anticheat which teleports the client back if they enter the floor, + // but since Minestom does not have an anticheat this provides a similar effect. + if (needsChunkPositionSync) { + synchronizePositionAfterTeleport(getPosition(), RelativeFlags.NONE, true); + needsChunkPositionSync = false; + } } finally { chunkQueueLock.unlock(); } @@ -866,7 +888,7 @@ private boolean canFitWithBoundingBox(@NotNull Pose pose) { var iter = bb.getBlocks(getPosition()); while (iter.hasNext()) { var pos = iter.next(); - var block = instance.getBlock(pos, Block.Getter.Condition.TYPE); + var block = instance.getBlock(pos.blockX(), pos.blockY(), pos.blockZ(), Block.Getter.Condition.TYPE); // For now just ignore scaffolding. It seems to have a dynamic bounding box, or is just parsed // incorrectly in MinestomDataGenerator. @@ -880,6 +902,13 @@ private boolean canFitWithBoundingBox(@NotNull Pose pose) { return true; } + @Override + @SuppressWarnings({"UnstableApiUsage", "deprecation"}) + public void sendMessage(final @NotNull Identity source, final @NotNull Component message, final @NotNull MessageType type) { + // Note to readers: this method may be deprecated, however it is in fact required. + Messenger.sendMessage(this, message, ChatPosition.fromMessageType(type), source.uuid()); + } + /** * Sends a plugin message to the player. * @@ -902,18 +931,6 @@ public void sendPluginMessage(@NotNull String channel, @NotNull String message) sendPluginMessage(channel, message.getBytes(StandardCharsets.UTF_8)); } - /** - * Deprecated, as the Adventure library has deprecated this method in the Audience class - * @param source the identity of the source of the message - * @param message a message - * @param type the type - */ - @Override - @Deprecated - public void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) { - Messenger.sendMessage(this, message, ChatPosition.fromMessageType(type), source.uuid()); - } - @Override public void playSound(@NotNull Sound sound) { this.playSound(sound, this.position.x(), this.position.y(), this.position.z()); @@ -1000,13 +1017,13 @@ public void openBook(@NotNull Book book) { closeInventory(); } + // TODO: when adventure updates, delete this + String title = PlainTextComponentSerializer.plainText().serialize(book.title()); + String author = PlainTextComponentSerializer.plainText().serialize(book.author()); final ItemStack writtenBook = ItemStack.builder(Material.WRITTEN_BOOK) - .meta(WrittenBookMeta.class, builder -> builder.resolved(false) - .generation(WrittenBookMeta.WrittenBookGeneration.ORIGINAL) - .author(book.author()) - .title(book.title()) - .pages(book.pages())) + .set(ItemComponent.WRITTEN_BOOK_CONTENT, new WrittenBookContent(book.pages(), title, author, 0, false)) .build(); + // Set book in offhand sendPacket(new SetSlotPacket((byte) 0, 0, (short) PlayerInventoryUtils.OFFHAND_SLOT, writtenBook)); // Open the book @@ -1016,17 +1033,17 @@ public void openBook(@NotNull Book book) { } @Override - public boolean isImmune(@NotNull DamageType type) { + public boolean isImmune(@NotNull DynamicRegistry.Key type) { if (!getGameMode().canTakeDamage()) { - return type != DamageType.OUT_OF_WORLD; + return !DamageType.OUT_OF_WORLD.equals(type); } return super.isImmune(type); } @Override public void setHealth(float health) { - super.setHealth(health); sendPacket(new UpdateHealthPacket(health, food, foodSaturation)); + super.setHealth(health); } /** @@ -1036,7 +1053,7 @@ public void setHealth(float health) { * been changed with {@link #switchEntityType(EntityType)}. It is wise to check * {@link #getEntityType()} first.

*/ - public @NotNull PlayerMeta getUnsafeEntityMeta() { + public @NotNull PlayerMeta getPlayerMeta() { return (PlayerMeta) super.getEntityMeta(); } @@ -1049,7 +1066,7 @@ public void setHealth(float health) { * @return the player additional hearts */ public float getAdditionalHearts() { - return getUnsafeEntityMeta().getAdditionalHearts(); + return getPlayerMeta().getAdditionalHearts(); } /** @@ -1061,7 +1078,7 @@ public float getAdditionalHearts() { * @param additionalHearts the count of additional hearts */ public void setAdditionalHearts(float additionalHearts) { - getUnsafeEntityMeta().setAdditionalHearts(additionalHearts); + getPlayerMeta().setAdditionalHearts(additionalHearts); } /** @@ -1109,34 +1126,37 @@ public void setFoodSaturation(float foodSaturation) { * @return true if the player is eating, false otherwise */ public boolean isEating() { - return eatingHand != null; + if (!isUsingItem()) return false; + final ItemStack itemStack = getItemInHand(itemUseHand); + return itemStack.has(ItemComponent.FOOD) || itemStack.material() == Material.POTION; } /** - * Gets the hand which the player is eating from. + * Gets if the player is using an item. * - * @return the eating hand, null if none + * @return true if the player is using an item, false otherwise */ - public @Nullable Hand getEatingHand() { - return eatingHand; + public boolean isUsingItem() { + return itemUseHand != null; } /** - * Gets the player default eating time. + * Gets the hand which the player is using an item from. * - * @return the player default eating time + * @return the item use hand, null if none */ - public long getDefaultEatingTime() { - return defaultEatingTime; + public @Nullable Hand getItemUseHand() { + return itemUseHand; } /** - * Used to change the default eating time animation. + * Gets the amount of ticks which have passed since the player started using an item. * - * @param defaultEatingTime the default eating time in milliseconds + * @return the amount of ticks which have passed, or zero if the player is not using an item */ - public void setDefaultEatingTime(long defaultEatingTime) { - this.defaultEatingTime = defaultEatingTime; + public long getCurrentItemUseTime() { + if (!isUsingItem()) return 0; + return getAliveTicks() - startItemUseTime; } @Override @@ -1198,7 +1218,7 @@ public synchronized void setSkin(@Nullable PlayerSkin skin) { final PlayerInfoRemovePacket removePlayerPacket = getRemovePlayerToList(); final PlayerInfoUpdatePacket addPlayerPacket = getAddPlayerToList(); - RespawnPacket respawnPacket = new RespawnPacket(getDimensionType().toString(), instance.getDimensionName(), + RespawnPacket respawnPacket = new RespawnPacket(dimensionTypeId, instance.getDimensionName(), 0, gameMode, gameMode, false, levelFlat, deathLocation, portalCooldown, RespawnPacket.COPY_ALL); sendPacket(removePlayerPacket); @@ -1221,8 +1241,12 @@ public synchronized void setSkin(@Nullable PlayerSkin skin) { teleport(getPosition()); } - public void setDeathLocation(@NotNull DimensionType type, @NotNull Pos position) { - this.deathLocation = new WorldPos(type.getName().asString(), position); + public void setDeathLocation(@NotNull Pos position) { + setDeathLocation(getInstance().getDimensionName(), position); + } + + public void setDeathLocation(@NotNull String dimension, @NotNull Pos position) { + this.deathLocation = new WorldPos(dimension, position); } public @Nullable WorldPos getDeathLocation() { @@ -1298,9 +1322,9 @@ public boolean dropItem(@NotNull ItemStack item) { public void sendResourcePacks(@NotNull ResourcePackRequest request) { if (request.replace()) clearResourcePacks(); - for (var pack : request.packs()) { + for (final ResourcePackInfo pack : request.packs()) { sendPacket(new ResourcePackPushPacket(pack, request.required(), request.prompt())); - resourcePackCallbacks.put(pack.id(), request.callback()); + pendingResourcePacks.put(pack.id(), new PendingResourcePack(request.required(), request.callback())); if (resourcePackFuture == null) { resourcePackFuture = new CompletableFuture<>(); } @@ -1331,15 +1355,20 @@ public void clearResourcePacks() { @ApiStatus.Internal public void onResourcePackStatus(@NotNull UUID id, @NotNull ResourcePackStatus status) { - var callback = resourcePackCallbacks.get(id); - if (callback == null) return; + var pendingPack = pendingResourcePacks.get(id); + if (pendingPack == null) return; - callback.packEventReceived(id, status, this); + pendingPack.callback().packEventReceived(id, status, this); if (!status.intermediate()) { // Remove the callback and finish the future if relevant - resourcePackCallbacks.remove(id); + pendingResourcePacks.remove(id); - if (resourcePackCallbacks.isEmpty() && resourcePackFuture != null) { + // If the resource pack is required and failed to load, bye bye! + if (pendingPack.required() && status != ResourcePackStatus.SUCCESSFULLY_LOADED) { + kick(MISSING_REQUIRED_RESOURCE_PACK); + } + + if (pendingResourcePacks.isEmpty() && resourcePackFuture != null) { resourcePackFuture.complete(null); resourcePackFuture = null; } @@ -1506,17 +1535,14 @@ public void setPortalCooldown(int portalCooldown) { * * @param packet the packet to send */ - @ApiStatus.Experimental public void sendPacket(@NotNull SendablePacket packet) { this.playerConnection.sendPacket(packet); } - @ApiStatus.Experimental public void sendPackets(@NotNull SendablePacket... packets) { this.playerConnection.sendPackets(packets); } - @ApiStatus.Experimental public void sendPackets(@NotNull Collection packets) { this.playerConnection.sendPackets(packets); } @@ -1545,7 +1571,7 @@ public boolean isOnline() { * @return the player current dimension */ public DimensionType getDimensionType() { - return dimensionType; + return DIMENSION_TYPE_REGISTRY.get(dimensionTypeId); } public @NotNull PlayerInventory getInventory() { @@ -1630,26 +1656,17 @@ public boolean setGameMode(@NotNull GameMode gameMode) { return true; } - /** - * Gets if this player is in creative. Used for code readability. - * - * @return true if the player is in creative mode - */ - public boolean isCreative() { - return gameMode == GameMode.CREATIVE; - } - /** * Changes the dimension of the player. * Mostly unsafe since it requires sending chunks after. * * @param dimensionType the new player dimension */ - protected void sendDimension(@NotNull DimensionType dimensionType, @NotNull String dimensionName) { + protected void sendDimension(@NotNull DynamicRegistry.Key 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.toString(), dimensionName, + this.dimensionTypeId = DIMENSION_TYPE_REGISTRY.getId(dimensionType); + sendPacket(new RespawnPacket(dimensionTypeId, dimensionName, 0, gameMode, gameMode, false, levelFlat, deathLocation, portalCooldown, RespawnPacket.COPY_ALL)); refreshClientStateAfterRespawn(); @@ -1768,14 +1785,9 @@ public void closeInventory(boolean fromClient) { Inventory openInventory = getOpenInventory(); // Drop cursor item when closing inventory - ItemStack cursorItem; - if (openInventory == null) { - cursorItem = getInventory().getCursorItem(); - getInventory().setCursorItem(ItemStack.AIR); - } else { - cursorItem = openInventory.getCursorItem(this); - openInventory.setCursorItem(this, ItemStack.AIR); - } + ItemStack cursorItem = getInventory().getCursorItem(); + getInventory().setCursorItem(ItemStack.AIR); + if (!cursorItem.isAir()) { // Add item to inventory if he hasn't been able to drop it if (!dropItem(cursorItem)) { @@ -1799,19 +1811,15 @@ public void closeInventory(boolean fromClient) { } /** - * Used internally to prevent an inventory click to be processed - * when the inventory listeners closed the inventory. - *

- * Should only be used within an inventory listener (event or condition). - * - * @return true if the inventory has been closed, false otherwise + * Used internally to determine when sending the close inventory packet should be skipped. */ public boolean didCloseInventory() { return didCloseInventory; } /** - * Used internally to reset the didCloseInventory field. + * Used internally to reset the skipClosePacket field, which determines when sending the close inventory packet + * should be skipped. *

* Shouldn't be used externally without proper understanding of its consequence. * @@ -1835,20 +1843,23 @@ public int getLastReceivedTeleportId() { } public void refreshReceivedTeleportId(int receivedTeleportId) { + if (receivedTeleportId < 0) return; this.receivedTeleportId = receivedTeleportId; } /** * Used to synchronize player position with viewers on spawn or after {@link Entity#teleport(Pos, long[], int)} - * in cases where a {@link PlayerPositionAndLookPacket} is required + * in properties where a {@link PlayerPositionAndLookPacket} is required * - * @param position the position used by {@link PlayerPositionAndLookPacket} - * this may not be the same as the {@link Entity#position} + * @param position the position used by {@link PlayerPositionAndLookPacket} + * this may not be the same as the {@link Entity#position} * @param relativeFlags byte flags used by {@link PlayerPositionAndLookPacket} + * @param shouldConfirm if false, the teleportation will be done without confirmation */ @ApiStatus.Internal - void synchronizePositionAfterTeleport(@NotNull Pos position, int relativeFlags) { - sendPacket(new PlayerPositionAndLookPacket(position, (byte) relativeFlags, getNextTeleportId())); + void synchronizePositionAfterTeleport(@NotNull Pos position, int relativeFlags, boolean shouldConfirm) { + int teleportId = shouldConfirm ? getNextTeleportId() : -1; + sendPacket(new PlayerPositionAndLookPacket(position, (byte) relativeFlags, teleportId)); super.synchronizePosition(); } @@ -2120,7 +2131,6 @@ public void addPacketToQueue(@NotNull ClientPacket packet) { } @ApiStatus.Internal - @ApiStatus.Experimental public void interpretPacketQueue() { if (this.packets.size() >= ServerFlag.PLAYER_PACKET_QUEUE_SIZE) { kick(Component.text("Too Many Packets", NamedTextColor.RED)); @@ -2173,7 +2183,7 @@ public void refreshAnswerKeepAlive(boolean answerKeepAlive) { /** * Changes the held item for the player viewers - * Also cancel eating if {@link #isEating()} was true. + * Also cancel item usage if {@link #isUsingItem()} was true. *

* Warning: the player will not be noticed by this chance, only his viewers, * see instead: {@link #setHeldItemSlot(byte)}. @@ -2181,62 +2191,40 @@ public void refreshAnswerKeepAlive(boolean answerKeepAlive) { * @param slot the new held slot */ public void refreshHeldSlot(byte slot) { + byte oldHeldSlot = this.heldSlot; this.heldSlot = slot; syncEquipment(EquipmentSlot.MAIN_HAND); - refreshEating(null); + updateEquipmentAttributes(inventory.getItemStack(oldHeldSlot), inventory.getItemStack(this.heldSlot), EquipmentSlot.MAIN_HAND); + clearItemUse(); } - public void refreshEating(@Nullable Hand eatingHand, long eatingTime) { - this.eatingHand = eatingHand; - if (eatingHand != null) { - this.startEatingTime = System.currentTimeMillis(); - this.eatingTime = eatingTime; + public void refreshItemUse(@Nullable Hand itemUseHand, long itemUseTimeTicks) { + this.itemUseHand = itemUseHand; + if (itemUseHand != null) { + this.startItemUseTime = getAliveTicks(); + this.itemUseTime = itemUseTimeTicks; } else { - this.startEatingTime = 0; + this.startItemUseTime = 0; } } - public void refreshEating(@Nullable Hand eatingHand) { - refreshEating(eatingHand, defaultEatingTime); + public void clearItemUse() { + refreshItemUse(null, 0); } /** * Used to call {@link ItemUpdateStateEvent} with the proper item * It does check which hand to get the item to update. * - * @param allowFood true if food should be updated, false otherwise * @return the called {@link ItemUpdateStateEvent}, - * null if there is no item to update the state - * @deprecated Use {@link #callItemUpdateStateEvent(Hand)} instead */ - @Deprecated - public @Nullable ItemUpdateStateEvent callItemUpdateStateEvent(boolean allowFood, @Nullable Hand hand) { - if (hand == null) - return null; - - final ItemStack updatedItem = getItemInHand(hand); - final boolean isFood = updatedItem.material().isFood(); - - if (isFood && !allowFood) - return null; - - ItemUpdateStateEvent itemUpdateStateEvent = new ItemUpdateStateEvent(this, hand, updatedItem); + public @NotNull ItemUpdateStateEvent callItemUpdateStateEvent(@NotNull Hand hand) { + ItemUpdateStateEvent itemUpdateStateEvent = new ItemUpdateStateEvent(this, hand, getItemInHand(hand)); EventDispatcher.call(itemUpdateStateEvent); return itemUpdateStateEvent; } - /** - * Used to call {@link ItemUpdateStateEvent} with the proper item - * It does check which hand to get the item to update. Allows food. - * - * @return the called {@link ItemUpdateStateEvent}, - * null if there is no item to update the state - */ - public @Nullable ItemUpdateStateEvent callItemUpdateStateEvent(@Nullable Hand hand) { - return callItemUpdateStateEvent(true, hand); - } - public void refreshVehicleSteer(float sideways, float forward, boolean jump, boolean unmount) { this.vehicleInformation.refresh(sideways, forward, jump, unmount); } @@ -2252,7 +2240,7 @@ public long getLastKeepAlive() { @Override public @NotNull HoverEvent asHoverEvent(@NotNull UnaryOperator op) { - return HoverEvent.showEntity(ShowEntity.showEntity(EntityType.PLAYER, this.uuid, this.displayName)); + return HoverEvent.showEntity(ShowEntity.showEntity(EntityType.PLAYER, getUuid(), this.displayName)); } /** @@ -2396,13 +2384,6 @@ public void setLocale(@Nullable Locale locale) { return this.pointers; } - @Override - public void setUuid(@NotNull UUID uuid) { - super.setUuid(uuid); - // update identity - this.identity = Identity.identity(uuid); - } - protected void sendChunkUpdates(Chunk newChunk) { if (chunkUpdateLimitChecker.addToHistory(newChunk)) { final int newX = newChunk.getChunkX(); @@ -2548,7 +2529,7 @@ public void refresh(String locale, byte viewDistance, ChatMessageType chatMessag this.allowServerListings = allowServerListings; boolean isInPlayState = getPlayerConnection().getConnectionState() == ConnectionState.PLAY; - PlayerMeta playerMeta = getUnsafeEntityMeta(); + PlayerMeta playerMeta = getPlayerMeta(); if (isInPlayState) playerMeta.setNotifyAboutChanges(false); playerMeta.setDisplayedSkinParts(displayedSkinParts); playerMeta.setRightMainHand(this.mainHand == MainHand.RIGHT); @@ -2567,4 +2548,10 @@ private int compareChunkDistance(long chunkIndexA, long chunkIndexB) { return Integer.compare(chunkDistanceA, chunkDistanceB); } + @SuppressWarnings("unchecked") + @ApiStatus.Experimental + @Override + public @NotNull Acquirable acquirable() { + return (Acquirable) super.acquirable(); + } } diff --git a/src/main/java/net/minestom/server/entity/PlayerProjectile.java b/src/main/java/net/minestom/server/entity/PlayerProjectile.java index 79230ffeb04..6c5a6be25bd 100644 --- a/src/main/java/net/minestom/server/entity/PlayerProjectile.java +++ b/src/main/java/net/minestom/server/entity/PlayerProjectile.java @@ -14,13 +14,15 @@ import net.minestom.server.event.entity.projectile.ProjectileCollideWithEntityEvent; import net.minestom.server.instance.Instance; import net.minestom.server.instance.block.Block; +import net.minestom.server.thread.Acquirable; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import java.util.Random; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ThreadLocalRandom; -public class PlayerProjectile extends LivingEntity { +public class PlayerProjectile extends Entity { private final Entity shooter; private long cooldown = 0; @@ -112,9 +114,9 @@ private Pos checkInsideBlock(@NotNull Instance instance) { while (iterator.hasNext()) { var block = iterator.next(); - Block b = instance.getBlock(block); - var hit = b.registry().collisionShape().intersectBox(this.getPosition().sub(block), this.getBoundingBox()); - if (hit) return Pos.fromPoint(block); + Block b = instance.getBlock(block.blockX(), block.blockY(), block.blockZ()); + var hit = b.registry().collisionShape().intersectBox(this.getPosition().sub(block.x(), block.y(), block.z()), this.getBoundingBox()); + if (hit) return new Pos(block.blockX(), block.blockY(), block.blockZ()); } return null; @@ -128,6 +130,8 @@ public void refreshPosition(@NotNull Pos newPosition) { public void tick(long time) { final Pos posBefore = getPosition(); super.tick(time); + if (super.isRemoved()) return; + final Pos posNow = getPosition(); Vec diff = Vec.fromPoint(posNow.sub(posBefore)); @@ -176,4 +180,11 @@ public void tick(long time) { MinecraftServer.getGlobalEventHandler().call(e); } } + + @ApiStatus.Experimental + @SuppressWarnings("unchecked") + @Override + public @NotNull Acquirable acquirable() { + return (Acquirable) super.acquirable(); + } } diff --git a/src/main/java/net/minestom/server/entity/ai/goal/FollowTargetGoal.java b/src/main/java/net/minestom/server/entity/ai/goal/FollowTargetGoal.java index f3cce651e29..647dd93e686 100644 --- a/src/main/java/net/minestom/server/entity/ai/goal/FollowTargetGoal.java +++ b/src/main/java/net/minestom/server/entity/ai/goal/FollowTargetGoal.java @@ -75,7 +75,7 @@ public void tick(long time) { return; } final Pos targetPos = entityCreature.getTarget() != null ? entityCreature.getTarget().getPosition() : null; - if (targetPos != null && !targetPos.samePoint(lastTargetPos)) { + if (targetPos != null && !targetPos.sameBlock(lastTargetPos)) { this.lastUpdateTime = time; this.lastTargetPos = targetPos; this.entityCreature.getNavigator().setPathTo(targetPos); diff --git a/src/main/java/net/minestom/server/entity/attribute/Attribute.java b/src/main/java/net/minestom/server/entity/attribute/Attribute.java new file mode 100644 index 00000000000..de52d5156ba --- /dev/null +++ b/src/main/java/net/minestom/server/entity/attribute/Attribute.java @@ -0,0 +1,63 @@ +package net.minestom.server.entity.attribute; + +import net.minestom.server.network.NetworkBuffer; +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.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +public sealed interface Attribute extends StaticProtocolObject, Attributes permits AttributeImpl { + @NotNull NetworkBuffer.Type NETWORK_TYPE = NetworkBuffer.VAR_INT.map(AttributeImpl::getId, Attribute::id); + @NotNull BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.STRING.map(AttributeImpl::get, Attribute::name); + + @Contract(pure = true) + @NotNull Registry.AttributeEntry registry(); + + @Override + default @NotNull NamespaceID namespace() { + return registry().namespace(); + } + + @Override + default int id() { + return registry().id(); + } + + default double defaultValue() { + return registry().defaultValue(); + } + + default double minValue() { + return registry().minValue(); + } + + default double maxValue() { + return registry().maxValue(); + } + + default boolean isSynced() { + return registry().clientSync(); + } + + static @NotNull Collection<@NotNull Attribute> values() { + return AttributeImpl.values(); + } + + static @Nullable Attribute fromNamespaceId(@NotNull String namespaceID) { + return AttributeImpl.getSafe(namespaceID); + } + + static @Nullable Attribute fromNamespaceId(@NotNull NamespaceID namespaceID) { + return fromNamespaceId(namespaceID.asString()); + } + + static @Nullable Attribute fromId(int id) { + return AttributeImpl.getId(id); + } + +} diff --git a/src/main/java/net/minestom/server/entity/attribute/AttributeImpl.java b/src/main/java/net/minestom/server/entity/attribute/AttributeImpl.java new file mode 100644 index 00000000000..89ba36f58f9 --- /dev/null +++ b/src/main/java/net/minestom/server/entity/attribute/AttributeImpl.java @@ -0,0 +1,32 @@ +package net.minestom.server.entity.attribute; + +import net.minestom.server.registry.Registry; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +record AttributeImpl(@NotNull Registry.AttributeEntry registry) implements Attribute { + private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.ATTRIBUTES, + (namespace, properties) -> new AttributeImpl(Registry.attribute(namespace, properties))); + + static Attribute get(@NotNull String namespace) { + return CONTAINER.get(namespace); + } + + static Attribute getSafe(@NotNull String namespace) { + return CONTAINER.getSafe(namespace); + } + + static Attribute getId(int id) { + return CONTAINER.getId(id); + } + + static Collection values() { + return CONTAINER.values(); + } + + @Override + public String toString() { + return name(); + } +} diff --git a/src/main/java/net/minestom/server/entity/attribute/AttributeInstance.java b/src/main/java/net/minestom/server/entity/attribute/AttributeInstance.java new file mode 100644 index 00000000000..c55f0ff532c --- /dev/null +++ b/src/main/java/net/minestom/server/entity/attribute/AttributeInstance.java @@ -0,0 +1,209 @@ +package net.minestom.server.entity.attribute; + +import net.minestom.server.entity.LivingEntity; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.NamespaceID; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnmodifiableView; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Consumer; + +/** + * Represents an instance of an attribute and its modifiers. This class is thread-safe (you do not need to acquire the + * entity to modify its attributes). + */ +public final class AttributeInstance { + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, AttributeInstance value) { + buffer.write(Attribute.NETWORK_TYPE, value.attribute()); + buffer.write(NetworkBuffer.DOUBLE, value.getBaseValue()); + buffer.writeCollection(AttributeModifier.NETWORK_TYPE, value.modifiers()); + } + + @Override + public AttributeInstance read(@NotNull NetworkBuffer buffer) { + return new AttributeInstance(buffer.read(Attribute.NETWORK_TYPE), buffer.read(NetworkBuffer.DOUBLE), + buffer.readCollection(AttributeModifier.NETWORK_TYPE, Short.MAX_VALUE), null); + } + }; + + private final Attribute attribute; + private final Map modifiers; + private final Collection unmodifiableModifiers; + private final AtomicLong baseValueBits; + + private final Consumer propertyChangeListener; + private volatile double cachedValue = 0.0D; + + public AttributeInstance(@NotNull Attribute attribute, @Nullable Consumer listener) { + this(attribute, attribute.defaultValue(), new ArrayList<>(), listener); + } + + public AttributeInstance(@NotNull Attribute attribute, double baseValue, @NotNull Collection modifiers, @Nullable Consumer listener) { + this.attribute = attribute; + this.modifiers = new ConcurrentHashMap<>(); + for (var modifier : modifiers) this.modifiers.put(modifier.id(), modifier); + this.unmodifiableModifiers = Collections.unmodifiableCollection(this.modifiers.values()); + this.baseValueBits = new AtomicLong(Double.doubleToLongBits(baseValue)); + + this.propertyChangeListener = listener; + refreshCachedValue(baseValue); + } + + /** + * Gets the attribute associated to this instance. + * + * @return the associated attribute + */ + public @NotNull Attribute attribute() { + return attribute; + } + + /** + * The base value of this instance without modifiers + * + * @return the instance base value + * @see #setBaseValue(double) + */ + public double getBaseValue() { + return Double.longBitsToDouble(baseValueBits.get()); + } + + /** + * Sets the base value of this instance. + * + * @param baseValue the new base value + * @see #getBaseValue() + */ + public void setBaseValue(double baseValue) { + long newBits = Double.doubleToLongBits(baseValue); + long oldBits = this.baseValueBits.getAndSet(newBits); + if (oldBits != newBits) { + refreshCachedValue(baseValue); + } + } + + /** + * Get the modifiers applied to this instance. + * + * @return an immutable collection of the modifiers applied to this attribute. + */ + @NotNull + @UnmodifiableView + public Collection modifiers() { + return unmodifiableModifiers; + } + + /** + * Add a modifier to this instance. + * + * @param modifier the modifier to add + * @return the old modifier, or null if none + */ + public AttributeModifier addModifier(@NotNull AttributeModifier modifier) { + final AttributeModifier previousModifier = modifiers.put(modifier.id(), modifier); + if (!modifier.equals(previousModifier)) refreshCachedValue(getBaseValue()); + return previousModifier; + } + + /** + * Remove a modifier from this instance. + * + * @param modifier the modifier to remove + * @return the modifier that was removed, or null if none + */ + public AttributeModifier removeModifier(@NotNull AttributeModifier modifier) { + return removeModifier(modifier.id()); + } + + /** + * Clears all modifiers on this instance, excepting those whose ID is defined in + * {@link LivingEntity#PROTECTED_MODIFIERS}. + */ + public void clearModifiers() { + this.modifiers.values().removeIf(modifier -> !LivingEntity.PROTECTED_MODIFIERS.contains(modifier.id())); + refreshCachedValue(getBaseValue()); + } + + /** + * Remove a modifier from this instance. + * + * @param id The namespace id of the modifier to remove + * @return the modifier that was removed, or null if none + */ + public AttributeModifier removeModifier(@NotNull NamespaceID id) { + final AttributeModifier removed = modifiers.remove(id); + if (removed != null) { + refreshCachedValue(getBaseValue()); + } + + return removed; + } + + /** + * Gets the value of this instance calculated with modifiers applied. + * + * @return the attribute value + */ + public double getValue() { + return cachedValue; + } + + /** + * Gets the value of this instance, calculated assuming the given {@code baseValue}. + * + * @param baseValue the value to be used as the base for this operation, rather than this instance's normal base + * value + * @return the attribute value + */ + public double applyModifiers(double baseValue) { + return computeValue(baseValue); + } + + private double computeValue(double base) { + final Collection modifiers = modifiers(); + + for (var modifier : modifiers.stream().filter(mod -> mod.operation() == AttributeOperation.ADD_VALUE).toArray(AttributeModifier[]::new)) { + base += modifier.amount(); + } + + double result = base; + + for (var modifier : modifiers.stream().filter(mod -> mod.operation() == AttributeOperation.MULTIPLY_BASE).toArray(AttributeModifier[]::new)) { + result += (base * modifier.amount()); + } + for (var modifier : modifiers.stream().filter(mod -> mod.operation() == AttributeOperation.MULTIPLY_TOTAL).toArray(AttributeModifier[]::new)) { + result *= (1.0f + modifier.amount()); + } + + return Math.clamp(result, getAttribute().minValue(), getAttribute().maxValue()); + } + + /** + * Recalculate the value of this attribute instance using the modifiers. + */ + private void refreshCachedValue(double baseValue) { + this.cachedValue = computeValue(baseValue); + + // Signal entity + if (propertyChangeListener != null) { + propertyChangeListener.accept(this); + } + } + + @Deprecated + @NotNull + public Collection getModifiers() { + return modifiers(); + } + + @Deprecated + public @NotNull Attribute getAttribute() { + return attribute; + } +} diff --git a/src/main/java/net/minestom/server/entity/attribute/AttributeModifier.java b/src/main/java/net/minestom/server/entity/attribute/AttributeModifier.java new file mode 100644 index 00000000000..dd2cfba9e12 --- /dev/null +++ b/src/main/java/net/minestom/server/entity/attribute/AttributeModifier.java @@ -0,0 +1,48 @@ +package net.minestom.server.entity.attribute; + +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.NamespaceID; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +/** + * Represent an attribute modifier. + */ +public record AttributeModifier(@NotNull NamespaceID id, double amount, @NotNull AttributeOperation operation) { + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, AttributeModifier value) { + buffer.write(NetworkBuffer.STRING, value.id.asString()); + buffer.write(NetworkBuffer.DOUBLE, value.amount); + buffer.write(AttributeOperation.NETWORK_TYPE, value.operation); + } + + @Override + public AttributeModifier read(@NotNull NetworkBuffer buffer) { + return new AttributeModifier(NamespaceID.from(buffer.read(NetworkBuffer.STRING)), + buffer.read(NetworkBuffer.DOUBLE), buffer.read(AttributeOperation.NETWORK_TYPE)); + } + }; + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> new AttributeModifier(NamespaceID.from(tag.getString("id")), tag.getDouble("amount"), + AttributeOperation.NBT_TYPE.read(tag.get("operation"))), + value -> CompoundBinaryTag.builder() + .putString("id", value.id.asString()) + .putDouble("amount", value.amount) + .put("operation", AttributeOperation.NBT_TYPE.write(value.operation)) + .build() + ); + + /** + * Creates a new modifier with a random id. + * + * @param id the (namespace) id of this modifier + * @param amount the value of this modifier + * @param operation the operation to apply this modifier with + */ + public AttributeModifier(@NotNull String id, double amount, @NotNull AttributeOperation operation) { + this(NamespaceID.from(id), amount, operation); + } + +} diff --git a/src/main/java/net/minestom/server/attribute/AttributeOperation.java b/src/main/java/net/minestom/server/entity/attribute/AttributeOperation.java similarity index 50% rename from src/main/java/net/minestom/server/attribute/AttributeOperation.java rename to src/main/java/net/minestom/server/entity/attribute/AttributeOperation.java index 33da291c7f5..c19da8b12a6 100644 --- a/src/main/java/net/minestom/server/attribute/AttributeOperation.java +++ b/src/main/java/net/minestom/server/entity/attribute/AttributeOperation.java @@ -1,13 +1,18 @@ -package net.minestom.server.attribute; +package net.minestom.server.entity.attribute; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.nbt.BinaryTagSerializer; import org.jetbrains.annotations.Nullable; public enum AttributeOperation { - ADDITION(0), + ADD_VALUE(0), MULTIPLY_BASE(1), MULTIPLY_TOTAL(2); - private static final AttributeOperation[] VALUES = new AttributeOperation[]{ADDITION, MULTIPLY_BASE, MULTIPLY_TOTAL}; + public static final NetworkBuffer.Type NETWORK_TYPE = NetworkBuffer.Enum(AttributeOperation.class); + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.fromEnumStringable(AttributeOperation.class); + + private static final AttributeOperation[] VALUES = new AttributeOperation[]{ADD_VALUE, MULTIPLY_BASE, MULTIPLY_TOTAL}; private final int id; AttributeOperation(int id) { diff --git a/src/main/java/net/minestom/server/entity/damage/Damage.java b/src/main/java/net/minestom/server/entity/damage/Damage.java index 37b86d76c83..fdaab6bb4ec 100644 --- a/src/main/java/net/minestom/server/entity/damage/Damage.java +++ b/src/main/java/net/minestom/server/entity/damage/Damage.java @@ -1,13 +1,16 @@ package net.minestom.server.entity.damage; import net.kyori.adventure.text.Component; +import net.minestom.server.MinecraftServer; import net.minestom.server.coordinate.Point; import net.minestom.server.entity.Entity; import net.minestom.server.entity.LivingEntity; import net.minestom.server.entity.Player; +import net.minestom.server.registry.DynamicRegistry; import net.minestom.server.sound.SoundEvent; import net.minestom.server.tag.TagHandler; import net.minestom.server.tag.Taggable; +import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -18,7 +21,9 @@ * This class can be extended if you need to include custom fields and/or methods. */ public class Damage implements Taggable { + private static final DynamicRegistry DAMAGE_TYPE_REGISTRY = MinecraftServer.getDamageTypeRegistry(); + private final DynamicRegistry.Key typeKey; private final DamageType type; private final Entity source; private final Entity attacker; @@ -30,11 +35,16 @@ public class Damage implements Taggable { /** * Creates a new damage type. * + * @param attacker The attacker that initiated this damage + * @param source The source of the damage. For direct hits (melee), this will be the same as the attacker. For indirect hits (projectiles), this will be the projectile * @param type the type of this damage * @param amount amount of damage + * @param sourcePosition The position of the source of damage */ - public Damage(@NotNull DamageType type, @Nullable Entity source, @Nullable Entity attacker, @Nullable Point sourcePosition, float amount) { - this.type = type; + public Damage(@NotNull DynamicRegistry.Key type, @Nullable Entity source, @Nullable Entity attacker, @Nullable Point sourcePosition, float amount) { + this.typeKey = type; + this.type = DAMAGE_TYPE_REGISTRY.get(type); + Check.argCondition(this.type == null, "Damage type is not registered: {0}", type); this.source = source; this.attacker = attacker; this.sourcePosition = sourcePosition; @@ -44,14 +54,20 @@ public Damage(@NotNull DamageType type, @Nullable Entity source, @Nullable Entit /** * Gets the type of this damage. *

- * It does not have to be unique to this object.o + * It does not have to be unique to this object. * * @return the damage type */ - public @NotNull DamageType getType() { - return type; + public @NotNull DynamicRegistry.Key getType() { + return typeKey; } + /** + * Gets the integer id of the damage type that has been set + * @return The integer id of the damage type + */ + public int getTypeId() { return DAMAGE_TYPE_REGISTRY.getId(typeKey); } + /** * Gets the "attacker" of the damage. * This is the indirect cause of the damage, like the shooter of a projectile, or null if there was none. @@ -128,7 +144,7 @@ public Damage(@NotNull DamageType type, @Nullable Entity source, @Nullable Entit return new EntityDamage(entity, amount); } - public static @NotNull PositionalDamage fromPosition(@NotNull DamageType type, @NotNull Point sourcePosition, float amount) { + public static @NotNull PositionalDamage fromPosition(@NotNull DynamicRegistry.Key type, @NotNull Point sourcePosition, float amount) { return new PositionalDamage(type, sourcePosition, amount); } @@ -160,7 +176,7 @@ protected SoundEvent getGenericSound(@NotNull LivingEntity entity) { } protected SoundEvent getPlayerSound(@NotNull Player player) { - if (type == DamageType.ON_FIRE) return SoundEvent.ENTITY_PLAYER_HURT_ON_FIRE; + if (DamageType.ON_FIRE.equals(typeKey)) return SoundEvent.ENTITY_PLAYER_HURT_ON_FIRE; return SoundEvent.ENTITY_PLAYER_HURT; } diff --git a/src/main/java/net/minestom/server/entity/damage/DamageType.java b/src/main/java/net/minestom/server/entity/damage/DamageType.java index 6f6fec6fd58..206e41c709b 100644 --- a/src/main/java/net/minestom/server/entity/damage/DamageType.java +++ b/src/main/java/net/minestom/server/entity/damage/DamageType.java @@ -1,60 +1,77 @@ package net.minestom.server.entity.damage; -import net.minestom.server.registry.StaticProtocolObject; +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.Registries; import net.minestom.server.registry.Registry; -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 org.jglrxavpok.hephaistos.nbt.NBTCompound; -import java.util.Collection; +public sealed interface DamageType extends ProtocolObject, DamageTypes permits DamageTypeImpl { -public sealed interface DamageType extends StaticProtocolObject, DamageTypes permits DamageTypeImpl { - /** - * Returns the damage type registry. - * - * @return the damage type registry - */ - @Contract(pure = true) - @NotNull Registry.DamageTypeEntry registry(); + @NotNull BinaryTagSerializer> NBT_TYPE = BinaryTagSerializer.registryKey(Registries::damageType); - @Override - default @NotNull NamespaceID namespace() { - return registry().namespace(); + static @NotNull DamageType create( + float exhaustion, + @NotNull String messageId, + @NotNull String scaling + ) { + return new DamageTypeImpl(exhaustion, messageId, scaling, null); } - default double exhaustion() { - return registry().exhaustion(); + static @NotNull Builder builder() { + return new Builder(); } - default String messageId() { - return registry().messageId(); + /** + *

Creates a new registry for damage types, loading the vanilla damage types.

+ * + * @see net.minestom.server.MinecraftServer to get an existing instance of the registry + */ + @ApiStatus.Internal + static @NotNull DynamicRegistry createDefaultRegistry() { + return DynamicRegistry.create( + "minecraft:damage_type", DamageTypeImpl.REGISTRY_NBT_TYPE, Registry.Resource.DAMAGE_TYPES, + (namespace, props) -> new DamageTypeImpl(Registry.damageType(namespace, props)) + ); } - default String scaling() { - return registry().scaling(); - } + float exhaustion(); - NBTCompound asNBT(); + @NotNull String messageId(); - static @NotNull Collection<@NotNull DamageType> values() { - return DamageTypeImpl.values(); - } + @NotNull String scaling(); - static DamageType fromNamespaceId(@NotNull String namespaceID) { - return DamageTypeImpl.getSafe(namespaceID); - } + @Nullable Registry.DamageTypeEntry registry(); - static DamageType fromNamespaceId(@NotNull NamespaceID namespaceID) { - return fromNamespaceId(namespaceID.asString()); - } + final class Builder { + private float exhaustion = 0f; + private String messageId; + private String scaling; - static @Nullable DamageType fromId(int id) { - return DamageTypeImpl.getId(id); - } + private Builder() { + } + + public @NotNull Builder exhaustion(float exhaustion) { + this.exhaustion = exhaustion; + return this; + } - static NBTCompound getNBT() { - return DamageTypeImpl.getNBT(); + 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(exhaustion, messageId, scaling, null); + } } + } \ No newline at end of file diff --git a/src/main/java/net/minestom/server/entity/damage/DamageTypeImpl.java b/src/main/java/net/minestom/server/entity/damage/DamageTypeImpl.java index 03c8e132e01..7a70e267fa2 100644 --- a/src/main/java/net/minestom/server/entity/damage/DamageTypeImpl.java +++ b/src/main/java/net/minestom/server/entity/damage/DamageTypeImpl.java @@ -1,80 +1,38 @@ package net.minestom.server.entity.damage; +import net.kyori.adventure.nbt.CompoundBinaryTag; import net.minestom.server.registry.Registry; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.NotNull; -import org.jglrxavpok.hephaistos.nbt.NBT; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; -import org.jglrxavpok.hephaistos.nbt.NBTType; +import org.jetbrains.annotations.Nullable; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; +record DamageTypeImpl( + 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 AtomicInteger INDEX = new AtomicInteger(); - private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.DAMAGE_TYPES, DamageTypeImpl::createImpl); + static final BinaryTagSerializer REGISTRY_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() + ); - private DamageTypeImpl(Registry.DamageTypeEntry registry) { - this(registry, INDEX.getAndIncrement()); + @SuppressWarnings("ConstantValue") // The builder can violate the nullability constraints + DamageTypeImpl { + Check.argCondition(messageId == null || messageId.isEmpty(), "missing message id"); + Check.argCondition(scaling == null || scaling.isEmpty(), "missing scaling"); } - private static DamageType createImpl(String namespace, Registry.Properties properties) { - return new DamageTypeImpl(Registry.damageType(namespace, properties)); + DamageTypeImpl(@NotNull Registry.DamageTypeEntry registry) { + this(registry.exhaustion(), registry.messageId(), registry.scaling(), registry); } - static DamageType get(@NotNull String namespace) { - return CONTAINER.get(namespace); - } - - static DamageType getSafe(@NotNull String namespace) { - return CONTAINER.getSafe(namespace); - } - - static DamageType getId(int id) { - return CONTAINER.getId(id); - } - - @Override - public NBTCompound asNBT() { - var elem = new HashMap(); - elem.put("exhaustion", NBT.Float(registry.exhaustion())); - elem.put("message_id", NBT.String(registry.messageId())); - elem.put("scaling", NBT.String(registry.scaling())); - return NBT.Compound(elem); - } - - static Collection values() { - return CONTAINER.values(); - } - - @Override - public String toString() { - return name(); - } - - @Override - public int id() { - return id; - } - - private static NBTCompound lazyNbt = null; - - static NBTCompound getNBT() { - if (lazyNbt == null) { - var damageTypes = values().stream() - .map((damageType) -> NBT.Compound(Map.of( - "id", NBT.Int(damageType.id()), - "name", NBT.String(damageType.name()), - "element", damageType.asNBT() - ))) - .toList(); - - lazyNbt = NBT.Compound(Map.of( - "type", NBT.String("minecraft:damage_type"), - "value", NBT.List(NBTType.TAG_Compound, damageTypes) - )); - } - return lazyNbt; - } } \ No newline at end of file diff --git a/src/main/java/net/minestom/server/entity/damage/PositionalDamage.java b/src/main/java/net/minestom/server/entity/damage/PositionalDamage.java index 12c414bac9b..8e11f6c4869 100644 --- a/src/main/java/net/minestom/server/entity/damage/PositionalDamage.java +++ b/src/main/java/net/minestom/server/entity/damage/PositionalDamage.java @@ -1,6 +1,7 @@ package net.minestom.server.entity.damage; import net.minestom.server.coordinate.Point; +import net.minestom.server.registry.DynamicRegistry; import org.jetbrains.annotations.NotNull; /** @@ -8,12 +9,8 @@ */ public class PositionalDamage extends Damage { - public PositionalDamage(@NotNull DamageType type, @NotNull Point sourcePosition, float amount) { + public PositionalDamage(@NotNull DynamicRegistry.Key type, @NotNull Point sourcePosition, float amount) { super(type, null, null, sourcePosition, amount); } - @Override - public @NotNull Point getSourcePosition() { - return super.getSourcePosition(); - } } \ No newline at end of file diff --git a/src/main/java/net/minestom/server/entity/metadata/AgeableMobMeta.java b/src/main/java/net/minestom/server/entity/metadata/AgeableMobMeta.java index 6dc860d76c4..780b2e640bd 100644 --- a/src/main/java/net/minestom/server/entity/metadata/AgeableMobMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/AgeableMobMeta.java @@ -3,13 +3,14 @@ import net.minestom.server.collision.BoundingBox; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class AgeableMobMeta extends PathfinderMobMeta { public static final byte OFFSET = PathfinderMobMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - protected AgeableMobMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected AgeableMobMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/EntityMeta.java b/src/main/java/net/minestom/server/entity/metadata/EntityMeta.java index c1a4ae454e2..11aa4444a50 100644 --- a/src/main/java/net/minestom/server/entity/metadata/EntityMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/EntityMeta.java @@ -3,6 +3,7 @@ import net.kyori.adventure.text.Component; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -24,9 +25,9 @@ public class EntityMeta { //Microtus end - update java keyword usage private final WeakReference entityRef; - protected final Metadata metadata; + protected final MetadataHolder metadata; - public EntityMeta(@Nullable Entity entity, @NotNull Metadata metadata) { + public EntityMeta(@Nullable Entity entity, @NotNull MetadataHolder metadata) { this.entityRef = new WeakReference<>(entity); this.metadata = metadata; } diff --git a/src/main/java/net/minestom/server/entity/metadata/LivingEntityMeta.java b/src/main/java/net/minestom/server/entity/metadata/LivingEntityMeta.java index 1640a25fa4f..193b144cebd 100644 --- a/src/main/java/net/minestom/server/entity/metadata/LivingEntityMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/LivingEntityMeta.java @@ -3,10 +3,14 @@ import net.minestom.server.coordinate.Point; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.Player; +import net.minestom.server.particle.Particle; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.List; + public class LivingEntityMeta extends EntityMeta { public static final byte OFFSET = EntityMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 7; @@ -17,7 +21,7 @@ public class LivingEntityMeta extends EntityMeta { private static final byte IS_IN_SPIN_ATTACK_BIT = 0x04; //Microtus end - update java keyword usage - protected LivingEntityMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected LivingEntityMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } @@ -54,12 +58,12 @@ public void setHealth(float value) { super.metadata.setIndex(OFFSET + 1, Metadata.Float(value)); } - public int getPotionEffectColor() { - return super.metadata.getIndex(OFFSET + 2, 0); + public @NotNull List getEffectParticles() { + return super.metadata.getIndex(OFFSET + 2, List.of()); } - public void setPotionEffectColor(int value) { - super.metadata.setIndex(OFFSET + 2, Metadata.VarInt(value)); + public void setEffectParticles(@NotNull List value) { + super.metadata.setIndex(OFFSET + 2, Metadata.ParticleList(value)); } public boolean isPotionEffectAmbient() { @@ -78,30 +82,9 @@ public void setArrowCount(int value) { super.metadata.setIndex(OFFSET + 4, Metadata.VarInt(value)); } - /** - * @deprecated - * This returns the bee stinger count, not the absorption heart count - * Use {@link #getBeeStingerCount()} instead - * @return The number of bee stingers in this entity - */ - @Deprecated - public int getHealthAddedByAbsorption() { - return super.metadata.getIndex(OFFSET + 5, 0); - } - - /** - * @deprecated - * This sets the bee stinger count, not the absorption heart count - * Use {@link #setBeeStingerCount(int)} instead - * @param value The number of bee stingers for this entity to have - */ - @Deprecated - public void setHealthAddedByAbsorption(int value) { - super.metadata.setIndex(OFFSET + 5, Metadata.VarInt(value)); - } - /** * Gets the amount of bee stingers in this entity + * * @return The amount of bee stingers */ public int getBeeStingerCount() { @@ -110,6 +93,7 @@ public int getBeeStingerCount() { /** * Sets the amount of bee stingers in this entity + * * @param value The amount of bee stingers to set, use 0 to clear all stingers */ public void setBeeStingerCount(int value) { @@ -122,7 +106,7 @@ public Point getBedInWhichSleepingPosition() { } public void setBedInWhichSleepingPosition(@Nullable Point value) { - super.metadata.setIndex(OFFSET + 6, Metadata.OptPosition(value)); + super.metadata.setIndex(OFFSET + 6, Metadata.OptBlockPosition(value)); } } diff --git a/src/main/java/net/minestom/server/entity/metadata/MobMeta.java b/src/main/java/net/minestom/server/entity/metadata/MobMeta.java index bb823ab1ad0..47d99a20a6d 100644 --- a/src/main/java/net/minestom/server/entity/metadata/MobMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/MobMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class MobMeta extends LivingEntityMeta { @@ -14,7 +14,7 @@ public class MobMeta extends LivingEntityMeta { private static final byte IS_AGGRESSIVE_BIT = 0x04; //Microtus end - update java keyword usage - protected MobMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected MobMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/PathfinderMobMeta.java b/src/main/java/net/minestom/server/entity/metadata/PathfinderMobMeta.java index 954cf0b0163..c4d091acccf 100644 --- a/src/main/java/net/minestom/server/entity/metadata/PathfinderMobMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/PathfinderMobMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class PathfinderMobMeta extends MobMeta { public static final byte OFFSET = MobMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - protected PathfinderMobMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected PathfinderMobMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/PlayerMeta.java b/src/main/java/net/minestom/server/entity/metadata/PlayerMeta.java index b067f114fbb..54ca81fe192 100644 --- a/src/main/java/net/minestom/server/entity/metadata/PlayerMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/PlayerMeta.java @@ -1,12 +1,12 @@ package net.minestom.server.entity.metadata; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jglrxavpok.hephaistos.nbt.NBT; - -import java.util.Map; public class PlayerMeta extends LivingEntityMeta { public static final byte OFFSET = LivingEntityMeta.MAX_OFFSET; @@ -22,7 +22,7 @@ public class PlayerMeta extends LivingEntityMeta { private static final byte HAT_BIT = 0x40; //Microtus end - update java keyword usage - public PlayerMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public PlayerMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } @@ -111,23 +111,23 @@ public void setRightMainHand(boolean value) { } @Nullable - public NBT getLeftShoulderEntityData() { + public BinaryTag getLeftShoulderEntityData() { return super.metadata.getIndex(OFFSET + 4, null); } - public void setLeftShoulderEntityData(@Nullable NBT value) { - if (value == null) value = NBT.Compound(Map.of()); + public void setLeftShoulderEntityData(@Nullable BinaryTag value) { + if (value == null) value = CompoundBinaryTag.empty(); super.metadata.setIndex(OFFSET + 4, Metadata.NBT(value)); } @Nullable - public NBT getRightShoulderEntityData() { + public BinaryTag getRightShoulderEntityData() { return super.metadata.getIndex(OFFSET + 5, null); } - public void setRightShoulderEntityData(@Nullable NBT value) { - if (value == null) value = NBT.Compound(Map.of()); + public void setRightShoulderEntityData(@Nullable BinaryTag value) { + if (value == null) value = CompoundBinaryTag.empty(); super.metadata.setIndex(OFFSET + 5, Metadata.NBT(value)); } diff --git a/src/main/java/net/minestom/server/entity/metadata/ambient/AmbientCreatureMeta.java b/src/main/java/net/minestom/server/entity/metadata/ambient/AmbientCreatureMeta.java index b44f13a6b38..427f8640d1a 100644 --- a/src/main/java/net/minestom/server/entity/metadata/ambient/AmbientCreatureMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/ambient/AmbientCreatureMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.ambient; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.MobMeta; import org.jetbrains.annotations.NotNull; @@ -9,7 +9,7 @@ public class AmbientCreatureMeta extends MobMeta { public static final byte OFFSET = MobMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - protected AmbientCreatureMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected AmbientCreatureMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/ambient/BatMeta.java b/src/main/java/net/minestom/server/entity/metadata/ambient/BatMeta.java index e722b13e990..4c2866d0950 100644 --- a/src/main/java/net/minestom/server/entity/metadata/ambient/BatMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/ambient/BatMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.ambient; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class BatMeta extends AmbientCreatureMeta { @@ -10,7 +10,7 @@ public class BatMeta extends AmbientCreatureMeta { private static final byte IS_HANGING_BIT = 0x01; //Microtus - update java keyword usage - public BatMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public BatMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/AbstractHorseMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/AbstractHorseMeta.java index dea9ed51f05..a42e6dd73fc 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/AbstractHorseMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/AbstractHorseMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.animal; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class AbstractHorseMeta extends AnimalMeta { @@ -17,7 +17,7 @@ public class AbstractHorseMeta extends AnimalMeta { private static final byte MOUTH_OPEN_BIT = 0x40; //Microtus end - update java keyword usage - protected AbstractHorseMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected AbstractHorseMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/AnimalMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/AnimalMeta.java index 06e15387ebd..854e05e4e73 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/AnimalMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/AnimalMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.animal; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.AgeableMobMeta; import org.jetbrains.annotations.NotNull; @@ -9,7 +9,7 @@ public class AnimalMeta extends AgeableMobMeta { public static final byte OFFSET = AgeableMobMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - protected AnimalMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected AnimalMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/ArmadilloMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/ArmadilloMeta.java new file mode 100644 index 00000000000..77c2db0385f --- /dev/null +++ b/src/main/java/net/minestom/server/entity/metadata/animal/ArmadilloMeta.java @@ -0,0 +1,34 @@ +package net.minestom.server.entity.metadata.animal; + +import net.minestom.server.entity.Entity; +import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; +import net.minestom.server.network.NetworkBuffer; +import org.jetbrains.annotations.NotNull; + +public class ArmadilloMeta extends AnimalMeta { + public static final byte OFFSET = AnimalMeta.MAX_OFFSET; + public static final byte MAX_OFFSET = OFFSET + 1; + + public ArmadilloMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { + super(entity, metadata); + } + + @NotNull + public State getState() { + return super.metadata.getIndex(OFFSET, State.IDLE); + } + + public void setState(@NotNull State value) { + super.metadata.setIndex(OFFSET, Metadata.ArmadilloState(value)); + } + + public enum State { + IDLE, + ROLLING, + SCARED, + UNROLLING; + + public static final NetworkBuffer.Type NETWORK_TYPE = NetworkBuffer.Enum(State.class); + } +} diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/BeeMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/BeeMeta.java index 957f5dd479b..9cbb7f62ecc 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/BeeMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/BeeMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class BeeMeta extends AnimalMeta { @@ -14,7 +15,7 @@ public class BeeMeta extends AnimalMeta { private static final byte HAS_NECTAR_BIT = 0x08; //Microtus end - update java keyword usage - public BeeMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public BeeMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/CamelMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/CamelMeta.java index 7f672d5508c..1796171befd 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/CamelMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/CamelMeta.java @@ -2,13 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class CamelMeta extends AbstractHorseMeta { public static final byte OFFSET = AbstractHorseMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - public CamelMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public CamelMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } @@ -25,6 +26,6 @@ public long getLastPoseChangeTick() { } public void setLastPoseChangeTick(long value) { - super.metadata.setIndex(OFFSET + 1, Metadata.Long(value)); + super.metadata.setIndex(OFFSET + 1, Metadata.VarLong(value)); } } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/ChestedHorseMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/ChestedHorseMeta.java index affef5089e7..5cc9fd1add6 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/ChestedHorseMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/ChestedHorseMeta.java @@ -2,13 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class ChestedHorseMeta extends AbstractHorseMeta { public static final byte OFFSET = AbstractHorseMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - protected ChestedHorseMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected ChestedHorseMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/ChickenMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/ChickenMeta.java index cf8b842889d..3b6863ba631 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/ChickenMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/ChickenMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.animal; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class ChickenMeta extends AnimalMeta { public static final byte OFFSET = AnimalMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public ChickenMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public ChickenMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/CowMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/CowMeta.java index 4e53db9ce96..b16ee6b4dad 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/CowMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/CowMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.animal; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class CowMeta extends AnimalMeta { public static final byte OFFSET = AnimalMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public CowMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public CowMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/DonkeyMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/DonkeyMeta.java index 3ea0eaa6374..c04935a8348 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/DonkeyMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/DonkeyMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.animal; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class DonkeyMeta extends ChestedHorseMeta { public static final byte OFFSET = AnimalMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public DonkeyMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public DonkeyMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/FoxMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/FoxMeta.java index 8f58b2e370a..84e6e3ef95a 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/FoxMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/FoxMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -21,7 +22,7 @@ public class FoxMeta extends AnimalMeta { private static final byte DEFENDING_BIT = (byte) 0x80; //Microtus end - update java keyword usage - public FoxMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public FoxMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/FrogMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/FrogMeta.java index f8ebe95a2eb..5110d383c02 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/FrogMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/FrogMeta.java @@ -2,6 +2,8 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; +import net.minestom.server.network.NetworkBuffer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -9,7 +11,7 @@ public class FrogMeta extends AnimalMeta { public static final byte OFFSET = AnimalMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 2; - public FrogMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public FrogMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } @@ -60,17 +62,6 @@ public enum Variant { WARM, COLD; - private static final FrogMeta.Variant[] VALUES = values(); - - // Microtus start - meta update - /** - * Add method to get a variant from a frog over the ordinal id - * @param id the ordinal id - * @return the entry which matches with the id - */ - public static @Nullable FrogMeta.Variant getVariant(int id) { - return id >= 0 && id <= VALUES.length ? VALUES[id] : null; - } - // Microtus end - meta update + public static final NetworkBuffer.Type NETWORK_TYPE = NetworkBuffer.Enum(Variant.class); } } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/GoatMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/GoatMeta.java index 6d78f6cf69a..12f60559f10 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/GoatMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/GoatMeta.java @@ -2,13 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class GoatMeta extends AnimalMeta { public static final byte OFFSET = AnimalMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 3; - public GoatMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public GoatMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/HoglinMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/HoglinMeta.java index 8fb29d6efab..9de163b1ad1 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/HoglinMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/HoglinMeta.java @@ -2,13 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class HoglinMeta extends AnimalMeta { public static final byte OFFSET = AnimalMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - public HoglinMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public HoglinMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/HorseMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/HorseMeta.java index 12d816e5e24..21a2c88904a 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/HorseMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/HorseMeta.java @@ -2,13 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class HorseMeta extends AbstractHorseMeta { public static final byte OFFSET = AbstractHorseMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - public HorseMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public HorseMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/LlamaMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/LlamaMeta.java index e506582b0c6..62ec398ea79 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/LlamaMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/LlamaMeta.java @@ -2,13 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class LlamaMeta extends ChestedHorseMeta { public static final byte OFFSET = ChestedHorseMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 3; - public LlamaMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public LlamaMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/MooshroomMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/MooshroomMeta.java index 9d7d115078a..43ec494af63 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/MooshroomMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/MooshroomMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; import java.util.Locale; @@ -10,7 +11,7 @@ public class MooshroomMeta extends CowMeta { public static final byte OFFSET = CowMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - public MooshroomMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public MooshroomMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/MuleMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/MuleMeta.java index 394a53181bb..0efddd3164d 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/MuleMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/MuleMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.animal; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class MuleMeta extends ChestedHorseMeta { public static final byte OFFSET = ChestedHorseMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public MuleMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public MuleMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/OcelotMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/OcelotMeta.java index 1b6621a8da5..fb6693f711d 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/OcelotMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/OcelotMeta.java @@ -2,13 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class OcelotMeta extends AnimalMeta { public static final byte OFFSET = AnimalMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - public OcelotMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public OcelotMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/PandaMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/PandaMeta.java index f18666a67d0..cbf91f3aaff 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/PandaMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/PandaMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class PandaMeta extends AnimalMeta { @@ -15,7 +16,7 @@ public class PandaMeta extends AnimalMeta { private static final byte ON_BACK_BIT = 0x10; //Microtus end - update java keyword usage - public PandaMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public PandaMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/PigMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/PigMeta.java index ffe7f905292..8a797fed61e 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/PigMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/PigMeta.java @@ -2,13 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class PigMeta extends AnimalMeta { public static final byte OFFSET = AnimalMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 2; - public PigMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public PigMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/PolarBearMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/PolarBearMeta.java index b465d2bbbfb..8587dfdfc8c 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/PolarBearMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/PolarBearMeta.java @@ -2,13 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class PolarBearMeta extends AnimalMeta { public static final byte OFFSET = AnimalMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - public PolarBearMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public PolarBearMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/RabbitMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/RabbitMeta.java index c384948e0ff..df12867acf7 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/RabbitMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/RabbitMeta.java @@ -2,13 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class RabbitMeta extends AnimalMeta { public static final byte OFFSET = AnimalMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - public RabbitMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public RabbitMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/SheepMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/SheepMeta.java index bc6bd54c017..ea127614ec9 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/SheepMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/SheepMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.animal; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class SheepMeta extends AnimalMeta { @@ -13,7 +13,7 @@ public class SheepMeta extends AnimalMeta { private static final byte SHEARED_BIT = 0x10; //Microtus end - update java keyword usage - public SheepMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public SheepMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/SkeletonHorseMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/SkeletonHorseMeta.java index 6c5af187cb2..a34a97bc2f5 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/SkeletonHorseMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/SkeletonHorseMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.animal; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class SkeletonHorseMeta extends AbstractHorseMeta { public static final byte OFFSET = AbstractHorseMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public SkeletonHorseMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public SkeletonHorseMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/SnifferMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/SnifferMeta.java index b1bf238ecbe..f8eda3b1e7e 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/SnifferMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/SnifferMeta.java @@ -2,6 +2,8 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; +import net.minestom.server.network.NetworkBuffer; import org.jetbrains.annotations.NotNull; public class SnifferMeta extends AnimalMeta { @@ -9,7 +11,7 @@ public class SnifferMeta extends AnimalMeta { public static final byte OFFSET = AnimalMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 2; - public SnifferMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public SnifferMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } @@ -38,6 +40,6 @@ public enum State { DIGGING, RISING; - private static final State[] VALUES = values(); + public static final NetworkBuffer.Type NETWORK_TYPE = NetworkBuffer.Enum(State.class); } } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/StriderMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/StriderMeta.java index 8a73e4042ab..f03bc6f3523 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/StriderMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/StriderMeta.java @@ -2,13 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class StriderMeta extends AnimalMeta { public static final byte OFFSET = AnimalMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 3; - public StriderMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public StriderMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/TurtleMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/TurtleMeta.java index 2f12487aeda..57e4fa21068 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/TurtleMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/TurtleMeta.java @@ -4,13 +4,14 @@ import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class TurtleMeta extends AnimalMeta { public static final byte OFFSET = AnimalMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 6; - public TurtleMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public TurtleMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } @@ -19,7 +20,7 @@ public TurtleMeta(@NotNull Entity entity, @NotNull Metadata metadata) { } public void setBlockPosition(@NotNull Point value) { - super.metadata.setIndex(OFFSET, Metadata.Position(value)); + super.metadata.setIndex(OFFSET, Metadata.BlockPosition(value)); } public boolean isHasEgg() { @@ -43,7 +44,7 @@ public void setLayingEgg(boolean value) { } public void setTravelPosition(@NotNull Point value) { - super.metadata.setIndex(OFFSET + 3, Metadata.Position(value)); + super.metadata.setIndex(OFFSET + 3, Metadata.BlockPosition(value)); } public boolean isGoingHome() { diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/ZombieHorseMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/ZombieHorseMeta.java index 8d82dba7641..b8a1cc5f8a3 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/ZombieHorseMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/ZombieHorseMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.animal; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class ZombieHorseMeta extends AbstractHorseMeta { public static final byte OFFSET = AbstractHorseMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public ZombieHorseMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public ZombieHorseMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/tameable/CatMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/tameable/CatMeta.java index 529d30aa4c7..f866f8e398c 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/tameable/CatMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/tameable/CatMeta.java @@ -3,13 +3,17 @@ import net.minestom.server.color.DyeColor; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; +import net.minestom.server.network.NetworkBuffer; import org.jetbrains.annotations.NotNull; public class CatMeta extends TameableAnimalMeta { public static final byte OFFSET = TameableAnimalMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 4; - public CatMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + private static final DyeColor[] DYE_VALUES = DyeColor.values(); + + public CatMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } @@ -39,7 +43,7 @@ public void setRelaxed(boolean value) { } public @NotNull DyeColor getCollarColor() { - return DyeColor.getValue(super.metadata.getIndex(OFFSET + 3, DyeColor.RED.ordinal())); // Microtus - Banner and Shield Meta + return DYE_VALUES[super.metadata.getIndex(OFFSET + 3, DyeColor.RED.ordinal())]; } public void setCollarColor(@NotNull DyeColor value) { @@ -59,7 +63,7 @@ public enum Variant { JELLIE, ALL_BLACK; - private static final Variant[] VALUES = values(); + public static final NetworkBuffer.Type NETWORK_TYPE = NetworkBuffer.Enum(Variant.class); } } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/tameable/ParrotMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/tameable/ParrotMeta.java index 2be4e7568e0..0d13ab447da 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/tameable/ParrotMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/tameable/ParrotMeta.java @@ -2,13 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class ParrotMeta extends TameableAnimalMeta { public static final byte OFFSET = TameableAnimalMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - public ParrotMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public ParrotMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/tameable/TameableAnimalMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/tameable/TameableAnimalMeta.java index 852d33fa37a..48d3904ef99 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/tameable/TameableAnimalMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/tameable/TameableAnimalMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.animal.AnimalMeta; import org.jetbrains.annotations.NotNull; @@ -14,7 +15,7 @@ public class TameableAnimalMeta extends AnimalMeta { private final static byte SITTING_BIT = 0x01; private final static byte TAMED_BIT = 0x04; - protected TameableAnimalMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected TameableAnimalMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/animal/tameable/WolfMeta.java b/src/main/java/net/minestom/server/entity/metadata/animal/tameable/WolfMeta.java index ce593bd8435..5c6a6ed0d43 100644 --- a/src/main/java/net/minestom/server/entity/metadata/animal/tameable/WolfMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/animal/tameable/WolfMeta.java @@ -1,17 +1,35 @@ 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.entity.MetadataHolder; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.Registries; +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; - public static final byte MAX_OFFSET = OFFSET + 3; + public static final byte MAX_OFFSET = OFFSET + 4; - public WolfMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public WolfMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } + //todo variant + public boolean isBegging() { return super.metadata.getIndex(OFFSET, false); } @@ -36,4 +54,136 @@ public void setAngerTime(int value) { super.metadata.setIndex(OFFSET + 2, Metadata.VarInt(value)); } + public @NotNull DynamicRegistry.Key getVariant() { + return super.metadata.getIndex(OFFSET + 3, Variant.PALE); + } + + public void setVariant(@NotNull DynamicRegistry.Key value) { + super.metadata.setIndex(OFFSET + 3, Metadata.WolfVariant(value)); + } + + public sealed interface Variant extends ProtocolObject, WolfVariants permits VariantImpl { + @NotNull NetworkBuffer.Type> NETWORK_TYPE = NetworkBuffer.RegistryKey(Registries::wolfVariant); + @NotNull BinaryTagSerializer> NBT_TYPE = BinaryTagSerializer.registryKey(Registries::wolfVariant); + + static @NotNull Variant create( + @NotNull NamespaceID wildTexture, + @NotNull NamespaceID tameTexture, + @NotNull NamespaceID angryTexture, + @NotNull String biome + ) { + return new VariantImpl(wildTexture, tameTexture, angryTexture, List.of(biome), null); + } + + static @NotNull Builder builder() { + return new Builder(); + } + + /** + *

Creates a new registry for wolf variants, loading the vanilla wolf variants.

+ * + * @see net.minestom.server.MinecraftServer to get an existing instance of the registry + */ + @ApiStatus.Internal + static @NotNull DynamicRegistry createDefaultRegistry() { + return DynamicRegistry.create( + "minecraft:wolf_variant", VariantImpl.REGISTRY_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 biomes(); + + @Override + @Nullable Registry.WolfVariantEntry registry(); + + final class Builder { + private NamespaceID wildTexture; + private NamespaceID tameTexture; + private NamespaceID angryTexture; + private List biomes; + + private Builder() { + } + + 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 biomes) { + this.biomes = biomes; + return this; + } + + public @NotNull Variant build() { + return new VariantImpl(wildTexture, tameTexture, angryTexture, biomes, null); + } + } + } + + record VariantImpl( + @NotNull NamespaceID wildTexture, + @NotNull NamespaceID tameTexture, + @NotNull NamespaceID angryTexture, + @NotNull List biomes, + @Nullable Registry.WolfVariantEntry registry + ) implements Variant { + + private static final BinaryTagSerializer> BIOMES_NBT_TYPE = BinaryTagSerializer.STRING.list(); + static final BinaryTagSerializer REGISTRY_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(wildTexture, "missing wild texture"); + Check.notNull(tameTexture, "missing tame texture"); + Check.notNull(angryTexture, "missing angry texture"); + Check.notNull(biomes, "missing biomes"); + } + + VariantImpl(@NotNull Registry.WolfVariantEntry registry) { + this(registry.wildTexture(), registry.tameTexture(), + registry.angryTexture(), registry.biomes(), registry); + } + } + } diff --git a/src/main/java/net/minestom/server/entity/metadata/display/AbstractDisplayMeta.java b/src/main/java/net/minestom/server/entity/metadata/display/AbstractDisplayMeta.java index b007122ad6c..bdba2ec4402 100644 --- a/src/main/java/net/minestom/server/entity/metadata/display/AbstractDisplayMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/display/AbstractDisplayMeta.java @@ -4,6 +4,7 @@ import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; import org.jetbrains.annotations.NotNull; @@ -12,7 +13,7 @@ public class AbstractDisplayMeta extends EntityMeta { public static final byte OFFSET = EntityMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 15; - protected AbstractDisplayMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected AbstractDisplayMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/display/BlockDisplayMeta.java b/src/main/java/net/minestom/server/entity/metadata/display/BlockDisplayMeta.java index dd2f7da12b6..2fc325d0ef2 100644 --- a/src/main/java/net/minestom/server/entity/metadata/display/BlockDisplayMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/display/BlockDisplayMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.instance.block.Block; import org.jetbrains.annotations.NotNull; @@ -9,15 +10,15 @@ public class BlockDisplayMeta extends AbstractDisplayMeta { public static final byte OFFSET = AbstractDisplayMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - public BlockDisplayMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public BlockDisplayMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } - public int getBlockStateId() { - return super.metadata.getIndex(OFFSET, Block.AIR.stateId()); + public @NotNull Block getBlockStateId() { + return super.metadata.getIndex(OFFSET, Block.AIR); } - public void setBlockState(int value) { + public void setBlockState(@NotNull Block value) { super.metadata.setIndex(OFFSET, Metadata.BlockState(value)); } } diff --git a/src/main/java/net/minestom/server/entity/metadata/display/ItemDisplayMeta.java b/src/main/java/net/minestom/server/entity/metadata/display/ItemDisplayMeta.java index a9453696d45..1d2b93f5954 100644 --- a/src/main/java/net/minestom/server/entity/metadata/display/ItemDisplayMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/display/ItemDisplayMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.item.ItemStack; import org.jetbrains.annotations.NotNull; @@ -9,7 +10,7 @@ public class ItemDisplayMeta extends AbstractDisplayMeta { public static final byte OFFSET = AbstractDisplayMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 2; - public ItemDisplayMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public ItemDisplayMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } @@ -18,7 +19,7 @@ public ItemDisplayMeta(@NotNull Entity entity, @NotNull Metadata metadata) { } public void setItemStack(@NotNull ItemStack value) { - super.metadata.setIndex(OFFSET, Metadata.Slot(value)); + super.metadata.setIndex(OFFSET, Metadata.ItemStack(value)); } public @NotNull DisplayContext getDisplayContext() { diff --git a/src/main/java/net/minestom/server/entity/metadata/display/TextDisplayMeta.java b/src/main/java/net/minestom/server/entity/metadata/display/TextDisplayMeta.java index 10e09204c8f..ec5c9671f30 100644 --- a/src/main/java/net/minestom/server/entity/metadata/display/TextDisplayMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/display/TextDisplayMeta.java @@ -3,6 +3,7 @@ import net.kyori.adventure.text.Component; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class TextDisplayMeta extends AbstractDisplayMeta { @@ -15,7 +16,7 @@ public class TextDisplayMeta extends AbstractDisplayMeta { private static final byte ALIGN_LEFT = 8; private static final byte ALIGN_RIGHT = 16; - public TextDisplayMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public TextDisplayMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } @@ -43,8 +44,8 @@ public void setBackgroundColor(int value) { super.metadata.setIndex(OFFSET + 2, Metadata.VarInt(value)); } - public int getTextOpacity() { - return super.metadata.getIndex(OFFSET + 3, -1); + public byte getTextOpacity() { + return super.metadata.getIndex(OFFSET + 3, (byte) -1); } public void setTextOpacity(byte value) { diff --git a/src/main/java/net/minestom/server/entity/metadata/flying/FlyingMeta.java b/src/main/java/net/minestom/server/entity/metadata/flying/FlyingMeta.java index 0a07b19a67a..f4ea4a27d1f 100644 --- a/src/main/java/net/minestom/server/entity/metadata/flying/FlyingMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/flying/FlyingMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.flying; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.MobMeta; import org.jetbrains.annotations.NotNull; @@ -9,7 +9,7 @@ public class FlyingMeta extends MobMeta { public static final byte OFFSET = MobMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - protected FlyingMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected FlyingMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/flying/GhastMeta.java b/src/main/java/net/minestom/server/entity/metadata/flying/GhastMeta.java index 93245421c11..1c944e01a60 100644 --- a/src/main/java/net/minestom/server/entity/metadata/flying/GhastMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/flying/GhastMeta.java @@ -2,13 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class GhastMeta extends FlyingMeta { public static final byte OFFSET = FlyingMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - public GhastMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public GhastMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/flying/PhantomMeta.java b/src/main/java/net/minestom/server/entity/metadata/flying/PhantomMeta.java index f268efdbe28..f132b3cf75b 100644 --- a/src/main/java/net/minestom/server/entity/metadata/flying/PhantomMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/flying/PhantomMeta.java @@ -2,13 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class PhantomMeta extends FlyingMeta { public static final byte OFFSET = FlyingMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - public PhantomMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public PhantomMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/golem/AbstractGolemMeta.java b/src/main/java/net/minestom/server/entity/metadata/golem/AbstractGolemMeta.java index 1fb548cf55f..5a52013f00f 100644 --- a/src/main/java/net/minestom/server/entity/metadata/golem/AbstractGolemMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/golem/AbstractGolemMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.golem; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.PathfinderMobMeta; import org.jetbrains.annotations.NotNull; @@ -9,7 +9,7 @@ public class AbstractGolemMeta extends PathfinderMobMeta { public static final byte OFFSET = PathfinderMobMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - protected AbstractGolemMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected AbstractGolemMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/golem/IronGolemMeta.java b/src/main/java/net/minestom/server/entity/metadata/golem/IronGolemMeta.java index a6c82357089..9ce3966e9a5 100644 --- a/src/main/java/net/minestom/server/entity/metadata/golem/IronGolemMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/golem/IronGolemMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.golem; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class IronGolemMeta extends AbstractGolemMeta { @@ -9,7 +9,7 @@ public class IronGolemMeta extends AbstractGolemMeta { public static final byte MAX_OFFSET = OFFSET + 1; private static final byte PLAYER_CREATED_BIT = 0x01; //Microtus - update java keyword usage - public IronGolemMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public IronGolemMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/golem/ShulkerMeta.java b/src/main/java/net/minestom/server/entity/metadata/golem/ShulkerMeta.java index e182dd9dc0c..7a9cb03f676 100644 --- a/src/main/java/net/minestom/server/entity/metadata/golem/ShulkerMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/golem/ShulkerMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.utils.Direction; import org.jetbrains.annotations.NotNull; @@ -9,7 +10,7 @@ public class ShulkerMeta extends AbstractGolemMeta { public static final byte OFFSET = AbstractGolemMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 3; - public ShulkerMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public ShulkerMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/golem/SnowGolemMeta.java b/src/main/java/net/minestom/server/entity/metadata/golem/SnowGolemMeta.java index 3b99fadfe9c..5d0f18ca225 100644 --- a/src/main/java/net/minestom/server/entity/metadata/golem/SnowGolemMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/golem/SnowGolemMeta.java @@ -2,13 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class SnowGolemMeta extends AbstractGolemMeta { public static final byte OFFSET = AbstractGolemMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - public SnowGolemMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public SnowGolemMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/item/EyeOfEnderMeta.java b/src/main/java/net/minestom/server/entity/metadata/item/EyeOfEnderMeta.java index 36db8690853..489530dd289 100644 --- a/src/main/java/net/minestom/server/entity/metadata/item/EyeOfEnderMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/item/EyeOfEnderMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.item; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.item.Material; import org.jetbrains.annotations.NotNull; @@ -9,7 +9,7 @@ public class EyeOfEnderMeta extends ItemContainingMeta { public static final byte OFFSET = ItemContainingMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public EyeOfEnderMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public EyeOfEnderMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata, Material.ENDER_EYE); } diff --git a/src/main/java/net/minestom/server/entity/metadata/item/FireballMeta.java b/src/main/java/net/minestom/server/entity/metadata/item/FireballMeta.java index 1da1ea587cf..c07c1a84d58 100644 --- a/src/main/java/net/minestom/server/entity/metadata/item/FireballMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/item/FireballMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.item; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.ObjectDataProvider; import net.minestom.server.entity.metadata.projectile.ProjectileMeta; import net.minestom.server.item.Material; @@ -14,7 +14,7 @@ public class FireballMeta extends ItemContainingMeta implements ObjectDataProvid private Entity shooter; - public FireballMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public FireballMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata, Material.AIR); } diff --git a/src/main/java/net/minestom/server/entity/metadata/item/ItemContainingMeta.java b/src/main/java/net/minestom/server/entity/metadata/item/ItemContainingMeta.java index 2949ec13391..b42d061ca5d 100644 --- a/src/main/java/net/minestom/server/entity/metadata/item/ItemContainingMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/item/ItemContainingMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; @@ -13,7 +14,7 @@ class ItemContainingMeta extends EntityMeta { private final ItemStack defaultItem; - protected ItemContainingMeta(@NotNull Entity entity, @NotNull Metadata metadata, @NotNull Material defaultItemMaterial) { + protected ItemContainingMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata, @NotNull Material defaultItemMaterial) { super(entity, metadata); this.defaultItem = ItemStack.of(defaultItemMaterial); } @@ -24,7 +25,7 @@ public ItemStack getItem() { } public void setItem(@NotNull ItemStack item) { - super.metadata.setIndex(OFFSET, Metadata.Slot(item)); + super.metadata.setIndex(OFFSET, Metadata.ItemStack(item)); } } diff --git a/src/main/java/net/minestom/server/entity/metadata/item/ItemEntityMeta.java b/src/main/java/net/minestom/server/entity/metadata/item/ItemEntityMeta.java index c34a963346d..50554671973 100644 --- a/src/main/java/net/minestom/server/entity/metadata/item/ItemEntityMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/item/ItemEntityMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.item; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.ObjectDataProvider; import net.minestom.server.item.Material; import org.jetbrains.annotations.NotNull; @@ -10,7 +10,7 @@ public class ItemEntityMeta extends ItemContainingMeta implements ObjectDataProv public static final byte OFFSET = ItemContainingMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public ItemEntityMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public ItemEntityMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata, Material.AIR); } diff --git a/src/main/java/net/minestom/server/entity/metadata/item/SmallFireballMeta.java b/src/main/java/net/minestom/server/entity/metadata/item/SmallFireballMeta.java index 241d0b6a178..d21487a89b3 100644 --- a/src/main/java/net/minestom/server/entity/metadata/item/SmallFireballMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/item/SmallFireballMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.item; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.ObjectDataProvider; import net.minestom.server.entity.metadata.projectile.ProjectileMeta; import net.minestom.server.item.Material; @@ -14,7 +14,7 @@ public class SmallFireballMeta extends ItemContainingMeta implements ObjectDataP private Entity shooter; - public SmallFireballMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public SmallFireballMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata, Material.FIRE_CHARGE); } diff --git a/src/main/java/net/minestom/server/entity/metadata/item/SnowballMeta.java b/src/main/java/net/minestom/server/entity/metadata/item/SnowballMeta.java index 22bbcf060ad..8419beee641 100644 --- a/src/main/java/net/minestom/server/entity/metadata/item/SnowballMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/item/SnowballMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.item; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.item.Material; import org.jetbrains.annotations.NotNull; @@ -9,7 +9,7 @@ public class SnowballMeta extends ItemContainingMeta { public static final byte OFFSET = ItemContainingMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public SnowballMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public SnowballMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata, Material.SNOWBALL); } diff --git a/src/main/java/net/minestom/server/entity/metadata/item/ThrownEggMeta.java b/src/main/java/net/minestom/server/entity/metadata/item/ThrownEggMeta.java index 7f62c0bcc7a..8b55078f713 100644 --- a/src/main/java/net/minestom/server/entity/metadata/item/ThrownEggMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/item/ThrownEggMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.item; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.item.Material; import org.jetbrains.annotations.NotNull; @@ -9,7 +9,7 @@ public class ThrownEggMeta extends ItemContainingMeta { public static final byte OFFSET = ItemContainingMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public ThrownEggMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public ThrownEggMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata, Material.EGG); } diff --git a/src/main/java/net/minestom/server/entity/metadata/item/ThrownEnderPearlMeta.java b/src/main/java/net/minestom/server/entity/metadata/item/ThrownEnderPearlMeta.java index 9aebefa6f41..c2237dda887 100644 --- a/src/main/java/net/minestom/server/entity/metadata/item/ThrownEnderPearlMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/item/ThrownEnderPearlMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.item; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.item.Material; import org.jetbrains.annotations.NotNull; @@ -9,7 +9,7 @@ public class ThrownEnderPearlMeta extends ItemContainingMeta { public static final byte OFFSET = ItemContainingMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public ThrownEnderPearlMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public ThrownEnderPearlMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata, Material.ENDER_PEARL); } diff --git a/src/main/java/net/minestom/server/entity/metadata/item/ThrownExperienceBottleMeta.java b/src/main/java/net/minestom/server/entity/metadata/item/ThrownExperienceBottleMeta.java index dd64e02e0ff..b754d37f451 100644 --- a/src/main/java/net/minestom/server/entity/metadata/item/ThrownExperienceBottleMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/item/ThrownExperienceBottleMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.item; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.item.Material; import org.jetbrains.annotations.NotNull; @@ -9,7 +9,7 @@ public class ThrownExperienceBottleMeta extends ItemContainingMeta { public static final byte OFFSET = ItemContainingMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public ThrownExperienceBottleMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public ThrownExperienceBottleMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata, Material.EXPERIENCE_BOTTLE); } diff --git a/src/main/java/net/minestom/server/entity/metadata/item/ThrownPotionMeta.java b/src/main/java/net/minestom/server/entity/metadata/item/ThrownPotionMeta.java index 2f950c670ed..a86bdd5046e 100644 --- a/src/main/java/net/minestom/server/entity/metadata/item/ThrownPotionMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/item/ThrownPotionMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.item; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.item.Material; import org.jetbrains.annotations.NotNull; @@ -9,7 +9,7 @@ public class ThrownPotionMeta extends ItemContainingMeta { public static final byte OFFSET = ItemContainingMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public ThrownPotionMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public ThrownPotionMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata, Material.AIR); } diff --git a/src/main/java/net/minestom/server/entity/metadata/minecart/AbstractMinecartContainerMeta.java b/src/main/java/net/minestom/server/entity/metadata/minecart/AbstractMinecartContainerMeta.java index af22809c517..f6176dce7b2 100644 --- a/src/main/java/net/minestom/server/entity/metadata/minecart/AbstractMinecartContainerMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/minecart/AbstractMinecartContainerMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.minecart; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public abstract class AbstractMinecartContainerMeta extends AbstractMinecartMeta { public static final byte OFFSET = AbstractMinecartMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - protected AbstractMinecartContainerMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected AbstractMinecartContainerMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/minecart/AbstractMinecartMeta.java b/src/main/java/net/minestom/server/entity/metadata/minecart/AbstractMinecartMeta.java index f8c2cc007c8..c238c668935 100644 --- a/src/main/java/net/minestom/server/entity/metadata/minecart/AbstractMinecartMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/minecart/AbstractMinecartMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; import net.minestom.server.entity.metadata.ObjectDataProvider; import org.jetbrains.annotations.NotNull; @@ -10,7 +11,7 @@ public abstract class AbstractMinecartMeta extends EntityMeta implements ObjectD public static final byte OFFSET = EntityMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 6; - protected AbstractMinecartMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected AbstractMinecartMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/minecart/ChestMinecartMeta.java b/src/main/java/net/minestom/server/entity/metadata/minecart/ChestMinecartMeta.java index ced0b670663..7ad2aac2b28 100644 --- a/src/main/java/net/minestom/server/entity/metadata/minecart/ChestMinecartMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/minecart/ChestMinecartMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.minecart; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class ChestMinecartMeta extends AbstractMinecartContainerMeta { public static final byte OFFSET = AbstractMinecartContainerMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public ChestMinecartMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public ChestMinecartMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/minecart/CommandBlockMinecartMeta.java b/src/main/java/net/minestom/server/entity/metadata/minecart/CommandBlockMinecartMeta.java index d9b29dc6e88..249b5660384 100644 --- a/src/main/java/net/minestom/server/entity/metadata/minecart/CommandBlockMinecartMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/minecart/CommandBlockMinecartMeta.java @@ -3,13 +3,14 @@ import net.kyori.adventure.text.Component; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class CommandBlockMinecartMeta extends AbstractMinecartMeta { public static final byte OFFSET = AbstractMinecartMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 2; - public CommandBlockMinecartMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public CommandBlockMinecartMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/minecart/FurnaceMinecartMeta.java b/src/main/java/net/minestom/server/entity/metadata/minecart/FurnaceMinecartMeta.java index 06ac9b069e2..aa1a879088d 100644 --- a/src/main/java/net/minestom/server/entity/metadata/minecart/FurnaceMinecartMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/minecart/FurnaceMinecartMeta.java @@ -2,13 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class FurnaceMinecartMeta extends AbstractMinecartMeta { public static final byte OFFSET = AbstractMinecartMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - public FurnaceMinecartMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public FurnaceMinecartMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/minecart/HopperMinecartMeta.java b/src/main/java/net/minestom/server/entity/metadata/minecart/HopperMinecartMeta.java index 777538ee44a..e4274e77a3e 100644 --- a/src/main/java/net/minestom/server/entity/metadata/minecart/HopperMinecartMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/minecart/HopperMinecartMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.minecart; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class HopperMinecartMeta extends AbstractMinecartContainerMeta { public static final byte OFFSET = AbstractMinecartContainerMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public HopperMinecartMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public HopperMinecartMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/minecart/MinecartMeta.java b/src/main/java/net/minestom/server/entity/metadata/minecart/MinecartMeta.java index 64d4a44f922..21e758bfabb 100644 --- a/src/main/java/net/minestom/server/entity/metadata/minecart/MinecartMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/minecart/MinecartMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.minecart; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class MinecartMeta extends AbstractMinecartMeta { public static final byte OFFSET = AbstractMinecartMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public MinecartMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public MinecartMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/minecart/SpawnerMinecartMeta.java b/src/main/java/net/minestom/server/entity/metadata/minecart/SpawnerMinecartMeta.java index 60ccbc6bd45..5c2a710e8e0 100644 --- a/src/main/java/net/minestom/server/entity/metadata/minecart/SpawnerMinecartMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/minecart/SpawnerMinecartMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.minecart; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class SpawnerMinecartMeta extends AbstractMinecartMeta { public static final byte OFFSET = AbstractMinecartMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public SpawnerMinecartMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public SpawnerMinecartMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/minecart/TntMinecartMeta.java b/src/main/java/net/minestom/server/entity/metadata/minecart/TntMinecartMeta.java index 18fa442a7eb..9c1f859bb58 100644 --- a/src/main/java/net/minestom/server/entity/metadata/minecart/TntMinecartMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/minecart/TntMinecartMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.minecart; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class TntMinecartMeta extends AbstractMinecartMeta { public static final byte OFFSET = AbstractMinecartMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public TntMinecartMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public TntMinecartMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/BasePiglinMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/BasePiglinMeta.java index c0ef5739aab..675a9147ad6 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/BasePiglinMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/BasePiglinMeta.java @@ -2,13 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class BasePiglinMeta extends MonsterMeta { public static final byte OFFSET = MonsterMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - protected BasePiglinMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected BasePiglinMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/BlazeMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/BlazeMeta.java index 32cb10b6810..a5fab29ff42 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/BlazeMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/BlazeMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.monster; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class BlazeMeta extends MonsterMeta { @@ -9,7 +9,7 @@ public class BlazeMeta extends MonsterMeta { public static final byte MAX_OFFSET = OFFSET + 1; private static final byte ON_FIRE_BIT = 0x01; //Microtus - update java keyword usage - public BlazeMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public BlazeMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/BreezeMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/BreezeMeta.java index 8d0d1f2d880..d6edc4c148d 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/BreezeMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/BreezeMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.monster; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class BreezeMeta extends MonsterMeta { public static final byte OFFSET = MonsterMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public BreezeMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public BreezeMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/CaveSpiderMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/CaveSpiderMeta.java index 3180a0b5207..c848bd1a760 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/CaveSpiderMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/CaveSpiderMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.monster; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class CaveSpiderMeta extends SpiderMeta { public static final byte OFFSET = SpiderMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public CaveSpiderMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public CaveSpiderMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/CreeperMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/CreeperMeta.java index a989ec4e97b..80ce629180e 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/CreeperMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/CreeperMeta.java @@ -2,13 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class CreeperMeta extends MonsterMeta { public static final byte OFFSET = MonsterMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 3; - public CreeperMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public CreeperMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/ElderGuardianMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/ElderGuardianMeta.java index 4a4bcc12e92..f93659f936a 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/ElderGuardianMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/ElderGuardianMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.monster; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class ElderGuardianMeta extends GuardianMeta { public static final byte OFFSET = GuardianMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public ElderGuardianMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public ElderGuardianMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/EndermanMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/EndermanMeta.java index 756cbf14c84..ea230c211b7 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/EndermanMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/EndermanMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -9,7 +10,7 @@ public class EndermanMeta extends MonsterMeta { public static final byte OFFSET = MonsterMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 3; - public EndermanMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public EndermanMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/EndermiteMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/EndermiteMeta.java index 738e1cd6e46..3f6ff2f8141 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/EndermiteMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/EndermiteMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.monster; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class EndermiteMeta extends MonsterMeta { public static final byte OFFSET = MonsterMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public EndermiteMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public EndermiteMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/GiantMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/GiantMeta.java index 30659a2e254..fb988610105 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/GiantMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/GiantMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.monster; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class GiantMeta extends MonsterMeta { public static final byte OFFSET = MonsterMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public GiantMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public GiantMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/GuardianMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/GuardianMeta.java index 4726c3a608e..465edb70178 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/GuardianMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/GuardianMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -11,7 +12,7 @@ public class GuardianMeta extends MonsterMeta { private Entity target; - public GuardianMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public GuardianMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/MonsterMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/MonsterMeta.java index cc9aec0be90..8285e31ecf2 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/MonsterMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/MonsterMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.monster; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.PathfinderMobMeta; import org.jetbrains.annotations.NotNull; @@ -9,7 +9,7 @@ public class MonsterMeta extends PathfinderMobMeta { public static final byte OFFSET = PathfinderMobMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - protected MonsterMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected MonsterMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/PiglinBruteMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/PiglinBruteMeta.java index 5164f882859..af6d342250f 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/PiglinBruteMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/PiglinBruteMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.monster; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class PiglinBruteMeta extends BasePiglinMeta { public static final byte OFFSET = BasePiglinMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public PiglinBruteMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public PiglinBruteMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/PiglinMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/PiglinMeta.java index 99780fbe2b9..e42ae948c2c 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/PiglinMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/PiglinMeta.java @@ -3,13 +3,14 @@ import net.minestom.server.collision.BoundingBox; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class PiglinMeta extends BasePiglinMeta { public static final byte OFFSET = BasePiglinMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 3; - public PiglinMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public PiglinMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/SilverfishMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/SilverfishMeta.java index 514aecdcbdf..eee05964596 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/SilverfishMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/SilverfishMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.monster; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class SilverfishMeta extends MonsterMeta { public static final byte OFFSET = MonsterMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public SilverfishMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public SilverfishMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/SpiderMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/SpiderMeta.java index 1b46fb30da5..f2bd7c63bb3 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/SpiderMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/SpiderMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.monster; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class SpiderMeta extends MonsterMeta { @@ -9,7 +9,7 @@ public class SpiderMeta extends MonsterMeta { public static final byte MAX_OFFSET = OFFSET + 1; private static final byte CLIMBING_BIT = 0x01; //Microtus - update java keyword usage - public SpiderMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public SpiderMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/VexMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/VexMeta.java index f566c236957..2b44ac08305 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/VexMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/VexMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.monster; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class VexMeta extends MonsterMeta { @@ -9,7 +9,7 @@ public class VexMeta extends MonsterMeta { public static final byte MAX_OFFSET = OFFSET + 1; private static final byte ATTACKING_BIT = 0x01; //Microtus - update java keyword usage - public VexMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public VexMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/WardenMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/WardenMeta.java index 4354c9ce62a..05237bf473c 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/WardenMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/WardenMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; // Microtus - entity meta update @@ -16,12 +17,13 @@ public class WardenMeta extends MonsterMeta { public static final byte OFFSET = MonsterMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; + /** * Creates a new metadata which can be set to a warden entity. * @param entity the warden entity reference * @param metadata the reference to a {@link Metadata} */ - public WardenMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public WardenMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/WitherMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/WitherMeta.java index 73b5f842fe4..96ccb897a0f 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/WitherMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/WitherMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -13,7 +14,7 @@ public class WitherMeta extends MonsterMeta { private Entity leftHead; private Entity rightHead; - public WitherMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public WitherMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/ZoglinMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/ZoglinMeta.java index a48e2b43574..ae950dcaa1e 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/ZoglinMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/ZoglinMeta.java @@ -3,13 +3,14 @@ import net.minestom.server.collision.BoundingBox; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class ZoglinMeta extends MonsterMeta { public static final byte OFFSET = MonsterMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - public ZoglinMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public ZoglinMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/raider/AbstractIllagerMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/raider/AbstractIllagerMeta.java index f6be18573a3..35a1f065ab0 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/raider/AbstractIllagerMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/raider/AbstractIllagerMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.monster.raider; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class AbstractIllagerMeta extends RaiderMeta { public static final byte OFFSET = RaiderMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - protected AbstractIllagerMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected AbstractIllagerMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/raider/EvokerMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/raider/EvokerMeta.java index ab8426f1b00..da73f958de6 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/raider/EvokerMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/raider/EvokerMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.monster.raider; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class EvokerMeta extends SpellcasterIllagerMeta { public static final byte OFFSET = SpellcasterIllagerMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public EvokerMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public EvokerMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/raider/IllusionerMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/raider/IllusionerMeta.java index f4a74780f33..c5b32e43764 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/raider/IllusionerMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/raider/IllusionerMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.monster.raider; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class IllusionerMeta extends SpellcasterIllagerMeta { public static final byte OFFSET = SpellcasterIllagerMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public IllusionerMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public IllusionerMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/raider/PillagerMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/raider/PillagerMeta.java index 1cf1168ecd1..1c0e3d6c9f5 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/raider/PillagerMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/raider/PillagerMeta.java @@ -2,13 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class PillagerMeta extends AbstractIllagerMeta { public static final byte OFFSET = AbstractIllagerMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - public PillagerMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public PillagerMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/raider/RaiderMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/raider/RaiderMeta.java index 053f7ccf600..a7820578da8 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/raider/RaiderMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/raider/RaiderMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.monster.MonsterMeta; import org.jetbrains.annotations.NotNull; @@ -9,7 +10,7 @@ public class RaiderMeta extends MonsterMeta { public static final byte OFFSET = MonsterMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - protected RaiderMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected RaiderMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/raider/RavagerMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/raider/RavagerMeta.java index 8a41dffa712..e19cbd595f3 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/raider/RavagerMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/raider/RavagerMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.monster.raider; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class RavagerMeta extends RaiderMeta { public static final byte OFFSET = RaiderMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public RavagerMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public RavagerMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/raider/SpellcasterIllagerMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/raider/SpellcasterIllagerMeta.java index ea1a781a264..fc0770365df 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/raider/SpellcasterIllagerMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/raider/SpellcasterIllagerMeta.java @@ -2,13 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class SpellcasterIllagerMeta extends AbstractIllagerMeta { public static final byte OFFSET = AbstractIllagerMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - protected SpellcasterIllagerMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected SpellcasterIllagerMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/raider/VindicatorMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/raider/VindicatorMeta.java index cb4659cc2ef..fe03d58649c 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/raider/VindicatorMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/raider/VindicatorMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.monster.raider; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class VindicatorMeta extends AbstractIllagerMeta { public static final byte OFFSET = AbstractIllagerMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public VindicatorMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public VindicatorMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/raider/WitchMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/raider/WitchMeta.java index 42d94751e52..85ceb95f856 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/raider/WitchMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/raider/WitchMeta.java @@ -2,13 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class WitchMeta extends RaiderMeta { public static final byte OFFSET = RaiderMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - public WitchMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public WitchMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/skeleton/AbstractSkeletonMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/skeleton/AbstractSkeletonMeta.java index 431feba44df..409f72212a4 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/skeleton/AbstractSkeletonMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/skeleton/AbstractSkeletonMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.monster.skeleton; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.monster.MonsterMeta; import org.jetbrains.annotations.NotNull; @@ -9,7 +9,7 @@ public class AbstractSkeletonMeta extends MonsterMeta { public static final byte OFFSET = MonsterMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - protected AbstractSkeletonMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected AbstractSkeletonMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/skeleton/BoggedMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/skeleton/BoggedMeta.java new file mode 100644 index 00000000000..6681d684a58 --- /dev/null +++ b/src/main/java/net/minestom/server/entity/metadata/monster/skeleton/BoggedMeta.java @@ -0,0 +1,23 @@ +package net.minestom.server.entity.metadata.monster.skeleton; + +import net.minestom.server.entity.Entity; +import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; +import org.jetbrains.annotations.NotNull; + +public class BoggedMeta extends AbstractSkeletonMeta { + public static final byte OFFSET = AbstractSkeletonMeta.MAX_OFFSET; + public static final byte MAX_OFFSET = OFFSET + 1; + + public BoggedMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { + super(entity, metadata); + } + + public boolean isSheared() { + return super.metadata.getIndex(OFFSET, false); + } + + public void setSheared(boolean value) { + super.metadata.setIndex(OFFSET, Metadata.Boolean(value)); + } +} diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/skeleton/SkeletonMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/skeleton/SkeletonMeta.java index 83bfb413fd0..af17d7656d5 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/skeleton/SkeletonMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/skeleton/SkeletonMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.monster.skeleton; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class SkeletonMeta extends AbstractSkeletonMeta { public static final byte OFFSET = AbstractSkeletonMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public SkeletonMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public SkeletonMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/skeleton/StrayMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/skeleton/StrayMeta.java index 34b6d02632b..4e226f2eb4a 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/skeleton/StrayMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/skeleton/StrayMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.monster.skeleton; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class StrayMeta extends AbstractSkeletonMeta { public static final byte OFFSET = AbstractSkeletonMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public StrayMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public StrayMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/skeleton/WitherSkeletonMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/skeleton/WitherSkeletonMeta.java index ef4a396dd6f..8a8d37c6be7 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/skeleton/WitherSkeletonMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/skeleton/WitherSkeletonMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.monster.skeleton; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class WitherSkeletonMeta extends AbstractSkeletonMeta { public static final byte OFFSET = AbstractSkeletonMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public WitherSkeletonMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public WitherSkeletonMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/zombie/DrownedMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/zombie/DrownedMeta.java index e28d739a56a..c04f590345a 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/zombie/DrownedMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/zombie/DrownedMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.monster.zombie; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class DrownedMeta extends ZombieMeta { public static final byte OFFSET = ZombieMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public DrownedMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public DrownedMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/zombie/HuskMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/zombie/HuskMeta.java index 98858a59780..c103dba748d 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/zombie/HuskMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/zombie/HuskMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.monster.zombie; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class HuskMeta extends ZombieMeta { public static final byte OFFSET = ZombieMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public HuskMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public HuskMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/zombie/ZombieMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/zombie/ZombieMeta.java index a78ac5b9706..4ffef8425c8 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/zombie/ZombieMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/zombie/ZombieMeta.java @@ -3,6 +3,7 @@ import net.minestom.server.collision.BoundingBox; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.monster.MonsterMeta; import org.jetbrains.annotations.NotNull; @@ -10,7 +11,7 @@ public class ZombieMeta extends MonsterMeta { public static final byte OFFSET = MonsterMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 3; - public ZombieMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public ZombieMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/zombie/ZombieVillagerMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/zombie/ZombieVillagerMeta.java index 7a8378c19c4..66023cc1212 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/zombie/ZombieVillagerMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/zombie/ZombieVillagerMeta.java @@ -2,16 +2,15 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.villager.VillagerMeta; -import net.minestom.server.entity.villager.VillagerProfession; -import net.minestom.server.entity.villager.VillagerType; import org.jetbrains.annotations.NotNull; public class ZombieVillagerMeta extends ZombieMeta { public static final byte OFFSET = ZombieMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 2; - public ZombieVillagerMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public ZombieVillagerMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } @@ -26,15 +25,15 @@ public void setConverting(boolean value) { public VillagerMeta.VillagerData getVillagerData() { int[] data = super.metadata.getIndex(OFFSET + 1, null); if (data == null) { - return new VillagerMeta.VillagerData(VillagerType.PLAINS, VillagerProfession.NONE, VillagerMeta.Level.NOVICE); + return new VillagerMeta.VillagerData(VillagerMeta.Type.PLAINS, VillagerMeta.Profession.NONE, VillagerMeta.Level.NOVICE); } - return new VillagerMeta.VillagerData(VillagerType.fromId(data[0]), VillagerProfession.fromId(data[1]), VillagerMeta.Level.VALUES[data[2] - 1]); + return new VillagerMeta.VillagerData(VillagerMeta.Type.VALUES[data[0]], VillagerMeta.Profession.VALUES[data[1]], VillagerMeta.Level.VALUES[data[2] - 1]); } public void setVillagerData(VillagerMeta.VillagerData data) { super.metadata.setIndex(OFFSET + 1, Metadata.VillagerData( - data.getType().id(), - data.getProfession().id(), + data.getType().ordinal(), + data.getProfession().ordinal(), data.getLevel().ordinal() + 1 )); } diff --git a/src/main/java/net/minestom/server/entity/metadata/monster/zombie/ZombifiedPiglinMeta.java b/src/main/java/net/minestom/server/entity/metadata/monster/zombie/ZombifiedPiglinMeta.java index 3d9ed7fd401..df8a381f3e3 100644 --- a/src/main/java/net/minestom/server/entity/metadata/monster/zombie/ZombifiedPiglinMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/monster/zombie/ZombifiedPiglinMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.monster.zombie; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class ZombifiedPiglinMeta extends ZombieMeta { public static final byte OFFSET = ZombieMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public ZombifiedPiglinMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public ZombifiedPiglinMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/other/AllayMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/AllayMeta.java index 3fc2044b514..51ae995bbeb 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/AllayMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/AllayMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.PathfinderMobMeta; import org.jetbrains.annotations.NotNull; @@ -9,7 +10,7 @@ public class AllayMeta extends PathfinderMobMeta { public static final byte OFFSET = PathfinderMobMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 2; - public AllayMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public AllayMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/other/AreaEffectCloudMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/AreaEffectCloudMeta.java index 4b4381f2004..8be65541f40 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/AreaEffectCloudMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/AreaEffectCloudMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; import net.minestom.server.particle.Particle; import org.jetbrains.annotations.NotNull; @@ -10,7 +11,7 @@ public class AreaEffectCloudMeta extends EntityMeta { public static final byte OFFSET = EntityMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 4; - public AreaEffectCloudMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public AreaEffectCloudMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/other/ArmorStandMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/ArmorStandMeta.java index a6c860cf662..c1f53492724 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/ArmorStandMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/ArmorStandMeta.java @@ -3,6 +3,7 @@ import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.LivingEntityMeta; import org.jetbrains.annotations.NotNull; @@ -17,7 +18,7 @@ public class ArmorStandMeta extends LivingEntityMeta { private static final byte IS_MARKER_BIT = 0x10; //Microtus end - update java keyword usage - public ArmorStandMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public ArmorStandMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/other/BoatMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/BoatMeta.java index 84a81a7b326..a0a3d02ff95 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/BoatMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/BoatMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; import org.jetbrains.annotations.NotNull; @@ -9,7 +10,7 @@ public class BoatMeta extends EntityMeta { public static final byte OFFSET = EntityMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 7; - public BoatMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public BoatMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/other/EndCrystalMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/EndCrystalMeta.java index c4889ddaa09..f7e7d7f866a 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/EndCrystalMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/EndCrystalMeta.java @@ -3,6 +3,7 @@ import net.minestom.server.coordinate.Point; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -11,7 +12,7 @@ public class EndCrystalMeta extends EntityMeta { public static final byte OFFSET = EntityMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 2; - public EndCrystalMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public EndCrystalMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } @@ -20,7 +21,7 @@ public EndCrystalMeta(@NotNull Entity entity, @NotNull Metadata metadata) { } public void setBeamTarget(@Nullable Point value) { - super.metadata.setIndex(OFFSET, Metadata.OptPosition(value)); + super.metadata.setIndex(OFFSET, Metadata.OptBlockPosition(value)); } public boolean isShowingBottom() { diff --git a/src/main/java/net/minestom/server/entity/metadata/other/EnderDragonMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/EnderDragonMeta.java index 8e6603ef87d..931be9044f3 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/EnderDragonMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/EnderDragonMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.MobMeta; import org.jetbrains.annotations.NotNull; @@ -9,7 +10,7 @@ public class EnderDragonMeta extends MobMeta { public static final byte OFFSET = MobMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public EnderDragonMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public EnderDragonMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/other/EvokerFangsMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/EvokerFangsMeta.java index 02a4a763b25..e91a4331793 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/EvokerFangsMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/EvokerFangsMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.other; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; import org.jetbrains.annotations.NotNull; @@ -9,7 +9,7 @@ public class EvokerFangsMeta extends EntityMeta { public static final byte OFFSET = EntityMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public EvokerFangsMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public EvokerFangsMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/other/ExperienceOrbMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/ExperienceOrbMeta.java index 0b92dc31ca3..27111685564 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/ExperienceOrbMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/ExperienceOrbMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.other; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; import org.jetbrains.annotations.NotNull; @@ -11,7 +11,7 @@ public class ExperienceOrbMeta extends EntityMeta { private int count = 1; - public ExperienceOrbMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public ExperienceOrbMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/other/FallingBlockMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/FallingBlockMeta.java index f05fb93277e..b86b5069ed1 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/FallingBlockMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/FallingBlockMeta.java @@ -4,6 +4,7 @@ import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; import net.minestom.server.entity.metadata.ObjectDataProvider; import net.minestom.server.instance.block.Block; @@ -15,7 +16,7 @@ public class FallingBlockMeta extends EntityMeta implements ObjectDataProvider { private Block block = Block.STONE; - public FallingBlockMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public FallingBlockMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } @@ -24,7 +25,7 @@ public Point getSpawnPosition() { } public void setSpawnPosition(Point value) { - super.metadata.setIndex(OFFSET, Metadata.Position(value)); + super.metadata.setIndex(OFFSET, Metadata.BlockPosition(value)); } @NotNull diff --git a/src/main/java/net/minestom/server/entity/metadata/other/FishingHookMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/FishingHookMeta.java index 00c53f2ef1b..290dde6676c 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/FishingHookMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/FishingHookMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; import net.minestom.server.entity.metadata.ObjectDataProvider; import org.jetbrains.annotations.NotNull; @@ -14,7 +15,7 @@ public class FishingHookMeta extends EntityMeta implements ObjectDataProvider { private Entity hooked; private Entity owner; - public FishingHookMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public FishingHookMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/other/GlowItemFrameMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/GlowItemFrameMeta.java index d217403a582..83cde544462 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/GlowItemFrameMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/GlowItemFrameMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.other; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class GlowItemFrameMeta extends ItemFrameMeta { public static final byte OFFSET = ItemFrameMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public GlowItemFrameMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public GlowItemFrameMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } } diff --git a/src/main/java/net/minestom/server/entity/metadata/other/InteractionMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/InteractionMeta.java index 42ba7d2be68..0102dee57a6 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/InteractionMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/InteractionMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -11,7 +12,7 @@ public class InteractionMeta extends EntityMeta { public static final byte OFFSET = EntityMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 3; - public InteractionMeta(@Nullable Entity entity, @NotNull Metadata metadata) { + public InteractionMeta(@Nullable Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/other/ItemFrameMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/ItemFrameMeta.java index 9fe83fd1e14..b7c68e0e8df 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/ItemFrameMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/ItemFrameMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; import net.minestom.server.entity.metadata.ObjectDataProvider; import net.minestom.server.item.ItemStack; @@ -14,7 +15,7 @@ public class ItemFrameMeta extends EntityMeta implements ObjectDataProvider { private Orientation orientation; - public ItemFrameMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public ItemFrameMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); this.orientation = Orientation.DOWN; } @@ -25,7 +26,7 @@ public ItemStack getItem() { } public void setItem(@NotNull ItemStack value) { - super.metadata.setIndex(OFFSET, Metadata.Slot(value)); + super.metadata.setIndex(OFFSET, Metadata.ItemStack(value)); } @NotNull diff --git a/src/main/java/net/minestom/server/entity/metadata/other/LeashKnotMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/LeashKnotMeta.java index 9be6356a810..b6a85a8d1ad 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/LeashKnotMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/LeashKnotMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.other; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; import org.jetbrains.annotations.NotNull; @@ -9,7 +9,7 @@ public class LeashKnotMeta extends EntityMeta { public static final byte OFFSET = EntityMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public LeashKnotMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public LeashKnotMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/other/LightningBoltMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/LightningBoltMeta.java index 9831757edfd..ca9235a3046 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/LightningBoltMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/LightningBoltMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.other; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; import org.jetbrains.annotations.NotNull; @@ -9,7 +9,7 @@ public class LightningBoltMeta extends EntityMeta { public static final byte OFFSET = EntityMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public LightningBoltMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public LightningBoltMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/other/LlamaSpitMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/LlamaSpitMeta.java index ead9227fd6e..bd76c2a13f9 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/LlamaSpitMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/LlamaSpitMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.other; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; import net.minestom.server.entity.metadata.ObjectDataProvider; import org.jetbrains.annotations.NotNull; @@ -10,7 +10,7 @@ public class LlamaSpitMeta extends EntityMeta implements ObjectDataProvider { public static final byte OFFSET = EntityMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public LlamaSpitMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public LlamaSpitMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/other/MagmaCubeMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/MagmaCubeMeta.java index 924b10b53be..2997f6db52c 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/MagmaCubeMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/MagmaCubeMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.other; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class MagmaCubeMeta extends SlimeMeta { public static final byte OFFSET = SlimeMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public MagmaCubeMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public MagmaCubeMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/other/MarkerMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/MarkerMeta.java index 2cb4e91091f..37197a76633 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/MarkerMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/MarkerMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.other; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; import org.jetbrains.annotations.NotNull; @@ -9,7 +9,7 @@ public class MarkerMeta extends EntityMeta { public static final byte OFFSET = EntityMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public MarkerMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public MarkerMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } } diff --git a/src/main/java/net/minestom/server/entity/metadata/other/OminousItemSpawnerMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/OminousItemSpawnerMeta.java new file mode 100644 index 00000000000..9097fcd55e9 --- /dev/null +++ b/src/main/java/net/minestom/server/entity/metadata/other/OminousItemSpawnerMeta.java @@ -0,0 +1,26 @@ +package net.minestom.server.entity.metadata.other; + +import net.minestom.server.entity.Entity; +import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; +import net.minestom.server.entity.metadata.EntityMeta; +import net.minestom.server.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +public class OminousItemSpawnerMeta extends EntityMeta { + public static final byte OFFSET = EntityMeta.MAX_OFFSET; + public static final byte MAX_OFFSET = OFFSET + 1; + + public OminousItemSpawnerMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { + super(entity, metadata); + } + + public @NotNull ItemStack getItem() { + return super.metadata.getIndex(OFFSET, ItemStack.AIR); + } + + public void setItem(@NotNull ItemStack value) { + super.metadata.setIndex(OFFSET, Metadata.ItemStack(value)); + } + +} diff --git a/src/main/java/net/minestom/server/entity/metadata/other/PaintingMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/PaintingMeta.java index 06b0ec616b1..8922bd5103a 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/PaintingMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/PaintingMeta.java @@ -1,32 +1,39 @@ package net.minestom.server.entity.metadata.other; +import net.kyori.adventure.nbt.CompoundBinaryTag; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; import net.minestom.server.entity.metadata.ObjectDataProvider; -import net.minestom.server.registry.StaticProtocolObject; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.Registries; +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.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Locale; - public class PaintingMeta extends EntityMeta implements ObjectDataProvider { public static final byte OFFSET = EntityMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; private Orientation orientation = null; - public PaintingMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public PaintingMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } - public @NotNull Variant getVariant() { + public @NotNull DynamicRegistry.Key getVariant() { return super.metadata.getIndex(OFFSET, Variant.KEBAB); } - public void setVariant(@NotNull Variant value) { + public void setVariant(@NotNull DynamicRegistry.Key value) { super.metadata.setIndex(OFFSET, Metadata.PaintingVariant(value)); } @@ -73,88 +80,101 @@ public int id() { } } - public enum Variant implements StaticProtocolObject { - KEBAB(16, 16), - AZTEC(16, 16), - ALBAN(16, 16), - AZTEC2(16, 16), - BOMB(16, 16), - PLANT(16, 16), - WASTELAND(16, 16), - POOL(32, 16), - COURBET(32, 16), - SEA(32, 16), - SUNSET(32, 16), - CREEBET(32, 16), - WANDERER(16, 32), - GRAHAM(16, 32), - MATCH(32, 32), - BUST(32, 32), - STAGE(32, 32), - VOID(32, 32), - SKULL_AND_ROSES(32, 32), - WITHER(32, 32), - FIGHTERS(64, 32), - POINTER(64, 64), - PIGSCENE(64, 64), - BURNING_SKULL(64, 64), - SKELETON(64, 48), - EARTH(32, 32), - WIND(32, 32), - WATER(32, 32), - FIRE(32, 32), - DONKEY_KONG(64, 48); - - private static final Variant[] VALUES = values(); - - public static @Nullable Variant fromId(int id) { - if (id < 0 || id >= VALUES.length) { - return null; - } - return VALUES[id]; + public sealed interface Variant extends ProtocolObject, PaintingVariants permits VariantImpl { + @NotNull NetworkBuffer.Type> NETWORK_TYPE = NetworkBuffer.RegistryKey(Registries::paintingVariant); + @NotNull BinaryTagSerializer> NBT_TYPE = BinaryTagSerializer.registryKey(Registries::paintingVariant); + + static @NotNull Variant create( + @NotNull NamespaceID assetId, + int width, int height + ) { + return new VariantImpl(assetId, width, height, null); } - public static @Nullable Variant fromNamespaceId(@Nullable String namespaceId) { - if (namespaceId == null) return null; - return fromNamespaceId(NamespaceID.from(namespaceId)); + static @NotNull Builder builder() { + return new Builder(); } - public static @Nullable Variant fromNamespaceId(@Nullable NamespaceID namespaceId) { - if (namespaceId == null) return null; - for (Variant value : VALUES) { - if (value.namespace().equals(namespaceId)) { - return value; - } - } - return null; + /** + *

Creates a new registry for painting variants, loading the vanilla painting variants.

+ * + * @see net.minestom.server.MinecraftServer to get an existing instance of the registry + */ + @ApiStatus.Internal + static @NotNull DynamicRegistry createDefaultRegistry() { + return DynamicRegistry.create( + "minecraft:painting_variant", VariantImpl.REGISTRY_NBT_TYPE, Registry.Resource.PAINTING_VARIANTS, + (namespace, props) -> new VariantImpl(Registry.paintingVariant(namespace, props)) + ); } - private final NamespaceID namespace; - private final int width; - private final int height; + @NotNull NamespaceID assetId(); - Variant(int width, int height) { - this.namespace = NamespaceID.from("minecraft", name().toLowerCase(Locale.ROOT)); - this.width = width; - this.height = height; - } + int width(); + + int height(); @Override - public int id() { - return ordinal(); - } + @Nullable Registry.PaintingVariantEntry registry(); + + class Builder { + private NamespaceID assetId; + private int width; + private int height; + + private Builder() { + } + + @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 width(int width) { + this.width = width; + return this; + } - public int width() { - return width; + @Contract(value = "_ -> this", pure = true) + public @NotNull Builder height(int height) { + this.height = height; + return this; + } + + public @NotNull Variant build() { + return new VariantImpl(assetId, width, height, null); + } } + } - public int height() { - return height; + record VariantImpl( + @NotNull NamespaceID assetId, + int width, + int height, + @Nullable Registry.PaintingVariantEntry registry + ) implements Variant { + private static final BinaryTagSerializer REGISTRY_NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> { + throw new UnsupportedOperationException("PaintingVariant is read-only"); + }, + variant -> CompoundBinaryTag.builder() + .putString("asset_id", variant.assetId().asString()) + .putInt("width", variant.width()) + .putInt("height", variant.height()) + .build() + ); + + @SuppressWarnings("ConstantValue") // The builder can violate the nullability constraints + VariantImpl { + Check.argCondition(assetId == null, "missing asset id"); + Check.argCondition(width <= 0, "width must be positive"); + Check.argCondition(height <= 0, "height must be positive"); } - @Override - public @NotNull NamespaceID namespace() { - return namespace; + VariantImpl(@NotNull Registry.PaintingVariantEntry registry) { + this(registry.assetId(), registry.width(), registry.height(), registry); } } diff --git a/src/main/java/net/minestom/server/entity/metadata/other/PrimedTntMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/PrimedTntMeta.java index f32654c3ec9..717193bf7f6 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/PrimedTntMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/PrimedTntMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; import org.jetbrains.annotations.NotNull; @@ -9,7 +10,7 @@ public class PrimedTntMeta extends EntityMeta { public static final byte OFFSET = EntityMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - public PrimedTntMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public PrimedTntMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/other/ShulkerBulletMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/ShulkerBulletMeta.java index 4dcf214d49c..ef7e959fa04 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/ShulkerBulletMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/ShulkerBulletMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.other; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; import net.minestom.server.entity.metadata.ObjectDataProvider; import org.jetbrains.annotations.NotNull; @@ -10,7 +10,7 @@ public class ShulkerBulletMeta extends EntityMeta implements ObjectDataProvider public static final byte OFFSET = EntityMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public ShulkerBulletMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public ShulkerBulletMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/other/SlimeMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/SlimeMeta.java index 59fce135099..2b0fb1e024c 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/SlimeMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/SlimeMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.MobMeta; import org.jetbrains.annotations.NotNull; @@ -9,7 +10,7 @@ public class SlimeMeta extends MobMeta { public static final byte OFFSET = MobMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - public SlimeMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public SlimeMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/other/TraderLlamaMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/TraderLlamaMeta.java index cd6d9ce5574..d13a46d16c8 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/TraderLlamaMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/TraderLlamaMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.other; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; import org.jetbrains.annotations.NotNull; @@ -9,7 +9,7 @@ public class TraderLlamaMeta extends EntityMeta { public static final byte OFFSET = EntityMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public TraderLlamaMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public TraderLlamaMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/projectile/AbstractArrowMeta.java b/src/main/java/net/minestom/server/entity/metadata/projectile/AbstractArrowMeta.java index fb7864ded10..53b50c7eb67 100644 --- a/src/main/java/net/minestom/server/entity/metadata/projectile/AbstractArrowMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/projectile/AbstractArrowMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; import org.jetbrains.annotations.NotNull; @@ -14,7 +15,7 @@ public class AbstractArrowMeta extends EntityMeta { private static final byte NO_CLIP_BIT = 0x02; //Microtus end - update java keyword usage - protected AbstractArrowMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected AbstractArrowMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/projectile/AbstractWindChargeMeta.java b/src/main/java/net/minestom/server/entity/metadata/projectile/AbstractWindChargeMeta.java new file mode 100644 index 00000000000..e41a6a31d7b --- /dev/null +++ b/src/main/java/net/minestom/server/entity/metadata/projectile/AbstractWindChargeMeta.java @@ -0,0 +1,40 @@ +package net.minestom.server.entity.metadata.projectile; + +import net.minestom.server.entity.Entity; +import net.minestom.server.entity.MetadataHolder; +import net.minestom.server.entity.metadata.EntityMeta; +import net.minestom.server.entity.metadata.ObjectDataProvider; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class AbstractWindChargeMeta extends EntityMeta implements ObjectDataProvider, ProjectileMeta { + public static final byte OFFSET = EntityMeta.MAX_OFFSET; + public static final byte MAX_OFFSET = OFFSET + 0; + + private Entity shooter; + + public AbstractWindChargeMeta(@Nullable Entity entity, @NotNull MetadataHolder metadata) { + super(entity, metadata); + } + + @Override + public @Nullable Entity getShooter() { + return shooter; + } + + @Override + public void setShooter(@Nullable Entity shooter) { + this.shooter = shooter; + } + + @Override + public int getObjectData() { + return this.shooter == null ? 0 : this.shooter.getEntityId(); + } + + @Override + public boolean requiresVelocityPacketAtSpawn() { + return true; + } + +} diff --git a/src/main/java/net/minestom/server/entity/metadata/projectile/ArrowMeta.java b/src/main/java/net/minestom/server/entity/metadata/projectile/ArrowMeta.java index 40dcaf574d1..e71ce10dae4 100644 --- a/src/main/java/net/minestom/server/entity/metadata/projectile/ArrowMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/projectile/ArrowMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.ObjectDataProvider; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -12,7 +13,7 @@ public class ArrowMeta extends AbstractArrowMeta implements ObjectDataProvider, private Entity shooter; - public ArrowMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public ArrowMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/projectile/BreezeWindChargeMeta.java b/src/main/java/net/minestom/server/entity/metadata/projectile/BreezeWindChargeMeta.java new file mode 100644 index 00000000000..da3b0a53212 --- /dev/null +++ b/src/main/java/net/minestom/server/entity/metadata/projectile/BreezeWindChargeMeta.java @@ -0,0 +1,15 @@ +package net.minestom.server.entity.metadata.projectile; + +import net.minestom.server.entity.Entity; +import net.minestom.server.entity.MetadataHolder; +import org.jetbrains.annotations.NotNull; + +public class BreezeWindChargeMeta extends AbstractWindChargeMeta { + public static final byte OFFSET = AbstractWindChargeMeta.MAX_OFFSET; + public static final byte MAX_OFFSET = OFFSET + 0; + + public BreezeWindChargeMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { + super(entity, metadata); + } + +} diff --git a/src/main/java/net/minestom/server/entity/metadata/projectile/DragonFireballMeta.java b/src/main/java/net/minestom/server/entity/metadata/projectile/DragonFireballMeta.java index 4945eba96bf..8e1f1e2f59f 100644 --- a/src/main/java/net/minestom/server/entity/metadata/projectile/DragonFireballMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/projectile/DragonFireballMeta.java @@ -1,10 +1,9 @@ package net.minestom.server.entity.metadata.projectile; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; import net.minestom.server.entity.metadata.ObjectDataProvider; -import net.minestom.server.entity.metadata.projectile.ProjectileMeta; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -14,7 +13,7 @@ public class DragonFireballMeta extends EntityMeta implements ObjectDataProvider private Entity shooter; - public DragonFireballMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public DragonFireballMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/projectile/FireworkRocketMeta.java b/src/main/java/net/minestom/server/entity/metadata/projectile/FireworkRocketMeta.java index 65c05523671..00d76dfa595 100644 --- a/src/main/java/net/minestom/server/entity/metadata/projectile/FireworkRocketMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/projectile/FireworkRocketMeta.java @@ -2,8 +2,8 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; -import net.minestom.server.entity.metadata.projectile.ProjectileMeta; import net.minestom.server.item.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -14,7 +14,7 @@ public class FireworkRocketMeta extends EntityMeta implements ProjectileMeta { private Entity shooter; - public FireworkRocketMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public FireworkRocketMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } @@ -24,7 +24,7 @@ public ItemStack getFireworkInfo() { } public void setFireworkInfo(@NotNull ItemStack value) { - super.metadata.setIndex(OFFSET, Metadata.Slot(value)); + super.metadata.setIndex(OFFSET, Metadata.ItemStack(value)); } @Override diff --git a/src/main/java/net/minestom/server/entity/metadata/projectile/SpectralArrowMeta.java b/src/main/java/net/minestom/server/entity/metadata/projectile/SpectralArrowMeta.java index a94c1d94312..069bc369c3d 100644 --- a/src/main/java/net/minestom/server/entity/metadata/projectile/SpectralArrowMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/projectile/SpectralArrowMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.projectile; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.ObjectDataProvider; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -12,7 +12,7 @@ public class SpectralArrowMeta extends AbstractArrowMeta implements ObjectDataPr private Entity shooter; - public SpectralArrowMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public SpectralArrowMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/projectile/ThrownTridentMeta.java b/src/main/java/net/minestom/server/entity/metadata/projectile/ThrownTridentMeta.java index 1c6896291df..22723b193ef 100644 --- a/src/main/java/net/minestom/server/entity/metadata/projectile/ThrownTridentMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/projectile/ThrownTridentMeta.java @@ -2,22 +2,23 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class ThrownTridentMeta extends AbstractArrowMeta { public static final byte OFFSET = AbstractArrowMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 2; - public ThrownTridentMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public ThrownTridentMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } - public int getLoyaltyLevel() { - return super.metadata.getIndex(OFFSET, 0); + public byte getLoyaltyLevel() { + return super.metadata.getIndex(OFFSET, (byte) 0); } - public void setLoyaltyLevel(int value) { - super.metadata.setIndex(OFFSET, Metadata.VarInt(value)); + public void setLoyaltyLevel(byte value) { + super.metadata.setIndex(OFFSET, Metadata.Byte(value)); } public boolean isHasEnchantmentGlint() { diff --git a/src/main/java/net/minestom/server/entity/metadata/projectile/WindChargeMeta.java b/src/main/java/net/minestom/server/entity/metadata/projectile/WindChargeMeta.java index 6e9a396d5bc..5724546bbca 100644 --- a/src/main/java/net/minestom/server/entity/metadata/projectile/WindChargeMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/projectile/WindChargeMeta.java @@ -1,40 +1,15 @@ package net.minestom.server.entity.metadata.projectile; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; -import net.minestom.server.entity.metadata.EntityMeta; -import net.minestom.server.entity.metadata.ObjectDataProvider; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -public class WindChargeMeta extends EntityMeta implements ObjectDataProvider, ProjectileMeta { - public static final byte OFFSET = EntityMeta.MAX_OFFSET; +public class WindChargeMeta extends AbstractWindChargeMeta { + public static final byte OFFSET = AbstractWindChargeMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - private Entity shooter; - - public WindChargeMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public WindChargeMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } - @Override - @Nullable - public Entity getShooter() { - return shooter; - } - - @Override - public void setShooter(@Nullable Entity shooter) { - this.shooter = shooter; - } - - @Override - public int getObjectData() { - return this.shooter == null ? 0 : this.shooter.getEntityId(); - } - - @Override - public boolean requiresVelocityPacketAtSpawn() { - return true; - } } diff --git a/src/main/java/net/minestom/server/entity/metadata/projectile/WitherSkullMeta.java b/src/main/java/net/minestom/server/entity/metadata/projectile/WitherSkullMeta.java index 552c4d1ec2a..b379bdffa70 100644 --- a/src/main/java/net/minestom/server/entity/metadata/projectile/WitherSkullMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/projectile/WitherSkullMeta.java @@ -2,9 +2,9 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.EntityMeta; import net.minestom.server.entity.metadata.ObjectDataProvider; -import net.minestom.server.entity.metadata.projectile.ProjectileMeta; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -14,7 +14,7 @@ public class WitherSkullMeta extends EntityMeta implements ObjectDataProvider, P private Entity shooter; - public WitherSkullMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public WitherSkullMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/villager/AbstractVillagerMeta.java b/src/main/java/net/minestom/server/entity/metadata/villager/AbstractVillagerMeta.java index 140242c77e6..27689c4adef 100644 --- a/src/main/java/net/minestom/server/entity/metadata/villager/AbstractVillagerMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/villager/AbstractVillagerMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.AgeableMobMeta; import org.jetbrains.annotations.NotNull; @@ -9,7 +10,7 @@ public class AbstractVillagerMeta extends AgeableMobMeta { public static final byte OFFSET = AgeableMobMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - protected AbstractVillagerMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected AbstractVillagerMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/villager/VillagerMeta.java b/src/main/java/net/minestom/server/entity/metadata/villager/VillagerMeta.java index 0ffaf43705c..aa72c04c097 100644 --- a/src/main/java/net/minestom/server/entity/metadata/villager/VillagerMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/villager/VillagerMeta.java @@ -2,15 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; -import net.minestom.server.entity.villager.VillagerProfession; -import net.minestom.server.entity.villager.VillagerType; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class VillagerMeta extends AbstractVillagerMeta { public static final byte OFFSET = AbstractVillagerMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - public VillagerMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public VillagerMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } @@ -18,46 +17,46 @@ public VillagerMeta(@NotNull Entity entity, @NotNull Metadata metadata) { public VillagerData getVillagerData() { int[] data = super.metadata.getIndex(OFFSET, null); if (data == null) { - return new VillagerData(VillagerType.PLAINS, VillagerProfession.NONE, Level.NOVICE); + return new VillagerData(Type.PLAINS, Profession.NONE, Level.NOVICE); } - return new VillagerData(VillagerType.fromId(data[0]), VillagerProfession.fromId(data[1]), Level.VALUES[data[2] - 1]); + return new VillagerData(Type.VALUES[data[0]], Profession.VALUES[data[1]], Level.VALUES[data[2] - 1]); } public void setVillagerData(@NotNull VillagerData data) { super.metadata.setIndex(OFFSET, Metadata.VillagerData( - data.type.id(), - data.profession.id(), + data.type.ordinal(), + data.profession.ordinal(), data.level.ordinal() + 1 )); } public static class VillagerData { - private VillagerType type; - private VillagerProfession profession; + private Type type; + private Profession profession; private Level level; - public VillagerData(@NotNull VillagerType type, @NotNull VillagerProfession profession, @NotNull Level level) { + public VillagerData(@NotNull Type type, @NotNull Profession profession, @NotNull Level level) { this.type = type; this.profession = profession; this.level = level; } @NotNull - public VillagerType getType() { + public Type getType() { return this.type; } - public void setType(@NotNull VillagerType type) { + public void setType(@NotNull Type type) { this.type = type; } @NotNull - public VillagerProfession getProfession() { + public Profession getProfession() { return this.profession; } - public void setProfession(@NotNull VillagerProfession profession) { + public void setProfession(@NotNull Profession profession) { this.profession = profession; } @@ -71,6 +70,39 @@ public void setLevel(@NotNull Level level) { } } + public enum Type { + DESERT, + JUNGLE, + PLAINS, + SAVANNA, + SNOW, + SWAMP, + TAIGA; + + public final static Type[] VALUES = values(); + } + + public enum Profession { + NONE, + ARMORER, + BUTCHER, + CARTOGRAPHER, + CLERIC, + FARMER, + FISHERMAN, + FLETCHER, + LEATHERWORKER, + LIBRARIAN, + NITWIT, + UNEMPLOYED, + MASON, + SHEPHERD, + TOOLSMITH, + WEAPONSMITH; + + public final static Profession[] VALUES = values(); + } + public enum Level { NOVICE, APPRENTICE, diff --git a/src/main/java/net/minestom/server/entity/metadata/villager/WanderingTraderMeta.java b/src/main/java/net/minestom/server/entity/metadata/villager/WanderingTraderMeta.java index 34019267dd3..79398542eea 100644 --- a/src/main/java/net/minestom/server/entity/metadata/villager/WanderingTraderMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/villager/WanderingTraderMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.villager; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class WanderingTraderMeta extends VillagerMeta { public static final byte OFFSET = VillagerMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public WanderingTraderMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public WanderingTraderMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/water/AxolotlMeta.java b/src/main/java/net/minestom/server/entity/metadata/water/AxolotlMeta.java index 83539dd8658..68c68bce3fb 100644 --- a/src/main/java/net/minestom/server/entity/metadata/water/AxolotlMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/water/AxolotlMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.animal.AnimalMeta; import org.jetbrains.annotations.NotNull; @@ -9,7 +10,7 @@ public class AxolotlMeta extends AnimalMeta { public static final byte OFFSET = AnimalMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 3; - public AxolotlMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public AxolotlMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/water/DolphinMeta.java b/src/main/java/net/minestom/server/entity/metadata/water/DolphinMeta.java index 1e39f9b4c70..bc37f0b2cfa 100644 --- a/src/main/java/net/minestom/server/entity/metadata/water/DolphinMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/water/DolphinMeta.java @@ -4,11 +4,10 @@ import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; -// Microtus start - meta update public class DolphinMeta extends WaterAnimalMeta { - public static final byte OFFSET = WaterAnimalMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 3; @@ -17,7 +16,7 @@ public class DolphinMeta extends WaterAnimalMeta { * @param entity the involved entity * @param metadata the base metadata */ - public DolphinMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public DolphinMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } @@ -34,17 +33,26 @@ public DolphinMeta(@NotNull Entity entity, @NotNull Metadata metadata) { * @param value the point to set */ public void setTreasurePosition(@NotNull Point value) { - super.metadata.setIndex(OFFSET, Metadata.Position(value)); + super.metadata.setIndex(OFFSET, Metadata.BlockPosition(value)); } /** * Returns an indicator if a Dolphin received a fish from a player. * @return true when yes otherwise false */ + @Deprecated public boolean hasFish() { return super.metadata.getIndex(OFFSET + 1, false); } + /** + * Returns an indicator if a Dolphin received a fish from a player. + * @return true when yes otherwise false + */ + public boolean isHasFish() { + return super.metadata.getIndex(OFFSET + 1, false); + } + /** * Set the indicator if a Dolphin got a fish from a player. * @param value the value to set @@ -68,4 +76,5 @@ public int getMoistureLevel() { public void setMoistureLevel(int level) { super.metadata.setIndex(OFFSET + 2, Metadata.VarInt(level)); } + } diff --git a/src/main/java/net/minestom/server/entity/metadata/water/GlowSquidMeta.java b/src/main/java/net/minestom/server/entity/metadata/water/GlowSquidMeta.java index f0c19230ad0..aedeaf983f1 100644 --- a/src/main/java/net/minestom/server/entity/metadata/water/GlowSquidMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/water/GlowSquidMeta.java @@ -2,13 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class GlowSquidMeta extends WaterAnimalMeta { public static final byte OFFSET = WaterAnimalMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - public GlowSquidMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public GlowSquidMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/water/SquidMeta.java b/src/main/java/net/minestom/server/entity/metadata/water/SquidMeta.java index 892ed41eac0..c27f6dc4fea 100644 --- a/src/main/java/net/minestom/server/entity/metadata/water/SquidMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/water/SquidMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.water; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class SquidMeta extends WaterAnimalMeta { public static final byte OFFSET = WaterAnimalMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public SquidMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public SquidMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/water/WaterAnimalMeta.java b/src/main/java/net/minestom/server/entity/metadata/water/WaterAnimalMeta.java index e26923cf242..d778bbac8b4 100644 --- a/src/main/java/net/minestom/server/entity/metadata/water/WaterAnimalMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/water/WaterAnimalMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.water; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.PathfinderMobMeta; import org.jetbrains.annotations.NotNull; @@ -9,7 +9,7 @@ public class WaterAnimalMeta extends PathfinderMobMeta { public static final byte OFFSET = PathfinderMobMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - protected WaterAnimalMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected WaterAnimalMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/water/fish/AbstractFishMeta.java b/src/main/java/net/minestom/server/entity/metadata/water/fish/AbstractFishMeta.java index c144d2ca3a3..6571f7f27a8 100644 --- a/src/main/java/net/minestom/server/entity/metadata/water/fish/AbstractFishMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/water/fish/AbstractFishMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.water.WaterAnimalMeta; import org.jetbrains.annotations.NotNull; @@ -9,7 +10,7 @@ public class AbstractFishMeta extends WaterAnimalMeta { public static final byte OFFSET = WaterAnimalMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - protected AbstractFishMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + protected AbstractFishMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/water/fish/CodMeta.java b/src/main/java/net/minestom/server/entity/metadata/water/fish/CodMeta.java index ddf2a69985c..d80120976e2 100644 --- a/src/main/java/net/minestom/server/entity/metadata/water/fish/CodMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/water/fish/CodMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.water.fish; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class CodMeta extends AbstractFishMeta { public static final byte OFFSET = AbstractFishMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public CodMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public CodMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/water/fish/PufferfishMeta.java b/src/main/java/net/minestom/server/entity/metadata/water/fish/PufferfishMeta.java index 64e6fa502fd..c7227b616a1 100644 --- a/src/main/java/net/minestom/server/entity/metadata/water/fish/PufferfishMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/water/fish/PufferfishMeta.java @@ -2,13 +2,14 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class PufferfishMeta extends AbstractFishMeta { public static final byte OFFSET = AbstractFishMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - public PufferfishMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public PufferfishMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); updateBoundingBox(State.UNPUFFED); } diff --git a/src/main/java/net/minestom/server/entity/metadata/water/fish/SalmonMeta.java b/src/main/java/net/minestom/server/entity/metadata/water/fish/SalmonMeta.java index cc33dc17f5c..fb1dde66ac9 100644 --- a/src/main/java/net/minestom/server/entity/metadata/water/fish/SalmonMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/water/fish/SalmonMeta.java @@ -1,14 +1,14 @@ package net.minestom.server.entity.metadata.water.fish; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; public class SalmonMeta extends AbstractFishMeta { public static final byte OFFSET = AbstractFishMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 0; - public SalmonMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public SalmonMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/metadata/water/fish/TadpoleMeta.java b/src/main/java/net/minestom/server/entity/metadata/water/fish/TadpoleMeta.java index ad9152ca44e..466e766d595 100644 --- a/src/main/java/net/minestom/server/entity/metadata/water/fish/TadpoleMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/water/fish/TadpoleMeta.java @@ -1,7 +1,7 @@ package net.minestom.server.entity.metadata.water.fish; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import org.jetbrains.annotations.NotNull; // Microtus - entity meta update @@ -15,7 +15,7 @@ public final class TadpoleMeta extends AbstractFishMeta { * @param entity the involved entity * @param metadata the involved metadata */ - public TadpoleMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public TadpoleMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } } diff --git a/src/main/java/net/minestom/server/entity/metadata/water/fish/TropicalFishMeta.java b/src/main/java/net/minestom/server/entity/metadata/water/fish/TropicalFishMeta.java index 4916ef37b8f..da5d8364a89 100644 --- a/src/main/java/net/minestom/server/entity/metadata/water/fish/TropicalFishMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/water/fish/TropicalFishMeta.java @@ -2,6 +2,7 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; +import net.minestom.server.entity.MetadataHolder; import net.minestom.server.entity.metadata.ObjectDataProvider; import org.jetbrains.annotations.NotNull; @@ -9,7 +10,7 @@ public class TropicalFishMeta extends AbstractFishMeta implements ObjectDataProv public static final byte OFFSET = AbstractFishMeta.MAX_OFFSET; public static final byte MAX_OFFSET = OFFSET + 1; - public TropicalFishMeta(@NotNull Entity entity, @NotNull Metadata metadata) { + public TropicalFishMeta(@NotNull Entity entity, @NotNull MetadataHolder metadata) { super(entity, metadata); } diff --git a/src/main/java/net/minestom/server/entity/pathfinding/Navigator.java b/src/main/java/net/minestom/server/entity/pathfinding/Navigator.java index 3ac3b48e423..f218a05e438 100644 --- a/src/main/java/net/minestom/server/entity/pathfinding/Navigator.java +++ b/src/main/java/net/minestom/server/entity/pathfinding/Navigator.java @@ -1,110 +1,90 @@ package net.minestom.server.entity.pathfinding; -import com.extollit.gaming.ai.path.HydrazinePathFinder; -import com.extollit.gaming.ai.path.PathOptions; -import com.extollit.gaming.ai.path.model.IPath; -import net.minestom.server.collision.CollisionUtils; -import net.minestom.server.collision.PhysicsResult; +import net.minestom.server.collision.BoundingBox; import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Pos; -import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.Entity; import net.minestom.server.entity.LivingEntity; +import net.minestom.server.entity.pathfinding.followers.GroundNodeFollower; +import net.minestom.server.entity.pathfinding.followers.NodeFollower; +import net.minestom.server.entity.pathfinding.generators.GroundNodeGenerator; +import net.minestom.server.entity.pathfinding.generators.NodeGenerator; import net.minestom.server.instance.Chunk; import net.minestom.server.instance.Instance; import net.minestom.server.instance.WorldBorder; +import net.minestom.server.network.packet.server.play.ParticlePacket; +import net.minestom.server.particle.Particle; import net.minestom.server.utils.chunk.ChunkUtils; -import net.minestom.server.utils.position.PositionUtils; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -// TODO all pathfinding requests could be processed in another thread +import java.util.List; +import java.util.function.Supplier; /** * Necessary object for all {@link NavigableEntity}. */ public final class Navigator { - private final PFPathingEntity pathingEntity; - private HydrazinePathFinder pathFinder; - private Point pathPosition; - + private Point goalPosition; private final Entity entity; + // Essentially a double buffer. Wait until a path is done computing before replacing the old one. + private PPath computingPath; + private PPath path; + + private double minimumDistance; + + NodeGenerator nodeGenerator = new GroundNodeGenerator(); + private NodeFollower nodeFollower; + public Navigator(@NotNull Entity entity) { this.entity = entity; - this.pathingEntity = new PFPathingEntity(this); + nodeFollower = new GroundNodeFollower(entity); } - /** - * Used to move the entity toward {@code direction} in the X and Z axis - * Gravity is still applied but the entity will not attempt to jump - * Also update the yaw/pitch of the entity to look along 'direction' - * - * @param direction the targeted position - * @param speed define how far the entity will move - */ - public PhysicsResult moveTowards(@NotNull Point direction, double speed) { - final Pos position = entity.getPosition(); - final double dx = direction.x() - position.x(); - final double dy = direction.y() - position.y(); - final double dz = direction.z() - position.z(); - // the purpose of these few lines is to slow down entities when they reach their destination - final double distSquared = dx * dx + dy * dy + dz * dz; - if (speed > distSquared) { - speed = distSquared; - } - final double radians = Math.atan2(dz, dx); - final double speedX = Math.cos(radians) * speed; - final double speedY = dy * speed; - final double speedZ = Math.sin(radians) * speed; - final float yaw = PositionUtils.getLookYaw(dx, dz); - final float pitch = PositionUtils.getLookPitch(dx, dy, dz); - // Prevent ghosting - final var physicsResult = CollisionUtils.handlePhysics(entity, new Vec(speedX, speedY, speedZ)); - this.entity.refreshPosition(physicsResult.newPosition().withView(yaw, pitch)); - return physicsResult; + public @NotNull PPath.PathState getState() { + if (path == null && computingPath == null) return PPath.PathState.INVALID; + if (path == null) return computingPath.getState(); + return path.getState(); } - public void jump(float height) { - // FIXME magic value - this.entity.setVelocity(new Vec(0, height * 2.5f, 0)); + public synchronized boolean setPathTo(@Nullable Point point) { + BoundingBox bb = this.entity.getBoundingBox(); + double centerToCorner = Math.sqrt(bb.width() * bb.width() + bb.depth() * bb.depth()) / 2; + return setPathTo(point, centerToCorner, null); + } + + public synchronized boolean setPathTo(@Nullable Point point, double minimumDistance, @Nullable Runnable onComplete) { + return setPathTo(point, minimumDistance, 50, 20, onComplete); } /** - * Retrieves the path to {@code position} and ask the entity to follow the path. - *

- * Can be set to null to reset the pathfinder. - *

- * The position is cloned, if you want the entity to continually follow this position object - * you need to call this when you want the path to update. + * Sets the path to {@code position} and ask the entity to follow the path. * - * @param point the position to find the path to, null to reset the pathfinder - * @param bestEffort whether to use the best-effort algorithm to the destination, - * if false then this method is more likely to return immediately - * @return true if a path has been found + * @param point the position to find the path to, null to reset the pathfinder + * @param minimumDistance distance to target when completed + * @param maxDistance maximum search distance + * @param pathVariance how far to search off of the direct path. For open worlds, this can be low (around 20) and for large mazes this needs to be very high. + * @param onComplete called when the path has been completed + * @return true if a path is being generated */ - public synchronized boolean setPathTo(@Nullable Point point, boolean bestEffort) { - if (point != null && pathPosition != null && point.samePoint(pathPosition)) { - // Tried to set path to the same target position - return false; - } + public synchronized boolean setPathTo(@Nullable Point point, double minimumDistance, double maxDistance, double pathVariance, @Nullable Runnable onComplete) { final Instance instance = entity.getInstance(); - if (pathFinder == null) { - // Unexpected error - return false; - } - this.pathFinder.reset(); if (point == null) { + this.path = null; return false; } + // Can't path with a null instance. if (instance == null) { + this.path = null; return false; } + // Can't path outside the world border final WorldBorder worldBorder = instance.getWorldBorder(); - if (!worldBorder.isInside(point)) { + if (!worldBorder.inBounds(point)) { return false; } // Can't path in an unloaded chunk @@ -113,35 +93,93 @@ public synchronized boolean setPathTo(@Nullable Point point, boolean bestEffort) return false; } - final PathOptions pathOptions = new PathOptions() - .targetingStrategy(bestEffort ? PathOptions.TargetingStrategy.gravitySnap : - PathOptions.TargetingStrategy.none); - final IPath path = pathFinder.initiatePathTo( - point.x(), - point.y(), - point.z(), - pathOptions); + this.minimumDistance = minimumDistance; + if (this.entity.getPosition().distance(point) < minimumDistance) { + if (onComplete != null) onComplete.run(); + return false; + } - final boolean success = path != null; - this.pathPosition = success ? point : null; - return success; - } + if (point.sameBlock(entity.getPosition())) { + if (onComplete != null) onComplete.run(); + return false; + } - /** - * @see #setPathTo(Point, boolean) with {@code bestEffort} sets to {@code true}. - */ - public boolean setPathTo(@Nullable Point position) { - return setPathTo(position, true); + if (this.computingPath != null) this.computingPath.setState(PPath.PathState.TERMINATING); + + this.computingPath = PathGenerator.generate(instance, + this.entity.getPosition(), + point, + minimumDistance, maxDistance, + pathVariance, + this.entity.getBoundingBox(), + this.entity.isOnGround(), + this.nodeGenerator, + onComplete); + + this.goalPosition = point; + return true; } @ApiStatus.Internal public synchronized void tick() { - if (pathPosition == null) return; // No path - if (entity instanceof LivingEntity && ((LivingEntity) entity).isDead()) - return; // No pathfinding tick for dead entities - if (pathFinder.updatePathFor(pathingEntity) == null) { - reset(); + if (goalPosition == null) return; // No path + if (entity instanceof LivingEntity && ((LivingEntity) entity).isDead()) return; // No pathfinding tick for dead entities + if (computingPath != null && (computingPath.getState() == PPath.PathState.COMPUTED || computingPath.getState() == PPath.PathState.BEST_EFFORT)) { + path = computingPath; + computingPath = null; + } + + if (path == null) return; + + // If the path is computed start following it + if (path.getState() == PPath.PathState.COMPUTED || path.getState() == PPath.PathState.BEST_EFFORT) { + path.setState(PPath.PathState.FOLLOWING); + // Remove nodes that are too close to the start. Prevents doubling back to hit points that have already been hit + for (int i = 0; i < path.getNodes().size(); i++) { + if (isSameBlock(path.getNodes().get(i), entity.getPosition())) { + path.getNodes().subList(0, i).clear(); + break; + } + } + } + + // If the state is not following, wait until it is + if (path.getState() != PPath.PathState.FOLLOWING) return; + + // If we're near the entity, we're done + if (this.entity.getPosition().distance(goalPosition) < minimumDistance) { + path.runComplete(); + path = null; + + return; + } + + Point currentTarget = path.getCurrent(); + Point nextTarget = path.getNext(); + + // Repath + if (currentTarget == null || path.getCurrentType() == PNode.NodeType.REPATH || path.getCurrentType() == null) { + if (computingPath != null && computingPath.getState() == PPath.PathState.CALCULATING) return; + + computingPath = PathGenerator.generate(entity.getInstance(), + entity.getPosition(), + Pos.fromPoint(goalPosition), + minimumDistance, path.maxDistance(), + path.pathVariance(), entity.getBoundingBox(), this.entity.isOnGround(), nodeGenerator, null); + + return; + } + + if (nextTarget == null) { + path.setState(PPath.PathState.INVALID); + return; } + + boolean nextIsRepath = nextTarget.sameBlock(Pos.ZERO); + nodeFollower.moveTowards(currentTarget, nodeFollower.movementSpeed(), nextIsRepath ? currentTarget : nextTarget); + + if (nodeFollower.isAtPoint(currentTarget)) path.next(); + else if (path.getCurrentType() == PNode.NodeType.JUMP) nodeFollower.jump(currentTarget, nextTarget); } /** @@ -149,26 +187,65 @@ public synchronized void tick() { * * @return the target pathfinder position, null if there is no one */ - public @Nullable Point getPathPosition() { - return pathPosition; + public @Nullable Point getGoalPosition() { + return goalPosition; } + /** + * Gets the entity which is navigating. + * + * @return the entity + */ public @NotNull Entity getEntity() { return entity; } - @ApiStatus.Internal - public @NotNull PFPathingEntity getPathingEntity() { - return pathingEntity; + public void reset() { + if (this.path != null) this.path.setState(PPath.PathState.TERMINATING); + this.goalPosition = null; + this.path = null; + + if (this.computingPath != null) this.computingPath.setState(PPath.PathState.TERMINATING); + this.computingPath = null; } - @ApiStatus.Internal - public void setPathFinder(@Nullable HydrazinePathFinder pathFinder) { - this.pathFinder = pathFinder; + public boolean isComplete() { + if (this.path == null) return true; + return goalPosition == null || entity.getPosition().sameBlock(goalPosition); + } + + public List getNodes() { + if (this.path == null && computingPath == null) return null; + if (this.path == null) return computingPath.getNodes(); + return this.path.getNodes(); + } + + public Point getPathPosition() { + return goalPosition; + } + + public void setNodeFollower(@NotNull Supplier nodeFollower) { + this.nodeFollower = nodeFollower.get(); + } + + public void setNodeGenerator(@NotNull Supplier nodeGenerator) { + this.nodeGenerator = nodeGenerator.get(); + } + + /** + * Visualise path for debugging + * @param path the path to draw + */ + private void drawPath(PPath path) { + if (path == null) return; + + for (PNode point : path.getNodes()) { + var packet = new ParticlePacket(Particle.COMPOSTER, point.x(), point.y() + 0.5, point.z(), 0, 0, 0, 0, 1); + entity.sendPacketToViewers(packet); + } } - private void reset() { - this.pathPosition = null; - this.pathFinder.reset(); + private static boolean isSameBlock(PNode pNode, Pos position) { + return Math.floor(pNode.x()) == position.blockX() && Math.floor(pNode.y()) == position.blockY() && Math.floor(pNode.z()) == position.blockZ(); } } diff --git a/src/main/java/net/minestom/server/entity/pathfinding/PFBlock.java b/src/main/java/net/minestom/server/entity/pathfinding/PFBlock.java deleted file mode 100644 index 77c63f26f76..00000000000 --- a/src/main/java/net/minestom/server/entity/pathfinding/PFBlock.java +++ /dev/null @@ -1,100 +0,0 @@ -package net.minestom.server.entity.pathfinding; - -import com.extollit.gaming.ai.path.model.IBlockDescription; -import com.extollit.gaming.ai.path.model.IBlockObject; -import com.extollit.linalg.immutable.AxisAlignedBBox; -import net.minestom.server.collision.Shape; -import net.minestom.server.instance.block.Block; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import space.vectrix.flare.fastutil.Short2ObjectSyncMap; - -@ApiStatus.Internal -public final class PFBlock implements IBlockDescription, IBlockObject { - private static final Short2ObjectSyncMap BLOCK_DESCRIPTION_MAP = Short2ObjectSyncMap.hashmap(); - - /** - * Gets the {@link PFBlock} linked to the block state id. - *

- * Cache the result if it is not already. - * - * @param block the block - * @return the {@link PFBlock} linked to {@code blockStateId} - */ - public static @NotNull PFBlock get(@NotNull Block block) { - return BLOCK_DESCRIPTION_MAP.computeIfAbsent(block.stateId(), state -> new PFBlock(block)); - } - - private final Block block; - - PFBlock(Block block) { - this.block = block; - } - - @Override - public AxisAlignedBBox bounds() { - Shape shape = this.block.registry().collisionShape(); - return new AxisAlignedBBox( - shape.relativeStart().x(), shape.relativeStart().y(), shape.relativeStart().z(), - shape.relativeEnd().x(), shape.relativeEnd().y(), shape.relativeEnd().z() - ); - } - - @Override - public boolean isFenceLike() { - // TODO: Use Hitbox - // Return fences, fencegates and walls. - // It just so happens that their namespace IDs contain "fence". - if (block.namespace().asString().contains("fence")) { - return true; - } - // Return all walls - // It just so happens that their namespace IDs all end with "wall". - return block.namespace().asString().endsWith("wall"); - } - - @Override - public boolean isClimbable() { - // Return ladders and vines (including weeping and twisting vines) - // Note that no other Namespace IDs contain "vine" except vines. - return block.compare(Block.LADDER) || block.namespace().asString().contains("vine"); - } - - @Override - public boolean isDoor() { - // Return all normal doors and trap doors. - // It just so happens that their namespace IDs all end with "door". - return block.namespace().asString().endsWith("door"); - } - - @Override - public boolean isIntractable() { - // TODO: Interactability of blocks. - return false; - } - - @Override - public boolean isImpeding() { - return block.isSolid(); - } - - @Override - public boolean isFullyBounded() { - Shape shape = block.registry().collisionShape(); - return shape.relativeStart().isZero() - && shape.relativeEnd().x() == 1.0d - && shape.relativeEnd().y() == 1.0d - && shape.relativeEnd().z() == 1.0d; - } - - @Override - public boolean isLiquid() { - return block.isLiquid(); - } - - @Override - public boolean isIncinerating() { - return block == Block.LAVA || block == Block.FIRE || block == Block.SOUL_FIRE; - } - -} diff --git a/src/main/java/net/minestom/server/entity/pathfinding/PFColumnarSpace.java b/src/main/java/net/minestom/server/entity/pathfinding/PFColumnarSpace.java deleted file mode 100644 index 3ef7e6e005a..00000000000 --- a/src/main/java/net/minestom/server/entity/pathfinding/PFColumnarSpace.java +++ /dev/null @@ -1,42 +0,0 @@ -package net.minestom.server.entity.pathfinding; - -import com.extollit.gaming.ai.path.model.ColumnarOcclusionFieldList; -import com.extollit.gaming.ai.path.model.IBlockDescription; -import com.extollit.gaming.ai.path.model.IColumnarSpace; -import com.extollit.gaming.ai.path.model.IInstanceSpace; -import net.minestom.server.instance.Chunk; -import net.minestom.server.instance.block.Block; -import org.jetbrains.annotations.ApiStatus; - -@ApiStatus.Internal -public final class PFColumnarSpace implements IColumnarSpace { - private final ColumnarOcclusionFieldList occlusionFieldList = new ColumnarOcclusionFieldList(this); - private final PFInstanceSpace instanceSpace; - private final Chunk chunk; - - PFColumnarSpace(PFInstanceSpace instanceSpace, Chunk chunk) { - this.instanceSpace = instanceSpace; - this.chunk = chunk; - } - - @Override - public IBlockDescription blockAt(int x, int y, int z) { - final Block block = chunk.getBlock(x, y, z); - return PFBlock.get(block); - } - - @Override - public int metaDataAt(int x, int y, int z) { - return 0; - } - - @Override - public ColumnarOcclusionFieldList occlusionFields() { - return occlusionFieldList; - } - - @Override - public IInstanceSpace instance() { - return instanceSpace; - } -} diff --git a/src/main/java/net/minestom/server/entity/pathfinding/PFInstanceSpace.java b/src/main/java/net/minestom/server/entity/pathfinding/PFInstanceSpace.java deleted file mode 100644 index d59126f9c8e..00000000000 --- a/src/main/java/net/minestom/server/entity/pathfinding/PFInstanceSpace.java +++ /dev/null @@ -1,41 +0,0 @@ -package net.minestom.server.entity.pathfinding; - -import com.extollit.gaming.ai.path.model.IBlockObject; -import com.extollit.gaming.ai.path.model.IColumnarSpace; -import com.extollit.gaming.ai.path.model.IInstanceSpace; -import net.minestom.server.instance.Chunk; -import net.minestom.server.instance.Instance; -import net.minestom.server.instance.block.Block; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public final class PFInstanceSpace implements IInstanceSpace { - private final Instance instance; - private final Map chunkSpaceMap = new ConcurrentHashMap<>(); - - public PFInstanceSpace(Instance instance) { - this.instance = instance; - } - - @Override - public IBlockObject blockObjectAt(int x, int y, int z) { - final Block block = instance.getBlock(x, y, z); - return PFBlock.get(block); - } - - @Override - public IColumnarSpace columnarSpaceAt(int cx, int cz) { - final Chunk chunk = instance.getChunk(cx, cz); - if (chunk == null) return null; - return chunkSpaceMap.computeIfAbsent(chunk, c -> { - final PFColumnarSpace cs = new PFColumnarSpace(this, c); - c.setColumnarSpace(cs); - return cs; - }); - } - - public Instance getInstance() { - return instance; - } -} diff --git a/src/main/java/net/minestom/server/entity/pathfinding/PFPathingEntity.java b/src/main/java/net/minestom/server/entity/pathfinding/PFPathingEntity.java deleted file mode 100644 index f3a0450c292..00000000000 --- a/src/main/java/net/minestom/server/entity/pathfinding/PFPathingEntity.java +++ /dev/null @@ -1,228 +0,0 @@ -package net.minestom.server.entity.pathfinding; - -import com.extollit.gaming.ai.path.model.Gravitation; -import com.extollit.gaming.ai.path.model.IPathingEntity; -import com.extollit.gaming.ai.path.model.Passibility; -import com.extollit.linalg.immutable.Vec3d; -import net.minestom.server.attribute.Attribute; -import net.minestom.server.attribute.AttributeImpl; -import net.minestom.server.attribute.VanillaAttribute; -import net.minestom.server.coordinate.Point; -import net.minestom.server.coordinate.Vec; -import net.minestom.server.entity.Entity; -import net.minestom.server.entity.LivingEntity; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; - -@ApiStatus.Internal -public final class PFPathingEntity implements IPathingEntity { - private final Navigator navigator; - private final Entity entity; - - private float searchRange; - - // Capacities - private boolean fireResistant; - private boolean cautious; - private boolean climber; - private boolean swimmer; - private boolean aquatic; - private boolean avian; - private boolean aquaphobic; - private boolean avoidsDoorways; - private boolean opensDoors; - - public PFPathingEntity(Navigator navigator) { - this.navigator = navigator; - this.entity = navigator.getEntity(); - - this.searchRange = getAttributeValue(VanillaAttribute.GENERIC_FOLLOW_RANGE); - } - - @Override - public int age() { - return (int) entity.getAliveTicks(); - } - - @Override - public boolean bound() { - return entity.hasVelocity(); - } - - @Override - public float searchRange() { - return searchRange; - } - - /** - * Changes the search range of the entity - * - * @param searchRange the new entity's search range - */ - public void setSearchRange(float searchRange) { - this.searchRange = searchRange; - } - - public boolean isFireResistant() { - return fireResistant; - } - - public void setFireResistant(boolean fireResistant) { - this.fireResistant = fireResistant; - } - - public boolean isCautious() { - return cautious; - } - - public void setCautious(boolean cautious) { - this.cautious = cautious; - } - - public boolean isClimber() { - return climber; - } - - public void setClimber(boolean climber) { - this.climber = climber; - } - - public boolean isSwimmer() { - return swimmer; - } - - public void setSwimmer(boolean swimmer) { - this.swimmer = swimmer; - } - - public boolean isAquatic() { - return aquatic; - } - - public void setAquatic(boolean aquatic) { - this.aquatic = aquatic; - } - - public boolean isAvian() { - return avian; - } - - public void setAvian(boolean avian) { - this.avian = avian; - } - - public boolean isAquaphobic() { - return aquaphobic; - } - - public void setAquaphobic(boolean aquaphobic) { - this.aquaphobic = aquaphobic; - } - - public boolean isAvoidsDoorways() { - return avoidsDoorways; - } - - public void setAvoidsDoorways(boolean avoidsDoorways) { - this.avoidsDoorways = avoidsDoorways; - } - - public boolean isOpensDoors() { - return opensDoors; - } - - public void setOpensDoors(boolean opensDoors) { - this.opensDoors = opensDoors; - } - - @Override - public Capabilities capabilities() { - return new Capabilities() { - @Override - public float speed() { - return getAttributeValue(VanillaAttribute.GENERIC_MOVEMENT_SPEED); - } - - @Override - public boolean fireResistant() { - return fireResistant; - } - - @Override - public boolean cautious() { - return cautious; - } - - @Override - public boolean climber() { - return climber; - } - - @Override - public boolean swimmer() { - return swimmer; - } - - @Override - public boolean aquatic() { - return aquatic; - } - - @Override - public boolean avian() { - return avian; - } - - @Override - public boolean aquaphobic() { - return aquaphobic; - } - - @Override - public boolean avoidsDoorways() { - return avoidsDoorways; - } - - @Override - public boolean opensDoors() { - return opensDoors; - } - }; - } - - @Override - public void moveTo(Vec3d position, Passibility passibility, Gravitation gravitation) { - final Point targetPosition = new Vec(position.x, position.y, position.z); - this.navigator.moveTowards(targetPosition, getAttributeValue(VanillaAttribute.GENERIC_MOVEMENT_SPEED)); - final double entityY = entity.getPosition().y() + 0.00001D; // After any negative y movement, entities will always be extremely - // slightly below floor level. This +0.00001D is here to offset this - // error and stop the entity from permanently jumping. - - if (entityY < targetPosition.y()) { - this.navigator.jump(1); - } - } - - @Override - public Vec3d coordinates() { - final var position = entity.getPosition(); - return new Vec3d(position.x(), position.y(), position.z()); - } - - @Override - public float width() { - return (float) entity.getBoundingBox().width(); - } - - @Override - public float height() { - return (float) entity.getBoundingBox().height(); - } - - private float getAttributeValue(@NotNull Attribute attribute) { - if (entity instanceof LivingEntity) { - return ((LivingEntity) entity).getAttributeValue(attribute); - } - return 0f; - } -} diff --git a/src/main/java/net/minestom/server/entity/pathfinding/PNode.java b/src/main/java/net/minestom/server/entity/pathfinding/PNode.java new file mode 100644 index 00000000000..d51a15f93c9 --- /dev/null +++ b/src/main/java/net/minestom/server/entity/pathfinding/PNode.java @@ -0,0 +1,151 @@ +package net.minestom.server.entity.pathfinding; + +import net.minestom.server.coordinate.Point; +import net.minestom.server.coordinate.Vec; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class PNode { + public enum NodeType { + WALK, + JUMP, + FALL, + CLIMB, + CLIMB_WALL, + SWIM, + FLY, REPATH + } + + private double g; + private double h; + private PNode parent; + private double pointX; + private double pointY; + private double pointZ; + private int hashCode; + + private int cantor(int a, int b) { + int ca = a >= 0 ? 2 * a : -2 * a - 1; + int cb = b >= 0 ? 2 * b : -2 * b - 1; + return (ca + cb + 1) * (ca + cb) / 2 + cb; + } + + private NodeType type; + + public PNode(double px, double py, double pz, double g, double h, @Nullable PNode parent) { + this(px, py, pz, g, h, NodeType.WALK, parent); + } + + public PNode(double px, double py, double pz, double g, double h, @NotNull NodeType type, @Nullable PNode parent) { + this.g = g; + this.h = h; + this.parent = parent; + this.type = type; + this.pointX = px; + this.pointY = py; + this.pointZ = pz; + this.hashCode = cantor((int) Math.floor(px), cantor((int) Math.floor(py), (int) Math.floor(pz))); + } + + public PNode(Point point, double g, double h, NodeType walk, @Nullable PNode parent) { + this(point.x(), point.y(), point.z(), g, h, walk, parent); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) return false; + if (obj == this) return true; + if (!(obj instanceof PNode other)) return false; + return this.hashCode == other.hashCode; + } + + @Override + public String toString() { + return "PNode{" + + "point=" + pointX + ", " + pointY + ", " + pointZ + + ", d=" + (g + h) + + ", type=" + type + + '}'; + } + + @ApiStatus.Internal + public double x() { + return pointX; + } + + @ApiStatus.Internal + public double y() { + return pointY; + } + + @ApiStatus.Internal + public double z() { + return pointZ; + } + + public int blockX() { + return (int) Math.floor(pointX); + } + + public int blockY() { + return (int) Math.floor(pointY); + } + + public int blockZ() { + return (int) Math.floor(pointZ); + } + + @ApiStatus.Internal + @NotNull NodeType getType() { + return type; + } + + @ApiStatus.Internal + public double g() { + return g; + } + + @ApiStatus.Internal + public double h() { + return h; + } + + @ApiStatus.Internal + public void setG(double v) { + this.g = v; + } + + @ApiStatus.Internal + public void setH(double heuristic) { + this.h = heuristic; + } + + @ApiStatus.Internal + public void setType(@NotNull NodeType newType) { + this.type = newType; + } + + @ApiStatus.Internal + public void setPoint(double px, double py, double pz) { + this.pointX = px; + this.pointY = py; + this.pointZ = pz; + this.hashCode = cantor((int) Math.floor(px), cantor((int) Math.floor(py), (int) Math.floor(pz))); + } + + @ApiStatus.Internal + public @Nullable PNode parent() { + return parent; + } + + @ApiStatus.Internal + public void setParent(@Nullable PNode current) { + this.parent = current; + } +} diff --git a/src/main/java/net/minestom/server/entity/pathfinding/PPath.java b/src/main/java/net/minestom/server/entity/pathfinding/PPath.java new file mode 100644 index 00000000000..d3af3a0f042 --- /dev/null +++ b/src/main/java/net/minestom/server/entity/pathfinding/PPath.java @@ -0,0 +1,86 @@ +package net.minestom.server.entity.pathfinding; + +import net.minestom.server.coordinate.Point; +import net.minestom.server.coordinate.Vec; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +public class PPath { + private final Runnable onComplete; + private final List nodes = new ArrayList<>(); + + private final double pathVariance; + private final double maxDistance; + private int index = 0; + private final AtomicReference state = new AtomicReference<>(PathState.CALCULATING); + + public Point getNext() { + if (index + 1 >= nodes.size()) return null; + var current = nodes.get(index + 1); + return new Vec(current.x(), current.y(), current.z()); + } + + public void setState(@NotNull PathState newState) { + state.set(newState); + } + + public enum PathState { + CALCULATING, + FOLLOWING, + TERMINATING, TERMINATED, COMPUTED, BEST_EFFORT, INVALID + } + + @NotNull PathState getState() { + return state.get(); + } + + public @NotNull List getNodes() { + return nodes; + } + + public PPath(double maxDistance, double pathVariance, Runnable onComplete) { + this.onComplete = onComplete; + this.maxDistance = maxDistance; + this.pathVariance = pathVariance; + } + + void runComplete() { + if (onComplete != null) onComplete.run(); + } + + @Override + public @NotNull String toString() { + return nodes.toString(); + } + + @Nullable + PNode.NodeType getCurrentType() { + if (index >= nodes.size()) return null; + var current = nodes.get(index); + return current.getType(); + } + + @Nullable + Point getCurrent() { + if (index >= nodes.size()) return null; + var current = nodes.get(index); + return new Vec(current.x(), current.y(), current.z()); + } + + void next() { + if (index >= nodes.size()) return; + index++; + } + + double maxDistance() { + return maxDistance; + } + + double pathVariance() { + return pathVariance; + } +} diff --git a/src/main/java/net/minestom/server/entity/pathfinding/PathGenerator.java b/src/main/java/net/minestom/server/entity/pathfinding/PathGenerator.java new file mode 100644 index 00000000000..14016ccafdd --- /dev/null +++ b/src/main/java/net/minestom/server/entity/pathfinding/PathGenerator.java @@ -0,0 +1,135 @@ +package net.minestom.server.entity.pathfinding; + +import it.unimi.dsi.fastutil.objects.ObjectHeapPriorityQueue; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashBigSet; +import net.minestom.server.collision.BoundingBox; +import net.minestom.server.coordinate.Point; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.entity.pathfinding.generators.NodeGenerator; +import net.minestom.server.instance.Instance; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class PathGenerator { + private static final ExecutorService pool = Executors.newWorkStealingPool(); + private static final PNode repathNode = new PNode(0, 0, 0, 0, 0, PNode.NodeType.REPATH, null); + private static final Comparator pNodeComparator = (s1, s2) -> (int) (((s1.g() + s1.h()) - (s2.g() + s2.h())) * 1000); + + public static @NotNull PPath generate(@NotNull Instance instance, @NotNull Pos orgStart, @NotNull Point orgTarget, double closeDistance, double maxDistance, double pathVariance, @NotNull BoundingBox boundingBox, boolean isOnGround, @NotNull NodeGenerator generator, @Nullable Runnable onComplete) { + Point start = (!isOnGround && generator.hasGravitySnap()) + ? orgStart.withY(generator.gravitySnap(instance, orgStart.x(), orgStart.y(), orgStart.z(), boundingBox, 100).orElse(orgStart.y())) + : orgStart; + + Point target = (generator.hasGravitySnap()) + ? orgTarget.withY(generator.gravitySnap(instance, orgTarget.x(), orgTarget.y(), orgTarget.z(), boundingBox, 100).orElse(orgTarget.y())) + : Pos.fromPoint(orgTarget); + + PPath path = new PPath(maxDistance, pathVariance, onComplete); + pool.submit(() -> computePath(instance, start, target, closeDistance, maxDistance, pathVariance, boundingBox, path, generator)); + + return path; + } + + private static void computePath(Instance instance, Point start, Point target, double closeDistance, double maxDistance, double pathVariance, BoundingBox boundingBox, PPath path, NodeGenerator generator) { + double closestDistance = Double.MAX_VALUE; + double straightDistance = generator.heuristic(start, target); + int maxSize = (int) Math.floor(maxDistance * 10); + + closeDistance = Math.max(0.8, closeDistance); + List closestFoundNodes = List.of(); + + PNode pStart = new PNode(start, 0, generator.heuristic(start, target), PNode.NodeType.WALK, null); + + ObjectHeapPriorityQueue open = new ObjectHeapPriorityQueue<>(pNodeComparator); + open.enqueue(pStart); + + Set closed = new ObjectOpenHashBigSet<>(maxSize); + + while (!open.isEmpty() && closed.size() < maxSize) { + if (path.getState() == PPath.PathState.TERMINATING) { + path.setState(PPath.PathState.TERMINATED); + return; + } + + PNode current = open.dequeue(); + + var chunk = instance.getChunkAt(current.x(), current.z()); + if (chunk == null) continue; + if (!chunk.isLoaded()) continue; + + if (((current.g() + current.h()) - straightDistance) > pathVariance) continue; + if (!withinDistance(current, start, maxDistance)) continue; + if (withinDistance(current, target, closeDistance)) { + open.enqueue(current); + break; + } + + if (current.h() < closestDistance) { + closestDistance = current.h(); + closestFoundNodes = List.of(current); + } + + Collection found = generator.getWalkable(instance, closed, current, target, boundingBox); + found.forEach(p -> { + if (getDistanceSquared(p.x(), p.y(), p.z(), start) <= (maxDistance * maxDistance)) { + open.enqueue(p); + closed.add(p); + } + }); + } + + PNode current = open.isEmpty() ? null : open.dequeue(); + + if (current == null || open.isEmpty() || !withinDistance(current, target, closeDistance)) { + if (closestFoundNodes.isEmpty()) { + path.setState(PPath.PathState.INVALID); + return; + } + + current = closestFoundNodes.get(0); + + if (!open.isEmpty()) { + repathNode.setParent(current); + current = repathNode; + } + } + + while (current.parent() != null) { + path.getNodes().add(current); + current = current.parent(); + } + + Collections.reverse(path.getNodes()); + + if (path.getCurrentType() == PNode.NodeType.REPATH) { + path.setState(PPath.PathState.INVALID); + path.getNodes().clear(); + return; + } + + var lastNode = path.getNodes().get(path.getNodes().size() - 1); + if (getDistanceSquared(lastNode.x(), lastNode.y(), lastNode.z(), target) > (closeDistance * closeDistance)) { + path.setState(PPath.PathState.BEST_EFFORT); + return; + } + + PNode pEnd = new PNode(target, 0, 0, PNode.NodeType.WALK, null); + path.getNodes().add(pEnd); + path.setState(PPath.PathState.COMPUTED); + } + + private static boolean withinDistance(PNode point, Point target, double closeDistance) { + return getDistanceSquared(point.x(), point.y(), point.z(), target) < (closeDistance * closeDistance); + } + + private static double getDistanceSquared(double x, double y, double z, Point target) { + double dx = x - target.x(); + double dy = y - target.y(); + double dz = z - target.z(); + return dx * dx + dy * dy + dz * dz; + } +} diff --git a/src/main/java/net/minestom/server/entity/pathfinding/followers/FlyingNodeFollower.java b/src/main/java/net/minestom/server/entity/pathfinding/followers/FlyingNodeFollower.java new file mode 100644 index 00000000000..57505452474 --- /dev/null +++ b/src/main/java/net/minestom/server/entity/pathfinding/followers/FlyingNodeFollower.java @@ -0,0 +1,78 @@ +package net.minestom.server.entity.pathfinding.followers; + +import net.minestom.server.collision.CollisionUtils; +import net.minestom.server.collision.PhysicsResult; +import net.minestom.server.coordinate.Point; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.coordinate.Vec; +import net.minestom.server.entity.Entity; +import net.minestom.server.entity.LivingEntity; +import net.minestom.server.entity.attribute.Attribute; +import net.minestom.server.utils.position.PositionUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class FlyingNodeFollower implements NodeFollower { + private final Entity entity; + + public FlyingNodeFollower(@NotNull Entity entity) { + this.entity = entity; + } + + /** + * Used to move the entity toward {@code direction} in the X and Z axis + * Gravity is still applied but the entity will not attempt to jump + * Also update the yaw/pitch of the entity to look along 'direction' + * + * @param direction the targeted position + * @param speed define how far the entity will move + */ + public void moveTowards(@NotNull Point direction, double speed, @NotNull Point lookAt) { + final Pos position = entity.getPosition(); + final double dx = direction.x() - position.x(); + final double dy = direction.y() - position.y(); + final double dz = direction.z() - position.z(); + + final double dxLook = lookAt.x() - position.x(); + final double dyLook = lookAt.y() - position.y(); + final double dzLook = lookAt.z() - position.z(); + + // the purpose of these few lines is to slow down entities when they reach their destination + final double distSquared = dx * dx + dy * dy + dz * dz; + if (speed > distSquared) { + speed = distSquared; + } + + final double radians = Math.atan2(dz, dx); + final double speedX = Math.cos(radians) * speed; + final double speedZ = Math.sin(radians) * speed; + final float yaw = PositionUtils.getLookYaw(dxLook, dzLook); + final float pitch = PositionUtils.getLookPitch(dxLook, dyLook, dzLook); + + double speedY = Math.signum(dy) * 0.5 * speed; + if (Math.min(Math.abs(dy), Math.abs(speedY)) == Math.abs(dy)) { + speedY = dy; + } + + final var physicsResult = CollisionUtils.handlePhysics(entity, new Vec(speedX, speedY, speedZ)); + this.entity.refreshPosition(Pos.fromPoint(physicsResult.newPosition()).withView(yaw, pitch)); + } + + @Override + public void jump(@Nullable Point point, @Nullable Point target) { + } + + @Override + public boolean isAtPoint(@NotNull Point point) { + return entity.getPosition().sameBlock(point); + } + + @Override + public double movementSpeed() { + if (entity instanceof LivingEntity living) { + return living.getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).getValue(); + } + + return 0.1f; + } +} diff --git a/src/main/java/net/minestom/server/entity/pathfinding/followers/GroundNodeFollower.java b/src/main/java/net/minestom/server/entity/pathfinding/followers/GroundNodeFollower.java new file mode 100644 index 00000000000..d20118a3a99 --- /dev/null +++ b/src/main/java/net/minestom/server/entity/pathfinding/followers/GroundNodeFollower.java @@ -0,0 +1,80 @@ +package net.minestom.server.entity.pathfinding.followers; + +import net.minestom.server.collision.CollisionUtils; +import net.minestom.server.collision.PhysicsResult; +import net.minestom.server.coordinate.Point; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.coordinate.Vec; +import net.minestom.server.entity.Entity; +import net.minestom.server.entity.LivingEntity; +import net.minestom.server.entity.attribute.Attribute; +import net.minestom.server.utils.position.PositionUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class GroundNodeFollower implements NodeFollower { + private final Entity entity; + + public GroundNodeFollower(@NotNull Entity entity) { + this.entity = entity; + } + + /** + * Used to move the entity toward {@code direction} in the X and Z axis + * Gravity is still applied but the entity will not attempt to jump + * Also update the yaw/pitch of the entity to look along 'direction' + * + * @param direction the targeted position + * @param speed define how far the entity will move + */ + public void moveTowards(@NotNull Point direction, double speed, @NotNull Point lookAt) { + final Pos position = entity.getPosition(); + final double dx = direction.x() - position.x(); + final double dy = direction.y() - position.y(); + final double dz = direction.z() - position.z(); + + final double dxLook = lookAt.x() - position.x(); + final double dyLook = lookAt.y() - position.y(); + final double dzLook = lookAt.z() - position.z(); + + // the purpose of these few lines is to slow down entities when they reach their destination + final double distSquared = dx * dx + dy * dy + dz * dz; + if (speed > distSquared) { + speed = distSquared; + } + + final double radians = Math.atan2(dz, dx); + final double speedX = Math.cos(radians) * speed; + final double speedZ = Math.sin(radians) * speed; + final float yaw = PositionUtils.getLookYaw(dxLook, dzLook); + final float pitch = PositionUtils.getLookPitch(dxLook, dyLook, dzLook); + + final var physicsResult = CollisionUtils.handlePhysics(entity, new Vec(speedX, 0, speedZ)); + this.entity.refreshPosition(Pos.fromPoint(physicsResult.newPosition()).withView(yaw, pitch)); + } + + @Override + public void jump(@Nullable Point point, @Nullable Point target) { + if (entity.isOnGround()) { + jump(4f); + } + } + + @Override + public boolean isAtPoint(@NotNull Point point) { + return entity.getPosition().sameBlock(point); + } + + public void jump(float height) { + this.entity.setVelocity(new Vec(0, height * 2.5f, 0)); + } + + @Override + public double movementSpeed() { + if (entity instanceof LivingEntity living) { + return living.getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).getValue(); + } + + return 0.1f; + } +} diff --git a/src/main/java/net/minestom/server/entity/pathfinding/followers/NoPhysicsNodeFollower.java b/src/main/java/net/minestom/server/entity/pathfinding/followers/NoPhysicsNodeFollower.java new file mode 100644 index 00000000000..6e24f1fa302 --- /dev/null +++ b/src/main/java/net/minestom/server/entity/pathfinding/followers/NoPhysicsNodeFollower.java @@ -0,0 +1,80 @@ +package net.minestom.server.entity.pathfinding.followers; + +import net.minestom.server.coordinate.Point; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.coordinate.Vec; +import net.minestom.server.entity.Entity; +import net.minestom.server.entity.LivingEntity; +import net.minestom.server.entity.attribute.Attribute; +import net.minestom.server.utils.position.PositionUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class NoPhysicsNodeFollower implements NodeFollower { + private final Entity entity; + + public NoPhysicsNodeFollower(@NotNull Entity entity) { + this.entity = entity; + } + + /** + * Used to move the entity toward {@code direction} in the X and Z axis + * Gravity is still applied but the entity will not attempt to jump + * Also update the yaw/pitch of the entity to look along 'direction' + * + * @param direction the targeted position + * @param speed define how far the entity will move + */ + public void moveTowards(@NotNull Point direction, double speed, @NotNull Point lookAt) { + final Pos position = entity.getPosition(); + final double dx = direction.x() - position.x(); + final double dy = direction.y() - position.y(); + final double dz = direction.z() - position.z(); + + if (dy > 0 && entity.isOnGround()) jump(4f); + + final double dxLook = lookAt.x() - position.x(); + final double dyLook = lookAt.y() - position.y(); + final double dzLook = lookAt.z() - position.z(); + + // the purpose of these few lines is to slow down entities when they reach their destination + final double distSquared = dx * dx + dy * dy + dz * dz; + if (speed > distSquared) { + speed = distSquared; + } + + final double radians = Math.atan2(dz, dx); + final double speedX = Math.cos(radians) * speed; + final double speedZ = Math.sin(radians) * speed; + final float yaw = PositionUtils.getLookYaw(dxLook, dzLook); + final float pitch = PositionUtils.getLookPitch(dxLook, dyLook, dzLook); + + var newPosition = position.add(speedX, 0, speedZ); + this.entity.refreshPosition(newPosition.withView(yaw, pitch)); + } + + @Override + public void jump(@Nullable Point point, @Nullable Point target) { + if (entity.isOnGround()) { + jump(4f); + } + } + + @Override + public boolean isAtPoint(@NotNull Point point) { + return entity.getPosition().sameBlock(point); + } + + public void jump(float height) { + this.entity.setVelocity(new Vec(0, height * 2.5f, 0)); + } + + @Override + public double movementSpeed() { + if (entity instanceof LivingEntity living) { + return living.getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).getValue(); + } + + return 0.1f; + } +} diff --git a/src/main/java/net/minestom/server/entity/pathfinding/followers/NodeFollower.java b/src/main/java/net/minestom/server/entity/pathfinding/followers/NodeFollower.java new file mode 100644 index 00000000000..46de7ee77b6 --- /dev/null +++ b/src/main/java/net/minestom/server/entity/pathfinding/followers/NodeFollower.java @@ -0,0 +1,35 @@ +package net.minestom.server.entity.pathfinding.followers; + +import net.minestom.server.collision.PhysicsResult; +import net.minestom.server.coordinate.Point; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface NodeFollower { + /** + * Move towards the specified point + * + * @param target the point to move towards + * @param speed the speed to move at + * @param lookAt the point to look at + */ + void moveTowards(@NotNull Point target, double speed, @NotNull Point lookAt); + + /** + * Jump + */ + void jump(@Nullable Point point, @Nullable Point target); + + /** + * Check if the follower is at the specified point + * @param point the point to check + * @return true if the follower is at the point + */ + boolean isAtPoint(@NotNull Point point); + + /** + * Get the movement speed of the follower + * @return the movement speed + */ + double movementSpeed(); +} \ No newline at end of file diff --git a/src/main/java/net/minestom/server/entity/pathfinding/followers/WaterNodeFollower.java b/src/main/java/net/minestom/server/entity/pathfinding/followers/WaterNodeFollower.java new file mode 100644 index 00000000000..ee1a42763f5 --- /dev/null +++ b/src/main/java/net/minestom/server/entity/pathfinding/followers/WaterNodeFollower.java @@ -0,0 +1,85 @@ +package net.minestom.server.entity.pathfinding.followers; + +import net.minestom.server.collision.CollisionUtils; +import net.minestom.server.collision.PhysicsResult; +import net.minestom.server.coordinate.Point; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.coordinate.Vec; +import net.minestom.server.entity.Entity; +import net.minestom.server.entity.LivingEntity; +import net.minestom.server.entity.attribute.Attribute; +import net.minestom.server.utils.position.PositionUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class WaterNodeFollower implements NodeFollower { + private final Entity entity; + private static final double WATER_SPEED_MULTIPLIER = 0.5; + + public WaterNodeFollower(@NotNull Entity entity) { + this.entity = entity; + } + + /** + * Used to move the entity toward {@code direction} in the X and Z axis + * Gravity is still applied but the entity will not attempt to jump + * Also update the yaw/pitch of the entity to look along 'direction' + * + * @param direction the targeted position + * @param speed define how far the entity will move + */ + public void moveTowards(@NotNull Point direction, double speed, @NotNull Point lookAt) { + final Pos position = entity.getPosition(); + final double dx = direction.x() - position.x(); + final double dy = direction.y() - position.y(); + final double dz = direction.z() - position.z(); + + final double dxLook = lookAt.x() - position.x(); + final double dyLook = lookAt.y() - position.y(); + final double dzLook = lookAt.z() - position.z(); + + // the purpose of these few lines is to slow down entities when they reach their destination + final double distSquared = dx * dx + dy * dy + dz * dz; + if (speed > distSquared) { + speed = distSquared; + } + + var instance = entity.getInstance(); + if (instance != null) + if (instance.getBlock(position).isLiquid()) { + speed *= WATER_SPEED_MULTIPLIER; + } + + final double radians = Math.atan2(dz, dx); + final double speedX = Math.cos(radians) * speed; + final double speedZ = Math.sin(radians) * speed; + final float yaw = PositionUtils.getLookYaw(dxLook, dzLook); + final float pitch = PositionUtils.getLookPitch(dxLook, dyLook, dzLook); + + double speedY = Math.signum(dy) * 0.5 * speed; + if (Math.min(Math.abs(dy), Math.abs(speedY)) == Math.abs(dy)) { + speedY = dy; + } + + final var physicsResult = CollisionUtils.handlePhysics(entity, new Vec(speedX, speedY, speedZ)); + this.entity.refreshPosition(Pos.fromPoint(physicsResult.newPosition()).withView(yaw, pitch)); + } + + @Override + public void jump(@Nullable Point point, @Nullable Point target) { + } + + @Override + public boolean isAtPoint(@NotNull Point point) { + return entity.getPosition().sameBlock(point); + } + + @Override + public double movementSpeed() { + if (entity instanceof LivingEntity living) { + return living.getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).getValue(); + } + + return 0.1f; + } +} diff --git a/src/main/java/net/minestom/server/entity/pathfinding/generators/FlyingNodeGenerator.java b/src/main/java/net/minestom/server/entity/pathfinding/generators/FlyingNodeGenerator.java new file mode 100644 index 00000000000..c3ecf4a81a1 --- /dev/null +++ b/src/main/java/net/minestom/server/entity/pathfinding/generators/FlyingNodeGenerator.java @@ -0,0 +1,101 @@ +package net.minestom.server.entity.pathfinding.generators; + +import net.minestom.server.collision.BoundingBox; +import net.minestom.server.coordinate.Point; +import net.minestom.server.coordinate.Vec; +import net.minestom.server.entity.pathfinding.PNode; +import net.minestom.server.instance.Instance; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.OptionalDouble; +import java.util.Set; + +public class FlyingNodeGenerator implements NodeGenerator { + private PNode tempNode = null; + + @Override + public @NotNull Collection getWalkable(@NotNull Instance instance, @NotNull Set visited, @NotNull PNode current, @NotNull Point goal, @NotNull BoundingBox boundingBox) { + Collection nearby = new ArrayList<>(); + tempNode = new PNode(0, 0, 0, 0, 0, current); + + int stepSize = (int) Math.max(Math.floor(boundingBox.width() / 2), 1); + if (stepSize < 1) stepSize = 1; + + for (int x = -stepSize; x <= stepSize; ++x) { + for (int z = -stepSize; z <= stepSize; ++z) { + if (x == 0 && z == 0) continue; + double cost = Math.sqrt(x * x + z * z) * 0.98; + + double currentLevelPointX = current.blockX() + 0.5 + x; + double currentLevelPointY = current.blockY() + 0.5; + double currentLevelPointZ = current.blockZ() + 0.5 + z; + + double upPointX = current.blockX() + 0.5 + x; + double upPointY = current.blockY() + 1 + 0.5; + double upPointZ = current.blockZ() + 0.5 + z; + + double downPointX = current.blockX() + 0.5 + x; + double downPointY = current.blockY() - 1 + 0.5; + double downPointZ = current.blockZ() + 0.5 + z; + + var nodeWalk = createFly(instance, new Vec(currentLevelPointX, currentLevelPointY, currentLevelPointZ), boundingBox, cost, current, goal, visited); + if (nodeWalk != null && !visited.contains(nodeWalk)) nearby.add(nodeWalk); + + var nodeJump = createFly(instance, new Vec(upPointX, upPointY, upPointZ), boundingBox, cost, current, goal, visited); + if (nodeJump != null && !visited.contains(nodeJump)) nearby.add(nodeJump); + + var nodeFall = createFly(instance, new Vec(downPointX, downPointY, downPointZ), boundingBox, cost, current, goal, visited); + if (nodeFall != null && !visited.contains(nodeFall)) nearby.add(nodeFall); + } + } + + // Straight up + double upPointX = current.x(); + double upPointY = current.blockY() + 1 + 0.5; + double upPointZ = current.z(); + + var nodeJump = createFly(instance, new Vec(upPointX, upPointY, upPointZ), boundingBox, 2, current, goal, visited); + if (nodeJump != null && !visited.contains(nodeJump)) nearby.add(nodeJump); + + // Straight down + double downPointX = current.x(); + double downPointY = current.blockY() - 1 + 0.5; + double downPointZ = current.z(); + + var nodeFall = createFly(instance, new Vec(downPointX, downPointY, downPointZ), boundingBox, 2, current, goal, visited); + if (nodeFall != null && !visited.contains(nodeFall)) nearby.add(nodeFall); + + return nearby; + } + + @Override + public boolean hasGravitySnap() { + return false; + } + + private PNode createFly(Instance instance, Point point, BoundingBox boundingBox, double cost, PNode start, Point goal, Set closed) { + var n = newNode(start, cost, point, goal); + if (closed.contains(n)) return null; + if (!canMoveTowards(instance, new Vec(start.x(), start.y(), start.z()), point, boundingBox)) return null; + n.setType(PNode.NodeType.FLY); + return n; + } + + private PNode newNode(PNode current, double cost, Point point, Point goal) { + tempNode.setG(current.g() + cost); + tempNode.setH(heuristic(point, goal)); + tempNode.setPoint(point.x(), point.y(), point.z()); + + var newNode = tempNode; + tempNode = new PNode(0, 0, 0, 0, 0, PNode.NodeType.WALK, current); + + return newNode; + } + + @Override + public @NotNull OptionalDouble gravitySnap(@NotNull Instance instance, double pointX, double pointY, double pointZ, @NotNull BoundingBox boundingBox, double maxFall) { + return OptionalDouble.of(pointY); + } +} diff --git a/src/main/java/net/minestom/server/entity/pathfinding/generators/GroundNodeGenerator.java b/src/main/java/net/minestom/server/entity/pathfinding/generators/GroundNodeGenerator.java new file mode 100644 index 00000000000..63f9e83a340 --- /dev/null +++ b/src/main/java/net/minestom/server/entity/pathfinding/generators/GroundNodeGenerator.java @@ -0,0 +1,133 @@ +package net.minestom.server.entity.pathfinding.generators; + +import net.minestom.server.collision.BoundingBox; +import net.minestom.server.coordinate.Point; +import net.minestom.server.coordinate.Vec; +import net.minestom.server.entity.pathfinding.PNode; +import net.minestom.server.instance.Chunk; +import net.minestom.server.instance.Instance; +import net.minestom.server.instance.block.Block; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.OptionalDouble; +import java.util.Set; + +public class GroundNodeGenerator implements NodeGenerator { + private PNode tempNode = null; + private final BoundingBox.PointIterator pointIterator = new BoundingBox.PointIterator(); + private final static int MAX_FALL_DISTANCE = 5; + + @Override + public @NotNull Collection getWalkable(@NotNull Instance instance, @NotNull Set visited, @NotNull PNode current, @NotNull Point goal, @NotNull BoundingBox boundingBox) { + Collection nearby = new ArrayList<>(); + tempNode = new PNode(0, 0, 0, 0, 0, current); + + int stepSize = (int) Math.max(Math.floor(boundingBox.width() / 2), 1); + if (stepSize < 1) stepSize = 1; + + for (int x = -stepSize; x <= stepSize; ++x) { + for (int z = -stepSize; z <= stepSize; ++z) { + if (x == 0 && z == 0) continue; + double cost = Math.sqrt(x * x + z * z) * 0.98; + + double floorPointX = current.blockX() + 0.5 + x; + double floorPointY = current.blockY(); + double floorPointZ = current.blockZ() + 0.5 + z; + + var optionalFloorPointY = gravitySnap(instance, floorPointX, floorPointY, floorPointZ, boundingBox, MAX_FALL_DISTANCE); + if (optionalFloorPointY.isEmpty()) continue; + floorPointY = optionalFloorPointY.getAsDouble(); + + var floorPoint = new Vec(floorPointX, floorPointY, floorPointZ); + + var nodeWalk = createWalk(instance, floorPoint, boundingBox, cost, current, goal, visited); + if (nodeWalk != null && !visited.contains(nodeWalk)) nearby.add(nodeWalk); + + for (int i = 1; i <= 1; ++i) { + Point jumpPoint = new Vec(current.blockX() + 0.5 + x, current.blockY() + i, current.blockZ() + 0.5 + z); + OptionalDouble jumpPointY = gravitySnap(instance, jumpPoint.x(), jumpPoint.y(), jumpPoint.z(), boundingBox, MAX_FALL_DISTANCE); + if (jumpPointY.isEmpty()) continue; + jumpPoint = jumpPoint.withY(jumpPointY.getAsDouble()); + + if (!floorPoint.sameBlock(jumpPoint)) { + var nodeJump = createJump(instance, jumpPoint, boundingBox, cost + 0.2, current, goal, visited); + if (nodeJump != null && !visited.contains(nodeJump)) nearby.add(nodeJump); + } + } + } + } + + return nearby; + } + + @Override + public boolean hasGravitySnap() { + return true; + } + + private PNode createWalk(Instance instance, Point point, BoundingBox boundingBox, double cost, PNode start, Point goal, Set closed) { + var n = newNode(start, cost, point, goal); + if (closed.contains(n)) return null; + + if (Math.abs(point.y() - start.y()) > Vec.EPSILON && point.y() < start.y()) { + if (start.y() - point.y() > MAX_FALL_DISTANCE) return null; + if (!canMoveTowards(instance, new Vec(start.x(), start.y(), start.z()), point.withY(start.y()), boundingBox)) return null; + n.setType(PNode.NodeType.FALL); + } else { + if (!canMoveTowards(instance, new Vec(start.x(), start.y(), start.z()), point, boundingBox)) return null; + } + return n; + } + + private PNode createJump(Instance instance, Point point, BoundingBox boundingBox, double cost, PNode start, Point goal, Set closed) { + if (Math.abs(point.y() - start.y()) < Vec.EPSILON) return null; + if (point.y() - start.y() > 2) return null; + if (point.blockX() != start.blockX() && point.blockZ() != start.blockZ()) return null; + + var n = newNode(start, cost, point, goal); + if (closed.contains(n)) return null; + + if (pointInvalid(instance, point, boundingBox)) return null; + if (pointInvalid(instance, new Vec(start.x(), start.y() + 1, start.z()), boundingBox)) return null; + + n.setType(PNode.NodeType.JUMP); + return n; + } + + private PNode newNode(PNode current, double cost, Point point, Point goal) { + tempNode.setG(current.g() + cost); + tempNode.setH(heuristic(point, goal)); + tempNode.setPoint(point.x(), point.y(), point.z()); + + var newNode = tempNode; + tempNode = new PNode(0, 0, 0, 0, 0, PNode.NodeType.WALK, current); + + return newNode; + } + + @Override + public @NotNull OptionalDouble gravitySnap(@NotNull Instance instance, double pointOrgX, double pointOrgY, double pointOrgZ, @NotNull BoundingBox boundingBox, double maxFall) { + double pointX = (int) Math.floor(pointOrgX) + 0.5; + double pointY = (int) Math.floor(pointOrgY); + double pointZ = (int) Math.floor(pointOrgZ) + 0.5; + + Chunk c = instance.getChunkAt(pointX, pointZ); + if (c == null) return OptionalDouble.of(pointY); + + for (int axis = 1; axis <= maxFall; ++axis) { + pointIterator.reset(boundingBox, pointX, pointY, pointZ, BoundingBox.AxisMask.Y, -axis); + + while (pointIterator.hasNext()) { + var block = pointIterator.next(); + + if (instance.getBlock(block.blockX(), block.blockY(), block.blockZ(), Block.Getter.Condition.TYPE).isSolid()) { + return OptionalDouble.of(block.blockY() + 1); + } + } + } + + return OptionalDouble.empty(); + } +} diff --git a/src/main/java/net/minestom/server/entity/pathfinding/generators/NodeGenerator.java b/src/main/java/net/minestom/server/entity/pathfinding/generators/NodeGenerator.java new file mode 100644 index 00000000000..247158b4a87 --- /dev/null +++ b/src/main/java/net/minestom/server/entity/pathfinding/generators/NodeGenerator.java @@ -0,0 +1,92 @@ +package net.minestom.server.entity.pathfinding.generators; + +import net.minestom.server.collision.BoundingBox; +import net.minestom.server.collision.CollisionUtils; +import net.minestom.server.collision.PhysicsResult; +import net.minestom.server.coordinate.Point; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.coordinate.Vec; +import net.minestom.server.entity.pathfinding.PNode; +import net.minestom.server.instance.Instance; +import net.minestom.server.instance.block.Block; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.OptionalDouble; +import java.util.Set; + +public interface NodeGenerator { + /** + * Gets the walkable nodes. + * + * @param instance the instance + * @param visited the visited nodes + * @param current the current node + * @param goal the goal + * @param boundingBox the bounding box + * @return the walkable nodes + */ + @NotNull Collection getWalkable(@NotNull Instance instance, @NotNull Set visited, @NotNull PNode current, @NotNull Point goal, @NotNull BoundingBox boundingBox); + + /** + * @return snap start and end points to the ground + */ + boolean hasGravitySnap(); + + /** + * Snap point to the ground + * @param instance the instance + * @param pointX the x coordinate + * @param pointY the y coordinate + * @param pointZ the z coordinate + * @param boundingBox the bounding box + * @param maxFall the maximum fall distance + * @return the snapped y coordinate. Empty if the snap point is not found + */ + @NotNull OptionalDouble gravitySnap(@NotNull Instance instance, double pointX, double pointY, double pointZ, @NotNull BoundingBox boundingBox, double maxFall); + + /** + * Check if we can move directly from one point to another + * @param instance + * @param start + * @param end + * @param boundingBox + * @return true if we can move directly from start to end + */ + default boolean canMoveTowards(@NotNull Instance instance, @NotNull Point start, @NotNull Point end, @NotNull BoundingBox boundingBox) { + Point diff = end.sub(start); + + if (instance.getBlock(end) != Block.AIR) return false; + PhysicsResult res = CollisionUtils.handlePhysics(instance, instance.getChunkAt(start), boundingBox, Pos.fromPoint(start), Vec.fromPoint(diff), null, false); + return !res.collisionZ() && !res.collisionY() && !res.collisionX(); + } + + /** + * Check if the point is invalid + * @param instance + * @param point + * @param boundingBox + * @return true if the point is invalid + */ + default boolean pointInvalid(@NotNull Instance instance, @NotNull Point point, @NotNull BoundingBox boundingBox) { + var iterator = boundingBox.getBlocks(point); + while (iterator.hasNext()) { + var block = iterator.next(); + if (instance.getBlock(block.blockX(), block.blockY(), block.blockZ(), Block.Getter.Condition.TYPE).isSolid()) { + return true; + } + } + + return false; + } + + /** + * Heuristic use for the distance from the node to the target + * @param node + * @param target + * @return the heuristic + */ + default double heuristic(@NotNull Point node, @NotNull Point target) { + return node.distance(target); + } +} diff --git a/src/main/java/net/minestom/server/entity/pathfinding/generators/PreciseGroundNodeGenerator.java b/src/main/java/net/minestom/server/entity/pathfinding/generators/PreciseGroundNodeGenerator.java new file mode 100644 index 00000000000..cec5992dc85 --- /dev/null +++ b/src/main/java/net/minestom/server/entity/pathfinding/generators/PreciseGroundNodeGenerator.java @@ -0,0 +1,144 @@ +package net.minestom.server.entity.pathfinding.generators; + +import net.minestom.server.collision.BoundingBox; +import net.minestom.server.collision.CollisionUtils; +import net.minestom.server.collision.PhysicsResult; +import net.minestom.server.coordinate.Point; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.coordinate.Vec; +import net.minestom.server.entity.pathfinding.PNode; +import net.minestom.server.instance.Instance; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.OptionalDouble; +import java.util.Set; + +public class PreciseGroundNodeGenerator implements NodeGenerator { + private PNode tempNode = null; + private final static int MAX_FALL_DISTANCE = 5; + + @Override + public @NotNull Collection getWalkable(@NotNull Instance instance, @NotNull Set visited, @NotNull PNode current, @NotNull Point goal, @NotNull BoundingBox boundingBox) { + Collection nearby = new ArrayList<>(); + tempNode = new PNode(0, 0, 0, 0, 0, current); + + int stepSize = (int) Math.max(Math.floor(boundingBox.width() / 2), 1); + if (stepSize < 1) stepSize = 1; + + for (int x = -stepSize; x <= stepSize; ++x) { + for (int z = -stepSize; z <= stepSize; ++z) { + if (x == 0 && z == 0) continue; + double cost = Math.sqrt(x * x + z * z) * 0.98; + + double floorPointX = current.blockX() + 0.5 + x; + double floorPointY = current.y(); + double floorPointZ = current.blockZ() + 0.5 + z; + + var optionalFloorPointY = gravitySnap(instance, floorPointX, floorPointY, floorPointZ, boundingBox, MAX_FALL_DISTANCE); + if (optionalFloorPointY.isEmpty()) continue; + floorPointY = optionalFloorPointY.getAsDouble(); + + var floorPoint = new Vec(floorPointX, floorPointY, floorPointZ); + var nodeWalk = createWalk(instance, floorPoint, boundingBox, cost, current, goal, visited); + + if (nodeWalk != null && !visited.contains(nodeWalk)) nearby.add(nodeWalk); + + for (int i = 1; i <= 1; ++i) { + Point jumpPoint = new Vec(current.blockX() + 0.5 + x, current.y() + i, current.blockZ() + 0.5 + z); + OptionalDouble jumpPointY = gravitySnap(instance, jumpPoint.x(), jumpPoint.y(), jumpPoint.z(), boundingBox, MAX_FALL_DISTANCE); + if (jumpPointY.isEmpty()) continue; + jumpPoint = jumpPoint.withY(jumpPointY.getAsDouble()); + + if (!floorPoint.sameBlock(jumpPoint)) { + var nodeJump = createJump(instance, jumpPoint, boundingBox, cost + 0.8, current, goal, visited); + if (nodeJump != null && !visited.contains(nodeJump)) nearby.add(nodeJump); + } + } + } + } + + return nearby; + } + + @Override + public boolean hasGravitySnap() { + return true; + } + + private PNode createWalk(Instance instance, Point point, BoundingBox boundingBox, double cost, PNode start, Point goal, Set closed) { + var snapped = gravitySnap(instance, point.x(), point.y(), point.z(), boundingBox, MAX_FALL_DISTANCE); + + if (snapped.isPresent()) { + var snappedPoint = new Vec(point.x(), snapped.getAsDouble(), point.z()); + + var n = newNode(start, cost, snappedPoint, goal); + if (closed.contains(n)) { + return null; + } + + if (Math.abs(snappedPoint.y() - start.y()) > Vec.EPSILON && snappedPoint.y() < start.y()) { + if (start.y() - snappedPoint.y() > MAX_FALL_DISTANCE) { + return null; + } + if (!canMoveTowards(instance, new Vec(start.x(), start.y(), start.z()), snappedPoint.withY(start.y()), boundingBox)) { + return null; + } + n.setType(PNode.NodeType.FALL); + } else { + if (!canMoveTowards(instance, new Vec(start.x(), start.y(), start.z()), snappedPoint, boundingBox)) { + return null; + } + } + + return n; + } else { + return null; + } + } + + private PNode createJump(Instance instance, Point point, BoundingBox boundingBox, double cost, PNode start, Point goal, Set closed) { + if (Math.abs(point.y() - start.y()) < Vec.EPSILON) return null; + if (point.y() - start.y() > 2) return null; + if (point.blockX() != start.blockX() && point.blockZ() != start.blockZ()) return null; + + var n = newNode(start, cost, point, goal); + if (closed.contains(n)) return null; + + if (pointInvalid(instance, point, boundingBox)) return null; + if (pointInvalid(instance, new Vec(start.x(), start.y() + 1, start.z()), boundingBox)) return null; + + n.setType(PNode.NodeType.JUMP); + return n; + } + + private PNode newNode(PNode current, double cost, Point point, Point goal) { + tempNode.setG(current.g() + cost); + tempNode.setH(heuristic(point, goal)); + tempNode.setPoint(point.x(), point.y(), point.z()); + + var newNode = tempNode; + tempNode = new PNode(0, 0, 0, 0, 0, PNode.NodeType.WALK, current); + + return newNode; + } + + @Override + public @NotNull OptionalDouble gravitySnap(@NotNull Instance instance, double pointOrgX, double pointOrgY, double pointOrgZ, @NotNull BoundingBox boundingBox, double maxFall) { + double pointX = (int) Math.floor(pointOrgX) + 0.5; + double pointZ = (int) Math.floor(pointOrgZ) + 0.5; + var res= CollisionUtils.handlePhysics(instance, boundingBox, new Pos(pointX, pointOrgY, pointZ), new Vec(0, -MAX_FALL_DISTANCE, 0), null, true); + return OptionalDouble.of(res.newPosition().y()); + } + + @Override + public boolean canMoveTowards(@NotNull Instance instance, @NotNull Point startOrg, @NotNull Point endOrg, @NotNull BoundingBox boundingBox) { + var end = endOrg.add(0, Vec.EPSILON, 0); + var start = startOrg.add(0, Vec.EPSILON, 0); + + Point diff = end.sub(start); + PhysicsResult res = CollisionUtils.handlePhysics(instance, instance.getChunkAt(start), boundingBox, Pos.fromPoint(start), Vec.fromPoint(diff), null, false); + return !res.collisionZ() && !res.collisionY() && !res.collisionX(); + } +} diff --git a/src/main/java/net/minestom/server/entity/pathfinding/generators/WaterNodeGenerator.java b/src/main/java/net/minestom/server/entity/pathfinding/generators/WaterNodeGenerator.java new file mode 100644 index 00000000000..97d83eacd41 --- /dev/null +++ b/src/main/java/net/minestom/server/entity/pathfinding/generators/WaterNodeGenerator.java @@ -0,0 +1,134 @@ +package net.minestom.server.entity.pathfinding.generators; + +import net.minestom.server.collision.BoundingBox; +import net.minestom.server.coordinate.Point; +import net.minestom.server.coordinate.Vec; +import net.minestom.server.entity.pathfinding.PNode; +import net.minestom.server.instance.Chunk; +import net.minestom.server.instance.Instance; +import net.minestom.server.instance.block.Block; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.OptionalDouble; +import java.util.Set; + +public class WaterNodeGenerator implements NodeGenerator { + private PNode tempNode = null; + private final BoundingBox.PointIterator pointIterator = new BoundingBox.PointIterator(); + + @Override + public @NotNull Collection getWalkable(@NotNull Instance instance, @NotNull Set visited, @NotNull PNode current, @NotNull Point goal, @NotNull BoundingBox boundingBox) { + Collection nearby = new ArrayList<>(); + tempNode = new PNode(0, 0, 0, 0, 0, current); + + int stepSize = (int) Math.max(Math.floor(boundingBox.width() / 2), 1); + if (stepSize < 1) stepSize = 1; + + for (int x = -stepSize; x <= stepSize; ++x) { + for (int z = -stepSize; z <= stepSize; ++z) { + if (x == 0 && z == 0) continue; + double cost = Math.sqrt(x * x + z * z) * 0.98; + + double currentLevelPointX = current.blockX() + 0.5 + x; + double currentLevelPointY = current.blockY(); + double currentLevelPointZ = current.blockZ() + 0.5 + z; + + double upPointX = current.blockX() + 0.5 + x; + double upPointY = current.blockY() + 1 + 0.5; + double upPointZ = current.blockZ() + 0.5 + z; + + double downPointX = current.blockX() + 0.5 + x; + double downPointY = current.blockY() - 1 + 0.5; + double downPointZ = current.blockZ() + 0.5 + z; + + if (instance.getBlock((int) Math.floor(currentLevelPointX), (int) Math.floor(currentLevelPointY), (int) Math.floor(currentLevelPointZ)).compare(Block.WATER)) { + var nodeWalk = createFly(instance, new Vec(currentLevelPointX, currentLevelPointY, currentLevelPointZ), boundingBox, cost, current, goal, visited); + if (nodeWalk != null && !visited.contains(nodeWalk)) nearby.add(nodeWalk); + } + + if (instance.getBlock((int) Math.floor(upPointX), (int) Math.floor(upPointY), (int) Math.floor(upPointZ)).compare(Block.WATER)) { + var nodeJump = createFly(instance, new Vec(upPointX, upPointY, upPointZ), boundingBox, cost, current, goal, visited); + if (nodeJump != null && !visited.contains(nodeJump)) nearby.add(nodeJump); + } + + if (instance.getBlock((int) Math.floor(downPointX), (int) Math.floor(downPointY), (int) Math.floor(downPointZ)).compare(Block.WATER)) { + var nodeFall = createFly(instance, new Vec(downPointX, downPointY, downPointZ), boundingBox, cost, current, goal, visited); + if (nodeFall != null && !visited.contains(nodeFall)) nearby.add(nodeFall); + } + } + } + + // Straight up + double upPointX = current.x(); + double upPointY = current.blockY() + 1 + 0.5; + double upPointZ = current.z(); + + if (instance.getBlock((int) Math.floor(upPointX), (int) Math.floor(upPointY), (int) Math.floor(upPointZ)).compare(Block.WATER)) { + var nodeJump = createFly(instance, new Vec(current.x(), current.y(), current.z()), boundingBox, 2, current, goal, visited); + if (nodeJump != null && !visited.contains(nodeJump)) nearby.add(nodeJump); + } + + // Straight down + double downPointX = current.x(); + double downPointY = current.blockY() - 1 + 0.5; + double downPointZ = current.z(); + + if (instance.getBlock((int) Math.floor(downPointX), (int) Math.floor(downPointY), (int) Math.floor(downPointZ)).compare(Block.WATER)) { + var nodeFall = createFly(instance, new Vec(downPointX, downPointY, downPointZ), boundingBox, 2, current, goal, visited); + if (nodeFall != null && !visited.contains(nodeFall)) nearby.add(nodeFall); + } + + return nearby; + } + + @Override + public boolean hasGravitySnap() { + return false; + } + + private PNode createFly(Instance instance, Point point, BoundingBox boundingBox, double cost, PNode start, Point goal, Set closed) { + var n = newNode(start, cost, point, goal); + if (closed.contains(n)) return null; + if (!canMoveTowards(instance, new Vec(start.x(), start.y(), start.z()), point, boundingBox)) return null; + n.setType(PNode.NodeType.FLY); + return n; + } + + private PNode newNode(PNode current, double cost, Point point, Point goal) { + tempNode.setG(current.g() + cost); + tempNode.setH(heuristic(point, goal)); + tempNode.setPoint(point.x(), point.y(), point.z()); + + var newNode = tempNode; + tempNode = new PNode(0, 0, 0, 0, 0, PNode.NodeType.WALK, current); + + return newNode; + } + + public @NotNull OptionalDouble gravitySnap(@NotNull Instance instance, double pointOrgX, double pointOrgY, double pointOrgZ, @NotNull BoundingBox boundingBox, double maxFall) { + double pointX = (int) Math.floor(pointOrgX) + 0.5; + double pointY = (int) Math.floor(pointOrgY); + double pointZ = (int) Math.floor(pointOrgZ) + 0.5; + + Chunk c = instance.getChunkAt(pointX, pointZ); + if (c == null) return OptionalDouble.of(pointY); + + for (int axis = 1; axis <= maxFall; ++axis) { + pointIterator.reset(boundingBox, pointX, pointY, pointZ, BoundingBox.AxisMask.Y, -axis); + + while (pointIterator.hasNext()) { + var block = pointIterator.next(); + + var foundBlock = instance.getBlock(block.blockX(), block.blockY(), block.blockZ(), Block.Getter.Condition.TYPE); + // Stop falling when water is hit + if (foundBlock.isSolid() || foundBlock.compare(Block.WATER)) { + return OptionalDouble.of(block.blockY() + 1); + } + } + } + + return OptionalDouble.empty(); + } +} diff --git a/src/main/java/net/minestom/server/entity/villager/VillagerProfession.java b/src/main/java/net/minestom/server/entity/villager/VillagerProfession.java deleted file mode 100644 index faf65662f88..00000000000 --- a/src/main/java/net/minestom/server/entity/villager/VillagerProfession.java +++ /dev/null @@ -1,42 +0,0 @@ -package net.minestom.server.entity.villager; - -import net.kyori.adventure.key.Key; -import net.minestom.server.registry.Registry; -import net.minestom.server.registry.StaticProtocolObject; -import net.minestom.server.utils.NamespaceID; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; - -public interface VillagerProfession extends StaticProtocolObject, VillagerProfessions { - static @NotNull Collection<@NotNull VillagerProfession> values() { - return VillagerProfessionImpl.values(); - } - - static @Nullable VillagerProfession fromNamespaceId(@NotNull String namespaceID) { - return VillagerProfessionImpl.getSafe(namespaceID); - } - - static @Nullable VillagerProfession fromNamespaceId(@NotNull NamespaceID namespaceID) { - return fromNamespaceId(namespaceID.asString()); - } - - static @Nullable VillagerProfession fromId(int id) { - return VillagerProfessionImpl.getId(id); - } - - @Override - default @NotNull Key key() { - return StaticProtocolObject.super.key(); - } - - @Contract(pure = true) - Registry.VillagerProfession registry(); - - @Override - default @NotNull NamespaceID namespace() { - return registry().namespace(); - } -} diff --git a/src/main/java/net/minestom/server/entity/villager/VillagerProfessionImpl.java b/src/main/java/net/minestom/server/entity/villager/VillagerProfessionImpl.java deleted file mode 100644 index 338cbb39e3f..00000000000 --- a/src/main/java/net/minestom/server/entity/villager/VillagerProfessionImpl.java +++ /dev/null @@ -1,39 +0,0 @@ -package net.minestom.server.entity.villager; - -import net.minestom.server.registry.Registry; -import org.jetbrains.annotations.NotNull; - -import java.util.Collection; - -public record VillagerProfessionImpl(Registry.VillagerProfession registry, int id) implements VillagerProfession { - private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.VILLAGER_PROFESSION, VillagerProfessionImpl::createImpl); - - public VillagerProfessionImpl(Registry.VillagerProfession registry) { - this(registry, registry.id()); - } - - private static VillagerProfession createImpl(String namespace, Registry.Properties properties) { - return new VillagerProfessionImpl(Registry.villagerProfession(namespace, properties)); - } - - static VillagerProfession get(@NotNull String namespace) { - return CONTAINER.get(namespace); - } - - static VillagerProfession getSafe(@NotNull String namespace) { - return CONTAINER.getSafe(namespace); - } - - static VillagerProfession getId(int id) { - return CONTAINER.getId(id); - } - - static Collection values() { - return CONTAINER.values(); - } - - @Override - public String toString() { - return name(); - } -} diff --git a/src/main/java/net/minestom/server/entity/villager/VillagerType.java b/src/main/java/net/minestom/server/entity/villager/VillagerType.java deleted file mode 100644 index ac8fcfd4f21..00000000000 --- a/src/main/java/net/minestom/server/entity/villager/VillagerType.java +++ /dev/null @@ -1,43 +0,0 @@ -package net.minestom.server.entity.villager; - -import net.kyori.adventure.key.Key; -import net.minestom.server.registry.Registry; -import net.minestom.server.registry.StaticProtocolObject; -import net.minestom.server.utils.NamespaceID; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; - -public sealed interface VillagerType extends StaticProtocolObject, VillagerTypes permits VillagerTypeImpl { - - static @NotNull Collection<@NotNull VillagerType> values() { - return VillagerTypeImpl.values(); - } - - static @Nullable VillagerType fromNamespaceId(@NotNull String namespaceID) { - return VillagerTypeImpl.getSafe(namespaceID); - } - - static @Nullable VillagerType fromNamespaceId(@NotNull NamespaceID namespaceID) { - return fromNamespaceId(namespaceID.asString()); - } - - static @Nullable VillagerType fromId(int id) { - return VillagerTypeImpl.getId(id); - } - - @Override - default @NotNull Key key() { - return StaticProtocolObject.super.key(); - } - - @Contract(pure = true) - Registry.VillagerType registry(); - - @Override - default @NotNull NamespaceID namespace() { - return registry().namespace(); - } -} diff --git a/src/main/java/net/minestom/server/entity/villager/VillagerTypeImpl.java b/src/main/java/net/minestom/server/entity/villager/VillagerTypeImpl.java deleted file mode 100644 index ec8af984f09..00000000000 --- a/src/main/java/net/minestom/server/entity/villager/VillagerTypeImpl.java +++ /dev/null @@ -1,39 +0,0 @@ -package net.minestom.server.entity.villager; - -import net.minestom.server.registry.Registry; -import org.jetbrains.annotations.NotNull; - -import java.util.Collection; - -public record VillagerTypeImpl(Registry.VillagerType registry, int id) implements VillagerType { - private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.VILLAGER_TYPES, VillagerTypeImpl::createImpl); - - public VillagerTypeImpl(Registry.VillagerType registry) { - this(registry, registry.id()); - } - - private static VillagerType createImpl(String namespace, Registry.Properties properties) { - return new VillagerTypeImpl(Registry.villagerType(namespace, properties)); - } - - static VillagerType get(@NotNull String namespace) { - return CONTAINER.get(namespace); - } - - static VillagerType getSafe(@NotNull String namespace) { - return CONTAINER.getSafe(namespace); - } - - static VillagerType getId(int id) { - return CONTAINER.getId(id); - } - - static Collection values() { - return CONTAINER.values(); - } - - @Override - public String toString() { - return name(); - } -} diff --git a/src/main/java/net/minestom/server/event/EventNodeImpl.java b/src/main/java/net/minestom/server/event/EventNodeImpl.java index b732f87fe59..a3f643398a5 100644 --- a/src/main/java/net/minestom/server/event/EventNodeImpl.java +++ b/src/main/java/net/minestom/server/event/EventNodeImpl.java @@ -337,6 +337,7 @@ public boolean hasListener() { void invalidate() { this.updated = false; + this.listener = null; } @Nullable Consumer updatedListener() { diff --git a/src/main/java/net/minestom/server/event/entity/EntityDamageEvent.java b/src/main/java/net/minestom/server/event/entity/EntityDamageEvent.java index b0c2e5a9512..685bba25926 100644 --- a/src/main/java/net/minestom/server/event/entity/EntityDamageEvent.java +++ b/src/main/java/net/minestom/server/event/entity/EntityDamageEvent.java @@ -3,7 +3,6 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.LivingEntity; import net.minestom.server.entity.damage.Damage; -import net.minestom.server.entity.damage.DamageType; import net.minestom.server.event.trait.CancellableEvent; import net.minestom.server.event.trait.EntityInstanceEvent; import net.minestom.server.sound.SoundEvent; @@ -11,7 +10,7 @@ import org.jetbrains.annotations.Nullable; /** - * Called with {@link LivingEntity#damage(DamageType, float)}. + * Called with {@link LivingEntity#damage(net.minestom.server.registry.DynamicRegistry.Key, float)}. */ public class EntityDamageEvent implements EntityInstanceEvent, CancellableEvent { diff --git a/src/main/java/net/minestom/server/event/entity/EntityFireEvent.java b/src/main/java/net/minestom/server/event/entity/EntityFireEvent.java deleted file mode 100644 index a7b4e301e3b..00000000000 --- a/src/main/java/net/minestom/server/event/entity/EntityFireEvent.java +++ /dev/null @@ -1,53 +0,0 @@ -package net.minestom.server.event.entity; - -import net.minestom.server.entity.Entity; -import net.minestom.server.event.trait.CancellableEvent; -import net.minestom.server.event.trait.EntityInstanceEvent; -import org.jetbrains.annotations.NotNull; - -import java.time.Duration; -import java.time.temporal.TemporalUnit; - -public class EntityFireEvent implements EntityInstanceEvent, CancellableEvent { - - private final Entity entity; - private Duration duration; - - private boolean cancelled; - - public EntityFireEvent(Entity entity, int duration, TemporalUnit temporalUnit) { - this(entity, Duration.of(duration, temporalUnit)); - } - - public EntityFireEvent(Entity entity, Duration duration) { - this.entity = entity; - setFireTime(duration); - } - - public long getFireTime(TemporalUnit temporalUnit) { - return duration.toNanos() / temporalUnit.getDuration().toNanos(); - } - - public void setFireTime(int duration, TemporalUnit temporalUnit) { - setFireTime(Duration.of(duration, temporalUnit)); - } - - public void setFireTime(Duration duration) { - this.duration = duration; - } - - @Override - public boolean isCancelled() { - return cancelled; - } - - @Override - public void setCancelled(boolean cancel) { - this.cancelled = cancel; - } - - @Override - public @NotNull Entity getEntity() { - return entity; - } -} diff --git a/src/main/java/net/minestom/server/event/entity/EntityFireExtinguishEvent.java b/src/main/java/net/minestom/server/event/entity/EntityFireExtinguishEvent.java new file mode 100644 index 00000000000..e24cc8602af --- /dev/null +++ b/src/main/java/net/minestom/server/event/entity/EntityFireExtinguishEvent.java @@ -0,0 +1,38 @@ +package net.minestom.server.event.entity; + +import net.minestom.server.entity.Entity; +import net.minestom.server.event.trait.CancellableEvent; +import net.minestom.server.event.trait.EntityInstanceEvent; +import org.jetbrains.annotations.NotNull; + +public class EntityFireExtinguishEvent implements EntityInstanceEvent, CancellableEvent { + + private final Entity entity; + private boolean natural; + + private boolean cancelled; + + public EntityFireExtinguishEvent(Entity entity, boolean natural) { + this.entity = entity; + this.natural = natural; + } + + public boolean isNatural() { + return natural; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public @NotNull Entity getEntity() { + return entity; + } +} diff --git a/src/main/java/net/minestom/server/event/entity/EntitySetFireEvent.java b/src/main/java/net/minestom/server/event/entity/EntitySetFireEvent.java new file mode 100644 index 00000000000..e82db94a53f --- /dev/null +++ b/src/main/java/net/minestom/server/event/entity/EntitySetFireEvent.java @@ -0,0 +1,42 @@ +package net.minestom.server.event.entity; + +import net.minestom.server.entity.Entity; +import net.minestom.server.event.trait.CancellableEvent; +import net.minestom.server.event.trait.EntityInstanceEvent; +import org.jetbrains.annotations.NotNull; + +public class EntitySetFireEvent implements EntityInstanceEvent, CancellableEvent { + + private final Entity entity; + private int ticks; + + private boolean cancelled; + + public EntitySetFireEvent(Entity entity, int ticks) { + this.entity = entity; + this.ticks = ticks; + } + + public int getFireTicks() { + return ticks; + } + + public void setFireTicks(int ticks) { + this.ticks = ticks; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public @NotNull Entity getEntity() { + return entity; + } +} diff --git a/src/main/java/net/minestom/server/event/item/ItemUpdateStateEvent.java b/src/main/java/net/minestom/server/event/item/ItemUpdateStateEvent.java index 2ae8cefc3b1..ecfd8baf2fb 100644 --- a/src/main/java/net/minestom/server/event/item/ItemUpdateStateEvent.java +++ b/src/main/java/net/minestom/server/event/item/ItemUpdateStateEvent.java @@ -6,11 +6,15 @@ import net.minestom.server.item.ItemStack; import org.jetbrains.annotations.NotNull; +/** + * Event when a player updates an item state, meaning when they stop using the item. + */ public class ItemUpdateStateEvent implements PlayerInstanceEvent, ItemEvent { private final Player player; private final Player.Hand hand; private final ItemStack itemStack; + private boolean handAnimation; private boolean riptideSpinAttack; diff --git a/src/main/java/net/minestom/server/event/item/ItemUsageCompleteEvent.java b/src/main/java/net/minestom/server/event/item/ItemUsageCompleteEvent.java new file mode 100644 index 00000000000..0205451ab64 --- /dev/null +++ b/src/main/java/net/minestom/server/event/item/ItemUsageCompleteEvent.java @@ -0,0 +1,39 @@ +package net.minestom.server.event.item; + +import net.minestom.server.entity.Player; +import net.minestom.server.event.trait.ItemEvent; +import net.minestom.server.event.trait.PlayerInstanceEvent; +import net.minestom.server.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +/** + * Event when the item usage duration has passed for a player, meaning when the item has completed its usage. + */ +public class ItemUsageCompleteEvent implements PlayerInstanceEvent, ItemEvent { + + private final Player player; + private final Player.Hand hand; + private final ItemStack itemStack; + + public ItemUsageCompleteEvent(@NotNull Player player, @NotNull Player.Hand hand, + @NotNull ItemStack itemStack) { + this.player = player; + this.hand = hand; + this.itemStack = itemStack; + } + + @NotNull + public Player.Hand getHand() { + return hand; + } + + @Override + public @NotNull ItemStack getItemStack() { + return itemStack; + } + + @Override + public @NotNull Player getPlayer() { + return player; + } +} diff --git a/src/main/java/net/minestom/server/event/player/AsyncPlayerConfigurationEvent.java b/src/main/java/net/minestom/server/event/player/AsyncPlayerConfigurationEvent.java index c4d1a9f41fd..355ebd258b9 100644 --- a/src/main/java/net/minestom/server/event/player/AsyncPlayerConfigurationEvent.java +++ b/src/main/java/net/minestom/server/event/player/AsyncPlayerConfigurationEvent.java @@ -1,17 +1,18 @@ package net.minestom.server.event.player; -import java.util.List; -import java.util.Set; - -import net.kyori.adventure.key.Namespaced; +import it.unimi.dsi.fastutil.objects.ObjectArraySet; +import it.unimi.dsi.fastutil.objects.ObjectSets; +import net.minestom.server.FeatureFlag; import net.minestom.server.entity.Player; import net.minestom.server.event.trait.PlayerEvent; -import net.minestom.server.featureflag.FeatureFlag; import net.minestom.server.instance.Instance; -import net.minestom.server.utils.NamespaceID; +import net.minestom.server.network.packet.server.configuration.ResetChatPacket; +import net.minestom.server.network.packet.server.configuration.UpdateEnabledFeaturesPacket; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Set; + /** * Called when a player enters the configuration state (either on first connection, or if they are * sent back to configuration later). The player is moved to the play state as soon as all event @@ -20,22 +21,27 @@ *

The spawning instance must be set for the player to join.

* *

The event is called off the tick threads, so it is safe to block here

+ * + *

It is valid to kick a player using {@link Player#kick(net.kyori.adventure.text.Component)} during this event.

*/ public class AsyncPlayerConfigurationEvent implements PlayerEvent { private final Player player; private final boolean isFirstConfig; + private final ObjectArraySet featureFlags = new ObjectArraySet<>(); private boolean hardcore; + private boolean clearChat; private boolean sendRegistryData; private Instance spawningInstance; - private Set enabledFeatures; - public AsyncPlayerConfigurationEvent(@NotNull Player player, boolean isFirstConfig, @NotNull Set enabledFeatures) { + public AsyncPlayerConfigurationEvent(@NotNull Player player, boolean isFirstConfig) { this.player = player; this.isFirstConfig = isFirstConfig; - this.enabledFeatures = enabledFeatures; + + this.featureFlags.add(FeatureFlag.VANILLA); // Vanilla feature-set, without this you get nothing at all. Kinda wacky! this.hardcore = false; + this.clearChat = false; this.sendRegistryData = isFirstConfig; this.spawningInstance = null; } @@ -60,6 +66,68 @@ public void setHardcore(boolean hardcore) { this.hardcore = hardcore; } + /** + * Add a feature flag, see Wiki.vg Feature Flags for a list of applicable features + * Note: the flag "minecraft:vanilla" is already included by default. + * + * @param feature A minecraft feature flag + * + * @see UpdateEnabledFeaturesPacket + * @see net.minestom.server.FeatureFlag + */ + public void addFeatureFlag(@NotNull FeatureFlag feature) { + this.featureFlags.add(feature); + } + + /** + * Remove a feature flag, see Wiki.vg Feature Flags for a list of applicable features + * Note: removing the flag "minecraft:vanilla" may result in weird behavior + * + * @param feature A minecraft feature flag + * @return if the feature specified existed prior to being removed + * + * @see UpdateEnabledFeaturesPacket + * @see net.minestom.server.FeatureFlag + */ + public boolean removeFeatureFlag(@NotNull FeatureFlag feature) { + return this.featureFlags.remove(feature); // Should this have sanity checking to see if the feature was actually contained in the list? + } + + /** + * The list of currently added feature flags. This is an unmodifiable copy of what will be sent to the client. + * + * @return An unmodifiable set of feature flags + * + * @see UpdateEnabledFeaturesPacket + * @see net.minestom.server.FeatureFlag + */ + public @NotNull Set getFeatureFlags() { + return ObjectSets.unmodifiable(this.featureFlags); + } + + /** + * If true, the player's chat will be cleared when exiting the configuration state, otherwise + * it will be preserved. The default is not to clear the chat. + * + * @return true if the chat will be cleared, false otherwise + * + * @see ResetChatPacket + */ + public boolean willClearChat() { + return clearChat; + } + + /** + * Set whether the player's chat will be cleared when exiting the configuration state. + * + * @param clearChat true to clear the chat, false otherwise + * + * @see ResetChatPacket + */ + public void setClearChat(boolean clearChat) { + this.clearChat = clearChat; + } + public boolean willSendRegistryData() { return sendRegistryData; } @@ -75,12 +143,4 @@ public void setSendRegistryData(boolean sendRegistryData) { public void setSpawningInstance(@Nullable Instance spawningInstance) { this.spawningInstance = spawningInstance; } - - public void setEnabledFeatures(Set enabledFeatures) { - this.enabledFeatures = enabledFeatures; - } - - public Set getEnabledFeatures() { - return enabledFeatures; - } } diff --git a/src/main/java/net/minestom/server/event/player/AsyncPlayerPreLoginEvent.java b/src/main/java/net/minestom/server/event/player/AsyncPlayerPreLoginEvent.java index 7dee182ddd9..13eea96a382 100644 --- a/src/main/java/net/minestom/server/event/player/AsyncPlayerPreLoginEvent.java +++ b/src/main/java/net/minestom/server/event/player/AsyncPlayerPreLoginEvent.java @@ -19,13 +19,11 @@ public class AsyncPlayerPreLoginEvent implements PlayerEvent { private final LoginPluginMessageProcessor pluginMessageProcessor; private String username; - private UUID playerUuid; public AsyncPlayerPreLoginEvent(@NotNull Player player, @NotNull LoginPluginMessageProcessor pluginMessageProcessor) { this.player = player; this.pluginMessageProcessor = pluginMessageProcessor; this.username = player.getUsername(); - this.playerUuid = player.getUuid(); } /** @@ -54,16 +52,7 @@ public void setUsername(@NotNull String username) { */ @NotNull public UUID getPlayerUuid() { - return playerUuid; - } - - /** - * Changes the player uuid. - * - * @param playerUuid the new player uuid - */ - public void setPlayerUuid(@NotNull UUID playerUuid) { - this.playerUuid = playerUuid; + return player.getUuid(); } /** diff --git a/src/main/java/net/minestom/server/event/player/PlayerAnvilInputEvent.java b/src/main/java/net/minestom/server/event/player/PlayerAnvilInputEvent.java new file mode 100644 index 00000000000..5307d3d5321 --- /dev/null +++ b/src/main/java/net/minestom/server/event/player/PlayerAnvilInputEvent.java @@ -0,0 +1,31 @@ +package net.minestom.server.event.player; + +import net.minestom.server.entity.Player; +import net.minestom.server.event.trait.PlayerInstanceEvent; +import net.minestom.server.network.packet.client.play.ClientNameItemPacket; +import org.jetbrains.annotations.NotNull; + +/** + * Called every time a {@link Player} types a letter in an anvil GUI. + * + * @see ClientNameItemPacket + */ +public class PlayerAnvilInputEvent implements PlayerInstanceEvent { + + private final Player player; + private final String input; + + public PlayerAnvilInputEvent(@NotNull Player player, @NotNull String input) { + this.player = player; + this.input = input; + } + + @Override + public @NotNull Player getPlayer() { + return player; + } + + public @NotNull String getInput() { + return input; + } +} diff --git a/src/main/java/net/minestom/server/event/player/PlayerBlockPlaceEvent.java b/src/main/java/net/minestom/server/event/player/PlayerBlockPlaceEvent.java index 7454103ba3e..07db5f41e39 100644 --- a/src/main/java/net/minestom/server/event/player/PlayerBlockPlaceEvent.java +++ b/src/main/java/net/minestom/server/event/player/PlayerBlockPlaceEvent.java @@ -19,6 +19,7 @@ public class PlayerBlockPlaceEvent implements PlayerInstanceEvent, BlockEvent, C private Block block; private final BlockFace blockFace; private final BlockVec blockPosition; + private final Point cursorPosition; private final Player.Hand hand; private boolean consumeBlock; @@ -27,12 +28,13 @@ public class PlayerBlockPlaceEvent implements PlayerInstanceEvent, BlockEvent, C private boolean cancelled; public PlayerBlockPlaceEvent(@NotNull Player player, @NotNull Block block, - @NotNull BlockFace blockFace, - @NotNull BlockVec blockPosition, @NotNull Player.Hand hand) { + @NotNull BlockFace blockFace, @NotNull BlockVec blockPosition, + @NotNull Point cursorPosition, @NotNull Player.Hand hand) { this.player = player; this.block = block; this.blockFace = blockFace; this.blockPosition = blockPosition; + this.cursorPosition = cursorPosition; this.hand = hand; this.consumeBlock = true; this.doBlockUpdates = true; @@ -71,6 +73,10 @@ public void setBlock(@NotNull Block block) { return blockPosition; } + public @NotNull Point getCursorPosition() { + return cursorPosition; + } + /** * Gets the hand with which the player is trying to place. * diff --git a/src/main/java/net/minestom/server/event/player/PlayerChangeHeldSlotEvent.java b/src/main/java/net/minestom/server/event/player/PlayerChangeHeldSlotEvent.java index 428082e600b..2c22707258d 100644 --- a/src/main/java/net/minestom/server/event/player/PlayerChangeHeldSlotEvent.java +++ b/src/main/java/net/minestom/server/event/player/PlayerChangeHeldSlotEvent.java @@ -23,9 +23,9 @@ public PlayerChangeHeldSlotEvent(@NotNull Player player, byte slot) { } /** - * Gets the slot which the player will held. + * Gets the slot which the player will hold. * - * @return the held slot + * @return the future slot */ public byte getSlot() { return slot; diff --git a/src/main/java/net/minestom/server/event/player/PlayerItemAnimationEvent.java b/src/main/java/net/minestom/server/event/player/PlayerItemAnimationEvent.java index 9340ba45c18..dcbda3ec248 100644 --- a/src/main/java/net/minestom/server/event/player/PlayerItemAnimationEvent.java +++ b/src/main/java/net/minestom/server/event/player/PlayerItemAnimationEvent.java @@ -6,7 +6,7 @@ import org.jetbrains.annotations.NotNull; /** - * Used when a {@link Player} finish the animation of an item. + * Used when a {@link Player} starts the animation of an item. * * @see ItemAnimationType */ @@ -51,7 +51,11 @@ public enum ItemAnimationType { CROSSBOW, TRIDENT, SHIELD, - EAT + SPYGLASS, + HORN, + BRUSH, + EAT, + OTHER } @Override diff --git a/src/main/java/net/minestom/server/event/player/PlayerPreEatEvent.java b/src/main/java/net/minestom/server/event/player/PlayerPreEatEvent.java index 0585af8ab08..5531c7ac14b 100644 --- a/src/main/java/net/minestom/server/event/player/PlayerPreEatEvent.java +++ b/src/main/java/net/minestom/server/event/player/PlayerPreEatEvent.java @@ -33,9 +33,7 @@ public PlayerPreEatEvent(@NotNull Player player, @NotNull ItemStack foodItem, @N } /** - * Gets the food eating time. - *

- * This is by default {@link Player#getDefaultEatingTime()}. + * Gets the food eating time in ticks. * * @return the eating time */ @@ -46,7 +44,7 @@ public long getEatingTime() { /** * Changes the food eating time. * - * @param eatingTime the new eating time + * @param eatingTime the new eating time in ticks */ public void setEatingTime(long eatingTime) { this.eatingTime = eatingTime; diff --git a/src/main/java/net/minestom/server/event/player/PlayerUseItemEvent.java b/src/main/java/net/minestom/server/event/player/PlayerUseItemEvent.java index 94202e4ea98..9f091dad574 100644 --- a/src/main/java/net/minestom/server/event/player/PlayerUseItemEvent.java +++ b/src/main/java/net/minestom/server/event/player/PlayerUseItemEvent.java @@ -1,6 +1,7 @@ package net.minestom.server.event.player; import net.minestom.server.entity.Player; +import net.minestom.server.event.item.ItemUpdateStateEvent; import net.minestom.server.event.trait.CancellableEvent; import net.minestom.server.event.trait.ItemEvent; import net.minestom.server.event.trait.PlayerInstanceEvent; @@ -16,12 +17,14 @@ public class PlayerUseItemEvent implements PlayerInstanceEvent, ItemEvent, Cance private final Player.Hand hand; private final ItemStack itemStack; + private long itemUseTime; private boolean cancelled; - public PlayerUseItemEvent(@NotNull Player player, @NotNull Player.Hand hand, @NotNull ItemStack itemStack) { + public PlayerUseItemEvent(@NotNull Player player, @NotNull Player.Hand hand, @NotNull ItemStack itemStack, long itemUseTime) { this.player = player; this.hand = hand; this.itemStack = itemStack; + this.itemUseTime = itemUseTime; } /** @@ -29,21 +32,39 @@ public PlayerUseItemEvent(@NotNull Player player, @NotNull Player.Hand hand, @No * * @return the hand used */ - @NotNull - public Player.Hand getHand() { + public @NotNull Player.Hand getHand() { return hand; } /** - * Gets the item which have been used. + * Gets the item which has been used. * * @return the item */ - @NotNull - public ItemStack getItemStack() { + @Override + public @NotNull ItemStack getItemStack() { return itemStack; } + /** + * Gets the item usage duration. After this amount of milliseconds, + * the animation will stop automatically and {@link ItemUpdateStateEvent} is called. + * + * @return the item use time + */ + public long getItemUseTime() { + return itemUseTime; + } + + /** + * Changes the item usage duration. + * + * @param itemUseTime the new item use time + */ + public void setItemUseTime(long itemUseTime) { + this.itemUseTime = itemUseTime; + } + @Override public boolean isCancelled() { return cancelled; diff --git a/src/main/java/net/minestom/server/event/trait/EntityInstanceEvent.java b/src/main/java/net/minestom/server/event/trait/EntityInstanceEvent.java index 4e36d53d407..75391186415 100644 --- a/src/main/java/net/minestom/server/event/trait/EntityInstanceEvent.java +++ b/src/main/java/net/minestom/server/event/trait/EntityInstanceEvent.java @@ -2,7 +2,6 @@ import net.minestom.server.entity.Entity; import net.minestom.server.instance.Instance; -import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; /** @@ -11,8 +10,6 @@ *

* Be aware that the entity's instance must be non-null. */ -@ApiStatus.Internal -@ApiStatus.Experimental public interface EntityInstanceEvent extends EntityEvent, InstanceEvent { @Override default @NotNull Instance getInstance() { diff --git a/src/main/java/net/minestom/server/event/trait/PlayerInstanceEvent.java b/src/main/java/net/minestom/server/event/trait/PlayerInstanceEvent.java index b39ac880681..70de7f3dc62 100644 --- a/src/main/java/net/minestom/server/event/trait/PlayerInstanceEvent.java +++ b/src/main/java/net/minestom/server/event/trait/PlayerInstanceEvent.java @@ -1,7 +1,6 @@ package net.minestom.server.event.trait; import net.minestom.server.entity.Player; -import org.jetbrains.annotations.ApiStatus; /** * Represents an {@link PlayerEvent} which happen in {@link Player#getInstance()}. @@ -9,7 +8,5 @@ *

* Be aware that the player's instance must be non-null. */ -@ApiStatus.Internal -@ApiStatus.Experimental public interface PlayerInstanceEvent extends PlayerEvent, EntityInstanceEvent { } diff --git a/src/main/java/net/minestom/server/featureflag/FeatureFlag.java b/src/main/java/net/minestom/server/featureflag/FeatureFlag.java deleted file mode 100644 index 485d9a873bd..00000000000 --- a/src/main/java/net/minestom/server/featureflag/FeatureFlag.java +++ /dev/null @@ -1,39 +0,0 @@ -package net.minestom.server.featureflag; - -import net.minestom.server.registry.Registry; -import net.minestom.server.registry.StaticProtocolObject; -import net.minestom.server.utils.NamespaceID; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; - -public sealed interface FeatureFlag extends StaticProtocolObject, FeatureFlags permits FeatureFlagImpl { - - /** - * Returns the entity registry. - * - * @return the entity registry or null if it was created with a builder - */ - @Contract(pure = true) - @Nullable - Registry.FeatureFlagEntry registry(); - - @Override - @NotNull - NamespaceID namespace(); - - static @NotNull Collection<@NotNull FeatureFlag> values() { - return FeatureFlagImpl.values(); - } - - static @Nullable FeatureFlag fromNamespaceId(@NotNull String namespaceID) { - return FeatureFlagImpl.getSafe(namespaceID); - } - - static @Nullable FeatureFlag fromNamespaceId(@NotNull NamespaceID namespaceID) { - return fromNamespaceId(namespaceID.asString()); - } - -} diff --git a/src/main/java/net/minestom/server/featureflag/FeatureFlagImpl.java b/src/main/java/net/minestom/server/featureflag/FeatureFlagImpl.java deleted file mode 100644 index e5aab62cbe6..00000000000 --- a/src/main/java/net/minestom/server/featureflag/FeatureFlagImpl.java +++ /dev/null @@ -1,33 +0,0 @@ -package net.minestom.server.featureflag; - -import net.minestom.server.registry.Registry; -import net.minestom.server.utils.NamespaceID; -import org.jetbrains.annotations.NotNull; - -import java.util.Collection; -import java.util.concurrent.atomic.AtomicInteger; - -public record FeatureFlagImpl(Registry.FeatureFlagEntry registry, NamespaceID namespace, int id) implements FeatureFlag { - private static final AtomicInteger INDEX = new AtomicInteger(); - private static final Registry.DynamicContainer CONTAINER = Registry.createDynamicContainer(Registry.Resource.FEATURE_FLAGS, FeatureFlagImpl::createImpl); - - private static FeatureFlagImpl createImpl(String namespace, Registry.Properties properties) { - return new FeatureFlagImpl(Registry.featureFlag(namespace, properties)); - } - - private FeatureFlagImpl(Registry.FeatureFlagEntry registry) { - this(registry, registry.namespace(), INDEX.getAndIncrement()); - } - - static Collection values() { - return CONTAINER.values(); - } - - public static FeatureFlag get(@NotNull String namespace) { - return CONTAINER.get(namespace); - } - - static FeatureFlag getSafe(@NotNull String namespace) { - return CONTAINER.getSafe(namespace); - } -} diff --git a/src/main/java/net/minestom/server/fluid/Fluid.java b/src/main/java/net/minestom/server/fluid/Fluid.java deleted file mode 100644 index c9e6bedf370..00000000000 --- a/src/main/java/net/minestom/server/fluid/Fluid.java +++ /dev/null @@ -1,37 +0,0 @@ -package net.minestom.server.fluid; - -import net.minestom.server.registry.Registry; -import net.minestom.server.registry.StaticProtocolObject; -import net.minestom.server.utils.NamespaceID; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; - -public sealed interface Fluid extends StaticProtocolObject, Fluids permits FluidImpl { - /** - * Returns the entity registry. - * - * @return the entity registry or null if it was created with a builder - */ - @Contract(pure = true) - @Nullable - Registry.FluidEntry registry(); - - @Override - @NotNull - NamespaceID namespace(); - - static @NotNull Collection<@NotNull Fluid> values() { - return FluidImpl.values(); - } - - static @Nullable Fluid fromNamespaceId(@NotNull String namespaceID) { - return FluidImpl.getSafe(namespaceID); - } - - static @Nullable Fluid fromNamespaceId(@NotNull NamespaceID namespaceID) { - return fromNamespaceId(namespaceID.asString()); - } -} diff --git a/src/main/java/net/minestom/server/fluid/FluidImpl.java b/src/main/java/net/minestom/server/fluid/FluidImpl.java deleted file mode 100644 index 8066c423375..00000000000 --- a/src/main/java/net/minestom/server/fluid/FluidImpl.java +++ /dev/null @@ -1,34 +0,0 @@ -package net.minestom.server.fluid; - -import net.minestom.server.registry.Registry; -import net.minestom.server.utils.NamespaceID; -import org.jetbrains.annotations.NotNull; - -import java.util.Collection; -import java.util.concurrent.atomic.AtomicInteger; - -public record FluidImpl(Registry.FluidEntry registry, NamespaceID namespace, int id) implements Fluid { - - private static final AtomicInteger INDEX = new AtomicInteger(); - private static final Registry.DynamicContainer CONTAINER = Registry.createDynamicContainer(Registry.Resource.FLUIDS, FluidImpl::createImpl); - - private static FluidImpl createImpl(String namespace, Registry.Properties properties) { - return new FluidImpl(Registry.fluidEntry(namespace, properties)); - } - - private FluidImpl(Registry.FluidEntry registry) { - this(registry, registry.namespace(), INDEX.getAndIncrement()); - } - - static Collection values() { - return CONTAINER.values(); - } - - public static Fluid get(@NotNull String namespace) { - return CONTAINER.get(namespace); - } - - static Fluid getSafe(@NotNull String namespace) { - return CONTAINER.getSafe(namespace); - } -} diff --git a/src/main/java/net/minestom/server/gamedata/DataPack.java b/src/main/java/net/minestom/server/gamedata/DataPack.java new file mode 100644 index 00000000000..cd40885b885 --- /dev/null +++ b/src/main/java/net/minestom/server/gamedata/DataPack.java @@ -0,0 +1,23 @@ +package net.minestom.server.gamedata; + +import net.minestom.server.utils.NamespaceID; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +@ApiStatus.Experimental +public sealed interface DataPack permits DataPackImpl { + + @NotNull DataPack MINECRAFT_CORE = new DataPackImpl(NamespaceID.from("minecraft:core"), true); + + /** + *

Returns true if this data pack is synced with the client. The null data pack is never synced.

+ * + *

In practice, this currently only makes sense for vanilla and modded content.

+ * + *

TODO: in the future this should be based on what the client responds with known packs, I suppose.

+ * + * @return true if this data pack is synced with the client, false otherwise. + */ + boolean isSynced(); + +} diff --git a/src/main/java/net/minestom/server/gamedata/DataPackImpl.java b/src/main/java/net/minestom/server/gamedata/DataPackImpl.java new file mode 100644 index 00000000000..e12b7fa1ed6 --- /dev/null +++ b/src/main/java/net/minestom/server/gamedata/DataPackImpl.java @@ -0,0 +1,12 @@ +package net.minestom.server.gamedata; + +import net.minestom.server.utils.NamespaceID; +import org.jetbrains.annotations.NotNull; + +record DataPackImpl(@NotNull NamespaceID namespaceId, boolean isSynced) implements DataPack { + + @Override + public boolean isSynced() { + return false; + } +} diff --git a/src/main/java/net/minestom/server/gamedata/tags/Tag.java b/src/main/java/net/minestom/server/gamedata/tags/Tag.java index 7e6a665f3a2..663f9a1299d 100644 --- a/src/main/java/net/minestom/server/gamedata/tags/Tag.java +++ b/src/main/java/net/minestom/server/gamedata/tags/Tag.java @@ -1,11 +1,17 @@ package net.minestom.server.gamedata.tags; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.Keyed; +import net.minestom.server.MinecraftServer; import net.minestom.server.entity.EntityType; -import net.minestom.server.fluid.Fluid; import net.minestom.server.instance.block.Block; import net.minestom.server.item.Material; +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.registry.FluidRegistries; +import net.minestom.server.registry.ProtocolObject; import net.minestom.server.registry.Registry; import net.minestom.server.utils.NamespaceID; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -19,14 +25,14 @@ * Represents a group of items, blocks, fluids, entity types or function. * Immutable by design */ -public final class Tag { +public final class Tag implements ProtocolObject, Keyed { private final NamespaceID name; private final Set values; /** * Creates a new empty tag. This does not cache the tag. */ - public Tag(NamespaceID name) { + public Tag(@NotNull NamespaceID name) { this.name = name; this.values = new HashSet<>(); } @@ -34,7 +40,7 @@ public Tag(NamespaceID name) { /** * Creates a new tag with the given values. This does not cache the tag. */ - public Tag(NamespaceID name, Set values) { + public Tag(@NotNull NamespaceID name, @NotNull Set values) { this.name = name; this.values = new HashSet<>(values); } @@ -45,7 +51,7 @@ public Tag(NamespaceID name, Set values) { * @param id the id to check against * @return 'true' iif this tag contains the given id */ - public boolean contains(NamespaceID id) { + public boolean contains(@NotNull NamespaceID id) { return values.contains(id); } @@ -54,13 +60,29 @@ public boolean contains(NamespaceID id) { * * @return immutable set of values present in this tag */ - public Set getValues() { + public @NotNull Set getValues() { return Collections.unmodifiableSet(values); } + public @NotNull NamespaceID namespace() { + return name; + } + + @Contract(pure = true) + public @NotNull String name() { + return namespace().asString(); + } + + @Override + @Contract(pure = true) + public @NotNull Key key() { + return namespace(); + } + /** * Returns the name of this tag */ + @Deprecated public NamespaceID getName() { return name; } @@ -71,11 +93,17 @@ public enum BasicType { ITEMS("minecraft:item", Registry.Resource.ITEM_TAGS, name -> Objects.requireNonNull(Material.fromNamespaceId(name)).id()), FLUIDS("minecraft:fluid", Registry.Resource.FLUID_TAGS, - name -> Objects.requireNonNull(Fluid.fromNamespaceId(name)).id()), + name -> FluidRegistries.getFluid(name).ordinal()), ENTITY_TYPES("minecraft:entity_type", Registry.Resource.ENTITY_TYPE_TAGS, - name -> Objects.requireNonNull(EntityType.fromNamespaceId(name)).id());/* + name -> Objects.requireNonNull(EntityType.fromNamespaceId(name)).id()), GAME_EVENTS("minecraft:game_event", Registry.Resource.GAMEPLAY_TAGS, - name -> Objects.requireNonNull(Fluid.fromNamespaceId(name)).id());*/ + name -> FluidRegistries.getFluid(name).ordinal()), + SOUND_EVENTS("minecraft:sound_event", null, null), // Seems not to be included in server data + POTION_EFFECTS("minecraft:sound_event", null, null), // Seems not to be included in server data + + //todo this is cursed. it does not update as the registry changes. Fix later. + ENCHANTMENTS("minecraft:enchantment", Registry.Resource.ENCHANTMENT_TAGS, + name -> MinecraftServer.getEnchantmentRegistry().getId(DynamicRegistry.Key.of(name))); private final static BasicType[] VALUES = values(); private final String identifier; @@ -83,8 +111,8 @@ public enum BasicType { private final Function function; BasicType(@NotNull String identifier, - @NotNull Registry.Resource resource, - @NotNull Function function) { + @Nullable Registry.Resource resource, + @Nullable Function function) { this.identifier = identifier; this.resource = resource; this.function = function; diff --git a/src/main/java/net/minestom/server/gamedata/tags/TagManager.java b/src/main/java/net/minestom/server/gamedata/tags/TagManager.java index 79b2b3a6cf2..e0deb7f3724 100644 --- a/src/main/java/net/minestom/server/gamedata/tags/TagManager.java +++ b/src/main/java/net/minestom/server/gamedata/tags/TagManager.java @@ -17,6 +17,7 @@ public final class TagManager { public TagManager() { // Load required tags from files for (var type : Tag.BasicType.values()) { + if (type.getResource() == null || type.getFunction() == null) continue; final var json = Registry.load(type.getResource()); final var tagIdentifierMap = tagMap.computeIfAbsent(type, s -> new CopyOnWriteArrayList<>()); json.keySet().forEach(tagName -> { diff --git a/src/main/java/net/minestom/server/instance/AnvilLoader.java b/src/main/java/net/minestom/server/instance/AnvilLoader.java deleted file mode 100644 index 09e6b8eeb26..00000000000 --- a/src/main/java/net/minestom/server/instance/AnvilLoader.java +++ /dev/null @@ -1,487 +0,0 @@ -package net.minestom.server.instance; - -import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.IntIntImmutablePair; -import net.minestom.server.MinecraftServer; -import net.minestom.server.instance.block.Block; -import net.minestom.server.instance.block.BlockHandler; -import net.minestom.server.utils.MathUtils; -import net.minestom.server.utils.NamespaceID; -import net.minestom.server.utils.async.AsyncUtils; -import net.minestom.server.world.biomes.Biome; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jglrxavpok.hephaistos.mca.*; -import org.jglrxavpok.hephaistos.mca.readers.ChunkReader; -import org.jglrxavpok.hephaistos.mca.readers.ChunkSectionReader; -import org.jglrxavpok.hephaistos.mca.readers.SectionBiomeInformation; -import org.jglrxavpok.hephaistos.mca.writer.ChunkSectionWriter; -import org.jglrxavpok.hephaistos.mca.writer.ChunkWriter; -import org.jglrxavpok.hephaistos.nbt.*; -import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; - -public class AnvilLoader implements IChunkLoader { - private final static Logger LOGGER = LoggerFactory.getLogger(AnvilLoader.class); - private final static Biome PLAINS = MinecraftServer.getBiomeManager().getByName(NamespaceID.from("minecraft:plains")); - - private final Map alreadyLoaded = new ConcurrentHashMap<>(); - private final Path path; - private final Path levelPath; - private final Path regionPath; - - private static class RegionCache extends ConcurrentHashMap> { - } - - /** - * Represents the chunks currently loaded per region. Used to determine when a region file can be unloaded. - */ - private final RegionCache perRegionLoadedChunks = new RegionCache(); - - // thread local to avoid contention issues with locks - private final ThreadLocal> blockStateId2ObjectCacheTLS = ThreadLocal.withInitial(Int2ObjectArrayMap::new); - - public AnvilLoader(@NotNull Path path) { - this.path = path; - this.levelPath = path.resolve("level.dat"); - this.regionPath = path.resolve("region"); - } - - public AnvilLoader(@NotNull String path) { - this(Path.of(path)); - } - - @Override - public void loadInstance(@NotNull Instance instance) { - if (!Files.exists(levelPath)) { - return; - } - try (var reader = new NBTReader(Files.newInputStream(levelPath))) { - final NBTCompound tag = (NBTCompound) reader.read(); - Files.copy(levelPath, path.resolve("level.dat_old"), StandardCopyOption.REPLACE_EXISTING); - instance.tagHandler().updateContent(tag); - } catch (IOException | NBTException e) { - MinecraftServer.getExceptionManager().handleException(e); - } - } - - @Override - public @NotNull CompletableFuture<@Nullable Chunk> loadChunk(@NotNull Instance instance, int chunkX, int chunkZ) { - if (!Files.exists(path)) { - // No world folder - return CompletableFuture.completedFuture(null); - } - try { - return loadMCA(instance, chunkX, chunkZ); - } catch (Exception e) { - MinecraftServer.getExceptionManager().handleException(e); - } - return CompletableFuture.completedFuture(null); - } - - private @NotNull CompletableFuture<@Nullable Chunk> loadMCA(Instance instance, int chunkX, int chunkZ) throws IOException, AnvilException { - final RegionFile mcaFile = getMCAFile(instance, chunkX, chunkZ); - if (mcaFile == null) - return CompletableFuture.completedFuture(null); - final NBTCompound chunkData = mcaFile.getChunkData(chunkX, chunkZ); - if (chunkData == null) - return CompletableFuture.completedFuture(null); - - final ChunkReader chunkReader = new ChunkReader(chunkData); - - Chunk chunk = instance.getChunkSupplier().createChunk(instance, chunkX, chunkZ); - synchronized (chunk) { - var yRange = chunkReader.getYRange(); - if (yRange.getStart() < instance.getDimensionType().getMinY()) { - throw new AnvilException( - String.format("Trying to load chunk with minY = %d, but instance dimension type (%s) has a minY of %d", - yRange.getStart(), - instance.getDimensionType().getName().asString(), - instance.getDimensionType().getMinY() - )); - } - if (yRange.getEndInclusive() > instance.getDimensionType().getMaxY()) { - throw new AnvilException( - String.format("Trying to load chunk with maxY = %d, but instance dimension type (%s) has a maxY of %d", - yRange.getEndInclusive(), - instance.getDimensionType().getName().asString(), - instance.getDimensionType().getMaxY() - )); - } - - // TODO: Parallelize block, block entities and biome loading - // Blocks + Biomes - loadSections(chunk, chunkReader); - - // Block entities - loadBlockEntities(chunk, chunkReader); - - chunk.loadHeightmapsFromNBT(chunkReader.getHeightmaps()); - } - synchronized (perRegionLoadedChunks) { - int regionX = CoordinatesKt.chunkToRegion(chunkX); - int regionZ = CoordinatesKt.chunkToRegion(chunkZ); - var chunks = perRegionLoadedChunks.computeIfAbsent(new IntIntImmutablePair(regionX, regionZ), r -> new HashSet<>()); // region cache may have been removed on another thread due to unloadChunk - chunks.add(new IntIntImmutablePair(chunkX, chunkZ)); - } - return CompletableFuture.completedFuture(chunk); - } - - private @Nullable RegionFile getMCAFile(Instance instance, int chunkX, int chunkZ) { - final int regionX = CoordinatesKt.chunkToRegion(chunkX); - final int regionZ = CoordinatesKt.chunkToRegion(chunkZ); - return alreadyLoaded.computeIfAbsent(RegionFile.Companion.createFileName(regionX, regionZ), n -> { - try { - final Path regionPath = this.regionPath.resolve(n); - if (!Files.exists(regionPath)) { - return null; - } - synchronized (perRegionLoadedChunks) { - Set previousVersion = perRegionLoadedChunks.put(new IntIntImmutablePair(regionX, regionZ), new HashSet<>()); - assert previousVersion == null : "The AnvilLoader cache should not already have data for this region."; - } - return new RegionFile(new RandomAccessFile(regionPath.toFile(), "rw"), regionX, regionZ, instance.getDimensionType().getMinY(), instance.getDimensionType().getMaxY() - 1); - } catch (IOException | AnvilException e) { - MinecraftServer.getExceptionManager().handleException(e); - return null; - } - }); - } - - private void loadSections(Chunk chunk, ChunkReader chunkReader) { - final HashMap biomeCache = new HashMap<>(); - for (NBTCompound sectionNBT : chunkReader.getSections()) { - ChunkSectionReader sectionReader = new ChunkSectionReader(chunkReader.getMinecraftVersion(), sectionNBT); - - if (sectionReader.isSectionEmpty()) continue; - final int sectionY = sectionReader.getY(); - final int yOffset = Chunk.CHUNK_SECTION_SIZE * sectionY; - - Section section = chunk.getSection(sectionY); - - if (sectionReader.getSkyLight() != null) { - section.setSkyLight(sectionReader.getSkyLight().copyArray()); - } - if (sectionReader.getBlockLight() != null) { - section.setBlockLight(sectionReader.getBlockLight().copyArray()); - } - - // Biomes - if (chunkReader.getGenerationStatus().compareTo(ChunkColumn.GenerationStatus.Biomes) > 0) { - SectionBiomeInformation sectionBiomeInformation = chunkReader.readSectionBiomes(sectionReader); - - if (sectionBiomeInformation != null && sectionBiomeInformation.hasBiomeInformation()) { - if (sectionBiomeInformation.isFilledWithSingleBiome()) { - for (int y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) { - for (int z = 0; z < Chunk.CHUNK_SIZE_Z; z++) { - for (int x = 0; x < Chunk.CHUNK_SIZE_X; x++) { - int finalX = chunk.chunkX * Chunk.CHUNK_SIZE_X + x; - int finalZ = chunk.chunkZ * Chunk.CHUNK_SIZE_Z + z; - int finalY = sectionY * Chunk.CHUNK_SECTION_SIZE + y; - String biomeName = sectionBiomeInformation.getBaseBiome(); - Biome biome = biomeCache.computeIfAbsent(biomeName, n -> - Objects.requireNonNullElse(MinecraftServer.getBiomeManager().getByName(NamespaceID.from(n)), PLAINS)); - chunk.setBiome(finalX, finalY, finalZ, biome); - } - } - } - } else { - for (int y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) { - for (int z = 0; z < Chunk.CHUNK_SIZE_Z; z++) { - for (int x = 0; x < Chunk.CHUNK_SIZE_X; x++) { - int finalX = chunk.chunkX * Chunk.CHUNK_SIZE_X + x; - int finalZ = chunk.chunkZ * Chunk.CHUNK_SIZE_Z + z; - int finalY = sectionY * Chunk.CHUNK_SECTION_SIZE + y; - - int index = x / 4 + (z / 4) * 4 + (y / 4) * 16; - String biomeName = sectionBiomeInformation.getBiomes()[index]; - Biome biome = biomeCache.computeIfAbsent(biomeName, n -> - Objects.requireNonNullElse(MinecraftServer.getBiomeManager().getByName(NamespaceID.from(n)), PLAINS)); - chunk.setBiome(finalX, finalY, finalZ, biome); - } - } - } - } - } - } - - // Blocks - final NBTList blockPalette = sectionReader.getBlockPalette(); - if (blockPalette != null) { - final int[] blockStateIndices = sectionReader.getUncompressedBlockStateIDs(); - Block[] convertedPalette = new Block[blockPalette.getSize()]; - for (int i = 0; i < convertedPalette.length; i++) { - final NBTCompound paletteEntry = blockPalette.get(i); - String blockName = Objects.requireNonNull(paletteEntry.getString("Name")); - if (blockName.equals("minecraft:air")) { - convertedPalette[i] = Block.AIR; - } else { - if (blockName.equals("minecraft:grass")) { - blockName = "minecraft:short_grass"; - } - Block block = Objects.requireNonNull(Block.fromNamespaceId(blockName)); - // Properties - final Map properties = new HashMap<>(); - NBTCompound propertiesNBT = paletteEntry.getCompound("Properties"); - if (propertiesNBT != null) { - for (var property : propertiesNBT) { - if (property.getValue().getID() != NBTType.TAG_String) { - LOGGER.warn("Fail to parse block state properties {}, expected a TAG_String for {}, but contents were {}", - propertiesNBT, - property.getKey(), - property.getValue().toSNBT()); - } else { - properties.put(property.getKey(), ((NBTString) property.getValue()).getValue()); - } - } - } - - if (!properties.isEmpty()) block = block.withProperties(properties); - // Handler - final BlockHandler handler = MinecraftServer.getBlockManager().getHandler(block.name()); - if (handler != null) block = block.withHandler(handler); - - convertedPalette[i] = block; - } - } - - for (int y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) { - for (int z = 0; z < Chunk.CHUNK_SECTION_SIZE; z++) { - for (int x = 0; x < Chunk.CHUNK_SECTION_SIZE; x++) { - try { - final int blockIndex = y * Chunk.CHUNK_SECTION_SIZE * Chunk.CHUNK_SECTION_SIZE + z * Chunk.CHUNK_SECTION_SIZE + x; - final int paletteIndex = blockStateIndices[blockIndex]; - final Block block = convertedPalette[paletteIndex]; - - chunk.setBlock(x, y + yOffset, z, block); - } catch (Exception e) { - MinecraftServer.getExceptionManager().handleException(e); - } - } - } - } - } - } - } - - private void loadBlockEntities(Chunk loadedChunk, ChunkReader chunkReader) { - for (NBTCompound te : chunkReader.getBlockEntities()) { - final var x = te.getInt("x"); - final var y = te.getInt("y"); - final var z = te.getInt("z"); - if (x == null || y == null || z == null) { - LOGGER.warn("Tile entity has failed to load due to invalid coordinate"); - continue; - } - Block block = loadedChunk.getBlock(x, y, z); - - final String tileEntityID = te.getString("id"); - if (tileEntityID != null) { - final BlockHandler handler = MinecraftServer.getBlockManager().getHandlerOrDummy(tileEntityID); - block = block.withHandler(handler); - } - // Remove anvil tags - MutableNBTCompound mutableCopy = te.toMutableCompound(); - mutableCopy.remove("id"); - mutableCopy.remove("x"); - mutableCopy.remove("y"); - mutableCopy.remove("z"); - mutableCopy.remove("keepPacked"); - // Place block - final var finalBlock = mutableCopy.getSize() > 0 ? - block.withNbt(mutableCopy.toCompound()) : block; - loadedChunk.setBlock(x, y, z, finalBlock); - } - } - - @Override - public @NotNull CompletableFuture saveInstance(@NotNull Instance instance) { - final NBTCompound nbt = instance.tagHandler().asCompound(); - if (nbt.isEmpty()) { - // Instance has no data - return AsyncUtils.VOID_FUTURE; - } - try (NBTWriter writer = new NBTWriter(Files.newOutputStream(levelPath))) { - writer.writeNamed("", nbt); - } catch (IOException e) { - MinecraftServer.getExceptionManager().handleException(e); - } - return AsyncUtils.VOID_FUTURE; - } - - @Override - public @NotNull CompletableFuture saveChunk(@NotNull Chunk chunk) { - final int chunkX = chunk.getChunkX(); - final int chunkZ = chunk.getChunkZ(); - RegionFile mcaFile; - synchronized (alreadyLoaded) { - mcaFile = getMCAFile(chunk.instance, chunkX, chunkZ); - if (mcaFile == null) { - final int regionX = CoordinatesKt.chunkToRegion(chunkX); - final int regionZ = CoordinatesKt.chunkToRegion(chunkZ); - final String n = RegionFile.Companion.createFileName(regionX, regionZ); - File regionFile = new File(regionPath.toFile(), n); - try { - if (!regionFile.exists()) { - if (!regionFile.getParentFile().exists()) { - regionFile.getParentFile().mkdirs(); - } - regionFile.createNewFile(); - } - mcaFile = new RegionFile(new RandomAccessFile(regionFile, "rw"), regionX, regionZ); - alreadyLoaded.put(n, mcaFile); - } catch (AnvilException | IOException e) { - LOGGER.error("Failed to save chunk " + chunkX + ", " + chunkZ, e); - MinecraftServer.getExceptionManager().handleException(e); - return AsyncUtils.VOID_FUTURE; - } - } - } - ChunkWriter writer = new ChunkWriter(SupportedVersion.Companion.getLatest()); - save(chunk, writer); - try { - LOGGER.debug("Attempt saving at {} {}", chunk.getChunkX(), chunk.getChunkZ()); - mcaFile.writeColumnData(writer.toNBT(), chunk.getChunkX(), chunk.getChunkZ()); - } catch (IOException e) { - LOGGER.error("Failed to save chunk " + chunkX + ", " + chunkZ, e); - MinecraftServer.getExceptionManager().handleException(e); - return AsyncUtils.VOID_FUTURE; - } - return AsyncUtils.VOID_FUTURE; - } - - private BlockState getBlockState(final Block block) { - return blockStateId2ObjectCacheTLS.get().computeIfAbsent(block.stateId(), _unused -> new BlockState(block.name(), block.properties())); - } - - private void save(Chunk chunk, ChunkWriter chunkWriter) { - final int minY = chunk.getMinSection() * Chunk.CHUNK_SECTION_SIZE; - final int maxY = chunk.getMaxSection() * Chunk.CHUNK_SECTION_SIZE - 1; - chunkWriter.setYPos(minY); - List blockEntities = new ArrayList<>(); - chunkWriter.setStatus(ChunkColumn.GenerationStatus.Full); - - List sectionData = new ArrayList<>((maxY - minY + 1) / Chunk.CHUNK_SECTION_SIZE); - int[] palettedBiomes = new int[ChunkSection.Companion.getBiomeArraySize()]; - int[] palettedBlockStates = new int[Chunk.CHUNK_SIZE_X * Chunk.CHUNK_SECTION_SIZE * Chunk.CHUNK_SIZE_Z]; - for (int sectionY = chunk.getMinSection(); sectionY < chunk.getMaxSection(); sectionY++) { - ChunkSectionWriter sectionWriter = new ChunkSectionWriter(SupportedVersion.Companion.getLatest(), (byte) sectionY); - - Section section = chunk.getSection(sectionY); - sectionWriter.setSkyLights(section.skyLight().array()); - sectionWriter.setBlockLights(section.blockLight().array()); - - BiomePalette biomePalette = new BiomePalette(); - BlockPalette blockPalette = new BlockPalette(); - for (int sectionLocalY = 0; sectionLocalY < Chunk.CHUNK_SECTION_SIZE; sectionLocalY++) { - for (int z = 0; z < Chunk.CHUNK_SIZE_Z; z++) { - for (int x = 0; x < Chunk.CHUNK_SIZE_X; x++) { - final int y = sectionLocalY + sectionY * Chunk.CHUNK_SECTION_SIZE; - - final int blockIndex = x + sectionLocalY * 16 * 16 + z * 16; - - final Block block = chunk.getBlock(x, y, z); - - final BlockState hephaistosBlockState = getBlockState(block); - blockPalette.increaseReference(hephaistosBlockState); - - palettedBlockStates[blockIndex] = blockPalette.getPaletteIndex(hephaistosBlockState); - - // biome are stored for 4x4x4 volumes, avoid unnecessary work - if (x % 4 == 0 && sectionLocalY % 4 == 0 && z % 4 == 0) { - int biomeIndex = (x / 4) + (sectionLocalY / 4) * 4 * 4 + (z / 4) * 4; - final Biome biome = chunk.getBiome(x, y, z); - final String biomeName = biome.name(); - - biomePalette.increaseReference(biomeName); - palettedBiomes[biomeIndex] = biomePalette.getPaletteIndex(biomeName); - } - - // Block entities - final BlockHandler handler = block.handler(); - final NBTCompound originalNBT = block.nbt(); - if (originalNBT != null || handler != null) { - MutableNBTCompound nbt = originalNBT != null ? - originalNBT.toMutableCompound() : new MutableNBTCompound(); - - if (handler != null) { - nbt.setString("id", handler.getNamespaceId().asString()); - } - nbt.setInt("x", x + Chunk.CHUNK_SIZE_X * chunk.getChunkX()); - nbt.setInt("y", y); - nbt.setInt("z", z + Chunk.CHUNK_SIZE_Z * chunk.getChunkZ()); - nbt.setByte("keepPacked", (byte) 0); - blockEntities.add(nbt.toCompound()); - } - } - } - } - - sectionWriter.setPalettedBiomes(biomePalette, palettedBiomes); - sectionWriter.setPalettedBlockStates(blockPalette, palettedBlockStates); - - sectionData.add(sectionWriter.toNBT()); - } - - chunkWriter.setSectionsData(NBT.List(NBTType.TAG_Compound, sectionData)); - chunkWriter.setBlockEntityData(NBT.List(NBTType.TAG_Compound, blockEntities)); - - // Save heightmaps - chunkWriter.setMotionBlockingHeightMap(chunk.motionBlockingHeightmap().getNBT()); - chunkWriter.setWorldSurfaceHeightMap(chunk.worldSurfaceHeightmap().getNBT()); - } - - /** - * Unload a given chunk. Also unloads a region when no chunk from that region is loaded. - * - * @param chunk the chunk to unload - */ - @Override - public void unloadChunk(Chunk chunk) { - final int regionX = CoordinatesKt.chunkToRegion(chunk.chunkX); - final int regionZ = CoordinatesKt.chunkToRegion(chunk.chunkZ); - - final IntIntImmutablePair regionKey = new IntIntImmutablePair(regionX, regionZ); - synchronized (perRegionLoadedChunks) { - Set chunks = perRegionLoadedChunks.get(regionKey); - if (chunks != null) { // if null, trying to unload a chunk from a region that was not created by the AnvilLoader - // don't check return value, trying to unload a chunk not created by the AnvilLoader is valid - chunks.remove(new IntIntImmutablePair(chunk.chunkX, chunk.chunkZ)); - - if (chunks.isEmpty()) { - perRegionLoadedChunks.remove(regionKey); - RegionFile regionFile = alreadyLoaded.remove(RegionFile.Companion.createFileName(regionX, regionZ)); - if (regionFile != null) { - try { - regionFile.close(); - } catch (IOException e) { - MinecraftServer.getExceptionManager().handleException(e); - } - } - } - } - } - } - - @Override - public boolean supportsParallelLoading() { - return true; - } - - @Override - public boolean supportsParallelSaving() { - return true; - } -} diff --git a/src/main/java/net/minestom/server/instance/Chunk.java b/src/main/java/net/minestom/server/instance/Chunk.java index dc9722e32e0..631f14dbdb2 100644 --- a/src/main/java/net/minestom/server/instance/Chunk.java +++ b/src/main/java/net/minestom/server/instance/Chunk.java @@ -1,13 +1,14 @@ package net.minestom.server.instance; +import net.kyori.adventure.nbt.CompoundBinaryTag; import net.minestom.server.Tickable; import net.minestom.server.Viewable; import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.Player; -import net.minestom.server.entity.pathfinding.PFColumnarSpace; import net.minestom.server.instance.block.Block; import net.minestom.server.instance.block.BlockHandler; +import net.minestom.server.instance.generator.Generator; import net.minestom.server.instance.heightmap.Heightmap; import net.minestom.server.network.packet.server.SendablePacket; import net.minestom.server.network.packet.server.play.ChunkDataPacket; @@ -16,11 +17,11 @@ 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.DimensionType; +import net.minestom.server.world.biome.Biome; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; import java.util.List; import java.util.Set; @@ -57,9 +58,6 @@ public abstract class Chunk implements Block.Getter, Block.Setter, Biome.Getter, protected volatile boolean loaded = true; private final Viewable viewable; - // Path finding - protected PFColumnarSpace columnarSpace; - // Data private final TagHandler tagHandler = TagHandler.newHandler(); @@ -69,8 +67,9 @@ public Chunk(@NotNull Instance instance, int chunkX, int chunkZ, boolean shouldG 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; + final DimensionType instanceDim = instance.getCachedDimensionType(); + this.minSection = instanceDim.minY() / CHUNK_SECTION_SIZE; + this.maxSection = (instanceDim.minY() + instanceDim.height()) / CHUNK_SECTION_SIZE; final List shared = instance instanceof InstanceContainer instanceContainer ? instanceContainer.getSharedInstances() : List.of(); this.viewable = instance.getEntityTracker().viewable(shared, chunkX, chunkZ); @@ -105,7 +104,7 @@ protected abstract void setBlock(int x, int y, int z, @NotNull Block block, public abstract @NotNull Heightmap motionBlockingHeightmap(); public abstract @NotNull Heightmap worldSurfaceHeightmap(); - public abstract void loadHeightmapsFromNBT(NBTCompound heightmaps); + public abstract void loadHeightmapsFromNBT(CompoundBinaryTag heightmaps); public @NotNull Section getSectionAt(int blockY) { return getSection(ChunkUtils.getChunkCoordinate(blockY)); @@ -233,11 +232,11 @@ public int getMaxSection() { } /** - * Gets if this chunk will or had been loaded with a {@link ChunkGenerator}. + * Gets if this chunk will or had been loaded with a {@link Generator}. *

* If false, the chunk will be entirely empty when loaded. * - * @return true if this chunk is affected by a {@link ChunkGenerator} + * @return true if this chunk is affected by a {@link Generator} */ public boolean shouldGenerate() { return shouldGenerate; @@ -247,7 +246,7 @@ public boolean shouldGenerate() { * Gets if this chunk is read-only. *

* Being read-only should prevent block placing/breaking and setting block from an {@link Instance}. - * It does not affect {@link IChunkLoader} and {@link ChunkGenerator}. + * It does not affect {@link IChunkLoader} and {@link Generator}. * * @return true if the chunk is read-only */ @@ -259,7 +258,7 @@ public boolean isReadOnly() { * Changes the read state of the chunk. *

* Being read-only should prevent block placing/breaking and setting block from an {@link Instance}. - * It does not affect {@link IChunkLoader} and {@link ChunkGenerator}. + * It does not affect {@link IChunkLoader} and {@link Generator}. * * @param readOnly true to make the chunk read-only, false otherwise */ @@ -267,15 +266,6 @@ public void setReadOnly(boolean readOnly) { this.readOnly = readOnly; } - /** - * Changes this chunk columnar space. - * - * @param columnarSpace the new columnar space - */ - public void setColumnarSpace(PFColumnarSpace columnarSpace) { - this.columnarSpace = columnarSpace; - } - /** * Used to verify if the chunk should still be kept in memory. * diff --git a/src/main/java/net/minestom/server/instance/ChunkGenerator.java b/src/main/java/net/minestom/server/instance/ChunkGenerator.java deleted file mode 100644 index ab8725e049d..00000000000 --- a/src/main/java/net/minestom/server/instance/ChunkGenerator.java +++ /dev/null @@ -1,38 +0,0 @@ -package net.minestom.server.instance; - -import net.minestom.server.instance.batch.ChunkBatch; -import net.minestom.server.instance.block.Block; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -/** - * Responsible for the {@link Chunk} generation, can be set using {@link Instance#setChunkGenerator(ChunkGenerator)}. - *

- * Called if the instance {@link IChunkLoader} hasn't been able to load the chunk. - * @deprecated Replaced by {@link net.minestom.server.instance.generator.Generator} - */ -@Deprecated -public interface ChunkGenerator { - - /** - * Called when the blocks in the {@link Chunk} should be set using {@link ChunkBatch#setBlock(int, int, int, Block)} - * or similar. - *

- * WARNING: all positions are chunk-based (0-15). - * - * @param batch the {@link ChunkBatch} which will be flush after the generation - * @param chunkX the chunk X - * @param chunkZ the chunk Z - */ - void generateChunkData(@NotNull ChunkBatch batch, int chunkX, int chunkZ); - - /** - * Gets all the {@link ChunkPopulator} of this generator. - * - * @return a {@link List} of {@link ChunkPopulator}, can be null or empty - */ - @Nullable - List getPopulators(); -} diff --git a/src/main/java/net/minestom/server/instance/ChunkGeneratorCompatibilityLayer.java b/src/main/java/net/minestom/server/instance/ChunkGeneratorCompatibilityLayer.java deleted file mode 100644 index 06166c41d6a..00000000000 --- a/src/main/java/net/minestom/server/instance/ChunkGeneratorCompatibilityLayer.java +++ /dev/null @@ -1,42 +0,0 @@ -package net.minestom.server.instance; - -import net.minestom.server.coordinate.Point; -import net.minestom.server.instance.batch.ChunkBatch; -import net.minestom.server.instance.block.Block; -import net.minestom.server.instance.generator.GenerationUnit; -import net.minestom.server.instance.generator.Generator; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -/** - * Provides full compatibility for the deprecated {@link ChunkGenerator} - */ -@SuppressWarnings("deprecation") -record ChunkGeneratorCompatibilityLayer(@NotNull ChunkGenerator chunkGenerator) implements Generator { - @Override - public void generate(@NotNull GenerationUnit unit) { - if (!(unit instanceof GeneratorImpl.UnitImpl impl) || - !(impl.modifier() instanceof GeneratorImpl.AreaModifierImpl modifier && modifier.chunk() != null)) { - throw new IllegalArgumentException("Invalid unit"); - } - - final int startY = unit.absoluteStart().blockY(); - ChunkBatch batch = new ChunkBatch() { - @Override - public void setBlock(int x, int y, int z, @NotNull Block block) { - unit.modifier().setRelative(x, y - startY, z, block); - } - }; - final Point start = unit.absoluteStart(); - chunkGenerator.generateChunkData(batch, start.chunkX(), start.chunkZ()); - - final List populators = chunkGenerator.getPopulators(); - final boolean hasPopulator = populators != null && !populators.isEmpty(); - if (hasPopulator) { - for (ChunkPopulator chunkPopulator : populators) { - chunkPopulator.populateChunk(batch, modifier.chunk()); - } - } - } -} diff --git a/src/main/java/net/minestom/server/instance/ChunkPopulator.java b/src/main/java/net/minestom/server/instance/ChunkPopulator.java deleted file mode 100644 index b6130c49900..00000000000 --- a/src/main/java/net/minestom/server/instance/ChunkPopulator.java +++ /dev/null @@ -1,10 +0,0 @@ -package net.minestom.server.instance; - -import net.minestom.server.instance.batch.ChunkBatch; - -@Deprecated -public interface ChunkPopulator { - - void populateChunk(ChunkBatch batch, Chunk chunk); - -} diff --git a/src/main/java/net/minestom/server/instance/DynamicChunk.java b/src/main/java/net/minestom/server/instance/DynamicChunk.java index 7dfe0c77831..51ca29be4e2 100644 --- a/src/main/java/net/minestom/server/instance/DynamicChunk.java +++ b/src/main/java/net/minestom/server/instance/DynamicChunk.java @@ -1,15 +1,17 @@ package net.minestom.server.instance; -import com.extollit.gaming.ai.path.model.ColumnarOcclusionFieldList; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.LongArrayBinaryTag; import net.minestom.server.MinecraftServer; import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.pathfinding.PFBlock; import net.minestom.server.instance.block.Block; import net.minestom.server.instance.block.BlockHandler; -import net.minestom.server.instance.heightmap.*; +import net.minestom.server.instance.heightmap.Heightmap; +import net.minestom.server.instance.heightmap.MotionBlockingHeightmap; +import net.minestom.server.instance.heightmap.WorldSurfaceHeightmap; import net.minestom.server.network.NetworkBuffer; import net.minestom.server.network.packet.server.CachedPacket; import net.minestom.server.network.packet.server.SendablePacket; @@ -17,17 +19,17 @@ 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.utils.validate.Check; +import net.minestom.server.world.DimensionType; +import net.minestom.server.world.biome.Biome; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jglrxavpok.hephaistos.nbt.NBT; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,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_REGISTRY = MinecraftServer.getBiomeRegistry(); public DynamicChunk(@NotNull Instance instance, int chunkX, int chunkZ) { super(instance, chunkX, chunkZ, true); @@ -69,9 +71,10 @@ public DynamicChunk(@NotNull Instance instance, int chunkX, int chunkZ) { 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()) { + final DimensionType instanceDim = instance.getCachedDimensionType(); + if (y >= instanceDim.maxY() || y < instanceDim.minY()) { LOGGER.warn("tried to set a block outside the world bounds, should be within [{}, {}): {}", - instance.getDimensionType().getMinY(), instance.getDimensionType().getMaxY(), y); + instanceDim.minY(), instanceDim.maxY(), y); return; } assertLock(); @@ -79,12 +82,6 @@ public void setBlock(int x, int y, int z, @NotNull Block block, this.lastChange = System.currentTimeMillis(); this.chunkCache.invalidate(); - // Update pathfinder - if (columnarSpace != null) { - final ColumnarOcclusionFieldList columnarOcclusionFieldList = columnarSpace.occlusionFields(); - final var blockDescription = PFBlock.get(block); - columnarOcclusionFieldList.onBlockChanged(x, y, z, blockDescription, 0); - } Section section = getSectionAt(y); int sectionRelativeX = toSectionRelativeCoordinate(x); @@ -134,12 +131,12 @@ public void setBlock(int x, int y, int z, @NotNull Block block, } @Override - public void setBiome(int x, int y, int z, @NotNull Biome biome) { + public void setBiome(int x, int y, int z, @NotNull DynamicRegistry.Key biome) { assertLock(); 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( @@ -169,13 +166,13 @@ public void setBiome(int x, int y, int z, @NotNull Biome biome) { } @Override - public void loadHeightmapsFromNBT(NBTCompound heightmapsNBT) { - if (heightmapsNBT.contains(motionBlockingHeightmap().NBTName())) { - motionBlockingHeightmap().loadFrom(heightmapsNBT.getLongArray(motionBlockingHeightmap().NBTName())); + public void loadHeightmapsFromNBT(CompoundBinaryTag heightmapsNBT) { + if (heightmapsNBT.get(motionBlockingHeightmap().NBTName()) instanceof LongArrayBinaryTag array) { + motionBlockingHeightmap().loadFrom(array.value()); } - if (heightmapsNBT.contains(worldSurfaceHeightmap().NBTName())) { - worldSurfaceHeightmap().loadFrom(heightmapsNBT.getLongArray(worldSurfaceHeightmap().NBTName())); + if (heightmapsNBT.get(worldSurfaceHeightmap().NBTName()) instanceof LongArrayBinaryTag array) { + worldSurfaceHeightmap().loadFrom(array.value()); } } @@ -214,17 +211,14 @@ public void tick(long time) { } @Override - public @NotNull Biome getBiome(int x, int y, int z) { + public @NotNull DynamicRegistry.Key getBiome(int x, int y, int z) { assertLock(); final Section section = getSectionAt(y); final int id = section.biomePalette() .get(toSectionRelativeCoordinate(x) / 4, toSectionRelativeCoordinate(y) / 4, toSectionRelativeCoordinate(z) / 4); - Biome biome = BIOME_MANAGER.getById(id); - if (biome == null) { - throw new IllegalStateException("Biome with id " + id + " is not registered"); - } - + DynamicRegistry.Key biome = BIOME_REGISTRY.getKey(id); + Check.notNull(biome, "Biome with id {0} is not registered", id); return biome; } @@ -259,7 +253,7 @@ public void invalidate() { private @NotNull ChunkDataPacket createChunkPacket() { final byte[] data; - final NBTCompound heightmapsNBT; + final CompoundBinaryTag heightmapsNBT; synchronized (this) { heightmapsNBT = getHeightmapNBT(); @@ -270,15 +264,15 @@ public void invalidate() { return new ChunkDataPacket(chunkX, chunkZ, new ChunkData(heightmapsNBT, data, entries), - createLightData() + createLightData(true) ); } @NotNull UpdateLightPacket createLightPacket() { - return new UpdateLightPacket(chunkX, chunkZ, createLightData()); + return new UpdateLightPacket(chunkX, chunkZ, createLightData(false)); } - protected LightData createLightData() { + protected LightData createLightData(boolean requiredFullChunk) { BitSet skyMask = new BitSet(); BitSet blockMask = new BitSet(); BitSet emptySkyMask = new BitSet(); @@ -311,12 +305,12 @@ protected LightData createLightData() { ); } - private NBTCompound getHeightmapNBT() { + protected CompoundBinaryTag getHeightmapNBT() { if (needsCompleteHeightmapRefresh) calculateFullHeightmap(); - return NBT.Compound(Map.of( - motionBlocking.NBTName(), motionBlocking.getNBT(), - worldSurface.NBTName(), worldSurface.getNBT() - )); + return CompoundBinaryTag.builder() + .putLongArray(motionBlocking.NBTName(), motionBlocking.getNBT()) + .putLongArray(worldSurface.NBTName(), worldSurface.getNBT()) + .build(); } private void calculateFullHeightmap() { diff --git a/src/main/java/net/minestom/server/instance/EntityTracker.java b/src/main/java/net/minestom/server/instance/EntityTracker.java index 0ca789ebd77..3aa3b9c0d7f 100644 --- a/src/main/java/net/minestom/server/instance/EntityTracker.java +++ b/src/main/java/net/minestom/server/instance/EntityTracker.java @@ -14,6 +14,7 @@ import java.util.Collection; import java.util.List; import java.util.Set; +import java.util.UUID; import java.util.function.Consumer; /** @@ -21,7 +22,6 @@ *

* Implementations are expected to be thread-safe. */ -@ApiStatus.Experimental public sealed interface EntityTracker permits EntityTrackerImpl { static @NotNull EntityTracker newTracker() { return new EntityTrackerImpl(); @@ -38,6 +38,22 @@ void register(@NotNull Entity entity, @NotNull Point point, */ void unregister(@NotNull Entity entity, @NotNull Target target, @Nullable Update update); + /** + * Gets an entity based on its id (from {@link Entity#getEntityId()}). + * + * @param id the entity id + * @return the entity having the specified id, null if not found + */ + @Nullable Entity getEntityById(int id); + + /** + * Gets an entity based on its UUID (from {@link Entity#getUuid()}). + * + * @param uuid the entity UUID + * @return the entity having the specified uuid, null if not found + */ + @Nullable Entity getEntityByUuid(UUID uuid); + /** * Called every time an entity move, you may want to verify if the new * position is in a different chunk. diff --git a/src/main/java/net/minestom/server/instance/EntityTrackerImpl.java b/src/main/java/net/minestom/server/instance/EntityTrackerImpl.java index cc6d97b5a77..8b387f1dab3 100644 --- a/src/main/java/net/minestom/server/instance/EntityTrackerImpl.java +++ b/src/main/java/net/minestom/server/instance/EntityTrackerImpl.java @@ -1,7 +1,6 @@ package net.minestom.server.instance; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import net.minestom.server.MinecraftServer; import net.minestom.server.ServerFlag; import net.minestom.server.Viewable; import net.minestom.server.coordinate.Point; @@ -9,10 +8,13 @@ import net.minestom.server.entity.Entity; import net.minestom.server.entity.Player; import net.minestom.server.utils.chunk.ChunkUtils; +import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; import org.jetbrains.annotations.UnmodifiableView; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import space.vectrix.flare.fastutil.Int2ObjectSyncMap; import space.vectrix.flare.fastutil.Long2ObjectSyncMap; @@ -28,23 +30,32 @@ import static net.minestom.server.utils.chunk.ChunkUtils.*; final class EntityTrackerImpl implements EntityTracker { + private static final Logger LOGGER = LoggerFactory.getLogger(EntityTrackerImpl.class); + static final AtomicInteger TARGET_COUNTER = new AtomicInteger(); // Store all data associated to a Target // The array index is the Target enum ordinal - final TargetEntry[] entries = EntityTracker.Target.TARGETS.stream().map((Function, TargetEntry>) TargetEntry::new).toArray(TargetEntry[]::new); - private final Int2ObjectSyncMap entityPositions = Int2ObjectSyncMap.hashmap(); + final TargetEntry[] targetEntries = EntityTracker.Target.TARGETS.stream().map((Function, TargetEntry>) TargetEntry::new).toArray(TargetEntry[]::new); + + private final Int2ObjectSyncMap entriesByEntityId = Int2ObjectSyncMap.hashmap(); + private final Map entriesByEntityUuid = new ConcurrentHashMap<>(); @Override public void register(@NotNull Entity entity, @NotNull Point point, @NotNull Target target, @Nullable Update update) { - var prevPoint = entityPositions.putIfAbsent(entity.getEntityId(), point); - if (prevPoint != null) return; + EntityTrackerEntry newEntry = new EntityTrackerEntry(entity, point); + + EntityTrackerEntry prevEntryWithId = entriesByEntityId.putIfAbsent(entity.getEntityId(), newEntry); + Check.isTrue(prevEntryWithId == null, "There is already an entity registered with id {0}", entity.getEntityId()); + EntityTrackerEntry prevEntryWithUuid = entriesByEntityUuid.putIfAbsent(entity.getUuid(), newEntry); + Check.isTrue(prevEntryWithUuid == null, "There is already an entity registered with uuid {0}", entity.getUuid()); + final long index = getChunkIndex(point); - for (TargetEntry entry : entries) { - if (entry.target.type().isInstance(entity)) { - entry.entities.add(entity); - entry.addToChunk(index, entity); + for (TargetEntry targetEntry : targetEntries) { + if (targetEntry.target.type().isInstance(entity)) { + targetEntry.entities.add(entity); + targetEntry.addToChunk(index, entity); } } if (update != null) { @@ -59,13 +70,16 @@ public void register(@NotNull Entity entity, @NotNull Point p @Override public void unregister(@NotNull Entity entity, @NotNull Target target, @Nullable Update update) { - final Point point = entityPositions.remove(entity.getEntityId()); + EntityTrackerEntry entry = entriesByEntityId.remove(entity.getEntityId()); + entriesByEntityUuid.remove(entity.getUuid()); + final Point point = entry == null ? null : entry.getLastPosition(); if (point == null) return; + final long index = getChunkIndex(point); - for (TargetEntry entry : entries) { - if (entry.target.type().isInstance(entity)) { - entry.entities.remove(entity); - entry.removeFromChunk(index, entity); + for (TargetEntry targetEntry : targetEntries) { + if (targetEntry.target.type().isInstance(entity)) { + targetEntry.entities.remove(entity); + targetEntry.removeFromChunk(index, entity); } } if (update != null) { @@ -77,17 +91,35 @@ public void unregister(@NotNull Entity entity, } } + @Override + public @Nullable Entity getEntityById(int id) { + EntityTrackerEntry entry = entriesByEntityId.get(id); + return entry == null ? null : entry.getEntity(); + } + + @Override + public @Nullable Entity getEntityByUuid(UUID uuid) { + EntityTrackerEntry entry = entriesByEntityUuid.get(uuid); + return entry == null ? null : entry.getEntity(); + } + @Override public void move(@NotNull Entity entity, @NotNull Point newPoint, @NotNull Target target, @Nullable Update update) { - Point oldPoint = entityPositions.put(entity.getEntityId(), newPoint); + EntityTrackerEntry entry = entriesByEntityId.get(entity.getEntityId()); + if (entry == null) { + LOGGER.warn("Attempted to move unregistered entity {} in the entity tracker", entity.getEntityId()); + return; + } + Point oldPoint = entry.getLastPosition(); + entry.setLastPosition(newPoint); if (oldPoint == null || oldPoint.sameChunk(newPoint)) return; final long oldIndex = getChunkIndex(oldPoint); final long newIndex = getChunkIndex(newPoint); - for (TargetEntry entry : entries) { - if (entry.target.type().isInstance(entity)) { - entry.addToChunk(newIndex, entity); - entry.removeFromChunk(oldIndex, entity); + for (TargetEntry targetEntry : targetEntries) { + if (targetEntry.target.type().isInstance(entity)) { + targetEntry.addToChunk(newIndex, entity); + targetEntry.removeFromChunk(oldIndex, entity); } } if (update != null) { @@ -108,7 +140,7 @@ public void remove(@NotNull T removed) { @Override public @Unmodifiable Collection chunkEntities(int chunkX, int chunkZ, @NotNull Target target) { - final TargetEntry entry = entries[target.ordinal()]; + final TargetEntry entry = targetEntries[target.ordinal()]; //noinspection unchecked var chunkEntities = (List) entry.chunkEntities(getChunkIndex(chunkX, chunkZ)); return Collections.unmodifiableList(chunkEntities); @@ -116,7 +148,7 @@ public void remove(@NotNull T removed) { @Override public void nearbyEntitiesByChunkRange(@NotNull Point point, int chunkRange, @NotNull Target target, @NotNull Consumer query) { - final Long2ObjectSyncMap> entities = entries[target.ordinal()].chunkEntities; + final Long2ObjectSyncMap> entities = targetEntries[target.ordinal()].chunkEntities; if (chunkRange == 0) { // Single chunk final var chunkEntities = (List) entities.get(getChunkIndex(point)); @@ -135,7 +167,7 @@ public void nearbyEntitiesByChunkRange(@NotNull Point point, @Override public void nearbyEntities(@NotNull Point point, double range, @NotNull Target target, @NotNull Consumer query) { - final Long2ObjectSyncMap> entities = entries[target.ordinal()].chunkEntities; + final Long2ObjectSyncMap> entities = targetEntries[target.ordinal()].chunkEntities; final int minChunkX = ChunkUtils.getChunkCoordinate(point.x() - range); final int minChunkZ = ChunkUtils.getChunkCoordinate(point.z() - range); final int maxChunkX = ChunkUtils.getChunkCoordinate(point.x() + range); @@ -146,7 +178,7 @@ public void nearbyEntities(@NotNull Point point, double range final var chunkEntities = (List) entities.get(getChunkIndex(point)); if (chunkEntities != null && !chunkEntities.isEmpty()) { chunkEntities.forEach(entity -> { - final Point position = entityPositions.get(entity.getEntityId()); + final Point position = entriesByEntityId.get(entity.getEntityId()).getLastPosition(); if (point.distanceSquared(position) <= squaredRange) query.accept(entity); }); } @@ -157,7 +189,7 @@ public void nearbyEntities(@NotNull Point point, double range final var chunkEntities = (List) entities.get(getChunkIndex(chunkX, chunkZ)); if (chunkEntities == null || chunkEntities.isEmpty()) return; chunkEntities.forEach(entity -> { - final Point position = entityPositions.get(entity.getEntityId()); + final Point position = entriesByEntityId.get(entity.getEntityId()).getLastPosition(); if (point.distanceSquared(position) <= squaredRange) { query.accept(entity); } @@ -169,18 +201,41 @@ public void nearbyEntities(@NotNull Point point, double range @Override public @UnmodifiableView @NotNull Set<@NotNull T> entities(@NotNull Target target) { //noinspection unchecked - return (Set) entries[target.ordinal()].entitiesView; + return (Set) targetEntries[target.ordinal()].entitiesView; } @Override public @NotNull Viewable viewable(@NotNull List<@NotNull SharedInstance> sharedInstances, int chunkX, int chunkZ) { - var entry = entries[Target.PLAYERS.ordinal()]; + var entry = targetEntries[Target.PLAYERS.ordinal()]; return entry.viewers.computeIfAbsent(new ChunkViewKey(sharedInstances, chunkX, chunkZ), ChunkView::new); } + private static class EntityTrackerEntry { + private final Entity entity; + private Point lastPosition; + + private EntityTrackerEntry(Entity entity, @Nullable Point lastPosition) { + this.entity = entity; + this.lastPosition = lastPosition; + } + + public Entity getEntity() { + return entity; + } + + @Nullable + public Point getLastPosition() { + return lastPosition; + } + + public void setLastPosition(Point lastPosition) { + this.lastPosition = lastPosition; + } + } + private void difference(Point oldPoint, Point newPoint, @NotNull Target target, @NotNull Update update) { - final TargetEntry entry = entries[target.ordinal()]; + final TargetEntry entry = targetEntries[target.ordinal()]; forDifferingChunksInRange(newPoint.chunkX(), newPoint.chunkZ(), oldPoint.chunkX(), oldPoint.chunkZ(), ServerFlag.ENTITY_VIEW_DISTANCE, (chunkX, chunkZ) -> { // Add diff --git a/src/main/java/net/minestom/server/instance/ExplosionSupplier.java b/src/main/java/net/minestom/server/instance/ExplosionSupplier.java index ea408e3109e..bdb7fcbbe09 100644 --- a/src/main/java/net/minestom/server/instance/ExplosionSupplier.java +++ b/src/main/java/net/minestom/server/instance/ExplosionSupplier.java @@ -1,6 +1,6 @@ package net.minestom.server.instance; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; +import net.kyori.adventure.nbt.CompoundBinaryTag; @FunctionalInterface public interface ExplosionSupplier { @@ -12,9 +12,9 @@ public interface ExplosionSupplier { * @param centerY center Y of the explosion * @param centerZ center Z of the explosion * @param strength strength of the explosion - * @param additionalData data passed via {@link Instance#explode(float, float, float, float, NBTCompound)} )}. Can be null + * @param additionalData data passed via {@link Instance#explode(float, float, float, float, CompoundBinaryTag)} )}. Can be null * @return Explosion object representing the algorithm to use */ - Explosion createExplosion(float centerX, float centerY, float centerZ, float strength, NBTCompound additionalData); + Explosion createExplosion(float centerX, float centerY, float centerZ, float strength, CompoundBinaryTag additionalData); } diff --git a/src/main/java/net/minestom/server/instance/IChunkLoader.java b/src/main/java/net/minestom/server/instance/IChunkLoader.java index 549f32e0099..09b47ffd1cf 100644 --- a/src/main/java/net/minestom/server/instance/IChunkLoader.java +++ b/src/main/java/net/minestom/server/instance/IChunkLoader.java @@ -1,6 +1,7 @@ package net.minestom.server.instance; import net.minestom.server.MinecraftServer; +import net.minestom.server.instance.anvil.AnvilLoader; import net.minestom.server.utils.async.AsyncUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -18,6 +19,10 @@ */ public interface IChunkLoader { + static @NotNull IChunkLoader noop() { + return NoopChunkLoaderImpl.INSTANCE; + } + /** * Loads instance data from the loader. * @@ -27,7 +32,7 @@ default void loadInstance(@NotNull Instance instance) { } /** - * Loads a {@link Chunk}, all blocks should be set since the {@link ChunkGenerator} is not applied. + * Loads a {@link Chunk}, all blocks should be set since the {@link net.minestom.server.instance.generator.Generator} is not applied. * * @param instance the {@link Instance} where the {@link Chunk} belong * @param chunkX the chunk X diff --git a/src/main/java/net/minestom/server/instance/Instance.java b/src/main/java/net/minestom/server/instance/Instance.java index e503b5c164f..8e86c8ff6e1 100644 --- a/src/main/java/net/minestom/server/instance/Instance.java +++ b/src/main/java/net/minestom/server/instance/Instance.java @@ -2,8 +2,10 @@ import it.unimi.dsi.fastutil.objects.ObjectArraySet; import net.kyori.adventure.identity.Identity; +import net.kyori.adventure.nbt.CompoundBinaryTag; import net.kyori.adventure.pointer.Pointers; import net.minestom.server.MinecraftServer; +import net.minestom.server.ServerFlag; import net.minestom.server.ServerProcess; import net.minestom.server.Tickable; import net.minestom.server.adventure.audience.PacketGroupingAudience; @@ -13,7 +15,6 @@ import net.minestom.server.entity.EntityCreature; import net.minestom.server.entity.ExperienceOrb; import net.minestom.server.entity.Player; -import net.minestom.server.entity.pathfinding.PFInstanceSpace; import net.minestom.server.event.EventDispatcher; import net.minestom.server.event.EventFilter; import net.minestom.server.event.EventHandler; @@ -27,8 +28,10 @@ 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.InitializeWorldBorderPacket; import net.minestom.server.network.packet.server.play.SpawnPositionPacket; 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; @@ -41,16 +44,12 @@ import net.minestom.server.utils.chunk.ChunkCache; import net.minestom.server.utils.chunk.ChunkSupplier; import net.minestom.server.utils.chunk.ChunkUtils; -import net.minestom.server.utils.time.Cooldown; -import net.minestom.server.utils.time.TimeUnit; 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.jglrxavpok.hephaistos.nbt.NBTCompound; -import java.time.Duration; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; @@ -72,10 +71,14 @@ public abstract class Instance implements Block.Getter, Block.Setter, private boolean registered; - private final DimensionType dimensionType; + private final DynamicRegistry.Key dimensionType; + private final DimensionType cachedDimensionType; // Cached to prevent self-destruction if the registry is changed, and to avoid the lookups. private final String dimensionName; - private final WorldBorder worldBorder; + // World border of the instance + private WorldBorder worldBorder; + private double targetBorderDiameter; + private long remainingWorldBorderTransitionTicks; // Tick since the creation of the instance private long worldAge; @@ -83,8 +86,7 @@ public abstract class Instance implements Block.Getter, Block.Setter, // The time of the instance private long time; private int timeRate = 1; - private Duration timeUpdate = Duration.of(1, TimeUnit.SECOND); - private long lastTimeUpdate; + private int timeSynchronizationTicks = ServerFlag.SERVER_TICKS_PER_SECOND; // Weather of the instance private Weather weather = Weather.CLEAR; @@ -110,9 +112,6 @@ public abstract class Instance implements Block.Getter, Block.Setter, // the explosion supplier private ExplosionSupplier explosionSupplier; - // Pathfinder - private final PFInstanceSpace instanceSpace = new PFInstanceSpace(this); - // Adventure private final Pointers pointers; @@ -124,8 +123,18 @@ public abstract class Instance implements Block.Getter, Block.Setter, * @param uniqueId the {@link UUID} of the instance * @param dimensionType the {@link DimensionType} of the instance */ - public Instance(@NotNull UUID uniqueId, @NotNull DimensionType dimensionType) { - this(uniqueId, dimensionType, dimensionType.getName()); + public Instance(@NotNull UUID uniqueId, @NotNull DynamicRegistry.Key dimensionType) { + this(uniqueId, dimensionType, dimensionType.namespace()); + } + + /** + * Creates a new instance. + * + * @param uniqueId the {@link UUID} of the instance + * @param dimensionType the {@link DimensionType} of the instance + */ + public Instance(@NotNull UUID uniqueId, @NotNull DynamicRegistry.Key dimensionType, @NotNull NamespaceID dimensionName) { + this(MinecraftServer.getDimensionTypeRegistry(), uniqueId, dimensionType, dimensionName); } /** @@ -134,14 +143,15 @@ public Instance(@NotNull UUID uniqueId, @NotNull DimensionType dimensionType) { * @param uniqueId the {@link UUID} of the instance * @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"); + public Instance(@NotNull DynamicRegistry dimensionTypeRegistry, @NotNull UUID uniqueId, @NotNull DynamicRegistry.Key dimensionType, @NotNull NamespaceID dimensionName) { this.uniqueId = uniqueId; this.dimensionType = dimensionType; + this.cachedDimensionType = dimensionTypeRegistry.get(dimensionType); + Check.argCondition(cachedDimensionType == null, "The dimension " + dimensionType + " is not registered! Please add it to the registry (`MinecraftServer.getDimensionTypeRegistry().registry(dimensionType)`)."); this.dimensionName = dimensionName.asString(); - this.worldBorder = new WorldBorder(this); + this.worldBorder = WorldBorder.DEFAULT_BORDER; + targetBorderDiameter = this.worldBorder.diameter(); this.pointers = Pointers.builder() .withDynamic(Identity.UUID, this::getUniqueId) @@ -210,7 +220,7 @@ public boolean breakBlock(@NotNull Player player, @NotNull Point blockPosition, public abstract boolean breakBlock(@NotNull Player player, @NotNull Point blockPosition, @NotNull BlockFace blockFace, boolean doBlockUpdates); /** - * Forces the generation of a {@link Chunk}, even if no file and {@link ChunkGenerator} are defined. + * Forces the generation of a {@link Chunk}, even if no file and {@link Generator} are defined. * * @param chunkX the chunk X * @param chunkZ the chunk Z @@ -305,7 +315,6 @@ public boolean isChunkLoaded(Point point) { * * @return the future called once the instance data has been saved */ - @ApiStatus.Experimental public abstract @NotNull CompletableFuture saveInstance(); /** @@ -323,17 +332,6 @@ public boolean isChunkLoaded(Point point) { */ public abstract @NotNull CompletableFuture saveChunksToStorage(); - /** - * Changes the instance {@link ChunkGenerator}. - * - * @param chunkGenerator the new {@link ChunkGenerator} of the instance - * @deprecated Use {@link #setGenerator(Generator)} - */ - @Deprecated - public void setChunkGenerator(@Nullable ChunkGenerator chunkGenerator) { - setGenerator(chunkGenerator != null ? new ChunkGeneratorCompatibilityLayer(chunkGenerator) : null); - } - public abstract void setChunkSupplier(@NotNull ChunkSupplier chunkSupplier); /** @@ -411,10 +409,15 @@ protected void setRegistered(boolean registered) { * * @return the dimension of the instance */ - public DimensionType getDimensionType() { + public DynamicRegistry.Key getDimensionType() { return dimensionType; } + @ApiStatus.Internal + public @NotNull DimensionType getCachedDimensionType() { + return cachedDimensionType; + } + /** * Gets the instance dimension name. * @return the dimension name of the instance @@ -491,7 +494,7 @@ public long getTime() { *

* This method is unaffected by {@link #getTimeRate()} *

- * It does send the new time to all players in the instance, unaffected by {@link #getTimeUpdate()} + * It does send the new time to all players in the instance, unaffected by {@link #getTimeSynchronizationTicks()} * * @param time the new time of the instance */ @@ -527,20 +530,21 @@ public void setTimeRate(int timeRate) { * * @return the client update rate for time related packet */ - public @Nullable Duration getTimeUpdate() { - return timeUpdate; + public int getTimeSynchronizationTicks() { + return timeSynchronizationTicks; } /** - * Changes the rate at which the client is updated about the time + * Changes the natural client time packet synchronization period, defaults to {@link ServerFlag#SERVER_TICKS_PER_SECOND}. *

- * Setting it to null means that the client will never know about time change - * (but will still change server-side) + * Supplying 0 means that the client will never be synchronized with the current natural instance time + * (time will still change server-side) * - * @param timeUpdate the new update rate concerning time + * @param timeSynchronizationTicks the rate to update time in ticks */ - public void setTimeUpdate(@Nullable Duration timeUpdate) { - this.timeUpdate = timeUpdate; + public void setTimeSynchronizationTicks(int timeSynchronizationTicks) { + Check.stateCondition(timeSynchronizationTicks < 0, "The time Synchronization ticks cannot be lower than 0"); + this.timeSynchronizationTicks = timeSynchronizationTicks; } /** @@ -560,14 +564,67 @@ public void setTimeUpdate(@Nullable Duration timeUpdate) { } /** - * Gets the instance {@link WorldBorder}; + * Gets the current state of the instance {@link WorldBorder}. * - * @return the {@link WorldBorder} linked to the instance + * @return the {@link WorldBorder} for the instance of the current tick */ public @NotNull WorldBorder getWorldBorder() { return worldBorder; } + /** + * Set the instance {@link WorldBorder} with a smooth transition. + * + * @param worldBorder the desired final state of the world border + * @param transitionTime the time in seconds this world border's diameter + * will transition for (0 makes this instant) + * + */ + public void setWorldBorder(@NotNull WorldBorder worldBorder, double transitionTime) { + Check.stateCondition(transitionTime < 0, "Transition time cannot be lower than 0"); + long transitionMilliseconds = (long) (transitionTime * 1000); + sendNewWorldBorderPackets(worldBorder, transitionMilliseconds); + + this.targetBorderDiameter = worldBorder.diameter(); + long transitionTicks = transitionMilliseconds / MinecraftServer.TICK_MS; + remainingWorldBorderTransitionTicks = transitionTicks; + if (transitionTicks == 0) this.worldBorder = worldBorder; + else this.worldBorder = worldBorder.withDiameter(this.worldBorder.diameter()); + } + + /** + * Set the instance {@link WorldBorder} with an instant transition. + * see {@link Instance#setWorldBorder(WorldBorder, double)}. + */ + public void setWorldBorder(@NotNull WorldBorder worldBorder) { + setWorldBorder(worldBorder, 0); + } + + /** + * Creates the {@link InitializeWorldBorderPacket} sent to players who join this instance. + */ + public @NotNull InitializeWorldBorderPacket createInitializeWorldBorderPacket() { + return worldBorder.createInitializePacket(targetBorderDiameter, remainingWorldBorderTransitionTicks * MinecraftServer.TICK_MS); + } + + private void sendNewWorldBorderPackets(@NotNull WorldBorder newBorder, long transitionMilliseconds) { + // Only send the relevant border packets + if (this.worldBorder.diameter() != newBorder.diameter()) { + if (transitionMilliseconds == 0) sendGroupedPacket(newBorder.createSizePacket()); + else sendGroupedPacket(this.worldBorder.createLerpSizePacket(newBorder.diameter(), transitionMilliseconds)); + } + if (this.worldBorder.centerX() != newBorder.centerX() || this.worldBorder.centerZ() != newBorder.centerZ()) { + sendGroupedPacket(newBorder.createCenterPacket()); + } + if (this.worldBorder.warningTime() != newBorder.warningTime()) sendGroupedPacket(newBorder.createWarningDelayPacket()); + if (this.worldBorder.warningDistance() != newBorder.warningDistance()) sendGroupedPacket(newBorder.createWarningReachPacket()); + } + + private @NotNull WorldBorder transitionWorldBorder(long remainingTicks) { + if (remainingTicks <= 1) return worldBorder.withDiameter(targetBorderDiameter); + return worldBorder.withDiameter(worldBorder.diameter() + (targetBorderDiameter - worldBorder.diameter()) * (1 / (double)remainingTicks)); + } + /** * Gets the entities in the instance; * @@ -577,6 +634,40 @@ public void setTimeUpdate(@Nullable Duration timeUpdate) { return entityTracker.entities(); } + /** + * Gets an entity based on its id (from {@link Entity#getEntityId()}). + * + * @param id the entity id + * @return the entity having the specified id, null if not found + */ + public @Nullable Entity getEntityById(int id) { + return entityTracker.getEntityById(id); + } + + /** + * Gets an entity based on its UUID (from {@link Entity#getUuid()}). + * + * @param uuid the entity UUID + * @return the entity having the specified uuid, null if not found + */ + public @Nullable Entity getEntityByUuid(UUID uuid) { + return entityTracker.getEntityByUuid(uuid); + } + + /** + * Gets a player based on its UUID (from {@link Entity#getUuid()}). + * + * @param uuid the player UUID + * @return the player having the specified uuid, null if not found or not a player + */ + public @Nullable Player getPlayerByUuid(UUID uuid) { + Entity entity = entityTracker.getEntityByUuid(uuid); + if (entity instanceof Player player) { + return player; + } + return null; + } + /** * Gets the players in the instance; * @@ -681,7 +772,6 @@ public void sendBlockAction(@NotNull Point blockPosition, byte actionId, byte ac return getChunk(point.chunkX(), point.chunkZ()); } - @ApiStatus.Experimental public EntityTracker getEntityTracker() { return entityTracker; } @@ -711,9 +801,8 @@ public void tick(long time) { this.worldAge++; this.time += timeRate; // time needs to be sent to players - if (timeUpdate != null && !Cooldown.hasCooldown(time, lastTimeUpdate, timeUpdate)) { + if (timeSynchronizationTicks > 0 && this.worldAge % timeSynchronizationTicks == 0) { PacketUtils.sendGroupedPacket(getPlayers(), createTimePacket()); - this.lastTimeUpdate = time; } } @@ -732,7 +821,12 @@ public void tick(long time) { // Set last tick age this.lastTickAge = time; } - this.worldBorder.update(); + // World border + if (remainingWorldBorderTransitionTicks > 0) { + worldBorder = transitionWorldBorder(remainingWorldBorderTransitionTicks); + if (worldBorder.diameter() == targetBorderDiameter) remainingWorldBorderTransitionTicks = 0; + else remainingWorldBorderTransitionTicks--; + } // End of tick scheduled tasks this.scheduler.processTickEnd(); } @@ -835,7 +929,7 @@ public void explode(float centerX, float centerY, float centerZ, float strength) * @param additionalData data to pass to the explosion supplier * @throws IllegalStateException If no {@link ExplosionSupplier} was supplied */ - public void explode(float centerX, float centerY, float centerZ, float strength, @Nullable NBTCompound additionalData) { + public void explode(float centerX, float centerY, float centerZ, float strength, @Nullable CompoundBinaryTag additionalData) { final ExplosionSupplier explosionSupplier = getExplosionSupplier(); Check.stateCondition(explosionSupplier == null, "Tried to create an explosion with no explosion supplier"); final Explosion explosion = explosionSupplier.createExplosion(centerX, centerY, centerZ, strength, additionalData); @@ -860,18 +954,6 @@ public void setExplosionSupplier(@Nullable ExplosionSupplier supplier) { this.explosionSupplier = supplier; } - /** - * Gets the instance space. - *

- * Used by the pathfinder for entities. - * - * @return the instance space - */ - @ApiStatus.Internal - public @NotNull PFInstanceSpace getInstanceSpace() { - return instanceSpace; - } - @Override public @NotNull Pointers pointers() { return this.pointers; diff --git a/src/main/java/net/minestom/server/instance/InstanceContainer.java b/src/main/java/net/minestom/server/instance/InstanceContainer.java index 31459fcb6ab..ea28b8b2a15 100644 --- a/src/main/java/net/minestom/server/instance/InstanceContainer.java +++ b/src/main/java/net/minestom/server/instance/InstanceContainer.java @@ -1,6 +1,7 @@ package net.minestom.server.instance; import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; +import net.kyori.adventure.nbt.CompoundBinaryTag; import net.minestom.server.MinecraftServer; import net.minestom.server.coordinate.BlockVec; import net.minestom.server.coordinate.Point; @@ -11,16 +12,19 @@ import net.minestom.server.event.instance.InstanceChunkLoadEvent; import net.minestom.server.event.instance.InstanceChunkUnloadEvent; import net.minestom.server.event.player.PlayerBlockBreakEvent; +import net.minestom.server.instance.anvil.AnvilLoader; import net.minestom.server.instance.block.Block; import net.minestom.server.instance.block.BlockFace; import net.minestom.server.instance.block.BlockHandler; import net.minestom.server.instance.block.rule.BlockPlacementRule; import net.minestom.server.instance.generator.Generator; +import net.minestom.server.instance.generator.GeneratorImpl; import net.minestom.server.instance.palette.Palette; import net.minestom.server.network.packet.server.play.BlockChangePacket; import net.minestom.server.network.packet.server.play.BlockEntityDataPacket; import net.minestom.server.network.packet.server.play.EffectPacket; import net.minestom.server.network.packet.server.play.UnloadChunkPacket; +import net.minestom.server.registry.DynamicRegistry; import net.minestom.server.utils.NamespaceID; import net.minestom.server.utils.PacketUtils; import net.minestom.server.utils.async.AsyncUtils; @@ -30,10 +34,8 @@ 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.jglrxavpok.hephaistos.nbt.NBTCompound; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import space.vectrix.flare.fastutil.Long2ObjectSyncMap; @@ -87,22 +89,30 @@ public class InstanceContainer extends Instance { protected InstanceContainer srcInstance; // only present if this instance has been created using a copy 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()); + public InstanceContainer(@NotNull UUID uniqueId, @NotNull DynamicRegistry.Key dimensionType) { + this(uniqueId, dimensionType, null, dimensionType.namespace()); } - public InstanceContainer(@NotNull UUID uniqueId, @NotNull DimensionType dimensionType, @NotNull NamespaceID dimensionName) { + public InstanceContainer(@NotNull UUID uniqueId, @NotNull DynamicRegistry.Key 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()); + public InstanceContainer(@NotNull UUID uniqueId, @NotNull DynamicRegistry.Key dimensionType, @Nullable IChunkLoader loader) { + 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); + public InstanceContainer(@NotNull UUID uniqueId, @NotNull DynamicRegistry.Key dimensionType, @Nullable IChunkLoader loader, @NotNull NamespaceID dimensionName) { + this(MinecraftServer.getDimensionTypeRegistry(), uniqueId, dimensionType, loader, dimensionName); + } + + public InstanceContainer( + @NotNull DynamicRegistry dimensionTypeRegistry, + @NotNull UUID uniqueId, + @NotNull DynamicRegistry.Key dimensionType, + @Nullable IChunkLoader loader, + @NotNull NamespaceID dimensionName + ) { + super(dimensionTypeRegistry, uniqueId, dimensionType, dimensionName); setChunkSupplier(DynamicChunk::new); setChunkLoader(Objects.requireNonNullElse(loader, DEFAULT_LOADER)); this.chunkLoader.loadInstance(this); @@ -134,8 +144,9 @@ private synchronized void UNSAFE_setBlock(@NotNull Chunk chunk, int x, int y, in @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); + final DimensionType dim = getCachedDimensionType(); + if (y >= dim.maxY() || y < dim.minY()) { + LOGGER.warn("tried to set a block outside the world bounds, should be within [{}, {}): {}", dim.minY(), dim.maxY(), y); return; } @@ -159,7 +170,7 @@ private synchronized void UNSAFE_setBlock(@NotNull Chunk chunk, int x, int y, in this, block, pp.getBlockFace(), blockPosition, new Vec(pp.getCursorX(), pp.getCursorY(), pp.getCursorZ()), pp.getPlayer().getPosition(), - pp.getPlayer().getItemInHand(pp.getHand()).meta(), + pp.getPlayer().getItemInHand(pp.getHand()), pp.getPlayer().isSneaking() ); } else { @@ -187,7 +198,7 @@ private synchronized void UNSAFE_setBlock(@NotNull Chunk chunk, int x, int y, in chunk.sendPacketToViewers(new BlockChangePacket(blockPosition, block.stateId())); var registry = block.registry(); if (registry.isBlockEntity()) { - final NBTCompound data = BlockUtils.extractClientNbt(block); + final CompoundBinaryTag data = BlockUtils.extractClientNbt(block); chunk.sendPacketToViewers(new BlockEntityDataPacket(blockPosition, registry.blockEntityId(), data)); } } @@ -259,9 +270,7 @@ public synchronized void unloadChunk(@NotNull Chunk chunk) { // Clear cache this.chunks.remove(getChunkIndex(chunkX, chunkZ)); chunk.unload(); - if (chunkLoader != null) { - chunkLoader.unloadChunk(chunk); - } + chunkLoader.unloadChunk(chunk); var dispatcher = MinecraftServer.process().dispatcher(); dispatcher.deletePartition(chunk); } @@ -335,7 +344,13 @@ public Chunk getChunk(int chunkX, int chunkZ) { CompletableFuture resultFuture = new CompletableFuture<>(); // TODO: virtual thread once Loom is available ForkJoinPool.commonPool().submit(() -> { - var chunkUnit = GeneratorImpl.chunk(chunk); + GeneratorImpl.GenSection[] genSections = new GeneratorImpl.GenSection[chunk.getSections().size()]; + Arrays.setAll(genSections, i -> { + Section section = chunk.getSections().get(i); + return new GeneratorImpl.GenSection(section.blockPalette(), section.biomePalette()); + }); + var chunkUnit = GeneratorImpl.chunk(MinecraftServer.getBiomeRegistry(), genSections, + chunk.getChunkX(), chunk.minSection, chunk.getChunkZ()); try { // Generate block/biome palette generator.generate(chunkUnit); @@ -352,7 +367,7 @@ public Chunk getChunk(int chunkX, int chunkZ) { var sections = ((GeneratorImpl.AreaModifierImpl) fork.modifier()).sections(); for (var section : sections) { if (section.modifier() instanceof GeneratorImpl.SectionModifierImpl sectionModifier) { - if (sectionModifier.blockPalette().count() == 0) + if (sectionModifier.genSection().blocks().count() == 0) continue; final Point start = section.absoluteStart(); final Chunk forkChunk = start.chunkX() == chunkX && start.chunkZ() == chunkZ ? chunk : getChunkAt(start); @@ -362,7 +377,7 @@ public Chunk getChunk(int chunkX, int chunkZ) { forkChunk.invalidate(); forkChunk.sendChunk(); } else { - final long index = ChunkUtils.getChunkIndex(start); + final long index = getChunkIndex(start); this.generationForks.compute(index, (i, sectionModifiers) -> { if (sectionModifiers == null) sectionModifiers = new ArrayList<>(); sectionModifiers.add(sectionModifier); @@ -406,13 +421,13 @@ private void applyFork(Chunk chunk, GeneratorImpl.SectionModifierImpl sectionMod Section section = chunk.getSectionAt(sectionModifier.start().blockY()); Palette currentBlocks = section.blockPalette(); // -1 is necessary because forked units handle explicit changes by changing AIR 0 to 1 - sectionModifier.blockPalette().getAllPresent((x, y, z, value) -> currentBlocks.set(x, y, z, value - 1)); + sectionModifier.genSection().blocks().getAllPresent((x, y, z, value) -> currentBlocks.set(x, y, z, value - 1)); applyGenerationData(chunk, sectionModifier); } } private void applyGenerationData(Chunk chunk, GeneratorImpl.SectionModifierImpl section) { - var cache = section.cache(); + var cache = section.genSection().specials(); if (cache.isEmpty()) return; final int height = section.start().blockY(); synchronized (chunk) { @@ -440,7 +455,7 @@ public boolean hasEnabledAutoChunkLoad() { @Override public boolean isInVoid(@NotNull Point point) { // TODO: more customizable - return point.y() < getDimensionType().getMinY() - 64; + return point.y() < getCachedDimensionType().minY() - 64; } /** @@ -578,17 +593,19 @@ public void setGenerator(@Nullable Generator generator) { * * @return the {@link IChunkLoader} of this instance */ - public IChunkLoader getChunkLoader() { + public @NotNull IChunkLoader getChunkLoader() { return chunkLoader; } /** * Changes the {@link IChunkLoader} of this instance (to change how chunks are retrieved when not already loaded). * + *

{@link IChunkLoader#noop()} can be used to do nothing.

+ * * @param chunkLoader the new {@link IChunkLoader} */ - public void setChunkLoader(IChunkLoader chunkLoader) { - this.chunkLoader = chunkLoader; + public void setChunkLoader(@NotNull IChunkLoader chunkLoader) { + this.chunkLoader = Objects.requireNonNull(chunkLoader, "Chunk loader cannot be null"); } @Override @@ -629,10 +646,10 @@ private void executeNeighboursBlockPlacementRule(@NotNull Point blockPosition, i 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 < getCachedDimensionType().minY() || neighborY > getCachedDimensionType().height()) continue; - final Block neighborBlock = cache.getBlock(neighborX, neighborY, neighborZ, Condition.TYPE); - if (neighborBlock == null) + final Block neighborBlock = cache.getBlock(neighborX, neighborY, neighborZ, Condition.NONE); + if (neighborBlock == null || neighborBlock.isAir()) continue; final BlockPlacementRule neighborBlockPlacementRule = MinecraftServer.getBlockManager().getBlockPlacementRule(neighborBlock); if (neighborBlockPlacementRule == null || updateDistance >= neighborBlockPlacementRule.maxUpdateDistance()) continue; diff --git a/src/main/java/net/minestom/server/instance/InstanceManager.java b/src/main/java/net/minestom/server/instance/InstanceManager.java index 2a0bc676993..6b4bc2d12b2 100644 --- a/src/main/java/net/minestom/server/instance/InstanceManager.java +++ b/src/main/java/net/minestom/server/instance/InstanceManager.java @@ -1,12 +1,14 @@ package net.minestom.server.instance; import net.minestom.server.MinecraftServer; +import net.minestom.server.entity.Player; import net.minestom.server.event.EventDispatcher; import net.minestom.server.event.instance.InstanceRegisterEvent; import net.minestom.server.event.instance.InstanceUnregisterEvent; +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.registry.Registries; 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; @@ -21,8 +23,13 @@ */ public final class InstanceManager { + private final Registries registries; private final Set instances = new CopyOnWriteArraySet<>(); + public InstanceManager(@NotNull Registries registries) { + this.registries = registries; + } + /** * Registers an {@link Instance} internally. *

@@ -44,18 +51,16 @@ public void registerInstance(@NotNull Instance instance) { * @param loader the chunk loader * @return the created {@link InstanceContainer} */ - @ApiStatus.Experimental - public @NotNull InstanceContainer createInstanceContainer(@NotNull DimensionType dimensionType, @Nullable IChunkLoader loader) { - final InstanceContainer instanceContainer = new InstanceContainer(UUID.randomUUID(), dimensionType, loader); + public @NotNull InstanceContainer createInstanceContainer(@NotNull DynamicRegistry.Key dimensionType, @Nullable IChunkLoader loader) { + final InstanceContainer instanceContainer = new InstanceContainer(registries.dimensionType(), UUID.randomUUID(), dimensionType, loader, dimensionType.namespace()); registerInstance(instanceContainer); return instanceContainer; } - public @NotNull InstanceContainer createInstanceContainer(@NotNull DimensionType dimensionType) { + public @NotNull InstanceContainer createInstanceContainer(@NotNull DynamicRegistry.Key dimensionType) { return createInstanceContainer(dimensionType, null); } - @ApiStatus.Experimental public @NotNull InstanceContainer createInstanceContainer(@Nullable IChunkLoader loader) { return createInstanceContainer(DimensionType.OVERWORLD, loader); } @@ -110,7 +115,8 @@ public void registerInstance(@NotNull Instance instance) { * @param instance the {@link Instance} to unregister */ public void unregisterInstance(@NotNull Instance instance) { - Check.stateCondition(!instance.getPlayers().isEmpty(), "You cannot unregister an instance with players inside."); + long onlinePlayers = instance.getPlayers().stream().filter(Player::isOnline).count(); + Check.stateCondition(onlinePlayers > 0, "You cannot unregister an instance with players inside."); synchronized (instance) { InstanceUnregisterEvent event = new InstanceUnregisterEvent(instance); EventDispatcher.call(event); diff --git a/src/main/java/net/minestom/server/instance/LightingChunk.java b/src/main/java/net/minestom/server/instance/LightingChunk.java index 8e4801a8c6f..c95dfc761d3 100644 --- a/src/main/java/net/minestom/server/instance/LightingChunk.java +++ b/src/main/java/net/minestom/server/instance/LightingChunk.java @@ -1,6 +1,5 @@ package net.minestom.server.instance; -import net.minestom.server.MinecraftServer; import net.minestom.server.ServerFlag; import net.minestom.server.collision.Shape; import net.minestom.server.coordinate.Point; @@ -38,11 +37,12 @@ public class LightingChunk extends DynamicChunk { private static final ExecutorService pool = Executors.newWorkStealingPool(); private int[] occlusionMap; - final CachedPacket lightCache = new CachedPacket(this::createLightPacket); - private LightData lightData; + final CachedPacket partialLightCache = new CachedPacket(this::createLightPacket); + private LightData partialLightData; + private LightData fullLightData; - boolean chunkLoaded = false; private int highestBlock; + private boolean freezeInvalidation = false; private final ReentrantLock packetGenerationLock = new ReentrantLock(); private final AtomicInteger resendTimer = new AtomicInteger(-1); @@ -87,9 +87,10 @@ private enum QueueType { ); public void invalidate() { - this.lightCache.invalidate(); + this.partialLightCache.invalidate(); this.chunkCache.invalidate(); - this.lightData = null; + this.partialLightData = null; + this.fullLightData = null; } public LightingChunk(@NotNull Instance instance, int chunkX, int chunkZ) { @@ -107,7 +108,15 @@ private boolean checkSkyOcclusion(Block block) { return occludesBottom || occludesTop; } - private void invalidateSection(int coordinate) { + public void setFreezeInvalidation(boolean freezeInvalidation) { + this.freezeInvalidation = freezeInvalidation; + } + + public void invalidateNeighborsSection(int coordinate) { + if (freezeInvalidation) { + return; + } + for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { Chunk neighborChunk = instance.getChunk(chunkX + i, chunkZ + j); @@ -126,6 +135,21 @@ private void invalidateSection(int coordinate) { } } + public void invalidateResendDelay() { + if (!doneInit || freezeInvalidation) { + return; + } + + for (int i = -1; i <= 1; i++) { + for (int j = -1; j <= 1; j++) { + Chunk neighborChunk = instance.getChunk(chunkX + i, chunkZ + j); + if (neighborChunk instanceof LightingChunk light) { + light.resendTimer.set(resendDelay); + } + } + } + } + @Override public void setBlock(int x, int y, int z, @NotNull Block block, @Nullable BlockHandler.Placement placement, @@ -135,33 +159,20 @@ public void setBlock(int x, int y, int z, @NotNull Block block, // Invalidate neighbor chunks, since they can be updated by this block change int coordinate = ChunkUtils.getChunkCoordinate(y); - if (chunkLoaded) { - invalidateSection(coordinate); - this.lightCache.invalidate(); - - if (doneInit) { - for (int i = -1; i <= 1; i++) { - for (int j = -1; j <= 1; j++) { - Chunk neighborChunk = instance.getChunk(chunkX + i, chunkZ + j); - if (neighborChunk == null) continue; - - if (neighborChunk instanceof LightingChunk light) { - light.resendTimer.set(resendDelay); - } - } - } - } + if (doneInit && !freezeInvalidation) { + invalidateNeighborsSection(coordinate); + invalidateResendDelay(); + this.partialLightCache.invalidate(); } } public void sendLighting() { if (!isLoaded()) return; - sendPacketToViewers(lightCache); + sendPacketToViewers(partialLightCache); } @Override protected void onLoad() { - chunkLoaded = true; doneInit = true; } @@ -176,27 +187,24 @@ public void onGenerate() { invalidate(); - MinecraftServer.getSchedulerManager().scheduleNextTick(() -> { - for (int i = -1; i <= 1; i++) { - for (int j = -1; j <= 1; j++) { - Chunk neighborChunk = instance.getChunk(chunkX + i, chunkZ + j); - if (neighborChunk == null) continue; + for (int i = -1; i <= 1; i++) { + for (int j = -1; j <= 1; j++) { + Chunk neighborChunk = instance.getChunk(chunkX + i, chunkZ + j); + if (neighborChunk == null) continue; + + if (neighborChunk instanceof LightingChunk light) { + if (light.doneInit) { + light.resendTimer.set(20); + light.invalidate(); - if (neighborChunk instanceof LightingChunk light) { - for (int section = light.minSection; section < light.maxSection; section++) { + for (int section = minSection; section < maxSection; section++) { light.getSection(section).blockLight().invalidate(); light.getSection(section).skyLight().invalidate(); } - - light.invalidate(); - - light.resendTimer.set(20); } } } - }); - - doneInit = true; + } } // Lazy compute occlusion map @@ -204,7 +212,7 @@ public int[] getOcclusionMap() { 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.getCachedDimensionType().minY(); highestBlock = minY - 1; synchronized (this) { @@ -229,11 +237,18 @@ public int[] getOcclusionMap() { } @Override - protected LightData createLightData() { + protected LightData createLightData(boolean requiredFullChunk) { packetGenerationLock.lock(); - if (lightData != null) { - packetGenerationLock.unlock(); - return lightData; + if (requiredFullChunk) { + if (fullLightData != null) { + packetGenerationLock.unlock(); + return fullLightData; + } + } else { + if (partialLightData != null) { + packetGenerationLock.unlock(); + return partialLightData; + } } BitSet skyMask = new BitSet(); @@ -243,9 +258,8 @@ protected LightData createLightData() { List skyLights = new ArrayList<>(); List blockLights = new ArrayList<>(); - int chunkMin = instance.getDimensionType().getMinY(); - - int highestNeighborBlock = instance.getDimensionType().getMinY(); + int chunkMin = instance.getCachedDimensionType().minY(); + int highestNeighborBlock = instance.getCachedDimensionType().minY(); for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { Chunk neighborChunk = instance.getChunk(chunkX + i, chunkZ + j); @@ -266,21 +280,21 @@ protected LightData createLightData() { if (section.blockLight().requiresUpdate()) { relightSection(instance, this.chunkX, index + minSection, chunkZ, LightType.BLOCK); wasUpdatedBlock = true; - } else if (section.blockLight().requiresSend()) { + } else if (requiredFullChunk || section.blockLight().requiresSend()) { wasUpdatedBlock = true; } if (section.skyLight().requiresUpdate()) { relightSection(instance, this.chunkX, index + minSection, chunkZ, LightType.SKY); wasUpdatedSky = true; - } else if (section.skyLight().requiresSend()) { + } else if (requiredFullChunk || section.skyLight().requiresSend()) { wasUpdatedSky = true; } final int sectionMinY = index * 16 + chunkMin; index++; - if ((wasUpdatedSky) && this.instance.getDimensionType().isSkylightEnabled() && sectionMinY <= (highestNeighborBlock + 16)) { + if ((wasUpdatedSky) && this.instance.getCachedDimensionType().hasSkylight() && sectionMinY <= (highestNeighborBlock + 16)) { final byte[] skyLight = section.skyLight().array(); if (skyLight.length != 0 && skyLight != emptyContent) { @@ -303,13 +317,19 @@ protected LightData createLightData() { } } - this.lightData = new LightData(skyMask, blockMask, + LightData lightData = new LightData(skyMask, blockMask, emptySkyMask, emptyBlockMask, skyLights, blockLights); + if (requiredFullChunk) { + this.fullLightData = lightData; + } else { + this.partialLightData = lightData; + } + packetGenerationLock.unlock(); - return this.lightData; + return lightData; } @Override @@ -337,11 +357,16 @@ private static Set flushQueue(Instance instance, Set queue, LightT var section = chunk.getSection(point.blockY()); responseChunks.add(chunk); - var light = type == LightType.BLOCK ? section.blockLight() : section.skyLight(); + Light light = switch(type) { + case BLOCK -> section.blockLight(); + case SKY -> section.skyLight(); + }; CompletableFuture task = CompletableFuture.runAsync(() -> { - if (queueType == QueueType.INTERNAL) light.calculateInternal(instance, chunk.getChunkX(), point.blockY(), chunk.getChunkZ()); - else light.calculateExternal(instance, chunk, point.blockY()); + switch (queueType) { + case INTERNAL -> light.calculateInternal(instance, chunk.getChunkX(), point.blockY(), chunk.getChunkZ()); + case EXTERNAL -> light.calculateExternal(instance, chunk, point.blockY()); + } sections.add(light); @@ -422,7 +447,7 @@ private static Set getNearbyRequired(Instance instance, Point point, Ligh Set collected = new HashSet<>(); collected.add(point); - int highestRegionPoint = instance.getDimensionType().getMinY() - 1; + int highestRegionPoint = instance.getCachedDimensionType().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,12 +469,13 @@ private static Set getNearbyRequired(Instance instance, Point point, Ligh 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.getCachedDimensionType().minY() + 16 * y; if ((sectionHeight + 16) > highestRegionPoint && type == LightType.SKY) continue; if (sectionPosition.blockY() < chunkCheck.getMaxSection() && sectionPosition.blockY() >= chunkCheck.getMinSection()) { Section s = chunkCheck.getSection(sectionPosition.blockY()); - if (!s.blockLight().requiresUpdate() && !s.skyLight().requiresUpdate()) continue; + if (type == LightType.BLOCK && !s.blockLight().requiresUpdate()) continue; + if (type == LightType.SKY && !s.skyLight().requiresUpdate()) continue; collected.add(sectionPosition); } @@ -509,4 +535,9 @@ private static Set relight(Instance instance, Set queue, LightType lightingChunk.entries.putAll(entries); return lightingChunk; } + + @Override + public boolean isLoaded() { + return super.isLoaded() && doneInit; + } } \ No newline at end of file diff --git a/src/main/java/net/minestom/server/instance/NoopChunkLoaderImpl.java b/src/main/java/net/minestom/server/instance/NoopChunkLoaderImpl.java new file mode 100644 index 00000000000..96b6fab6701 --- /dev/null +++ b/src/main/java/net/minestom/server/instance/NoopChunkLoaderImpl.java @@ -0,0 +1,23 @@ +package net.minestom.server.instance; + +import net.minestom.server.utils.async.AsyncUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.concurrent.CompletableFuture; + +final class NoopChunkLoaderImpl implements IChunkLoader { + static final NoopChunkLoaderImpl INSTANCE = new NoopChunkLoaderImpl(); + + private NoopChunkLoaderImpl(){} + + @Override + public @NotNull CompletableFuture<@Nullable Chunk> loadChunk(@NotNull Instance instance, int chunkX, int chunkZ) { + return CompletableFuture.completedFuture(null); + } + + @Override + public @NotNull CompletableFuture saveChunk(@NotNull Chunk chunk) { + return AsyncUtils.VOID_FUTURE; + } +} diff --git a/src/main/java/net/minestom/server/instance/SharedInstance.java b/src/main/java/net/minestom/server/instance/SharedInstance.java index 925252634cb..c01540fa81e 100644 --- a/src/main/java/net/minestom/server/instance/SharedInstance.java +++ b/src/main/java/net/minestom/server/instance/SharedInstance.java @@ -125,4 +125,36 @@ public boolean isInVoid(@NotNull Point point) { public @NotNull InstanceContainer getInstanceContainer() { return instanceContainer; } + + /** + * Gets if two instances share the same chunks. + * + * @param instance1 the first instance + * @param instance2 the second instance + * @return true if the two instances share the same chunks + */ + public static boolean areLinked(Instance instance1, Instance instance2) { + // SharedInstance check + if (instance1 instanceof InstanceContainer && instance2 instanceof SharedInstance) { + return ((SharedInstance) instance2).getInstanceContainer().equals(instance1); + } else if (instance2 instanceof InstanceContainer && instance1 instanceof SharedInstance) { + return ((SharedInstance) instance1).getInstanceContainer().equals(instance2); + } else if (instance1 instanceof SharedInstance && instance2 instanceof SharedInstance) { + final InstanceContainer container1 = ((SharedInstance) instance1).getInstanceContainer(); + final InstanceContainer container2 = ((SharedInstance) instance2).getInstanceContainer(); + return container1.equals(container2); + } + + // InstanceContainer check (copied from) + if (instance1 instanceof InstanceContainer container1 && instance2 instanceof InstanceContainer container2) { + if (container1.getSrcInstance() != null) { + return container1.getSrcInstance().equals(container2) + && container1.getLastBlockChangeTime() == container2.getLastBlockChangeTime(); + } else if (container2.getSrcInstance() != null) { + return container2.getSrcInstance().equals(container1) + && container2.getLastBlockChangeTime() == container1.getLastBlockChangeTime(); + } + } + return false; + } } diff --git a/src/main/java/net/minestom/server/instance/WorldBorder.java b/src/main/java/net/minestom/server/instance/WorldBorder.java index 80542a63596..c848f5fffd9 100644 --- a/src/main/java/net/minestom/server/instance/WorldBorder.java +++ b/src/main/java/net/minestom/server/instance/WorldBorder.java @@ -3,263 +3,141 @@ import net.minestom.server.ServerFlag; import net.minestom.server.coordinate.Point; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.Player; -import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.play.*; -import net.minestom.server.utils.PacketUtils; -import org.jetbrains.annotations.ApiStatus; +import net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; /** - * Represents the world border of an {@link Instance}, + * Represents the world border state of an {@link Instance}, * can be retrieved with {@link Instance#getWorldBorder()}. + * + * @param diameter the diameter of this world border + * @param centerX the center x coordinate of this world border + * @param centerZ the center z coordinate of this world border + * @param warningDistance the distance from this world border before + * the warning indicator is displayed + * @param warningTime the length of time the warning indicator + * is displayed + * @param dimensionTeleportBoundary restricts the distance travelled when entering + * this world from another dimension (should be at + * least the diameter of the world border) */ -public class WorldBorder { - - private final Instance instance; - - private float centerX, centerZ; - - private volatile double currentDiameter; - - private double oldDiameter; - private double newDiameter; - - private long lerpStartTime; - - private long speed; - private final int portalTeleportBoundary; - private int warningTime; - private int warningBlocks; - - protected WorldBorder(@NotNull Instance instance) { - this.instance = instance; - - this.oldDiameter = Double.MAX_VALUE; - this.newDiameter = Double.MAX_VALUE; - - this.speed = 0; - - this.portalTeleportBoundary = ServerFlag.WORLD_BORDER_SIZE; - - // Update immediately so the current size is present on init - update(); - } +public record WorldBorder(double diameter, double centerX, double centerZ, int warningDistance, int warningTime, int dimensionTeleportBoundary) { + public static final WorldBorder DEFAULT_BORDER = new WorldBorder(ServerFlag.WORLD_BORDER_SIZE * 2, 0, 0, 5, 15, ServerFlag.WORLD_BORDER_SIZE); /** - * Changes the X and Z position of the center. - * - * @param centerX the X center - * @param centerZ the Z center + * @throws IllegalArgumentException if {@code diameter} is less than 0 */ - public void setCenter(float centerX, float centerZ) { - this.centerX = centerX; - this.centerZ = centerZ; - refreshCenter(); + public WorldBorder { + Check.argCondition(diameter < 0, "Diameter should be >= 0"); } - /** - * Gets the center X of the world border. - * - * @return the X center - */ - public float getCenterX() { - return centerX; - } - - /** - * Changes the center X of the world border. - * - * @param centerX the new center X - */ - public void setCenterX(float centerX) { - this.centerX = centerX; - refreshCenter(); - } - - /** - * Gets the center Z of the world border. - * - * @return the Z center - */ - public float getCenterZ() { - return centerZ; - } - - /** - * Changes the center Z of the world border. - * - * @param centerZ the new center Z - */ - public void setCenterZ(float centerZ) { - this.centerZ = centerZ; - refreshCenter(); + public WorldBorder(double diameter, double centerX, double centerZ, int warningDistance, int warningTime) { + this(diameter, centerX, centerZ, warningDistance, warningTime, ServerFlag.WORLD_BORDER_SIZE); } - public int getWarningTime() { - return warningTime; + @Contract(pure = true) + public @NotNull WorldBorder withDiameter(double diameter) { + return new WorldBorder(diameter, centerX, centerZ, warningDistance, warningTime, dimensionTeleportBoundary); } - /** - * @param warningTime In seconds as /worldborder warning time - */ - public void setWarningTime(int warningTime) { - this.warningTime = warningTime; - sendPacket(new WorldBorderWarningDelayPacket(warningTime)); + @Contract(pure = true) + public @NotNull WorldBorder withCenter(double centerX, double centerZ) { + return new WorldBorder(diameter, centerX, centerZ, warningDistance, warningTime, dimensionTeleportBoundary); } - public int getWarningBlocks() { - return warningBlocks; + @Contract(pure = true) + public @NotNull WorldBorder withWarningDistance(int warningDistance) { + return new WorldBorder(diameter, centerX, centerZ, warningDistance, warningTime, dimensionTeleportBoundary); } - /** - * @param warningBlocks In meters - */ - public void setWarningBlocks(int warningBlocks) { - this.warningBlocks = warningBlocks; - sendPacket(new WorldBorderWarningReachPacket(warningBlocks)); + @Contract(pure = true) + public @NotNull WorldBorder withWarningTime(int warningTime) { + return new WorldBorder(diameter, centerX, centerZ, warningDistance, warningTime, dimensionTeleportBoundary); } /** - * Changes the diameter to {@code diameter} in {@code speed} milliseconds (interpolation). + * Used to know if a position is located inside the world border or not. * - * @param diameter the diameter target - * @param speed the time it will take to reach {@code diameter} in milliseconds + * @param point the point to check + * @return true if {@code position} is inside the world border, false otherwise */ - public void setDiameter(double diameter, long speed) { - if (speed <= 0) { - setDiameter(diameter); - return; - } - this.newDiameter = diameter; - this.speed = speed; - this.lerpStartTime = System.currentTimeMillis(); - sendPacket(new WorldBorderLerpSizePacket(oldDiameter, newDiameter, speed)); + public boolean inBounds(@NotNull Point point) { + double radius = diameter / 2; + return point.x() <= centerX + radius && point.x() >= centerX - radius && + point.z() <= centerZ + radius && point.z() >= centerZ - radius; } /** - * Gets the diameter of the world border. - * It takes lerp in consideration. + * Used to know if an entity is located inside the world border or not. * - * @return the current world border diameter + * @param entity the entity to check + * @return true if {@code entity} is inside the world border, false otherwise */ - public double getDiameter() { - return currentDiameter; + public boolean inBounds(@NotNull Entity entity) { + return inBounds(entity.getPosition()); } /** - * Changes the diameter of the world border. + * Creates a {@link InitializeWorldBorderPacket} which dictates every property + * of the world border. * - * @param diameter the new diameter of the world border + * @param targetDiameter the target diameter if there is a current lerp in progress + * @param transitionTime the transition time in milliseconds of the current + * lerp in progress + * @return an {@link InitializeWorldBorderPacket} reflecting the + * properties of this border */ - public void setDiameter(double diameter) { - this.currentDiameter = diameter; - this.oldDiameter = diameter; - this.newDiameter = diameter; - this.lerpStartTime = 0; - sendPacket(new WorldBorderSizePacket(diameter)); + public @NotNull InitializeWorldBorderPacket createInitializePacket(double targetDiameter, long transitionTime) { + return new InitializeWorldBorderPacket(centerX, centerZ, diameter, targetDiameter, transitionTime, dimensionTeleportBoundary, warningTime, warningDistance); } /** - * Used to check at which axis does the position collides with the world border. + * Creates a {@link WorldBorderSizePacket} which dictates the origin of the world border. * - * @param point the point to check - * @return the axis where the position collides with the world border + * @return the {@link WorldBorderSizePacket} with the center values of this world border */ - public @NotNull CollisionAxis getCollisionAxis(@NotNull Point point) { - final double radius = getDiameter() / 2d; - final boolean checkX = point.x() <= getCenterX() + radius && point.x() >= getCenterX() - radius; - final boolean checkZ = point.z() <= getCenterZ() + radius && point.z() >= getCenterZ() - radius; - if (!checkX || !checkZ) { - if (!checkX && !checkZ) { - return CollisionAxis.BOTH; - } else if (!checkX) { - return CollisionAxis.X; - } else { - return CollisionAxis.Z; - } - } - return CollisionAxis.NONE; + public @NotNull WorldBorderCenterPacket createCenterPacket() { + return new WorldBorderCenterPacket(centerX, centerZ); } /** - * Used to know if a position is located inside the world border or not. + * Creates a {@link WorldBorderLerpSizePacket} which lerps the border from its current + * diameter to the target diameter over the given transition time. * - * @param point the point to check - * @return true if {@code position} is inside the world border, false otherwise + * @param targetDiameter the final diameter of the border after this transition + * @param transitionTime the transition time in milliseconds for this lerp + * @return the {@link WorldBorderLerpSizePacket} representing this lerp */ - public boolean isInside(@NotNull Point point) { - return getCollisionAxis(point) == CollisionAxis.NONE; + public @NotNull WorldBorderLerpSizePacket createLerpSizePacket(double targetDiameter, long transitionTime) { + return new WorldBorderLerpSizePacket(diameter, targetDiameter, transitionTime); } /** - * Used to know if an entity is located inside the world border or not. + * Creates a {@link WorldBorderSizePacket} with this world border's diameter. * - * @param entity the entity to check - * @return true if {@code entity} is inside the world border, false otherwise - */ - public boolean isInside(@NotNull Entity entity) { - return isInside(entity.getPosition()); - } - - /** - * Used to update in real-time the current diameter time. - * Called in the instance tick update. + * @return the {@link WorldBorderSizePacket} with this world border's diameter */ - protected void update() { - if (lerpStartTime == 0) { - this.currentDiameter = oldDiameter; - } else { - double diameterDelta = newDiameter - oldDiameter; - long elapsedTime = System.currentTimeMillis() - lerpStartTime; - double percentage = (double) elapsedTime / (double) speed; - - // World border finished lerp - if (percentage > 0.99) { - this.lerpStartTime = 0; - this.speed = 0; - this.oldDiameter = newDiameter; - this.currentDiameter = newDiameter; - } else { - this.currentDiameter = oldDiameter + (diameterDelta * percentage); - } - } + public @NotNull WorldBorderSizePacket createSizePacket() { + return new WorldBorderSizePacket(diameter); } /** - * Sends the world border init packet to a player. + * Creates a {@link WorldBorderWarningDelayPacket} with this world border's warning time * - * @param player the player to send the packet to + * @return the {@link WorldBorderWarningDelayPacket} with this world border's warning time */ - @ApiStatus.Internal - public void init(@NotNull Player player) { - player.sendPacket(new InitializeWorldBorderPacket(centerX, centerZ, - oldDiameter, newDiameter, speed, portalTeleportBoundary, warningTime, warningBlocks)); + public @NotNull WorldBorderWarningDelayPacket createWarningDelayPacket() { + return new WorldBorderWarningDelayPacket(warningTime); } /** - * Gets the {@link Instance} linked to this world border. + * Creates a {@link WorldBorderWarningReachPacket} with this world border's warning distance * - * @return the {@link Instance} of this world border + * @return the {@link WorldBorderWarningReachPacket} with this world border's warning distance */ - @NotNull - public Instance getInstance() { - return instance; + public @NotNull WorldBorderWarningReachPacket createWarningReachPacket() { + return new WorldBorderWarningReachPacket(warningDistance); } - - /** - * Sends the new world border centers to all instance players. - */ - private void refreshCenter() { - sendPacket(new WorldBorderCenterPacket(centerX, centerZ)); - } - - private void sendPacket(@NotNull ServerPacket packet) { - PacketUtils.sendGroupedPacket(instance.getPlayers(), packet); - } - - public enum CollisionAxis { - X, Z, BOTH, NONE - } - } diff --git a/src/main/java/net/minestom/server/instance/anvil/AnvilLoader.java b/src/main/java/net/minestom/server/instance/anvil/AnvilLoader.java new file mode 100644 index 00000000000..adccffd15ad --- /dev/null +++ b/src/main/java/net/minestom/server/instance/anvil/AnvilLoader.java @@ -0,0 +1,565 @@ +package net.minestom.server.instance.anvil; + +import it.unimi.dsi.fastutil.ints.*; +import net.kyori.adventure.nbt.*; +import net.minestom.server.MinecraftServer; +import net.minestom.server.instance.Chunk; +import net.minestom.server.instance.IChunkLoader; +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.MathUtils; +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.biome.Biome; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantLock; + +public class AnvilLoader implements IChunkLoader { + private final static Logger LOGGER = LoggerFactory.getLogger(AnvilLoader.class); + private static final DynamicRegistry BIOME_REGISTRY = MinecraftServer.getBiomeRegistry(); + private final static int PLAINS_ID = BIOME_REGISTRY.getId(NamespaceID.from("minecraft:plains")); + + private final ReentrantLock fileCreationLock = new ReentrantLock(); + private final Map alreadyLoaded = new ConcurrentHashMap<>(); + private final Path path; + private final Path levelPath; + private final Path regionPath; + + private static class RegionCache extends ConcurrentHashMap> { + } + + /** + * Represents the chunks currently loaded per region. Used to determine when a region file can be unloaded. + */ + private final RegionCache perRegionLoadedChunks = new RegionCache(); + private final ReentrantLock perRegionLoadedChunksLock = new ReentrantLock(); + + // thread local to avoid contention issues with locks + private final ThreadLocal> blockStateId2ObjectCacheTLS = ThreadLocal.withInitial(Int2ObjectArrayMap::new); + + public AnvilLoader(@NotNull Path path) { + this.path = path; + this.levelPath = path.resolve("level.dat"); + this.regionPath = path.resolve("region"); + } + + public AnvilLoader(@NotNull String path) { + this(Path.of(path)); + } + + @Override + public void loadInstance(@NotNull Instance instance) { + if (!Files.exists(levelPath)) { + return; + } + try (InputStream is = Files.newInputStream(levelPath)) { + final CompoundBinaryTag tag = BinaryTagIO.reader().readNamed(is, BinaryTagIO.Compression.GZIP).getValue(); + Files.copy(levelPath, path.resolve("level.dat_old"), StandardCopyOption.REPLACE_EXISTING); + instance.tagHandler().updateContent(tag); + } catch (IOException e) { + MinecraftServer.getExceptionManager().handleException(e); + } + } + + @Override + public @NotNull CompletableFuture<@Nullable Chunk> loadChunk(@NotNull Instance instance, int chunkX, int chunkZ) { + if (!Files.exists(path)) { + // No world folder + return CompletableFuture.completedFuture(null); + } + try { + return loadMCA(instance, chunkX, chunkZ); + } catch (Exception e) { + MinecraftServer.getExceptionManager().handleException(e); + return CompletableFuture.completedFuture(null); + } + } + + private @NotNull CompletableFuture<@Nullable Chunk> loadMCA(Instance instance, int chunkX, int chunkZ) throws IOException { + final RegionFile mcaFile = getMCAFile(chunkX, chunkZ); + if (mcaFile == null) + return CompletableFuture.completedFuture(null); + final CompoundBinaryTag chunkData = mcaFile.readChunkData(chunkX, chunkZ); + if (chunkData == null) + return CompletableFuture.completedFuture(null); + + // Load the chunk data (assuming it is fully generated) + final Chunk chunk = instance.getChunkSupplier().createChunk(instance, chunkX, chunkZ); + synchronized (chunk) { // todo: boo, synchronized + final String status = chunkData.getString("status"); + + // TODO: Should we handle other statuses? + if (status.isEmpty() || "minecraft:full".equals(status)) { + // TODO: Parallelize block, block entities and biome loading + // Blocks + Biomes + loadSections(chunk, chunkData); + + // Block entities + loadBlockEntities(chunk, chunkData); + + chunk.loadHeightmapsFromNBT(chunkData.getCompound("Heightmaps")); + } else { + LOGGER.warn("Skipping partially generated chunk at {}, {} with status {}", chunkX, chunkZ, status); + } + } + + // Cache the index of the loaded chunk + perRegionLoadedChunksLock.lock(); + try { + int regionX = ChunkUtils.toRegionCoordinate(chunkX); + int regionZ = ChunkUtils.toRegionCoordinate(chunkZ); + var chunks = perRegionLoadedChunks.computeIfAbsent(new IntIntImmutablePair(regionX, regionZ), r -> new HashSet<>()); // region cache may have been removed on another thread due to unloadChunk + chunks.add(new IntIntImmutablePair(chunkX, chunkZ)); + } finally { + perRegionLoadedChunksLock.unlock(); + } + return CompletableFuture.completedFuture(chunk); + } + + private @Nullable RegionFile getMCAFile(int chunkX, int chunkZ) { + final int regionX = ChunkUtils.toRegionCoordinate(chunkX); + final int regionZ = ChunkUtils.toRegionCoordinate(chunkZ); + return alreadyLoaded.computeIfAbsent(RegionFile.getFileName(regionX, regionZ), n -> { + final Path regionPath = this.regionPath.resolve(n); + if (!Files.exists(regionPath)) { + return null; + } + perRegionLoadedChunksLock.lock(); + try { + Set previousVersion = perRegionLoadedChunks.put(new IntIntImmutablePair(regionX, regionZ), new HashSet<>()); + assert previousVersion == null : "The AnvilLoader cache should not already have data for this region."; + return new RegionFile(regionPath); + } catch (IOException e) { + MinecraftServer.getExceptionManager().handleException(e); + return null; + } finally { + perRegionLoadedChunksLock.unlock(); + } + }); + } + + private void loadSections(@NotNull Chunk chunk, @NotNull CompoundBinaryTag chunkData) { + for (BinaryTag sectionTag : chunkData.getList("sections", BinaryTagTypes.COMPOUND)) { + final CompoundBinaryTag sectionData = (CompoundBinaryTag) sectionTag; + + final int sectionY = sectionData.getInt("Y", Integer.MIN_VALUE); + Check.stateCondition(sectionY == Integer.MIN_VALUE, "Missing section Y value"); + final int yOffset = Chunk.CHUNK_SECTION_SIZE * sectionY; + + if (sectionY < chunk.getMinSection() || sectionY >= chunk.getMaxSection()) { + // Vanilla stores a section below and above the world for lighting, throw it out. + continue; + } + + final Section section = chunk.getSection(sectionY); + + // Lighting + if (sectionData.get("SkyLight") instanceof ByteArrayBinaryTag skyLightTag && skyLightTag.size() == 2048) { + section.setSkyLight(skyLightTag.value()); + } + if (sectionData.get("BlockLight") instanceof ByteArrayBinaryTag blockLightTag && blockLightTag.size() == 2048) { + section.setBlockLight(blockLightTag.value()); + } + + { // Biomes + final CompoundBinaryTag biomesTag = sectionData.getCompound("biomes"); + final ListBinaryTag biomePaletteTag = biomesTag.getList("palette", BinaryTagTypes.STRING); + int[] convertedBiomePalette = loadBiomePalette(biomePaletteTag); + + if (convertedBiomePalette.length == 1) { + // One solid block, no need to check the data + 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]; + + int bitsPerEntry = packedIndices.length * 64 / biomeIndices.length; + if (bitsPerEntry > 3) bitsPerEntry = MathUtils.bitsToRepresent(convertedBiomePalette.length); + ArrayUtils.unpack(biomeIndices, packedIndices, bitsPerEntry); + + section.biomePalette().setAll((x, y, z) -> { + final int index = x + z * 4 + y * 16; + return convertedBiomePalette[biomeIndices[index]]; + }); + } + } + + { // Blocks + final CompoundBinaryTag blockStatesTag = sectionData.getCompound("block_states"); + final ListBinaryTag blockPaletteTag = blockStatesTag.getList("palette", BinaryTagTypes.COMPOUND); + Block[] convertedPalette = loadBlockPalette(blockPaletteTag); + if (blockPaletteTag.size() == 1) { + // One solid block, no need to check the data + section.blockPalette().fill(convertedPalette[0].stateId()); + } else if (blockPaletteTag.size() > 1) { + final long[] packedStates = blockStatesTag.getLongArray("data"); + Check.stateCondition(packedStates.length == 0, "Missing packed states data"); + int[] blockStateIndices = new int[Chunk.CHUNK_SECTION_SIZE * Chunk.CHUNK_SECTION_SIZE * Chunk.CHUNK_SECTION_SIZE]; + ArrayUtils.unpack(blockStateIndices, packedStates, packedStates.length * 64 / blockStateIndices.length); + + for (int y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) { + for (int z = 0; z < Chunk.CHUNK_SECTION_SIZE; z++) { + for (int x = 0; x < Chunk.CHUNK_SECTION_SIZE; x++) { + try { + final int blockIndex = y * Chunk.CHUNK_SECTION_SIZE * Chunk.CHUNK_SECTION_SIZE + z * Chunk.CHUNK_SECTION_SIZE + x; + final int paletteIndex = blockStateIndices[blockIndex]; + final Block block = convertedPalette[paletteIndex]; + + chunk.setBlock(x, y + yOffset, z, block); + } catch (Exception e) { + MinecraftServer.getExceptionManager().handleException(e); + } + } + } + } + } + } + } + } + + private Block[] loadBlockPalette(@NotNull ListBinaryTag paletteTag) { + Block[] convertedPalette = new Block[paletteTag.size()]; + for (int i = 0; i < convertedPalette.length; i++) { + CompoundBinaryTag paletteEntry = paletteTag.getCompound(i); + String blockName = paletteEntry.getString("Name"); + if (blockName.equals("minecraft:air")) { + convertedPalette[i] = Block.AIR; + } else { + + if (blockName.equals("minecraft:grass")) { + blockName = "minecraft:grass_block"; + } + Block block = Objects.requireNonNull(Block.fromNamespaceId(blockName), "Unknown block " + blockName); + // Properties + final Map properties = new HashMap<>(); + CompoundBinaryTag propertiesNBT = paletteEntry.getCompound("Properties"); + for (var property : propertiesNBT) { + if (property.getValue() instanceof StringBinaryTag propertyValue) { + properties.put(property.getKey(), propertyValue.value()); + } else { + LOGGER.warn("Fail to parse block state properties {}, expected a string for {}, but contents were {}", + propertiesNBT, property.getKey(), TagStringIOExt.writeTag(property.getValue())); + } + } + if (!properties.isEmpty()) block = block.withProperties(properties); + + // Handler + final BlockHandler handler = MinecraftServer.getBlockManager().getHandler(block.name()); + if (handler != null) block = block.withHandler(handler); + + convertedPalette[i] = block; + } + } + return convertedPalette; + } + + 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); + int biomeId = BIOME_REGISTRY.getId(NamespaceID.from(name)); + if (biomeId == -1) biomeId = PLAINS_ID; + convertedPalette[i] = biomeId; + } + return convertedPalette; + } + + private void loadBlockEntities(@NotNull Chunk loadedChunk, @NotNull CompoundBinaryTag chunkData) { + for (BinaryTag blockEntityTag : chunkData.getList("block_entities", BinaryTagTypes.COMPOUND)) { + final CompoundBinaryTag blockEntity = (CompoundBinaryTag) blockEntityTag; + + final int x = blockEntity.getInt("x"); + final int y = blockEntity.getInt("y"); + final int z = blockEntity.getInt("z"); + Block block = loadedChunk.getBlock(x, y, z); + + // Load the block handler if the id is present + if (blockEntity.get("id") instanceof StringBinaryTag blockEntityId) { + final BlockHandler handler = MinecraftServer.getBlockManager().getHandlerOrDummy(blockEntityId.value()); + block = block.withHandler(handler); + } + + // Remove anvil tags + CompoundBinaryTag trimmedTag = CompoundBinaryTag.builder().put(blockEntity) + .remove("id").remove("keepPacked") + .remove("x").remove("y").remove("z") + .build(); + + // Place block + final var finalBlock = trimmedTag.size() > 0 ? block.withNbt(trimmedTag) : block; + loadedChunk.setBlock(x, y, z, finalBlock); + } + } + + @Override + public @NotNull CompletableFuture saveInstance(@NotNull Instance instance) { + final CompoundBinaryTag nbt = instance.tagHandler().asCompound(); + if (nbt.size() == 0) { + // Instance has no data + return AsyncUtils.VOID_FUTURE; + } + try (OutputStream os = Files.newOutputStream(levelPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { + BinaryTagIO.writer().writeNamed(Map.entry("", nbt), os, BinaryTagIO.Compression.GZIP); + } catch (IOException e) { + MinecraftServer.getExceptionManager().handleException(e); + } + return AsyncUtils.VOID_FUTURE; + } + + @Override + public @NotNull CompletableFuture saveChunk(@NotNull Chunk chunk) { + final int chunkX = chunk.getChunkX(); + final int chunkZ = chunk.getChunkZ(); + + // Find the region file or create an empty one if missing + RegionFile mcaFile; + fileCreationLock.lock(); + try { + mcaFile = getMCAFile(chunkX, chunkZ); + if (mcaFile == null) { + final int regionX = ChunkUtils.toRegionCoordinate(chunkX); + final int regionZ = ChunkUtils.toRegionCoordinate(chunkZ); + final String regionFileName = RegionFile.getFileName(regionX, regionZ); + try { + Path regionFile = regionPath.resolve(regionFileName); + if (!Files.exists(regionFile)) { + Files.createDirectories(regionFile.getParent()); + Files.createFile(regionFile); + } + + mcaFile = new RegionFile(regionFile); + alreadyLoaded.put(regionFileName, mcaFile); + } catch (IOException e) { + LOGGER.error("Failed to create region file for " + chunkX + ", " + chunkZ, e); + MinecraftServer.getExceptionManager().handleException(e); + return AsyncUtils.VOID_FUTURE; + } + } + } finally { + fileCreationLock.unlock(); + } + + try { + final CompoundBinaryTag.Builder chunkData = CompoundBinaryTag.builder(); + + chunkData.putInt("DataVersion", MinecraftServer.DATA_VERSION); + chunkData.putInt("xPos", chunkX); + chunkData.putInt("zPos", chunkZ); + chunkData.putInt("yPos", chunk.getMinSection()); + chunkData.putString("status", "minecraft:full"); + chunkData.putLong("LastUpdate", chunk.getInstance().getWorldAge()); + + saveSectionData(chunk, chunkData); + + mcaFile.writeChunkData(chunkX, chunkZ, chunkData.build()); + } catch (IOException e) { + LOGGER.error("Failed to save chunk " + chunkX + ", " + chunkZ, e); + MinecraftServer.getExceptionManager().handleException(e); + } + return AsyncUtils.VOID_FUTURE; + } + + private void saveSectionData(@NotNull Chunk chunk, @NotNull CompoundBinaryTag.Builder chunkData) { + final ListBinaryTag.Builder sections = ListBinaryTag.builder(BinaryTagTypes.COMPOUND); + final ListBinaryTag.Builder blockEntities = ListBinaryTag.builder(BinaryTagTypes.COMPOUND); + + // Block & Biome arrays reused for each chunk + List biomePalette = new ArrayList<>(); + int[] biomeIndices = new int[64]; + + List blockPaletteEntries = new ArrayList<>(); + IntList blockPaletteIndices = new IntArrayList(); // Map block indices by state id to avoid doing a deep comparison on every block tag + int[] blockIndices = new int[Chunk.CHUNK_SECTION_SIZE * Chunk.CHUNK_SECTION_SIZE * Chunk.CHUNK_SECTION_SIZE]; + + synchronized (chunk) { + for (int sectionY = chunk.getMinSection(); sectionY < chunk.getMaxSection(); sectionY++) { + final Section section = chunk.getSection(sectionY); + + final CompoundBinaryTag.Builder sectionData = CompoundBinaryTag.builder(); + sectionData.putByte("Y", (byte) sectionY); + + // Lighting + byte[] skyLight = section.skyLight().array(); + if (skyLight != null && skyLight.length > 0) + sectionData.putByteArray("SkyLight", skyLight); + byte[] blockLight = section.blockLight().array(); + if (blockLight != null && blockLight.length > 0) + sectionData.putByteArray("BlockLight", blockLight); + + // Build block, biome palettes & collect block entities + for (int sectionLocalY = 0; sectionLocalY < Chunk.CHUNK_SECTION_SIZE; sectionLocalY++) { + for (int z = 0; z < Chunk.CHUNK_SIZE_Z; z++) { + for (int x = 0; x < Chunk.CHUNK_SIZE_X; x++) { + final int y = sectionLocalY + (sectionY * Chunk.CHUNK_SECTION_SIZE); + + final int blockIndex = x + sectionLocalY * 16 * 16 + z * 16; + final Block block = chunk.getBlock(x, y, z); + + // Add block state + final int blockStateId = block.stateId(); + final CompoundBinaryTag blockState = getBlockState(block); + int blockPaletteIndex = blockPaletteIndices.indexOf(blockStateId); + if (blockPaletteIndex == -1) { + blockPaletteIndex = blockPaletteEntries.size(); + blockPaletteEntries.add(blockState); + blockPaletteIndices.add(blockStateId); + } + blockIndices[blockIndex] = blockPaletteIndex; + + // Add biome (biome are stored for 4x4x4 volumes, avoid unnecessary work) + if (x % 4 == 0 && sectionLocalY % 4 == 0 && z % 4 == 0) { + int biomeIndex = (x / 4) + (sectionLocalY / 4) * 4 * 4 + (z / 4) * 4; + final DynamicRegistry.Key biomeKey = chunk.getBiome(x, y, z); + final BinaryTag biomeName = StringBinaryTag.stringBinaryTag(biomeKey.name()); + + int biomePaletteIndex = biomePalette.indexOf(biomeName); + if (biomePaletteIndex == -1) { + biomePaletteIndex = biomePalette.size(); + biomePalette.add(biomeName); + } + + biomeIndices[biomeIndex] = biomePaletteIndex; + } + + // Add block entity if present + final BlockHandler handler = block.handler(); + final CompoundBinaryTag originalNBT = block.nbt(); + if (originalNBT != null || handler != null) { + CompoundBinaryTag.Builder blockEntityTag = CompoundBinaryTag.builder(); + if (originalNBT != null) { + blockEntityTag.put(originalNBT); + } + if (handler != null) { + blockEntityTag.putString("id", handler.getNamespaceId().asString()); + } + blockEntityTag.putInt("x", x + Chunk.CHUNK_SIZE_X * chunk.getChunkX()); + blockEntityTag.putInt("y", y); + blockEntityTag.putInt("z", z + Chunk.CHUNK_SIZE_Z * chunk.getChunkZ()); + blockEntityTag.putByte("keepPacked", (byte) 0); + blockEntities.add(blockEntityTag.build()); + } + } + } + } + + // Save the block and biome palettes + final CompoundBinaryTag.Builder blockStates = CompoundBinaryTag.builder(); + blockStates.put("palette", ListBinaryTag.listBinaryTag(BinaryTagTypes.COMPOUND, blockPaletteEntries)); + if (blockPaletteEntries.size() > 1) { + // If there is only one entry we do not need to write the packed indices + var bitsPerEntry = (int) Math.max(1, Math.ceil(Math.log(blockPaletteEntries.size()) / Math.log(2))); + blockStates.putLongArray("data", ArrayUtils.pack(blockIndices, bitsPerEntry)); + } + sectionData.put("block_states", blockStates.build()); + + final CompoundBinaryTag.Builder biomes = CompoundBinaryTag.builder(); + biomes.put("palette", ListBinaryTag.listBinaryTag(BinaryTagTypes.STRING, biomePalette)); + if (biomePalette.size() > 1) { + // If there is only one entry we do not need to write the packed indices + var bitsPerEntry = (int) Math.max(1, Math.ceil(Math.log(biomePalette.size()) / Math.log(2))); + biomes.putLongArray("data", ArrayUtils.pack(biomeIndices, bitsPerEntry)); + } + sectionData.put("biomes", biomes.build()); + + biomePalette.clear(); + blockPaletteEntries.clear(); + blockPaletteIndices.clear(); + + sections.add(sectionData.build()); + } + } + + chunkData.put("sections", sections.build()); + chunkData.put("block_entities", blockEntities.build()); + } + + private CompoundBinaryTag getBlockState(final Block block) { + return blockStateId2ObjectCacheTLS.get().computeIfAbsent(block.stateId(), _unused -> { + final CompoundBinaryTag.Builder tag = CompoundBinaryTag.builder(); + tag.putString("Name", block.name()); + + if (!block.properties().isEmpty()) { + final Map defaultProperties = Block.fromBlockId(block.id()).properties(); // Never null + final CompoundBinaryTag.Builder propertiesTag = CompoundBinaryTag.builder(); + for (var entry : block.properties().entrySet()) { + String key = entry.getKey(), value = entry.getValue(); + if (defaultProperties.get(key).equals(value)) + continue; // Skip default values + + propertiesTag.putString(key, value); + } + var properties = propertiesTag.build(); + if (properties.size() > 0) { + tag.put("Properties", properties); + } + } + return tag.build(); + }); + } + + /** + * Unload a given chunk. Also unloads a region when no chunk from that region is loaded. + * + * @param chunk the chunk to unload + */ + @Override + public void unloadChunk(Chunk chunk) { + final int regionX = ChunkUtils.toRegionCoordinate(chunk.getChunkX()); + final int regionZ = ChunkUtils.toRegionCoordinate(chunk.getChunkZ()); + final IntIntImmutablePair regionKey = new IntIntImmutablePair(regionX, regionZ); + + perRegionLoadedChunksLock.lock(); + try { + Set chunks = perRegionLoadedChunks.get(regionKey); + if (chunks != null) { // if null, trying to unload a chunk from a region that was not created by the AnvilLoader + // don't check return value, trying to unload a chunk not created by the AnvilLoader is valid + chunks.remove(new IntIntImmutablePair(chunk.getChunkX(), chunk.getChunkZ())); + + if (chunks.isEmpty()) { + perRegionLoadedChunks.remove(regionKey); + RegionFile regionFile = alreadyLoaded.remove(RegionFile.getFileName(regionX, regionZ)); + if (regionFile != null) { + try { + regionFile.close(); + } catch (IOException e) { + MinecraftServer.getExceptionManager().handleException(e); + } + } + } + } + } finally { + perRegionLoadedChunksLock.unlock(); + } + } + + @Override + public boolean supportsParallelLoading() { + return true; + } + + @Override + public boolean supportsParallelSaving() { + return true; + } +} diff --git a/src/main/java/net/minestom/server/instance/anvil/RegionFile.java b/src/main/java/net/minestom/server/instance/anvil/RegionFile.java new file mode 100644 index 00000000000..3014ab9a1f8 --- /dev/null +++ b/src/main/java/net/minestom/server/instance/anvil/RegionFile.java @@ -0,0 +1,217 @@ +package net.minestom.server.instance.anvil; + +import it.unimi.dsi.fastutil.booleans.BooleanArrayList; +import it.unimi.dsi.fastutil.booleans.BooleanList; +import net.kyori.adventure.nbt.BinaryTagIO; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minestom.server.utils.chunk.ChunkUtils; +import net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.file.Path; +import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Implements a thread-safe reader and writer for Minecraft region files. + * + * @see Region file format + * @see Hephaistos implementation + */ +final class RegionFile implements AutoCloseable { + + private static final int MAX_ENTRY_COUNT = 1024; + private static final int SECTOR_SIZE = 4096; + private static final int SECTOR_1MB = 1024 * 1024 / SECTOR_SIZE; + private static final int HEADER_LENGTH = MAX_ENTRY_COUNT * 2 * 4; // 2 4-byte fields per entry + private static final int CHUNK_HEADER_LENGTH = 4 + 1; // Length + Compression type (todo non constant to support custom compression) + + private static final int COMPRESSION_ZLIB = 2; + + private static final BinaryTagIO.Reader TAG_READER = BinaryTagIO.unlimitedReader(); + private static final BinaryTagIO.Writer TAG_WRITER = BinaryTagIO.writer(); + + public static @NotNull String getFileName(int regionX, int regionZ) { + return "r." + regionX + "." + regionZ + ".mca"; + } + + private final ReentrantLock lock = new ReentrantLock(); + private final RandomAccessFile file; + + private final int[] locations = new int[MAX_ENTRY_COUNT]; + private final int[] timestamps = new int[MAX_ENTRY_COUNT]; + private final BooleanList freeSectors = new BooleanArrayList(2); + + public RegionFile(@NotNull Path path) throws IOException { + this.file = new RandomAccessFile(path.toFile(), "rw"); + + readHeader(); + } + + public boolean hasChunkData(int chunkX, int chunkZ) { + lock.lock(); + try { + return locations[getChunkIndex(chunkX, chunkZ)] != 0; + } finally { + lock.unlock(); + } + } + + public @Nullable CompoundBinaryTag readChunkData(int chunkX, int chunkZ) throws IOException { + lock.lock(); + try { + if (!hasChunkData(chunkX, chunkZ)) return null; + + int location = locations[getChunkIndex(chunkX, chunkZ)]; + file.seek((long) (location >> 8) * SECTOR_SIZE); // Move to start of first sector + int length = file.readInt(); + int compressionType = file.readByte(); + BinaryTagIO.Compression compression = switch (compressionType) { + case 1 -> BinaryTagIO.Compression.GZIP; + case COMPRESSION_ZLIB -> BinaryTagIO.Compression.ZLIB; + case 3 -> BinaryTagIO.Compression.NONE; + default -> throw new IOException("Unsupported compression type: " + compressionType); + }; + + // Read the raw content + byte[] data = new byte[length - 1]; + file.read(data); + + // Parse it as a compound tag + return TAG_READER.read(new ByteArrayInputStream(data), compression); + } finally { + lock.unlock(); + } + } + + public void writeChunkData(int chunkX, int chunkZ, @NotNull CompoundBinaryTag data) throws IOException { + // Write the data (compressed) + ByteArrayOutputStream out = new ByteArrayOutputStream(); + TAG_WRITER.writeNamed(Map.entry("", data), out, BinaryTagIO.Compression.ZLIB); + byte[] dataBytes = out.toByteArray(); + int chunkLength = CHUNK_HEADER_LENGTH + dataBytes.length; + + int sectorCount = (int) Math.ceil(chunkLength / (double) SECTOR_SIZE); + Check.stateCondition(sectorCount >= SECTOR_1MB, "Chunk data is too large to fit in a region file"); + + lock.lock(); + try { + // We don't attempt to reuse the current allocation, just write it to a new position and free the old one. + int chunkIndex = getChunkIndex(chunkX, chunkZ); + int oldLocation = locations[chunkIndex]; + + // Find a new location + int firstSector = findFreeSectors(sectorCount); + if (firstSector == -1) { + firstSector = allocSectors(sectorCount); + } + int newLocation = (firstSector << 8) | sectorCount; + + // Mark the sectors as used & free the old sectors + markLocation(oldLocation, true); + markLocation(newLocation, false); + + // Write the chunk data + file.seek((long) firstSector * SECTOR_SIZE); + file.writeInt(chunkLength); + file.writeByte(COMPRESSION_ZLIB); + file.write(dataBytes); + + // Update the header and write it + locations[chunkIndex] = newLocation; + timestamps[chunkIndex] = (int) (System.currentTimeMillis() / 1000); + writeHeader(); + } finally { + lock.unlock(); + } + } + + @Override + public void close() throws IOException { + file.close(); + } + + private int getChunkIndex(int chunkX, int chunkZ) { + return (ChunkUtils.toRegionLocal(chunkZ) << 5) | ChunkUtils.toRegionLocal(chunkX); + } + + private void readHeader() throws IOException { + file.seek(0); + if (file.length() < HEADER_LENGTH) { + // new file, fill in data + file.write(new byte[HEADER_LENGTH]); + } + + //todo: addPadding() + + final long totalSectors = ((file.length() - 1) / SECTOR_SIZE) + 1; // Round up, last sector does not need to be full size + for (int i = 0; i < totalSectors; i++) freeSectors.add(true); + freeSectors.set(0, false); // First sector is locations + freeSectors.set(1, false); // Second sector is timestamps + + // Read locations + file.seek(0); + for (int i = 0; i < MAX_ENTRY_COUNT; i++) { + int location = locations[i] = file.readInt(); + if (location != 0) { + markLocation(location, false); + } + } + + // Read timestamps + for (int i = 0; i < MAX_ENTRY_COUNT; i++) { + timestamps[i] = file.readInt(); + } + } + + private void writeHeader() throws IOException { + file.seek(0); + for (int location : locations) { + file.writeInt(location); + } + for (int timestamp : timestamps) { + file.writeInt(timestamp); + } + } + + private int findFreeSectors(int length) { + for (int start = 0; start < freeSectors.size() - length; start++) { + boolean found = true; + for (int i = 0; i < length; i++) { + if (!freeSectors.getBoolean(start++)) { + found = false; + break; + } + } + if (found) return start - length; + } + return -1; + } + + private int allocSectors(int count) throws IOException { + var eof = file.length(); + file.seek(eof); + + byte[] emptySector = new byte[SECTOR_SIZE]; + for (int i = 0; i < count; i++) { + freeSectors.add(true); + file.write(emptySector); + } + + return (int) (eof / SECTOR_SIZE); + } + + private void markLocation(int location, boolean free) { + int sectorCount = location & 0xFF; + int sectorStart = location >> 8; + Check.stateCondition(sectorStart + sectorCount > freeSectors.size(), "Invalid sector count"); + for (int i = sectorStart; i < sectorStart + sectorCount; i++) { + freeSectors.set(i, free); + } + } +} diff --git a/src/main/java/net/minestom/server/instance/batch/AbsoluteBlockBatch.java b/src/main/java/net/minestom/server/instance/batch/AbsoluteBlockBatch.java index d47259dde39..3177ebb6b85 100644 --- a/src/main/java/net/minestom/server/instance/batch/AbsoluteBlockBatch.java +++ b/src/main/java/net/minestom/server/instance/batch/AbsoluteBlockBatch.java @@ -9,7 +9,6 @@ import net.minestom.server.instance.LightingChunk; import net.minestom.server.instance.block.Block; import net.minestom.server.utils.chunk.ChunkUtils; -import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -180,12 +179,10 @@ protected AbsoluteBlockBatch apply(@NotNull Instance instance, @Nullable Runnabl return inverse; } - @ApiStatus.Experimental public @NotNull BatchOption getInverseOption() { return inverseOption; } - @ApiStatus.Experimental public void setInverseOption(@NotNull BatchOption inverseOption) { this.inverseOption = inverseOption; } diff --git a/src/main/java/net/minestom/server/instance/block/Block.java b/src/main/java/net/minestom/server/instance/block/Block.java index 2f4f90cfbe0..492dab1cc7a 100644 --- a/src/main/java/net/minestom/server/instance/block/Block.java +++ b/src/main/java/net/minestom/server/instance/block/Block.java @@ -1,15 +1,16 @@ package net.minestom.server.instance.block; +import net.kyori.adventure.nbt.CompoundBinaryTag; import net.minestom.server.coordinate.Point; import net.minestom.server.instance.Instance; import net.minestom.server.instance.batch.Batch; -import net.minestom.server.registry.StaticProtocolObject; +import net.minestom.server.network.NetworkBuffer; import net.minestom.server.registry.Registry; +import net.minestom.server.registry.StaticProtocolObject; import net.minestom.server.tag.Tag; import net.minestom.server.tag.TagReadable; import net.minestom.server.utils.NamespaceID; import org.jetbrains.annotations.*; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; import java.util.Collection; import java.util.Map; @@ -25,6 +26,9 @@ */ public sealed interface Block extends StaticProtocolObject, TagReadable, Blocks permits BlockImpl { + @NotNull + NetworkBuffer.Type NETWORK_TYPE = NetworkBuffer.VAR_INT.map(Block::fromStateId, Block::stateId); + /** * Creates a new block with the the property {@code property} sets to {@code value}. * @@ -67,7 +71,7 @@ public sealed interface Block extends StaticProtocolObject, TagReadable, Blocks * @return a new block with different nbt */ @Contract(pure = true) - @NotNull Block withNbt(@Nullable NBTCompound compound); + @NotNull Block withNbt(@Nullable CompoundBinaryTag compound); /** * Creates a new block with the specified {@link BlockHandler handler}. @@ -86,7 +90,7 @@ public sealed interface Block extends StaticProtocolObject, TagReadable, Blocks * @return the block nbt, null if not present */ @Contract(pure = true) - @Nullable NBTCompound nbt(); + @Nullable CompoundBinaryTag nbt(); @Contract(pure = true) default boolean hasNbt() { @@ -110,6 +114,15 @@ default boolean hasNbt() { @Contract(pure = true) @NotNull Map properties(); + /** + * Returns this block type with default properties, no tags and no handler. + * As found in the {@link Blocks} listing. + * + * @return the default block + */ + @Contract(pure = true) + @NotNull Block defaultState(); + /** * Returns a property value from {@link #properties()}. * @@ -122,7 +135,6 @@ default String getProperty(@NotNull String property) { } @Contract(pure = true) - @ApiStatus.Experimental @NotNull Collection<@NotNull Block> possibleStates(); /** @@ -145,8 +157,8 @@ default int id() { return registry().id(); } - default short stateId() { - return (short) registry().stateId(); + default int stateId() { + return registry().stateId(); } default boolean isAir() { @@ -181,7 +193,7 @@ default boolean compare(@NotNull Block block) { return fromNamespaceId(namespaceID.asString()); } - static @Nullable Block fromStateId(short stateId) { + static @Nullable Block fromStateId(int stateId) { return BlockImpl.getState(stateId); } @@ -230,7 +242,6 @@ interface Getter { * Represents a hint to retrieve blocks more efficiently. * Implementing interfaces do not have to honor this. */ - @ApiStatus.Experimental enum Condition { /** * Returns a block no matter what. diff --git a/src/main/java/net/minestom/server/instance/block/BlockImpl.java b/src/main/java/net/minestom/server/instance/block/BlockImpl.java index 2aa868f8b50..7395633fdad 100644 --- a/src/main/java/net/minestom/server/instance/block/BlockImpl.java +++ b/src/main/java/net/minestom/server/instance/block/BlockImpl.java @@ -4,6 +4,7 @@ import com.github.benmanes.caffeine.cache.Caffeine; import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; +import net.kyori.adventure.nbt.CompoundBinaryTag; import net.minestom.server.registry.Registry; import net.minestom.server.tag.Tag; import net.minestom.server.utils.ArrayUtils; @@ -14,8 +15,6 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.UnknownNullability; import org.jetbrains.annotations.Unmodifiable; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; -import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound; import java.time.Duration; import java.util.*; @@ -23,7 +22,7 @@ record BlockImpl(@NotNull Registry.BlockEntry registry, byte @NotNull [] propertiesArray, - @Nullable NBTCompound nbt, + @Nullable CompoundBinaryTag nbt, @Nullable BlockHandler handler) implements Block { // Block state -> block object private static final ObjectArray BLOCK_STATE_MAP = ObjectArray.singleThread(); @@ -31,8 +30,62 @@ record BlockImpl(@NotNull Registry.BlockEntry registry, private static final ObjectArray PROPERTIES_TYPE = ObjectArray.singleThread(); // Block id -> Map private static final ObjectArray> POSSIBLE_STATES = ObjectArray.singleThread(); - private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.BLOCKS, BlockImpl::createImpl); - private static final Cache NBT_CACHE = Caffeine.newBuilder() + private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.BLOCKS, + (namespace, properties) -> { + final int blockId = properties.getInt("id"); + final Registry.Properties stateObject = properties.section("states"); + + // Retrieve properties + PropertyType[] propertyTypes; + { + Registry.Properties stateProperties = properties.section("properties"); + if (stateProperties != null) { + final int stateCount = stateProperties.size(); + propertyTypes = new PropertyType[stateCount]; + int i = 0; + for (var entry : stateProperties) { + final var k = entry.getKey(); + final var v = (List) entry.getValue(); + propertyTypes[i++] = new PropertyType(k, v); + } + } else { + propertyTypes = new PropertyType[0]; + } + } + PROPERTIES_TYPE.set(blockId, propertyTypes); + + // Retrieve block states + { + final int propertiesCount = stateObject.size(); + PropertiesHolder[] propertiesKeys = new PropertiesHolder[propertiesCount]; + BlockImpl[] blocksValues = new BlockImpl[propertiesCount]; + int propertiesOffset = 0; + for (var stateEntry : stateObject) { + final String query = stateEntry.getKey(); + final var stateOverride = (Map) stateEntry.getValue(); + final var propertyMap = BlockUtils.parseProperties(query); + assert propertyTypes.length == propertyMap.size(); + byte[] propertiesArray = new byte[propertyTypes.length]; + for (var entry : propertyMap.entrySet()) { + final byte keyIndex = findKeyIndex(propertyTypes, entry.getKey(), null); + final byte valueIndex = findValueIndex(propertyTypes[keyIndex], entry.getValue(), null); + propertiesArray[keyIndex] = valueIndex; + } + + var mainProperties = Registry.Properties.fromMap(new MergedMap<>(stateOverride, properties.asMap())); + final BlockImpl block = new BlockImpl(Registry.block(namespace, mainProperties), + propertiesArray, null, null); + BLOCK_STATE_MAP.set(block.stateId(), block); + propertiesKeys[propertiesOffset] = new PropertiesHolder(propertiesArray); + blocksValues[propertiesOffset++] = block; + } + POSSIBLE_STATES.set(blockId, ArrayUtils.toMap(propertiesKeys, blocksValues, propertiesOffset)); + } + // Register default state + final int defaultState = properties.getInt("defaultStateId"); + return getState(defaultState); + }); + private static final Cache NBT_CACHE = Caffeine.newBuilder() .expireAfterWrite(Duration.ofMinutes(5)) .weakValues() .build(); @@ -90,14 +143,16 @@ static Collection values() { @Override public @NotNull Block withTag(@NotNull Tag tag, @Nullable T value) { - var temporaryNbt = new MutableNBTCompound(Objects.requireNonNullElse(nbt, NBTCompound.EMPTY)); - tag.write(temporaryNbt, value); - final var finalNbt = temporaryNbt.getSize() > 0 ? NBT_CACHE.get(temporaryNbt.toCompound(), Function.identity()) : null; + var builder = CompoundBinaryTag.builder(); + if (nbt != null) builder.put(nbt); + tag.write(builder, value); + var temporaryNbt = builder.build(); + final var finalNbt = temporaryNbt.size() > 0 ? NBT_CACHE.get(temporaryNbt, Function.identity()) : null; return new BlockImpl(registry, propertiesArray, finalNbt, handler); } @Override - public @NotNull Block withNbt(@Nullable NBTCompound compound) { + public @NotNull Block withNbt(@Nullable CompoundBinaryTag compound) { return new BlockImpl(registry, propertiesArray, compound, handler); } @@ -122,6 +177,11 @@ static Collection values() { return Object2ObjectMaps.unmodifiable(new Object2ObjectArrayMap<>(keys, values, length)); } + @Override + public @NotNull Block defaultState() { + return Block.fromBlockId(id()); + } + @Override public @NotNull Collection<@NotNull Block> possibleStates() { return Collection.class.cast(possibleProperties().values()); @@ -129,7 +189,7 @@ static Collection values() { @Override public @UnknownNullability T getTag(@NotNull Tag tag) { - return tag.read(Objects.requireNonNullElse(nbt, NBTCompound.EMPTY)); + return tag.read(Objects.requireNonNullElse(nbt, CompoundBinaryTag.empty())); } private Map possibleProperties() { @@ -160,61 +220,6 @@ private Block compute(byte[] properties) { return nbt == null && handler == null ? block : new BlockImpl(block.registry(), block.propertiesArray, nbt, handler); } - private static Block createImpl(String namespace, Registry.Properties properties) { - final int blockId = properties.getInt("id"); - final Registry.Properties stateObject = properties.section("states"); - - // Retrieve properties - PropertyType[] propertyTypes; - { - Registry.Properties stateProperties = properties.section("properties"); - if (stateProperties != null) { - final int stateCount = stateProperties.size(); - propertyTypes = new PropertyType[stateCount]; - int i = 0; - for (var entry : stateProperties) { - final var k = entry.getKey(); - final var v = (List) entry.getValue(); - propertyTypes[i++] = new PropertyType(k, v); - } - } else { - propertyTypes = new PropertyType[0]; - } - } - PROPERTIES_TYPE.set(blockId, propertyTypes); - - // Retrieve block states - { - final int propertiesCount = stateObject.size(); - PropertiesHolder[] propertiesKeys = new PropertiesHolder[propertiesCount]; - BlockImpl[] blocksValues = new BlockImpl[propertiesCount]; - int propertiesOffset = 0; - for (var stateEntry : stateObject) { - final String query = stateEntry.getKey(); - final var stateOverride = (Map) stateEntry.getValue(); - final var propertyMap = BlockUtils.parseProperties(query); - assert propertyTypes.length == propertyMap.size(); - byte[] propertiesArray = new byte[propertyTypes.length]; - for (var entry : propertyMap.entrySet()) { - final byte keyIndex = findKeyIndex(propertyTypes, entry.getKey(), null); - final byte valueIndex = findValueIndex(propertyTypes[keyIndex], entry.getValue(), null); - propertiesArray[keyIndex] = valueIndex; - } - - var mainProperties = Registry.Properties.fromMap(new MergedMap<>(stateOverride, properties.asMap())); - final BlockImpl block = new BlockImpl(Registry.block(namespace, mainProperties), - propertiesArray, null, null); - BLOCK_STATE_MAP.set(block.stateId(), block); - propertiesKeys[propertiesOffset] = new PropertiesHolder(propertiesArray); - blocksValues[propertiesOffset++] = block; - } - POSSIBLE_STATES.set(blockId, ArrayUtils.toMap(propertiesKeys, blocksValues, propertiesOffset)); - } - // Register default state - final int defaultState = properties.getInt("defaultStateId"); - return getState(defaultState); - } - private static byte findKeyIndex(PropertyType[] properties, String key, BlockImpl block) { for (byte i = 0; i < properties.length; i++) { if (properties[i].key().equals(key)) return i; diff --git a/src/main/java/net/minestom/server/instance/block/banner/BannerPattern.java b/src/main/java/net/minestom/server/instance/block/banner/BannerPattern.java new file mode 100644 index 00000000000..5b7774ba147 --- /dev/null +++ b/src/main/java/net/minestom/server/instance/block/banner/BannerPattern.java @@ -0,0 +1,73 @@ +package net.minestom.server.instance.block.banner; + +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.Registries; +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 NetworkBuffer.Type> NETWORK_TYPE = NetworkBuffer.RegistryKey(Registries::bannerPattern); + @NotNull BinaryTagSerializer> NBT_TYPE = BinaryTagSerializer.registryKey(Registries::bannerPattern); + + static @NotNull BannerPattern create( + @NotNull NamespaceID assetId, + @NotNull String translationKey + ) { + return new BannerPatternImpl(assetId, translationKey, null); + } + + static @NotNull Builder builder() { + return new Builder(); + } + + /** + *

Creates a new registry for banner patterns, loading the vanilla banner patterns.

+ * + * @see net.minestom.server.MinecraftServer to get an existing instance of the registry + */ + @ApiStatus.Internal + static @NotNull DynamicRegistry createDefaultRegistry() { + return DynamicRegistry.create( + "minecraft:banner_pattern", BannerPatternImpl.REGISTRY_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 NamespaceID assetId; + private String translationKey; + + private Builder() { + } + + @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(assetId, translationKey, null); + } + } +} diff --git a/src/main/java/net/minestom/server/instance/block/banner/BannerPatternImpl.java b/src/main/java/net/minestom/server/instance/block/banner/BannerPatternImpl.java new file mode 100644 index 00000000000..4d5d5565ab6 --- /dev/null +++ b/src/main/java/net/minestom/server/instance/block/banner/BannerPatternImpl.java @@ -0,0 +1,37 @@ +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 assetId, + @NotNull String translationKey, + @Nullable Registry.BannerPatternEntry registry +) implements BannerPattern { + + static final BinaryTagSerializer REGISTRY_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.argCondition(assetId == null, "missing asset id"); + Check.argCondition(translationKey == null || translationKey.isEmpty(), "missing translation key"); + } + + BannerPatternImpl(@NotNull Registry.BannerPatternEntry registry) { + this(registry.assetId(), registry.translationKey(), registry); + } + +} diff --git a/src/main/java/net/minestom/server/instance/block/jukebox/JukeboxSong.java b/src/main/java/net/minestom/server/instance/block/jukebox/JukeboxSong.java new file mode 100644 index 00000000000..51dce5e39f7 --- /dev/null +++ b/src/main/java/net/minestom/server/instance/block/jukebox/JukeboxSong.java @@ -0,0 +1,91 @@ +package net.minestom.server.instance.block.jukebox; + +import net.kyori.adventure.text.Component; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.Registries; +import net.minestom.server.registry.Registry; +import net.minestom.server.sound.SoundEvent; +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 JukeboxSong extends ProtocolObject, JukeboxSongs permits JukeboxSongImpl { + + @NotNull NetworkBuffer.Type> NETWORK_TYPE = NetworkBuffer.RegistryKey(Registries::jukeboxSong); + @NotNull BinaryTagSerializer> NBT_TYPE = BinaryTagSerializer.registryKey(Registries::jukeboxSong); + + static @NotNull JukeboxSong create( + @NotNull SoundEvent soundEvent, + @NotNull Component description, + float lengthInSeconds, + int comparatorOutput + ) { + return new JukeboxSongImpl(soundEvent, description, lengthInSeconds, comparatorOutput, null); + } + + static @NotNull Builder builder() { + return new Builder(); + } + + /** + *

Creates a new registry for banner patterns, loading the vanilla banner patterns.

+ * + * @see net.minestom.server.MinecraftServer to get an existing instance of the registry + */ + @ApiStatus.Internal + static @NotNull DynamicRegistry createDefaultRegistry() { + return DynamicRegistry.create( + "minecraft:jukebox_song", JukeboxSongImpl.REGISTRY_NBT_TYPE, Registry.Resource.JUKEBOX_SONGS, + (namespace, props) -> new JukeboxSongImpl(Registry.jukeboxSong(namespace, props)) + ); + } + + @NotNull SoundEvent soundEvent(); + + @NotNull Component description(); + + float lengthInSeconds(); + + int comparatorOutput(); + + @Override + @Nullable Registry.JukeboxSongEntry registry(); + + final class Builder { + private SoundEvent soundEvent; + private Component description; + private float lengthInSeconds; + private int comparatorOutput = 0; + + private Builder() { + } + + public @NotNull Builder soundEvent(@NotNull SoundEvent soundEvent) { + this.soundEvent = soundEvent; + return this; + } + + public @NotNull Builder description(@NotNull Component description) { + this.description = description; + return this; + } + + public @NotNull Builder lengthInSeconds(float lengthInSeconds) { + this.lengthInSeconds = lengthInSeconds; + return this; + } + + public @NotNull Builder comparatorOutput(int comparatorOutput) { + this.comparatorOutput = comparatorOutput; + return this; + } + + public @NotNull JukeboxSong build() { + return new JukeboxSongImpl(soundEvent, description, lengthInSeconds, comparatorOutput, null); + } + } + +} diff --git a/src/main/java/net/minestom/server/instance/block/jukebox/JukeboxSongImpl.java b/src/main/java/net/minestom/server/instance/block/jukebox/JukeboxSongImpl.java new file mode 100644 index 00000000000..4020805d391 --- /dev/null +++ b/src/main/java/net/minestom/server/instance/block/jukebox/JukeboxSongImpl.java @@ -0,0 +1,42 @@ +package net.minestom.server.instance.block.jukebox; + +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.text.Component; +import net.minestom.server.registry.Registry; +import net.minestom.server.sound.SoundEvent; +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 JukeboxSongImpl( + @NotNull SoundEvent soundEvent, + @NotNull Component description, + float lengthInSeconds, + int comparatorOutput, + @Nullable Registry.JukeboxSongEntry registry +) implements JukeboxSong { + + static final BinaryTagSerializer REGISTRY_NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> { + throw new UnsupportedOperationException("JukeboxSong is read-only"); + }, + jukeboxSong -> CompoundBinaryTag.builder() + .putString("sound_event", jukeboxSong.soundEvent().name()) + .put("description", BinaryTagSerializer.NBT_COMPONENT.write(jukeboxSong.description())) + .putFloat("length_in_seconds", jukeboxSong.lengthInSeconds()) + .putInt("comparator_output", jukeboxSong.comparatorOutput()) + .build() + ); + + @SuppressWarnings("ConstantValue") // The builder can violate the nullability constraints + JukeboxSongImpl { + Check.argCondition(soundEvent == null, "missing sound event"); + Check.argCondition(description == null, "missing description"); + } + + JukeboxSongImpl(@NotNull Registry.JukeboxSongEntry registry) { + this(registry.soundEvent(), registry.description(), registry.lengthInSeconds(), registry.comparatorOutput(), registry); + } + +} diff --git a/src/main/java/net/minestom/server/instance/block/predicate/BlockPredicate.java b/src/main/java/net/minestom/server/instance/block/predicate/BlockPredicate.java new file mode 100644 index 00000000000..850e53cb3d1 --- /dev/null +++ b/src/main/java/net/minestom/server/instance/block/predicate/BlockPredicate.java @@ -0,0 +1,120 @@ +package net.minestom.server.instance.block.predicate; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minestom.server.instance.block.Block; +import net.minestom.server.instance.block.BlockHandler; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.block.BlockUtils; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.Objects; +import java.util.function.Predicate; + +/** + *

A predicate to filter blocks based on their name, properties, and/or nbt.

+ * + *

Note: Inline with vanilla, providing none of the filters will match any block.

+ * + *

Note: To match the vanilla behavior of comparing block NBT, the NBT predicate + * will ONLY match data which would be sent to the client eg with + * {@link BlockHandler#getBlockEntityTags()}. This is relevant because this structure + * is used for matching adventure mode blocks and must line up with client prediction.

+ * + * @param blocks The block names/tags to match. + * @param state The block properties to match. + * @param nbt The block nbt to match. + */ +public record BlockPredicate( + @Nullable BlockTypeFilter blocks, + @Nullable PropertiesPredicate state, + @Nullable CompoundBinaryTag nbt +) implements Predicate { + /** + * Matches all blocks. + */ + public static final BlockPredicate ALL = new BlockPredicate(null, null, null); + /** + *

Matches no blocks.

+ * + *

Works based on the property that an exact property will never match a property which doesnt exist on any block.

+ */ + public static final BlockPredicate NONE = new BlockPredicate(null, new PropertiesPredicate(Map.of("no_such_property", new PropertiesPredicate.ValuePredicate.Exact("never"))), null); + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, BlockPredicate value) { + buffer.writeOptional(BlockTypeFilter.NETWORK_TYPE, value.blocks); + buffer.writeOptional(PropertiesPredicate.NETWORK_TYPE, value.state); + buffer.writeOptional(NetworkBuffer.NBT, value.nbt); + } + + @Override + public BlockPredicate read(@NotNull NetworkBuffer buffer) { + return new BlockPredicate( + buffer.readOptional(BlockTypeFilter.NETWORK_TYPE), + buffer.readOptional(PropertiesPredicate.NETWORK_TYPE), + (CompoundBinaryTag) buffer.readOptional(NetworkBuffer.NBT) + ); + } + }; + public static final BinaryTagSerializer NBT_TYPE = new BinaryTagSerializer<>() { + @Override + public @NotNull BinaryTag write(@NotNull BlockPredicate value) { + CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder(); + if (value.blocks != null) + builder.put("blocks", BlockTypeFilter.NBT_TYPE.write(value.blocks)); + if (value.state != null) + builder.put("state", PropertiesPredicate.NBT_TYPE.write(value.state)); + if (value.nbt != null) + builder.put("nbt", value.nbt); + return builder.build(); + } + + @Override + public @NotNull BlockPredicate read(@NotNull BinaryTag tag) { + if (!(tag instanceof CompoundBinaryTag compound)) return BlockPredicate.ALL; + + BinaryTag entry; + BlockTypeFilter blocks = null; + if ((entry = compound.get("blocks")) != null) + blocks = BlockTypeFilter.NBT_TYPE.read(entry); + PropertiesPredicate state = null; + if ((entry = compound.get("state")) != null) + state = PropertiesPredicate.NBT_TYPE.read(entry); + CompoundBinaryTag nbt = null; + if ((entry = compound.get("nbt")) != null) + nbt = BinaryTagSerializer.COMPOUND_COERCED.read(entry); + return new BlockPredicate(blocks, state, nbt); + } + }; + + public BlockPredicate(@NotNull BlockTypeFilter blocks) { + this(blocks, null, null); + } + + public BlockPredicate(@NotNull Block... blocks) { + this(new BlockTypeFilter.Blocks(blocks)); + } + + public BlockPredicate(@NotNull PropertiesPredicate state) { + this(null, state, null); + } + + public BlockPredicate(@NotNull CompoundBinaryTag nbt) { + this(null, null, nbt); + } + + @Override + public boolean test(@NotNull Block block) { + if (blocks != null && !blocks.test(block)) + return false; + if (state != null && !state.test(block)) + return false; + return nbt == null || Objects.equals(nbt, BlockUtils.extractClientNbt(block)); + } + +} diff --git a/src/main/java/net/minestom/server/instance/block/predicate/BlockTypeFilter.java b/src/main/java/net/minestom/server/instance/block/predicate/BlockTypeFilter.java new file mode 100644 index 00000000000..acb8b1c9906 --- /dev/null +++ b/src/main/java/net/minestom/server/instance/block/predicate/BlockTypeFilter.java @@ -0,0 +1,128 @@ +package net.minestom.server.instance.block.predicate; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.BinaryTagTypes; +import net.kyori.adventure.nbt.ListBinaryTag; +import net.kyori.adventure.nbt.StringBinaryTag; +import net.minestom.server.MinecraftServer; +import net.minestom.server.gamedata.tags.TagManager; +import net.minestom.server.instance.block.Block; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Predicate; + +public sealed interface BlockTypeFilter extends Predicate permits BlockTypeFilter.Blocks, BlockTypeFilter.Tag { + + record Blocks(@NotNull List blocks) implements BlockTypeFilter { + public Blocks { + blocks = List.copyOf(blocks); + } + + public Blocks(@NotNull Block... blocks) { + this(List.of(blocks)); + } + + @Override + public boolean test(@NotNull Block block) { + final int blockId = block.id(); + for (Block b : blocks) { + if (blockId == b.id()) { + return true; + } + } + return false; + } + } + + record Tag(@NotNull net.minestom.server.gamedata.tags.Tag tag) implements BlockTypeFilter { + private static final TagManager TAG_MANAGER = Objects.requireNonNull(MinecraftServer.getTagManager()); + + public Tag(@NotNull String namespaceId) { + this(Objects.requireNonNull(TAG_MANAGER.getTag(net.minestom.server.gamedata.tags.Tag.BasicType.BLOCKS, namespaceId), + "No such block tag: " + namespaceId)); + } + + @Override + public boolean test(Block block) { + return tag.contains(block.namespace()); + } + } + + NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, BlockTypeFilter value) { + switch (value) { + case Blocks blocks -> { + buffer.write(NetworkBuffer.VAR_INT, blocks.blocks.size() + 1); + for (Block block : blocks.blocks) { + buffer.write(NetworkBuffer.VAR_INT, block.id()); + } + } + case Tag tag -> { + buffer.write(NetworkBuffer.VAR_INT, 0); + buffer.write(NetworkBuffer.STRING, tag.tag.name()); + } + } + } + + @Override + public BlockTypeFilter read(@NotNull NetworkBuffer buffer) { + final int count = buffer.read(NetworkBuffer.VAR_INT) - 1; + if (count == -1) { + return new Tag(buffer.read(NetworkBuffer.STRING)); + } else { + final List blocks = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + blocks.add(Block.fromBlockId(buffer.read(NetworkBuffer.VAR_INT))); + } + return new Blocks(blocks); + } + } + }; + + BinaryTagSerializer NBT_TYPE = new BinaryTagSerializer<>() { + @Override + public @NotNull BinaryTag write(@NotNull BlockTypeFilter value) { + return switch (value) { + case Blocks blocks -> { + ListBinaryTag.Builder builder = ListBinaryTag.builder(BinaryTagTypes.STRING); + for (Block block : blocks.blocks) { + builder.add(StringBinaryTag.stringBinaryTag(block.name())); + } + yield builder.build(); + } + case Tag tag -> StringBinaryTag.stringBinaryTag("#" + tag.tag.name()); + }; + } + + @Override + public @NotNull BlockTypeFilter read(@NotNull BinaryTag tag) { + return switch (tag) { + case ListBinaryTag list -> { + final List blocks = new ArrayList<>(list.size()); + for (BinaryTag binaryTag : list) { + if (!(binaryTag instanceof StringBinaryTag string)) continue; + blocks.add(Objects.requireNonNull(Block.fromNamespaceId(string.value()))); + } + yield new Blocks(blocks); + } + case StringBinaryTag string -> { + // Could be a tag or a block name depending if it starts with a # + final String value = string.value(); + if (value.startsWith("#")) { + yield new Tag(value.substring(1)); + } else { + yield new Blocks(Objects.requireNonNull(Block.fromNamespaceId(value))); + } + } + default -> throw new IllegalArgumentException("Invalid tag type: " + tag.type()); + }; + } + }; + +} diff --git a/src/main/java/net/minestom/server/instance/block/predicate/PropertiesPredicate.java b/src/main/java/net/minestom/server/instance/block/predicate/PropertiesPredicate.java new file mode 100644 index 00000000000..32210ef765a --- /dev/null +++ b/src/main/java/net/minestom/server/instance/block/predicate/PropertiesPredicate.java @@ -0,0 +1,177 @@ +package net.minestom.server.instance.block.predicate; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.StringBinaryTag; +import net.minestom.server.instance.block.Block; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Predicate; + +public record PropertiesPredicate(@NotNull Map properties) implements Predicate { + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, PropertiesPredicate value) { + buffer.write(NetworkBuffer.VAR_INT, value.properties.size()); + for (Map.Entry entry : value.properties.entrySet()) { + buffer.write(NetworkBuffer.STRING, entry.getKey()); + buffer.write(ValuePredicate.NETWORK_TYPE, entry.getValue()); + } + } + + @Override + public PropertiesPredicate read(@NotNull NetworkBuffer buffer) { + int size = buffer.read(NetworkBuffer.VAR_INT); + Map properties = new HashMap<>(size); + for (int i = 0; i < size; i++) { + properties.put(buffer.read(NetworkBuffer.STRING), buffer.read(ValuePredicate.NETWORK_TYPE)); + } + return new PropertiesPredicate(properties); + } + }; + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> { + Map properties = new HashMap<>(); + for (Map.Entry entry : tag) { + properties.put(entry.getKey(), ValuePredicate.NBT_TYPE.read(entry.getValue())); + } + return new PropertiesPredicate(properties); + }, + value -> { + CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder(); + for (Map.Entry entry : value.properties.entrySet()) { + builder.put(entry.getKey(), ValuePredicate.NBT_TYPE.write(entry.getValue())); + } + return builder.build(); + } + ); + + public static @NotNull PropertiesPredicate exact(@NotNull String key, @NotNull String value) { + return new PropertiesPredicate(Map.of(key, new ValuePredicate.Exact(value))); + } + + public PropertiesPredicate { + properties = Map.copyOf(properties); + } + + @Override + public boolean test(@NotNull Block block) { + for (Map.Entry entry : properties.entrySet()) { + final String value = block.getProperty(entry.getKey()); + if (!entry.getValue().test(value)) + return false; + } + return true; + } + + public sealed interface ValuePredicate extends Predicate<@Nullable String> permits ValuePredicate.Exact, ValuePredicate.Range { + + record Exact(@Nullable String value) implements ValuePredicate { + + public static final NetworkBuffer.Type NETWORK_TYPE = NetworkBuffer.STRING.map(Exact::new, Exact::value); + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.STRING.map(Exact::new, Exact::value); + + @Override + public boolean test(@Nullable String prop) { + return prop != null && prop.equals(value); + } + } + + /** + *

Vanilla has some fancy behavior to get integer properties as ints, but seems to just compare the value + * anyway if its a string. Our behavior here is to attempt to parse the values as an integer and default + * to a string.compareTo otherwise.

+ * + *

Providing no min or max or a property which does exist results in a constant false.

+ * + * @param min The min value to match, inclusive + * @param max The max value to match, exclusive + */ + record Range(@Nullable String min, @Nullable String max) implements ValuePredicate { + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, Range value) { + buffer.writeOptional(NetworkBuffer.STRING, value.min); + buffer.writeOptional(NetworkBuffer.STRING, value.max); + } + + @Override + public Range read(@NotNull NetworkBuffer buffer) { + return new Range(buffer.readOptional(NetworkBuffer.STRING), buffer.readOptional(NetworkBuffer.STRING)); + } + }; + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> new Range( + tag.get("min") instanceof StringBinaryTag string ? string.value() : null, + tag.get("max") instanceof StringBinaryTag string ? string.value() : null), + value -> { + CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder(); + if (value.min != null) builder.putString("min", value.min); + if (value.max != null) builder.putString("max", value.max); + return builder.build(); + } + ); + + @Override + public boolean test(@Nullable String prop) { + if (prop == null || (min == null && max == null)) return false; + try { + // Try to match as integers + int value = Integer.parseInt(prop); + return (min == null || value >= Integer.parseInt(min)) + && (max == null || value < Integer.parseInt(max)); + } catch (NumberFormatException e) { + // Not an integer, just compare the strings + return (min == null || prop.compareTo(min) >= 0) + && (max == null || prop.compareTo(max) < 0); + } + } + } + + NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, ValuePredicate value) { + switch (value) { + case Exact exact -> { + buffer.write(NetworkBuffer.BOOLEAN, true); + buffer.write(Exact.NETWORK_TYPE, exact); + } + case Range range -> { + buffer.write(NetworkBuffer.BOOLEAN, false); + buffer.write(Range.NETWORK_TYPE, range); + } + } + } + + @Override + public ValuePredicate read(@NotNull NetworkBuffer buffer) { + return buffer.read(NetworkBuffer.BOOLEAN) ? buffer.read(Exact.NETWORK_TYPE) : buffer.read(Range.NETWORK_TYPE); + } + }; + BinaryTagSerializer NBT_TYPE = new BinaryTagSerializer<>() { + @Override + public @NotNull BinaryTag write(@NotNull ValuePredicate value) { + return switch (value) { + case Exact exact -> Exact.NBT_TYPE.write(exact); + case Range range -> Range.NBT_TYPE.write(range); + }; + } + + @Override + public @NotNull ValuePredicate read(@NotNull BinaryTag tag) { + if (tag instanceof StringBinaryTag) { + return Exact.NBT_TYPE.read(tag); + } else { + return Range.NBT_TYPE.read(tag); + } + } + }; + } +} diff --git a/src/main/java/net/minestom/server/instance/block/rule/BlockPlacementRule.java b/src/main/java/net/minestom/server/instance/block/rule/BlockPlacementRule.java index 64351b9767e..9488b6105da 100644 --- a/src/main/java/net/minestom/server/instance/block/rule/BlockPlacementRule.java +++ b/src/main/java/net/minestom/server/instance/block/rule/BlockPlacementRule.java @@ -4,7 +4,7 @@ import net.minestom.server.coordinate.Pos; import net.minestom.server.instance.block.Block; import net.minestom.server.instance.block.BlockFace; -import net.minestom.server.item.ItemMeta; +import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -61,7 +61,7 @@ public record PlacementState( @NotNull Point placePosition, @Nullable Point cursorPosition, @Nullable Pos playerPosition, - @Nullable ItemMeta usedItemMeta, + @Nullable ItemStack usedItemStack, boolean isPlayerShifting ) { } diff --git a/src/main/java/net/minestom/server/instance/GeneratorImpl.java b/src/main/java/net/minestom/server/instance/generator/GeneratorImpl.java similarity index 76% rename from src/main/java/net/minestom/server/instance/GeneratorImpl.java rename to src/main/java/net/minestom/server/instance/generator/GeneratorImpl.java index e01933a8a94..34026a5af3c 100644 --- a/src/main/java/net/minestom/server/instance/GeneratorImpl.java +++ b/src/main/java/net/minestom/server/instance/generator/GeneratorImpl.java @@ -1,70 +1,85 @@ -package net.minestom.server.instance; +package net.minestom.server.instance.generator; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import net.minestom.server.MinecraftServer; import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Vec; import net.minestom.server.instance.block.Block; -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.utils.validate.Check; +import net.minestom.server.world.biome.Biome; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import static net.minestom.server.utils.chunk.ChunkUtils.*; -final class GeneratorImpl { +@ApiStatus.Internal +public final class GeneratorImpl { private static final Vec SECTION_SIZE = new Vec(16); - private static final BiomeManager BIOME_MANAGER = MinecraftServer.getBiomeManager(); - static GenerationUnit section(Section section, int sectionX, int sectionY, int sectionZ, + public record GenSection(Palette blocks, Palette biomes, Int2ObjectMap specials) { + public GenSection(Palette blocks, Palette biomes) { + this(blocks, biomes, new Int2ObjectOpenHashMap<>(0)); + } + + public GenSection() { + this(Palette.blocks(), Palette.biomes()); + } + } + + static GenerationUnit section(DynamicRegistry biomeRegistry, GenSection section, + int sectionX, int sectionY, int sectionZ, boolean fork) { final Vec start = SECTION_SIZE.mul(sectionX, sectionY, sectionZ); final Vec end = start.add(SECTION_SIZE); - final UnitModifier modifier = new SectionModifierImpl(SECTION_SIZE, start, end, - section.blockPalette(), section.biomePalette(), new Int2ObjectOpenHashMap<>(0), fork); - return unit(modifier, start, end, null); + final UnitModifier modifier = new SectionModifierImpl(biomeRegistry, SECTION_SIZE, + start, end, section, fork); + return unit(biomeRegistry, modifier, start, end, null); } - static GenerationUnit section(Section section, int sectionX, int sectionY, int sectionZ) { - return section(section, sectionX, sectionY, sectionZ, false); + public static GenerationUnit section(DynamicRegistry biomeRegistry, GenSection section, int sectionX, int sectionY, int sectionZ) { + return section(biomeRegistry, section, sectionX, sectionY, sectionZ, false); } - static UnitImpl chunk(Chunk chunk, int minSection, int maxSection, - List
chunkSections, int chunkX, int chunkZ) { - final int minY = minSection * 16; - AtomicInteger sectionCounterY = new AtomicInteger(minSection); - List sections = chunkSections.stream() - .map(section -> section(section, chunkX, sectionCounterY.getAndIncrement(), chunkZ)) - .toList(); - - final Vec size = new Vec(16, (maxSection - minSection) * 16, 16); - final Vec start = new Vec(chunkX * 16, minY, chunkZ * 16); - final Vec end = new Vec(chunkX * 16 + 16, size.y() + minY, chunkZ * 16 + 16); - final UnitModifier modifier = new AreaModifierImpl(chunk, - size, start, end, 1, sections.size(), 1, sections); - return unit(modifier, start, end, sections); + public static UnitImpl chunk(DynamicRegistry biomeRegistry, GenSection[] chunkSections, int chunkX, int minSection, int chunkZ) { + final Vec start = new Vec(chunkX * 16, minSection * 16, chunkZ * 16); + return area(biomeRegistry, start, 1, chunkSections.length, 1, chunkSections); } - static UnitImpl chunk(int minSection, int maxSection, - List
chunkSections, int chunkX, int chunkZ) { - return chunk(null, minSection, maxSection, chunkSections, chunkX, chunkZ); - } + public static UnitImpl area(DynamicRegistry biomeRegistry, Point start, int width, int height, int depth, GenSection[] areaSections) { + if (width == 0 || height == 0 || depth == 0) { + throw new IllegalArgumentException("Width, height and depth must be greater than 0, got " + width + ", " + height + ", " + depth); + } + if (areaSections.length != width * height * depth) { + throw new IllegalArgumentException("Invalid section count, expected " + width * height * depth + " but got " + areaSections.length); + } + + List sections = new ArrayList<>(); + for (int i = 0; i < areaSections.length; i++) { + GenSection section = areaSections[i]; + final Point point = to3D(i, width, height, depth); + final int sectionX = (int) point.x() + start.chunkX(); + final int sectionY = (int) point.y() + start.section(); + final int sectionZ = (int) point.z() + start.chunkZ(); + sections.add(section(biomeRegistry, section, sectionX, sectionY, sectionZ)); + } + sections = List.copyOf(sections); - static UnitImpl chunk(Chunk chunk) { - return chunk(chunk, chunk.minSection, chunk.maxSection, chunk.getSections(), chunk.getChunkX(), chunk.getChunkZ()); + final Point size = SECTION_SIZE.mul(width, height, depth); + final Point end = start.add(size); + final UnitModifier modifier = new AreaModifierImpl(size, start, end, width, height, depth, sections); + return unit(biomeRegistry, modifier, start, end, sections); } - static UnitImpl unit(UnitModifier modifier, Point start, Point end, - List divided) { + public static UnitImpl unit(DynamicRegistry biomeRegistry, UnitModifier modifier, Point start, Point end, + List divided) { if (start.x() > end.x() || start.y() > end.y() || start.z() > end.z()) { throw new IllegalArgumentException("absoluteStart must be before absoluteEnd"); } @@ -75,14 +90,19 @@ static UnitImpl unit(UnitModifier modifier, Point start, Point end, throw new IllegalArgumentException("absoluteEnd must be a multiple of 16"); } final Point size = end.sub(start); - return new UnitImpl(modifier, size, start, end, divided, new CopyOnWriteArrayList<>()); + return new UnitImpl(biomeRegistry, modifier, size, start, end, divided, new CopyOnWriteArrayList<>()); } static final class DynamicFork implements Block.Setter { + final DynamicRegistry biomeRegistry; Vec minSection; int width, height, depth; List sections; + DynamicFork(DynamicRegistry biomeRegistry) { + this.biomeRegistry = biomeRegistry; + } + @Override public void setBlock(int x, int y, int z, @NotNull Block block) { resize(x, y, z); @@ -103,7 +123,7 @@ private void resize(int x, int y, int z) { this.width = 1; this.height = 1; this.depth = 1; - this.sections = List.of(section(new Section(), sectionX, sectionY, sectionZ, true)); + this.sections = List.of(section(biomeRegistry, new GenSection(), sectionX, sectionY, sectionZ, true)); } else if (x < minSection.x() || y < minSection.y() || z < minSection.z() || x >= minSection.x() + width * 16 || y >= minSection.y() + height * 16 || z >= minSection.z() + depth * 16) { // Resize necessary @@ -137,7 +157,7 @@ private void resize(int x, int y, int z) { final int newX = coordinates.blockX() + startX; final int newY = coordinates.blockY() + startY; final int newZ = coordinates.blockZ() + startZ; - final GenerationUnit unit = section(new Section(), newX, newY, newZ, true); + final GenerationUnit unit = section(biomeRegistry, new GenSection(), newX, newY, newZ, true); newSections[i] = unit; } } @@ -150,10 +170,10 @@ private void resize(int x, int y, int z) { } } - record UnitImpl(UnitModifier modifier, Point size, - Point absoluteStart, Point absoluteEnd, - List divided, - List forks) implements GenerationUnit { + public record UnitImpl(DynamicRegistry biomeRegistry, UnitModifier modifier, Point size, + Point absoluteStart, Point absoluteEnd, + List divided, + List forks) implements GenerationUnit { @Override public @NotNull GenerationUnit fork(@NotNull Point start, @NotNull Point end) { final int minSectionX = floorSection(start.blockX()) / 16; @@ -173,7 +193,7 @@ record UnitImpl(UnitModifier modifier, Point size, for (int sectionX = minSectionX; sectionX < maxSectionX; sectionX++) { for (int sectionY = minSectionY; sectionY < maxSectionY; sectionY++) { for (int sectionZ = minSectionZ; sectionZ < maxSectionZ; sectionZ++) { - final GenerationUnit unit = section(new Section(), sectionX, sectionY, sectionZ, true); + final GenerationUnit unit = section(biomeRegistry, new GenSection(), sectionX, sectionY, sectionZ, true); units[index++] = unit; } } @@ -185,7 +205,7 @@ record UnitImpl(UnitModifier modifier, Point size, @Override public void fork(@NotNull Consumer consumer) { - DynamicFork dynamicFork = new DynamicFork(); + DynamicFork dynamicFork = new DynamicFork(biomeRegistry); consumer.accept(dynamicFork); final Point startSection = dynamicFork.minSection; if (startSection == null) @@ -206,24 +226,22 @@ private GenerationUnit registerFork(Point start, List sections, int width, int height, int depth) { final Point end = start.add(width * 16, height * 16, depth * 16); final Point size = end.sub(start); - final AreaModifierImpl modifier = new AreaModifierImpl(null, - size, start, end, width, height, depth, sections); - final UnitImpl fork = new UnitImpl(modifier, size, start, end, sections, forks); + final AreaModifierImpl modifier = new AreaModifierImpl(size, start, end, width, height, depth, sections); + final UnitImpl fork = new UnitImpl(biomeRegistry, modifier, size, start, end, sections, forks); forks.add(fork); return fork; } } - record SectionModifierImpl(Point size, Point start, Point end, - Palette blockPalette, Palette biomePalette, - Int2ObjectMap cache, boolean fork) implements GenericModifier { + public record SectionModifierImpl(DynamicRegistry biomeRegistry, Point size, Point start, Point end, + GenSection genSection, boolean fork) implements GenericModifier { + @Override - public void setBiome(int x, int y, int z, @NotNull Biome biome) { + public void setBiome(int x, int y, int z, @NotNull DynamicRegistry.Key biome) { if (fork) throw new IllegalStateException("Cannot modify biomes of a fork"); - var id = BIOME_MANAGER.getId(biome); - if (id == -1) throw new IllegalStateException("Biome has not been registered: " + biome.namespace()); - - this.biomePalette.set( + final int id = biomeRegistry.getId(biome); + Check.argCondition(id == -1, "Biome has not been registered: {0}", biome); + this.genSection.biomes.set( toSectionRelativeCoordinate(x) / 4, toSectionRelativeCoordinate(y) / 4, toSectionRelativeCoordinate(z) / 4, id); @@ -235,18 +253,18 @@ public void setBlock(int x, int y, int z, @NotNull Block block) { final int localY = toSectionRelativeCoordinate(y); final int localZ = toSectionRelativeCoordinate(z); handleCache(localX, localY, localZ, block); - this.blockPalette.set(localX, localY, localZ, retrieveBlockId(block)); + this.genSection.blocks.set(localX, localY, localZ, retrieveBlockId(block)); } @Override public void setRelative(int x, int y, int z, @NotNull Block block) { handleCache(x, y, z, block); - this.blockPalette.set(x, y, z, retrieveBlockId(block)); + this.genSection.blocks.set(x, y, z, retrieveBlockId(block)); } @Override public void setAllRelative(@NotNull Supplier supplier) { - this.blockPalette.setAll((x, y, z) -> { + this.genSection.blocks.setAll((x, y, z) -> { final Block block = supplier.get(x, y, z); handleCache(x, y, z, block); return retrieveBlockId(block); @@ -259,20 +277,20 @@ public void fill(@NotNull Block block) { for (int x = 0; x < 16; x++) { for (int y = 0; y < 16; y++) { for (int z = 0; z < 16; z++) { - this.cache.put(getBlockIndex(x, y, z), block); + this.genSection.specials.put(getBlockIndex(x, y, z), block); } } } } - this.blockPalette.fill(retrieveBlockId(block)); + this.genSection.blocks.fill(retrieveBlockId(block)); } @Override - public void fillBiome(@NotNull Biome biome) { + public void fillBiome(@NotNull DynamicRegistry.Key biome) { if (fork) throw new IllegalStateException("Cannot modify biomes of a fork"); - var id = MinecraftServer.getBiomeManager().getId(biome); - if (id == -1) throw new IllegalStateException("Biome has not been registered: " + biome.namespace()); - this.biomePalette.fill(id); + final int id = biomeRegistry.getId(biome); + Check.argCondition(id == -1, "Biome has not been registered: {0}", biome); + this.genSection.biomes.fill(id); } private int retrieveBlockId(Block block) { @@ -281,9 +299,9 @@ private int retrieveBlockId(Block block) { private void handleCache(int x, int y, int z, Block block) { if (requireCache(block)) { - this.cache.put(getBlockIndex(x, y, z), block); - } else if (!cache.isEmpty()) { - this.cache.remove(getBlockIndex(x, y, z)); + this.genSection.specials.put(getBlockIndex(x, y, z), block); + } else if (!genSection.specials.isEmpty()) { + this.genSection.specials.remove(getBlockIndex(x, y, z)); } } @@ -292,10 +310,9 @@ private boolean requireCache(Block block) { } } - record AreaModifierImpl(Chunk chunk, - Point size, Point start, Point end, - int width, int height, int depth, - List sections) implements GenericModifier { + public record AreaModifierImpl(Point size, Point start, Point end, + int width, int height, int depth, + List sections) implements GenericModifier { @Override public void setBlock(int x, int y, int z, @NotNull Block block) { checkBorder(x, y, z); @@ -305,7 +322,7 @@ public void setBlock(int x, int y, int z, @NotNull Block block) { } @Override - public void setBiome(int x, int y, int z, @NotNull Biome biome) { + public void setBiome(int x, int y, int z, @NotNull DynamicRegistry.Key biome) { checkBorder(x, y, z); final GenerationUnit section = findAbsoluteSection(x, y, z); y -= start.y(); @@ -357,7 +374,7 @@ public void fill(@NotNull Block block) { } @Override - public void fillBiome(@NotNull Biome biome) { + public void fillBiome(@NotNull DynamicRegistry.Key biome) { for (GenerationUnit section : sections) { section.modifier().fillBiome(biome); } diff --git a/src/main/java/net/minestom/server/instance/generator/UnitModifier.java b/src/main/java/net/minestom/server/instance/generator/UnitModifier.java index 59006dec34d..dfd297232c7 100644 --- a/src/main/java/net/minestom/server/instance/generator/UnitModifier.java +++ b/src/main/java/net/minestom/server/instance/generator/UnitModifier.java @@ -2,7 +2,8 @@ import net.minestom.server.coordinate.Point; import net.minestom.server.instance.block.Block; -import net.minestom.server.world.biomes.Biome; +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.world.biome.Biome; import org.jetbrains.annotations.NotNull; public interface UnitModifier extends Block.Setter, Biome.Setter { @@ -60,7 +61,7 @@ public interface UnitModifier extends Block.Setter, Biome.Setter { * * @param biome the biome to fill */ - void fillBiome(@NotNull Biome biome); + void fillBiome(@NotNull DynamicRegistry.Key biome); interface Supplier { @NotNull Block get(int x, int y, int z); diff --git a/src/main/java/net/minestom/server/instance/heightmap/Heightmap.java b/src/main/java/net/minestom/server/instance/heightmap/Heightmap.java index cc208765e46..33d5e730cc7 100644 --- a/src/main/java/net/minestom/server/instance/heightmap/Heightmap.java +++ b/src/main/java/net/minestom/server/instance/heightmap/Heightmap.java @@ -4,9 +4,6 @@ import net.minestom.server.instance.block.Block; import net.minestom.server.utils.MathUtils; import org.jetbrains.annotations.NotNull; -import org.jglrxavpok.hephaistos.collections.ImmutableLongArray; -import org.jglrxavpok.hephaistos.nbt.NBT; -import org.jglrxavpok.hephaistos.nbt.NBTLongArray; import static net.minestom.server.instance.Chunk.CHUNK_SIZE_X; import static net.minestom.server.instance.Chunk.CHUNK_SIZE_Z; @@ -19,7 +16,7 @@ public abstract class Heightmap { public Heightmap(Chunk chunk) { this.chunk = chunk; - minHeight = chunk.getInstance().getDimensionType().getMinY() - 1; + minHeight = chunk.getInstance().getCachedDimensionType().minY() - 1; } protected abstract boolean checkBlock(@NotNull Block block); @@ -59,14 +56,14 @@ public void refresh(int x, int z, int startY) { setHeightY(x, z, y); } - public NBTLongArray getNBT() { - final int dimensionHeight = chunk.getInstance().getDimensionType().getHeight(); + public long[] getNBT() { + final int dimensionHeight = chunk.getInstance().getCachedDimensionType().height(); final int bitsForHeight = MathUtils.bitsToRepresent(dimensionHeight); - return NBT.LongArray(encode(heights, bitsForHeight)); + return encode(heights, bitsForHeight); } - public void loadFrom(ImmutableLongArray data) { - final int dimensionHeight = chunk.getInstance().getDimensionType().getHeight(); + public void loadFrom(long[] data) { + final int dimensionHeight = chunk.getInstance().getCachedDimensionType().height(); final int bitsPerEntry = MathUtils.bitsToRepresent(dimensionHeight); final int entriesPerLong = 64 / bitsPerEntry; @@ -78,7 +75,7 @@ public void loadFrom(ImmutableLongArray data) { for (int i = 0; i < heights.length; i++) { final int indexInContainer = i % entriesPerLong; - heights[i] = (short) ((int)(data.get(containerIndex) >> (indexInContainer * bitsPerEntry)) & entryMask); + heights[i] = (short) ((int)(data[containerIndex] >> (indexInContainer * bitsPerEntry)) & entryMask); if (indexInContainer == maxPossibleIndexInContainer) containerIndex++; } @@ -97,7 +94,7 @@ private void setHeightY(int x, int z, int height) { } public static int getHighestBlockSection(Chunk chunk) { - int y = chunk.getInstance().getDimensionType().getMaxY(); + int y = chunk.getInstance().getCachedDimensionType().maxY(); final int sectionsCount = chunk.getMaxSection() - chunk.getMinSection(); for (int i = 0; i < sectionsCount; i++) { diff --git a/src/main/java/net/minestom/server/instance/light/BlockLight.java b/src/main/java/net/minestom/server/instance/light/BlockLight.java index 19a3dd2d430..a1cc22f64ad 100644 --- a/src/main/java/net/minestom/server/instance/light/BlockLight.java +++ b/src/main/java/net/minestom/server/instance/light/BlockLight.java @@ -10,10 +10,11 @@ import net.minestom.server.instance.block.BlockFace; import net.minestom.server.instance.palette.Palette; import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; +import java.util.Arrays; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import static net.minestom.server.instance.light.LightCompute.*; @@ -24,8 +25,8 @@ final class BlockLight implements Light { private byte[] contentPropagation; private byte[] contentPropagationSwap; - private boolean isValidBorders = true; - private boolean needsSend = false; + private final AtomicBoolean isValidBorders = new AtomicBoolean(true); + private final AtomicBoolean needsSend = new AtomicBoolean(false); private Set toUpdateSet = new HashSet<>(); private final Section[] neighborSections = new Section[BlockFace.values().length]; @@ -83,7 +84,7 @@ private ShortArrayFIFOQueue buildExternalQueue(Instance instance, Palette blockP neighborSections[face.ordinal()] = otherSection; } - var otherLight = otherSection.blockLight(); + Light otherLight = otherSection.blockLight(); for (int bx = 0; bx < 16; bx++) { for (int by = 0; by < 16; by++) { @@ -145,14 +146,14 @@ private ShortArrayFIFOQueue buildExternalQueue(Instance instance, Palette blockP @Override public Light calculateInternal(Instance instance, int chunkX, int sectionY, int chunkZ) { + this.isValidBorders.set(true); + Chunk chunk = instance.getChunk(chunkX, chunkZ); if (chunk == null) { this.toUpdateSet = Set.of(); return this; } - this.isValidBorders = true; - Set toUpdate = new HashSet<>(); // Update single section with base lighting changes @@ -171,8 +172,8 @@ public Light calculateInternal(Instance instance, int chunkX, int sectionY, int Vec neighborPos = new Vec(chunkX + i, sectionY + k, chunkZ + j); if (neighborPos.blockY() >= neighborChunk.getMinSection() && neighborPos.blockY() < neighborChunk.getMaxSection()) { - toUpdate.add(new Vec(neighborChunk.getChunkX(), neighborPos.blockY(), neighborChunk.getChunkZ())); - neighborChunk.getSection(neighborPos.blockY()).blockLight().invalidatePropagation(); + if (neighborChunk.getSection(neighborPos.blockY()).blockLight() instanceof BlockLight blockLight) + blockLight.contentPropagation = null; } } } @@ -186,12 +187,14 @@ public Light calculateInternal(Instance instance, int chunkX, int sectionY, int @Override public void invalidate() { - invalidatePropagation(); + this.needsSend.set(true); + this.isValidBorders.set(false); + this.contentPropagation = null; } @Override public boolean requiresUpdate() { - return !isValidBorders; + return !isValidBorders.get(); } @Override @@ -199,21 +202,13 @@ public boolean requiresUpdate() { public void set(byte[] copyArray) { this.content = copyArray.clone(); this.contentPropagation = this.content; - this.isValidBorders = true; - this.needsSend = true; + this.isValidBorders.set(true); + this.needsSend.set(true); } @Override public boolean requiresSend() { - boolean res = needsSend; - needsSend = false; - return res; - } - - private void clearCache() { - this.contentPropagation = null; - isValidBorders = true; - needsSend = true; + return needsSend.getAndSet(false); } @Override @@ -227,7 +222,10 @@ public byte[] array() { @Override public Light calculateExternal(Instance instance, Chunk chunk, int sectionY) { - if (!isValidBorders) clearCache(); + if (!isValidBorders.get()) { + this.toUpdateSet = Set.of(); + return this; + } Point[] neighbors = Light.getNeighbors(chunk, sectionY); @@ -262,6 +260,8 @@ private byte[] bake(byte[] content1, byte[] content2) { if (content1 == null) return content2; if (content2 == null) return content1; + if (Arrays.equals(content1, emptyContent) && Arrays.equals(content2, emptyContent)) return emptyContent; + byte[] lightMax = new byte[LIGHT_LENGTH]; for (int i = 0; i < content1.length; i++) { // Lower @@ -280,13 +280,6 @@ private byte[] bake(byte[] content1, byte[] content2) { return lightMax; } - @Override - public void invalidatePropagation() { - this.isValidBorders = false; - this.needsSend = false; - this.contentPropagation = null; - } - @Override public int getLevel(int x, int y, int z) { if (content == null) return 0; diff --git a/src/main/java/net/minestom/server/instance/light/Light.java b/src/main/java/net/minestom/server/instance/light/Light.java index 540aa7ca054..9e119f5a9ca 100644 --- a/src/main/java/net/minestom/server/instance/light/Light.java +++ b/src/main/java/net/minestom/server/instance/light/Light.java @@ -10,8 +10,6 @@ import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; -import java.util.HashMap; -import java.util.Map; import java.util.Set; import static net.minestom.server.instance.light.LightCompute.SECTION_SIZE; @@ -36,9 +34,6 @@ static Light block(@NotNull Palette blockPalette) { @ApiStatus.Internal Light calculateExternal(Instance instance, Chunk chunk, int sectionY); - @ApiStatus.Internal - void invalidatePropagation(); - int getLevel(int x, int y, int z); @ApiStatus.Internal diff --git a/src/main/java/net/minestom/server/instance/light/SkyLight.java b/src/main/java/net/minestom/server/instance/light/SkyLight.java index 1fa0448ea92..c569bca92ce 100644 --- a/src/main/java/net/minestom/server/instance/light/SkyLight.java +++ b/src/main/java/net/minestom/server/instance/light/SkyLight.java @@ -12,8 +12,10 @@ import net.minestom.server.instance.palette.Palette; import org.jetbrains.annotations.ApiStatus; +import java.util.Arrays; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import static net.minestom.server.instance.light.LightCompute.*; @@ -24,8 +26,8 @@ final class SkyLight implements Light { private byte[] contentPropagation; private byte[] contentPropagationSwap; - private boolean isValidBorders = true; - private boolean needsSend = false; + private final AtomicBoolean isValidBorders = new AtomicBoolean(true); + private final AtomicBoolean needsSend = new AtomicBoolean(false); private Set toUpdateSet = new HashSet<>(); private final Section[] neighborSections = new Section[BlockFace.values().length]; @@ -51,7 +53,7 @@ static ShortArrayFIFOQueue buildInternalQueue(Chunk c, int sectionY) { if (c instanceof LightingChunk lc) { int[] heightmap = lc.getOcclusionMap(); - int maxY = c.getInstance().getDimensionType().getMinY() + c.getInstance().getDimensionType().getHeight(); + int maxY = c.getInstance().getCachedDimensionType().minY() + c.getInstance().getCachedDimensionType().height(); int sectionMaxY = (sectionY + 1) * 16 - 1; int sectionMinY = sectionY * 16; @@ -160,7 +162,7 @@ public Light calculateInternal(Instance instance, int chunkX, int sectionY, int this.toUpdateSet = Set.of(); return this; } - this.isValidBorders = true; + this.isValidBorders.set(true); // Update single section with base lighting changes int queueSize = SECTION_SIZE * SECTION_SIZE * SECTION_SIZE; @@ -190,8 +192,10 @@ public Light calculateInternal(Instance instance, int chunkX, int sectionY, int Vec neighborPos = new Vec(chunkX + i, sectionY + k, chunkZ + j); if (neighborPos.blockY() >= neighborChunk.getMinSection() && neighborPos.blockY() < neighborChunk.getMaxSection()) { - toUpdate.add(new Vec(neighborChunk.getChunkX(), neighborPos.blockY(), neighborChunk.getChunkZ())); - neighborChunk.getSection(neighborPos.blockY()).skyLight().invalidatePropagation(); + if (neighborChunk.getSection(neighborPos.blockY()).skyLight() instanceof SkyLight skyLight) { + skyLight.contentPropagation = null; + toUpdate.add(new Vec(neighborChunk.getChunkX(), neighborPos.blockY(), neighborChunk.getChunkZ())); + } } } } @@ -205,12 +209,14 @@ public Light calculateInternal(Instance instance, int chunkX, int sectionY, int @Override public void invalidate() { - invalidatePropagation(); + this.needsSend.set(true); + this.isValidBorders.set(false); + this.contentPropagation = null; } @Override public boolean requiresUpdate() { - return !isValidBorders; + return !isValidBorders.get(); } @Override @@ -218,22 +224,13 @@ public boolean requiresUpdate() { public void set(byte[] copyArray) { this.content = copyArray.clone(); this.contentPropagation = this.content; - this.isValidBorders = true; - this.needsSend = true; + this.isValidBorders.set(true); + this.needsSend.set(true); } @Override public boolean requiresSend() { - boolean res = needsSend; - needsSend = false; - return res; - } - - private void clearCache() { - this.contentPropagation = null; - isValidBorders = true; - needsSend = true; - fullyLit = false; + return needsSend.getAndSet(false); } @Override @@ -247,7 +244,10 @@ public byte[] array() { @Override public Light calculateExternal(Instance instance, Chunk chunk, int sectionY) { - if (!isValidBorders) clearCache(); + if (!isValidBorders.get()) { + this.toUpdateSet = Set.of(); + return this; + } Point[] neighbors = Light.getNeighbors(chunk, sectionY); Set toUpdate = new HashSet<>(); @@ -289,6 +289,8 @@ private byte[] bake(byte[] content1, byte[] content2) { if (content1 == null) return content2; if (content2 == null) return content1; + if (Arrays.equals(content1, emptyContent) && Arrays.equals(content2, emptyContent)) return emptyContent; + byte[] lightMax = new byte[LIGHT_LENGTH]; for (int i = 0; i < content1.length; i++) { // Lower @@ -307,13 +309,6 @@ private byte[] bake(byte[] content1, byte[] content2) { return lightMax; } - @Override - public void invalidatePropagation() { - this.isValidBorders = false; - this.needsSend = false; - this.contentPropagation = null; - } - @Override public int getLevel(int x, int y, int z) { if (content == null) return 0; diff --git a/src/main/java/net/minestom/server/inventory/Inventory.java b/src/main/java/net/minestom/server/inventory/Inventory.java index a267f721633..ace374daffc 100644 --- a/src/main/java/net/minestom/server/inventory/Inventory.java +++ b/src/main/java/net/minestom/server/inventory/Inventory.java @@ -16,7 +16,6 @@ import java.util.Collections; import java.util.List; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicInteger; @@ -41,8 +40,6 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable // the players currently viewing this inventory private final Set viewers = new CopyOnWriteArraySet<>(); private final Set unmodifiableViewers = Collections.unmodifiableSet(viewers); - // (player -> cursor item) map, used by the click listeners - private final ConcurrentHashMap cursorPlayersItem = new ConcurrentHashMap<>(); public Inventory(@NotNull InventoryType inventoryType, @NotNull Component title) { super(inventoryType.getSize()); @@ -99,12 +96,6 @@ public byte getWindowId() { return id; } - @Override - public synchronized void clear() { - this.cursorPlayersItem.clear(); - super.clear(); - } - /** * Refreshes the inventory for all viewers. */ @@ -152,38 +143,30 @@ public boolean addViewer(@NotNull Player player) { @Override public boolean removeViewer(@NotNull Player player) { final boolean result = this.viewers.remove(player); - setCursorItem(player, ItemStack.AIR); this.clickProcessor.clearCache(player); return result; } /** - * Gets the cursor item of a viewer. + * Gets the cursor item of a player. * - * @param player the player to get the cursor item from - * @return the player cursor item, air item if the player is not a viewer + * @deprecated normal inventories no longer store cursor items + * @see the relevant PR */ + @Deprecated public @NotNull ItemStack getCursorItem(@NotNull Player player) { - return cursorPlayersItem.getOrDefault(player, ItemStack.AIR); + return player.getInventory().getCursorItem(); } /** - * Changes the cursor item of a viewer, - * does nothing if player is not a viewer. + * Changes the cursor item of a player. * - * @param player the player to change the cursor item - * @param cursorItem the new player cursor item + * @deprecated normal inventories no longer store cursor items + * @see the relevant PR */ + @Deprecated public void setCursorItem(@NotNull Player player, @NotNull ItemStack cursorItem) { - final ItemStack currentCursorItem = cursorPlayersItem.getOrDefault(player, ItemStack.AIR); - if (!currentCursorItem.equals(cursorItem)) { - player.sendPacket(SetSlotPacket.createCursorPacket(cursorItem)); - } - if (!cursorItem.isAir()) { - this.cursorPlayersItem.put(player, cursorItem); - } else { - this.cursorPlayersItem.remove(player); - } + player.getInventory().setCursorItem(cursorItem); } @Override @@ -193,7 +176,7 @@ protected void UNSAFE_itemInsert(int slot, @NotNull ItemStack itemStack, boolean } private @NotNull WindowItemsPacket createNewWindowItemsPacket(Player player) { - return new WindowItemsPacket(getWindowId(), 0, List.of(getItemStacks()), cursorPlayersItem.getOrDefault(player, ItemStack.AIR)); + return new WindowItemsPacket(getWindowId(), 0, List.of(getItemStacks()), player.getInventory().getCursorItem()); } /** @@ -210,7 +193,7 @@ protected void sendProperty(@NotNull InventoryProperty property, short value) { @Override public boolean leftClick(@NotNull Player player, int slot) { final PlayerInventory playerInventory = player.getInventory(); - final ItemStack cursor = getCursorItem(player); + final ItemStack cursor = playerInventory.getCursorItem(); final boolean isInWindow = isClickInWindow(slot); final int clickSlot = isInWindow ? slot : PlayerInventoryUtils.convertSlot(slot, offset); final ItemStack clicked = isInWindow ? getItemStack(slot) : playerInventory.getItemStack(clickSlot); @@ -225,7 +208,7 @@ public boolean leftClick(@NotNull Player player, int slot) { } else { playerInventory.setItemStack(clickSlot, clickResult.getClicked()); } - this.cursorPlayersItem.put(player, clickResult.getCursor()); + playerInventory.setCursorItem(clickResult.getCursor()); callClickEvent(player, isInWindow ? this : null, slot, ClickType.LEFT_CLICK, clicked, cursor); return true; } @@ -233,7 +216,7 @@ public boolean leftClick(@NotNull Player player, int slot) { @Override public boolean rightClick(@NotNull Player player, int slot) { final PlayerInventory playerInventory = player.getInventory(); - final ItemStack cursor = getCursorItem(player); + final ItemStack cursor = playerInventory.getCursorItem(); final boolean isInWindow = isClickInWindow(slot); final int clickSlot = isInWindow ? slot : PlayerInventoryUtils.convertSlot(slot, offset); final ItemStack clicked = isInWindow ? getItemStack(slot) : playerInventory.getItemStack(clickSlot); @@ -248,7 +231,7 @@ public boolean rightClick(@NotNull Player player, int slot) { } else { playerInventory.setItemStack(clickSlot, clickResult.getClicked()); } - this.cursorPlayersItem.put(player, clickResult.getCursor()); + playerInventory.setCursorItem(clickResult.getCursor()); callClickEvent(player, isInWindow ? this : null, slot, ClickType.RIGHT_CLICK, clicked, cursor); return true; } @@ -259,7 +242,7 @@ public boolean shiftClick(@NotNull Player player, int slot, int button) { // Mic final boolean isInWindow = isClickInWindow(slot); final int clickSlot = isInWindow ? slot : PlayerInventoryUtils.convertSlot(slot, offset); final ItemStack clicked = isInWindow ? getItemStack(slot) : playerInventory.getItemStack(clickSlot); - final ItemStack cursor = getCursorItem(player); // Isn't used in the algorithm + final ItemStack cursor = playerInventory.getCursorItem(); // Isn't used in the algorithm final InventoryClickResult clickResult = clickProcessor.shiftClick( isInWindow ? this : playerInventory, isInWindow ? playerInventory : this, @@ -275,7 +258,7 @@ public boolean shiftClick(@NotNull Player player, int slot, int button) { // Mic playerInventory.setItemStack(clickSlot, clickResult.getClicked()); } updateAll(player); // FIXME: currently not properly client-predicted - this.cursorPlayersItem.put(player, clickResult.getCursor()); + playerInventory.setCursorItem(clickResult.getCursor()); return true; } @@ -299,7 +282,7 @@ public boolean changeHeld(@NotNull Player player, int slot, int key) { playerInventory.setItemStack(clickSlot, clickResult.getClicked()); } playerInventory.setItemStack(convertedKey, clickResult.getCursor()); - callClickEvent(player, isInWindow ? this : null, slot, ClickType.CHANGE_HELD, clicked, getCursorItem(player)); + callClickEvent(player, isInWindow ? this : null, slot, ClickType.CHANGE_HELD, clicked, playerInventory.getCursorItem()); return true; } @@ -318,7 +301,7 @@ public boolean drop(@NotNull Player player, boolean all, int slot, int button) { final int clickSlot = isInWindow ? slot : PlayerInventoryUtils.convertSlot(slot, offset); final ItemStack clicked = outsideDrop ? ItemStack.AIR : (isInWindow ? getItemStack(slot) : playerInventory.getItemStack(clickSlot)); - final ItemStack cursor = getCursorItem(player); + final ItemStack cursor = playerInventory.getCursorItem(); final InventoryClickResult clickResult = clickProcessor.drop(player, isInWindow ? this : playerInventory, all, clickSlot, button, clicked, cursor); if (clickResult.isCancel()) { @@ -333,7 +316,7 @@ public boolean drop(@NotNull Player player, boolean all, int slot, int button) { playerInventory.setItemStack(clickSlot, resultClicked); } } - this.cursorPlayersItem.put(player, clickResult.getCursor()); + playerInventory.setCursorItem(clickResult.getCursor()); return true; } @@ -345,7 +328,7 @@ public boolean dragging(@NotNull Player player, int slot, int button) { final ItemStack clicked = slot != -999 ? (isInWindow ? getItemStack(slot) : playerInventory.getItemStack(clickSlot)) : ItemStack.AIR; - final ItemStack cursor = getCursorItem(player); + final ItemStack cursor = playerInventory.getCursorItem(); final InventoryClickResult clickResult = clickProcessor.dragging(player, slot != -999 ? (isInWindow ? this : playerInventory) : null, clickSlot, button, @@ -354,7 +337,7 @@ public boolean dragging(@NotNull Player player, int slot, int button) { updateAll(player); return false; } - this.cursorPlayersItem.put(player, clickResult.getCursor()); + playerInventory.setCursorItem(clickResult.getCursor()); updateAll(player); // FIXME: currently not properly client-predicted return true; } @@ -367,14 +350,14 @@ public boolean doubleClick(@NotNull Player player, int slot) { final ItemStack clicked = slot != -999 ? (isInWindow ? getItemStack(slot) : playerInventory.getItemStack(clickSlot)) : ItemStack.AIR; - final ItemStack cursor = getCursorItem(player); + final ItemStack cursor = playerInventory.getCursorItem(); final InventoryClickResult clickResult = clickProcessor.doubleClick(isInWindow ? this : playerInventory, this, player, clickSlot, clicked, cursor); if (clickResult.isCancel()) { updateAll(player); return false; } - this.cursorPlayersItem.put(player, clickResult.getCursor()); + playerInventory.setCursorItem(clickResult.getCursor()); updateAll(player); // FIXME: currently not properly client-predicted return true; } diff --git a/src/main/java/net/minestom/server/inventory/PlayerInventory.java b/src/main/java/net/minestom/server/inventory/PlayerInventory.java index 52cf9cb39cf..85774354e92 100644 --- a/src/main/java/net/minestom/server/inventory/PlayerInventory.java +++ b/src/main/java/net/minestom/server/inventory/PlayerInventory.java @@ -1,6 +1,7 @@ package net.minestom.server.inventory; import net.minestom.server.entity.EquipmentSlot; +import net.minestom.server.entity.GameMode; import net.minestom.server.entity.Player; import net.minestom.server.event.EventDispatcher; import net.minestom.server.event.item.EntityEquipEvent; @@ -147,6 +148,7 @@ protected void UNSAFE_itemInsert(int slot, @NotNull ItemStack itemStack, boolean EntityEquipEvent entityEquipEvent = new EntityEquipEvent(player, itemStack, equipmentSlot); EventDispatcher.call(entityEquipEvent); itemStack = entityEquipEvent.getEquippedItem(); + this.player.updateEquipmentAttributes(this.itemStacks[slot], itemStack, equipmentSlot); } this.itemStacks[slot] = itemStack; diff --git a/src/main/java/net/minestom/server/inventory/TransactionType.java b/src/main/java/net/minestom/server/inventory/TransactionType.java index 253b4a4e389..30268d22476 100644 --- a/src/main/java/net/minestom/server/inventory/TransactionType.java +++ b/src/main/java/net/minestom/server/inventory/TransactionType.java @@ -4,7 +4,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.minestom.server.item.ItemStack; -import net.minestom.server.item.StackingRule; +import net.minestom.server.utils.MathUtils; import org.jetbrains.annotations.NotNull; import java.util.Map; @@ -21,32 +21,31 @@ public interface TransactionType { */ TransactionType ADD = (inventory, itemStack, slotPredicate, start, end, step) -> { Int2ObjectMap itemChangesMap = new Int2ObjectOpenHashMap<>(); - final StackingRule stackingRule = StackingRule.get(); // Check filled slot (not air) for (int i = start; i < end; i += step) { ItemStack inventoryItem = inventory.getItemStack(i); if (inventoryItem.isAir()) { continue; } - if (stackingRule.canBeStacked(itemStack, inventoryItem)) { - final int itemAmount = stackingRule.getAmount(inventoryItem); - final int maxSize = stackingRule.getMaxSize(inventoryItem); + if (itemStack.isSimilar(inventoryItem)) { + final int itemAmount = inventoryItem.amount(); + final int maxSize = inventoryItem.maxStackSize(); if (itemAmount >= maxSize) continue; if (!slotPredicate.test(i, inventoryItem)) { // Cancelled transaction continue; } - final int itemStackAmount = stackingRule.getAmount(itemStack); + final int itemStackAmount = itemStack.amount(); final int totalAmount = itemStackAmount + itemAmount; - if (!stackingRule.canApply(itemStack, totalAmount)) { + if (!MathUtils.isBetween(totalAmount, 0, itemStack.maxStackSize())) { // Slot cannot accept the whole item, reduce amount to 'itemStack' - itemChangesMap.put(i, stackingRule.apply(inventoryItem, maxSize)); - itemStack = stackingRule.apply(itemStack, totalAmount - maxSize); + itemChangesMap.put(i, inventoryItem.withAmount(maxSize)); + itemStack = itemStack.withAmount(totalAmount - maxSize); } else { // Slot can accept the whole item - itemChangesMap.put(i, stackingRule.apply(inventoryItem, totalAmount)); - itemStack = stackingRule.apply(itemStack, 0); + itemChangesMap.put(i, inventoryItem.withAmount(totalAmount)); + itemStack = ItemStack.AIR; break; } } @@ -59,10 +58,20 @@ public interface TransactionType { // Cancelled transaction continue; } - // Fill the slot - itemChangesMap.put(i, itemStack); - itemStack = stackingRule.apply(itemStack, 0); - break; + + final int maxSize = itemStack.maxStackSize(); + final int currentSize = itemStack.amount(); + + if (!MathUtils.isBetween(currentSize, 0, maxSize)) { + // Slot cannot accept the whole item, reduce amount to 'itemStack' + itemChangesMap.put(i, itemStack.withAmount(maxSize)); + itemStack = itemStack.withAmount(currentSize - maxSize); + } else { + // Slot can accept the whole item + itemChangesMap.put(i, itemStack.withAmount(currentSize)); + itemStack = ItemStack.AIR; + break; + } } return Pair.of(itemStack, itemChangesMap); }; @@ -73,27 +82,26 @@ public interface TransactionType { */ TransactionType TAKE = (inventory, itemStack, slotPredicate, start, end, step) -> { Int2ObjectMap itemChangesMap = new Int2ObjectOpenHashMap<>(); - final StackingRule stackingRule = StackingRule.get(); for (int i = start; i < end; i += step) { final ItemStack inventoryItem = inventory.getItemStack(i); if (inventoryItem.isAir()) continue; - if (stackingRule.canBeStacked(itemStack, inventoryItem)) { + if (itemStack.isSimilar(inventoryItem)) { if (!slotPredicate.test(i, inventoryItem)) { // Cancelled transaction continue; } - final int itemAmount = stackingRule.getAmount(inventoryItem); - final int itemStackAmount = stackingRule.getAmount(itemStack); + final int itemAmount = inventoryItem.amount(); + final int itemStackAmount = itemStack.amount(); if (itemStackAmount < itemAmount) { - itemChangesMap.put(i, stackingRule.apply(inventoryItem, itemAmount - itemStackAmount)); - itemStack = stackingRule.apply(itemStack, 0); + itemChangesMap.put(i, inventoryItem.withAmount(itemAmount - itemStackAmount)); + itemStack = ItemStack.AIR; break; } - itemChangesMap.put(i, stackingRule.apply(inventoryItem, 0)); - itemStack = stackingRule.apply(itemStack, itemStackAmount - itemAmount); - if (stackingRule.getAmount(itemStack) == 0) { - itemStack = stackingRule.apply(itemStack, 0); + itemChangesMap.put(i, ItemStack.AIR); + itemStack = itemStack.withAmount(itemStackAmount - itemAmount); + if (itemStack.amount() == 0) { + itemStack = ItemStack.AIR; break; } } diff --git a/src/main/java/net/minestom/server/inventory/click/InventoryClickProcessor.java b/src/main/java/net/minestom/server/inventory/click/InventoryClickProcessor.java index ead4ef6fed5..72f17ffaa32 100644 --- a/src/main/java/net/minestom/server/inventory/click/InventoryClickProcessor.java +++ b/src/main/java/net/minestom/server/inventory/click/InventoryClickProcessor.java @@ -13,7 +13,7 @@ import net.minestom.server.inventory.condition.InventoryConditionResult; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; -import net.minestom.server.item.StackingRule; +import net.minestom.server.utils.MathUtils; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -38,19 +38,18 @@ public final class InventoryClickProcessor { if (result.isCancel()) return result; clicked = result.getClicked(); cursor = result.getCursor(); - final StackingRule rule = StackingRule.get(); - if (rule.canBeStacked(cursor, clicked)) { + if (cursor.isSimilar(clicked)) { // Try to stack items - final int totalAmount = rule.getAmount(cursor) + rule.getAmount(clicked); - final int maxSize = rule.getMaxSize(cursor); - if (!rule.canApply(clicked, totalAmount)) { + final int totalAmount = cursor.amount() + clicked.amount(); + final int maxSize = cursor.maxStackSize(); + if (!MathUtils.isBetween(totalAmount, 0, clicked.maxStackSize())) { // Size is too big, stack as much as possible into clicked - result.setCursor(rule.apply(cursor, totalAmount - maxSize)); - result.setClicked(rule.apply(clicked, maxSize)); + result.setCursor(cursor.withAmount(totalAmount - maxSize)); + result.setClicked(clicked.withAmount(maxSize)); } else { // Merge cursor item clicked - result.setCursor(rule.apply(cursor, 0)); - result.setClicked(rule.apply(clicked, totalAmount)); + result.setCursor(ItemStack.AIR); + result.setClicked(clicked.withAmount(totalAmount)); } } else { // Items are not compatible, swap them @@ -67,30 +66,29 @@ public final class InventoryClickProcessor { if (result.isCancel()) return result; clicked = result.getClicked(); cursor = result.getCursor(); - final StackingRule rule = StackingRule.get(); - if (rule.canBeStacked(clicked, cursor)) { + if (clicked.isSimilar(cursor)) { // Items can be stacked - final int amount = rule.getAmount(clicked) + 1; - if (!rule.canApply(clicked, amount)) { + final int amount = clicked.amount() + 1; + if (!MathUtils.isBetween(amount, 0, clicked.maxStackSize())) { // Size too large, stop here return result; } else { // Add 1 to clicked - result.setCursor(rule.apply(cursor, operand -> operand - 1)); - result.setClicked(rule.apply(clicked, amount)); + result.setCursor(cursor.withAmount(operand -> operand - 1)); + result.setClicked(clicked.withAmount(amount)); } } else { // Items cannot be stacked if (cursor.isAir()) { // Take half of clicked - final int amount = (int) Math.ceil((double) rule.getAmount(clicked) / 2d); - result.setCursor(rule.apply(clicked, amount)); - result.setClicked(rule.apply(clicked, operand -> operand / 2)); + final int amount = (int) Math.ceil((double) clicked.amount() / 2d); + result.setCursor(clicked.withAmount(amount)); + result.setClicked(clicked.withAmount(operand -> operand / 2)); } else { if (clicked.isAir()) { // Put 1 to clicked - result.setCursor(rule.apply(cursor, operand -> operand - 1)); - result.setClicked(rule.apply(cursor, 1)); + result.setCursor(cursor.withAmount(operand -> operand - 1)); + result.setClicked(cursor.withAmount(1)); } else { // Swap items result.setCursor(clicked); @@ -171,7 +169,6 @@ public final class InventoryClickProcessor { int slot, int button, @NotNull ItemStack clicked, @NotNull ItemStack cursor) { InventoryClickResult clickResult = null; - final StackingRule stackingRule = StackingRule.get(); if (slot != -999) { // Add slot if (button == 1) { @@ -199,7 +196,7 @@ public final class InventoryClickProcessor { final List slots = leftDraggingMap.remove(player); if (slots == null) return null; final int slotCount = slots.size(); - final int cursorAmount = stackingRule.getAmount(cursor); + final int cursorAmount = cursor.amount(); if (slotCount > cursorAmount) return null; for (DragData s : slots) { // Apply each drag element @@ -219,29 +216,29 @@ public final class InventoryClickProcessor { final var inv = dragData.inventory; final int s = dragData.slot; ItemStack slotItem = inv.getItemStack(s); - final int amount = stackingRule.getAmount(slotItem); - if (stackingRule.canBeStacked(cursor, slotItem)) { - if (stackingRule.canApply(slotItem, amount + slotSize)) { + final int amount = slotItem.amount(); + if (cursor.isSimilar(slotItem)) { + if (MathUtils.isBetween(amount + slotSize, 0, slotItem.maxStackSize())) { // Append divided amount to slot - slotItem = stackingRule.apply(slotItem, a -> a + slotSize); + slotItem = slotItem.withAmount(a -> a + slotSize); finalCursorAmount -= slotSize; } else { // Amount too big, fill as much as possible - final int maxSize = stackingRule.getMaxSize(cursor); + final int maxSize = cursor.maxStackSize(); final int removedAmount = maxSize - amount; - slotItem = stackingRule.apply(slotItem, maxSize); + slotItem = slotItem.withAmount(maxSize); finalCursorAmount -= removedAmount; } } else if (slotItem.isAir()) { // Slot is empty, add divided amount - slotItem = stackingRule.apply(cursor, slotSize); + slotItem = cursor.withAmount(slotSize); finalCursorAmount -= slotSize; } inv.setItemStack(s, slotItem); callClickEvent(player, inv, s, ClickType.LEFT_DRAGGING, slotItem, cursor); } // Update the cursor - clickResult.setCursor(stackingRule.apply(cursor, finalCursorAmount)); + clickResult.setCursor(cursor.withAmount(finalCursorAmount)); } else if (button == 4) { // Start right clickResult = startCondition(player, inventory, slot, ClickType.START_RIGHT_DRAGGING, clicked, cursor); @@ -251,7 +248,7 @@ public final class InventoryClickProcessor { final List slots = rightDraggingMap.remove(player); if (slots == null) return null; final int size = slots.size(); - int cursorAmount = stackingRule.getAmount(cursor); + int cursorAmount = cursor.amount(); if (size > cursorAmount) return null; // Verify if each slot can be modified (or cancel the whole drag) for (DragData s : slots) { @@ -269,23 +266,23 @@ public final class InventoryClickProcessor { final var inv = dragData.inventory; final int s = dragData.slot; ItemStack slotItem = inv.getItemStack(s); - if (stackingRule.canBeStacked(cursor, slotItem)) { + if (cursor.isSimilar(slotItem)) { // Compatible item in the slot, increment by 1 - final int amount = stackingRule.getAmount(slotItem) + 1; - if (stackingRule.canApply(slotItem, amount)) { - slotItem = stackingRule.apply(slotItem, amount); + final int amount = slotItem.amount() + 1; + if (MathUtils.isBetween(amount, 0, slotItem.maxStackSize())) { + slotItem = slotItem.withAmount(amount); finalCursorAmount -= 1; } } else if (slotItem.isAir()) { // No item at the slot, place one - slotItem = stackingRule.apply(cursor, 1); + slotItem = cursor.withAmount(1); finalCursorAmount -= 1; } inv.setItemStack(s, slotItem); callClickEvent(player, inv, s, ClickType.RIGHT_DRAGGING, slotItem, cursor); } // Update the cursor - clickResult.setCursor(stackingRule.apply(cursor, finalCursorAmount)); + clickResult.setCursor(cursor.withAmount(finalCursorAmount)); } } return clickResult; @@ -297,9 +294,8 @@ public final class InventoryClickProcessor { if (clickResult.isCancel()) return clickResult; if (cursor.isAir()) return clickResult.cancelled(); - final StackingRule rule = StackingRule.get(); - final int amount = rule.getAmount(cursor); - final int maxSize = rule.getMaxSize(cursor); + final int amount = cursor.amount(); + final int maxSize = cursor.maxStackSize(); final int remainingAmount = maxSize - amount; if (remainingAmount == 0) { // Item is already full @@ -321,7 +317,7 @@ public final class InventoryClickProcessor { return itemResult; }; - ItemStack remain = rule.apply(cursor, remainingAmount); + ItemStack remain = cursor.withAmount(remainingAmount); final var playerInventory = player.getInventory(); // Retrieve remain if (Objects.equals(clickedInventory, inventory)) { @@ -344,10 +340,10 @@ public final class InventoryClickProcessor { // Update cursor based on the remaining if (remain.isAir()) { // Item has been filled - clickResult.setCursor(rule.apply(cursor, maxSize)); + clickResult.setCursor(cursor.withAmount(maxSize)); } else { - final int tookAmount = remainingAmount - rule.getAmount(remain); - clickResult.setCursor(rule.apply(cursor, amount + tookAmount)); + final int tookAmount = remainingAmount - remain.amount(); + clickResult.setCursor(cursor.withAmount(amount + tookAmount)); } return clickResult; } @@ -358,8 +354,6 @@ public final class InventoryClickProcessor { final InventoryClickResult clickResult = startCondition(player, inventory, slot, ClickType.DROP, clicked, cursor); if (clickResult.isCancel()) return clickResult; - final StackingRule rule = StackingRule.get(); - ItemStack resultClicked = clicked; ItemStack resultCursor = cursor; @@ -367,44 +361,44 @@ public final class InventoryClickProcessor { // Click outside if (button == 0) { // Left (drop all) - final int amount = rule.getAmount(resultCursor); - final ItemStack dropItem = rule.apply(resultCursor, amount); + final int amount = resultCursor.amount(); + final ItemStack dropItem = resultCursor.withAmount(amount); final boolean dropResult = player.dropItem(dropItem); clickResult.setCancel(!dropResult); if (dropResult) { - resultCursor = rule.apply(resultCursor, 0); + resultCursor = ItemStack.AIR; } } else if (button == 1) { // Right (drop 1) - final ItemStack dropItem = rule.apply(resultCursor, 1); + final ItemStack dropItem = resultCursor.withAmount(1); final boolean dropResult = player.dropItem(dropItem); clickResult.setCancel(!dropResult); if (dropResult) { - final int amount = rule.getAmount(resultCursor); + final int amount = resultCursor.amount(); final int newAmount = amount - 1; - resultCursor = rule.apply(resultCursor, newAmount); + resultCursor = resultCursor.withAmount(newAmount); } } } else if (!all) { if (button == 0) { // Drop key Q (drop 1) - final ItemStack dropItem = rule.apply(resultClicked, 1); + final ItemStack dropItem = resultClicked.withAmount(1); final boolean dropResult = player.dropItem(dropItem); clickResult.setCancel(!dropResult); if (dropResult) { - final int amount = rule.getAmount(resultClicked); + final int amount = resultClicked.amount(); final int newAmount = amount - 1; - resultClicked = rule.apply(resultClicked, newAmount); + resultClicked = resultClicked.withAmount(newAmount); } } else if (button == 1) { // Ctrl + Drop key Q (drop all) - final int amount = rule.getAmount(resultClicked); - final ItemStack dropItem = rule.apply(resultClicked, amount); + final int amount = resultClicked.amount(); + final ItemStack dropItem = resultClicked.withAmount(amount); final boolean dropResult = player.dropItem(dropItem); clickResult.setCancel(!dropResult); if (dropResult) { - resultClicked = rule.apply(resultClicked, 0); + resultClicked = ItemStack.AIR; } } } diff --git a/src/main/java/net/minestom/server/inventory/type/EnchantmentTableInventory.java b/src/main/java/net/minestom/server/inventory/type/EnchantmentTableInventory.java index e448d882bbd..5b9d217a2cc 100644 --- a/src/main/java/net/minestom/server/inventory/type/EnchantmentTableInventory.java +++ b/src/main/java/net/minestom/server/inventory/type/EnchantmentTableInventory.java @@ -1,13 +1,17 @@ package net.minestom.server.inventory.type; import net.kyori.adventure.text.Component; +import net.minestom.server.MinecraftServer; import net.minestom.server.inventory.Inventory; import net.minestom.server.inventory.InventoryProperty; import net.minestom.server.inventory.InventoryType; -import net.minestom.server.item.Enchantment; +import net.minestom.server.item.enchant.Enchantment; +import net.minestom.server.registry.DynamicRegistry; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class EnchantmentTableInventory extends Inventory { + private static final DynamicRegistry ENCHANTMENT_REGISTRY = MinecraftServer.getEnchantmentRegistry(); private final short[] levelRequirements = new short[EnchantmentSlot.values().length]; private short seed; @@ -68,11 +72,10 @@ public void setSeed(short seed) { * @param enchantmentSlot the enchantment slot * @return the enchantment shown in the slot, null if it is hidden */ - public Enchantment getEnchantmentShown(EnchantmentSlot enchantmentSlot) { - final short id = enchantmentShown[enchantmentSlot.ordinal()]; - if (id == -1) - return null; - return Enchantment.fromId(id); + public DynamicRegistry.Key getEnchantmentShown(@NotNull EnchantmentSlot enchantmentSlot) { + final int id = enchantmentShown[enchantmentSlot.ordinal()]; + if (id == -1) return null; + return ENCHANTMENT_REGISTRY.getKey(id); } /** @@ -83,8 +86,8 @@ public Enchantment getEnchantmentShown(EnchantmentSlot enchantmentSlot) { * @param enchantmentSlot the enchantment slot * @param enchantment the enchantment */ - public void setEnchantmentShown(EnchantmentSlot enchantmentSlot, Enchantment enchantment) { - final short id = enchantment == null ? -1 : (short) enchantment.id(); + public void setEnchantmentShown(@NotNull EnchantmentSlot enchantmentSlot, @Nullable DynamicRegistry.Key enchantment) { + final short id = enchantment == null ? -1 : (short) ENCHANTMENT_REGISTRY.getId(enchantment); switch (enchantmentSlot) { case TOP -> sendProperty(InventoryProperty.ENCHANTMENT_TABLE_ENCH_ID_TOP, id); case MIDDLE -> sendProperty(InventoryProperty.ENCHANTMENT_TABLE_ENCH_ID_MIDDLE, id); diff --git a/src/main/java/net/minestom/server/item/Enchantment.java b/src/main/java/net/minestom/server/item/Enchantment.java deleted file mode 100644 index 39e27ac6614..00000000000 --- a/src/main/java/net/minestom/server/item/Enchantment.java +++ /dev/null @@ -1,47 +0,0 @@ -package net.minestom.server.item; - -import net.minestom.server.registry.StaticProtocolObject; -import net.minestom.server.registry.Registry; -import net.minestom.server.utils.NamespaceID; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; - -public sealed interface Enchantment extends StaticProtocolObject, Enchantments permits EnchantmentImpl { - - /** - * Returns the enchantment registry. - * - * @return the enchantment registry - */ - @Contract(pure = true) - @NotNull Registry.EnchantmentEntry registry(); - - @Override - default @NotNull NamespaceID namespace() { - return registry().namespace(); - } - - @Override - default int id() { - return registry().id(); - } - - static @NotNull Collection<@NotNull Enchantment> values() { - return EnchantmentImpl.values(); - } - - static @Nullable Enchantment fromNamespaceId(@NotNull String namespaceID) { - return EnchantmentImpl.getSafe(namespaceID); - } - - static @Nullable Enchantment fromNamespaceId(@NotNull NamespaceID namespaceID) { - return fromNamespaceId(namespaceID.asString()); - } - - static @Nullable Enchantment fromId(int id) { - return EnchantmentImpl.getId(id); - } -} diff --git a/src/main/java/net/minestom/server/item/EnchantmentImpl.java b/src/main/java/net/minestom/server/item/EnchantmentImpl.java deleted file mode 100644 index 40823696ba6..00000000000 --- a/src/main/java/net/minestom/server/item/EnchantmentImpl.java +++ /dev/null @@ -1,35 +0,0 @@ -package net.minestom.server.item; - -import net.minestom.server.registry.Registry; -import org.jetbrains.annotations.NotNull; - -import java.util.Collection; - -record EnchantmentImpl(Registry.EnchantmentEntry registry) implements Enchantment { - private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.ENCHANTMENTS, EnchantmentImpl::createImpl); - - private static Enchantment createImpl(String namespace, Registry.Properties properties) { - return new EnchantmentImpl(Registry.enchantment(namespace, properties)); - } - - static Enchantment get(@NotNull String namespace) { - return CONTAINER.get(namespace); - } - - static Enchantment getSafe(@NotNull String namespace) { - return CONTAINER.getSafe(namespace); - } - - static Enchantment getId(int id) { - return CONTAINER.getId(id); - } - - static Collection values() { - return CONTAINER.values(); - } - - @Override - public String toString() { - return name(); - } -} diff --git a/src/main/java/net/minestom/server/item/ItemComponent.java b/src/main/java/net/minestom/server/item/ItemComponent.java new file mode 100644 index 00000000000..4c66c448d7c --- /dev/null +++ b/src/main/java/net/minestom/server/item/ItemComponent.java @@ -0,0 +1,116 @@ +package net.minestom.server.item; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.util.RGBLike; +import net.minestom.server.color.Color; +import net.minestom.server.color.DyeColor; +import net.minestom.server.component.DataComponent; +import net.minestom.server.component.DataComponentMap; +import net.minestom.server.item.component.*; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.NamespaceID; +import net.minestom.server.utils.Unit; +import net.minestom.server.utils.collection.ObjectArray; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public final class ItemComponent { + // Note that even non-networked components are registered here as they still contribute to the component ID counter. + // The order in this file determines the component protocol IDs, so it is important to match the client. + + static final Map> NAMESPACES = new HashMap<>(32); + static final ObjectArray> IDS = ObjectArray.singleThread(32); + + public static final DataComponent CUSTOM_DATA = register("custom_data", CustomData.NETWORK_TYPE, CustomData.NBT_TYPE); + public static final DataComponent MAX_STACK_SIZE = register("max_stack_size", NetworkBuffer.VAR_INT, BinaryTagSerializer.INT); + public static final DataComponent MAX_DAMAGE = register("max_damage", NetworkBuffer.VAR_INT, BinaryTagSerializer.INT); + public static final DataComponent DAMAGE = register("damage", NetworkBuffer.VAR_INT, BinaryTagSerializer.INT); + public static final DataComponent UNBREAKABLE = register("unbreakable", Unbreakable.NETWORK_TYPE, Unbreakable.NBT_TYPE); + public static final DataComponent CUSTOM_NAME = register("custom_name", NetworkBuffer.COMPONENT, BinaryTagSerializer.JSON_COMPONENT); + public static final DataComponent ITEM_NAME = register("item_name", NetworkBuffer.COMPONENT, BinaryTagSerializer.JSON_COMPONENT); + public static final DataComponent> LORE = register("lore", NetworkBuffer.COMPONENT.list(256), BinaryTagSerializer.JSON_COMPONENT.list()); + public static final DataComponent RARITY = register("rarity", ItemRarity.NETWORK_TYPE, ItemRarity.NBT_TYPE); + public static final DataComponent ENCHANTMENTS = register("enchantments", EnchantmentList.NETWORK_TYPE, EnchantmentList.NBT_TYPE); + public static final DataComponent CAN_PLACE_ON = register("can_place_on", BlockPredicates.NETWORK_TYPE, BlockPredicates.NBT_TYPE); + public static final DataComponent CAN_BREAK = register("can_break", BlockPredicates.NETWORK_TYPE, BlockPredicates.NBT_TYPE); + public static final DataComponent ATTRIBUTE_MODIFIERS = register("attribute_modifiers", AttributeList.NETWORK_TYPE, AttributeList.NBT_TYPE); + public static final DataComponent CUSTOM_MODEL_DATA = register("custom_model_data", NetworkBuffer.VAR_INT, BinaryTagSerializer.INT); + public static final DataComponent HIDE_ADDITIONAL_TOOLTIP = register("hide_additional_tooltip", NetworkBuffer.UNIT, BinaryTagSerializer.UNIT); + public static final DataComponent HIDE_TOOLTIP = register("hide_tooltip", NetworkBuffer.UNIT, BinaryTagSerializer.UNIT); + public static final DataComponent REPAIR_COST = register("repair_cost", NetworkBuffer.VAR_INT, BinaryTagSerializer.INT); + public static final DataComponent CREATIVE_SLOT_LOCK = register("creative_slot_lock", NetworkBuffer.UNIT, null); + public static final DataComponent ENCHANTMENT_GLINT_OVERRIDE = register("enchantment_glint_override", NetworkBuffer.BOOLEAN, BinaryTagSerializer.BOOLEAN); + public static final DataComponent INTANGIBLE_PROJECTILE = register("intangible_projectile", null, BinaryTagSerializer.UNIT); + public static final DataComponent FOOD = register("food", Food.NETWORK_TYPE, Food.NBT_TYPE); + public static final DataComponent FIRE_RESISTANT = register("fire_resistant", NetworkBuffer.UNIT, BinaryTagSerializer.UNIT); + public static final DataComponent TOOL = register("tool", Tool.NETWORK_TYPE, Tool.NBT_TYPE); + public static final DataComponent STORED_ENCHANTMENTS = register("stored_enchantments", EnchantmentList.NETWORK_TYPE, EnchantmentList.NBT_TYPE); + public static final DataComponent DYED_COLOR = register("dyed_color", DyedItemColor.NETWORK_TYPE, DyedItemColor.NBT_TYPE); + public static final DataComponent MAP_COLOR = register("map_color", Color.NETWORK_TYPE, Color.NBT_TYPE); + public static final DataComponent MAP_ID = register("map_id", NetworkBuffer.VAR_INT, BinaryTagSerializer.INT); + public static final DataComponent MAP_DECORATIONS = register("map_decorations", null, MapDecorations.NBT_TYPE); + public static final DataComponent MAP_POST_PROCESSING = register("map_post_processing", MapPostProcessing.NETWORK_TYPE, null); + public static final DataComponent> CHARGED_PROJECTILES = register("charged_projectiles", ItemStack.NETWORK_TYPE.list(Short.MAX_VALUE), BinaryTagSerializer.ITEM.list()); + public static final DataComponent> BUNDLE_CONTENTS = register("bundle_contents", ItemStack.NETWORK_TYPE.list(Short.MAX_VALUE), BinaryTagSerializer.ITEM.list()); + public static final DataComponent POTION_CONTENTS = register("potion_contents", PotionContents.NETWORK_TYPE, PotionContents.NBT_TYPE); + public static final DataComponent SUSPICIOUS_STEW_EFFECTS = register("suspicious_stew_effects", SuspiciousStewEffects.NETWORK_TYPE, SuspiciousStewEffects.NBT_TYPE); + public static final DataComponent WRITABLE_BOOK_CONTENT = register("writable_book_content", WritableBookContent.NETWORK_TYPE, WritableBookContent.NBT_TYPE); + public static final DataComponent WRITTEN_BOOK_CONTENT = register("written_book_content", WrittenBookContent.NETWORK_TYPE, WrittenBookContent.NBT_TYPE); + public static final DataComponent TRIM = register("trim", ArmorTrim.NETWORK_TYPE, ArmorTrim.NBT_TYPE); + public static final DataComponent DEBUG_STICK_STATE = register("debug_stick_state", null, DebugStickState.NBT_TYPE); + public static final DataComponent ENTITY_DATA = register("entity_data", CustomData.NETWORK_TYPE, CustomData.NBT_TYPE); + public static final DataComponent BUCKET_ENTITY_DATA = register("bucket_entity_data", CustomData.NETWORK_TYPE, CustomData.NBT_TYPE); + public static final DataComponent BLOCK_ENTITY_DATA = register("block_entity_data", CustomData.NETWORK_TYPE, CustomData.NBT_TYPE); + public static final DataComponent INSTRUMENT = register("instrument", NetworkBuffer.STRING, BinaryTagSerializer.STRING); + public static final DataComponent OMINOUS_BOTTLE_AMPLIFIER = register("ominous_bottle_amplifier", NetworkBuffer.VAR_INT, BinaryTagSerializer.INT); + public static final DataComponent JUKEBOX_PLAYABLE = register("jukebox_playable", JukeboxPlayable.NETWORK_TYPE, JukeboxPlayable.NBT_TYPE); + public static final DataComponent> RECIPES = register("recipes", NetworkBuffer.STRING.list(Short.MAX_VALUE), BinaryTagSerializer.STRING.list()); + public static final DataComponent LODESTONE_TRACKER = register("lodestone_tracker", LodestoneTracker.NETWORK_TYPE, LodestoneTracker.NBT_TYPE); + public static final DataComponent FIREWORK_EXPLOSION = register("firework_explosion", FireworkExplosion.NETWORK_TYPE, FireworkExplosion.NBT_TYPE); + public static final DataComponent FIREWORKS = register("fireworks", FireworkList.NETWORK_TYPE, FireworkList.NBT_TYPE); + public static final DataComponent PROFILE = register("profile", HeadProfile.NETWORK_TYPE, HeadProfile.NBT_TYPE); + public static final DataComponent NOTE_BLOCK_SOUND = register("note_block_sound", NetworkBuffer.STRING, BinaryTagSerializer.STRING); + public static final DataComponent BANNER_PATTERNS = register("banner_patterns", BannerPatterns.NETWORK_TYPE, BannerPatterns.NBT_TYPE); + public static final DataComponent BASE_COLOR = register("base_color", DyeColor.NETWORK_TYPE, DyeColor.NBT_TYPE); + public static final DataComponent POT_DECORATIONS = register("pot_decorations", PotDecorations.NETWORK_TYPE, PotDecorations.NBT_TYPE); + public static final DataComponent> CONTAINER = register("container", ItemStack.NETWORK_TYPE.list(256), BinaryTagSerializer.ITEM.list()); + public static final DataComponent BLOCK_STATE = register("block_state", ItemBlockState.NETWORK_TYPE, ItemBlockState.NBT_TYPE); + public static final DataComponent> BEES = register("bees", Bee.NETWORK_TYPE.list(Short.MAX_VALUE), Bee.NBT_TYPE.list()); + public static final DataComponent LOCK = register("lock", null, BinaryTagSerializer.STRING); + public static final DataComponent CONTAINER_LOOT = register("container_loot", null, SeededContainerLoot.NBT_TYPE); + + public static final NetworkBuffer.Type PATCH_NETWORK_TYPE = DataComponentMap.patchNetworkType(ItemComponent::fromId); + public static final BinaryTagSerializer PATCH_NBT_TYPE = DataComponentMap.patchNbtType(ItemComponent::fromId, ItemComponent::fromNamespaceId); + + public static @Nullable DataComponent fromNamespaceId(@NotNull String namespaceId) { + return NAMESPACES.get(namespaceId); + } + + public static @Nullable DataComponent fromNamespaceId(@NotNull NamespaceID namespaceId) { + return fromNamespaceId(namespaceId.asString()); + } + + public static @Nullable DataComponent fromId(int id) { + return IDS.get(id); + } + + public static @NotNull Collection> values() { + return NAMESPACES.values(); + } + + static DataComponent register(@NotNull String name, @Nullable NetworkBuffer.Type network, @Nullable BinaryTagSerializer nbt) { + DataComponent impl = DataComponent.createHeadless(NAMESPACES.size(), NamespaceID.from(name), network, nbt); + NAMESPACES.put(impl.name(), impl); + IDS.set(impl.id(), impl); + return impl; + } + + private ItemComponent() { + } +} diff --git a/src/main/java/net/minestom/server/item/ItemHideFlag.java b/src/main/java/net/minestom/server/item/ItemHideFlag.java deleted file mode 100644 index 92087b07255..00000000000 --- a/src/main/java/net/minestom/server/item/ItemHideFlag.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.minestom.server.item; - -/** - * Represents a hide flag which can be applied to an {@link ItemStack} using {@link ItemMeta.Builder#hideFlag(int)}. - */ -public enum ItemHideFlag { - HIDE_ENCHANTS, - HIDE_ATTRIBUTES, - HIDE_UNBREAKABLE, - HIDE_DESTROYS, - HIDE_PLACED_ON, - HIDE_POTION_EFFECTS, - HIDE_DYE; - - private final int bitFieldPart = 1 << this.ordinal(); - - public int getBitFieldPart() { - return bitFieldPart; - } -} diff --git a/src/main/java/net/minestom/server/item/ItemMeta.java b/src/main/java/net/minestom/server/item/ItemMeta.java deleted file mode 100644 index 3aa718fa060..00000000000 --- a/src/main/java/net/minestom/server/item/ItemMeta.java +++ /dev/null @@ -1,186 +0,0 @@ -package net.minestom.server.item; - -import net.kyori.adventure.text.Component; -import net.minestom.server.instance.block.Block; -import net.minestom.server.item.attribute.ItemAttribute; -import net.minestom.server.network.NetworkBuffer; -import net.minestom.server.registry.StaticProtocolObject; -import net.minestom.server.tag.Tag; -import net.minestom.server.tag.TagReadable; -import net.minestom.server.tag.Taggable; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.UnknownNullability; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; - -import java.util.*; -import java.util.function.Consumer; - -public sealed interface ItemMeta extends TagReadable, NetworkBuffer.Writer - permits ItemMetaImpl { - - @Override - @UnknownNullability T getTag(@NotNull Tag tag); - - @Contract(value = "_, -> new", pure = true) - @NotNull ItemMeta with(@NotNull Consumer<@NotNull Builder> builderConsumer); - - @NotNull NBTCompound toNBT(); - - @NotNull String toSNBT(); - - @Contract(pure = true) - default int getDamage() { - return getTag(ItemTags.DAMAGE); - } - - @Contract(pure = true) - default boolean isUnbreakable() { - return getTag(ItemTags.UNBREAKABLE); - } - - @Contract(pure = true) - default int getHideFlag() { - return getTag(ItemTags.HIDE_FLAGS); - } - - @Contract(pure = true) - default @Nullable Component getDisplayName() { - return getTag(ItemTags.DISPLAY_NAME); // Microtus - Rename name tag to DISPLAY_NAME for consistency - } - - @Contract(pure = true) - default @NotNull List<@NotNull Component> getLore() { - return getTag(ItemTags.LORE); - } - - @Contract(pure = true) - default @NotNull Map getEnchantmentMap() { - return getTag(ItemTags.ENCHANTMENTS); - } - - @Contract(pure = true) - default @NotNull List<@NotNull ItemAttribute> getAttributes() { - return getTag(ItemTags.ATTRIBUTES); - } - - @Contract(pure = true) - default int getCustomModelData() { - return getTag(ItemTags.CUSTOM_MODEL_DATA); - } - - @Contract(pure = true) - default @NotNull Set<@NotNull String> getCanDestroy() { - return Set.copyOf(getTag(ItemTags.CAN_DESTROY)); - } - - @Contract(pure = true) - default boolean canDestroy(@NotNull Block block) { - return getCanDestroy().contains(block.name()); - } - - @Contract(pure = true) - default @NotNull Set<@NotNull String> getCanPlaceOn() { - return Set.copyOf(getTag(ItemTags.CAN_PLACE_ON)); - } - - @Contract(pure = true) - default boolean canPlaceOn(@NotNull Block block) { - return getCanPlaceOn().contains(block.name()); - } - - sealed interface Builder extends Taggable - permits ItemMetaImpl.Builder, ItemMetaView.Builder { - @NotNull ItemMeta build(); - - default @NotNull Builder set(@NotNull Tag tag, @Nullable T value) { - setTag(tag, value); - return this; - } - - @Contract("_ -> this") - default @NotNull Builder damage(int damage) { - return set(ItemTags.DAMAGE, damage); - } - - @Contract("_ -> this") - default @NotNull Builder unbreakable(boolean unbreakable) { - return set(ItemTags.UNBREAKABLE, unbreakable); - } - - @Contract("_ -> this") - default @NotNull Builder hideFlag(int hideFlag) { - return set(ItemTags.HIDE_FLAGS, hideFlag); - } - - @Contract("_ -> this") - default @NotNull Builder hideFlag(@NotNull ItemHideFlag... hideFlags) { - int result = 0; - for (ItemHideFlag hideFlag : hideFlags) result |= hideFlag.getBitFieldPart(); - return hideFlag(result); - } - - @Contract("_ -> this") - default @NotNull Builder displayName(@Nullable Component displayName) { - return set(ItemTags.DISPLAY_NAME, displayName); - } - - @Contract("_ -> this") - default @NotNull Builder lore(@NotNull List lore) { - return set(ItemTags.LORE, lore.isEmpty() ? null : List.class.cast(lore)); - } - - @Contract("_ -> this") - default @NotNull Builder lore(Component... lore) { - return lore(Arrays.asList(lore)); - } - - @Contract("_ -> this") - default @NotNull Builder enchantments(@NotNull Map enchantments) { - return set(ItemTags.ENCHANTMENTS, Map.copyOf(enchantments)); - } - - @Contract("_, _ -> this") - default @NotNull Builder enchantment(@NotNull Enchantment enchantment, short level) { - var enchantments = new HashMap<>(getTag(ItemTags.ENCHANTMENTS)); - enchantments.put(enchantment, level); - return enchantments(enchantments); - } - - @Contract("-> this") - default @NotNull Builder clearEnchantment() { - return enchantments(Map.of()); - } - - @Contract("_ -> this") - default @NotNull Builder attributes(@NotNull List<@NotNull ItemAttribute> attributes) { - return set(ItemTags.ATTRIBUTES, attributes.isEmpty() ? null : attributes); - } - - @Contract("_ -> this") - default @NotNull Builder customModelData(int customModelData) { - return set(ItemTags.CUSTOM_MODEL_DATA, customModelData); - } - - @Contract("_ -> this") - default @NotNull Builder canPlaceOn(@NotNull Set<@NotNull Block> blocks) { - return set(ItemTags.CAN_PLACE_ON, blocks.stream().map(StaticProtocolObject::name).toList()); - } - - @Contract("_ -> this") - default @NotNull Builder canPlaceOn(@NotNull Block... blocks) { - return canPlaceOn(Set.of(blocks)); - } - - @Contract("_ -> this") - default @NotNull Builder canDestroy(@NotNull Set<@NotNull Block> blocks) { - return set(ItemTags.CAN_DESTROY, blocks.stream().map(StaticProtocolObject::name).toList()); - } - - @Contract("_ -> this") - default @NotNull Builder canDestroy(@NotNull Block... blocks) { - return canDestroy(Set.of(blocks)); - } - } -} diff --git a/src/main/java/net/minestom/server/item/ItemMetaImpl.java b/src/main/java/net/minestom/server/item/ItemMetaImpl.java deleted file mode 100644 index 22483b9e555..00000000000 --- a/src/main/java/net/minestom/server/item/ItemMetaImpl.java +++ /dev/null @@ -1,74 +0,0 @@ -package net.minestom.server.item; - -import net.minestom.server.network.NetworkBuffer; -import net.minestom.server.tag.Tag; -import net.minestom.server.tag.TagHandler; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.UnknownNullability; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; - -import java.util.Objects; -import java.util.function.Consumer; - -import static net.minestom.server.network.NetworkBuffer.BYTE; -import static net.minestom.server.network.NetworkBuffer.NBT; - -record ItemMetaImpl(TagHandler tagHandler) implements ItemMeta { - static final ItemMetaImpl EMPTY = new ItemMetaImpl(TagHandler.newHandler()); - - @Override - public @UnknownNullability T getTag(@NotNull Tag tag) { - return tagHandler.getTag(tag); - } - - @Override - public @NotNull ItemMeta with(@NotNull Consumer builderConsumer) { - Builder builder = new Builder(tagHandler.copy()); - builderConsumer.accept(builder); - return builder.build(); - } - - @Override - public @NotNull NBTCompound toNBT() { - return tagHandler.asCompound(); - } - - @Override - public @NotNull String toSNBT() { - return toNBT().toSNBT(); - } - - @Override - public void write(@NotNull NetworkBuffer writer) { - final NBTCompound nbt = toNBT(); - if (nbt.isEmpty()) { - writer.write(BYTE, (byte) 0); - return; - } - writer.write(NBT, nbt); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof ItemMetaImpl itemMeta)) return false; - return toNBT().equals(itemMeta.toNBT()); - } - - @Override - public int hashCode() { - return Objects.hash(toNBT()); - } - - @Override - public String toString() { - return toSNBT(); - } - - record Builder(TagHandler tagHandler) implements ItemMeta.Builder { - @Override - public @NotNull ItemMetaImpl build() { - return new ItemMetaImpl(tagHandler.copy()); - } - } -} diff --git a/src/main/java/net/minestom/server/item/ItemMetaView.java b/src/main/java/net/minestom/server/item/ItemMetaView.java deleted file mode 100644 index 53232d3f749..00000000000 --- a/src/main/java/net/minestom/server/item/ItemMetaView.java +++ /dev/null @@ -1,16 +0,0 @@ -package net.minestom.server.item; - -import net.minestom.server.tag.TagReadable; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; - -@SuppressWarnings("ALL") -@ApiStatus.Experimental -public interface ItemMetaView extends TagReadable { - @ApiStatus.Experimental - non-sealed interface Builder extends ItemMeta.Builder { - default @NotNull ItemMeta build() { - return new ItemMetaImpl(tagHandler().copy()); - } - } -} diff --git a/src/main/java/net/minestom/server/item/ItemMetaViewImpl.java b/src/main/java/net/minestom/server/item/ItemMetaViewImpl.java deleted file mode 100644 index 0ec8d738693..00000000000 --- a/src/main/java/net/minestom/server/item/ItemMetaViewImpl.java +++ /dev/null @@ -1,37 +0,0 @@ -package net.minestom.server.item; - -import net.minestom.server.tag.TagHandler; -import net.minestom.server.tag.TagReadable; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; - -final class ItemMetaViewImpl { - static > Class viewType(Class metaClass) { - final Type type = metaClass.getGenericInterfaces()[0]; - return (Class) ((ParameterizedType) type).getActualTypeArguments()[0]; - } - - static > T construct(Class metaClass, TagReadable tagReadable) { - try { - final Constructor cons = metaClass.getDeclaredConstructor(TagReadable.class); - return cons.newInstance(tagReadable); - } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | - IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - static > V constructBuilder(Class metaClass, TagHandler tagHandler) { - final Class clazz = viewType(metaClass); - try { - final Constructor cons = clazz.getDeclaredConstructor(TagHandler.class); - return cons.newInstance(tagHandler); - } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | - IllegalAccessException e) { - throw new RuntimeException(e); - } - } -} diff --git a/src/main/java/net/minestom/server/item/ItemSerializers.java b/src/main/java/net/minestom/server/item/ItemSerializers.java deleted file mode 100644 index 06640681948..00000000000 --- a/src/main/java/net/minestom/server/item/ItemSerializers.java +++ /dev/null @@ -1,89 +0,0 @@ -package net.minestom.server.item; - -import net.minestom.server.MinecraftServer; -import net.minestom.server.attribute.Attribute; -import net.minestom.server.attribute.AttributeImpl; -import net.minestom.server.attribute.AttributeOperation; -import net.minestom.server.item.attribute.AttributeSlot; -import net.minestom.server.item.attribute.ItemAttribute; -import net.minestom.server.tag.Tag; -import net.minestom.server.tag.TagReadable; -import net.minestom.server.tag.TagSerializer; -import net.minestom.server.tag.TagWritable; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Locale; -import java.util.UUID; - -@ApiStatus.Internal -public final class ItemSerializers { - public static final TagSerializer ENCHANTMENT_SERIALIZER = new TagSerializer<>() { - static final Tag LEVEL = Tag.Short("lvl"); - static final Tag ID = Tag.String("id"); - - @Override - public @Nullable EnchantmentEntry read(@NotNull TagReadable reader) { - final String id = reader.getTag(ID); - final Short level = reader.getTag(LEVEL); - if (id == null || level == null) return null; - final Enchantment enchantment = Enchantment.fromNamespaceId(id); - return new EnchantmentEntry(enchantment, level); - } - - @Override - public void write(@NotNull TagWritable writer, @NotNull EnchantmentEntry value) { - writer.setTag(ID, value.enchantment.name()); - writer.setTag(LEVEL, value.level); - } - }; - - public record EnchantmentEntry(Enchantment enchantment, short level) { - } - - static final TagSerializer ATTRIBUTE_SERIALIZER = new TagSerializer<>() { - static final Tag ID = Tag.UUID("UUID"); - static final Tag AMOUNT = Tag.Double("Amount"); - static final Tag SLOT = Tag.String("Slot").defaultValue("mainhand"); - static final Tag ATTRIBUTE_NAME = Tag.String("AttributeName"); - static final Tag OPERATION = Tag.Integer("Operation"); - static final Tag NAME = Tag.String("Name"); - - @Override - public @Nullable ItemAttribute read(@NotNull TagReadable reader) { - final UUID uuid = reader.getTag(ID); - final double amount = reader.getTag(AMOUNT); - final String slot = reader.getTag(SLOT); - final String attributeName = reader.getTag(ATTRIBUTE_NAME); - final int operation = reader.getTag(OPERATION); - final String name = reader.getTag(NAME); - - final Attribute attribute = MinecraftServer.getAttributeManager().getByName(attributeName.toLowerCase(Locale.ROOT)); - // Wrong attribute name, stop here - if (attribute == null) return null; - final AttributeOperation attributeOperation = AttributeOperation.fromId(operation); - // Wrong attribute operation, stop here - if (attributeOperation == null) return null; - - // Find slot, default to the main hand if the nbt tag is invalid - AttributeSlot attributeSlot; - try { - attributeSlot = AttributeSlot.valueOf(slot.toUpperCase()); - } catch (IllegalArgumentException e) { - attributeSlot = AttributeSlot.MAINHAND; - } - return new ItemAttribute(uuid, name, attribute, attributeOperation, amount, attributeSlot); - } - - @Override - public void write(@NotNull TagWritable writer, @NotNull ItemAttribute value) { - writer.setTag(ID, value.uuid()); - writer.setTag(AMOUNT, value.amount()); - writer.setTag(SLOT, value.slot().name().toLowerCase(Locale.ROOT)); - writer.setTag(ATTRIBUTE_NAME, value.attribute().namespace().toString()); - writer.setTag(OPERATION, value.operation().getId()); - writer.setTag(NAME, value.name()); - } - }; -} diff --git a/src/main/java/net/minestom/server/item/ItemStack.java b/src/main/java/net/minestom/server/item/ItemStack.java index 9549cffaa81..9a6a61ac2dc 100644 --- a/src/main/java/net/minestom/server/item/ItemStack.java +++ b/src/main/java/net/minestom/server/item/ItemStack.java @@ -1,18 +1,27 @@ package net.minestom.server.item; +import net.kyori.adventure.nbt.CompoundBinaryTag; import net.kyori.adventure.nbt.api.BinaryTagHolder; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.event.HoverEventSource; +import net.minestom.server.MinecraftServer; import net.minestom.server.adventure.MinestomAdventure; +import net.minestom.server.component.DataComponent; +import net.minestom.server.component.DataComponentMap; +import net.minestom.server.item.component.CustomData; +import net.minestom.server.network.NetworkBuffer; import net.minestom.server.tag.Tag; -import net.minestom.server.tag.TagHandler; import net.minestom.server.tag.TagReadable; -import net.minestom.server.tag.TagWritable; +import net.minestom.server.utils.Unit; +import net.minestom.server.utils.nbt.BinaryTagSerializer; import net.minestom.server.utils.validate.Check; -import org.jetbrains.annotations.*; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnknownNullability; +import java.io.IOException; import java.util.List; import java.util.function.Consumer; import java.util.function.IntUnaryOperator; @@ -24,8 +33,40 @@ *

* An item stack cannot be null, {@link ItemStack#AIR} should be used instead. */ -public sealed interface ItemStack extends TagReadable, HoverEventSource +public sealed interface ItemStack extends TagReadable, DataComponent.Holder, HoverEventSource permits ItemStackImpl { + + @NotNull NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, ItemStack value) { + if (value.isAir()) { + buffer.write(NetworkBuffer.VAR_INT, 0); + return; + } + + buffer.write(NetworkBuffer.VAR_INT, value.amount()); + buffer.write(NetworkBuffer.VAR_INT, value.material().id()); + buffer.write(ItemComponent.PATCH_NETWORK_TYPE, ((ItemStackImpl) value).components()); + } + + @Override + public ItemStack read(@NotNull NetworkBuffer buffer) { + int amount = buffer.read(NetworkBuffer.VAR_INT); + if (amount <= 0) return ItemStack.AIR; + Material material = Material.fromId(buffer.read(NetworkBuffer.VAR_INT)); + DataComponentMap components = buffer.read(ItemComponent.PATCH_NETWORK_TYPE); + return ItemStackImpl.create(material, amount, components); + } + }; + @NotNull NetworkBuffer.Type STRICT_NETWORK_TYPE = NETWORK_TYPE.map(itemStack -> { + Check.argCondition(itemStack.amount() == 0 || itemStack.isAir(), "ItemStack cannot be empty"); + return itemStack; + }, itemStack -> { + Check.argCondition(itemStack.amount() == 0 || itemStack.isAir(), "ItemStack cannot be empty"); + return itemStack; + }); + @NotNull BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map(ItemStackImpl::fromCompound, ItemStackImpl::toCompound); + /** * Constant AIR item. Should be used instead of 'null'. */ @@ -36,43 +77,34 @@ public sealed interface ItemStack extends TagReadable, HoverEventSource> @NotNull T meta(@NotNull Class metaClass); - @Contract(value = "_, -> new", pure = true) @NotNull ItemStack with(@NotNull Consumer<@NotNull Builder> consumer); - @Contract(value = "_, _ -> new", pure = true) - @ApiStatus.Experimental - > @NotNull ItemStack withMeta(@NotNull Class metaType, - @NotNull Consumer consumer); - - @Contract(value = "_ -> new", pure = true) - @NotNull ItemStack withMeta(@NotNull Consumer consumer); - @Contract(value = "_, -> new", pure = true) @NotNull ItemStack withMaterial(@NotNull Material material); @@ -110,119 +127,202 @@ public sealed interface ItemStack extends TagReadable, HoverEventSourceReturns a new ItemStack with the given component set to the given value.

+ * + *

Note: this should not be used to remove components, see {@link #without(DataComponent)}.

+ */ + @Contract(value = "_, _ -> new", pure = true) + @NotNull ItemStack with(@NotNull DataComponent component, @NotNull T value); - @Contract(pure = true) - default @Nullable Component getDisplayName() { - return meta().getDisplayName(); + /** + * Returns a new ItemStack with the given {@link Unit} component applied. + * + * @param component The unit component to apply + * @return A new ItemStack with the given component applied + */ + @Contract(value = "_ -> new", pure = true) + default @NotNull ItemStack with(@NotNull DataComponent component) { + return with(component, Unit.INSTANCE); } - @Contract(pure = true) - default @NotNull List<@NotNull Component> getLore() { - return meta().getLore(); + /** + * Applies a transformation to the value of a component, only if present. + * + * @param component The component type to modify + * @param operator The transformation function + * @return A new ItemStack if the component was transformed, otherwise this. + * @param The component type + */ + default @NotNull ItemStack with(@NotNull DataComponent component, @NotNull UnaryOperator operator) { + T value = get(component); + if (value == null) return this; + return with(component, operator.apply(value)); } - @ApiStatus.Experimental - @Contract(value = "_ -> new", pure = true) - @NotNull ItemStack withMeta(@NotNull ItemMeta meta); + /** + *

Removes the given component from this item. This will explicitly remove the component from the item, as opposed + * to reverting back to the default.

+ * + *

For example, if {@link ItemComponent#FOOD} is applied to an apple, and then this method is called, + * the resulting itemstack will not be a food item at all, as opposed to returning to the default apple + * food type. Likewise, if this method is called on a default apple, it will no longer be a food item.

+ * + * @param component The component to remove + * @return A new ItemStack without the given component + */ + @Contract(value = "_, -> new", pure = true) + @NotNull ItemStack without(@NotNull DataComponent component); @Contract(value = "_, -> new", pure = true) - default @NotNull ItemStack withDisplayName(@Nullable Component displayName) { - return withMeta(builder -> builder.displayName(displayName)); + default @NotNull ItemStack withCustomName(@NotNull Component customName) { + return with(ItemComponent.CUSTOM_NAME, customName); } @Contract(value = "_, -> new", pure = true) - default @NotNull ItemStack withDisplayName(@NotNull UnaryOperator<@Nullable Component> componentUnaryOperator) { - return withDisplayName(componentUnaryOperator.apply(getDisplayName())); + default @NotNull ItemStack withLore(@NotNull Component... lore) { + return with(ItemComponent.LORE, List.of(lore)); } @Contract(value = "_, -> new", pure = true) - default @NotNull ItemStack withLore(@NotNull List lore) { - return withMeta(builder -> builder.lore(lore)); + default @NotNull ItemStack withLore(@NotNull List lore) { + return with(ItemComponent.LORE, lore); } - @Contract(value = "_, -> new", pure = true) - default @NotNull ItemStack withLore(@NotNull UnaryOperator<@NotNull List<@NotNull Component>> loreUnaryOperator) { - return withLore(loreUnaryOperator.apply(getLore())); + @Contract(value = "_ -> new", pure = true) + default @NotNull ItemStack withCustomModelData(int customModelData) { + return with(ItemComponent.CUSTOM_MODEL_DATA, customModelData); } - @Contract(pure = true) - default boolean isAir() { - return material() == Material.AIR; + @Contract(value = "_ -> new", pure = true) + default @NotNull ItemStack withGlowing(boolean glowing) { + return with(ItemComponent.ENCHANTMENT_GLINT_OVERRIDE, glowing); + } + + @Contract(value = "-> new", pure = true) + default @NotNull ItemStack withoutExtraTooltip() { + return builder().hideExtraTooltip().build(); } @Contract(pure = true) - boolean isSimilar(@NotNull ItemStack itemStack); + default int maxStackSize() { + return get(ItemComponent.MAX_STACK_SIZE, 64); + } + + @Contract(value = "_ -> new", pure = true) + default @NotNull ItemStack withMaxStackSize(int maxStackSize) { + return with(ItemComponent.MAX_STACK_SIZE, maxStackSize); + } @Contract(value = "_, _ -> new", pure = true) default @NotNull ItemStack withTag(@NotNull Tag tag, @Nullable T value) { - return withMeta(builder -> builder.set(tag, value)); + return with(ItemComponent.CUSTOM_DATA, get(ItemComponent.CUSTOM_DATA, CustomData.EMPTY).withTag(tag, value)); } @Override + @Contract(pure = true) default @UnknownNullability T getTag(@NotNull Tag tag) { - return meta().getTag(tag); + return get(ItemComponent.CUSTOM_DATA, CustomData.EMPTY).getTag(tag); } - @Override - default @NotNull HoverEvent asHoverEvent(@NotNull UnaryOperator op) { - final BinaryTagHolder tagHolder = BinaryTagHolder.encode(meta().toNBT(), MinestomAdventure.NBT_CODEC); - return HoverEvent.showItem(op.apply(HoverEvent.ShowItem.showItem(material(), amount(), tagHolder))); + @Contract(value = "_, -> new", pure = true) + @NotNull ItemStack consume(int amount); + + @Contract(pure = true) + default boolean isAir() { + return material() == Material.AIR; } + @Contract(pure = true) + boolean isSimilar(@NotNull ItemStack itemStack); + /** - * Converts this item to an NBT tag containing the id (material), count (amount), and tag (meta) + * Converts this itemstack back into a builder (starting from the current state). + * @return this itemstack, as a builder. + */ + @NotNull ItemStack.Builder builder(); + + /** + * Converts this item to an NBT tag containing the id (material), count (amount), and components (diff) * * @return The nbt representation of the item */ - @ApiStatus.Experimental - @NotNull NBTCompound toItemNBT(); + @NotNull CompoundBinaryTag toItemNBT(); - sealed interface Builder extends TagWritable - permits ItemStackImpl.Builder { - @Contract(value = "_ -> this") - @NotNull Builder amount(int amount); + @Override + default @NotNull HoverEvent asHoverEvent(@NotNull UnaryOperator op) { + try { + BinaryTagHolder tagHolder = BinaryTagHolder.encode((CompoundBinaryTag) NBT_TYPE.write(this), MinestomAdventure.NBT_CODEC); + return HoverEvent.showItem(op.apply(HoverEvent.ShowItem.showItem(material(), amount(), tagHolder))); + } catch (IOException e) { + throw new RuntimeException("failed to encode itemstack nbt", e); + } + } + + sealed interface Builder permits ItemStackImpl.Builder { @Contract(value = "_ -> this") - @NotNull Builder meta(@NotNull TagHandler tagHandler); + @NotNull Builder material(@NotNull Material material); @Contract(value = "_ -> this") - @NotNull Builder meta(@NotNull NBTCompound compound); + @NotNull Builder amount(int amount); + + @Contract(value = "_, _ -> this") + @NotNull Builder set(@NotNull DataComponent component, T value); @Contract(value = "_ -> this") - @NotNull Builder meta(@NotNull ItemMeta itemMeta); + default @NotNull Builder set(@NotNull DataComponent component) { + return set(component, Unit.INSTANCE); + } @Contract(value = "_ -> this") - @NotNull Builder meta(@NotNull Consumer consumer); + @NotNull Builder remove(@NotNull DataComponent component); - @Contract(value = "_, _ -> this") - > @NotNull Builder meta(@NotNull Class metaType, - @NotNull Consumer<@NotNull V> itemMetaConsumer); + default @NotNull Builder customName(@NotNull Component customName) { + return set(ItemComponent.CUSTOM_NAME, customName); + } - @Contract(value = "-> new", pure = true) - @NotNull ItemStack build(); + default @NotNull Builder lore(@NotNull Component... lore) { + return set(ItemComponent.LORE, List.of(lore)); + } - @Contract(value = "_, _ -> this") - default @NotNull Builder set(@NotNull Tag tag, @Nullable T value) { - setTag(tag, value); - return this; + default @NotNull Builder lore(@NotNull List lore) { + return set(ItemComponent.LORE, lore); } - @Contract(value = "_ -> this") - default @NotNull Builder displayName(@Nullable Component displayName) { - return meta(builder -> builder.displayName(displayName)); + default @NotNull Builder customModelData(int customModelData) { + return set(ItemComponent.CUSTOM_MODEL_DATA, customModelData); } - @Contract(value = "_ -> this") - default @NotNull Builder lore(@NotNull List lore) { - return meta(builder -> builder.lore(lore)); + default @NotNull Builder glowing() { + return set(ItemComponent.ENCHANTMENT_GLINT_OVERRIDE, true); } - @Contract(value = "_ -> this") - default @NotNull Builder lore(Component... lore) { - return meta(builder -> builder.lore(lore)); + default @NotNull Builder glowing(boolean glowing) { + return set(ItemComponent.ENCHANTMENT_GLINT_OVERRIDE, glowing); } + + default @NotNull Builder maxStackSize(int maxStackSize) { + return set(ItemComponent.MAX_STACK_SIZE, maxStackSize); + } + + /** + *

Sets all available tooltip hide flags. The result should be an item with only name and lore.

+ * + *

One notable behavior here is that {@link ItemComponent#ATTRIBUTE_MODIFIERS} will be added if it is not + * present. This is because armor flags in tooltips use attribute modifiers show flag to display or not, but + * are not actually based on the attribute modifiers component.

+ */ + @NotNull Builder hideExtraTooltip(); + + @Contract(value = "_, _ -> this") + @NotNull Builder set(@NotNull Tag tag, @Nullable T value); + + default void setTag(@NotNull Tag tag, @Nullable T value) { + set(tag, value); + } + + @Contract(value = "-> new", pure = true) + @NotNull ItemStack build(); } } diff --git a/src/main/java/net/minestom/server/item/ItemStackImpl.java b/src/main/java/net/minestom/server/item/ItemStackImpl.java index 64a83eccbb6..3fda0b1258d 100644 --- a/src/main/java/net/minestom/server/item/ItemStackImpl.java +++ b/src/main/java/net/minestom/server/item/ItemStackImpl.java @@ -1,48 +1,55 @@ package net.minestom.server.item; -import net.minestom.server.ServerFlag; -import net.minestom.server.item.rule.VanillaStackingRule; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minestom.server.MinecraftServer; +import net.minestom.server.component.DataComponent; +import net.minestom.server.component.DataComponentMap; +import net.minestom.server.item.component.*; import net.minestom.server.tag.Tag; -import net.minestom.server.tag.TagHandler; +import net.minestom.server.utils.Unit; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jglrxavpok.hephaistos.nbt.NBT; -import org.jglrxavpok.hephaistos.nbt.NBTByte; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; -import org.jglrxavpok.hephaistos.nbt.NBTString; -import java.util.Map; +import java.util.List; import java.util.function.Consumer; -record ItemStackImpl(Material material, int amount, ItemMetaImpl meta) implements ItemStack { - static final @NotNull StackingRule DEFAULT_STACKING_RULE; - - static { - if (ServerFlag.STACKING_RULE == null) { - DEFAULT_STACKING_RULE = new VanillaStackingRule(); - } else { - try { - DEFAULT_STACKING_RULE = (StackingRule) ClassLoader.getSystemClassLoader() - .loadClass(ServerFlag.STACKING_RULE).getConstructor().newInstance(); - } catch (Exception e) { - throw new RuntimeException("Could not instantiate default stacking rule", e); - } - } - } +record ItemStackImpl(Material material, int amount, DataComponentMap components) implements ItemStack { - static ItemStack create(Material material, int amount, ItemMetaImpl meta) { + static ItemStack create(Material material, int amount, DataComponentMap components) { if (amount <= 0) return AIR; - return new ItemStackImpl(material, amount, meta); + return new ItemStackImpl(material, amount, components); } static ItemStack create(Material material, int amount) { - return create(material, amount, ItemMetaImpl.EMPTY); + return create(material, amount, DataComponentMap.EMPTY); + } + + public ItemStackImpl { + Check.notNull(material, "Material cannot be null"); + + // It is relevant to create the minimal diff of the prototype so that #isSimilar returns consistent + // results for ItemStacks which would resolve to the same thing. For example, consider two items + // (name indicating prototype, brackets showing the components given during construction): + // 1: apple[max_stack_size=64, custom_name=Hello] + // 2: apple[custom_name=Hello] + // After resolution the first set of components would turn into the second one because apple already has a + // max stack size of 64. If we did not do this, #isSimilar would return false for these two items because of + // their different patches. + // It is worth noting that the client would handle both cases perfectly fine. + components = DataComponentMap.diff(material.prototype(), components); } @Override - public > @NotNull T meta(@NotNull Class metaClass) { - return ItemMetaViewImpl.construct(metaClass, meta); + public @Nullable T get(@NotNull DataComponent component) { + return components.get(material.prototype(), component); + } + + @Override + public boolean has(@NotNull DataComponent component) { + return components.has(material.prototype(), component); } @Override @@ -53,119 +60,146 @@ static ItemStack create(Material material, int amount) { } @Override - public @NotNull > ItemStack withMeta(@NotNull Class metaType, - @NotNull Consumer consumer) { - return builder().meta(metaType, consumer).build(); + public @NotNull ItemStack withMaterial(@NotNull Material material) { + return new ItemStackImpl(material, amount, components); } @Override - public @NotNull ItemStack withMeta(@NotNull Consumer consumer) { - return builder().meta(consumer).build(); + public @NotNull ItemStack withAmount(int amount) { + if (amount <= 0) return ItemStack.AIR; + return create(material, amount, components); } @Override - public @NotNull ItemStack withMaterial(@NotNull Material material) { - return new ItemStackImpl(material, amount, meta); + public @NotNull ItemStack with(@NotNull DataComponent component, @NotNull T value) { + return new ItemStackImpl(material, amount, components.set(component, value)); } @Override - public @NotNull ItemStack withAmount(int amount) { - return create(material, amount, meta); + public @NotNull ItemStack without(@NotNull DataComponent component) { + // We can be slightly smart here. If the component is not present, this will always be a noop. + // No need to make a new patch with the removal only for it to be removed again when doing a diff. + if (get(component) == null) return this; + return new ItemStackImpl(material, amount, components.remove(component)); } @Override public @NotNull ItemStack consume(int amount) { - return DEFAULT_STACKING_RULE.apply(this, currentAmount -> currentAmount - amount); + return withAmount(amount() - amount); } @Override - public @NotNull ItemStack withMeta(@NotNull ItemMeta meta) { - return new ItemStackImpl(material, amount, (ItemMetaImpl) meta); + public boolean isSimilar(@NotNull ItemStack itemStack) { + return material == itemStack.material() && components.equals(((ItemStackImpl) itemStack).components); } @Override - public boolean isSimilar(@NotNull ItemStack itemStack) { - return material == itemStack.material() && meta.equals(itemStack.meta()); + public @NotNull CompoundBinaryTag toItemNBT() { + return (CompoundBinaryTag) NBT_TYPE.write(this); } @Override - public @NotNull NBTCompound toItemNBT() { - final NBTString material = NBT.String(material().name()); - final NBTByte amount = NBT.Byte(amount()); - final NBTCompound nbt = meta().toNBT(); - if (nbt.isEmpty()) return NBT.Compound(Map.of("id", material, "Count", amount)); - return NBT.Compound(Map.of("id", material, "Count", amount, "tag", nbt)); + @Contract(value = "-> new", pure = true) + public @NotNull ItemStack.Builder builder() { + return new Builder(material, amount, components.toPatchBuilder()); } - @Contract(value = "-> new", pure = true) - private @NotNull ItemStack.Builder builder() { - return new Builder(material, amount, new ItemMetaImpl.Builder(meta.tagHandler().copy())); + static @NotNull ItemStack fromCompound(@NotNull CompoundBinaryTag tag) { + String id = tag.getString("id"); + Material material = Material.fromNamespaceId(id); + Check.notNull(material, "Unknown material: {0}", id); + int count = tag.getInt("count", 1); + + BinaryTagSerializer.Context context = new BinaryTagSerializer.ContextWithRegistries(MinecraftServer.process(), false); + DataComponentMap patch = ItemComponent.PATCH_NBT_TYPE.read(context, tag.getCompound("components")); + return new ItemStackImpl(material, count, patch); + } + + static @NotNull CompoundBinaryTag toCompound(@NotNull ItemStack itemStack) { + CompoundBinaryTag.Builder tag = CompoundBinaryTag.builder(); + tag.putString("id", itemStack.material().name()); + tag.putInt("count", itemStack.amount()); + + BinaryTagSerializer.Context context = new BinaryTagSerializer.ContextWithRegistries(MinecraftServer.process(), false); + CompoundBinaryTag components = (CompoundBinaryTag) ItemComponent.PATCH_NBT_TYPE.write(context, ((ItemStackImpl) itemStack).components); + if (components.size() > 0) tag.put("components", components); + + return tag.build(); } static final class Builder implements ItemStack.Builder { - final Material material; - int amount; - ItemMetaImpl.Builder metaBuilder; + private Material material; + private int amount; + private DataComponentMap.PatchBuilder components; - Builder(Material material, int amount, ItemMetaImpl.Builder metaBuilder) { + Builder(Material material, int amount, DataComponentMap.PatchBuilder components) { this.material = material; this.amount = amount; - this.metaBuilder = metaBuilder; + this.components = components; } Builder(Material material, int amount) { - this(material, amount, new ItemMetaImpl.Builder(TagHandler.newHandler())); - } - - @Override - public ItemStack.@NotNull Builder amount(int amount) { + this.material = material; this.amount = amount; - return this; + this.components = DataComponentMap.patchBuilder(); } @Override - public ItemStack.@NotNull Builder meta(@NotNull TagHandler tagHandler) { - return metaBuilder(new ItemMetaImpl.Builder(tagHandler.copy())); + public ItemStack.@NotNull Builder material(@NotNull Material material) { + this.material = material; + return this; } @Override - public ItemStack.@NotNull Builder meta(@NotNull NBTCompound compound) { - return metaBuilder(new ItemMetaImpl.Builder(TagHandler.fromCompound(compound))); + public ItemStack.@NotNull Builder amount(int amount) { + this.amount = amount; + return this; } @Override - public ItemStack.@NotNull Builder meta(@NotNull ItemMeta itemMeta) { - final TagHandler tagHandler = ((ItemMetaImpl) itemMeta).tagHandler(); - return metaBuilder(new ItemMetaImpl.Builder(tagHandler.copy())); + public ItemStack.@NotNull Builder set(@NotNull DataComponent component, T value) { + components.set(component, value); + return this; } @Override - public ItemStack.@NotNull Builder meta(@NotNull Consumer consumer) { - consumer.accept(metaBuilder); + public ItemStack.@NotNull Builder remove(@NotNull DataComponent component) { + components.remove(component); return this; } @Override - public > ItemStack.@NotNull Builder meta(@NotNull Class metaType, - @NotNull Consumer<@NotNull V> itemMetaConsumer) { - V view = ItemMetaViewImpl.constructBuilder(metaType, metaBuilder.tagHandler()); - itemMetaConsumer.accept(view); + public ItemStack.@NotNull Builder set(@NotNull Tag tag, @Nullable T value) { + components.set(ItemComponent.CUSTOM_DATA, components.get(ItemComponent.CUSTOM_DATA, CustomData.EMPTY).withTag(tag, value)); return this; } @Override - public void setTag(@NotNull Tag tag, @Nullable T value) { - this.metaBuilder.setTag(tag, value); + public ItemStack.@NotNull Builder hideExtraTooltip() { + AttributeList attributeModifiers = components.get(ItemComponent.ATTRIBUTE_MODIFIERS); + components.set(ItemComponent.ATTRIBUTE_MODIFIERS, attributeModifiers == null + ? new AttributeList(List.of(), false) : attributeModifiers.withTooltip(false)); + Unbreakable unbreakable = components.get(ItemComponent.UNBREAKABLE); + if (unbreakable != null) components.set(ItemComponent.UNBREAKABLE, new Unbreakable(false)); + ArmorTrim armorTrim = components.get(ItemComponent.TRIM); + if (armorTrim != null) components.set(ItemComponent.TRIM, armorTrim.withTooltip(false)); + BlockPredicates canBreak = components.get(ItemComponent.CAN_BREAK); + if (canBreak != null) components.set(ItemComponent.CAN_BREAK, canBreak.withTooltip(false)); + BlockPredicates canPlaceOn = components.get(ItemComponent.CAN_PLACE_ON); + if (canPlaceOn != null) components.set(ItemComponent.CAN_PLACE_ON, canPlaceOn.withTooltip(false)); + DyedItemColor dyedColor = components.get(ItemComponent.DYED_COLOR); + if (dyedColor != null) components.set(ItemComponent.DYED_COLOR, dyedColor.withTooltip(false)); + EnchantmentList enchantments = components.get(ItemComponent.ENCHANTMENTS); + if (enchantments != null) components.set(ItemComponent.ENCHANTMENTS, enchantments.withTooltip(false)); + JukeboxPlayable jukeboxPlayable = components.get(ItemComponent.JUKEBOX_PLAYABLE); + if (jukeboxPlayable != null) components.set(ItemComponent.JUKEBOX_PLAYABLE, jukeboxPlayable.withTooltip(false)); + return set(ItemComponent.HIDE_ADDITIONAL_TOOLTIP, Unit.INSTANCE); } @Override public @NotNull ItemStack build() { - return ItemStackImpl.create(material, amount, metaBuilder.build()); + return ItemStackImpl.create(material, amount, components.build()); } - private ItemStack.@NotNull Builder metaBuilder(@NotNull ItemMetaImpl.Builder builder) { - this.metaBuilder = builder; - return this; - } } } diff --git a/src/main/java/net/minestom/server/item/ItemTags.java b/src/main/java/net/minestom/server/item/ItemTags.java deleted file mode 100644 index 38ae8730884..00000000000 --- a/src/main/java/net/minestom/server/item/ItemTags.java +++ /dev/null @@ -1,37 +0,0 @@ -package net.minestom.server.item; - -import net.kyori.adventure.text.Component; -import net.minestom.server.item.attribute.ItemAttribute; -import net.minestom.server.tag.Tag; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static net.minestom.server.item.ItemSerializers.*; - -final class ItemTags { - static final Tag DAMAGE = Tag.Integer("Damage").defaultValue(0); - static final Tag UNBREAKABLE = Tag.Boolean("Unbreakable").defaultValue(false); - static final Tag HIDE_FLAGS = Tag.Integer("HideFlags").defaultValue(0); - static final Tag CUSTOM_MODEL_DATA = Tag.Integer("CustomModelData").defaultValue(0); - // Microtus - Rename name tag to DISPLAY_NAME for consistency - static final Tag DISPLAY_NAME = Tag.Component("Name").path("display"); - static final Tag> LORE = Tag.Component("Lore").path("display").list().defaultValue(List.of()); - static final Tag> ENCHANTMENTS = Tag.Structure("Enchantments", ENCHANTMENT_SERIALIZER).list().map(enchantmentEntry -> { - Map map = new HashMap<>(); - for (var entry : enchantmentEntry) map.put(entry.enchantment(), entry.level()); - return Map.copyOf(map); - }, o -> { - List entries = new ArrayList<>(); - for (var entry : o.entrySet()) entries.add(new EnchantmentEntry(entry.getKey(), entry.getValue())); - return List.copyOf(entries); - }).defaultValue(Map.of()); - static final Tag> ATTRIBUTES = Tag.Structure("AttributeModifiers", ATTRIBUTE_SERIALIZER).list().defaultValue(List.of()); - static final Tag> CAN_PLACE_ON = Tag.String("CanPlaceOn").list().defaultValue(List.of()); - static final Tag> CAN_DESTROY = Tag.String("CanDestroy").list().defaultValue(List.of()); - - // Microtus - Rename name tag to DISPLAY_NAME for consistency - private ItemTags () { } -} diff --git a/src/main/java/net/minestom/server/item/Material.java b/src/main/java/net/minestom/server/item/Material.java index efb8eab74ce..98fe2c8788a 100644 --- a/src/main/java/net/minestom/server/item/Material.java +++ b/src/main/java/net/minestom/server/item/Material.java @@ -1,21 +1,26 @@ package net.minestom.server.item; +import net.minestom.server.component.DataComponentMap; import net.minestom.server.instance.block.Block; -import net.minestom.server.registry.StaticProtocolObject; +import net.minestom.server.network.NetworkBuffer; 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.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnknownNullability; import java.util.Collection; public sealed interface Material extends StaticProtocolObject, Materials permits MaterialImpl { + NetworkBuffer.Type NETWORK_TYPE = NetworkBuffer.VAR_INT.map(MaterialImpl::getId, Material::id); + BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.STRING.map(MaterialImpl::getSafe, Material::name); + /** - * Returns the material registry. - * - * @return the material registry + * Returns the raw registry data for the material. */ @Contract(pure = true) @NotNull Registry.MaterialEntry registry(); @@ -30,32 +35,24 @@ default int id() { return registry().id(); } - default int maxStackSize() { - return registry().maxStackSize(); - } - - default boolean isFood() { - return registry().isFood(); - } - default boolean isBlock() { return registry().block() != null; } - default Block block() { + default @UnknownNullability Block block() { return registry().block(); } + default @NotNull DataComponentMap prototype() { + return registry().prototype(); + } + default boolean isArmor() { return registry().isArmor(); } - default boolean hasState() { - if (this == BOW || this == TRIDENT || this == CROSSBOW || this == SHIELD) { - return true; - } else { - return isFood(); - } + default int maxStackSize() { + return prototype().get(ItemComponent.MAX_STACK_SIZE, 64); } static @NotNull Collection<@NotNull Material> values() { diff --git a/src/main/java/net/minestom/server/item/StackingRule.java b/src/main/java/net/minestom/server/item/StackingRule.java deleted file mode 100644 index 15db1b602c2..00000000000 --- a/src/main/java/net/minestom/server/item/StackingRule.java +++ /dev/null @@ -1,70 +0,0 @@ -package net.minestom.server.item; - -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; - -import java.util.function.IntUnaryOperator; - -/** - * Represents the stacking rule of an {@link ItemStack}. - * This can be used to mimic the vanilla one (using the displayed item quantity) - * or a complete new one which can be stored in lore, name, etc... - */ -public interface StackingRule { - - static @NotNull StackingRule get() { - return ItemStackImpl.DEFAULT_STACKING_RULE; - } - - /** - * Used to know if two {@link ItemStack} can be stacked together. - * - * @param item1 the first {@link ItemStack} - * @param item2 the second {@link ItemStack} - * @return true if both {@link ItemStack} can be stacked together - * (without taking their amount in consideration) - */ - boolean canBeStacked(@NotNull ItemStack item1, @NotNull ItemStack item2); - - /** - * Used to know if an {@link ItemStack} can have the size {@code newAmount} applied. - * - * @param item the {@link ItemStack} to check - * @param amount the desired new amount - * @return true if {@code item} can have its stack size set to newAmount - */ - boolean canApply(@NotNull ItemStack item, int amount); - - /** - * Changes the size of the {@link ItemStack} to {@code newAmount}. - * At this point we know that the item can have this stack size applied. - * - * @param item the {@link ItemStack} to applies the size to - * @param newAmount the new item size - * @return a new {@link ItemStack item} with the specified amount - */ - @Contract("_, _ -> new") - @NotNull ItemStack apply(@NotNull ItemStack item, int newAmount); - - @Contract("_, _ -> new") - default @NotNull ItemStack apply(@NotNull ItemStack item, @NotNull IntUnaryOperator amountOperator) { - return apply(item, amountOperator.applyAsInt(getAmount(item))); - } - - /** - * Used to determine the current stack size of an {@link ItemStack}. - * It is possible to have it stored in its nbt. - * - * @param itemStack the {@link ItemStack} to check the size - * @return the correct size of {@link ItemStack} - */ - int getAmount(@NotNull ItemStack itemStack); - - /** - * Gets the max size of a stack. - * - * @param itemStack the item to get the max size from - * @return the max size of a stack - */ - int getMaxSize(@NotNull ItemStack itemStack); -} diff --git a/src/main/java/net/minestom/server/item/armor/TrimManager.java b/src/main/java/net/minestom/server/item/armor/TrimManager.java deleted file mode 100644 index 9de0def599c..00000000000 --- a/src/main/java/net/minestom/server/item/armor/TrimManager.java +++ /dev/null @@ -1,107 +0,0 @@ -package net.minestom.server.item.armor; - -import net.minestom.server.item.Material; -import org.jetbrains.annotations.Nullable; -import org.jglrxavpok.hephaistos.nbt.NBT; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; -import org.jglrxavpok.hephaistos.nbt.NBTType; - -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -public class TrimManager { - private final Set trimMaterials; - private final Set trimPatterns; - private NBTCompound trimMaterialCache = null; - private NBTCompound 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 NBTCompound getTrimMaterialNBT() { - if (trimMaterialCache == null) { - var trimMaterials = this.trimMaterials.stream() - .map((trimMaterial) -> NBT.Compound(Map.of( - "id", NBT.Int(trimMaterial.id()), - "name", NBT.String(trimMaterial.name()), - "element", trimMaterial.asNBT() - ))) - .toList(); - - trimMaterialCache = NBT.Compound(Map.of( - "type", NBT.String("minecraft:trim_material"), - "value", NBT.List(NBTType.TAG_Compound, trimMaterials) - )); - } - return trimMaterialCache; - } - - public NBTCompound getTrimPatternNBT() { - if (trimPatternCache == null) { - var trimPatterns = this.trimPatterns.stream() - .map((trimPattern) -> NBT.Compound(Map.of( - "id", NBT.Int(trimPattern.id()), - "name", NBT.String(trimPattern.name()), - "element", trimPattern.asNBT() - ))) - .toList(); - - trimPatternCache = NBT.Compound(Map.of( - "type", NBT.String("minecraft:trim_pattern"), - "value", NBT.List(NBTType.TAG_Compound, trimPatterns) - )); - } - - return trimPatternCache; - } - - public Set getTrimMaterials() { - return Set.copyOf(this.trimMaterials); - } - - public Set 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); - } -} diff --git a/src/main/java/net/minestom/server/item/armor/TrimMaterial.java b/src/main/java/net/minestom/server/item/armor/TrimMaterial.java index adb80be3d74..610b2e59c00 100644 --- a/src/main/java/net/minestom/server/item/armor/TrimMaterial.java +++ b/src/main/java/net/minestom/server/item/armor/TrimMaterial.java @@ -2,88 +2,123 @@ import net.kyori.adventure.text.Component; import net.minestom.server.item.Material; -import net.minestom.server.registry.StaticProtocolObject; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.Registries; 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.jglrxavpok.hephaistos.nbt.NBTCompound; +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 overrideArmorMaterials, - @NotNull Component description, - Registry.Properties custom) { +public sealed interface TrimMaterial extends ProtocolObject permits TrimMaterialImpl { + @NotNull NetworkBuffer.Type> NETWORK_TYPE = NetworkBuffer.RegistryKey(Registries::trimMaterial); + @NotNull BinaryTagSerializer> NBT_TYPE = BinaryTagSerializer.registryKey(Registries::trimMaterial); + + static @NotNull TrimMaterial create( + @NotNull String assetName, + @NotNull Material ingredient, + float itemModelIndex, + @NotNull Map overrideArmorMaterials, + @NotNull Component description + ) { return new TrimMaterialImpl( - new Registry.TrimMaterialEntry( - namespace, - assetName, - ingredient, - itemModelIndex, - overrideArmorMaterials, - description, - custom - ) + assetName, ingredient, itemModelIndex, + overrideArmorMaterials, description, null ); } - static @NotNull TrimMaterial create(@NotNull NamespaceID namespace, - @NotNull String assetName, - @NotNull Material ingredient, - float itemModelIndex, - @NotNull Map overrideArmorMaterials, - @NotNull Component description) { - return new TrimMaterialImpl( - new Registry.TrimMaterialEntry( - namespace, - assetName, - ingredient, - itemModelIndex, - overrideArmorMaterials, - description, - null - ) - ); + static @NotNull Builder builder() { + return new Builder(); } - static Collection values() { - return TrimMaterialImpl.values(); + /** + *

Creates a new registry for trim materials, loading the vanilla trim materials.

+ * + * @see net.minestom.server.MinecraftServer to get an existing instance of the registry + */ + @ApiStatus.Internal + static @NotNull DynamicRegistry createDefaultRegistry() { + return DynamicRegistry.create( + "minecraft:trim_material", TrimMaterialImpl.REGISTRY_NBT_TYPE, Registry.Resource.TRIM_MATERIALS, + (namespace, props) -> new TrimMaterialImpl(Registry.trimMaterial(namespace, props)) + ); } + @NotNull String assetName(); + + @NotNull Material ingredient(); + + float itemModelIndex(); + + @NotNull Map 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 String assetName; + private Material ingredient; + private float itemModelIndex; + private final Map overrideArmorMaterials = new HashMap<>(); + private Component description; - default @NotNull String assetName() { - return registry().assetName(); - } + private Builder() { + } - default @NotNull Material ingredient() { - return registry().ingredient(); - } + @Contract(value = "_ -> this", pure = true) + public @NotNull Builder assetName(@NotNull String assetName) { + this.assetName = assetName; + return this; + } - default float itemModelIndex() { - return registry().itemModelIndex(); - } + @Contract(value = "_ -> this", pure = true) + public @NotNull Builder ingredient(@NotNull Material ingredient) { + this.ingredient = ingredient; + return this; + } - default @NotNull Map overrideArmorMaterials() { - return registry().overrideArmorMaterials(); - } + @Contract(value = "_ -> this", pure = true) + public @NotNull Builder itemModelIndex(float itemModelIndex) { + this.itemModelIndex = itemModelIndex; + return this; + } - default @NotNull Component description() { - return registry().description(); - } + @Contract(value = "_ -> this", pure = true) + public @NotNull Builder overrideArmorMaterials(@NotNull Map 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; + } - NBTCompound asNBT(); + @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( + assetName, ingredient, itemModelIndex, + overrideArmorMaterials, description, null + ); + } + } } diff --git a/src/main/java/net/minestom/server/item/armor/TrimMaterialImpl.java b/src/main/java/net/minestom/server/item/armor/TrimMaterialImpl.java index c7e19ec0133..fe6f7f0de52 100644 --- a/src/main/java/net/minestom/server/item/armor/TrimMaterialImpl.java +++ b/src/main/java/net/minestom/server/item/armor/TrimMaterialImpl.java @@ -1,48 +1,58 @@ package net.minestom.server.item.armor; -import net.minestom.server.adventure.serializer.nbt.NbtComponentSerializer; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.text.Component; +import net.minestom.server.item.Material; import net.minestom.server.registry.Registry; -import org.jglrxavpok.hephaistos.nbt.NBT; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; +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 { - private static final AtomicInteger INDEX = new AtomicInteger(); - private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.TRIM_MATERIALS, TrimMaterialImpl::createImpl); - - public TrimMaterialImpl(Registry.TrimMaterialEntry registry) { - this(registry, INDEX.getAndIncrement()); - } - - private static TrimMaterial createImpl(String namespace, Registry.Properties properties) { - return new TrimMaterialImpl(Registry.trimMaterial(namespace, properties)); - } - - public static TrimMaterial get(String namespace) { - return CONTAINER.get(namespace); - } - - static Collection values() { - return CONTAINER.values(); +record TrimMaterialImpl( + @NotNull String assetName, + @NotNull Material ingredient, + float itemModelIndex, + @NotNull Map overrideArmorMaterials, + @NotNull Component description, + @Nullable Registry.TrimMaterialEntry registry +) implements TrimMaterial { + + static final BinaryTagSerializer REGISTRY_NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> { + throw new UnsupportedOperationException("TrimMaterial is read-only"); + }, + trimMaterial -> { + CompoundBinaryTag.Builder overrideArmorMaterials = CompoundBinaryTag.builder(); + for (Map.Entry 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.argCondition(assetName == null || assetName.isEmpty(), "missing asset name"); + Check.argCondition(ingredient == null, "missing ingredient"); + Check.argCondition(overrideArmorMaterials == null, "missing override armor materials"); + Check.argCondition(description == null, "missing description"); + overrideArmorMaterials = Map.copyOf(overrideArmorMaterials); } - public NBTCompound asNBT() { - return NBT.Compound(nbt -> { - nbt.setString("asset_name", assetName()); - nbt.setString("ingredient", ingredient().namespace().asString()); - nbt.setFloat("item_model_index", itemModelIndex()); - nbt.set("override_armor_materials", NBT.Compound(overrideArmorMaterials().entrySet().stream() - .collect(Collectors.toMap( - Map.Entry::getKey, - entry -> NBT.String(entry.getValue()) - )) - )); - nbt.set("description", NbtComponentSerializer.nbt().serialize(description())); - }); + TrimMaterialImpl(@NotNull Registry.TrimMaterialEntry registry) { + this(registry.assetName(), registry.ingredient(), + registry.itemModelIndex(), registry.overrideArmorMaterials(), + registry.description(), registry); } } diff --git a/src/main/java/net/minestom/server/item/armor/TrimPattern.java b/src/main/java/net/minestom/server/item/armor/TrimPattern.java index 1823e42bcf6..bfe2b76a1be 100644 --- a/src/main/java/net/minestom/server/item/armor/TrimPattern.java +++ b/src/main/java/net/minestom/server/item/armor/TrimPattern.java @@ -2,65 +2,100 @@ import net.kyori.adventure.text.Component; import net.minestom.server.item.Material; -import net.minestom.server.registry.StaticProtocolObject; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.Registries; 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.jglrxavpok.hephaistos.nbt.NBTCompound; - -import java.util.Collection; - -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) - ); +import org.jetbrains.annotations.Nullable; + +public sealed interface TrimPattern extends ProtocolObject permits TrimPatternImpl { + @NotNull NetworkBuffer.Type> NETWORK_TYPE = NetworkBuffer.RegistryKey(Registries::trimPattern); + @NotNull BinaryTagSerializer> NBT_TYPE = BinaryTagSerializer.registryKey(Registries::trimPattern); + + static @NotNull TrimPattern create( + @NotNull NamespaceID assetId, + @NotNull Material template, + @NotNull Component description, + boolean decal + ) { + return new TrimPatternImpl(assetId, template, description, decal, null); } - 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) - ); + static @NotNull Builder builder() { + return new Builder(); } - static Collection values() { - return TrimPatternImpl.values(); + /** + *

Creates a new registry for trim materials, loading the vanilla trim materials.

+ * + * @see net.minestom.server.MinecraftServer to get an existing instance of the registry + */ + @ApiStatus.Internal + static @NotNull DynamicRegistry createDefaultRegistry() { + return DynamicRegistry.create( + "minecraft:trim_pattern", TrimPatternImpl.REGISTRY_NBT_TYPE, Registry.Resource.TRIM_PATTERNS, + (namespace, props) -> new TrimPatternImpl(Registry.trimPattern(namespace, props)) + ); } + @NotNull NamespaceID assetId(); + + @NotNull Material template(); + + @NotNull Component description(); + + 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 NamespaceID assetId; + private Material template; + private Component description; + private boolean decal; - default @NotNull NamespaceID assetID() { - return registry().assetID(); - } + private Builder() { + } - default @NotNull Material template() { - return registry().template(); - } + @Contract(value = "_ -> this", pure = true) + public @NotNull Builder assetId(@NotNull String assetId) { + return assetId(NamespaceID.from(assetId)); + } - default @NotNull Component description() { - return registry().description(); - } + @Contract(value = "_ -> this", pure = true) + public @NotNull Builder assetId(@NotNull NamespaceID assetId) { + this.assetId = assetId; + return this; + } - default boolean decal() { - return registry().decal(); - } + @Contract(value = "_ -> this", pure = true) + public @NotNull Builder template(@NotNull Material template) { + this.template = template; + return this; + } - NBTCompound asNBT(); + @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(assetId, template, description, decal, null); + } + } } diff --git a/src/main/java/net/minestom/server/item/armor/TrimPatternImpl.java b/src/main/java/net/minestom/server/item/armor/TrimPatternImpl.java index c0bbd32561f..5ab4fbe4ad8 100644 --- a/src/main/java/net/minestom/server/item/armor/TrimPatternImpl.java +++ b/src/main/java/net/minestom/server/item/armor/TrimPatternImpl.java @@ -1,40 +1,43 @@ package net.minestom.server.item.armor; -import net.minestom.server.adventure.serializer.nbt.NbtComponentSerializer; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.text.Component; +import net.minestom.server.item.Material; import net.minestom.server.registry.Registry; -import org.jglrxavpok.hephaistos.nbt.NBT; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; - -import java.util.Collection; -import java.util.concurrent.atomic.AtomicInteger; - -record TrimPatternImpl(Registry.TrimPatternEntry registry, int id) implements TrimPattern { - static final AtomicInteger i = new AtomicInteger(); - private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.TRIM_PATTERNS, TrimPatternImpl::createImpl); - - public TrimPatternImpl(Registry.TrimPatternEntry registry) { - this(registry, i.getAndIncrement()); - } - - private static TrimPattern createImpl(String namespace, Registry.Properties properties) { - return new TrimPatternImpl(Registry.trimPattern(namespace, properties)); +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 TrimPatternImpl( + @NotNull NamespaceID assetId, + @NotNull Material template, + @NotNull Component description, + boolean isDecal, + @Nullable Registry.TrimPatternEntry registry +) implements TrimPattern { + + static final BinaryTagSerializer REGISTRY_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() + ); + + TrimPatternImpl { + Check.notNull(assetId, "missing asset id"); + Check.notNull(template, "missing template"); + Check.notNull(description, "missing description"); } - public static TrimPattern get(String namespace) { - return CONTAINER.get(namespace); + TrimPatternImpl(@NotNull Registry.TrimPatternEntry registry) { + this(registry.assetID(), registry.template(), + registry.description(), registry.decal(), registry); } - - static Collection values() { - return CONTAINER.values(); - } - - public NBTCompound asNBT() { - return NBT.Compound(nbt -> { - nbt.setString("asset_id", assetID().asString()); - nbt.setString("template_item", template().namespace().asString()); - nbt.set("description", NbtComponentSerializer.nbt().serialize(description())); - nbt.setByte("decal", (byte) (decal() ? 1 : 0)); - }); - } - } diff --git a/src/main/java/net/minestom/server/item/attribute/AttributeSlot.java b/src/main/java/net/minestom/server/item/attribute/AttributeSlot.java deleted file mode 100644 index 46d3c967854..00000000000 --- a/src/main/java/net/minestom/server/item/attribute/AttributeSlot.java +++ /dev/null @@ -1,10 +0,0 @@ -package net.minestom.server.item.attribute; - -public enum AttributeSlot { - MAINHAND, - OFFHAND, - FEET, - LEGS, - CHEST, - HEAD -} diff --git a/src/main/java/net/minestom/server/item/attribute/ItemAttribute.java b/src/main/java/net/minestom/server/item/attribute/ItemAttribute.java deleted file mode 100644 index 1e14f1999b6..00000000000 --- a/src/main/java/net/minestom/server/item/attribute/ItemAttribute.java +++ /dev/null @@ -1,14 +0,0 @@ -package net.minestom.server.item.attribute; - -import net.minestom.server.attribute.Attribute; -import net.minestom.server.attribute.AttributeOperation; -import org.jetbrains.annotations.NotNull; - -import java.util.UUID; - -public record ItemAttribute(@NotNull UUID uuid, - @NotNull String name, - @NotNull Attribute attribute, - @NotNull AttributeOperation operation, double amount, - @NotNull AttributeSlot slot) { -} diff --git a/src/main/java/net/minestom/server/item/book/FilteredText.java b/src/main/java/net/minestom/server/item/book/FilteredText.java new file mode 100644 index 00000000000..38c0ad7575f --- /dev/null +++ b/src/main/java/net/minestom/server/item/book/FilteredText.java @@ -0,0 +1,60 @@ +package net.minestom.server.item.book; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.text.Component; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public record FilteredText(@NotNull T text, @Nullable T filtered) { + + public static @NotNull NetworkBuffer.Type> STRING_NETWORK_TYPE = createNetworkType(NetworkBuffer.STRING); + public static @NotNull BinaryTagSerializer> STRING_NBT_TYPE = createNbtType(BinaryTagSerializer.STRING); + + public static @NotNull NetworkBuffer.Type> COMPONENT_NETWORK_TYPE = createNetworkType(NetworkBuffer.COMPONENT); + public static @NotNull BinaryTagSerializer> COMPONENT_NBT_TYPE = createNbtType(BinaryTagSerializer.JSON_COMPONENT); + + private static NetworkBuffer.@NotNull Type> createNetworkType(@NotNull NetworkBuffer.Type inner) { + return new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, FilteredText value) { + buffer.write(inner, value.text); + buffer.writeOptional(inner, value.filtered); + } + + @Override + public FilteredText read(@NotNull NetworkBuffer buffer) { + return new FilteredText<>(buffer.read(inner), buffer.readOptional(inner)); + } + }; + } + + private static @NotNull BinaryTagSerializer> createNbtType(@NotNull BinaryTagSerializer inner) { + return new BinaryTagSerializer<>() { + @Override + public @NotNull BinaryTag write(@NotNull FilteredText value) { + CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder(); + builder.put("text", inner.write(value.text)); + if (value.filtered != null) { + builder.put("filtered", inner.write(value.filtered)); + } + return builder.build(); + } + + @Override + public @NotNull FilteredText read(@NotNull BinaryTag tag) { + if (tag instanceof CompoundBinaryTag compound) { + BinaryTag textTag = compound.get("text"); + if (textTag != null) { + BinaryTag filteredTag = compound.get("filtered"); + T filtered = filteredTag == null ? null : inner.read(filteredTag); + return new FilteredText<>(inner.read(textTag), filtered); + } + } + return new FilteredText<>(inner.read(tag), null); + } + }; + } +} diff --git a/src/main/java/net/minestom/server/item/component/ArmorTrim.java b/src/main/java/net/minestom/server/item/component/ArmorTrim.java new file mode 100644 index 00000000000..2c67444e2c8 --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/ArmorTrim.java @@ -0,0 +1,47 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minestom.server.item.armor.TrimMaterial; +import net.minestom.server.item.armor.TrimPattern; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +public record ArmorTrim(@NotNull DynamicRegistry.Key material, @NotNull DynamicRegistry.Key pattern, boolean showInTooltip) { + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, ArmorTrim value) { + buffer.write(TrimMaterial.NETWORK_TYPE, value.material); + buffer.write(TrimPattern.NETWORK_TYPE, value.pattern); + buffer.write(NetworkBuffer.BOOLEAN, value.showInTooltip); + } + + @Override + public ArmorTrim read(@NotNull NetworkBuffer buffer) { + return new ArmorTrim(buffer.read(TrimMaterial.NETWORK_TYPE), + buffer.read(TrimPattern.NETWORK_TYPE), + buffer.read(NetworkBuffer.BOOLEAN)); + } + }; + + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> { + DynamicRegistry.Key material = TrimMaterial.NBT_TYPE.read(tag.get("material")); + DynamicRegistry.Key pattern = TrimPattern.NBT_TYPE.read(tag.get("pattern")); + boolean showInTooltip = tag.getBoolean("show_in_tooltip", true); + return new ArmorTrim(material, pattern, showInTooltip); + }, + value -> CompoundBinaryTag.builder() + .put("material", TrimMaterial.NBT_TYPE.write(value.material)) + .putString("pattern", value.pattern.name()) + .putBoolean("show_in_tooltip", value.showInTooltip) + .build() + ); + + public @NotNull ArmorTrim withTooltip(boolean showInTooltip) { + return new ArmorTrim(material, pattern, showInTooltip); + } + +} diff --git a/src/main/java/net/minestom/server/item/component/AttributeList.java b/src/main/java/net/minestom/server/item/component/AttributeList.java new file mode 100644 index 00000000000..7af57caecd5 --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/AttributeList.java @@ -0,0 +1,115 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.BinaryTagTypes; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.ListBinaryTag; +import net.minestom.server.entity.EquipmentSlotGroup; +import net.minestom.server.entity.attribute.Attribute; +import net.minestom.server.entity.attribute.AttributeModifier; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public record AttributeList(@NotNull List modifiers, boolean showInTooltip) { + public static final AttributeList EMPTY = new AttributeList(List.of(), true); + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, AttributeList value) { + buffer.writeCollection(Modifier.NETWORK_TYPE, value.modifiers); + buffer.write(NetworkBuffer.BOOLEAN, value.showInTooltip); + } + + @Override + public AttributeList read(@NotNull NetworkBuffer buffer) { + return new AttributeList(buffer.readCollection(Modifier.NETWORK_TYPE, Short.MAX_VALUE), + buffer.read(NetworkBuffer.BOOLEAN)); + } + }; + + public static final BinaryTagSerializer NBT_TYPE = new BinaryTagSerializer<>() { + @Override + public @NotNull BinaryTag write(@NotNull AttributeList value) { + ListBinaryTag.Builder modifiers = ListBinaryTag.builder(); + for (Modifier modifier : value.modifiers) { + modifiers.add(Modifier.NBT_TYPE.write(modifier)); + } + return CompoundBinaryTag.builder() + .put("modifiers", modifiers.build()) + .putBoolean("show_in_tooltip", value.showInTooltip) + .build(); + } + + @Override + public @NotNull AttributeList read(@NotNull BinaryTag tag) { + return switch (tag) { + case CompoundBinaryTag compound -> new AttributeList( + compound.getList("modifiers", BinaryTagTypes.COMPOUND).stream().map(Modifier.NBT_TYPE::read).toList(), + compound.getBoolean("show_in_tooltip", true) + ); + case ListBinaryTag list -> new AttributeList(list.stream().map(Modifier.NBT_TYPE::read).toList()); + default -> EMPTY; + }; + } + }; + + public record Modifier(@NotNull Attribute attribute, @NotNull AttributeModifier modifier, @NotNull EquipmentSlotGroup slot) { + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, Modifier value) { + buffer.write(Attribute.NETWORK_TYPE, value.attribute); + buffer.write(AttributeModifier.NETWORK_TYPE, value.modifier); + buffer.writeEnum(EquipmentSlotGroup.class, value.slot); + } + + @Override + public Modifier read(@NotNull NetworkBuffer buffer) { + return new Modifier(buffer.read(Attribute.NETWORK_TYPE), + buffer.read(AttributeModifier.NETWORK_TYPE), + buffer.readEnum(EquipmentSlotGroup.class)); + } + }; + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> new Modifier( + Attribute.NBT_TYPE.read(tag.get("type")), + AttributeModifier.NBT_TYPE.read(tag), + tag.get("slot") instanceof BinaryTag slot ? EquipmentSlotGroup.NBT_TYPE.read(slot) : EquipmentSlotGroup.ANY + ), + modifier -> CompoundBinaryTag.builder() + .put("type", Attribute.NBT_TYPE.write(modifier.attribute)) + .put((CompoundBinaryTag) AttributeModifier.NBT_TYPE.write(modifier.modifier)) + .put("slot", EquipmentSlotGroup.NBT_TYPE.write(modifier.slot)) + .build() + ); + } + + public AttributeList { + modifiers = List.copyOf(modifiers); + } + + public AttributeList(@NotNull List modifiers) { + this(modifiers, true); + } + + public AttributeList(@NotNull Modifier modifier, boolean showInTooltip) { + this(List.of(modifier), showInTooltip); + } + + public AttributeList(@NotNull Modifier modifier) { + this(modifier, true); + } + + public @NotNull AttributeList with(@NotNull Modifier modifier) { + List newModifiers = new ArrayList<>(modifiers); + newModifiers.add(modifier); + return new AttributeList(newModifiers, showInTooltip); + } + + public @NotNull AttributeList withTooltip(boolean showInTooltip) { + return new AttributeList(modifiers, showInTooltip); + } +} diff --git a/src/main/java/net/minestom/server/item/component/BannerPatterns.java b/src/main/java/net/minestom/server/item/component/BannerPatterns.java new file mode 100644 index 00000000000..a06d22b1981 --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/BannerPatterns.java @@ -0,0 +1,59 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minestom.server.color.DyeColor; +import net.minestom.server.instance.block.banner.BannerPattern; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public record BannerPatterns(@NotNull List layers) { + public static final int MAX_LAYERS = 1024; + + public static final NetworkBuffer.Type NETWORK_TYPE = Layer.NETWORK_TYPE.list(MAX_LAYERS).map(BannerPatterns::new, BannerPatterns::layers); + public static final BinaryTagSerializer NBT_TYPE = Layer.NBT_TYPE.list().map(BannerPatterns::new, BannerPatterns::layers); + + public record Layer(@NotNull DynamicRegistry.Key pattern, @NotNull DyeColor color) { + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, Layer value) { + buffer.write(BannerPattern.NETWORK_TYPE, value.pattern); + buffer.write(DyeColor.NETWORK_TYPE, value.color); + } + + @Override + public Layer read(@NotNull NetworkBuffer buffer) { + return new Layer(buffer.read(BannerPattern.NETWORK_TYPE), buffer.read(DyeColor.NETWORK_TYPE)); + } + }; + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> new Layer(BannerPattern.NBT_TYPE.read(tag.get("pattern")), DyeColor.NBT_TYPE.read(tag.get("color"))), + layer -> CompoundBinaryTag.builder() + .put("pattern", BannerPattern.NBT_TYPE.write(layer.pattern)) + .put("color", DyeColor.NBT_TYPE.write(layer.color)) + .build() + ); + } + + public BannerPatterns { + layers = List.copyOf(layers); + } + + public BannerPatterns(@NotNull Layer layer) { + this(List.of(layer)); + } + + public BannerPatterns(@NotNull DynamicRegistry.Key pattern, @NotNull DyeColor color) { + this(new Layer(pattern, color)); + } + + public @NotNull BannerPatterns with(@NotNull Layer layer) { + List layers = new ArrayList<>(this.layers); + layers.add(layer); + return new BannerPatterns(layers); + } +} diff --git a/src/main/java/net/minestom/server/item/component/Bee.java b/src/main/java/net/minestom/server/item/component/Bee.java new file mode 100644 index 00000000000..944cc8bc9c9 --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/Bee.java @@ -0,0 +1,47 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +public record Bee(@NotNull CustomData entityData, int ticksInHive, int minTicksInHive) { + + public static @NotNull NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type() { + @Override + public void write(@NotNull NetworkBuffer buffer, Bee value) { + buffer.write(CustomData.NETWORK_TYPE, value.entityData); + buffer.write(NetworkBuffer.VAR_INT, value.ticksInHive); + buffer.write(NetworkBuffer.VAR_INT, value.minTicksInHive); + } + + @Override + public Bee read(@NotNull NetworkBuffer buffer) { + return new Bee(buffer.read(CustomData.NETWORK_TYPE), + buffer.read(NetworkBuffer.VAR_INT), + buffer.read(NetworkBuffer.VAR_INT)); + } + }; + public static @NotNull BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> new Bee(CustomData.NBT_TYPE.read(tag.getCompound("entity_data")), + tag.getInt("ticks_in_hive"), + tag.getInt("min_ticks_in_hive")), + value -> CompoundBinaryTag.builder() + .put("entity_data", CustomData.NBT_TYPE.write(value.entityData)) + .putInt("ticks_in_hive", value.ticksInHive) + .putInt("min_ticks_in_hive", value.minTicksInHive) + .build() + ); + + public @NotNull Bee withEntityData(@NotNull CustomData entityData) { + return new Bee(entityData, ticksInHive, minTicksInHive); + } + + public @NotNull Bee withTicksInHive(int ticksInHive) { + return new Bee(entityData, ticksInHive, minTicksInHive); + } + + public @NotNull Bee withMinTicksInHive(int minTicksInHive) { + return new Bee(entityData, ticksInHive, minTicksInHive); + } +} diff --git a/src/main/java/net/minestom/server/item/component/BlockPredicates.java b/src/main/java/net/minestom/server/item/component/BlockPredicates.java new file mode 100644 index 00000000000..79c994a23bf --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/BlockPredicates.java @@ -0,0 +1,95 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minestom.server.instance.block.Block; +import net.minestom.server.instance.block.predicate.BlockPredicate; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.function.Predicate; + +public record BlockPredicates(@NotNull List predicates, boolean showInTooltip) implements Predicate { + /** + * Will never match any block. + */ + public static final BlockPredicates NEVER = new BlockPredicates(List.of(), false); + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + private static final NetworkBuffer.Type> PREDICATE_LIST_TYPE = BlockPredicate.NETWORK_TYPE.list(Short.MAX_VALUE); + + @Override + public void write(@NotNull NetworkBuffer buffer, BlockPredicates value) { + buffer.write(PREDICATE_LIST_TYPE, value.predicates); + buffer.write(NetworkBuffer.BOOLEAN, value.showInTooltip); + } + + @Override + public BlockPredicates read(@NotNull NetworkBuffer buffer) { + return new BlockPredicates(buffer.read(PREDICATE_LIST_TYPE), buffer.read(NetworkBuffer.BOOLEAN)); + } + }; + public static final BinaryTagSerializer NBT_TYPE = new BinaryTagSerializer<>() { + private static final BinaryTagSerializer> PREDICATES_LIST_TYPE = BlockPredicate.NBT_TYPE.list(); + + @Override + public @NotNull BinaryTag write(@NotNull BlockPredicates value) { + return CompoundBinaryTag.builder() + .put("predicates", PREDICATES_LIST_TYPE.write(value.predicates)) + .putBoolean("show_in_tooltip", value.showInTooltip) + .build(); + } + + @Override + public @NotNull BlockPredicates read(@NotNull BinaryTag tag) { + if (!(tag instanceof CompoundBinaryTag compound)) return BlockPredicates.NEVER; + + List predicates; + BinaryTag predicatesTag = compound.get("predicates"); + if (predicatesTag != null) { + predicates = PREDICATES_LIST_TYPE.read(predicatesTag); + } else { + // Try to read as a single predicate + predicates = List.of(BlockPredicate.NBT_TYPE.read(tag)); + } + + // This default is fine in either case because the single predicate shouldnt have this key anyway. + // https://github.com/KyoriPowered/adventure/issues/1068 + boolean showInTooltip = compound.getBoolean("show_in_tooltip", true); + + return new BlockPredicates(predicates, showInTooltip); + } + }; + + public BlockPredicates { + predicates = List.copyOf(predicates); + } + + public BlockPredicates(@NotNull List predicates) { + this(predicates, true); + } + + public BlockPredicates(@NotNull BlockPredicate predicate) { + this(List.of(predicate), true); + } + + public BlockPredicates(@NotNull BlockPredicate predicate, boolean showInTooltip) { + this(List.of(predicate), showInTooltip); + } + + public @NotNull BlockPredicates withTooltip(boolean showInTooltip) { + return new BlockPredicates(predicates, showInTooltip); + } + + @Override + public boolean test(Block block) { + for (BlockPredicate predicate : predicates) { + if (predicate.test(block)) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/net/minestom/server/item/component/CustomData.java b/src/main/java/net/minestom/server/item/component/CustomData.java new file mode 100644 index 00000000000..37b7505b2c7 --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/CustomData.java @@ -0,0 +1,39 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.tag.Tag; +import net.minestom.server.tag.TagReadable; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.UnknownNullability; + +public record CustomData(@NotNull CompoundBinaryTag nbt) implements TagReadable { + public static final CustomData EMPTY = new CustomData(CompoundBinaryTag.empty()); + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, CustomData value) { + buffer.write(NetworkBuffer.NBT, value.nbt); + } + + @Override + public CustomData read(@NotNull NetworkBuffer buffer) { + return new CustomData((CompoundBinaryTag) buffer.read(NetworkBuffer.NBT)); + } + }; + + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map(CustomData::new, CustomData::nbt); + + @Override + public @UnknownNullability T getTag(@NotNull Tag tag) { + return tag.read(nbt); + } + + public @NotNull CustomData withTag(@NotNull Tag tag, T value) { + CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder(); + builder.put(nbt); + tag.write(builder, value); + return new CustomData(builder.build()); + } +} diff --git a/src/main/java/net/minestom/server/item/component/DebugStickState.java b/src/main/java/net/minestom/server/item/component/DebugStickState.java new file mode 100644 index 00000000000..8c2116955ad --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/DebugStickState.java @@ -0,0 +1,50 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.StringBinaryTag; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +public record DebugStickState(@NotNull Map state) { + public static final DebugStickState EMPTY = new DebugStickState(Map.of()); + + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> { + Map state = new HashMap<>(); + for (Map.Entry entry : tag) { + if (!(entry.getValue() instanceof StringBinaryTag property)) + continue; + state.put(entry.getKey(), property.value()); + } + return new DebugStickState(state); + }, + state -> { + CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder(); + for (Map.Entry entry : state.state().entrySet()) { + builder.put(entry.getKey(), StringBinaryTag.stringBinaryTag(entry.getValue())); + } + return builder.build(); + } + ); + + public DebugStickState { + state = Map.copyOf(state); + } + + public @NotNull DebugStickState set(@NotNull String key, @NotNull String value) { + Map newState = new HashMap<>(state); + newState.put(key, value); + return new DebugStickState(newState); + } + + public @NotNull DebugStickState remove(@NotNull String key) { + Map newState = new HashMap<>(state); + newState.remove(key); + return new DebugStickState(newState); + } + +} diff --git a/src/main/java/net/minestom/server/item/component/DyedItemColor.java b/src/main/java/net/minestom/server/item/component/DyedItemColor.java new file mode 100644 index 00000000000..76b18771c8a --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/DyedItemColor.java @@ -0,0 +1,70 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.IntBinaryTag; +import net.kyori.adventure.util.RGBLike; +import net.minestom.server.color.Color; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +public record DyedItemColor(@NotNull RGBLike color, boolean showInTooltip) { + public static DyedItemColor LEATHER = new DyedItemColor(new Color(-6265536), true); + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, DyedItemColor value) { + buffer.write(Color.NETWORK_TYPE, value.color); + buffer.write(NetworkBuffer.BOOLEAN, value.showInTooltip); + } + + @Override + public DyedItemColor read(@NotNull NetworkBuffer buffer) { + return new DyedItemColor(buffer.read(Color.NETWORK_TYPE), buffer.read(NetworkBuffer.BOOLEAN)); + } + }; + + public static final BinaryTagSerializer NBT_TYPE = new BinaryTagSerializer<>() { + @Override + public @NotNull BinaryTag write(@NotNull DyedItemColor value) { + return CompoundBinaryTag.builder() + .putInt("color", Color.fromRGBLike(value.color).asRGB()) + .putBoolean("show_in_tooltip", value.showInTooltip) + .build(); + } + + @Override + public @NotNull DyedItemColor read(@NotNull BinaryTag tag) { + if (tag instanceof CompoundBinaryTag compoundTag) { + int color = compoundTag.getInt("color"); + boolean showInTooltip = compoundTag.getBoolean("show_in_tooltip", true); + return new DyedItemColor(new Color(color), showInTooltip); + } else if (tag instanceof IntBinaryTag intTag) { + return new DyedItemColor(new Color(intTag.intValue()), true); + } + return new DyedItemColor(new Color(0), false); + } + }; + + public DyedItemColor(int color) { + this(color, true); + } + + public DyedItemColor(@NotNull RGBLike color) { + this(color, true); + } + + public DyedItemColor(int color, boolean showInTooltip) { + this(new Color(color), showInTooltip); + } + + public @NotNull DyedItemColor withColor(@NotNull Color color) { + return new DyedItemColor(color, showInTooltip); + } + + public @NotNull DyedItemColor withTooltip(boolean showInTooltip) { + return new DyedItemColor(color, showInTooltip); + } + +} diff --git a/src/main/java/net/minestom/server/item/component/EnchantmentList.java b/src/main/java/net/minestom/server/item/component/EnchantmentList.java new file mode 100644 index 00000000000..643d88bba33 --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/EnchantmentList.java @@ -0,0 +1,115 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minestom.server.item.enchant.Enchantment; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +import static net.kyori.adventure.nbt.StringBinaryTag.stringBinaryTag; + +public record EnchantmentList(@NotNull Map, Integer> enchantments, + boolean showInTooltip) { + public static final EnchantmentList EMPTY = new EnchantmentList(Map.of(), true); + + public static NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, @NotNull EnchantmentList value) { + buffer.write(NetworkBuffer.VAR_INT, value.enchantments.size()); + for (Map.Entry, Integer> entry : value.enchantments.entrySet()) { + buffer.write(Enchantment.NETWORK_TYPE, entry.getKey()); + buffer.write(NetworkBuffer.VAR_INT, entry.getValue()); + } + buffer.write(NetworkBuffer.BOOLEAN, value.showInTooltip); + } + + @Override + public @NotNull EnchantmentList read(@NotNull NetworkBuffer buffer) { + int size = buffer.read(NetworkBuffer.VAR_INT); + Check.argCondition(size < 0 || size > Short.MAX_VALUE, "Invalid enchantment list size: {0}", size); + Map, Integer> enchantments = new HashMap<>(size); + for (int i = 0; i < size; i++) { + DynamicRegistry.Key enchantment = buffer.read(Enchantment.NETWORK_TYPE); + enchantments.put(enchantment, buffer.read(NetworkBuffer.VAR_INT)); + } + boolean showInTooltip = buffer.read(NetworkBuffer.BOOLEAN); + return new EnchantmentList(enchantments, showInTooltip); + } + }; + public static BinaryTagSerializer NBT_TYPE = new BinaryTagSerializer<>() { + @Override + public @NotNull BinaryTag write(@NotNull Context context, @NotNull EnchantmentList value) { + CompoundBinaryTag.Builder levels = CompoundBinaryTag.builder(); + for (Map.Entry, Integer> entry : value.enchantments.entrySet()) { + levels.put(entry.getKey().name(), BinaryTagSerializer.INT.write(context, entry.getValue())); + } + + return CompoundBinaryTag.builder() + .put("levels", levels.build()) + .putBoolean("show_in_tooltip", value.showInTooltip) + .build(); + } + + @Override + public @NotNull EnchantmentList read(@NotNull Context context, @NotNull BinaryTag raw) { + if (!(raw instanceof CompoundBinaryTag tag)) + throw new IllegalArgumentException("Enchantment list must be a compound tag"); + + // We have two variants of the enchantment list, one with {levels: {...}, show_in_tooltip: boolean} and one with {...}. + CompoundBinaryTag levels = tag.keySet().contains("levels") ? tag.getCompound("levels") : tag; + Map, Integer> enchantments = new HashMap<>(levels.size()); + for (Map.Entry entry : levels) { + DynamicRegistry.Key enchantment = Enchantment.NBT_TYPE.read(context, stringBinaryTag(entry.getKey())); + int level = BinaryTagSerializer.INT.read(entry.getValue()); + if (level > 0) enchantments.put(enchantment, level); + } + + // Doesnt matter which variant we chose, the default will work. + boolean showInTooltip = tag.getBoolean("show_in_tooltip", true); + + return new EnchantmentList(enchantments, showInTooltip); + } + }; + + public EnchantmentList { + enchantments = Map.copyOf(enchantments); + } + + public EnchantmentList(@NotNull Map, Integer> enchantments) { + this(enchantments, true); + } + + public EnchantmentList(@NotNull DynamicRegistry.Key enchantment, int level) { + this(Map.of(enchantment, level), true); + } + + public boolean has(@NotNull DynamicRegistry.Key enchantment) { + return enchantments.containsKey(enchantment); + } + + public int level(@NotNull DynamicRegistry.Key enchantment) { + return enchantments.getOrDefault(enchantment, 0); + } + + public @NotNull EnchantmentList with(@NotNull DynamicRegistry.Key enchantment, int level) { + Map, Integer> newEnchantments = new HashMap<>(enchantments); + newEnchantments.put(enchantment, level); + return new EnchantmentList(newEnchantments, showInTooltip); + } + + public @NotNull EnchantmentList remove(@NotNull DynamicRegistry.Key enchantment) { + Map, Integer> newEnchantments = new HashMap<>(enchantments); + newEnchantments.remove(enchantment); + return new EnchantmentList(newEnchantments, showInTooltip); + } + + public @NotNull EnchantmentList withTooltip(boolean showInTooltip) { + return new EnchantmentList(enchantments, showInTooltip); + } +} diff --git a/src/main/java/net/minestom/server/item/component/FireworkExplosion.java b/src/main/java/net/minestom/server/item/component/FireworkExplosion.java new file mode 100644 index 00000000000..50c0131d762 --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/FireworkExplosion.java @@ -0,0 +1,90 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.util.RGBLike; +import net.minestom.server.color.Color; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +public record FireworkExplosion( + @NotNull Shape shape, + @NotNull List colors, + @NotNull List fadeColors, + boolean hasTrail, + boolean hasTwinkle +) { + + public enum Shape { + SMALL_BALL, + LARGE_BALL, + STAR, + CREEPER, + BURST + } + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, FireworkExplosion value) { + buffer.writeEnum(Shape.class, value.shape); + buffer.writeCollection(Color.NETWORK_TYPE, value.colors); + buffer.writeCollection(Color.NETWORK_TYPE, value.fadeColors); + buffer.write(NetworkBuffer.BOOLEAN, value.hasTrail); + buffer.write(NetworkBuffer.BOOLEAN, value.hasTwinkle); + } + + @Override + public FireworkExplosion read(@NotNull NetworkBuffer buffer) { + return new FireworkExplosion( + buffer.readEnum(Shape.class), + buffer.readCollection(Color.NETWORK_TYPE, Short.MAX_VALUE), + buffer.readCollection(Color.NETWORK_TYPE, Short.MAX_VALUE), + buffer.read(NetworkBuffer.BOOLEAN), + buffer.read(NetworkBuffer.BOOLEAN) + ); + } + }; + + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> { + Shape shape = Shape.valueOf(tag.getString("shape").toUpperCase(Locale.ROOT)); + List colors = new ArrayList<>(); + for (int color : tag.getIntArray("colors")) + colors.add(new Color(color)); + List fadeColors = new ArrayList<>(); + for (int fadeColor : tag.getIntArray("fade_colors")) + fadeColors.add(new Color(fadeColor)); + boolean hasTrail = tag.getBoolean("has_trail"); + boolean hasTwinkle = tag.getBoolean("has_twinkle"); + return new FireworkExplosion(shape, colors, fadeColors, hasTrail, hasTwinkle); + }, + value -> { + CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder(); + builder.putString("shape", value.shape.name().toLowerCase(Locale.ROOT)); + if (!value.colors.isEmpty()) { + int[] colors = new int[value.colors.size()]; + for (int i = 0; i < value.colors.size(); i++) + colors[i] = Color.fromRGBLike(value.colors.get(i)).asRGB(); + builder.putIntArray("colors", colors); + } + if (!value.fadeColors.isEmpty()) { + int[] fadeColors = new int[value.fadeColors.size()]; + for (int i = 0; i < value.fadeColors.size(); i++) + fadeColors[i] = Color.fromRGBLike(value.fadeColors.get(i)).asRGB(); + builder.putIntArray("fade_colors", fadeColors); + } + if (value.hasTrail) builder.putBoolean("has_trail", value.hasTrail); + if (value.hasTwinkle) builder.putBoolean("has_twinkle", value.hasTwinkle); + return builder.build(); + } + ); + + public FireworkExplosion { + colors = List.copyOf(colors); + fadeColors = List.copyOf(fadeColors); + } +} diff --git a/src/main/java/net/minestom/server/item/component/FireworkList.java b/src/main/java/net/minestom/server/item/component/FireworkList.java new file mode 100644 index 00000000000..470457af129 --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/FireworkList.java @@ -0,0 +1,62 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.BinaryTagTypes; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.ListBinaryTag; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public record FireworkList(byte flightDuration, @NotNull List explosions) { + public static final FireworkList EMPTY = new FireworkList((byte) 0, List.of()); + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, FireworkList value) { + buffer.write(NetworkBuffer.BYTE, value.flightDuration); + buffer.writeCollection(FireworkExplosion.NETWORK_TYPE, value.explosions); + } + + @Override + public FireworkList read(@NotNull NetworkBuffer buffer) { + return new FireworkList(buffer.read(NetworkBuffer.BYTE), + buffer.readCollection(FireworkExplosion.NETWORK_TYPE, 256)); + } + }; + + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> { + byte flightDuration = tag.getByte("flight_duration"); + ListBinaryTag explosionsTag = tag.getList("explosions", BinaryTagTypes.COMPOUND); + List explosions = new ArrayList<>(explosionsTag.size()); + for (BinaryTag explosionTag : explosionsTag) + explosions.add(FireworkExplosion.NBT_TYPE.read(explosionTag)); + return new FireworkList(flightDuration, explosions); + }, + value -> { + ListBinaryTag.Builder explosionsTag = ListBinaryTag.builder(); + for (FireworkExplosion explosion : value.explosions) + explosionsTag.add(FireworkExplosion.NBT_TYPE.write(explosion)); + return CompoundBinaryTag.builder() + .putByte("flight_duration", value.flightDuration) + .put("explosions", explosionsTag.build()) + .build(); + } + ); + + public FireworkList { + explosions = List.copyOf(explosions); + } + + public @NotNull FireworkList withFlightDuration(byte flightDuration) { + return new FireworkList(flightDuration, explosions); + } + + public @NotNull FireworkList withExplosions(@NotNull List explosions) { + return new FireworkList(flightDuration, explosions); + } +} diff --git a/src/main/java/net/minestom/server/item/component/Food.java b/src/main/java/net/minestom/server/item/component/Food.java new file mode 100644 index 00000000000..6901be08ceb --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/Food.java @@ -0,0 +1,97 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.BinaryTagTypes; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minestom.server.ServerFlag; +import net.minestom.server.item.ItemStack; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.potion.CustomPotionEffect; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public record Food(int nutrition, float saturationModifier, boolean canAlwaysEat, float eatSeconds, + @NotNull ItemStack usingConvertsTo, @NotNull List effects) { + public static final float DEFAULT_EAT_SECONDS = 1.6f; + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, Food value) { + buffer.write(NetworkBuffer.VAR_INT, value.nutrition); + buffer.write(NetworkBuffer.FLOAT, value.saturationModifier); + buffer.write(NetworkBuffer.BOOLEAN, value.canAlwaysEat); + buffer.write(NetworkBuffer.FLOAT, value.eatSeconds); + buffer.write(ItemStack.NETWORK_TYPE, value.usingConvertsTo); + buffer.writeCollection(EffectChance.NETWORK_TYPE, value.effects); + } + + @Override + public Food read(@NotNull NetworkBuffer buffer) { + return new Food( + buffer.read(NetworkBuffer.VAR_INT), + buffer.read(NetworkBuffer.FLOAT), + buffer.read(NetworkBuffer.BOOLEAN), + buffer.read(NetworkBuffer.FLOAT), + buffer.read(ItemStack.NETWORK_TYPE), + buffer.readCollection(EffectChance.NETWORK_TYPE, Short.MAX_VALUE) + ); + } + }; + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> new Food( + tag.getInt("nutrition"), + tag.getFloat("saturation_modifier"), + tag.getBoolean("can_always_eat"), + tag.getFloat("eat_seconds", DEFAULT_EAT_SECONDS), + tag.get("using_converts_to") instanceof BinaryTag usingConvertsTo + ? ItemStack.NBT_TYPE.read(usingConvertsTo) : ItemStack.AIR, + EffectChance.NBT_LIST_TYPE.read(tag.getList("effects", BinaryTagTypes.COMPOUND))), + value -> { + CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder() + .putInt("nutrition", value.nutrition) + .putFloat("saturation_odifier", value.saturationModifier) + .putBoolean("can_always_eat", value.canAlwaysEat) + .putFloat("eat_seconds", value.eatSeconds) + .put("effects", EffectChance.NBT_LIST_TYPE.write(value.effects)); + if (!value.usingConvertsTo.isAir()) { + builder.put("using_converts_to", ItemStack.NBT_TYPE.write(value.usingConvertsTo)); + } + return builder.build(); + } + ); + + public Food { + effects = List.copyOf(effects); + } + + public int eatDurationTicks() { + return (int) (eatSeconds * ServerFlag.SERVER_TICKS_PER_SECOND); + } + + public record EffectChance(@NotNull CustomPotionEffect effect, float probability) { + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, EffectChance value) { + + } + + @Override + public EffectChance read(@NotNull NetworkBuffer buffer) { + return null; + } + }; + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> new EffectChance( + CustomPotionEffect.NBT_TYPE.read(tag.getCompound("effect")), + tag.getFloat("probability", 1f)), + value -> CompoundBinaryTag.builder() + .put("effect", CustomPotionEffect.NBT_TYPE.write(value.effect())) + .putFloat("probability", value.probability) + .build() + ); + public static final BinaryTagSerializer> NBT_LIST_TYPE = NBT_TYPE.list(); + } + +} diff --git a/src/main/java/net/minestom/server/item/component/HeadProfile.java b/src/main/java/net/minestom/server/item/component/HeadProfile.java new file mode 100644 index 00000000000..9e8afcae115 --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/HeadProfile.java @@ -0,0 +1,87 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.IntArrayBinaryTag; +import net.kyori.adventure.nbt.StringBinaryTag; +import net.minestom.server.entity.PlayerSkin; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.UUID; + +public record HeadProfile(@Nullable String name, @Nullable UUID uuid, @NotNull List properties) { + public static final HeadProfile EMPTY = new HeadProfile(null, null, List.of()); + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type() { + @Override + public void write(@NotNull NetworkBuffer buffer, HeadProfile value) { + buffer.writeOptional(NetworkBuffer.STRING, value.name); + buffer.writeOptional(NetworkBuffer.UUID, value.uuid); + buffer.writeCollection(Property.NETWORK_TYPE, value.properties); + } + + @Override + public HeadProfile read(@NotNull NetworkBuffer buffer) { + return new HeadProfile(buffer.readOptional(NetworkBuffer.STRING), buffer.readOptional(NetworkBuffer.UUID), buffer.readCollection(Property.NETWORK_TYPE, Short.MAX_VALUE)); + } + }; + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> new HeadProfile( + tag.get("name") instanceof StringBinaryTag string ? string.value() : null, + tag.get("uuid") instanceof IntArrayBinaryTag intArray ? BinaryTagSerializer.UUID.read(intArray) : null, + Property.NBT_LIST_TYPE.read(tag.getList("properties")) + ), + profile -> { + CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder(); + if (profile.name != null) builder.putString("name", profile.name); + if (profile.uuid != null) builder.put("uuid", BinaryTagSerializer.UUID.write(profile.uuid)); + if (!profile.properties.isEmpty()) builder.put("properties", Property.NBT_LIST_TYPE.write(profile.properties)); + return builder.build(); + } + ); + + public HeadProfile(@NotNull PlayerSkin playerSkin) { + this(null, null, List.of(new Property("textures", playerSkin.textures(), playerSkin.signature()))); + } + + public @Nullable PlayerSkin skin() { + for (Property property : properties) { + if ("textures".equals(property.name)) { + return new PlayerSkin(property.value, property.signature); + } + } + return null; + } + + public record Property(@NotNull String name, @NotNull String value, @Nullable String signature) { + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type() { + @Override + public void write(@NotNull NetworkBuffer buffer, Property value) { + buffer.write(NetworkBuffer.STRING, value.name); + buffer.write(NetworkBuffer.STRING, value.value); + buffer.writeOptional(NetworkBuffer.STRING, value.signature); + } + + @Override + public Property read(@NotNull NetworkBuffer buffer) { + return new Property(buffer.read(NetworkBuffer.STRING), buffer.read(NetworkBuffer.STRING), buffer.readOptional(NetworkBuffer.STRING)); + } + }; + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> new Property(tag.getString("name"), tag.getString("value"), + tag.get("signature") instanceof StringBinaryTag signature ? signature.value() : null), + property -> { + CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder(); + builder.putString("name", property.name); + builder.putString("value", property.value); + if (property.signature != null) builder.putString("signature", property.signature); + return builder.build(); + } + ); + public static final BinaryTagSerializer> NBT_LIST_TYPE = NBT_TYPE.list(); + } + +} diff --git a/src/main/java/net/minestom/server/item/component/ItemBlockState.java b/src/main/java/net/minestom/server/item/component/ItemBlockState.java new file mode 100644 index 00000000000..ae403be3aa1 --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/ItemBlockState.java @@ -0,0 +1,74 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.StringBinaryTag; +import net.minestom.server.instance.block.Block; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +public record ItemBlockState(@NotNull Map properties) { + public static final ItemBlockState EMPTY = new ItemBlockState(Map.of()); + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, ItemBlockState value) { + buffer.write(NetworkBuffer.VAR_INT, value.properties.size()); + for (Map.Entry entry : value.properties.entrySet()) { + buffer.write(NetworkBuffer.STRING, entry.getKey()); + buffer.write(NetworkBuffer.STRING, entry.getValue()); + } + } + + @Override + public ItemBlockState read(@NotNull NetworkBuffer buffer) { + int size = buffer.read(NetworkBuffer.VAR_INT); + Map properties = new HashMap<>(size); + for (int i = 0; i < size; i++) { + properties.put(buffer.read(NetworkBuffer.STRING), buffer.read(NetworkBuffer.STRING)); + } + return new ItemBlockState(properties); + } + }; + + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> { + Map properties = new HashMap<>(tag.size()); + for (Map.Entry entry : tag) { + if (!(entry.getValue() instanceof StringBinaryTag str)) continue; + properties.put(entry.getKey(), str.value()); + } + return new ItemBlockState(properties); + }, + value -> { + CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder(); + for (Map.Entry entry : value.properties.entrySet()) { + builder.put(entry.getKey(), StringBinaryTag.stringBinaryTag(entry.getValue())); + } + return builder.build(); + } + ); + + public ItemBlockState { + properties = Map.copyOf(properties); + } + + public @NotNull ItemBlockState with(@NotNull String key, @NotNull String value) { + Map newProperties = new HashMap<>(properties); + newProperties.put(key, value); + return new ItemBlockState(newProperties); + } + + public @NotNull Block apply(@NotNull Block block) { + for (Map.Entry entry : properties.entrySet()) { + if (block.getProperty(entry.getKey()) == null) + continue; // Ignore properties not present on this block + block = block.withProperty(entry.getKey(), entry.getValue()); + } + return block; + } +} diff --git a/src/main/java/net/minestom/server/item/component/ItemRarity.java b/src/main/java/net/minestom/server/item/component/ItemRarity.java new file mode 100644 index 00000000000..eed4e7aad58 --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/ItemRarity.java @@ -0,0 +1,16 @@ +package net.minestom.server.item.component; + +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.nbt.BinaryTagSerializer; + +public enum ItemRarity { + COMMON, + UNCOMMON, + RARE, + EPIC; + + private static final ItemRarity[] VALUES = values(); + + public static final NetworkBuffer.Type NETWORK_TYPE = NetworkBuffer.Enum(ItemRarity.class); + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.fromEnumStringable(ItemRarity.class); +} diff --git a/src/main/java/net/minestom/server/item/component/JukeboxPlayable.java b/src/main/java/net/minestom/server/item/component/JukeboxPlayable.java new file mode 100644 index 00000000000..b47858a73c2 --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/JukeboxPlayable.java @@ -0,0 +1,69 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minestom.server.MinecraftServer; +import net.minestom.server.instance.block.jukebox.JukeboxSong; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.NotNull; + +public record JukeboxPlayable(@NotNull DynamicRegistry.Key song, boolean showInTooltip) { + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + // For some reason I(matt) cannot discern, the wire format for this type can write the song + // as either a registry ID or a namespace ID. Minestom always writes as a registry id. + + @Override + public void write(@NotNull NetworkBuffer buffer, JukeboxPlayable value) { + buffer.write(NetworkBuffer.BOOLEAN, true); // First option (registry id) + buffer.write(JukeboxSong.NETWORK_TYPE, value.song); + buffer.write(NetworkBuffer.BOOLEAN, value.showInTooltip); + } + + @Override + public JukeboxPlayable read(@NotNull NetworkBuffer buffer) { + DynamicRegistry.Key song; + if (buffer.read(NetworkBuffer.BOOLEAN)) { + song = buffer.read(JukeboxSong.NETWORK_TYPE); + } else { + song = DynamicRegistry.Key.of(buffer.read(NetworkBuffer.STRING)); + final DynamicRegistry registry = MinecraftServer.getJukeboxSongRegistry(); + Check.stateCondition(registry.get(song) != null, "unknown song: {0}", song); + } + return new JukeboxPlayable(song, buffer.read(NetworkBuffer.BOOLEAN)); + } + }; + public static final BinaryTagSerializer NBT_TYPE = new BinaryTagSerializer<>() { + @Override + public @NotNull BinaryTag write(@NotNull Context context, @NotNull JukeboxPlayable value) { + return CompoundBinaryTag.builder() + .put("song", JukeboxSong.NBT_TYPE.write(context, value.song)) + .putBoolean("show_in_tooltip", value.showInTooltip) + .build(); + } + + @Override + public @NotNull JukeboxPlayable read(@NotNull Context context, @NotNull BinaryTag raw) { + if (!(raw instanceof CompoundBinaryTag tag)) throw new IllegalArgumentException("expected compound tag"); + return new JukeboxPlayable( + JukeboxSong.NBT_TYPE.read(context, tag.get("song")), + tag.getBoolean("show_in_tooltip") + ); + } + }; + + public JukeboxPlayable(@NotNull DynamicRegistry.Key song) { + this(song, true); + } + + public @NotNull JukeboxPlayable withSong(@NotNull DynamicRegistry.Key song) { + return new JukeboxPlayable(song, showInTooltip); + } + + public @NotNull JukeboxPlayable withTooltip(boolean showInTooltip) { + return new JukeboxPlayable(song, showInTooltip); + } + +} diff --git a/src/main/java/net/minestom/server/item/component/LodestoneTracker.java b/src/main/java/net/minestom/server/item/component/LodestoneTracker.java new file mode 100644 index 00000000000..befd8f6a5e5 --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/LodestoneTracker.java @@ -0,0 +1,51 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minestom.server.coordinate.Point; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.network.packet.server.play.data.WorldPos; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public record LodestoneTracker(@Nullable WorldPos target, boolean tracked) { + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, @NotNull LodestoneTracker value) { + buffer.writeOptional(WorldPos.NETWORK_TYPE, value.target); + buffer.write(NetworkBuffer.BOOLEAN, value.tracked); + } + + @Override + public @NotNull LodestoneTracker read(@NotNull NetworkBuffer buffer) { + return new LodestoneTracker( + buffer.readOptional(WorldPos.NETWORK_TYPE), + buffer.read(NetworkBuffer.BOOLEAN) + ); + } + }; + + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> new LodestoneTracker( + WorldPos.NBT_TYPE.read(tag.get("target")), + tag.getBoolean("tracked")), + value -> CompoundBinaryTag.builder() + .put("target", WorldPos.NBT_TYPE.write(value.target)) + .putBoolean("tracked", value.tracked) + .build() + ); + + public LodestoneTracker(@NotNull String dimension, @NotNull Point blockPosition, boolean tracked) { + this(new WorldPos(dimension, blockPosition), tracked); + } + + public @NotNull LodestoneTracker withTarget(@Nullable WorldPos target) { + return new LodestoneTracker(target, tracked); + } + + public @NotNull LodestoneTracker withTracked(boolean tracked) { + return new LodestoneTracker(target, tracked); + } + +} diff --git a/src/main/java/net/minestom/server/item/component/MapDecorations.java b/src/main/java/net/minestom/server/item/component/MapDecorations.java new file mode 100644 index 00000000000..4afbc0e84c3 --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/MapDecorations.java @@ -0,0 +1,64 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +public record MapDecorations(@NotNull Map decorations) { + + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> { + Map map = new HashMap<>(tag.size()); + for (Map.Entry entry : tag) { + if (!(entry.getValue() instanceof CompoundBinaryTag entryTag)) continue; + map.put(entry.getKey(), new Entry( + entryTag.getString("type"), + entryTag.getDouble("x"), + entryTag.getDouble("z"), + entryTag.getFloat("rotation") + )); + } + return new MapDecorations(map); + }, + decorations -> { + CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder(); + for (Map.Entry entry : decorations.decorations.entrySet()) { + CompoundBinaryTag entryTag = CompoundBinaryTag.builder() + .putString("type", entry.getValue().type) + .putDouble("x", entry.getValue().x) + .putDouble("z", entry.getValue().z) + .putFloat("rotation", entry.getValue().rotation) + .build(); + builder.put(entry.getKey(), entryTag); + } + return builder.build(); + } + ); + + public MapDecorations { + decorations = Map.copyOf(decorations); + } + + public @NotNull MapDecorations with(@NotNull String id, @NotNull String type, double x, double z, float rotation) { + return with(id, new Entry(type, x, z, rotation)); + } + + public @NotNull MapDecorations with(@NotNull String id, @NotNull Entry entry) { + Map newDecorations = new HashMap<>(decorations); + newDecorations.put(id, entry); + return new MapDecorations(newDecorations); + } + + public @NotNull MapDecorations remove(@NotNull String id) { + Map newDecorations = new HashMap<>(decorations); + newDecorations.remove(id); + return new MapDecorations(newDecorations); + } + + public record Entry(@NotNull String type, double x, double z, float rotation) { + } +} diff --git a/src/main/java/net/minestom/server/item/component/MapPostProcessing.java b/src/main/java/net/minestom/server/item/component/MapPostProcessing.java new file mode 100644 index 00000000000..a73723b3181 --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/MapPostProcessing.java @@ -0,0 +1,22 @@ +package net.minestom.server.item.component; + +import net.minestom.server.network.NetworkBuffer; +import org.jetbrains.annotations.NotNull; + +public enum MapPostProcessing { + LOCK, + SCALE; + private static final MapPostProcessing[] VALUES = values(); + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, MapPostProcessing value) { + buffer.write(NetworkBuffer.VAR_INT, value.ordinal()); + } + + @Override + public MapPostProcessing read(@NotNull NetworkBuffer buffer) { + return VALUES[buffer.read(NetworkBuffer.VAR_INT)]; + } + }; +} diff --git a/src/main/java/net/minestom/server/item/component/PotDecorations.java b/src/main/java/net/minestom/server/item/component/PotDecorations.java new file mode 100644 index 00000000000..a0de6724e88 --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/PotDecorations.java @@ -0,0 +1,37 @@ +package net.minestom.server.item.component; + +import net.minestom.server.item.Material; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public record PotDecorations( + @NotNull Material back, + @NotNull Material left, + @NotNull Material right, + @NotNull Material front +) { + public static final @NotNull Material DEFAULT_ITEM = Material.BRICK; + public static final PotDecorations EMPTY = new PotDecorations(DEFAULT_ITEM, DEFAULT_ITEM, DEFAULT_ITEM, DEFAULT_ITEM); + + public static NetworkBuffer.Type NETWORK_TYPE = Material.NETWORK_TYPE.list(4).map(PotDecorations::new, PotDecorations::asList); + public static BinaryTagSerializer NBT_TYPE = Material.NBT_TYPE.list().map(PotDecorations::new, PotDecorations::asList); + + public PotDecorations(@NotNull List list) { + this(getOrAir(list, 0), getOrAir(list, 1), getOrAir(list, 2), getOrAir(list, 3)); + } + + public PotDecorations(@NotNull Material material) { + this(material, material, material, material); + } + + public @NotNull List asList() { + return List.of(back, left, right, front); + } + + private static @NotNull Material getOrAir(@NotNull List list, int index) { + return index < list.size() ? list.get(index) : Material.BRICK; + } +} diff --git a/src/main/java/net/minestom/server/item/component/PotionContents.java b/src/main/java/net/minestom/server/item/component/PotionContents.java new file mode 100644 index 00000000000..fa399338175 --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/PotionContents.java @@ -0,0 +1,122 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.*; +import net.kyori.adventure.util.RGBLike; +import net.minestom.server.color.Color; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.potion.CustomPotionEffect; +import net.minestom.server.potion.PotionType; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public record PotionContents( + @Nullable PotionType potion, + @Nullable RGBLike customColor, + @NotNull List customEffects +) { + public static final int POTION_DRINK_TIME = 32; // 32 ticks, in ms + public static final PotionContents EMPTY = new PotionContents(null, null, List.of()); + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, PotionContents value) { + Integer typeId = value.potion == null ? null : value.potion.id(); + buffer.writeOptional(NetworkBuffer.VAR_INT, typeId); + buffer.writeOptional(Color.NETWORK_TYPE, value.customColor); + buffer.writeCollection(CustomPotionEffect.NETWORK_TYPE, value.customEffects); + } + + @Override + public PotionContents read(@NotNull NetworkBuffer buffer) { + Integer typeId = buffer.readOptional(NetworkBuffer.VAR_INT); + return new PotionContents( + typeId == null ? null : PotionType.fromId(typeId), + buffer.readOptional(Color.NETWORK_TYPE), + buffer.readCollection(CustomPotionEffect.NETWORK_TYPE, Short.MAX_VALUE) + ); + } + }; + + public static final BinaryTagSerializer NBT_TYPE = new BinaryTagSerializer<>() { + @Override + public @NotNull BinaryTag write(@NotNull PotionContents value) { + CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder(); + + if (value.potion != null) { + builder.put("potion", StringBinaryTag.stringBinaryTag(value.potion.name())); + } + + if (value.customColor != null) { + builder.put("custom_color", Color.NBT_TYPE.write(value.customColor)); + } + + if (!value.customEffects.isEmpty()) { + ListBinaryTag.Builder effectsBuilder = ListBinaryTag.builder(); + for (CustomPotionEffect effect : value.customEffects) { + effectsBuilder.add(CustomPotionEffect.NBT_TYPE.write(effect)); + } + builder.put("custom_effects", effectsBuilder.build()); + } + + return builder.build(); + } + + @Override + public @NotNull PotionContents read(@NotNull BinaryTag tag) { + // Can be a string with just a potion effect id + if (tag instanceof StringBinaryTag string) { + return new PotionContents(PotionType.fromNamespaceId(string.value()), null, List.of()); + } + + // Otherwise must be a compound + if (!(tag instanceof CompoundBinaryTag compound)) { + return EMPTY; + } + + PotionType potion = null; + if (compound.get("potion") instanceof StringBinaryTag potionTag) + potion = PotionType.fromNamespaceId(potionTag.value()); + + Color customColor = null; + if (compound.get("custom_color") instanceof IntBinaryTag colorTag) { + customColor = new Color(colorTag.value()); + } + + List customEffects = new ArrayList<>(); + ListBinaryTag customEffectsTag = compound.getList("custom_effects", BinaryTagTypes.COMPOUND); + for (BinaryTag customEffectTag : customEffectsTag) { + if (!(customEffectTag instanceof CompoundBinaryTag customEffectCompound)) { + continue; + } + customEffects.add(CustomPotionEffect.NBT_TYPE.read(customEffectCompound)); + } + + return new PotionContents(potion, customColor, customEffects); + } + }; + + public PotionContents { + customEffects = List.copyOf(customEffects); + } + + public PotionContents(@NotNull PotionType potion) { + this(potion, null, List.of()); + } + + public PotionContents(@NotNull PotionType potion, @NotNull RGBLike customColor) { + this(potion, customColor, List.of()); + } + + public PotionContents(@NotNull List customEffects) { + this(null, null, customEffects); + } + + public PotionContents(@NotNull CustomPotionEffect customEffect) { + this(null, null, List.of(customEffect)); + } + +} diff --git a/src/main/java/net/minestom/server/item/component/SeededContainerLoot.java b/src/main/java/net/minestom/server/item/component/SeededContainerLoot.java new file mode 100644 index 00000000000..cf10f051287 --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/SeededContainerLoot.java @@ -0,0 +1,16 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +public record SeededContainerLoot(@NotNull String lootTable, long seed) { + + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> new SeededContainerLoot(tag.getString("loot_table"), tag.getLong("seed")), + loot -> CompoundBinaryTag.builder() + .putString("loot_table", loot.lootTable) + .putLong("seed", loot.seed) + .build() + ); +} diff --git a/src/main/java/net/minestom/server/item/component/SuspiciousStewEffects.java b/src/main/java/net/minestom/server/item/component/SuspiciousStewEffects.java new file mode 100644 index 00000000000..681adcde649 --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/SuspiciousStewEffects.java @@ -0,0 +1,61 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.potion.PotionEffect; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public record SuspiciousStewEffects(@NotNull List effects) { + public static final int DEFAULT_DURATION = 160; + public static final SuspiciousStewEffects EMPTY = new SuspiciousStewEffects(List.of()); + + public static final NetworkBuffer.Type NETWORK_TYPE = Effect.NETWORK_TYPE.list(Short.MAX_VALUE).map(SuspiciousStewEffects::new, SuspiciousStewEffects::effects); + public static final BinaryTagSerializer NBT_TYPE = Effect.NBT_TYPE.list().map(SuspiciousStewEffects::new, SuspiciousStewEffects::effects); + + public SuspiciousStewEffects { + effects = List.copyOf(effects); + } + + public SuspiciousStewEffects(@NotNull Effect effect) { + this(List.of(effect)); + } + + public @NotNull SuspiciousStewEffects with(@NotNull Effect effect) { + List newEffects = new ArrayList<>(effects); + newEffects.add(effect); + return new SuspiciousStewEffects(newEffects); + } + + public record Effect(@NotNull PotionEffect id, int durationTicks) { + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, Effect value) { + buffer.write(PotionEffect.NETWORK_TYPE, value.id); + buffer.write(NetworkBuffer.VAR_INT, value.durationTicks); + } + + @Override + public Effect read(@NotNull NetworkBuffer buffer) { + return new Effect(buffer.read(PotionEffect.NETWORK_TYPE), buffer.read(NetworkBuffer.VAR_INT)); + } + }; + + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> new Effect(PotionEffect.fromNamespaceId(tag.getString("id")), + tag.getInt("duration", DEFAULT_DURATION)), + value -> CompoundBinaryTag.builder() + .putString("id", value.id.name()) + .putInt("duration", value.durationTicks) + .build() + ); + + public Effect(@NotNull PotionEffect id) { + this(id, DEFAULT_DURATION); + } + } +} diff --git a/src/main/java/net/minestom/server/item/component/Tool.java b/src/main/java/net/minestom/server/item/component/Tool.java new file mode 100644 index 00000000000..68e93053a52 --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/Tool.java @@ -0,0 +1,97 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.ByteBinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.FloatBinaryTag; +import net.minestom.server.instance.block.predicate.BlockTypeFilter; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Objects; + +public record Tool(@NotNull List rules, float defaultMiningSpeed, int damagePerBlock) { + public static final float DEFAULT_MINING_SPEED = 1.0f; + public static final int DEFAULT_DAMAGE_PER_BLOCK = 1; + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + private static final NetworkBuffer.Type> RULE_LIST_TYPE = Rule.NETWORK_TYPE.list(Short.MAX_VALUE); + + @Override + public void write(@NotNull NetworkBuffer buffer, Tool value) { + RULE_LIST_TYPE.write(buffer, value.rules()); + buffer.write(NetworkBuffer.FLOAT, value.defaultMiningSpeed()); + buffer.write(NetworkBuffer.VAR_INT, value.damagePerBlock()); + } + + @Override + public Tool read(@NotNull NetworkBuffer buffer) { + return new Tool( + RULE_LIST_TYPE.read(buffer), + buffer.read(NetworkBuffer.FLOAT), + buffer.read(NetworkBuffer.VAR_INT) + ); + } + }; + public static final BinaryTagSerializer NBT_TYPE = new BinaryTagSerializer<>() { + private static final BinaryTagSerializer> RULE_LIST_TYPE = Rule.NBT_TYPE.list(); + + @Override + public @NotNull BinaryTag write(@NotNull Tool value) { + return CompoundBinaryTag.builder() + .put("rules", RULE_LIST_TYPE.write(value.rules())) + .putFloat("default_mining_speed", value.defaultMiningSpeed()) + .putInt("damage_per_block", value.damagePerBlock()) + .build(); + } + + @Override + public @NotNull Tool read(@NotNull BinaryTag tag) { + if (!(tag instanceof CompoundBinaryTag compound)) + throw new IllegalArgumentException("Expected a compound tag, got " + tag.type()); + return new Tool( + RULE_LIST_TYPE.read(Objects.requireNonNull(compound.get("rules"))), + compound.getFloat("default_mining_speed", DEFAULT_MINING_SPEED), + compound.getInt("damage_per_block", DEFAULT_DAMAGE_PER_BLOCK) + ); + } + }; + + public record Rule(@NotNull BlockTypeFilter blocks, @Nullable Float speed, @Nullable Boolean correctForDrops) { + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, Rule value) { + buffer.write(BlockTypeFilter.NETWORK_TYPE, value.blocks()); + buffer.writeOptional(NetworkBuffer.FLOAT, value.speed()); + buffer.writeOptional(NetworkBuffer.BOOLEAN, value.correctForDrops()); + } + + @Override + public Rule read(@NotNull NetworkBuffer buffer) { + return new Rule( + buffer.read(BlockTypeFilter.NETWORK_TYPE), + buffer.readOptional(NetworkBuffer.FLOAT), + buffer.readOptional(NetworkBuffer.BOOLEAN) + ); + } + }; + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> new Rule( + BlockTypeFilter.NBT_TYPE.read(Objects.requireNonNull(tag.get("blocks"))), + tag.get("speed") instanceof FloatBinaryTag speed ? speed.floatValue() : null, + tag.get("correct_for_drops") instanceof ByteBinaryTag correctForDrops ? correctForDrops.value() != 0 : null + ), + rule -> { + CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder(); + builder.put("blocks", BlockTypeFilter.NBT_TYPE.write(rule.blocks())); + if (rule.speed() != null) builder.putFloat("speed", rule.speed()); + if (rule.correctForDrops() != null) builder.putBoolean("correct_for_drops", rule.correctForDrops()); + return builder.build(); + } + ); + } +} diff --git a/src/main/java/net/minestom/server/item/component/Unbreakable.java b/src/main/java/net/minestom/server/item/component/Unbreakable.java new file mode 100644 index 00000000000..087dc71c16e --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/Unbreakable.java @@ -0,0 +1,32 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +public record Unbreakable(boolean showInTooltip) { + public static final Unbreakable DEFAULT = new Unbreakable(); + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type() { + @Override + public void write(@NotNull NetworkBuffer buffer, Unbreakable value) { + buffer.write(NetworkBuffer.BOOLEAN, value.showInTooltip()); + } + + @Override + public Unbreakable read(@NotNull NetworkBuffer buffer) { + return new Unbreakable(buffer.read(NetworkBuffer.BOOLEAN)); + } + }; + + public Unbreakable() { + this(true); + } + + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> new Unbreakable(tag.getBoolean("showInTooltip", true)), + unbreakable -> CompoundBinaryTag.builder().putBoolean("showInTooltip", unbreakable.showInTooltip()).build() + ); + +} diff --git a/src/main/java/net/minestom/server/item/component/WritableBookContent.java b/src/main/java/net/minestom/server/item/component/WritableBookContent.java new file mode 100644 index 00000000000..b416f20ccb5 --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/WritableBookContent.java @@ -0,0 +1,57 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.ListBinaryTag; +import net.minestom.server.item.book.FilteredText; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public record WritableBookContent(@NotNull List> pages) { + public static final WritableBookContent EMPTY = new WritableBookContent(List.of()); + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, WritableBookContent value) { + buffer.writeCollection(FilteredText.STRING_NETWORK_TYPE, value.pages); + } + + @Override + public WritableBookContent read(@NotNull NetworkBuffer buffer) { + return new WritableBookContent(buffer.readCollection(FilteredText.STRING_NETWORK_TYPE, 100)); + } + }; + + public static final BinaryTagSerializer NBT_TYPE = new BinaryTagSerializer<>() { + @Override + public @NotNull BinaryTag write(@NotNull WritableBookContent value) { + ListBinaryTag.Builder pages = ListBinaryTag.builder(); + for (FilteredText page : value.pages) { + pages.add(FilteredText.STRING_NBT_TYPE.write(page)); + } + return CompoundBinaryTag.builder().put("pages", pages.build()).build(); + } + + @Override + public @NotNull WritableBookContent read(@NotNull BinaryTag tag) { + if (!(tag instanceof CompoundBinaryTag compound)) return EMPTY; + ListBinaryTag pagesTag = compound.getList("pages"); + if (pagesTag.size() == 0) return EMPTY; + + List> pages = new ArrayList<>(pagesTag.size()); + for (BinaryTag pageTag : pagesTag) { + pages.add(FilteredText.STRING_NBT_TYPE.read(pageTag)); + } + return new WritableBookContent(pages); + } + }; + + public WritableBookContent { + pages = List.copyOf(pages); + } + +} diff --git a/src/main/java/net/minestom/server/item/component/WrittenBookContent.java b/src/main/java/net/minestom/server/item/component/WrittenBookContent.java new file mode 100644 index 00000000000..ce4d55bd2bd --- /dev/null +++ b/src/main/java/net/minestom/server/item/component/WrittenBookContent.java @@ -0,0 +1,76 @@ +package net.minestom.server.item.component; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.ListBinaryTag; +import net.kyori.adventure.text.Component; +import net.minestom.server.item.book.FilteredText; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public record WrittenBookContent(@NotNull List> pages, @NotNull FilteredText title, @NotNull String author, int generation, boolean resolved) { + public static final WrittenBookContent EMPTY = new WrittenBookContent(List.of(), new FilteredText<>("", null), "", 0, true); + + public static final @NotNull NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, WrittenBookContent value) { + buffer.write(FilteredText.STRING_NETWORK_TYPE, value.title); + buffer.write(NetworkBuffer.STRING, value.author); + buffer.write(NetworkBuffer.VAR_INT, value.generation); + buffer.writeCollection(FilteredText.COMPONENT_NETWORK_TYPE, value.pages); + buffer.write(NetworkBuffer.BOOLEAN, value.resolved); + } + + @Override + public WrittenBookContent read(@NotNull NetworkBuffer buffer) { + FilteredText title = buffer.read(FilteredText.STRING_NETWORK_TYPE); + String author = buffer.read(NetworkBuffer.STRING); + int generation = buffer.read(NetworkBuffer.VAR_INT); + List> pages = buffer.readCollection(FilteredText.COMPONENT_NETWORK_TYPE, 100); + boolean resolved = buffer.read(NetworkBuffer.BOOLEAN); + return new WrittenBookContent(pages, title, author, generation, resolved); + } + }; + + public static final @NotNull BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + compound -> { + ListBinaryTag pagesTag = compound.getList("pages"); + List> pages = pagesTag.stream() + .map(FilteredText.COMPONENT_NBT_TYPE::read) + .toList(); + FilteredText title = FilteredText.STRING_NBT_TYPE.read(compound.get("title")); + String author = compound.getString("author"); + int generation = compound.getInt("generation"); + boolean resolved = compound.getBoolean("resolved"); + return new WrittenBookContent(pages, title, author, generation, resolved); + }, + value -> { + ListBinaryTag.Builder pagesTag = ListBinaryTag.builder(); + for (FilteredText page : value.pages) { + pagesTag.add(FilteredText.COMPONENT_NBT_TYPE.write(page)); + } + return CompoundBinaryTag.builder() + .put("pages", pagesTag.build()) + .put("title", FilteredText.STRING_NBT_TYPE.write(value.title)) + .putString("author", value.author) + .putInt("generation", value.generation) + .putBoolean("resolved", value.resolved) + .build(); + } + ); + + public WrittenBookContent { + pages = List.copyOf(pages); + } + + public WrittenBookContent(@NotNull List pages, @NotNull String title, @NotNull String author) { + this(pages, title, author, 0, true); + } + + public WrittenBookContent(@NotNull List pages, @NotNull String title, @NotNull String author, int generation, boolean resolved) { + this(pages.stream().map(page -> new FilteredText<>(page, null)).toList(), new FilteredText<>(title, null), author, generation, resolved); + } +} diff --git a/src/main/java/net/minestom/server/item/crossbow/CrossbowChargingSounds.java b/src/main/java/net/minestom/server/item/crossbow/CrossbowChargingSounds.java new file mode 100644 index 00000000000..12620d2cb85 --- /dev/null +++ b/src/main/java/net/minestom/server/item/crossbow/CrossbowChargingSounds.java @@ -0,0 +1,21 @@ +package net.minestom.server.item.crossbow; + +import net.minestom.server.gamedata.tags.Tag; +import net.minestom.server.registry.ObjectSet; +import net.minestom.server.sound.SoundEvent; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.Nullable; + +public record CrossbowChargingSounds( + @Nullable ObjectSet start, + @Nullable ObjectSet mid, + @Nullable ObjectSet end +) { + private static final BinaryTagSerializer> SOUND_SET_NBT_TYPE = ObjectSet.nbtType(Tag.BasicType.SOUND_EVENTS); + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.object( + "start", SOUND_SET_NBT_TYPE.optional(), CrossbowChargingSounds::start, + "mid", SOUND_SET_NBT_TYPE.optional(), CrossbowChargingSounds::mid, + "end", SOUND_SET_NBT_TYPE.optional(), CrossbowChargingSounds::end, + CrossbowChargingSounds::new + ); +} diff --git a/src/main/java/net/minestom/server/item/enchant/AttributeEffect.java b/src/main/java/net/minestom/server/item/enchant/AttributeEffect.java new file mode 100644 index 00000000000..fb29f2cf946 --- /dev/null +++ b/src/main/java/net/minestom/server/item/enchant/AttributeEffect.java @@ -0,0 +1,28 @@ +package net.minestom.server.item.enchant; + +import net.minestom.server.entity.attribute.Attribute; +import net.minestom.server.entity.attribute.AttributeOperation; +import net.minestom.server.utils.NamespaceID; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +public record AttributeEffect( + @NotNull NamespaceID id, + @NotNull Attribute attribute, + @NotNull LevelBasedValue amount, + @NotNull AttributeOperation operation +) implements Enchantment.Effect, LocationEffect { + + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.object( + "id", BinaryTagSerializer.STRING.map(NamespaceID::from, NamespaceID::asString), AttributeEffect::id, + "attribute", Attribute.NBT_TYPE, AttributeEffect::attribute, + "amount", LevelBasedValue.NBT_TYPE, AttributeEffect::amount, + "operation", AttributeOperation.NBT_TYPE, AttributeEffect::operation, + AttributeEffect::new + ); + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } +} diff --git a/src/main/java/net/minestom/server/item/enchant/ConditionalEffect.java b/src/main/java/net/minestom/server/item/enchant/ConditionalEffect.java new file mode 100644 index 00000000000..0d60f2a52cd --- /dev/null +++ b/src/main/java/net/minestom/server/item/enchant/ConditionalEffect.java @@ -0,0 +1,21 @@ +package net.minestom.server.item.enchant; + +import net.minestom.server.condition.DataPredicate; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public record ConditionalEffect( + @NotNull E effect, + @Nullable DataPredicate requirements +) implements Enchantment.Effect { + + public static @NotNull BinaryTagSerializer> nbtType(@NotNull BinaryTagSerializer effectType) { + return BinaryTagSerializer.object( + "effect", effectType, ConditionalEffect::effect, + "requirements", DataPredicate.NBT_TYPE.optional(), ConditionalEffect::requirements, + ConditionalEffect::new + ); + } + +} diff --git a/src/main/java/net/minestom/server/item/enchant/DamageImmunityEffect.java b/src/main/java/net/minestom/server/item/enchant/DamageImmunityEffect.java new file mode 100644 index 00000000000..a28892a344b --- /dev/null +++ b/src/main/java/net/minestom/server/item/enchant/DamageImmunityEffect.java @@ -0,0 +1,14 @@ +package net.minestom.server.item.enchant; + +import net.minestom.server.utils.Unit; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +public final class DamageImmunityEffect implements Enchantment.Effect { + public static final DamageImmunityEffect INSTANCE = new DamageImmunityEffect(); + + public static final @NotNull BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.UNIT + .map(ignored -> DamageImmunityEffect.INSTANCE, ignored -> Unit.INSTANCE); + + private DamageImmunityEffect() {} +} diff --git a/src/main/java/net/minestom/server/item/enchant/EffectComponent.java b/src/main/java/net/minestom/server/item/enchant/EffectComponent.java new file mode 100644 index 00000000000..d7a58d2091e --- /dev/null +++ b/src/main/java/net/minestom/server/item/enchant/EffectComponent.java @@ -0,0 +1,80 @@ +package net.minestom.server.item.enchant; + +import net.minestom.server.component.DataComponent; +import net.minestom.server.component.DataComponentMap; +import net.minestom.server.gamedata.tags.Tag; +import net.minestom.server.item.crossbow.CrossbowChargingSounds; +import net.minestom.server.registry.ObjectSet; +import net.minestom.server.sound.SoundEvent; +import net.minestom.server.utils.NamespaceID; +import net.minestom.server.utils.Unit; +import net.minestom.server.utils.collection.ObjectArray; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class EffectComponent { + static final Map> NAMESPACES = new HashMap<>(32); + static final ObjectArray> IDS = ObjectArray.singleThread(32); + + public static final DataComponent>> DAMAGE_PROTECTION = register("damage_protection", ConditionalEffect.nbtType(ValueEffect.NBT_TYPE).list()); + public static final DataComponent>> DAMAGE_IMMUNITY = register("damage_immunity", ConditionalEffect.nbtType(DamageImmunityEffect.NBT_TYPE).list()); + public static final DataComponent>> DAMAGE = register("damage", ConditionalEffect.nbtType(ValueEffect.NBT_TYPE).list()); + public static final DataComponent>> SMASH_DAMAGE_PER_FALLEN_BLOCK = register("smash_damage_per_fallen_block", ConditionalEffect.nbtType(ValueEffect.NBT_TYPE).list()); + public static final DataComponent>> KNOCKBACK = register("knockback", ConditionalEffect.nbtType(ValueEffect.NBT_TYPE).list()); + public static final DataComponent>> ARMOR_EFFECTIVENESS = register("armor_effectiveness", ConditionalEffect.nbtType(ValueEffect.NBT_TYPE).list()); + public static final DataComponent>> POST_ATTACK = register("post_attack", TargetedConditionalEffect.nbtType(EntityEffect.NBT_TYPE).list()); + public static final DataComponent>> HIT_BLOCK = register("hit_block", ConditionalEffect.nbtType(EntityEffect.NBT_TYPE).list()); + public static final DataComponent>> ITEM_DAMAGE = register("item_damage", ConditionalEffect.nbtType(ValueEffect.NBT_TYPE).list()); + public static final DataComponent> ATTRIBUTES = register("attributes", AttributeEffect.NBT_TYPE.list()); + public static final DataComponent>> EQUIPMENT_DROPS = register("equipment_drops", TargetedConditionalEffect.nbtType(ValueEffect.NBT_TYPE).list()); + public static final DataComponent>> LOCATION_CHANGED = register("location_changed", ConditionalEffect.nbtType(LocationEffect.NBT_TYPE).list()); + public static final DataComponent>> TICK = register("tick", ConditionalEffect.nbtType(EntityEffect.NBT_TYPE).list()); + public static final DataComponent>> AMMO_USE = register("ammo_use", ConditionalEffect.nbtType(ValueEffect.NBT_TYPE).list()); + public static final DataComponent>> PROJECTILE_PIERCING = register("projectile_piercing", ConditionalEffect.nbtType(ValueEffect.NBT_TYPE).list()); + public static final DataComponent>> PROJECTILE_SPAWNED = register("projectile_spawned", ConditionalEffect.nbtType(EntityEffect.NBT_TYPE).list()); + public static final DataComponent>> PROJECTILE_SPREAD = register("projectile_spread", ConditionalEffect.nbtType(ValueEffect.NBT_TYPE).list()); + public static final DataComponent>> PROJECTILE_COUNT = register("projectile_count", ConditionalEffect.nbtType(ValueEffect.NBT_TYPE).list()); + public static final DataComponent>> TRIDENT_RETURN_ACCELERATION = register("trident_return_acceleration", ConditionalEffect.nbtType(ValueEffect.NBT_TYPE).list()); + public static final DataComponent>> FISHING_TIME_REDUCTION = register("fishing_time_reduction", ConditionalEffect.nbtType(ValueEffect.NBT_TYPE).list()); + public static final DataComponent>> FISHING_LUCK_BONUS = register("fishing_luck_bonus", ConditionalEffect.nbtType(ValueEffect.NBT_TYPE).list()); + public static final DataComponent>> BLOCK_EXPERIENCE = register("block_experience", ConditionalEffect.nbtType(ValueEffect.NBT_TYPE).list()); + public static final DataComponent>> MOB_EXPERIENCE = register("mob_experience", ConditionalEffect.nbtType(ValueEffect.NBT_TYPE).list()); + public static final DataComponent>> REPAIR_WITH_XP = register("repair_with_xp", ConditionalEffect.nbtType(ValueEffect.NBT_TYPE).list()); + public static final DataComponent CROSSBOW_CHARGE_TIME = register("crossbow_charge_time", ValueEffect.NBT_TYPE); + public static final DataComponent> CROSSBOW_CHARGING_SOUNDS = register("crossbow_charging_sounds", CrossbowChargingSounds.NBT_TYPE.list()); + public static final DataComponent>> TRIDENT_SOUND = register("trident_sound", ObjectSet.nbtType(Tag.BasicType.SOUND_EVENTS).list()); + public static final DataComponent PREVENT_EQUIPMENT_DROP = register("prevent_equipment_drop", BinaryTagSerializer.UNIT); + public static final DataComponent PREVENT_ARMOR_CHANGE = register("prevent_armor_change", BinaryTagSerializer.UNIT); + public static final DataComponent TRIDENT_SPIN_ATTACK_STRENGTH = register("trident_spin_attack_strength", ValueEffect.NBT_TYPE); + + public static final BinaryTagSerializer MAP_NBT_TYPE = DataComponentMap.nbtType(EffectComponent::fromId, EffectComponent::fromNamespaceId); + + public static @Nullable DataComponent fromNamespaceId(@NotNull String namespaceId) { + return NAMESPACES.get(namespaceId); + } + + public static @Nullable DataComponent fromNamespaceId(@NotNull NamespaceID namespaceId) { + return fromNamespaceId(namespaceId.asString()); + } + + public static @Nullable DataComponent fromId(int id) { + return IDS.get(id); + } + + public static @NotNull Collection> values() { + return NAMESPACES.values(); + } + + static DataComponent register(@NotNull String name, @Nullable BinaryTagSerializer nbt) { + DataComponent impl = DataComponent.createHeadless(NAMESPACES.size(), NamespaceID.from(name), null, nbt); + NAMESPACES.put(impl.name(), impl); + IDS.set(impl.id(), impl); + return impl; + } +} diff --git a/src/main/java/net/minestom/server/item/enchant/Enchantment.java b/src/main/java/net/minestom/server/item/enchant/Enchantment.java new file mode 100644 index 00000000000..91b345b06d4 --- /dev/null +++ b/src/main/java/net/minestom/server/item/enchant/Enchantment.java @@ -0,0 +1,186 @@ +package net.minestom.server.item.enchant; + +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.text.Component; +import net.minestom.server.component.DataComponent; +import net.minestom.server.component.DataComponentMap; +import net.minestom.server.entity.EquipmentSlotGroup; +import net.minestom.server.item.Material; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.registry.*; +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.List; + +public sealed interface Enchantment extends ProtocolObject, Enchantments permits EnchantmentImpl { + @NotNull NetworkBuffer.Type> NETWORK_TYPE = NetworkBuffer.RegistryKey(Registries::enchantment); + @NotNull BinaryTagSerializer> NBT_TYPE = BinaryTagSerializer.registryKey(Registries::enchantment); + + static @NotNull Builder builder() { + return new Builder(); + } + + /** + *

Creates a new registry for enchantments, loading the vanilla enchantments.

+ * + * @see net.minestom.server.MinecraftServer to get an existing instance of the registry + */ + @ApiStatus.Internal + static @NotNull DynamicRegistry createDefaultRegistry(@NotNull Registries registries) { + return DynamicRegistry.create( + "minecraft:enchantment", EnchantmentImpl.REGISTRY_NBT_TYPE, + registries, Registry.Resource.ENCHANTMENTS + ); + } + + @NotNull Component description(); + + @NotNull ObjectSet exclusiveSet(); + + @NotNull ObjectSet supportedItems(); + + @NotNull ObjectSet primaryItems(); + + int weight(); + + int maxLevel(); + + @NotNull Cost minCost(); + + @NotNull Cost maxCost(); + + int anvilCost(); + + @NotNull List slots(); + + @NotNull DataComponentMap effects(); + + @Override + @Nullable Registry.EnchantmentEntry registry(); + + enum Target { + ATTACKER, + DAMAGING_ENTITY, + VICTIM; + + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.fromEnumStringable(Target.class); + } + + sealed interface Effect permits AttributeEffect, ConditionalEffect, DamageImmunityEffect, EntityEffect, LocationEffect, TargetedConditionalEffect, ValueEffect { + + } + + record Cost(int base, int perLevelAboveFirst) { + public static final Cost DEFAULT = new Cost(1, 1); + + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> new Cost(tag.getInt("base"), tag.getInt("per_level_above_first")), + cost -> CompoundBinaryTag.builder() + .putInt("base", cost.base) + .putInt("per_level_above_first", cost.perLevelAboveFirst) + .build() + ); + } + + class Builder { + private Component description = Component.empty(); + private ObjectSet exclusiveSet = ObjectSet.empty(); + private ObjectSet supportedItems = ObjectSet.empty(); + private ObjectSet primaryItems = ObjectSet.empty(); + private int weight = 1; + private int maxLevel = 1; + private Cost minCost = Cost.DEFAULT; + private Cost maxCost = Cost.DEFAULT; + private int anvilCost = 0; + private List slots = List.of(); + private DataComponentMap.Builder effects = DataComponentMap.builder(); + + private Builder() { + } + + public @NotNull Builder description(@NotNull Component description) { + this.description = description; + return this; + } + + public @NotNull Builder exclusiveSet(@NotNull ObjectSet exclusiveSet) { + this.exclusiveSet = exclusiveSet; + return this; + } + + public @NotNull Builder supportedItems(@NotNull ObjectSet supportedItems) { + this.supportedItems = supportedItems; + return this; + } + + public @NotNull Builder primaryItems(@NotNull ObjectSet primaryItems) { + this.primaryItems = primaryItems; + return this; + } + + public @NotNull Builder weight(int weight) { + this.weight = weight; + return this; + } + + public @NotNull Builder maxLevel(int maxLevel) { + this.maxLevel = maxLevel; + return this; + } + + public @NotNull Builder minCost(int base, int perLevelAboveFirst) { + return minCost(new Cost(base, perLevelAboveFirst)); + } + + public @NotNull Builder minCost(@NotNull Cost minCost) { + this.minCost = minCost; + return this; + } + + public @NotNull Builder maxCost(int base, int perLevelAboveFirst) { + return maxCost(new Cost(base, perLevelAboveFirst)); + } + + public @NotNull Builder maxCost(@NotNull Cost maxCost) { + this.maxCost = maxCost; + return this; + } + + public @NotNull Builder anvilCost(int anvilCost) { + this.anvilCost = anvilCost; + return this; + } + + public @NotNull Builder slots(@NotNull EquipmentSlotGroup... slots) { + this.slots = List.of(slots); + return this; + } + + public @NotNull Builder slots(@NotNull List slots) { + this.slots = slots; + return this; + } + + public @NotNull Builder effect(@NotNull DataComponent component, @NotNull T value) { + effects.set(component, value); + return this; + } + + public @NotNull Builder effects(@NotNull DataComponentMap effects) { + this.effects = effects.toBuilder(); + return this; + } + + public @NotNull Enchantment build() { + return new EnchantmentImpl( + description, exclusiveSet, supportedItems, + primaryItems, weight, maxLevel, minCost, maxCost, + anvilCost, slots, effects.build(), null + ); + } + } + +} diff --git a/src/main/java/net/minestom/server/item/enchant/EnchantmentImpl.java b/src/main/java/net/minestom/server/item/enchant/EnchantmentImpl.java new file mode 100644 index 00000000000..c2d25d9d827 --- /dev/null +++ b/src/main/java/net/minestom/server/item/enchant/EnchantmentImpl.java @@ -0,0 +1,102 @@ +package net.minestom.server.item.enchant; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.TagStringIOExt; +import net.kyori.adventure.text.Component; +import net.minestom.server.component.DataComponentMap; +import net.minestom.server.entity.EquipmentSlotGroup; +import net.minestom.server.gamedata.tags.Tag; +import net.minestom.server.item.Material; +import net.minestom.server.registry.ObjectSet; +import net.minestom.server.registry.Registries; +import net.minestom.server.registry.Registry; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.util.List; + +record EnchantmentImpl( + @NotNull Component description, + @NotNull ObjectSet exclusiveSet, + @NotNull ObjectSet supportedItems, + @NotNull ObjectSet primaryItems, + int weight, + int maxLevel, + @NotNull Cost minCost, + @NotNull Cost maxCost, + int anvilCost, + @NotNull List slots, + @NotNull DataComponentMap effects, + @Nullable Registry.EnchantmentEntry registry +) implements Enchantment { + + private static final BinaryTagSerializer> ENCHANTMENT_OBJECT_SET_NBT_TYPE = ObjectSet.nbtType(Tag.BasicType.ENCHANTMENTS); + private static final BinaryTagSerializer> MATERIAL_OBJECT_SET_NBT_TYPE = ObjectSet.nbtType(Tag.BasicType.ITEMS); + private static final BinaryTagSerializer> SLOTS_NBT_TYPE = EquipmentSlotGroup.NBT_TYPE.list(); + static final BinaryTagSerializer REGISTRY_NBT_TYPE = new BinaryTagSerializer<>() { + @Override + public @NotNull BinaryTag write(@NotNull Context context, @NotNull Enchantment value) { + return CompoundBinaryTag.builder() + .put("description", BinaryTagSerializer.NBT_COMPONENT.write(context, value.description())) + .put("exclusive_set", ENCHANTMENT_OBJECT_SET_NBT_TYPE.write(context, value.exclusiveSet())) + .put("supported_items", MATERIAL_OBJECT_SET_NBT_TYPE.write(context, value.supportedItems())) + .put("primary_items", MATERIAL_OBJECT_SET_NBT_TYPE.write(context, value.primaryItems())) + .putInt("weight", value.weight()) + .putInt("max_level", value.maxLevel()) + .put("min_cost", Cost.NBT_TYPE.write(context, value.minCost())) + .put("max_cost", Cost.NBT_TYPE.write(context, value.maxCost())) + .putInt("anvil_cost", value.anvilCost()) + .put("slots", SLOTS_NBT_TYPE.write(context, value.slots())) + .put("effects", EffectComponent.MAP_NBT_TYPE.write(context, value.effects())) + .build(); + } + + @Override + public @NotNull Enchantment read(@NotNull Context context, @NotNull BinaryTag raw) { + if (!(raw instanceof CompoundBinaryTag tag)) throw new IllegalArgumentException("Expected a compound tag"); + return new EnchantmentImpl( + BinaryTagSerializer.NBT_COMPONENT.read(context, tag.get("description")), + ENCHANTMENT_OBJECT_SET_NBT_TYPE.read(context, tag.get("exclusive_set")), + MATERIAL_OBJECT_SET_NBT_TYPE.read(context, tag.get("supported_items")), + MATERIAL_OBJECT_SET_NBT_TYPE.read(context, tag.get("primary_items")), + tag.getInt("weight"), + tag.getInt("max_level"), + Cost.NBT_TYPE.read(context, tag.get("min_cost")), + Cost.NBT_TYPE.read(context, tag.get("max_cost")), + tag.getInt("anvil_cost"), + SLOTS_NBT_TYPE.read(context, tag.get("slots")), + tag.get("effects") instanceof CompoundBinaryTag effects ? EffectComponent.MAP_NBT_TYPE.read(context, effects) : DataComponentMap.EMPTY, + null + ); + } + }; + + EnchantmentImpl { + slots = List.copyOf(slots); + } + + EnchantmentImpl(@NotNull Registries registries, @NotNull Registry.EnchantmentEntry registry) { + this(fromRawRegistry(registries, registry.raw()), registry); + } + + EnchantmentImpl(@NotNull Enchantment enchantment, @NotNull Registry.EnchantmentEntry registry) { + this(enchantment.description(), enchantment.exclusiveSet(), + enchantment.supportedItems(), enchantment.primaryItems(), + enchantment.weight(), enchantment.maxLevel(), enchantment.minCost(), + enchantment.maxCost(), enchantment.anvilCost(), enchantment.slots(), + enchantment.effects(), registry); + } + + private static @NotNull Enchantment fromRawRegistry(@NotNull Registries registries, @NotNull String raw) { + try { + BinaryTagSerializer.Context context = new BinaryTagSerializer.ContextWithRegistries(registries, false); + return REGISTRY_NBT_TYPE.read(context, TagStringIOExt.readTag(raw)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/net/minestom/server/item/enchant/EntityEffect.java b/src/main/java/net/minestom/server/item/enchant/EntityEffect.java new file mode 100644 index 00000000000..cdd2edb7e50 --- /dev/null +++ b/src/main/java/net/minestom/server/item/enchant/EntityEffect.java @@ -0,0 +1,226 @@ +package net.minestom.server.item.enchant; + +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minestom.server.entity.damage.DamageType; +import net.minestom.server.gamedata.DataPack; +import net.minestom.server.gamedata.tags.Tag; +import net.minestom.server.potion.PotionEffect; +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.registry.ObjectSet; +import net.minestom.server.registry.Registries; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + + +public non-sealed interface EntityEffect extends Enchantment.Effect { + + @NotNull BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.registryTaggedUnion( + Registries::enchantmentEntityEffects, EntityEffect::nbtType, "type"); + + @ApiStatus.Internal + static @NotNull DynamicRegistry> createDefaultRegistry() { + final DynamicRegistry> registry = DynamicRegistry.create("minestom:enchantment_value_effect"); + registry.register("all_of", AllOf.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("apply_mob_effect", ApplyPotionEffect.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("damage_entity", DamageEntity.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("damage_item", DamageItem.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("explode", Explode.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("ignite", Ignite.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("play_sound", PlaySound.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("replace_block", ReplaceBlock.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("replace_disk", ReplaceDisc.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("run_function", RunFunction.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("set_block_properties", SetBlockProperties.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("spawn_particles", SpawnParticles.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("summon_entity", SummonEntity.NBT_TYPE, DataPack.MINECRAFT_CORE); + return registry; + } + + @NotNull BinaryTagSerializer nbtType(); + + record AllOf(@NotNull List effect) implements EntityEffect { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.object( + "effects", EntityEffect.NBT_TYPE.list(), AllOf::effect, + AllOf::new + ); + + public AllOf { + effect = List.copyOf(effect); + } + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + + record ApplyPotionEffect( + @NotNull ObjectSet toApply, + @NotNull LevelBasedValue minDuration, + @NotNull LevelBasedValue maxDuration, + @NotNull LevelBasedValue minAmplifier, + @NotNull LevelBasedValue maxAmplifier + ) implements EntityEffect, LocationEffect { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.object( + "to_apply", ObjectSet.nbtType(Tag.BasicType.POTION_EFFECTS), ApplyPotionEffect::toApply, + "min_duration", LevelBasedValue.NBT_TYPE, ApplyPotionEffect::minDuration, + "max_duration", LevelBasedValue.NBT_TYPE, ApplyPotionEffect::maxDuration, + "min_amplifier", LevelBasedValue.NBT_TYPE, ApplyPotionEffect::minAmplifier, + "max_amplifier", LevelBasedValue.NBT_TYPE, ApplyPotionEffect::maxAmplifier, + ApplyPotionEffect::new + ); + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + + record DamageEntity( + @NotNull DynamicRegistry.Key damageType, + @NotNull LevelBasedValue minDamage, + @NotNull LevelBasedValue maxDamage + ) implements EntityEffect, LocationEffect { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.object( + "damage_type", DamageType.NBT_TYPE, DamageEntity::damageType, + "min_damage", LevelBasedValue.NBT_TYPE, DamageEntity::minDamage, + "max_damage", LevelBasedValue.NBT_TYPE, DamageEntity::maxDamage, + DamageEntity::new + ); + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + + record DamageItem(@NotNull LevelBasedValue amount) implements EntityEffect, LocationEffect { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.object( + "amount", LevelBasedValue.NBT_TYPE, DamageItem::amount, + DamageItem::new + ); + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + + record Explode( + CompoundBinaryTag content + //todo + ) implements EntityEffect, LocationEffect { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map(Explode::new, Explode::content); + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + + record Ignite(@NotNull LevelBasedValue duration) implements EntityEffect, LocationEffect { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.object( + "duration", LevelBasedValue.NBT_TYPE, Ignite::duration, + Ignite::new + ); + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + + record PlaySound( + CompoundBinaryTag content +// @NotNull SoundEvent sound, +// Object volume, // "A Float Provider between 0.00001 and 10.0 specifying the volume of the sound" +// Object pitch // "A Float Provider between 0.00001 and 2.0 specifying the pitch of the sound" + ) implements EntityEffect, LocationEffect { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map(PlaySound::new, PlaySound::content); + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + + record ReplaceBlock( + CompoundBinaryTag content +// Object blockState, // "A block state provider giving the block state to set" +// @NotNull Point offset, +// @Nullable Object predicate // "A World-generation style Block Predicate to used to determine if the block should be replaced" + ) implements EntityEffect, LocationEffect { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map(ReplaceBlock::new, ReplaceBlock::content); + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + + record ReplaceDisc( + CompoundBinaryTag content + // todo + ) implements EntityEffect, LocationEffect { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map(ReplaceDisc::new, ReplaceDisc::content); + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + + record RunFunction( + @NotNull String function + ) implements EntityEffect, LocationEffect { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.object( + "function", BinaryTagSerializer.STRING, RunFunction::function, + RunFunction::new + ); + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + + record SetBlockProperties( + CompoundBinaryTag content + //todo + ) implements EntityEffect, LocationEffect { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map(SetBlockProperties::new, SetBlockProperties::content); + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + + record SpawnParticles( + CompoundBinaryTag content + //todo + ) implements EntityEffect, LocationEffect { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map(SpawnParticles::new, SpawnParticles::content); + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + + record SummonEntity( + CompoundBinaryTag content + //todo + ) implements EntityEffect, LocationEffect { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map(SummonEntity::new, SummonEntity::content); + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + +} diff --git a/src/main/java/net/minestom/server/item/enchant/LevelBasedValue.java b/src/main/java/net/minestom/server/item/enchant/LevelBasedValue.java new file mode 100644 index 00000000000..26b86158b41 --- /dev/null +++ b/src/main/java/net/minestom/server/item/enchant/LevelBasedValue.java @@ -0,0 +1,156 @@ +package net.minestom.server.item.enchant; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.NumberBinaryTag; +import net.minestom.server.gamedata.DataPack; +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.registry.Registries; +import net.minestom.server.utils.MathUtils; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +import static net.kyori.adventure.nbt.FloatBinaryTag.floatBinaryTag; + +public interface LevelBasedValue { + + @NotNull BinaryTagSerializer TAGGED_NBT_TYPE = BinaryTagSerializer.registryTaggedUnion( + Registries::enchantmentLevelBasedValues, LevelBasedValue::nbtType, "type"); + @NotNull BinaryTagSerializer NBT_TYPE = new BinaryTagSerializer<>() { + @Override + public @NotNull BinaryTag write(@NotNull Context context, @NotNull LevelBasedValue value) { + if (value instanceof Constant constant) return floatBinaryTag(constant.value); + return TAGGED_NBT_TYPE.write(context, value); + } + + @Override + public @NotNull LevelBasedValue read(@NotNull Context context, @NotNull BinaryTag tag) { + if (tag instanceof NumberBinaryTag number) return new Constant(number.floatValue()); + return TAGGED_NBT_TYPE.read(context, tag); + } + }; + + @ApiStatus.Internal + static @NotNull DynamicRegistry> createDefaultRegistry() { + final DynamicRegistry> registry = DynamicRegistry.create("minestom:enchantment_value_effect"); + // Note that constant is omitted from the registry, it has serialization handled out of band above. + registry.register("linear", Linear.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("clamped", Clamped.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("fraction", Fraction.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("levels_squared", LevelsSquared.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("lookup", Lookup.NBT_TYPE, DataPack.MINECRAFT_CORE); + return registry; + } + + float calc(int level); + + @NotNull BinaryTagSerializer nbtType(); + + record Constant(float value) implements LevelBasedValue { + + @Override + public float calc(int level) { + return value; + } + + @Override + public @NotNull BinaryTagSerializer nbtType() { + throw new UnsupportedOperationException("Constant values are serialized as a special case, see LevelBasedValue.NBT_TYPE"); + } + } + + record Linear(float base, float perLevelAboveFirst) implements LevelBasedValue { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.object( + "base", BinaryTagSerializer.FLOAT, Linear::base, + "per_level_above_first", BinaryTagSerializer.FLOAT, Linear::perLevelAboveFirst, + Linear::new + ); + + @Override + public float calc(int level) { + return base + (perLevelAboveFirst * (level - 1)); + } + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + + record Clamped(@NotNull LevelBasedValue value, float min, float max) implements LevelBasedValue { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.object( + "value", LevelBasedValue.NBT_TYPE, Clamped::value, + "min", BinaryTagSerializer.FLOAT, Clamped::min, + "max", BinaryTagSerializer.FLOAT, Clamped::max, + Clamped::new + ); + + @Override + public float calc(int level) { + return MathUtils.clamp(value.calc(level), min, max); + } + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + + record Fraction(@NotNull LevelBasedValue numerator, @NotNull LevelBasedValue denominator) implements LevelBasedValue { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.object( + "numerator", LevelBasedValue.NBT_TYPE, Fraction::numerator, + "denominator", LevelBasedValue.NBT_TYPE, Fraction::denominator, + Fraction::new + ); + + @Override + public float calc(int level) { + float denominator = this.denominator.calc(level); + return denominator == 0f ? 0f : numerator.calc(level) / denominator; + } + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + + record LevelsSquared(float added) implements LevelBasedValue { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.object( + "added", BinaryTagSerializer.FLOAT, LevelsSquared::added, + LevelsSquared::new + ); + + @Override + public float calc(int level) { + return (level * level) + added; + } + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + + record Lookup(@NotNull List values, @NotNull LevelBasedValue fallback) implements LevelBasedValue { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.object( + "values", BinaryTagSerializer.FLOAT.list(), Lookup::values, + "fallback", LevelBasedValue.NBT_TYPE, Lookup::fallback, + Lookup::new + ); + + @Override + public float calc(int level) { + if (level < 0 || level > values.size()) return fallback.calc(level); + return values.get(level - 1); + } + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + +} diff --git a/src/main/java/net/minestom/server/item/enchant/LocationEffect.java b/src/main/java/net/minestom/server/item/enchant/LocationEffect.java new file mode 100644 index 00000000000..846d9c9f979 --- /dev/null +++ b/src/main/java/net/minestom/server/item/enchant/LocationEffect.java @@ -0,0 +1,55 @@ +package net.minestom.server.item.enchant; + +import net.minestom.server.gamedata.DataPack; +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.registry.Registries; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public non-sealed interface LocationEffect extends Enchantment.Effect { + + @NotNull BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.registryTaggedUnion( + Registries::enchantmentLocationEffects, LocationEffect::nbtType, "type"); + + @ApiStatus.Internal + static @NotNull DynamicRegistry> createDefaultRegistry() { + final DynamicRegistry> registry = DynamicRegistry.create("minestom:enchantment_value_effect"); + registry.register("all_of", AllOf.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("attribute", AttributeEffect.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("apply_mob_effect", EntityEffect.ApplyPotionEffect.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("damage_entity", EntityEffect.DamageEntity.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("damage_item", EntityEffect.DamageItem.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("explode", EntityEffect.Explode.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("ignite", EntityEffect.Ignite.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("play_sound", EntityEffect.PlaySound.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("replace_block", EntityEffect.ReplaceBlock.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("replace_disk", EntityEffect.ReplaceDisc.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("run_function", EntityEffect.RunFunction.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("set_block_properties", EntityEffect.SetBlockProperties.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("spawn_particles", EntityEffect.SpawnParticles.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("summon_entity", EntityEffect.SummonEntity.NBT_TYPE, DataPack.MINECRAFT_CORE); + return registry; + } + + @NotNull BinaryTagSerializer nbtType(); + + record AllOf(@NotNull List effect) implements LocationEffect { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.object( + "effects", LocationEffect.NBT_TYPE.list(), AllOf::effect, + AllOf::new + ); + + public AllOf { + effect = List.copyOf(effect); + } + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + +} diff --git a/src/main/java/net/minestom/server/item/enchant/TargetedConditionalEffect.java b/src/main/java/net/minestom/server/item/enchant/TargetedConditionalEffect.java new file mode 100644 index 00000000000..1c63f91f5e5 --- /dev/null +++ b/src/main/java/net/minestom/server/item/enchant/TargetedConditionalEffect.java @@ -0,0 +1,25 @@ +package net.minestom.server.item.enchant; + +import net.minestom.server.condition.DataPredicate; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public record TargetedConditionalEffect( + @NotNull Enchantment.Target enchanted, + @Nullable Enchantment.Target affected, + @NotNull E effect, + @Nullable DataPredicate requirements +) implements Enchantment.Effect { + + public static @NotNull BinaryTagSerializer> nbtType(@NotNull BinaryTagSerializer effectType) { + return BinaryTagSerializer.object( + "enchanted", Enchantment.Target.NBT_TYPE, TargetedConditionalEffect::enchanted, + "affected", Enchantment.Target.NBT_TYPE.optional(), TargetedConditionalEffect::affected, + "effect", effectType, TargetedConditionalEffect::effect, + "requirements", DataPredicate.NBT_TYPE.optional(), TargetedConditionalEffect::requirements, + TargetedConditionalEffect::new + ); + } + +} diff --git a/src/main/java/net/minestom/server/item/enchant/ValueEffect.java b/src/main/java/net/minestom/server/item/enchant/ValueEffect.java new file mode 100644 index 00000000000..3f55eef1b5f --- /dev/null +++ b/src/main/java/net/minestom/server/item/enchant/ValueEffect.java @@ -0,0 +1,108 @@ +package net.minestom.server.item.enchant; + +import net.minestom.server.gamedata.DataPack; +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.registry.Registries; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public non-sealed interface ValueEffect extends Enchantment.Effect { + + @NotNull BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.registryTaggedUnion( + Registries::enchantmentValueEffects, ValueEffect::nbtType, "type"); + + @ApiStatus.Internal + static @NotNull DynamicRegistry> createDefaultRegistry() { + final DynamicRegistry> registry = DynamicRegistry.create("minestom:enchantment_value_effect"); + registry.register("add", Add.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("all_of", AllOf.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("multiply", Multiply.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("remove_binomial", RemoveBinomial.NBT_TYPE, DataPack.MINECRAFT_CORE); + registry.register("set", Set.NBT_TYPE, DataPack.MINECRAFT_CORE); + return registry; + } + + float apply(float base, int level); + + @NotNull BinaryTagSerializer nbtType(); + + record Add(@NotNull LevelBasedValue value) implements ValueEffect { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.object("value", LevelBasedValue.NBT_TYPE, Add::value, Add::new); + + @Override + public float apply(float base, int level) { + return base + value.calc(level); + } + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + + record AllOf(@NotNull List effects) implements ValueEffect { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.object("effects", ValueEffect.NBT_TYPE.list(), AllOf::effects, AllOf::new); + + public AllOf { + effects = List.copyOf(effects); + } + + @Override + public float apply(float base, int level) { + for (ValueEffect effect : effects) + base = effect.apply(base, level); + return base; + } + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + + record Multiply(@NotNull LevelBasedValue factor) implements ValueEffect { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.object("factor", LevelBasedValue.NBT_TYPE, Multiply::factor, Multiply::new); + + @Override + public float apply(float base, int level) { + return base * factor.calc(level); + } + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + + record RemoveBinomial(@NotNull LevelBasedValue chance) implements ValueEffect { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.object("chance", LevelBasedValue.NBT_TYPE, RemoveBinomial::chance, RemoveBinomial::new); + + @Override + public float apply(float base, int level) { + throw new UnsupportedOperationException("todo"); + } + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + + record Set(@NotNull LevelBasedValue value) implements ValueEffect { + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.object("value", LevelBasedValue.NBT_TYPE, Set::value, Set::new); + + @Override + public float apply(float base, int level) { + return value.calc(level); + } + + @Override + public @NotNull BinaryTagSerializer nbtType() { + return NBT_TYPE; + } + } + +} diff --git a/src/main/java/net/minestom/server/item/firework/FireworkEffect.java b/src/main/java/net/minestom/server/item/firework/FireworkEffect.java deleted file mode 100644 index 4cbe1570f8b..00000000000 --- a/src/main/java/net/minestom/server/item/firework/FireworkEffect.java +++ /dev/null @@ -1,60 +0,0 @@ -package net.minestom.server.item.firework; - -import net.minestom.server.color.Color; -import org.jetbrains.annotations.NotNull; -import org.jglrxavpok.hephaistos.nbt.NBT; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; -import org.jglrxavpok.hephaistos.nbt.NBTIntArray; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public record FireworkEffect(boolean flicker, boolean trail, - @NotNull FireworkEffectType type, - @NotNull List colors, - @NotNull List fadeColors) { - public FireworkEffect { - colors = List.copyOf(colors); - fadeColors = List.copyOf(fadeColors); - } - - /** - * Retrieves a firework effect from the given {@code compound}. - * - * @param compound The NBT connection, which should be a fireworks effect. - * @return A new created firework effect. - */ - public static @NotNull FireworkEffect fromCompound(@NotNull NBTCompound compound) { - List primaryColor = new ArrayList<>(); - List secondaryColor = new ArrayList<>(); - - if (compound.get("Colors") instanceof NBTIntArray colors) { - for (int rgb : colors) primaryColor.add(new Color(rgb)); - } - if (compound.get("FadeColors") instanceof NBTIntArray fadeColors) { - for (int rgb : fadeColors) secondaryColor.add(new Color(rgb)); - } - - boolean flicker = compound.containsKey("Flicker") && compound.getBoolean("Flicker"); - boolean trail = compound.containsKey("Trail") && compound.getBoolean("Trail"); - FireworkEffectType type = compound.containsKey("Type") ? - FireworkEffectType.byId(compound.getAsByte("Type")) : FireworkEffectType.SMALL_BALL; - - return new FireworkEffect(flicker, trail, type, primaryColor, secondaryColor); - } - - /** - * Retrieves the {@link FireworkEffect} as a {@link NBTCompound}. - * - * @return The firework effect as a nbt compound. - */ - public @NotNull NBTCompound asCompound() { - return NBT.Compound(Map.of( - "Flicker", NBT.Boolean(flicker), - "Trail", NBT.Boolean(trail), - "Type", NBT.Byte(type.getType()), - "Colors", NBT.IntArray(colors.stream().mapToInt(Color::asRGB).toArray()), - "FadeColors", NBT.IntArray(fadeColors.stream().mapToInt(Color::asRGB).toArray()))); - } -} diff --git a/src/main/java/net/minestom/server/item/firework/FireworkEffectType.java b/src/main/java/net/minestom/server/item/firework/FireworkEffectType.java deleted file mode 100644 index 67efbb32c29..00000000000 --- a/src/main/java/net/minestom/server/item/firework/FireworkEffectType.java +++ /dev/null @@ -1,49 +0,0 @@ -package net.minestom.server.item.firework; - -import it.unimi.dsi.fastutil.bytes.Byte2ObjectMap; -import it.unimi.dsi.fastutil.bytes.Byte2ObjectOpenHashMap; - -/** - * An enumeration that representing all available firework types. - */ -public enum FireworkEffectType { - SMALL_BALL((byte) 0), - LARGE_BALL((byte) 1), - STAR_SHAPED((byte) 2), - CREEPER_SHAPED((byte) 3), - BURST((byte) 4); - - private static final Byte2ObjectMap BY_ID = new Byte2ObjectOpenHashMap<>(); - - static { - for (FireworkEffectType value : values()) { - BY_ID.put(value.type, value); - } - } - - private final byte type; - - FireworkEffectType(byte type) { - this.type = type; - } - - /** - * Retrieves a {@link FireworkEffectType} by the given {@code id}. - * - * @param id The identifier of the firework effect type. - * @return A firework effect type or {@code null}. - */ - public static FireworkEffectType byId(byte id) { - return BY_ID.get(id); - } - - /** - * Retrieves the type of the firework effect. - * - * @return The type of the firework effect as a byte. - */ - public byte getType() { - return type; - } -} - diff --git a/src/main/java/net/minestom/server/item/metadata/BannerMeta.java b/src/main/java/net/minestom/server/item/metadata/BannerMeta.java deleted file mode 100644 index 9d0a70d06e1..00000000000 --- a/src/main/java/net/minestom/server/item/metadata/BannerMeta.java +++ /dev/null @@ -1,165 +0,0 @@ -package net.minestom.server.item.metadata; - -/** - * @author theEvilReaper - * @version 1.0.0 - * @since - **/ - -import net.kyori.adventure.text.Component; -import net.minestom.server.color.DyeColor; -import net.minestom.server.item.ItemMetaView; -import net.minestom.server.item.banner.BannerPattern; -import net.minestom.server.tag.Tag; -import net.minestom.server.tag.TagHandler; -import net.minestom.server.tag.TagReadable; -import net.minestom.server.tag.TagSerializer; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.UnknownNullability; -import org.jglrxavpok.hephaistos.nbt.NBT; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** - * Meta for all {@link net.minestom.server.item.Material#WHITE_BANNER} and {@link net.minestom.server.item.Material#SHIELD} items - * - * @param readable data - */ -// Microtus - Banner and Shield Meta -@ApiStatus.Experimental -public record BannerMeta(TagReadable readable) implements ItemMetaView { - - private static final String PATTERN_KEY = "Pattern"; - private static final String COLOR_KEY = "Color"; - - private static final Tag CUSTOM_NAME = Tag.Component("CustomName").path("BlockEntityTag"); - private static final Tag> PATTERNS = Tag.Structure("Patterns", - TagSerializer.fromCompound(Pattern::fromCompound, Pattern::asCompound)) - .path("BlockEntityTag").list().defaultValue(List.of()); - - - @Contract(value = "_, _ -> new", pure = true) - public static @NotNull Pattern from(@NotNull DyeColor color, @NotNull BannerPattern pattern) { - return new Pattern(color, pattern); - } - - /** - * Get name of the marker, unused by Minestom - * - * @return name of the marker - */ - public @Nullable Component getCustomName() { - return getTag(CUSTOM_NAME); - } - - /** - * Get patterns of the banner - * - * @return patterns of the banner - */ - public @NotNull List getPatterns() { - return getTag(PATTERNS); - } - - @Override - public @UnknownNullability T getTag(@NotNull Tag tag) { - return readable.getTag(tag); - } - - public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder { - public Builder() { - this(TagHandler.newHandler()); - } - - /** - * Set name of the marker, unused by Minestom - * - * @param customName name of the marker - * @return this - */ - public @NotNull Builder customName(Component customName) { - setTag(CUSTOM_NAME, customName); - return this; - } - - /** - * Set the patterns of the banner - * - * @param patterns patterns of the banner - * @return this - */ - public @NotNull Builder patterns(@NotNull List patterns) { - setTag(PATTERNS, patterns); - return this; - } - - /** - * Add a pattern to the banner - * - * @param pattern pattern to add - * @return this - */ - public @NotNull Builder addPattern(Pattern pattern) { - var newList = new ArrayList<>(getTag(PATTERNS)); - newList.add(pattern); - return patterns(newList); - } - - /** - * Remove a pattern from the banner - * - * @param pattern pattern to remove - * @return this - */ - public @NotNull Builder removePattern(Pattern pattern) { - var newList = new ArrayList<>(getTag(PATTERNS)); - newList.remove(pattern); - return patterns(newList); - } - - /** - * Clears the underlying list which contains the pattern values. - * @return this - */ - public @NotNull Builder clearPatterns() { - return patterns(List.of()); - } - } - - public record Pattern(@NotNull DyeColor color, @NotNull BannerPattern pattern) { - - /** - * Retrieves a banner pattern from the given {@code compound}. - * - * @param compound The NBT connection, which should be a banner pattern. - * @return A new created banner pattern. - */ - public static @NotNull Pattern fromCompound(@NotNull NBTCompound compound) { - DyeColor color = compound.containsKey(COLOR_KEY) ? DyeColor.getValue(compound.getByte(COLOR_KEY)) : DyeColor.WHITE; - BannerPattern type; - if (compound.containsKey(PATTERN_KEY)) { - BannerPattern pattern = BannerPattern.fromIdentifier(compound.getString(PATTERN_KEY)); - type = pattern != null ? pattern : BannerPattern.BASE; - } else type = BannerPattern.BASE; - return new Pattern(color, type); - } - - /** - * Retrieves the {@link Pattern} as an {@link NBTCompound}. - * - * @return The banner pattern as a nbt compound. - */ - public @NotNull NBTCompound asCompound() { - return NBT.Compound(Map.of( - COLOR_KEY, NBT.Byte(color.ordinal()), - PATTERN_KEY, NBT.String(pattern.identifier()) - )); - } - } -} diff --git a/src/main/java/net/minestom/server/item/metadata/BundleMeta.java b/src/main/java/net/minestom/server/item/metadata/BundleMeta.java deleted file mode 100644 index 9392d0d474f..00000000000 --- a/src/main/java/net/minestom/server/item/metadata/BundleMeta.java +++ /dev/null @@ -1,52 +0,0 @@ -package net.minestom.server.item.metadata; - -import net.minestom.server.item.ItemMetaView; -import net.minestom.server.item.ItemStack; -import net.minestom.server.tag.Tag; -import net.minestom.server.tag.TagHandler; -import net.minestom.server.tag.TagReadable; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.UnknownNullability; - -import java.util.ArrayList; -import java.util.List; - -@ApiStatus.Experimental -public record BundleMeta(TagReadable readable) implements ItemMetaView { - private static final Tag> ITEMS = Tag.ItemStack("Items").list().defaultValue(List.of()); - - public @NotNull List getItems() { - return getTag(ITEMS); - } - - @Override - public @UnknownNullability T getTag(@NotNull Tag tag) { - return readable.getTag(tag); - } - - public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder { - public Builder() { - this(TagHandler.newHandler()); - } - - public Builder items(@NotNull List items) { - setTag(ITEMS, items); - return this; - } - - @ApiStatus.Experimental - public Builder addItem(@NotNull ItemStack item) { - var newList = new ArrayList<>(getTag(ITEMS)); - newList.add(item); - return items(newList); - } - - @ApiStatus.Experimental - public Builder removeItem(@NotNull ItemStack item) { - var newList = new ArrayList<>(getTag(ITEMS)); - newList.remove(item); - return items(newList); - } - } -} diff --git a/src/main/java/net/minestom/server/item/metadata/CompassMeta.java b/src/main/java/net/minestom/server/item/metadata/CompassMeta.java deleted file mode 100644 index 24040a879ec..00000000000 --- a/src/main/java/net/minestom/server/item/metadata/CompassMeta.java +++ /dev/null @@ -1,69 +0,0 @@ -package net.minestom.server.item.metadata; - -import net.minestom.server.coordinate.Point; -import net.minestom.server.coordinate.Vec; -import net.minestom.server.item.ItemMetaView; -import net.minestom.server.tag.*; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.UnknownNullability; - -public record CompassMeta(TagReadable readable) implements ItemMetaView { - private static final Tag LODESTONE_TRACKED = Tag.Boolean("LodestoneTracked").defaultValue(false); - private static final Tag LODESTONE_DIMENSION = Tag.String("LodestoneDimension"); - private static final Tag LODESTONE_POSITION = Tag.Structure("LodestonePos", new TagSerializer<>() { - @Override - public @Nullable Point read(@NotNull TagReadable reader) { - final Integer x = reader.getTag(Tag.Integer("X")); - final Integer y = reader.getTag(Tag.Integer("Y")); - final Integer z = reader.getTag(Tag.Integer("Z")); - if (x == null || y == null || z == null) return null; - return new Vec(x, y, z); - } - - @Override - public void write(@NotNull TagWritable writer, @NotNull Point value) { - writer.setTag(Tag.Integer("X"), value.blockX()); - writer.setTag(Tag.Integer("Y"), value.blockY()); - writer.setTag(Tag.Integer("Z"), value.blockZ()); - } - }); - - public boolean isLodestoneTracked() { - return getTag(LODESTONE_TRACKED); - } - - public @Nullable String getLodestoneDimension() { - return getTag(LODESTONE_DIMENSION); - } - - public @Nullable Point getLodestonePosition() { - return getTag(LODESTONE_POSITION); - } - - @Override - public @UnknownNullability T getTag(@NotNull Tag tag) { - return readable.getTag(tag); - } - - public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder { - public Builder() { - this(TagHandler.newHandler()); - } - - public Builder lodestoneTracked(boolean lodestoneTracked) { - setTag(LODESTONE_TRACKED, lodestoneTracked); - return this; - } - - public Builder lodestoneDimension(@Nullable String lodestoneDimension) { - setTag(LODESTONE_DIMENSION, lodestoneDimension); - return this; - } - - public Builder lodestonePosition(@Nullable Point lodestonePosition) { - setTag(LODESTONE_POSITION, lodestonePosition); - return this; - } - } -} diff --git a/src/main/java/net/minestom/server/item/metadata/CrossbowMeta.java b/src/main/java/net/minestom/server/item/metadata/CrossbowMeta.java deleted file mode 100644 index cfc525f2f9c..00000000000 --- a/src/main/java/net/minestom/server/item/metadata/CrossbowMeta.java +++ /dev/null @@ -1,50 +0,0 @@ -package net.minestom.server.item.metadata; - -import net.minestom.server.item.ItemMetaView; -import net.minestom.server.item.ItemStack; -import net.minestom.server.tag.Tag; -import net.minestom.server.tag.TagHandler; -import net.minestom.server.tag.TagReadable; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.UnknownNullability; - -import java.util.List; - -public record CrossbowMeta(TagReadable readable) implements ItemMetaView { - private static final Tag> PROJECTILES = Tag.ItemStack("ChargedProjectiles").list().defaultValue(List.of()); - private static final Tag CHARGED = Tag.Boolean("Charged").defaultValue(false); - - public @NotNull List getProjectiles() { - return getTag(PROJECTILES); - } - - public boolean isCharged() { - return getTag(CHARGED); - } - - @Override - public @UnknownNullability T getTag(@NotNull Tag tag) { - return readable.getTag(tag); - } - - public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder { - public Builder() { - this(TagHandler.newHandler()); - } - - public Builder projectile(@NotNull ItemStack projectile) { - setTag(PROJECTILES, List.of(projectile)); - return this; - } - - public Builder projectiles(@NotNull ItemStack projectile1, @NotNull ItemStack projectile2, @NotNull ItemStack projectile3) { - setTag(PROJECTILES, List.of(projectile1, projectile2, projectile3)); - return this; - } - - public Builder charged(boolean charged) { - setTag(CHARGED, charged); - return this; - } - } -} diff --git a/src/main/java/net/minestom/server/item/metadata/EnchantedBookMeta.java b/src/main/java/net/minestom/server/item/metadata/EnchantedBookMeta.java deleted file mode 100644 index 5bb73352306..00000000000 --- a/src/main/java/net/minestom/server/item/metadata/EnchantedBookMeta.java +++ /dev/null @@ -1,56 +0,0 @@ -package net.minestom.server.item.metadata; - -import net.minestom.server.item.Enchantment; -import net.minestom.server.item.ItemMetaView; -import net.minestom.server.item.ItemSerializers; -import net.minestom.server.tag.Tag; -import net.minestom.server.tag.TagHandler; -import net.minestom.server.tag.TagReadable; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.UnknownNullability; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static net.minestom.server.item.ItemSerializers.ENCHANTMENT_SERIALIZER; - -public record EnchantedBookMeta(TagReadable readable) implements ItemMetaView { - static final Tag> ENCHANTMENTS = Tag.Structure("StoredEnchantments", ENCHANTMENT_SERIALIZER).list().map(enchantmentEntry -> { - Map map = new HashMap<>(); - for (var entry : enchantmentEntry) map.put(entry.enchantment(), entry.level()); - return Map.copyOf(map); - }, o -> { - List entries = new ArrayList<>(); - for (var entry : o.entrySet()) - entries.add(new ItemSerializers.EnchantmentEntry(entry.getKey(), entry.getValue())); - return List.copyOf(entries); - }).defaultValue(Map.of()); - - public @NotNull Map getStoredEnchantmentMap() { - return getTag(ENCHANTMENTS); - } - - @Override - public @UnknownNullability T getTag(@NotNull Tag tag) { - return readable.getTag(tag); - } - - public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder { - public Builder() { - this(TagHandler.newHandler()); - } - - public @NotNull Builder enchantments(@NotNull Map enchantments) { - setTag(ENCHANTMENTS, Map.copyOf(enchantments)); - return this; - } - - public @NotNull Builder enchantment(@NotNull Enchantment enchantment, short level) { - var enchantments = new HashMap<>(getTag(ENCHANTMENTS)); - enchantments.put(enchantment, level); - return enchantments(enchantments); - } - } -} diff --git a/src/main/java/net/minestom/server/item/metadata/FireworkEffectMeta.java b/src/main/java/net/minestom/server/item/metadata/FireworkEffectMeta.java deleted file mode 100644 index abdd59781fd..00000000000 --- a/src/main/java/net/minestom/server/item/metadata/FireworkEffectMeta.java +++ /dev/null @@ -1,36 +0,0 @@ -package net.minestom.server.item.metadata; - -import net.minestom.server.item.ItemMetaView; -import net.minestom.server.item.firework.FireworkEffect; -import net.minestom.server.tag.Tag; -import net.minestom.server.tag.TagHandler; -import net.minestom.server.tag.TagReadable; -import net.minestom.server.tag.TagSerializer; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.UnknownNullability; - -public record FireworkEffectMeta(TagReadable readable) implements ItemMetaView { - private static final Tag FIREWORK_EFFECT = Tag.Structure("Explosion", - TagSerializer.fromCompound(FireworkEffect::fromCompound, FireworkEffect::asCompound)); - - public @Nullable FireworkEffect getFireworkEffect() { - return getTag(FIREWORK_EFFECT); - } - - @Override - public @UnknownNullability T getTag(@NotNull Tag tag) { - return readable.getTag(tag); - } - - public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder { - public Builder() { - this(TagHandler.newHandler()); - } - - public Builder effect(@Nullable FireworkEffect fireworkEffect) { - setTag(FIREWORK_EFFECT, fireworkEffect); - return this; - } - } -} diff --git a/src/main/java/net/minestom/server/item/metadata/FireworkMeta.java b/src/main/java/net/minestom/server/item/metadata/FireworkMeta.java deleted file mode 100644 index 722057271c4..00000000000 --- a/src/main/java/net/minestom/server/item/metadata/FireworkMeta.java +++ /dev/null @@ -1,49 +0,0 @@ -package net.minestom.server.item.metadata; - -import net.minestom.server.item.ItemMetaView; -import net.minestom.server.item.firework.FireworkEffect; -import net.minestom.server.tag.Tag; -import net.minestom.server.tag.TagHandler; -import net.minestom.server.tag.TagReadable; -import net.minestom.server.tag.TagSerializer; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.UnknownNullability; - -import java.util.List; - -public record FireworkMeta(TagReadable readable) implements ItemMetaView { - private static final Tag> EFFECTS = Tag.Structure("Explosions", - TagSerializer.fromCompound(FireworkEffect::fromCompound, FireworkEffect::asCompound)) - .path("Fireworks").list().defaultValue(List.of()); - private static final Tag FLIGHT_DURATION = Tag.Byte("Flight").path("Fireworks"); - - public @NotNull List getEffects() { - return getTag(EFFECTS); - } - - public @Nullable Byte getFlightDuration() { - return getTag(FLIGHT_DURATION); - } - - @Override - public @UnknownNullability T getTag(@NotNull Tag tag) { - return readable.getTag(tag); - } - - public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder { - public Builder() { - this(TagHandler.newHandler()); - } - - public Builder effects(List effects) { - setTag(EFFECTS, effects); - return this; - } - - public Builder flightDuration(byte flightDuration) { - setTag(FLIGHT_DURATION, flightDuration); - return this; - } - } -} diff --git a/src/main/java/net/minestom/server/item/metadata/LeatherArmorMeta.java b/src/main/java/net/minestom/server/item/metadata/LeatherArmorMeta.java deleted file mode 100644 index 427a023db8e..00000000000 --- a/src/main/java/net/minestom/server/item/metadata/LeatherArmorMeta.java +++ /dev/null @@ -1,34 +0,0 @@ -package net.minestom.server.item.metadata; - -import net.minestom.server.color.Color; -import net.minestom.server.item.ItemMetaView; -import net.minestom.server.tag.Tag; -import net.minestom.server.tag.TagHandler; -import net.minestom.server.tag.TagReadable; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.UnknownNullability; - -public record LeatherArmorMeta(TagReadable readable) implements ItemMetaView { - private static final Tag COLOR = Tag.Integer("color").path("display").map(Color::new, Color::asRGB); - - public @Nullable Color getColor() { - return getTag(COLOR); - } - - @Override - public @UnknownNullability T getTag(@NotNull Tag tag) { - return readable.getTag(tag); - } - - public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder { - public Builder() { - this(TagHandler.newHandler()); - } - - public Builder color(@Nullable Color color) { - setTag(COLOR, color); - return this; - } - } -} diff --git a/src/main/java/net/minestom/server/item/metadata/MapMeta.java b/src/main/java/net/minestom/server/item/metadata/MapMeta.java deleted file mode 100644 index 9a21425b41f..00000000000 --- a/src/main/java/net/minestom/server/item/metadata/MapMeta.java +++ /dev/null @@ -1,87 +0,0 @@ -package net.minestom.server.item.metadata; - -import net.minestom.server.color.Color; -import net.minestom.server.item.ItemMetaView; -import net.minestom.server.tag.*; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.UnknownNullability; - -import java.util.List; - -public record MapMeta(TagReadable readable) implements ItemMetaView { - private static final Tag MAP_ID = Tag.Integer("map").defaultValue(0); - private static final Tag MAP_SCALE_DIRECTION = Tag.Integer("map_scale_direction").defaultValue(0); - private static final Tag> DECORATIONS = Tag.Structure("Decorations", new TagSerializer() { - @Override - public @Nullable Decoration read(@NotNull TagReadable reader) { - final String id = reader.getTag(Tag.String("id")); - final Byte type = reader.getTag(Tag.Byte("type")); - final Byte x = reader.getTag(Tag.Byte("x")); - final Byte z = reader.getTag(Tag.Byte("z")); - final Double rot = reader.getTag(Tag.Double("rot")); - if (id == null || type == null || x == null || z == null || rot == null) return null; - return new Decoration(id, type, x, z, rot); - } - - @Override - public void write(@NotNull TagWritable writer, @NotNull Decoration value) { - writer.setTag(Tag.String("id"), value.id); - writer.setTag(Tag.Byte("type"), value.type); - writer.setTag(Tag.Byte("x"), value.x); - writer.setTag(Tag.Byte("z"), value.z); - writer.setTag(Tag.Double("rot"), value.rotation); - } - }).list().defaultValue(List.of()); - private static final Tag MAP_COLOR = Tag.Integer("MapColor").path("display").map(Color::new, Color::asRGB); - - public int getMapId() { - return getTag(MAP_ID); - } - - public int getMapScaleDirection() { - return getTag(MAP_SCALE_DIRECTION); - } - - public List getDecorations() { - return getTag(DECORATIONS); - } - - public @NotNull Color getMapColor() { - return getTag(MAP_COLOR); - } - - @Override - public @UnknownNullability T getTag(@NotNull Tag tag) { - return readable.getTag(tag); - } - - public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder { - public Builder() { - this(TagHandler.newHandler()); - } - - public Builder mapId(int value) { - setTag(MAP_ID, value); - return this; - } - - public Builder mapScaleDirection(int value) { - setTag(MAP_SCALE_DIRECTION, value); - return this; - } - - public Builder decorations(List value) { - setTag(DECORATIONS, value); - return this; - } - - public Builder mapColor(Color value) { - setTag(MAP_COLOR, value); - return this; - } - } - - public record Decoration(String id, byte type, byte x, byte z, double rotation) { - } -} diff --git a/src/main/java/net/minestom/server/item/metadata/PlayerHeadMeta.java b/src/main/java/net/minestom/server/item/metadata/PlayerHeadMeta.java deleted file mode 100644 index b22c195773c..00000000000 --- a/src/main/java/net/minestom/server/item/metadata/PlayerHeadMeta.java +++ /dev/null @@ -1,73 +0,0 @@ -package net.minestom.server.item.metadata; - -import net.minestom.server.entity.PlayerSkin; -import net.minestom.server.item.ItemMetaView; -import net.minestom.server.tag.*; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.UnknownNullability; -import org.jglrxavpok.hephaistos.nbt.NBT; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; -import org.jglrxavpok.hephaistos.nbt.NBTList; -import org.jglrxavpok.hephaistos.nbt.NBTType; - -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.UUID; - -public record PlayerHeadMeta(TagReadable readable) implements ItemMetaView { - public static final Tag SKULL_OWNER = Tag.UUID("Id").path("SkullOwner"); - public static final Tag SKIN = Tag.Structure("Properties", new TagSerializer() { - private static final Tag TEXTURES = Tag.NBT("textures"); - - @Override - public @Nullable PlayerSkin read(@NotNull TagReadable reader) { - final NBT result = reader.getTag(TEXTURES); - if (!(result instanceof NBTList)) return null; - final NBTList textures = (NBTList) result; - final NBTCompound texture = textures.get(0); - final String value = texture.getString("Value"); - final String signature = texture.getString("Signature"); - return new PlayerSkin(value, signature); - } - - @Override - public void write(@NotNull TagWritable writer, @NotNull PlayerSkin playerSkin) { - final String value = Objects.requireNonNullElse(playerSkin.textures(), ""); - final String signature = Objects.requireNonNullElse(playerSkin.signature(), ""); - NBTList textures = new NBTList<>(NBTType.TAG_Compound, - List.of(NBT.Compound(Map.of("Value", NBT.String(value), "Signature", NBT.String(signature))))); - writer.setTag(TEXTURES, textures); - } - }).path("SkullOwner"); - - public @Nullable UUID getSkullOwner() { - return getTag(SKULL_OWNER); - } - - public @Nullable PlayerSkin getPlayerSkin() { - return getTag(SKIN); - } - - @Override - public @UnknownNullability T getTag(@NotNull Tag tag) { - return readable.getTag(tag); - } - - public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder { - public Builder() { - this(TagHandler.newHandler()); - } - - public Builder skullOwner(@Nullable UUID skullOwner) { - setTag(SKULL_OWNER, skullOwner); - return this; - } - - public Builder playerSkin(@Nullable PlayerSkin playerSkin) { - setTag(SKIN, playerSkin); - return this; - } - } -} diff --git a/src/main/java/net/minestom/server/item/metadata/PotionMeta.java b/src/main/java/net/minestom/server/item/metadata/PotionMeta.java deleted file mode 100644 index a4ecfcc2ef2..00000000000 --- a/src/main/java/net/minestom/server/item/metadata/PotionMeta.java +++ /dev/null @@ -1,81 +0,0 @@ -package net.minestom.server.item.metadata; - -import net.minestom.server.color.Color; -import net.minestom.server.item.ItemMetaView; -import net.minestom.server.potion.CustomPotionEffect; -import net.minestom.server.potion.PotionType; -import net.minestom.server.registry.StaticProtocolObject; -import net.minestom.server.tag.*; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.UnknownNullability; - -import java.util.List; - -public record PotionMeta(TagReadable readable) implements ItemMetaView { - private static final Tag POTION_TYPE = Tag.String("Potion").map(PotionType::fromNamespaceId, StaticProtocolObject::name).defaultValue(PotionType.EMPTY); - private static final Tag> CUSTOM_POTION_EFFECTS = Tag.Structure("CustomPotionEffects", new TagSerializer() { - @Override - public @Nullable CustomPotionEffect read(@NotNull TagReadable reader) { - final Byte id = reader.getTag(Tag.Byte("Id")); - final Byte amplifier = reader.getTag(Tag.Byte("Amplifier")); - final Integer duration = reader.getTag(Tag.Integer("Duration")); - final Boolean ambient = reader.getTag(Tag.Boolean("Ambient")); - final Boolean showParticles = reader.getTag(Tag.Boolean("ShowParticles")); - final Boolean showIcon = reader.getTag(Tag.Boolean("ShowIcon")); - if (id == null || amplifier == null || duration == null || ambient == null || showParticles == null || showIcon == null) { - return null; - } - return new CustomPotionEffect(id, amplifier, duration, ambient, showParticles, showIcon); - } - - @Override - public void write(@NotNull TagWritable writer, @NotNull CustomPotionEffect value) { - writer.setTag(Tag.Byte("Id"), value.id()); - writer.setTag(Tag.Byte("Amplifier"), value.amplifier()); - writer.setTag(Tag.Integer("Duration"), value.duration()); - writer.setTag(Tag.Boolean("Ambient"), value.isAmbient()); - writer.setTag(Tag.Boolean("ShowParticles"), value.showParticles()); - writer.setTag(Tag.Boolean("ShowIcon"), value.showIcon()); - } - }).list().defaultValue(List.of()); - private static final Tag CUSTOM_POTION_COLOR = Tag.Integer("CustomPotionColor").path("display").map(Color::new, Color::asRGB); - - public @NotNull PotionType getPotionType() { - return getTag(POTION_TYPE); - } - - public @NotNull List getCustomPotionEffects() { - return getTag(CUSTOM_POTION_EFFECTS); - } - - public @Nullable Color getColor() { - return getTag(CUSTOM_POTION_COLOR); - } - - @Override - public @UnknownNullability T getTag(@NotNull Tag tag) { - return readable.getTag(tag); - } - - public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder { - public Builder() { - this(TagHandler.newHandler()); - } - - public Builder potionType(@NotNull PotionType potionType) { - setTag(POTION_TYPE, potionType); - return this; - } - - public Builder effects(@NotNull List customPotionEffects) { - setTag(CUSTOM_POTION_EFFECTS, customPotionEffects); - return this; - } - - public Builder color(@NotNull Color color) { - setTag(CUSTOM_POTION_COLOR, color); - return this; - } - } -} diff --git a/src/main/java/net/minestom/server/item/metadata/ShieldMeta.java b/src/main/java/net/minestom/server/item/metadata/ShieldMeta.java deleted file mode 100644 index 56a864253f2..00000000000 --- a/src/main/java/net/minestom/server/item/metadata/ShieldMeta.java +++ /dev/null @@ -1,131 +0,0 @@ -package net.minestom.server.item.metadata; - -import net.kyori.adventure.text.Component; -import net.minestom.server.color.DyeColor; -import net.minestom.server.item.ItemMetaView; -import net.minestom.server.tag.Tag; -import net.minestom.server.tag.TagHandler; -import net.minestom.server.tag.TagReadable; -import net.minestom.server.tag.TagSerializer; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.UnknownNullability; - -import java.util.ArrayList; -import java.util.List; - -// Microtus - Banner and Shield Meta -@ApiStatus.Experimental -public record ShieldMeta(@NotNull TagReadable readable) implements ItemMetaView { - - private static final String BASE_KEY = "Base"; - private static final String ENTITY_TAG = "BlockEntityTag"; - private static final Tag CUSTOM_NAME = Tag.Component("CustomName").path(ENTITY_TAG); - private static final Tag BASE_TAG = Tag.Integer(BASE_KEY).path(ENTITY_TAG); - private static final Tag> PATTERNS = Tag.Structure("Patterns", - TagSerializer.fromCompound(BannerMeta.Pattern::fromCompound, BannerMeta.Pattern::asCompound)) - .path(ENTITY_TAG).list().defaultValue(List.of()); - - /** - * Get base color of the shield - * - * @return the base color - */ - public @NotNull DyeColor getBaseColor() { - return DyeColor.getValue(getTag(BASE_TAG)); - } - - /** - * Get name of the marker, unused by Minestom - * - * @return name of the marker - */ - public @Nullable Component getCustomName() { - return getTag(CUSTOM_NAME); - } - - /** - * Get patterns of the shield - * - * @return patterns of the shield - */ - public @NotNull List getPatterns() { - return getTag(PATTERNS); - } - - @Override - public @UnknownNullability T getTag(@NotNull Tag tag) { - return readable.getTag(tag); - } - - public record Builder(@NotNull TagHandler tagHandler) implements ItemMetaView.Builder { - public Builder() { - this(TagHandler.newHandler()); - } - - /** - * Set the color which is used as base color for a shield. - * @param dyeColor the color to set - * @return the builder instance - */ - public @NotNull ShieldMeta.Builder baseColor(@NotNull DyeColor dyeColor) { - setTag(BASE_TAG, dyeColor.ordinal()); - return this; - } - - /** - * Set name of the marker, unused by Minestom - * - * @param customName name of the marker - * @return this - */ - public @NotNull ShieldMeta.Builder customName(@NotNull Component customName) { - setTag(CUSTOM_NAME, customName); - return this; - } - - /** - * Set the patterns of the shield - * - * @param patterns patterns of the shield - * @return this - */ - public @NotNull ShieldMeta.Builder patterns(@NotNull List patterns) { - setTag(PATTERNS, patterns); - return this; - } - - /** - * Add a pattern to the shield - * - * @param pattern pattern to add - * @return this - */ - public @NotNull ShieldMeta.Builder addPattern(BannerMeta.Pattern pattern) { - var newList = new ArrayList<>(getTag(PATTERNS)); - newList.add(pattern); - return patterns(newList); - } - - /** - * Remove a pattern from the shield - * - * @param pattern pattern to remove - * @return this - */ - public @NotNull ShieldMeta.Builder removePattern(BannerMeta.Pattern pattern) { - var newList = new ArrayList<>(getTag(PATTERNS)); - newList.remove(pattern); - return patterns(newList); - } - - /** - * Clears the underlying list which contains the pattern values. - * @return this - */ - public @NotNull ShieldMeta.Builder clearPatterns() { - return patterns(List.of()); - } - } -} diff --git a/src/main/java/net/minestom/server/item/metadata/WritableBookMeta.java b/src/main/java/net/minestom/server/item/metadata/WritableBookMeta.java deleted file mode 100644 index e90c6de5c2d..00000000000 --- a/src/main/java/net/minestom/server/item/metadata/WritableBookMeta.java +++ /dev/null @@ -1,39 +0,0 @@ -package net.minestom.server.item.metadata; - -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import net.minestom.server.item.ItemMetaView; -import net.minestom.server.tag.Tag; -import net.minestom.server.tag.TagHandler; -import net.minestom.server.tag.TagReadable; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.UnknownNullability; - -import java.util.List; - -public record WritableBookMeta(TagReadable readable) implements ItemMetaView { - private static final Tag> PAGES = Tag.String("pages") - .map(s -> LegacyComponentSerializer.legacySection().deserialize(s), - textComponent -> LegacyComponentSerializer.legacySection().serialize(textComponent)) - .list().defaultValue(List.of()); - - public @NotNull List<@NotNull Component> getPages() { - return getTag(PAGES); - } - - @Override - public @UnknownNullability T getTag(@NotNull Tag tag) { - return readable.getTag(tag); - } - - public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder { - public Builder() { - this(TagHandler.newHandler()); - } - - public Builder pages(@NotNull List<@NotNull Component> pages) { - setTag(PAGES, pages); - return this; - } - } -} diff --git a/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java b/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java deleted file mode 100644 index 20be04fc336..00000000000 --- a/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java +++ /dev/null @@ -1,97 +0,0 @@ -package net.minestom.server.item.metadata; - -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import net.minestom.server.item.ItemMetaView; -import net.minestom.server.tag.Tag; -import net.minestom.server.tag.TagHandler; -import net.minestom.server.tag.TagReadable; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.UnknownNullability; - -import java.util.Arrays; -import java.util.List; - -public record WrittenBookMeta(TagReadable readable) implements ItemMetaView { - private static final Tag RESOLVED = Tag.Boolean("resolved").defaultValue(false); - private static final Tag GENERATION = Tag.Integer("resolved").map(integer -> WrittenBookGeneration.values()[integer], Enum::ordinal); - private static final Tag AUTHOR = Tag.String("author"); - private static final Tag TITLE = Tag.String("title"); - private static final Tag> PAGES = Tag.String("pages") - .map(GsonComponentSerializer.gson()::deserialize, GsonComponentSerializer.gson()::serialize) - .list().defaultValue(List.of()); - - public boolean isResolved() { - return getTag(RESOLVED); - } - - public @Nullable WrittenBookGeneration getGeneration() { - return getTag(GENERATION); - } - - public @Nullable String getAuthor() { - return getTag(AUTHOR); - } - - public @Nullable String getTitle() { - return getTag(TITLE); - } - - public @NotNull List<@NotNull Component> getPages() { - return getTag(PAGES); - } - - @Override - public @UnknownNullability T getTag(@NotNull Tag tag) { - return readable.getTag(tag); - } - - public enum WrittenBookGeneration { - ORIGINAL, COPY_OF_ORIGINAL, COPY_OF_COPY, TATTERED - } - - public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder { - public Builder() { - this(TagHandler.newHandler()); - } - - public Builder resolved(boolean resolved) { - setTag(RESOLVED, resolved); - return this; - } - - public Builder generation(@Nullable WrittenBookGeneration generation) { - setTag(GENERATION, generation); - return this; - } - - public Builder author(@Nullable String author) { - setTag(AUTHOR, author); - return this; - } - - public Builder author(@Nullable Component author) { - return author(author != null ? LegacyComponentSerializer.legacySection().serialize(author) : null); - } - - public Builder title(@Nullable String title) { - setTag(TITLE, title); - return this; - } - - public Builder title(@Nullable Component title) { - return title(title != null ? LegacyComponentSerializer.legacySection().serialize(title) : null); - } - - public Builder pages(@NotNull List<@NotNull Component> pages) { - setTag(PAGES, pages); - return this; - } - - public Builder pages(Component... pages) { - return pages(Arrays.asList(pages)); - } - } -} diff --git a/src/main/java/net/minestom/server/item/rule/VanillaStackingRule.java b/src/main/java/net/minestom/server/item/rule/VanillaStackingRule.java deleted file mode 100644 index 496d23bb4a9..00000000000 --- a/src/main/java/net/minestom/server/item/rule/VanillaStackingRule.java +++ /dev/null @@ -1,40 +0,0 @@ -package net.minestom.server.item.rule; - -import net.minestom.server.item.ItemStack; -import net.minestom.server.item.StackingRule; -import net.minestom.server.utils.MathUtils; -import org.jetbrains.annotations.NotNull; - -public final class VanillaStackingRule implements StackingRule { - - @Override - public boolean canBeStacked(@NotNull ItemStack item1, @NotNull ItemStack item2) { - return item1.isSimilar(item2); - } - - @Override - public boolean canApply(@NotNull ItemStack item, int newAmount) { - return MathUtils.isBetween(newAmount, 0, getMaxSize(item)); - } - - @Override - public @NotNull ItemStack apply(@NotNull ItemStack item, int amount) { - return amount > 0 ? item.withAmount(amount) : ItemStack.AIR; - } - - @Override - public int getAmount(@NotNull ItemStack itemStack) { - return itemStack.amount(); - } - - @Override - public int getMaxSize(@NotNull ItemStack itemStack) { - return itemStack.material().maxStackSize(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - return obj != null && getClass() == obj.getClass(); - } -} diff --git a/src/main/java/net/minestom/server/listener/AbilitiesListener.java b/src/main/java/net/minestom/server/listener/AbilitiesListener.java index 69eddadfbfc..14a02cdfa9a 100644 --- a/src/main/java/net/minestom/server/listener/AbilitiesListener.java +++ b/src/main/java/net/minestom/server/listener/AbilitiesListener.java @@ -1,5 +1,6 @@ package net.minestom.server.listener; +import net.minestom.server.entity.GameMode; import net.minestom.server.entity.Player; import net.minestom.server.event.EventDispatcher; import net.minestom.server.event.player.PlayerStartFlyingEvent; @@ -9,7 +10,7 @@ public class AbilitiesListener { public static void listener(ClientPlayerAbilitiesPacket packet, Player player) { - final boolean canFly = player.isAllowFlying() || player.isCreative(); + final boolean canFly = player.isAllowFlying() || player.getGameMode() == GameMode.CREATIVE; if (canFly) { final boolean isFlying = (packet.flags() & 0x2) > 0; diff --git a/src/main/java/net/minestom/server/listener/AnimationListener.java b/src/main/java/net/minestom/server/listener/AnimationListener.java index 11186e9470d..f0a55641c70 100644 --- a/src/main/java/net/minestom/server/listener/AnimationListener.java +++ b/src/main/java/net/minestom/server/listener/AnimationListener.java @@ -17,8 +17,8 @@ public static void animationListener(ClientAnimationPacket packet, Player player PlayerHandAnimationEvent handAnimationEvent = new PlayerHandAnimationEvent(player, hand); EventDispatcher.callCancellable(handAnimationEvent, () -> { switch (hand) { - case MAIN -> player.swingMainHand(); - case OFF -> player.swingOffHand(); + case MAIN -> player.swingMainHand(true); + case OFF -> player.swingOffHand(true); } }); } diff --git a/src/main/java/net/minestom/server/listener/AnvilListener.java b/src/main/java/net/minestom/server/listener/AnvilListener.java new file mode 100644 index 00000000000..de04d8b2247 --- /dev/null +++ b/src/main/java/net/minestom/server/listener/AnvilListener.java @@ -0,0 +1,25 @@ +package net.minestom.server.listener; + +import net.minestom.server.entity.Player; +import net.minestom.server.event.EventDispatcher; +import net.minestom.server.event.player.PlayerAnvilInputEvent; +import net.minestom.server.inventory.Inventory; +import net.minestom.server.inventory.InventoryType; +import net.minestom.server.network.packet.client.play.ClientNameItemPacket; +import org.jetbrains.annotations.NotNull; + +public final class AnvilListener { + + public static void nameItemListener(@NotNull ClientNameItemPacket packet, @NotNull Player player) { + if (!(player.getOpenInventory() instanceof Inventory openInventory)) + return; + if (openInventory.getInventoryType() != InventoryType.ANVIL) + return; + + EventDispatcher.call(new PlayerAnvilInputEvent(player, packet.itemName())); + } + + private AnvilListener() { + } + +} diff --git a/src/main/java/net/minestom/server/listener/BlockPlacementListener.java b/src/main/java/net/minestom/server/listener/BlockPlacementListener.java index 38526303dc3..e80f545bdff 100644 --- a/src/main/java/net/minestom/server/listener/BlockPlacementListener.java +++ b/src/main/java/net/minestom/server/listener/BlockPlacementListener.java @@ -20,15 +20,17 @@ import net.minestom.server.instance.block.BlockManager; import net.minestom.server.instance.block.rule.BlockPlacementRule; import net.minestom.server.inventory.PlayerInventory; +import net.minestom.server.item.ItemComponent; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; +import net.minestom.server.item.component.BlockPredicates; +import net.minestom.server.item.component.ItemBlockState; import net.minestom.server.network.packet.client.play.ClientPlayerBlockPlacementPacket; import net.minestom.server.network.packet.server.play.AcknowledgeBlockChangePacket; import net.minestom.server.network.packet.server.play.BlockChangePacket; import net.minestom.server.utils.chunk.ChunkUtils; import net.minestom.server.utils.validate.Check; - -import java.util.concurrent.atomic.AtomicBoolean; +import net.minestom.server.world.DimensionType; public class BlockPlacementListener { private static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager(); @@ -90,7 +92,8 @@ public static void listener(ClientPlayerBlockPlacementPacket packet, Player play canPlaceBlock = false; // Spectators can't place blocks } else if (player.getGameMode() == GameMode.ADVENTURE) { //Check if the block can be placed on the block - canPlaceBlock = usedItem.meta().canPlaceOn(interactedBlock); + BlockPredicates placePredicate = usedItem.get(ItemComponent.CAN_PLACE_ON, BlockPredicates.NEVER); + canPlaceBlock = placePredicate.test(interactedBlock); } @@ -115,11 +118,13 @@ public static void listener(ClientPlayerBlockPlacementPacket packet, Player play } } - if (placementPosition.y() >= instance.getDimensionType().getMaxY() - || placementPosition.y() < instance.getDimensionType().getMinY()) return; + final DimensionType instanceDim = instance.getCachedDimensionType(); + if (placementPosition.y() >= instanceDim.maxY() || placementPosition.y() < instanceDim.minY()) { + return; + } // Ensure that the final placement position is inside the world border. - if (!instance.getWorldBorder().isInside(placementPosition)) { + if (!instance.getWorldBorder().inBounds(placementPosition)) { canPlaceBlock = false; } @@ -140,7 +145,9 @@ public static void listener(ClientPlayerBlockPlacementPacket packet, Player play return; } - final Block placedBlock = useMaterial.block(); + final ItemBlockState blockState = usedItem.get(ItemComponent.BLOCK_STATE, ItemBlockState.EMPTY); + final Block placedBlock = blockState.apply(useMaterial.block()); + Entity collisionEntity = CollisionUtils.canPlaceBlockAt(instance, placementPosition, placedBlock); if (collisionEntity != null) { // If a player is trying to place a block on themselves, the client will send a block change but will not set the block on the client @@ -154,7 +161,7 @@ public static void listener(ClientPlayerBlockPlacementPacket packet, Player play } // BlockPlaceEvent check - PlayerBlockPlaceEvent playerBlockPlaceEvent = new PlayerBlockPlaceEvent(player, placedBlock, blockFace, new BlockVec(placementPosition), packet.hand()); + PlayerBlockPlaceEvent playerBlockPlaceEvent = new PlayerBlockPlaceEvent(player, placedBlock, blockFace, new BlockVec(placementPosition), cursorPosition, packet.hand()); playerBlockPlaceEvent.consumeBlock(player.getGameMode() != GameMode.CREATIVE); EventDispatcher.call(playerBlockPlaceEvent); if (playerBlockPlaceEvent.isCancelled()) { diff --git a/src/main/java/net/minestom/server/listener/CreativeInventoryActionListener.java b/src/main/java/net/minestom/server/listener/CreativeInventoryActionListener.java index 1554fd5bfb1..bb668c56736 100644 --- a/src/main/java/net/minestom/server/listener/CreativeInventoryActionListener.java +++ b/src/main/java/net/minestom/server/listener/CreativeInventoryActionListener.java @@ -1,5 +1,6 @@ package net.minestom.server.listener; +import net.minestom.server.entity.GameMode; import net.minestom.server.entity.Player; import net.minestom.server.inventory.PlayerInventory; import net.minestom.server.item.ItemStack; @@ -10,7 +11,7 @@ public final class CreativeInventoryActionListener { public static void listener(ClientCreativeInventoryActionPacket packet, Player player) { - if (!player.isCreative()) return; + if (player.getGameMode() != GameMode.CREATIVE) return; short slot = packet.slot(); final ItemStack item = packet.item(); if (slot == -1) { diff --git a/src/main/java/net/minestom/server/listener/PlayerDiggingListener.java b/src/main/java/net/minestom/server/listener/PlayerDiggingListener.java index c95e5de2597..488966379c7 100644 --- a/src/main/java/net/minestom/server/listener/PlayerDiggingListener.java +++ b/src/main/java/net/minestom/server/listener/PlayerDiggingListener.java @@ -1,5 +1,6 @@ package net.minestom.server.listener; +import net.kyori.adventure.nbt.CompoundBinaryTag; import net.minestom.server.coordinate.BlockVec; import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Pos; @@ -16,14 +17,14 @@ import net.minestom.server.instance.block.Block; import net.minestom.server.instance.block.BlockFace; import net.minestom.server.inventory.PlayerInventory; +import net.minestom.server.item.ItemComponent; import net.minestom.server.item.ItemStack; -import net.minestom.server.item.StackingRule; +import net.minestom.server.item.component.BlockPredicates; import net.minestom.server.network.packet.client.play.ClientPlayerDiggingPacket; import net.minestom.server.network.packet.server.play.AcknowledgeBlockChangePacket; import net.minestom.server.network.packet.server.play.BlockEntityDataPacket; import net.minestom.server.utils.block.BlockUtils; import org.jetbrains.annotations.NotNull; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; public final class PlayerDiggingListener { @@ -59,7 +60,7 @@ public static void playerDiggingListener(ClientPlayerDiggingPacket packet, Playe // Refresh block on player screen in case it had special data (like a sign) var registry = diggingResult.block().registry(); if (registry.isBlockEntity()) { - final NBTCompound data = BlockUtils.extractClientNbt(diggingResult.block()); + final CompoundBinaryTag data = BlockUtils.extractClientNbt(diggingResult.block()); player.sendPacketToViewersAndSelf(new BlockEntityDataPacket(blockPosition, registry.blockEntityId(), data)); } } @@ -118,9 +119,8 @@ private static boolean shouldPreventBreaking(@NotNull Player player, Block block } else if (player.getGameMode() == GameMode.ADVENTURE) { // Check if the item can break the block with the current item final ItemStack itemInMainHand = player.getItemInMainHand(); - if (!itemInMainHand.meta().canDestroy(block)) { - return true; - } + final BlockPredicates breakPredicate = itemInMainHand.get(ItemComponent.CAN_BREAK, BlockPredicates.NEVER); + return !breakPredicate.test(block); } return false; } @@ -132,16 +132,15 @@ private static void dropStack(Player player) { private static void dropSingle(Player player) { final ItemStack handItem = player.getInventory().getItemInMainHand(); - final StackingRule stackingRule = StackingRule.get(); - final int handAmount = stackingRule.getAmount(handItem); + final int handAmount = handItem.amount(); if (handAmount <= 1) { // Drop the whole item without copy dropItem(player, handItem, ItemStack.AIR); } else { // Drop a single item dropItem(player, - stackingRule.apply(handItem, 1), // Single dropped item - stackingRule.apply(handItem, handAmount - 1)); // Updated hand + handItem.withAmount(1), // Single dropped item + handItem.withAmount(handAmount - 1)); // Updated hand } } @@ -150,17 +149,14 @@ private static void updateItemState(Player player) { if (meta == null || !meta.isHandActive()) return; Player.Hand hand = meta.getActiveHand(); - player.refreshEating(null); + ItemUpdateStateEvent itemUpdateStateEvent = player.callItemUpdateStateEvent(hand); + + player.clearItemUse(); player.triggerStatus((byte) 9); - ItemUpdateStateEvent itemUpdateStateEvent = player.callItemUpdateStateEvent(hand); - if (itemUpdateStateEvent == null) { - player.refreshActiveHand(true, false, false); - } else { - final boolean isOffHand = itemUpdateStateEvent.getHand() == Player.Hand.OFF; - player.refreshActiveHand(itemUpdateStateEvent.hasHandAnimation(), - isOffHand, itemUpdateStateEvent.isRiptideSpinAttack()); - } + final boolean isOffHand = itemUpdateStateEvent.getHand() == Player.Hand.OFF; + player.refreshActiveHand(itemUpdateStateEvent.hasHandAnimation(), + isOffHand, itemUpdateStateEvent.isRiptideSpinAttack()); } private static void swapItemHand(Player player) { diff --git a/src/main/java/net/minestom/server/listener/SpectateListener.java b/src/main/java/net/minestom/server/listener/SpectateListener.java index cab7033f35c..f528fd26e56 100644 --- a/src/main/java/net/minestom/server/listener/SpectateListener.java +++ b/src/main/java/net/minestom/server/listener/SpectateListener.java @@ -19,7 +19,7 @@ public static void listener(ClientSpectatePacket packet, Player player) { } final UUID targetUuid = packet.target(); - final Entity target = Entity.getEntity(targetUuid); + final Entity target = player.getInstance().getEntityByUuid(targetUuid); // Check if the target is valid if (target == null || target == player) { diff --git a/src/main/java/net/minestom/server/listener/UseEntityListener.java b/src/main/java/net/minestom/server/listener/UseEntityListener.java index 8040d759182..92fd21b48f3 100644 --- a/src/main/java/net/minestom/server/listener/UseEntityListener.java +++ b/src/main/java/net/minestom/server/listener/UseEntityListener.java @@ -13,7 +13,7 @@ public class UseEntityListener { public static void useEntityListener(ClientInteractEntityPacket packet, Player player) { - final Entity entity = Entity.getEntity(packet.targetId()); + final Entity entity = player.getInstance().getEntityById(packet.targetId()); if (entity == null || !entity.isViewer(player) || player.getDistanceSquared(entity) > 6 * 6) return; diff --git a/src/main/java/net/minestom/server/listener/UseItemListener.java b/src/main/java/net/minestom/server/listener/UseItemListener.java index 214bfb05c19..351dabf88ba 100644 --- a/src/main/java/net/minestom/server/listener/UseItemListener.java +++ b/src/main/java/net/minestom/server/listener/UseItemListener.java @@ -7,19 +7,23 @@ import net.minestom.server.event.player.PlayerPreEatEvent; import net.minestom.server.event.player.PlayerUseItemEvent; import net.minestom.server.inventory.PlayerInventory; +import net.minestom.server.item.ItemComponent; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; +import net.minestom.server.item.component.Food; +import net.minestom.server.item.component.PotionContents; import net.minestom.server.network.packet.client.play.ClientUseItemPacket; import net.minestom.server.network.packet.server.play.AcknowledgeBlockChangePacket; +import org.jetbrains.annotations.NotNull; public class UseItemListener { public static void useItemListener(ClientUseItemPacket packet, Player player) { - final PlayerInventory inventory = player.getInventory(); final Player.Hand hand = packet.hand(); - ItemStack itemStack = hand == Player.Hand.MAIN ? inventory.getItemInMainHand() : inventory.getItemInOffHand(); - //itemStack.onRightClick(player, hand); - PlayerUseItemEvent useItemEvent = new PlayerUseItemEvent(player, hand, itemStack); + final ItemStack itemStack = player.getInventory().getItemInHand(hand); + final Material material = itemStack.material(); + + PlayerUseItemEvent useItemEvent = new PlayerUseItemEvent(player, hand, itemStack, defaultUseItemTime(itemStack)); EventDispatcher.call(useItemEvent); player.sendPacket(new AcknowledgeBlockChangePacket(packet.sequence())); @@ -29,22 +33,16 @@ public static void useItemListener(ClientUseItemPacket packet, Player player) { return; } - itemStack = useItemEvent.getItemStack(); - final Material material = itemStack.material(); - // Equip armor with right click final EquipmentSlot equipmentSlot = material.registry().equipmentSlot(); if (equipmentSlot != null) { - final ItemStack currentlyEquipped = playerInventory.getEquipment(equipmentSlot); - if (currentlyEquipped.isAir()) { - playerInventory.setEquipment(equipmentSlot, itemStack); - playerInventory.setItemInHand(hand, currentlyEquipped); - } + final ItemStack currentlyEquipped = player.getEquipment(equipmentSlot); + player.setEquipment(equipmentSlot, itemStack); + player.setItemInHand(hand, currentlyEquipped); } - PlayerItemAnimationEvent.ItemAnimationType itemAnimationType = null; - - boolean cancelAnimation = false; + long itemUseTime = useItemEvent.getItemUseTime(); + PlayerItemAnimationEvent.ItemAnimationType itemAnimationType; if (material == Material.BOW) { itemAnimationType = PlayerItemAnimationEvent.ItemAnimationType.BOW; @@ -54,19 +52,26 @@ public static void useItemListener(ClientUseItemPacket packet, Player player) { itemAnimationType = PlayerItemAnimationEvent.ItemAnimationType.SHIELD; } else if (material == Material.TRIDENT) { itemAnimationType = PlayerItemAnimationEvent.ItemAnimationType.TRIDENT; - } else if (material.isFood()) { + } else if (material == Material.SPYGLASS) { + itemAnimationType = PlayerItemAnimationEvent.ItemAnimationType.SPYGLASS; + } else if (material == Material.GOAT_HORN) { + itemAnimationType = PlayerItemAnimationEvent.ItemAnimationType.HORN; + } else if (material == Material.BRUSH) { + itemAnimationType = PlayerItemAnimationEvent.ItemAnimationType.BRUSH; + } else if (itemStack.has(ItemComponent.FOOD) || itemStack.material() == Material.POTION) { itemAnimationType = PlayerItemAnimationEvent.ItemAnimationType.EAT; - // Eating code, contains the eating time customisation - PlayerPreEatEvent playerPreEatEvent = new PlayerPreEatEvent(player, itemStack, hand, player.getDefaultEatingTime()); - EventDispatcher.callCancellable(playerPreEatEvent, () -> player.refreshEating(hand, playerPreEatEvent.getEatingTime())); - - if (playerPreEatEvent.isCancelled()) { - cancelAnimation = true; - } + PlayerPreEatEvent playerPreEatEvent = new PlayerPreEatEvent(player, itemStack, hand, itemUseTime); + EventDispatcher.call(playerPreEatEvent); + if (playerPreEatEvent.isCancelled()) return; + itemUseTime = playerPreEatEvent.getEatingTime(); + } else { + itemAnimationType = PlayerItemAnimationEvent.ItemAnimationType.OTHER; } - if (!cancelAnimation && itemAnimationType != null) { + if (itemUseTime != 0) { + player.refreshItemUse(hand, itemUseTime); + PlayerItemAnimationEvent playerItemAnimationEvent = new PlayerItemAnimationEvent(player, itemAnimationType, hand); EventDispatcher.callCancellable(playerItemAnimationEvent, () -> { player.refreshActiveHand(true, hand == Player.Hand.OFF, false); @@ -75,4 +80,17 @@ public static void useItemListener(ClientUseItemPacket packet, Player player) { } } + private static int defaultUseItemTime(@NotNull ItemStack itemStack) { + final Food food = itemStack.get(ItemComponent.FOOD); + if (food != null) return food.eatDurationTicks(); + else if (itemStack.material() == Material.POTION) return PotionContents.POTION_DRINK_TIME; + else if (itemStack.material() == Material.BOW + || itemStack.material() == Material.CROSSBOW + || itemStack.material() == Material.SHIELD + || itemStack.material() == Material.TRIDENT + || itemStack.material() == Material.SPYGLASS + || itemStack.material() == Material.GOAT_HORN + || itemStack.material() == Material.BRUSH) return -1; + return 0; + } } diff --git a/src/main/java/net/minestom/server/listener/WindowListener.java b/src/main/java/net/minestom/server/listener/WindowListener.java index 10b13921359..dcf3ef91b6c 100644 --- a/src/main/java/net/minestom/server/listener/WindowListener.java +++ b/src/main/java/net/minestom/server/listener/WindowListener.java @@ -1,5 +1,6 @@ package net.minestom.server.listener; +import net.minestom.server.entity.GameMode; import net.minestom.server.entity.Player; import net.minestom.server.event.EventDispatcher; import net.minestom.server.event.inventory.InventoryCloseEvent; @@ -52,9 +53,9 @@ public static void clickWindowListener(ClientClickWindowPacket packet, Player pl } else if (clickType == ClientClickWindowPacket.ClickType.SWAP) { successful = inventory.changeHeld(player, slot, button); } else if (clickType == ClientClickWindowPacket.ClickType.CLONE) { - successful = player.isCreative(); + successful = player.getGameMode() == GameMode.CREATIVE; if (successful) { - setCursor(player, inventory, packet.clickedItem()); + player.getInventory().setCursorItem(packet.clickedItem()); } } else if (clickType == ClientClickWindowPacket.ClickType.THROW) { successful = inventory.drop(player, false, slot, button); @@ -73,7 +74,8 @@ public static void clickWindowListener(ClientClickWindowPacket packet, Player pl } // Prevent the player from picking a ghost item in cursor - refreshCursorItem(player, inventory); + ItemStack cursorItem = player.getInventory().getCursorItem(); + player.sendPacket(SetSlotPacket.createCursorPacket(cursorItem)); // (Why is the ping packet necessary?) player.sendPacket(new PingPacket((1 << 30) | (windowId << 16))); @@ -95,30 +97,4 @@ public static void closeWindowListener(ClientCloseWindowPacket packet, Player pl player.openInventory(newInventory); } - /** - * @param player the player to refresh the cursor item - * @param inventory the player open inventory, null if not any (could be player inventory) - */ - private static void refreshCursorItem(Player player, AbstractInventory inventory) { - ItemStack cursorItem; - if (inventory instanceof PlayerInventory playerInventory) { - cursorItem = playerInventory.getCursorItem(); - } else if (inventory instanceof Inventory standardInventory) { - cursorItem = standardInventory.getCursorItem(player); - } else { - throw new RuntimeException("Invalid inventory: " + inventory.getClass()); - } - final SetSlotPacket setSlotPacket = SetSlotPacket.createCursorPacket(cursorItem); - player.sendPacket(setSlotPacket); - } - - private static void setCursor(Player player, AbstractInventory inventory, ItemStack itemStack) { - if (inventory instanceof PlayerInventory playerInventory) { - playerInventory.setCursorItem(itemStack); - } else if (inventory instanceof Inventory standardInventory) { - standardInventory.setCursorItem(player, itemStack); - } else { - throw new RuntimeException("Invalid inventory: " + inventory.getClass()); - } - } } diff --git a/src/main/java/net/minestom/server/listener/common/CookieListener.java b/src/main/java/net/minestom/server/listener/common/CookieListener.java new file mode 100644 index 00000000000..c1fccd5ae94 --- /dev/null +++ b/src/main/java/net/minestom/server/listener/common/CookieListener.java @@ -0,0 +1,14 @@ +package net.minestom.server.listener.common; + +import net.minestom.server.network.packet.client.common.ClientCookieResponsePacket; +import net.minestom.server.network.player.PlayerConnection; +import org.jetbrains.annotations.NotNull; + +public final class CookieListener { + + public static void handleCookieResponse(@NotNull ClientCookieResponsePacket packet, @NotNull PlayerConnection connection) { + connection.receiveCookieResponse(packet.key(), packet.value()); + } + + private CookieListener() {} +} diff --git a/src/main/java/net/minestom/server/listener/common/ResourcePackListener.java b/src/main/java/net/minestom/server/listener/common/ResourcePackListener.java index 5cf0c328d58..4c6896ec1be 100644 --- a/src/main/java/net/minestom/server/listener/common/ResourcePackListener.java +++ b/src/main/java/net/minestom/server/listener/common/ResourcePackListener.java @@ -9,6 +9,7 @@ public class ResourcePackListener { public static void listener(ClientResourcePackStatusPacket packet, Player player) { EventDispatcher.call(new PlayerResourcePackStatusEvent(player, packet.status())); + if (!player.isOnline()) return; // Run adventure callbacks for the resource pack player.onResourcePackStatus(packet.id(), packet.status()); diff --git a/src/main/java/net/minestom/server/listener/manager/PacketListenerManager.java b/src/main/java/net/minestom/server/listener/manager/PacketListenerManager.java index 3a45935f81b..07ae247ed96 100644 --- a/src/main/java/net/minestom/server/listener/manager/PacketListenerManager.java +++ b/src/main/java/net/minestom/server/listener/manager/PacketListenerManager.java @@ -4,10 +4,7 @@ import net.minestom.server.event.EventDispatcher; import net.minestom.server.event.player.PlayerPacketEvent; import net.minestom.server.listener.*; -import net.minestom.server.listener.common.KeepAliveListener; -import net.minestom.server.listener.common.PluginMessageListener; -import net.minestom.server.listener.common.ResourcePackListener; -import net.minestom.server.listener.common.SettingsListener; +import net.minestom.server.listener.common.*; import net.minestom.server.listener.preplay.ConfigListener; import net.minestom.server.listener.preplay.HandshakeListener; import net.minestom.server.listener.preplay.LoginListener; @@ -16,6 +13,7 @@ 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; @@ -51,13 +49,16 @@ public PacketListenerManager() { setListener(ConnectionState.LOGIN, ClientEncryptionResponsePacket.class, LoginListener::loginEncryptionResponseListener); setListener(ConnectionState.LOGIN, ClientLoginPluginResponsePacket.class, LoginListener::loginPluginResponseListener); setListener(ConnectionState.LOGIN, ClientLoginAcknowledgedPacket.class, LoginListener::loginAckListener); + setListener(ConnectionState.LOGIN, ClientCookieResponsePacket.class, CookieListener::handleCookieResponse); setConfigurationListener(ClientSettingsPacket.class, SettingsListener::listener); setConfigurationListener(ClientPluginMessagePacket.class, PluginMessageListener::listener); 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); setPlayListener(ClientKeepAlivePacket.class, KeepAliveListener::listener); setPlayListener(ClientCommandChatPacket.class, ChatMessageListener::commandChatListener); @@ -95,6 +96,8 @@ public PacketListenerManager() { setPlayListener(ClientChatSessionUpdatePacket.class, (packet, player) -> {/* empty */}); setPlayListener(ClientChunkBatchReceivedPacket.class, ChunkBatchListener::batchReceivedListener); setPlayListener(ClientPingRequestPacket.class, PlayPingListener::requestListener); + setListener(ConnectionState.PLAY, ClientCookieResponsePacket.class, CookieListener::handleCookieResponse); + setPlayListener(ClientNameItemPacket.class, AnvilListener::nameItemListener); } /** diff --git a/src/main/java/net/minestom/server/listener/preplay/ConfigListener.java b/src/main/java/net/minestom/server/listener/preplay/ConfigListener.java index 054ccaf7c2b..ab932903476 100644 --- a/src/main/java/net/minestom/server/listener/preplay/ConfigListener.java +++ b/src/main/java/net/minestom/server/listener/preplay/ConfigListener.java @@ -4,11 +4,16 @@ 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) { + player.getPlayerConnection().receiveKnownPacksResponse(packet.entries()); + } + public static void finishConfigListener(@NotNull ClientFinishConfigurationPacket packet, @NotNull Player player) { CONNECTION_MANAGER.transitionConfigToPlay(player); } diff --git a/src/main/java/net/minestom/server/listener/preplay/HandshakeListener.java b/src/main/java/net/minestom/server/listener/preplay/HandshakeListener.java index 0cf0b869b50..b998be1206a 100644 --- a/src/main/java/net/minestom/server/listener/preplay/HandshakeListener.java +++ b/src/main/java/net/minestom/server/listener/preplay/HandshakeListener.java @@ -40,8 +40,8 @@ public final class HandshakeListener { public static void listener(@NotNull ClientHandshakePacket packet, @NotNull PlayerConnection connection) { String address = packet.serverAddress(); switch (packet.intent()) { - case 1 -> connection.setConnectionState(ConnectionState.STATUS); - case 2 -> { + case STATUS -> connection.setConnectionState(ConnectionState.STATUS); + case LOGIN -> { connection.setConnectionState(ConnectionState.LOGIN); if (packet.protocolVersion() != MinecraftServer.PROTOCOL_VERSION) { // Incorrect client version @@ -114,6 +114,9 @@ public static void listener(@NotNull ClientHandshakePacket packet, @NotNull Play } } } + case TRANSFER -> { + throw new UnsupportedOperationException("Transfer intent is not supported in HandshakeListener"); + } default -> { // Unexpected error } diff --git a/src/main/java/net/minestom/server/listener/preplay/LoginListener.java b/src/main/java/net/minestom/server/listener/preplay/LoginListener.java index 15df756e722..ab9bf1a89c2 100644 --- a/src/main/java/net/minestom/server/listener/preplay/LoginListener.java +++ b/src/main/java/net/minestom/server/listener/preplay/LoginListener.java @@ -75,14 +75,23 @@ public static void loginStartListener(@NotNull ClientLoginStartPacket packet, @N byte[] nonce = new byte[4]; ThreadLocalRandom.current().nextBytes(nonce); socketConnection.setNonce(nonce); - socketConnection.sendPacket(new EncryptionRequestPacket("", publicKey, nonce)); + socketConnection.sendPacket(new EncryptionRequestPacket("", publicKey, nonce, true)); } else { final boolean bungee = BungeeCordProxy.isEnabled(); // Offline - final UUID playerUuid = bungee && isSocketConnection ? - ((PlayerSocketConnection) connection).gameProfile().uuid() : - CONNECTION_MANAGER.getPlayerConnectionUuid(connection, packet.username()); - CONNECTION_MANAGER.createPlayer(connection, playerUuid, packet.username()); + AsyncUtils.runAsync(() -> { + try { + final UUID playerUuid; + if (bungee && isSocketConnection) + playerUuid = ((PlayerSocketConnection) connection).gameProfile().uuid(); + else playerUuid = CONNECTION_MANAGER.getPlayerConnectionUuid(connection, packet.username()); + CONNECTION_MANAGER.createPlayer(connection, playerUuid, packet.username()); + + } catch (Exception exception) { + connection.sendPacket(new LoginDisconnectPacket(Component.text(exception.getClass().getSimpleName() + ": " + exception.getMessage()))); + connection.disconnect(); + } + }); } } diff --git a/src/main/java/net/minestom/server/map/LargeFramebuffer.java b/src/main/java/net/minestom/server/map/LargeFramebuffer.java index cd4f26f4340..547592ffb6b 100644 --- a/src/main/java/net/minestom/server/map/LargeFramebuffer.java +++ b/src/main/java/net/minestom/server/map/LargeFramebuffer.java @@ -33,8 +33,8 @@ default MapDataPacket preparePacket(int mapId, int left, int top) { byte[] colors = new byte[Framebuffer.WIDTH * Framebuffer.WIDTH]; final int width = Math.min(width(), left + Framebuffer.WIDTH) - left; final int height = Math.min(height(), top + Framebuffer.HEIGHT) - top; - for (int y = top; y < height; y++) { - for (int x = left; x < width; x++) { + for (int y = top; y < top+height; y++) { + for (int x = left; x < left+width; x++) { final byte color = getMapColor(x, y); colors[Framebuffer.index(x - left, y - top)] = color; } diff --git a/src/main/java/net/minestom/server/message/ChatType.java b/src/main/java/net/minestom/server/message/ChatType.java new file mode 100644 index 00000000000..3c0cefb2cee --- /dev/null +++ b/src/main/java/net/minestom/server/message/ChatType.java @@ -0,0 +1,65 @@ +package net.minestom.server.message; + +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.Registry; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public sealed interface ChatType extends ProtocolObject, ChatTypes permits ChatTypeImpl { + + static @NotNull ChatType create( + @NotNull ChatTypeDecoration chat, + @NotNull ChatTypeDecoration narration + ) { + return new ChatTypeImpl(chat, narration, null); + } + + static @NotNull Builder builder() { + return new Builder(); + } + + + /** + *

Creates a new registry for chat types, loading the vanilla chat types.

+ * + * @see net.minestom.server.MinecraftServer to get an existing instance of the registry + */ + @ApiStatus.Internal + static @NotNull DynamicRegistry createDefaultRegistry() { + return DynamicRegistry.create( + "minecraft:chat_type", ChatTypeImpl.REGISTRY_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 ChatTypeDecoration chat; + private ChatTypeDecoration narration; + + private Builder() { + } + + 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(chat, narration, null); + } + } +} diff --git a/src/main/java/net/minestom/server/message/ChatTypeDecoration.java b/src/main/java/net/minestom/server/message/ChatTypeDecoration.java new file mode 100644 index 00000000000..d6f9989098a --- /dev/null +++ b/src/main/java/net/minestom/server/message/ChatTypeDecoration.java @@ -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 parameters, + @NotNull Style style +) { + + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> { + String translationKey = tag.getString("translation_key"); + List 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 NBT_TYPE = BinaryTagSerializer.fromEnumStringable(Parameter.class); + private static final BinaryTagSerializer> LIST_NBT_TYPE = NBT_TYPE.list(); + } + +} diff --git a/src/main/java/net/minestom/server/message/ChatTypeImpl.java b/src/main/java/net/minestom/server/message/ChatTypeImpl.java new file mode 100644 index 00000000000..b92812e0794 --- /dev/null +++ b/src/main/java/net/minestom/server/message/ChatTypeImpl.java @@ -0,0 +1,35 @@ +package net.minestom.server.message; + +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minestom.server.registry.Registry; +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 ChatTypeDecoration chat, + @NotNull ChatTypeDecoration narration, + @Nullable Registry.ChatTypeEntry registry +) implements ChatType { + + static final BinaryTagSerializer REGISTRY_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(chat, "missing chat"); + Check.notNull(narration, "missing narration"); + } + + ChatTypeImpl(@NotNull Registry.ChatTypeEntry registry) { + this(registry.chat(), registry.narration(), registry); + } + +} diff --git a/src/main/java/net/minestom/server/message/Messenger.java b/src/main/java/net/minestom/server/message/Messenger.java index 16af3663e65..3c508c6e1d5 100644 --- a/src/main/java/net/minestom/server/message/Messenger.java +++ b/src/main/java/net/minestom/server/message/Messenger.java @@ -7,11 +7,7 @@ import net.minestom.server.utils.PacketUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; -import org.jglrxavpok.hephaistos.nbt.NBTException; -import org.jglrxavpok.hephaistos.parser.SNBTParser; -import java.io.StringReader; import java.util.Collection; import java.util.Objects; import java.util.UUID; @@ -27,46 +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 NBTCompound CHAT_REGISTRY; - - static { - try { - CHAT_REGISTRY = (NBTCompound) new SNBTParser(new StringReader( - """ - { - "type": "minecraft:chat_type", - "value": [ - { - "name":"minecraft:chat", - "id":1, - "element":{ - "chat":{ - "translation_key":"chat.type.text", - "parameters":[ - "sender", - "content" - ] - }, - "narration":{ - "translation_key":"chat.type.text.narrate", - "parameters":[ - "sender", - "content" - ] - } - } - } ] - }""" - )).parse(); - } catch (NBTException e) { - throw new RuntimeException(e); - } - } - - public static @NotNull NBTCompound chatRegistry() { - return CHAT_REGISTRY; - } - /** * Sends a message to a player, respecting their chat settings. * diff --git a/src/main/java/net/minestom/server/network/ComponentNetworkBufferTypeImpl.java b/src/main/java/net/minestom/server/network/ComponentNetworkBufferTypeImpl.java new file mode 100644 index 00000000000..35e4cf805f9 --- /dev/null +++ b/src/main/java/net/minestom/server/network/ComponentNetworkBufferTypeImpl.java @@ -0,0 +1,323 @@ +package net.minestom.server.network; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.text.*; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.Style; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.format.TextDecoration; +import net.minestom.server.adventure.serializer.nbt.NbtComponentSerializer; +import net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Locale; + +import static net.minestom.server.network.NetworkBuffer.*; + +record ComponentNetworkBufferTypeImpl() implements NetworkBufferTypeImpl { + + @Override + public void write(@NotNull NetworkBuffer buffer, @NotNull Component value) { + Check.notNull(value, "Component cannot be null"); + + buffer.write(BYTE, TAG_COMPOUND); + writeInnerComponent(buffer, value); + } + + @Override + public Component read(@NotNull NetworkBuffer buffer) { + final BinaryTag nbt = buffer.read(NBT); + return NbtComponentSerializer.nbt().deserialize(nbt); + } + + // WRITING IMPL, pretty gross. Would not recommend reading. + + private static final byte TAG_END = 0; + private static final byte TAG_BYTE = 1; + private static final byte TAG_INT = 3; + private static final byte TAG_STRING = 8; + private static final byte TAG_LIST = 9; + private static final byte TAG_COMPOUND = 10; + + private void writeInnerComponent(@NotNull NetworkBuffer buffer, @NotNull Component component) { + buffer.write(BYTE, TAG_STRING); // Start first tag (always the type) + writeUtf(buffer, "type"); + switch (component) { + case TextComponent text -> { + writeUtf(buffer, "text"); + + buffer.write(BYTE, TAG_STRING); // Start "text" tag + writeUtf(buffer, "text"); + writeUtf(buffer, text.content()); + } + case TranslatableComponent translatable -> { + writeUtf(buffer, "translatable"); + + buffer.write(BYTE, TAG_STRING); // Start "translate" tag + writeUtf(buffer, "translate"); + writeUtf(buffer, translatable.key()); + + final String fallback = translatable.fallback(); + if (fallback != null) { + buffer.write(BYTE, TAG_STRING); + writeUtf(buffer, "fallback"); + writeUtf(buffer, fallback); + } + + final List args = translatable.arguments(); + if (!args.isEmpty()) { + buffer.write(BYTE, TAG_LIST); + writeUtf(buffer, "with"); + buffer.write(BYTE, TAG_COMPOUND); // List type + buffer.write(INT, args.size()); + for (final TranslationArgument arg : args) + writeInnerComponent(buffer, arg.asComponent()); + } + } + case ScoreComponent score -> { + writeUtf(buffer, "score"); + + buffer.write(BYTE, TAG_COMPOUND); // Start "score" tag + writeUtf(buffer, "score"); + { + buffer.write(BYTE, TAG_STRING); + writeUtf(buffer, "name"); + writeUtf(buffer, score.name()); + + buffer.write(BYTE, TAG_STRING); + writeUtf(buffer, "objective"); + writeUtf(buffer, score.objective()); + } + buffer.write(BYTE, TAG_END); // End "score" tag + + } + case SelectorComponent selector -> { + writeUtf(buffer, "selector"); + + buffer.write(BYTE, TAG_STRING); + writeUtf(buffer, "selector"); + writeUtf(buffer, selector.pattern()); + + final Component separator = selector.separator(); + if (separator != null) { + buffer.write(BYTE, TAG_COMPOUND); + writeUtf(buffer, "separator"); + writeInnerComponent(buffer, separator); + } + } + case KeybindComponent keybind -> { + writeUtf(buffer, "keybind"); + + buffer.write(BYTE, TAG_STRING); + writeUtf(buffer, "keybind"); + writeUtf(buffer, keybind.keybind()); + } + case NBTComponent nbt -> { + //todo + throw new UnsupportedOperationException("NBTComponent is not implemented yet"); + } + default -> throw new UnsupportedOperationException("Unsupported component type: " + component.getClass()); + } + + // Children + if (!component.children().isEmpty()) { + buffer.write(BYTE, TAG_LIST); + writeUtf(buffer, "extra"); + buffer.write(BYTE, TAG_COMPOUND); // List type + + buffer.write(INT, component.children().size()); + for (final Component child : component.children()) + writeInnerComponent(buffer, child); + } + + // Formatting/Interactivity + writeComponentStyle(buffer, component.style()); + + buffer.write(BYTE, TAG_END); + } + + private void writeComponentStyle(@NotNull NetworkBuffer buffer, @NotNull Style style) { + final TextColor color = style.color(); + if (color != null) { + buffer.write(BYTE, TAG_STRING); + writeUtf(buffer, "color"); + if (color instanceof NamedTextColor namedColor) + writeUtf(buffer, namedColor.toString()); + else writeUtf(buffer, color.asHexString()); + } + + final Key font = style.font(); + if (font != null) { + buffer.write(BYTE, TAG_STRING); + writeUtf(buffer, "font"); + writeUtf(buffer, font.asString()); + } + + final TextDecoration.State bold = style.decoration(TextDecoration.BOLD); + if (bold != TextDecoration.State.NOT_SET) { + buffer.write(BYTE, TAG_BYTE); + writeUtf(buffer, "bold"); + buffer.write(BYTE, bold == TextDecoration.State.TRUE ? (byte) 1 : (byte) 0); + } + + final TextDecoration.State italic = style.decoration(TextDecoration.ITALIC); + if (italic != TextDecoration.State.NOT_SET) { + buffer.write(BYTE, TAG_BYTE); + writeUtf(buffer, "italic"); + buffer.write(BYTE, italic == TextDecoration.State.TRUE ? (byte) 1 : (byte) 0); + } + + final TextDecoration.State underlined = style.decoration(TextDecoration.UNDERLINED); + if (underlined != TextDecoration.State.NOT_SET) { + buffer.write(BYTE, TAG_BYTE); + writeUtf(buffer, "underlined"); + buffer.write(BYTE, underlined == TextDecoration.State.TRUE ? (byte) 1 : (byte) 0); + } + + final TextDecoration.State strikethrough = style.decoration(TextDecoration.STRIKETHROUGH); + if (strikethrough != TextDecoration.State.NOT_SET) { + buffer.write(BYTE, TAG_BYTE); + writeUtf(buffer, "strikethrough"); + buffer.write(BYTE, strikethrough == TextDecoration.State.TRUE ? (byte) 1 : (byte) 0); + } + + final TextDecoration.State obfuscated = style.decoration(TextDecoration.OBFUSCATED); + if (obfuscated != TextDecoration.State.NOT_SET) { + buffer.write(BYTE, TAG_BYTE); + writeUtf(buffer, "obfuscated"); + buffer.write(BYTE, obfuscated == TextDecoration.State.TRUE ? (byte) 1 : (byte) 0); + } + + final String insertion = style.insertion(); + if (insertion != null) { + buffer.write(BYTE, TAG_STRING); + writeUtf(buffer, "insertion"); + writeUtf(buffer, insertion); + } + + final ClickEvent clickEvent = style.clickEvent(); + if (clickEvent != null) writeClickEvent(buffer, clickEvent); + + final HoverEvent hoverEvent = style.hoverEvent(); + if (hoverEvent != null) writeHoverEvent(buffer, hoverEvent); + } + + private void writeClickEvent(@NotNull NetworkBuffer buffer, @NotNull ClickEvent clickEvent) { + buffer.write(BYTE, TAG_COMPOUND); + writeUtf(buffer, "clickEvent"); + + buffer.write(BYTE, TAG_STRING); + writeUtf(buffer, "action"); + writeUtf(buffer, clickEvent.action().name().toLowerCase(Locale.ROOT)); + + buffer.write(BYTE, TAG_STRING); + writeUtf(buffer, "value"); + writeUtf(buffer, clickEvent.value()); + + buffer.write(BYTE, TAG_END); + } + + @SuppressWarnings("unchecked") private void writeHoverEvent(@NotNull NetworkBuffer buffer, @NotNull HoverEvent hoverEvent) { + buffer.write(BYTE, TAG_COMPOUND); + writeUtf(buffer, "hoverEvent"); + + buffer.write(BYTE, TAG_STRING); + writeUtf(buffer, "action"); + writeUtf(buffer, hoverEvent.action().toString().toLowerCase(Locale.ROOT)); + + buffer.write(BYTE, TAG_COMPOUND); // Start contents tag + writeUtf(buffer, "contents"); + if (hoverEvent.action() == HoverEvent.Action.SHOW_TEXT) { + writeInnerComponent(buffer, (Component) hoverEvent.value()); + } else if (hoverEvent.action() == HoverEvent.Action.SHOW_ITEM) { + var value = ((HoverEvent) hoverEvent).value(); + + buffer.write(BYTE, TAG_STRING); + writeUtf(buffer, "id"); + writeUtf(buffer, value.item().asString()); + + buffer.write(BYTE, TAG_INT); + writeUtf(buffer, "count"); + buffer.write(INT, value.count()); + + buffer.write(BYTE, TAG_COMPOUND); + writeUtf(buffer, "components"); + //todo item components + buffer.write(BYTE, TAG_END); + + buffer.write(BYTE, TAG_END); // End contents tag + } else if (hoverEvent.action() == HoverEvent.Action.SHOW_ENTITY) { + var value = ((HoverEvent) hoverEvent).value(); + + final Component name = value.name(); + if (name != null) { + buffer.write(BYTE, TAG_COMPOUND); + writeUtf(buffer, "name"); + writeInnerComponent(buffer, name); + } + + buffer.write(BYTE, TAG_STRING); + writeUtf(buffer, "type"); + writeUtf(buffer, value.type().asString()); + + buffer.write(BYTE, TAG_STRING); + writeUtf(buffer, "id"); + writeUtf(buffer, value.id().toString()); + + buffer.write(BYTE, TAG_END); // End contents tag + } else { + throw new UnsupportedOperationException("Unknown hover event action: " + hoverEvent.action()); + } + + buffer.write(BYTE, TAG_END); + } + + /** + * This is a very gross version of {@link java.io.DataOutputStream#writeUTF(String)}. We need the data in the java + * modified utf-8 format, and I couldnt find a method without creating a new buffer for it. + * + * @param buffer the buffer to write to + * @param str the string to write + */ + private static void writeUtf(@NotNull NetworkBuffer buffer, @NotNull String str) { + final int strlen = str.length(); + int utflen = strlen; // optimized for ASCII + + for (int i = 0; i < strlen; i++) { + int c = str.charAt(i); + if (c >= 0x80 || c == 0) + utflen += (c >= 0x800) ? 2 : 1; + } + + if (utflen > 65535 || /* overflow */ utflen < strlen) + throw new RuntimeException("UTF-8 string too long"); + + buffer.write(SHORT, (short) utflen); + buffer.ensureSize(utflen); + int i; + for (i = 0; i < strlen; i++) { // optimized for initial run of ASCII + int c = str.charAt(i); + if (c >= 0x80 || c == 0) break; + buffer.nioBuffer.put(buffer.writeIndex++, (byte) c); + } + + for (; i < strlen; i++) { + int c = str.charAt(i); + if (c < 0x80 && c != 0) { + buffer.nioBuffer.put(buffer.writeIndex++, (byte) c); + } else if (c >= 0x800) { + buffer.nioBuffer.put(buffer.writeIndex++, (byte) (0xE0 | ((c >> 12) & 0x0F))); + buffer.nioBuffer.put(buffer.writeIndex++, (byte) (0x80 | ((c >> 6) & 0x3F))); + buffer.nioBuffer.put(buffer.writeIndex++, (byte) (0x80 | ((c >> 0) & 0x3F))); + } else { + buffer.nioBuffer.put(buffer.writeIndex++, (byte) (0xC0 | ((c >> 6) & 0x1F))); + buffer.nioBuffer.put(buffer.writeIndex++, (byte) (0x80 | ((c >> 0) & 0x3F))); + } + } + } + +} diff --git a/src/main/java/net/minestom/server/network/ConnectionManager.java b/src/main/java/net/minestom/server/network/ConnectionManager.java index c716ddef9e3..14a58b7540a 100644 --- a/src/main/java/net/minestom/server/network/ConnectionManager.java +++ b/src/main/java/net/minestom/server/network/ConnectionManager.java @@ -5,46 +5,39 @@ 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.featureflag.FeatureFlag; 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; 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.RegistryDataPacket; +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.configuration.UpdateEnabledFeaturesPacket; 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; import net.minestom.server.network.player.PlayerSocketConnection; import net.minestom.server.network.plugin.LoginPluginMessageProcessor; +import net.minestom.server.registry.StaticProtocolObject; import net.minestom.server.utils.StringUtils; import net.minestom.server.utils.async.AsyncUtils; -import net.minestom.server.utils.debug.DebugUtils; import net.minestom.server.utils.validate.Check; import org.jctools.queues.MessagePassingQueue; import org.jctools.queues.MpscUnboundedArrayQueue; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jglrxavpok.hephaistos.nbt.NBT; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import java.util.function.Function; +import java.util.stream.Collectors; /** * Manages the connected clients. @@ -70,6 +63,8 @@ public final class ConnectionManager { private final Set unmodifiableConfigurationPlayers = Collections.unmodifiableSet(configurationPlayers); private final Set unmodifiablePlayPlayers = Collections.unmodifiableSet(playPlayers); + private final CachedPacket resetChatPacket = new CachedPacket(new ResetChatPacket()); + // The uuid provider once a player login private volatile UuidProvider uuidProvider = (playerConnection, username) -> UUID.randomUUID(); @@ -211,7 +206,7 @@ public void setPlayerProvider(@Nullable PlayerProvider playerProvider) { final Player player = playerProvider.createPlayer(uuid, username, connection); this.connectionPlayerMap.put(connection, player); var future = transitionLoginToConfig(player); - if (DebugUtils.INSIDE_TEST) future.join(); + if (ServerFlag.INSIDE_TEST) future.join(); return player; } @@ -240,9 +235,6 @@ public void setPlayerProvider(@Nullable PlayerProvider playerProvider) { if (!player.getUsername().equals(eventUsername)) { player.setUsernameField(eventUsername); } - if (!player.getUuid().equals(eventUuid)) { - player.setUuid(eventUuid); - } } // Wait for pending login plugin messages @@ -254,7 +246,7 @@ public void setPlayerProvider(@Nullable PlayerProvider playerProvider) { } // Send login success packet (and switch to configuration phase) - LoginSuccessPacket loginSuccessPacket = new LoginSuccessPacket(player.getUuid(), player.getUsername(), 0); + LoginSuccessPacket loginSuccessPacket = new LoginSuccessPacket(player.getUuid(), player.getUsername(), 0, true); playerConnection.sendPacket(loginSuccessPacket); }); } @@ -265,38 +257,64 @@ public void transitionPlayToConfig(@NotNull Player player) { configurationPlayers.add(player); } + /** + * Return value exposed for testing + */ @ApiStatus.Internal - public void doConfiguration(@NotNull Player player, boolean isFirstConfig) { + public CompletableFuture doConfiguration(@NotNull Player player, boolean isFirstConfig) { if (isFirstConfig) { configurationPlayers.add(player); keepAlivePlayers.add(player); } - player.getPlayerConnection().setConnectionState(ConnectionState.CONFIGURATION); - CompletableFuture configFuture = AsyncUtils.runAsync(() -> { - player.sendPacket(PluginMessagePacket.getBrandPacket()); + final PlayerConnection connection = player.getPlayerConnection(); + connection.setConnectionState(ConnectionState.CONFIGURATION); + + player.sendPacket(PluginMessagePacket.getBrandPacket()); + // Request known packs immediately, but don't wait for the response until required (sending registry data). + final var knownPacksFuture = connection.requestKnownPacks(List.of(SelectKnownPacksPacket.MINECRAFT_CORE)); - var event = new AsyncPlayerConfigurationEvent(player, isFirstConfig, Set.of(FeatureFlag.VANILLA)); + return AsyncUtils.runAsync(() -> { + var event = new AsyncPlayerConfigurationEvent(player, isFirstConfig); EventDispatcher.call(event); if (!player.isOnline()) return; // Player was kicked during config. + player.sendPacket(new UpdateEnabledFeaturesPacket(event.getFeatureFlags().stream().map(StaticProtocolObject::namespace).collect(Collectors.toSet()))); // send player features that were enabled or disabled during async config event + final Instance spawningInstance = event.getSpawningInstance(); Check.notNull(spawningInstance, "You need to specify a spawning instance in the AsyncPlayerConfigurationEvent"); + if (event.willClearChat()) { + player.sendPacket(resetChatPacket); + } + // Registry data (if it should be sent) if (event.willSendRegistryData()) { - var registry = new HashMap(); - registry.put("minecraft:chat_type", Messenger.chatRegistry()); - registry.put("minecraft:dimension_type", MinecraftServer.getDimensionTypeManager().toNBT()); - registry.put("minecraft:worldgen/biome", MinecraftServer.getBiomeManager().toNBT()); - registry.put("minecraft:damage_type", DamageType.getNBT()); - registry.put("minecraft:trim_material", MinecraftServer.getTrimManager().getTrimMaterialNBT()); - registry.put("minecraft:trim_pattern", MinecraftServer.getTrimManager().getTrimPatternNBT()); - player.sendPacket(new RegistryDataPacket(NBT.Compound(registry))); + List knownPacks; + try { + knownPacks = knownPacksFuture.get(5, TimeUnit.SECONDS); + } catch (InterruptedException | TimeoutException e) { + throw new RuntimeException("Client failed to respond to known packs request", e); + } catch (ExecutionException e) { + throw new RuntimeException("Error receiving known packs", e); + } + boolean excludeVanilla = knownPacks.contains(SelectKnownPacksPacket.MINECRAFT_CORE); + + 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(serverProcess.enchantment().registryDataPacket(excludeVanilla)); + player.sendPacket(serverProcess.paintingVariant().registryDataPacket(excludeVanilla)); + player.sendPacket(serverProcess.jukeboxSong().registryDataPacket(excludeVanilla)); player.sendPacket(TagsPacket.DEFAULT_TAGS); } - player.sendPacket(new UpdateEnabledFeaturesPacket(event.getEnabledFeatures())); // Wait for pending resource packs if any var packFuture = player.getResourcePackFuture(); @@ -306,7 +324,6 @@ public void doConfiguration(@NotNull Player player, boolean isFirstConfig) { player.setPendingOptions(spawningInstance, event.isHardcore()); player.sendPacket(new FinishConfigurationPacket()); }); - if (DebugUtils.INSIDE_TEST) configFuture.join(); } @ApiStatus.Internal @@ -363,11 +380,16 @@ public void updateWaitingPlayers() { playPlayers.add(player); keepAlivePlayers.add(player); + // This fixes a bug with Geyser. They do not reply to keep alive during config, meaning that + // `Player#didAnswerKeepAlive()` will always be false when entering the play state, so a new keep + // alive will never be sent and they will disconnect themselves or we will kick them for not replying. + player.refreshAnswerKeepAlive(true); + // Spawn the player at Player#getRespawnPoint CompletableFuture spawnFuture = player.UNSAFE_init(); // Required to get the exact moment the player spawns - if (DebugUtils.INSIDE_TEST) spawnFuture.join(); + if (ServerFlag.INSIDE_TEST) spawnFuture.join(); }); } diff --git a/src/main/java/net/minestom/server/network/NetworkBuffer.java b/src/main/java/net/minestom/server/network/NetworkBuffer.java index 6745713ecfd..3944320ecc8 100644 --- a/src/main/java/net/minestom/server/network/NetworkBuffer.java +++ b/src/main/java/net/minestom/server/network/NetworkBuffer.java @@ -1,23 +1,20 @@ package net.minestom.server.network; +import net.kyori.adventure.nbt.BinaryTag; import net.kyori.adventure.text.Component; +import net.minestom.server.MinecraftServer; import net.minestom.server.coordinate.Point; import net.minestom.server.entity.Entity; -import net.minestom.server.entity.metadata.animal.FrogMeta; -import net.minestom.server.entity.metadata.animal.SnifferMeta; -import net.minestom.server.entity.metadata.animal.tameable.CatMeta; -import net.minestom.server.entity.metadata.other.PaintingMeta; -import net.minestom.server.item.ItemStack; -import net.minestom.server.network.packet.server.play.data.WorldPos; -import net.minestom.server.particle.Particle; +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.Registries; import net.minestom.server.utils.Direction; +import net.minestom.server.utils.Unit; +import net.minestom.server.utils.nbt.BinaryTagReader; +import net.minestom.server.utils.nbt.BinaryTagWriter; import net.minestom.server.utils.validate.Check; -import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jglrxavpok.hephaistos.nbt.NBT; -import org.jglrxavpok.hephaistos.nbt.NBTReader; -import org.jglrxavpok.hephaistos.nbt.NBTWriter; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -25,9 +22,10 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Supplier; -@ApiStatus.Experimental public final class NetworkBuffer { + public static final Type UNIT = new NetworkBufferTypeImpl.UnitType(); public static final Type BOOLEAN = new NetworkBufferTypeImpl.BooleanType(); public static final Type BYTE = new NetworkBufferTypeImpl.ByteType(); public static final Type SHORT = new NetworkBufferTypeImpl.ShortType(); @@ -40,38 +38,47 @@ public final class NetworkBuffer { public static final Type VAR_LONG = new NetworkBufferTypeImpl.VarLongType(); public static final Type RAW_BYTES = new NetworkBufferTypeImpl.RawBytesType(); public static final Type STRING = new NetworkBufferTypeImpl.StringType(); - public static final Type NBT = new NetworkBufferTypeImpl.NbtType(); + public static final Type NBT = new NetworkBufferTypeImpl.NbtType(); public static final Type BLOCK_POSITION = new NetworkBufferTypeImpl.BlockPositionType(); - public static final Type COMPONENT = new NetworkBufferTypeImpl.ComponentType(); + public static final Type COMPONENT = new ComponentNetworkBufferTypeImpl(); public static final Type JSON_COMPONENT = new NetworkBufferTypeImpl.JsonComponentType(); public static final Type UUID = new NetworkBufferTypeImpl.UUIDType(); - public static final Type ITEM = new NetworkBufferTypeImpl.ItemType(); public static final Type BYTE_ARRAY = new NetworkBufferTypeImpl.ByteArrayType(); public static final Type LONG_ARRAY = new NetworkBufferTypeImpl.LongArrayType(); public static final Type VAR_INT_ARRAY = new NetworkBufferTypeImpl.VarIntArrayType(); public static final Type VAR_LONG_ARRAY = new NetworkBufferTypeImpl.VarLongArrayType(); + public static @NotNull Type> RegistryKey(@NotNull Function> selector) { + return new NetworkBufferTypeImpl.RegistryTypeType<>(selector); + } + // METADATA - public static final Type BLOCK_STATE = new NetworkBufferTypeImpl.BlockStateType(); public static final Type VILLAGER_DATA = new NetworkBufferTypeImpl.VillagerDataType(); - public static final Type DEATH_LOCATION = new NetworkBufferTypeImpl.DeathLocationType(); public static final Type VECTOR3 = new NetworkBufferTypeImpl.Vector3Type(); public static final Type VECTOR3D = new NetworkBufferTypeImpl.Vector3DType(); public static final Type QUATERNION = new NetworkBufferTypeImpl.QuaternionType(); - public static final Type PARTICLE = new NetworkBufferTypeImpl.ParticleType(); - public static final Type OPT_CHAT = NetworkBufferTypeImpl.fromOptional(COMPONENT); - public static final Type OPT_BLOCK_POSITION = NetworkBufferTypeImpl.fromOptional(BLOCK_POSITION); - public static final Type OPT_UUID = NetworkBufferTypeImpl.fromOptional(UUID); + public static final Type<@Nullable Component> OPT_CHAT = Optional(COMPONENT); + public static final Type<@Nullable Point> OPT_BLOCK_POSITION = Optional(BLOCK_POSITION); + public static final Type<@Nullable UUID> OPT_UUID = Optional(UUID); + + public static final Type DIRECTION = new NetworkBufferTypeImpl.EnumType<>(Direction.class); + public static final Type POSE = new NetworkBufferTypeImpl.EnumType<>(Entity.Pose.class); + + // Combinators + + public static @NotNull Type<@Nullable T> Optional(@NotNull Type type) { + return new NetworkBufferTypeImpl.OptionalType<>(type); + } - public static final Type DIRECTION = NetworkBufferTypeImpl.fromEnum(Direction.class); - public static final Type POSE = NetworkBufferTypeImpl.fromEnum(Entity.Pose.class); + public static > @NotNull Type Enum(@NotNull Class enumClass) { + return new NetworkBufferTypeImpl.EnumType<>(enumClass); + } - public static final Type CAT_VARIANT = NetworkBufferTypeImpl.fromEnum(CatMeta.Variant.class); - public static final Type FROG_VARIANT = NetworkBufferTypeImpl.fromEnum(FrogMeta.Variant.class); - public static final Type PAINTING_VARIANT = NetworkBufferTypeImpl.fromEnum(PaintingMeta.Variant.class); - public static final Type SNIFFER_STATE = NetworkBufferTypeImpl.fromEnum(SnifferMeta.State.class); + public static @NotNull Type Lazy(@NotNull Supplier> supplier) { + return new NetworkBufferTypeImpl.LazyType<>(supplier); + } ByteBuffer nioBuffer; @@ -79,8 +86,11 @@ public final class NetworkBuffer { int writeIndex; int readIndex; - NBTWriter nbtWriter; - NBTReader nbtReader; + BinaryTagWriter nbtWriter; + BinaryTagReader nbtReader; + + // In the future, this should be passed as a parameter. + final Registries registries = MinecraftServer.process(); public NetworkBuffer(@NotNull ByteBuffer buffer, boolean resizable) { this.nioBuffer = buffer.order(ByteOrder.BIG_ENDIAN); @@ -181,6 +191,25 @@ public void writeCollection(@Nullable Collection<@NotNull T> values, return values; } + public @NotNull Map writeMap(@NotNull NetworkBuffer.Type keyType, @NotNull NetworkBuffer.Type valueType, @NotNull Map map) { + write(VAR_INT, map.size()); + for (Map.Entry entry : map.entrySet()) { + write(keyType, entry.getKey()); + write(valueType, entry.getValue()); + } + return map; + } + + public @NotNull Map readMap(@NotNull NetworkBuffer.Type keyType, @NotNull NetworkBuffer.Type valueType, int maxSize) { + final int size = read(VAR_INT); + Check.argCondition(size > maxSize, "Map size ({0}) is higher than the maximum allowed size ({1})", size, maxSize); + final Map map = new HashMap<>(size); + for (int i = 0; i < size; i++) { + map.put(read(keyType), read(valueType)); + } + return map; + } + public > void writeEnum(@NotNull Class enumClass, @NotNull E value) { write(VAR_INT, value.ordinal()); } @@ -291,8 +320,15 @@ void ensureSize(int length) { public interface Type { void write(@NotNull NetworkBuffer buffer, T value); - T read(@NotNull NetworkBuffer buffer); + + default @NotNull Type map(@NotNull Function to, @NotNull Function from) { + return new NetworkBufferTypeImpl.MappedType<>(this, to, from); + } + + default @NotNull Type> list(int maxSize) { + return new NetworkBufferTypeImpl.ListType<>(this, maxSize); + } } @FunctionalInterface @@ -300,6 +336,11 @@ public interface Writer { void write(@NotNull NetworkBuffer writer); } + @FunctionalInterface + public interface Reader { + @NotNull T read(@NotNull NetworkBuffer reader); + } + public static byte[] makeArray(@NotNull Consumer<@NotNull NetworkBuffer> writing) { NetworkBuffer writer = new NetworkBuffer(); writing.accept(writer); diff --git a/src/main/java/net/minestom/server/network/NetworkBufferTypeImpl.java b/src/main/java/net/minestom/server/network/NetworkBufferTypeImpl.java index fe6b4068fca..7e7822ff8bb 100644 --- a/src/main/java/net/minestom/server/network/NetworkBufferTypeImpl.java +++ b/src/main/java/net/minestom/server/network/NetworkBufferTypeImpl.java @@ -1,24 +1,27 @@ package net.minestom.server.network; +import net.kyori.adventure.nbt.BinaryTag; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import net.minestom.server.adventure.serializer.nbt.NbtComponentSerializer; import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Vec; -import net.minestom.server.item.ItemStack; -import net.minestom.server.item.Material; import net.minestom.server.network.packet.server.play.data.WorldPos; -import net.minestom.server.particle.Particle; -import net.minestom.server.particle.data.ParticleData; +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.Registries; +import net.minestom.server.utils.Unit; +import net.minestom.server.utils.nbt.BinaryTagReader; +import net.minestom.server.utils.nbt.BinaryTagWriter; import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.NotNull; -import org.jglrxavpok.hephaistos.nbt.*; +import org.jetbrains.annotations.Nullable; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.*; import java.nio.charset.StandardCharsets; +import java.util.List; import java.util.UUID; +import java.util.function.Function; +import java.util.function.Supplier; import static net.minestom.server.network.NetworkBuffer.*; @@ -26,6 +29,17 @@ interface NetworkBufferTypeImpl extends NetworkBuffer.Type { int SEGMENT_BITS = 0x7F; int CONTINUE_BIT = 0x80; + record UnitType() implements NetworkBufferTypeImpl { + @Override + public void write(@NotNull NetworkBuffer buffer, Unit value) { + } + + @Override + public Unit read(@NotNull NetworkBuffer buffer) { + return Unit.INSTANCE; + } + } + record BooleanType() implements NetworkBufferTypeImpl { @Override public void write(@NotNull NetworkBuffer buffer, Boolean value) { @@ -283,37 +297,31 @@ public String read(@NotNull NetworkBuffer buffer) { } } - record NbtType() implements NetworkBufferTypeImpl { + record NbtType() implements NetworkBufferTypeImpl { @Override - public void write(@NotNull NetworkBuffer buffer, org.jglrxavpok.hephaistos.nbt.NBT value) { - NBTWriter nbtWriter = buffer.nbtWriter; + public void write(@NotNull NetworkBuffer buffer, BinaryTag value) { + BinaryTagWriter nbtWriter = buffer.nbtWriter; if (nbtWriter == null) { - nbtWriter = new NBTWriter(new OutputStream() { + nbtWriter = new BinaryTagWriter(new DataOutputStream(new OutputStream() { @Override public void write(int b) { buffer.write(BYTE, (byte) b); } - }, CompressedProcesser.NONE); + })); buffer.nbtWriter = nbtWriter; } try { - if (value == NBTEnd.INSTANCE) { - // Kotlin - https://discord.com/channels/706185253441634317/706186227493109860/1163703658341478462 - buffer.write(BYTE, (byte) NBTType.TAG_End.getOrdinal()); - } else { - buffer.write(BYTE, (byte) value.getID().getOrdinal()); - nbtWriter.writeRaw(value); - } + nbtWriter.writeNameless(value); } catch (IOException e) { throw new RuntimeException(e); } } @Override - public org.jglrxavpok.hephaistos.nbt.NBT read(@NotNull NetworkBuffer buffer) { - NBTReader nbtReader = buffer.nbtReader; + public BinaryTag read(@NotNull NetworkBuffer buffer) { + BinaryTagReader nbtReader = buffer.nbtReader; if (nbtReader == null) { - nbtReader = new NBTReader(new InputStream() { + nbtReader = new BinaryTagReader(new DataInputStream(new InputStream() { @Override public int read() { return buffer.read(BYTE) & 0xFF; @@ -323,15 +331,12 @@ public int read() { public int available() { return buffer.readableBytes(); } - }, CompressedProcesser.NONE); + })); buffer.nbtReader = nbtReader; } try { - byte tagId = buffer.read(BYTE); - if (tagId == NBTType.TAG_End.getOrdinal()) - return NBTEnd.INSTANCE; - return nbtReader.readRaw(tagId); - } catch (IOException | NBTException e) { + return nbtReader.readNameless(); + } catch (IOException e) { throw new RuntimeException(e); } } @@ -359,20 +364,6 @@ public Point read(@NotNull NetworkBuffer buffer) { } } - record ComponentType() implements NetworkBufferTypeImpl { - @Override - public void write(@NotNull NetworkBuffer buffer, Component value) { - final NBT nbt = NbtComponentSerializer.nbt().serialize(value); - buffer.write(NBT, nbt); - } - - @Override - public Component read(@NotNull NetworkBuffer buffer) { - final NBT nbt = buffer.read(NBT); - return NbtComponentSerializer.nbt().deserialize(nbt); - } - } - record JsonComponentType() implements NetworkBufferTypeImpl { @Override public void write(@NotNull NetworkBuffer buffer, Component value) { @@ -402,39 +393,6 @@ public java.util.UUID read(@NotNull NetworkBuffer buffer) { } } - record ItemType() implements NetworkBufferTypeImpl { - @Override - public void write(@NotNull NetworkBuffer buffer, ItemStack value) { - if (value.isAir()) { - buffer.write(BOOLEAN, false); - return; - } - buffer.write(BOOLEAN, true); - buffer.write(VAR_INT, value.material().id()); - buffer.write(BYTE, (byte) value.amount()); - // Vanilla does not write an empty object, just an end tag. - NBTCompound nbt = value.meta().toNBT(); - buffer.write(NBT, nbt.isEmpty() ? NBTEnd.INSTANCE : nbt); - } - - @Override - public ItemStack read(@NotNull NetworkBuffer buffer) { - final boolean present = buffer.read(BOOLEAN); - if (!present) return ItemStack.AIR; - - final int id = buffer.read(VAR_INT); - final Material material = Material.fromId(id); - if (material == null) throw new RuntimeException("Unknown material id: " + id); - - final int amount = buffer.read(BYTE); - final NBT nbt = buffer.read(NBT); - if (!(nbt instanceof NBTCompound compound)) { - return ItemStack.of(material, amount); - } - return ItemStack.fromNBT(material, compound, amount); - } - } - record ByteArrayType() implements NetworkBufferTypeImpl { @Override public void write(@NotNull NetworkBuffer buffer, byte[] value) { @@ -597,46 +555,95 @@ public float[] read(@NotNull NetworkBuffer buffer) { } } - record ParticleType() implements NetworkBufferTypeImpl { + // Combinators + + record EnumType>(@NotNull Class enumClass) implements NetworkBufferTypeImpl { @Override - public void write(@NotNull NetworkBuffer buffer, Particle value) { - Check.stateCondition(value.data() != null && !value.data().validate(value.id()), "Particle data {0} is not valid for this particle type {1}", value.data(), value.namespace()); - Check.stateCondition(value.data() == null && ParticleData.requiresData(value.id()), "Particle data is required for this particle type {0}", value.namespace()); - buffer.write(VAR_INT, value.id()); - if (value.data() != null) value.data().write(buffer); + public void write(@NotNull NetworkBuffer buffer, E value) { + buffer.writeEnum(enumClass, value); } @Override - public Particle read(@NotNull NetworkBuffer buffer) { - throw new UnsupportedOperationException("Cannot read a particle from the network buffer"); + public E read(@NotNull NetworkBuffer buffer) { + return buffer.readEnum(enumClass); } } - static > NetworkBufferTypeImpl fromEnum(Class enumClass) { - return new NetworkBufferTypeImpl<>() { - @Override - public void write(@NotNull NetworkBuffer buffer, T value) { - buffer.writeEnum(enumClass, value); - } + record OptionalType(@NotNull Type parent) implements NetworkBufferTypeImpl<@Nullable T> { + @Override + public void write(@NotNull NetworkBuffer buffer, T value) { + buffer.writeOptional(parent, value); + } - @Override - public T read(@NotNull NetworkBuffer buffer) { - return buffer.readEnum(enumClass); - } - }; + @Override + public T read(@NotNull NetworkBuffer buffer) { + return buffer.readOptional(parent); + } } - static NetworkBufferTypeImpl fromOptional(Type optionalType) { - return new NetworkBufferTypeImpl<>() { - @Override - public void write(@NotNull NetworkBuffer buffer, T value) { - buffer.writeOptional(optionalType, value); - } + final class LazyType implements NetworkBufferTypeImpl { + private final @NotNull Supplier> supplier; + private Type type; - @Override - public T read(@NotNull NetworkBuffer buffer) { - return buffer.readOptional(optionalType); - } - }; + public LazyType(@NotNull Supplier> supplier) { + this.supplier = supplier; + } + + @Override + public void write(@NotNull NetworkBuffer buffer, T value) { + if (type == null) type = supplier.get(); + type.write(buffer, value); + } + + @Override + public T read(@NotNull NetworkBuffer buffer) { + if (type == null) type = supplier.get(); + return null; + } + } + + record MappedType(@NotNull Type parent, @NotNull Function to, @NotNull Function from) implements NetworkBufferTypeImpl { + @Override + public void write(@NotNull NetworkBuffer buffer, S value) { + parent.write(buffer, from.apply(value)); + } + + @Override + public S read(@NotNull NetworkBuffer buffer) { + return to.apply(parent.read(buffer)); + } + } + + record ListType(@NotNull Type parent, int maxSize) implements NetworkBufferTypeImpl> { + @Override + public void write(@NotNull NetworkBuffer buffer, List value) { + buffer.writeCollection(parent, value); + } + + @Override + public List read(@NotNull NetworkBuffer buffer) { + return buffer.readCollection(parent, maxSize); + } + } + + record RegistryTypeType(@NotNull Function> selector) implements NetworkBufferTypeImpl> { + @Override + public void write(@NotNull NetworkBuffer buffer, DynamicRegistry.Key value) { + Check.stateCondition(buffer.registries == null, "Buffer does not have registries"); + final DynamicRegistry registry = selector.apply(buffer.registries); + final int id = registry.getId(value); + Check.argCondition(id == -1, "Key is not registered: {0} > {1}", registry, value); + buffer.write(VAR_INT, id); + } + + @Override + public DynamicRegistry.Key read(@NotNull NetworkBuffer buffer) { + Check.stateCondition(buffer.registries == null, "Buffer does not have registries"); + DynamicRegistry registry = selector.apply(buffer.registries); + final int id = buffer.read(VAR_INT); + final DynamicRegistry.Key key = registry.getKey(id); + Check.argCondition(key == null, "No such ID in registry: {0} > {1}", registry, id); + return key; + } } } diff --git a/src/main/java/net/minestom/server/network/PacketProcessor.java b/src/main/java/net/minestom/server/network/PacketProcessor.java index fd8e82f7490..7cadc525683 100644 --- a/src/main/java/net/minestom/server/network/PacketProcessor.java +++ b/src/main/java/net/minestom/server/network/PacketProcessor.java @@ -51,17 +51,14 @@ public PacketProcessor(@NotNull PacketListenerManager packetListenerManager) { public ClientPacket process(@NotNull PlayerConnection connection, int packetId, ByteBuffer body) { final ClientPacket packet = create(connection.getConnectionState(), packetId, body); - - switch (connection.getConnectionState()) { - // Process all pre-config packets immediately - case HANDSHAKE, STATUS, LOGIN -> packetListenerManager.processClientPacket(packet, connection); - // Process config and play packets on the next tick - case CONFIGURATION, PLAY -> { - final Player player = connection.getPlayer(); - assert player != null; - player.addPacketToQueue(packet); - } + if (packet.processImmediately()) { + packetListenerManager.processClientPacket(packet, connection); + return packet; } + + final Player player = connection.getPlayer(); + assert player != null; + player.addPacketToQueue(packet); return packet; } } diff --git a/src/main/java/net/minestom/server/network/UuidProvider.java b/src/main/java/net/minestom/server/network/UuidProvider.java index 8f2da5f5762..7fe21c17e97 100644 --- a/src/main/java/net/minestom/server/network/UuidProvider.java +++ b/src/main/java/net/minestom/server/network/UuidProvider.java @@ -1,6 +1,7 @@ package net.minestom.server.network; import net.minestom.server.network.player.PlayerConnection; +import org.jetbrains.annotations.NotNull; import java.util.UUID; @@ -22,5 +23,5 @@ public interface UuidProvider { * @param username the username given by the connection * @return the new {@link UUID} for the player */ - UUID provide(PlayerConnection playerConnection, String username); + @NotNull UUID provide(@NotNull PlayerConnection playerConnection, @NotNull String username); } diff --git a/src/main/java/net/minestom/server/network/packet/client/ClientPacket.java b/src/main/java/net/minestom/server/network/packet/client/ClientPacket.java index be55d454e93..cd22192c814 100644 --- a/src/main/java/net/minestom/server/network/packet/client/ClientPacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/ClientPacket.java @@ -1,6 +1,7 @@ package net.minestom.server.network.packet.client; import net.minestom.server.network.NetworkBuffer; +import org.jetbrains.annotations.ApiStatus; /** * Represents a packet received from a client. @@ -8,4 +9,13 @@ * Packets are value-based, and should therefore not be reliant on identity. */ public interface ClientPacket extends NetworkBuffer.Writer { + /** + * Determines whether this packet should be processed immediately + * or wait until the next server tick. + * @return true if this packet should process immediately + */ + @ApiStatus.Internal + default boolean processImmediately() { + return false; + } } diff --git a/src/main/java/net/minestom/server/network/packet/client/ClientPacketsHandler.java b/src/main/java/net/minestom/server/network/packet/client/ClientPacketsHandler.java index 80ec4e5c101..91d739fa451 100644 --- a/src/main/java/net/minestom/server/network/packet/client/ClientPacketsHandler.java +++ b/src/main/java/net/minestom/server/network/packet/client/ClientPacketsHandler.java @@ -3,6 +3,7 @@ import net.minestom.server.network.NetworkBuffer; 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.login.ClientEncryptionResponsePacket; import net.minestom.server.network.packet.client.login.ClientLoginAcknowledgedPacket; import net.minestom.server.network.packet.client.login.ClientLoginPluginResponsePacket; @@ -13,28 +14,26 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.UnknownNullability; -import java.util.function.Function; - /** * Contains registered packets and a way to instantiate them. *

- * Packets are registered using {@link #register(int, Function)} and created using {@link #create(int, NetworkBuffer)}. + * Packets are registered using {@link #register(int, NetworkBuffer.Reader)} and created using {@link #create(int, NetworkBuffer)}. */ public sealed class ClientPacketsHandler permits ClientPacketsHandler.Status, ClientPacketsHandler.Login, ClientPacketsHandler.Configuration, ClientPacketsHandler.Play { - private final ObjectArray> suppliers = ObjectArray.singleThread(0x10); + private final ObjectArray> suppliers = ObjectArray.singleThread(0x10); private ClientPacketsHandler() { } - public void register(int id, @NotNull Function<@NotNull NetworkBuffer, @NotNull ClientPacket> packetSupplier) { + public void register(int id, @NotNull NetworkBuffer.Reader packetSupplier) { this.suppliers.set(id, packetSupplier); } public @UnknownNullability ClientPacket create(int packetId, @NotNull NetworkBuffer reader) { - final Function supplier = suppliers.get(packetId); + final NetworkBuffer.Reader supplier = suppliers.get(packetId); if (supplier == null) throw new IllegalStateException("Packet id 0x" + Integer.toHexString(packetId) + " isn't registered!"); - return supplier.apply(reader); + return supplier.read(reader); } public static final class Status extends ClientPacketsHandler { @@ -60,6 +59,7 @@ public Login() { register(nextId(), ClientEncryptionResponsePacket::new); register(nextId(), ClientLoginPluginResponsePacket::new); register(nextId(), ClientLoginAcknowledgedPacket::new); + register(nextId(), ClientCookieResponsePacket::new); } } @@ -71,11 +71,13 @@ private static int nextId() { public Configuration() { register(nextId(), ClientSettingsPacket::new); + register(nextId(), ClientCookieResponsePacket::new); register(nextId(), ClientPluginMessagePacket::new); register(nextId(), ClientFinishConfigurationPacket::new); register(nextId(), ClientKeepAlivePacket::new); register(nextId(), ClientPongPacket::new); register(nextId(), ClientResourcePackStatusPacket::new); + register(nextId(), ClientSelectKnownPacksPacket::new); } } @@ -93,6 +95,7 @@ public Play() { nextId(); // difficulty packet register(nextId(), ClientChatAckPacket::new); register(nextId(), ClientCommandChatPacket::new); + register(nextId(), ClientSignedCommandChatPacket::new); register(nextId(), ClientChatMessagePacket::new); register(nextId(), ClientChatSessionUpdatePacket::new); register(nextId(), ClientChunkBatchReceivedPacket::new); @@ -104,7 +107,9 @@ public Play() { register(nextId(), ClientClickWindowPacket::new); register(nextId(), ClientCloseWindowPacket::new); register(nextId(), ClientWindowSlotStatePacket::new); + register(nextId(), ClientCookieResponsePacket::new); register(nextId(), ClientPluginMessagePacket::new); + register(nextId(), ClientDebugSampleSubscriptionPacket::new); register(nextId(), ClientEditBookPacket::new); register(nextId(), ClientQueryEntityNbtPacket::new); register(nextId(), ClientInteractEntityPacket::new); diff --git a/src/main/java/net/minestom/server/network/packet/client/common/ClientCookieResponsePacket.java b/src/main/java/net/minestom/server/network/packet/client/common/ClientCookieResponsePacket.java new file mode 100644 index 00000000000..de07946ac48 --- /dev/null +++ b/src/main/java/net/minestom/server/network/packet/client/common/ClientCookieResponsePacket.java @@ -0,0 +1,40 @@ +package net.minestom.server.network.packet.client.common; + +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.network.packet.client.ClientPacket; +import net.minestom.server.network.packet.server.common.CookieStorePacket; +import net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public record ClientCookieResponsePacket( + @NotNull String key, + byte @Nullable [] value +) implements ClientPacket { + + public ClientCookieResponsePacket { + Check.argCondition(value != null && value.length > CookieStorePacket.MAX_VALUE_LENGTH, + "Value is too long: {0} > {1}", value != null ? value.length : 0, CookieStorePacket.MAX_VALUE_LENGTH); + } + + public ClientCookieResponsePacket(@NotNull NetworkBuffer reader) { + this(reader.read(NetworkBuffer.STRING), reader.readOptional(buffer -> { + int valueLength = buffer.read(NetworkBuffer.VAR_INT); + Check.argCondition(valueLength > CookieStorePacket.MAX_VALUE_LENGTH, + "Value is too long: {0} > {1}", valueLength, CookieStorePacket.MAX_VALUE_LENGTH); + return buffer.readBytes(valueLength); + })); + } + + @Override + public boolean processImmediately() { + return true; + } + + @Override + public void write(@NotNull NetworkBuffer writer) { + writer.write(NetworkBuffer.STRING, key); + writer.writeOptional(NetworkBuffer.BYTE_ARRAY, value); + } + +} diff --git a/src/main/java/net/minestom/server/network/packet/client/common/ClientKeepAlivePacket.java b/src/main/java/net/minestom/server/network/packet/client/common/ClientKeepAlivePacket.java index 01db1c1d544..15a2dabc5f0 100644 --- a/src/main/java/net/minestom/server/network/packet/client/common/ClientKeepAlivePacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/common/ClientKeepAlivePacket.java @@ -11,6 +11,11 @@ public ClientKeepAlivePacket(@NotNull NetworkBuffer reader) { this(reader.read(LONG)); } + @Override + public boolean processImmediately() { + return true; + } + @Override public void write(@NotNull NetworkBuffer writer) { writer.write(LONG, id); diff --git a/src/main/java/net/minestom/server/network/packet/client/common/ClientPingRequestPacket.java b/src/main/java/net/minestom/server/network/packet/client/common/ClientPingRequestPacket.java index 901fdd4b4ad..ce27af1051e 100644 --- a/src/main/java/net/minestom/server/network/packet/client/common/ClientPingRequestPacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/common/ClientPingRequestPacket.java @@ -11,6 +11,11 @@ public ClientPingRequestPacket(@NotNull NetworkBuffer reader) { this(reader.read(LONG)); } + @Override + public boolean processImmediately() { + return true; + } + @Override public void write(@NotNull NetworkBuffer writer) { writer.write(LONG, number); diff --git a/src/main/java/net/minestom/server/network/packet/client/configuration/ClientSelectKnownPacksPacket.java b/src/main/java/net/minestom/server/network/packet/client/configuration/ClientSelectKnownPacksPacket.java new file mode 100644 index 00000000000..6be68623a1d --- /dev/null +++ b/src/main/java/net/minestom/server/network/packet/client/configuration/ClientSelectKnownPacksPacket.java @@ -0,0 +1,30 @@ +package net.minestom.server.network.packet.client.configuration; + +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.network.packet.client.ClientPacket; +import net.minestom.server.network.packet.server.configuration.SelectKnownPacksPacket; +import net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public record ClientSelectKnownPacksPacket( + @NotNull List entries +) implements ClientPacket { + private static final int MAX_ENTRIES = 64; + + public ClientSelectKnownPacksPacket { + Check.argCondition(entries.size() > MAX_ENTRIES, "Too many known packs: {0} > {1}", entries.size(), MAX_ENTRIES); + entries = List.copyOf(entries); + } + + public ClientSelectKnownPacksPacket(@NotNull NetworkBuffer reader) { + this(reader.readCollection(SelectKnownPacksPacket.Entry::new, MAX_ENTRIES)); + } + + @Override + public void write(@NotNull NetworkBuffer writer) { + writer.writeCollection(entries); + } + +} diff --git a/src/main/java/net/minestom/server/network/packet/client/handshake/ClientHandshakePacket.java b/src/main/java/net/minestom/server/network/packet/client/handshake/ClientHandshakePacket.java index bd1432190f8..f25e01d9802 100644 --- a/src/main/java/net/minestom/server/network/packet/client/handshake/ClientHandshakePacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/handshake/ClientHandshakePacket.java @@ -8,7 +8,7 @@ import static net.minestom.server.network.NetworkBuffer.*; public record ClientHandshakePacket(int protocolVersion, @NotNull String serverAddress, - int serverPort, int intent) implements ClientPacket { + int serverPort, @NotNull Intent intent) implements ClientPacket { public ClientHandshakePacket { if (serverAddress.length() > getMaxHandshakeLength()) { @@ -18,7 +18,14 @@ public record ClientHandshakePacket(int protocolVersion, @NotNull String serverA public ClientHandshakePacket(@NotNull NetworkBuffer reader) { this(reader.read(VAR_INT), reader.read(STRING), - reader.read(UNSIGNED_SHORT), reader.read(VAR_INT)); + reader.read(UNSIGNED_SHORT), + // Not a readEnum call because the indices are not 0-based + Intent.fromId(reader.read(VAR_INT))); + } + + @Override + public boolean processImmediately() { + return true; } @Override @@ -30,7 +37,8 @@ public void write(@NotNull NetworkBuffer writer) { } writer.write(STRING, serverAddress); writer.write(UNSIGNED_SHORT, serverPort); - writer.write(VAR_INT, intent); + // Not a writeEnum call because the indices are not 0-based + writer.write(VAR_INT, intent.id()); } private static int getMaxHandshakeLength() { @@ -38,4 +46,23 @@ private static int getMaxHandshakeLength() { return BungeeCordProxy.isEnabled() ? (BungeeCordProxy.isBungeeGuardEnabled() ? 2500 : Short.MAX_VALUE) : 255; } + public enum Intent { + STATUS, + LOGIN, + TRANSFER; + + public static @NotNull Intent fromId(int id) { + return switch (id) { + case 1 -> STATUS; + case 2 -> LOGIN; + case 3 -> TRANSFER; + default -> throw new IllegalArgumentException("Unknown connection intent: " + id); + }; + } + + public int id() { + return ordinal() + 1; + } + } + } diff --git a/src/main/java/net/minestom/server/network/packet/client/login/ClientEncryptionResponsePacket.java b/src/main/java/net/minestom/server/network/packet/client/login/ClientEncryptionResponsePacket.java index 6b993541e96..4ea7c74796a 100644 --- a/src/main/java/net/minestom/server/network/packet/client/login/ClientEncryptionResponsePacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/login/ClientEncryptionResponsePacket.java @@ -13,6 +13,11 @@ public ClientEncryptionResponsePacket(@NotNull NetworkBuffer reader) { this(reader.read(BYTE_ARRAY), reader.read(BYTE_ARRAY)); } + @Override + public boolean processImmediately() { + return true; + } + @Override public void write(@NotNull NetworkBuffer writer) { writer.write(BYTE_ARRAY, sharedSecret); diff --git a/src/main/java/net/minestom/server/network/packet/client/login/ClientLoginAcknowledgedPacket.java b/src/main/java/net/minestom/server/network/packet/client/login/ClientLoginAcknowledgedPacket.java index cb6127cf6da..9830a10a5cc 100644 --- a/src/main/java/net/minestom/server/network/packet/client/login/ClientLoginAcknowledgedPacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/login/ClientLoginAcknowledgedPacket.java @@ -10,6 +10,11 @@ public ClientLoginAcknowledgedPacket(@NotNull NetworkBuffer buffer) { this(); } + @Override + public boolean processImmediately() { + return true; + } + @Override public void write(@NotNull NetworkBuffer writer) { } diff --git a/src/main/java/net/minestom/server/network/packet/client/login/ClientLoginPluginResponsePacket.java b/src/main/java/net/minestom/server/network/packet/client/login/ClientLoginPluginResponsePacket.java index e3c74056a62..b11b6293069 100644 --- a/src/main/java/net/minestom/server/network/packet/client/login/ClientLoginPluginResponsePacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/login/ClientLoginPluginResponsePacket.java @@ -13,6 +13,11 @@ public ClientLoginPluginResponsePacket(@NotNull NetworkBuffer reader) { this(reader.read(VAR_INT), reader.readOptional(RAW_BYTES)); } + @Override + public boolean processImmediately() { + return true; + } + @Override public void write(@NotNull NetworkBuffer writer) { writer.write(VAR_INT, messageId); diff --git a/src/main/java/net/minestom/server/network/packet/client/login/ClientLoginStartPacket.java b/src/main/java/net/minestom/server/network/packet/client/login/ClientLoginStartPacket.java index ddc9b659535..b4ba4adc50c 100644 --- a/src/main/java/net/minestom/server/network/packet/client/login/ClientLoginStartPacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/login/ClientLoginStartPacket.java @@ -17,6 +17,11 @@ public ClientLoginStartPacket(@NotNull NetworkBuffer reader) { this(reader.read(STRING), reader.read(UUID)); } + @Override + public boolean processImmediately() { + return true; + } + @Override public void write(@NotNull NetworkBuffer writer) { if (username.length() > 16) diff --git a/src/main/java/net/minestom/server/network/packet/client/play/ClientClickWindowPacket.java b/src/main/java/net/minestom/server/network/packet/client/play/ClientClickWindowPacket.java index 1e1e64c3395..595bfc27e89 100644 --- a/src/main/java/net/minestom/server/network/packet/client/play/ClientClickWindowPacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/play/ClientClickWindowPacket.java @@ -22,7 +22,7 @@ public record ClientClickWindowPacket(byte windowId, int stateId, public ClientClickWindowPacket(@NotNull NetworkBuffer reader) { this(reader.read(BYTE), reader.read(VAR_INT), reader.read(SHORT), reader.read(BYTE), reader.readEnum(ClickType.class), - reader.readCollection(ChangedSlot::new, MAX_CHANGED_SLOTS), reader.read(ITEM)); + reader.readCollection(ChangedSlot::new, MAX_CHANGED_SLOTS), reader.read(ItemStack.NETWORK_TYPE)); } @Override @@ -33,18 +33,18 @@ public void write(@NotNull NetworkBuffer writer) { writer.write(BYTE, button); writer.write(VAR_INT, clickType.ordinal()); writer.writeCollection(changedSlots); - writer.write(ITEM, clickedItem); + writer.write(ItemStack.NETWORK_TYPE, clickedItem); } public record ChangedSlot(short slot, @NotNull ItemStack item) implements NetworkBuffer.Writer { public ChangedSlot(@NotNull NetworkBuffer reader) { - this(reader.read(SHORT), reader.read(ITEM)); + this(reader.read(SHORT), reader.read(ItemStack.NETWORK_TYPE)); } @Override public void write(@NotNull NetworkBuffer writer) { writer.write(SHORT, slot); - writer.write(ITEM, item); + writer.write(ItemStack.NETWORK_TYPE, item); } } diff --git a/src/main/java/net/minestom/server/network/packet/client/play/ClientCommandChatPacket.java b/src/main/java/net/minestom/server/network/packet/client/play/ClientCommandChatPacket.java index ef29ff5467d..66b302b3ecd 100644 --- a/src/main/java/net/minestom/server/network/packet/client/play/ClientCommandChatPacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/play/ClientCommandChatPacket.java @@ -1,35 +1,23 @@ package net.minestom.server.network.packet.client.play; -import net.minestom.server.crypto.ArgumentSignatures; -import net.minestom.server.crypto.LastSeenMessages; import net.minestom.server.network.NetworkBuffer; import net.minestom.server.network.packet.client.ClientPacket; +import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.NotNull; -import static net.minestom.server.network.NetworkBuffer.LONG; import static net.minestom.server.network.NetworkBuffer.STRING; -public record ClientCommandChatPacket(@NotNull String message, long timestamp, - long salt, @NotNull ArgumentSignatures signatures, - LastSeenMessages.@NotNull Update lastSeenMessages) implements ClientPacket { +public record ClientCommandChatPacket(@NotNull String message) implements ClientPacket { public ClientCommandChatPacket { - if (message.length() > 256) { - throw new IllegalArgumentException("Message length cannot be greater than 256"); - } + Check.argCondition(message.length() > 256, "Message length cannot be greater than 256"); } public ClientCommandChatPacket(@NotNull NetworkBuffer reader) { - this(reader.read(STRING), reader.read(LONG), - reader.read(LONG), new ArgumentSignatures(reader), - new LastSeenMessages.Update(reader)); + this(reader.read(STRING)); } @Override public void write(@NotNull NetworkBuffer writer) { writer.write(STRING, message); - writer.write(LONG, timestamp); - writer.write(LONG, salt); - writer.write(signatures); - writer.write(lastSeenMessages); } } diff --git a/src/main/java/net/minestom/server/network/packet/client/play/ClientCreativeInventoryActionPacket.java b/src/main/java/net/minestom/server/network/packet/client/play/ClientCreativeInventoryActionPacket.java index f1c382d6ffa..7940291119c 100644 --- a/src/main/java/net/minestom/server/network/packet/client/play/ClientCreativeInventoryActionPacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/play/ClientCreativeInventoryActionPacket.java @@ -5,17 +5,16 @@ import net.minestom.server.network.packet.client.ClientPacket; import org.jetbrains.annotations.NotNull; -import static net.minestom.server.network.NetworkBuffer.ITEM; import static net.minestom.server.network.NetworkBuffer.SHORT; public record ClientCreativeInventoryActionPacket(short slot, @NotNull ItemStack item) implements ClientPacket { public ClientCreativeInventoryActionPacket(@NotNull NetworkBuffer reader) { - this(reader.read(SHORT), reader.read(ITEM)); + this(reader.read(SHORT), reader.read(ItemStack.NETWORK_TYPE)); } @Override public void write(@NotNull NetworkBuffer writer) { writer.write(SHORT, slot); - writer.write(ITEM, item); + writer.write(ItemStack.NETWORK_TYPE, item); } } diff --git a/src/main/java/net/minestom/server/network/packet/client/play/ClientDebugSampleSubscriptionPacket.java b/src/main/java/net/minestom/server/network/packet/client/play/ClientDebugSampleSubscriptionPacket.java new file mode 100644 index 00000000000..2fd20890740 --- /dev/null +++ b/src/main/java/net/minestom/server/network/packet/client/play/ClientDebugSampleSubscriptionPacket.java @@ -0,0 +1,18 @@ +package net.minestom.server.network.packet.client.play; + +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.network.packet.client.ClientPacket; +import net.minestom.server.network.packet.server.play.DebugSamplePacket; +import org.jetbrains.annotations.NotNull; + +public record ClientDebugSampleSubscriptionPacket(@NotNull DebugSamplePacket.Type type) implements ClientPacket { + + public ClientDebugSampleSubscriptionPacket(@NotNull NetworkBuffer reader) { + this(reader.readEnum(DebugSamplePacket.Type.class)); + } + + @Override + public void write(@NotNull NetworkBuffer writer) { + writer.writeEnum(DebugSamplePacket.Type.class, type); + } +} diff --git a/src/main/java/net/minestom/server/network/packet/client/play/ClientSignedCommandChatPacket.java b/src/main/java/net/minestom/server/network/packet/client/play/ClientSignedCommandChatPacket.java new file mode 100644 index 00000000000..3693b80e8e5 --- /dev/null +++ b/src/main/java/net/minestom/server/network/packet/client/play/ClientSignedCommandChatPacket.java @@ -0,0 +1,34 @@ +package net.minestom.server.network.packet.client.play; + +import net.minestom.server.crypto.ArgumentSignatures; +import net.minestom.server.crypto.LastSeenMessages; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.network.packet.client.ClientPacket; +import net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.NotNull; + +import static net.minestom.server.network.NetworkBuffer.LONG; +import static net.minestom.server.network.NetworkBuffer.STRING; + +public record ClientSignedCommandChatPacket(@NotNull String message, long timestamp, + long salt, @NotNull ArgumentSignatures signatures, + LastSeenMessages.@NotNull Update lastSeenMessages) implements ClientPacket { + public ClientSignedCommandChatPacket { + Check.argCondition(message.length() > 256, "Message length cannot be greater than 256"); + } + + public ClientSignedCommandChatPacket(@NotNull NetworkBuffer reader) { + this(reader.read(STRING), reader.read(LONG), + reader.read(LONG), new ArgumentSignatures(reader), + new LastSeenMessages.Update(reader)); + } + + @Override + public void write(@NotNull NetworkBuffer writer) { + writer.write(STRING, message); + writer.write(LONG, timestamp); + writer.write(LONG, salt); + writer.write(signatures); + writer.write(lastSeenMessages); + } +} diff --git a/src/main/java/net/minestom/server/network/packet/client/play/ClientUseItemPacket.java b/src/main/java/net/minestom/server/network/packet/client/play/ClientUseItemPacket.java index 178cc2b5024..6d6fffdf7cb 100644 --- a/src/main/java/net/minestom/server/network/packet/client/play/ClientUseItemPacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/play/ClientUseItemPacket.java @@ -7,14 +7,17 @@ import static net.minestom.server.network.NetworkBuffer.VAR_INT; -public record ClientUseItemPacket(@NotNull Player.Hand hand, int sequence) implements ClientPacket { +public record ClientUseItemPacket(@NotNull Player.Hand hand, int sequence, float yaw, float pitch) implements ClientPacket { public ClientUseItemPacket(@NotNull NetworkBuffer reader) { - this(reader.readEnum(Player.Hand.class), reader.read(VAR_INT)); + this(reader.readEnum(Player.Hand.class), reader.read(VAR_INT), + reader.read(NetworkBuffer.FLOAT), reader.read(NetworkBuffer.FLOAT)); } @Override public void write(@NotNull NetworkBuffer writer) { writer.writeEnum(Player.Hand.class, hand); writer.write(VAR_INT, sequence); + writer.write(NetworkBuffer.FLOAT, yaw); + writer.write(NetworkBuffer.FLOAT, pitch); } } diff --git a/src/main/java/net/minestom/server/network/packet/client/status/StatusRequestPacket.java b/src/main/java/net/minestom/server/network/packet/client/status/StatusRequestPacket.java index 72fcb2a518a..ed380987679 100644 --- a/src/main/java/net/minestom/server/network/packet/client/status/StatusRequestPacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/status/StatusRequestPacket.java @@ -9,6 +9,11 @@ public StatusRequestPacket(@NotNull NetworkBuffer reader) { this(); } + @Override + public boolean processImmediately() { + return true; + } + @Override public void write(@NotNull NetworkBuffer writer) { // Empty diff --git a/src/main/java/net/minestom/server/network/packet/server/ServerPacketIdentifier.java b/src/main/java/net/minestom/server/network/packet/server/ServerPacketIdentifier.java index c6afcdc8775..0d48a9c3f20 100644 --- a/src/main/java/net/minestom/server/network/packet/server/ServerPacketIdentifier.java +++ b/src/main/java/net/minestom/server/network/packet/server/ServerPacketIdentifier.java @@ -3,27 +3,38 @@ import java.util.concurrent.atomic.AtomicInteger; public final class ServerPacketIdentifier { + private static final AtomicInteger STATUS_ID = new AtomicInteger(0); + private static final AtomicInteger LOGIN_ID = new AtomicInteger(0); + private static final AtomicInteger CONFIGURATION_ID = new AtomicInteger(0); private static final AtomicInteger PLAY_ID = new AtomicInteger(0); - public static final int STATUS_RESPONSE = 0x00; - public static final int STATUS_PING_RESPONSE = 0x01; + public static final int STATUS_RESPONSE = nextStatusId(); + public static final int STATUS_PING_RESPONSE = nextStatusId(); - public static final int LOGIN_DISCONNECT = 0x00; - public static final int LOGIN_ENCRYPTION_REQUEST = 0x01; - public static final int LOGIN_SUCCESS = 0x02; - public static final int LOGIN_SET_COMPRESSION = 0x03; - public static final int LOGIN_PLUGIN_REQUEST = 0x04; + public static final int LOGIN_DISCONNECT = nextLoginId(); + public static final int LOGIN_ENCRYPTION_REQUEST = nextLoginId(); + public static final int LOGIN_SUCCESS = nextLoginId(); + public static final int LOGIN_SET_COMPRESSION = nextLoginId(); + public static final int LOGIN_PLUGIN_REQUEST = nextLoginId(); + public static final int LOGIN_COOKIE_REQUEST = nextLoginId(); - public static final int CONFIGURATION_PLUGIN_MESSAGE = 0x00; - public static final int CONFIGURATION_DISCONNECT = 0x01; - public static final int CONFIGURATION_FINISH_CONFIGURATION = 0x02; - public static final int CONFIGURATION_KEEP_ALIVE = 0x03; - public static final int CONFIGURATION_PING = 0x04; - public static final int CONFIGURATION_REGISTRY_DATA = 0x05; - public static final int CONFIGURATION_RESOURCE_PACK_POP_PACKET = 0x06; - public static final int CONFIGURATION_RESOURCE_PACK_PUSH_PACKET = 0x07; - public static final int CONFIGURATION_UPDATE_ENABLED_FEATURES = 0x08; - public static final int CONFIGURATION_TAGS = 0x09; + public static final int CONFIGURATION_COOKIE_REQUEST = nextConfigurationId(); + public static final int CONFIGURATION_PLUGIN_MESSAGE = nextConfigurationId(); + public static final int CONFIGURATION_DISCONNECT = nextConfigurationId(); + public static final int CONFIGURATION_FINISH_CONFIGURATION = nextConfigurationId(); + public static final int CONFIGURATION_KEEP_ALIVE = nextConfigurationId(); + public static final int CONFIGURATION_PING = nextConfigurationId(); + public static final int CONFIGURATION_RESET_CHAT = nextConfigurationId(); + public static final int CONFIGURATION_REGISTRY_DATA = nextConfigurationId(); + public static final int CONFIGURATION_RESOURCE_PACK_POP = nextConfigurationId(); + public static final int CONFIGURATION_RESOURCE_PACK_PUSH = nextConfigurationId(); + public static final int CONFIGURATION_COOKIE_STORE = nextConfigurationId(); + public static final int CONFIGURATION_TRANSFER = nextConfigurationId(); + public static final int CONFIGURATION_UPDATE_ENABLED_FEATURES = nextConfigurationId(); + public static final int CONFIGURATION_TAGS = nextConfigurationId(); + public static final int CONFIGURATION_SELECT_KNOWN_PACKS = nextConfigurationId(); + public static final int CONFIGURATION_CUSTOM_REPORT_DETAILS = nextConfigurationId(); + public static final int CONFIGURATION_SERVER_LINKS = nextConfigurationId(); public static final int BUNDLE = nextPlayId(); public static final int SPAWN_ENTITY = nextPlayId(); @@ -47,10 +58,12 @@ public final class ServerPacketIdentifier { public static final int WINDOW_ITEMS = nextPlayId(); public static final int WINDOW_PROPERTY = nextPlayId(); public static final int SET_SLOT = nextPlayId(); + public static final int COOKIE_REQUEST = nextPlayId(); public static final int SET_COOLDOWN = nextPlayId(); public static final int CUSTOM_CHAT_COMPLETIONS = nextPlayId(); public static final int PLUGIN_MESSAGE = nextPlayId(); public static final int DAMAGE_EVENT = nextPlayId(); + public static final int DEBUG_SAMPLE = nextPlayId(); public static final int DELETE_CHAT_MESSAGE = nextPlayId(); public static final int DISCONNECT = nextPlayId(); public static final int DISGUISED_CHAT = nextPlayId(); @@ -130,6 +143,7 @@ public final class ServerPacketIdentifier { public static final int SOUND_EFFECT = nextPlayId(); public static final int START_CONFIGURATION_PACKET = nextPlayId(); public static final int STOP_SOUND = nextPlayId(); + public static final int COOKIE_STORE = nextPlayId(); public static final int SYSTEM_CHAT = nextPlayId(); public static final int PLAYER_LIST_HEADER_AND_FOOTER = nextPlayId(); public static final int NBT_QUERY_RESPONSE = nextPlayId(); @@ -137,11 +151,27 @@ public final class ServerPacketIdentifier { public static final int ENTITY_TELEPORT = nextPlayId(); public static final int TICK_STATE = nextPlayId(); public static final int TICK_STEP = nextPlayId(); + public static final int TRANSFER = nextPlayId(); public static final int ADVANCEMENTS = nextPlayId(); - public static final int ENTITY_PROPERTIES = nextPlayId(); + public static final int ENTITY_ATTRIBUTES = nextPlayId(); public static final int ENTITY_EFFECT = nextPlayId(); public static final int DECLARE_RECIPES = nextPlayId(); public static final int TAGS = nextPlayId(); + public static final int PROJECTILE_POWER = nextPlayId(); + public static final int CUSTOM_REPORT_DETAILS = nextPlayId(); + public static final int SERVER_LINKS = nextPlayId(); + + private static int nextStatusId() { + return STATUS_ID.getAndIncrement(); + } + + private static int nextLoginId() { + return LOGIN_ID.getAndIncrement(); + } + + private static int nextConfigurationId() { + return CONFIGURATION_ID.getAndIncrement(); + } private static int nextPlayId() { return PLAY_ID.getAndIncrement(); diff --git a/src/main/java/net/minestom/server/network/packet/server/common/CookieRequestPacket.java b/src/main/java/net/minestom/server/network/packet/server/common/CookieRequestPacket.java new file mode 100644 index 00000000000..853c11b39da --- /dev/null +++ b/src/main/java/net/minestom/server/network/packet/server/common/CookieRequestPacket.java @@ -0,0 +1,34 @@ +package net.minestom.server.network.packet.server.common; + +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.network.packet.server.ServerPacket; +import net.minestom.server.network.packet.server.ServerPacketIdentifier; +import org.jetbrains.annotations.NotNull; + +public record CookieRequestPacket(@NotNull String key) implements + ServerPacket.Login, ServerPacket.Configuration, ServerPacket.Play { + + public CookieRequestPacket(@NotNull NetworkBuffer reader) { + this(reader.read(NetworkBuffer.STRING)); + } + + @Override + public void write(@NotNull NetworkBuffer writer) { + writer.write(NetworkBuffer.STRING, key); + } + + @Override + public int configurationId() { + return ServerPacketIdentifier.CONFIGURATION_COOKIE_REQUEST; + } + + @Override + public int loginId() { + return ServerPacketIdentifier.LOGIN_COOKIE_REQUEST; + } + + @Override + public int playId() { + return ServerPacketIdentifier.COOKIE_REQUEST; + } +} diff --git a/src/main/java/net/minestom/server/network/packet/server/common/CookieStorePacket.java b/src/main/java/net/minestom/server/network/packet/server/common/CookieStorePacket.java new file mode 100644 index 00000000000..42775547f86 --- /dev/null +++ b/src/main/java/net/minestom/server/network/packet/server/common/CookieStorePacket.java @@ -0,0 +1,54 @@ +package net.minestom.server.network.packet.server.common; + +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.network.packet.server.ServerPacket; +import net.minestom.server.network.packet.server.ServerPacketIdentifier; +import net.minestom.server.utils.NamespaceID; +import net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.NotNull; + +public record CookieStorePacket( + @NotNull String key, byte[] value +) implements ServerPacket.Configuration, ServerPacket.Play { + public static final int MAX_VALUE_LENGTH = 5120; + + public CookieStorePacket { + Check.argCondition(value.length > MAX_VALUE_LENGTH, "Cookie value length too long: {0} > {1}", value.length, MAX_VALUE_LENGTH); + } + + public CookieStorePacket(@NotNull NamespaceID key, byte[] value) { + this(key.asString(), value); + } + + public CookieStorePacket(@NotNull NetworkBuffer reader) { + this(read(reader)); + } + + private CookieStorePacket(@NotNull CookieStorePacket other) { + this(other.key, other.value); + } + + private static @NotNull CookieStorePacket read(@NotNull NetworkBuffer reader) { + String key = reader.read(NetworkBuffer.STRING); + int valueLength = reader.read(NetworkBuffer.VAR_INT); + Check.argCondition(valueLength > MAX_VALUE_LENGTH, "Cookie value length too long: {0} > {1}", valueLength, MAX_VALUE_LENGTH); + byte[] value = reader.readBytes(valueLength); + return new CookieStorePacket(key, value); + } + + @Override + public void write(@NotNull NetworkBuffer writer) { + writer.write(NetworkBuffer.STRING, key); + writer.write(NetworkBuffer.BYTE_ARRAY, value); + } + + @Override + public int configurationId() { + return ServerPacketIdentifier.CONFIGURATION_COOKIE_STORE; + } + + @Override + public int playId() { + return ServerPacketIdentifier.COOKIE_STORE; + } +} diff --git a/src/main/java/net/minestom/server/network/packet/server/common/CustomReportDetailsPacket.java b/src/main/java/net/minestom/server/network/packet/server/common/CustomReportDetailsPacket.java new file mode 100644 index 00000000000..54863728685 --- /dev/null +++ b/src/main/java/net/minestom/server/network/packet/server/common/CustomReportDetailsPacket.java @@ -0,0 +1,37 @@ +package net.minestom.server.network.packet.server.common; + +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.network.packet.server.ServerPacket; +import net.minestom.server.network.packet.server.ServerPacketIdentifier; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; + +public record CustomReportDetailsPacket( + @NotNull Map details +) implements ServerPacket.Configuration, ServerPacket.Play { + private static final int MAX_DETAILS = 32; + + public CustomReportDetailsPacket { + details = Map.copyOf(details); + } + + public CustomReportDetailsPacket(@NotNull NetworkBuffer reader) { + this(reader.readMap(NetworkBuffer.STRING, NetworkBuffer.STRING, MAX_DETAILS)); + } + + @Override + public void write(@NotNull NetworkBuffer writer) { + writer.writeMap(NetworkBuffer.STRING, NetworkBuffer.STRING, details); + } + + @Override + public int configurationId() { + return ServerPacketIdentifier.CONFIGURATION_CUSTOM_REPORT_DETAILS; + } + + @Override + public int playId() { + return ServerPacketIdentifier.CUSTOM_REPORT_DETAILS; + } +} diff --git a/src/main/java/net/minestom/server/network/packet/server/common/ResourcePackPopPacket.java b/src/main/java/net/minestom/server/network/packet/server/common/ResourcePackPopPacket.java index a6774a3d753..82adfd11315 100644 --- a/src/main/java/net/minestom/server/network/packet/server/common/ResourcePackPopPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/common/ResourcePackPopPacket.java @@ -20,7 +20,7 @@ public void write(@NotNull NetworkBuffer writer) { @Override public int configurationId() { - return ServerPacketIdentifier.CONFIGURATION_RESOURCE_PACK_POP_PACKET; + return ServerPacketIdentifier.CONFIGURATION_RESOURCE_PACK_POP; } @Override diff --git a/src/main/java/net/minestom/server/network/packet/server/common/ResourcePackPushPacket.java b/src/main/java/net/minestom/server/network/packet/server/common/ResourcePackPushPacket.java index a2b2f5e80fa..b585c215275 100644 --- a/src/main/java/net/minestom/server/network/packet/server/common/ResourcePackPushPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/common/ResourcePackPushPacket.java @@ -42,7 +42,7 @@ public void write(@NotNull NetworkBuffer writer) { @Override public int configurationId() { - return ServerPacketIdentifier.CONFIGURATION_RESOURCE_PACK_PUSH_PACKET; + return ServerPacketIdentifier.CONFIGURATION_RESOURCE_PACK_PUSH; } @Override diff --git a/src/main/java/net/minestom/server/network/packet/server/common/ServerLinksPacket.java b/src/main/java/net/minestom/server/network/packet/server/common/ServerLinksPacket.java new file mode 100644 index 00000000000..3c98bc06d80 --- /dev/null +++ b/src/main/java/net/minestom/server/network/packet/server/common/ServerLinksPacket.java @@ -0,0 +1,97 @@ +package net.minestom.server.network.packet.server.common; + +import net.kyori.adventure.text.Component; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.network.packet.server.ServerPacket; +import net.minestom.server.network.packet.server.ServerPacketIdentifier; +import net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public record ServerLinksPacket(@NotNull List entries) implements ServerPacket.Configuration, ServerPacket.Play { + private static final int MAX_ENTRIES = 100; + + public ServerLinksPacket { + entries = List.copyOf(entries); + } + + public ServerLinksPacket(@NotNull Entry... entries) { + this(List.of(entries)); + } + + public ServerLinksPacket(@NotNull NetworkBuffer reader) { + this(reader.read(Entry.LIST_NETWORK_TYPE)); + } + + @Override + public void write(@NotNull NetworkBuffer writer) { + writer.write(Entry.LIST_NETWORK_TYPE, entries); + } + + @Override + public int configurationId() { + return ServerPacketIdentifier.CONFIGURATION_SERVER_LINKS; + } + + @Override + public int playId() { + return ServerPacketIdentifier.SERVER_LINKS; + } + + public record Entry(@Nullable KnownLinkType knownType, @Nullable Component customType, @NotNull String link) { + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, Entry value) { + buffer.write(NetworkBuffer.BOOLEAN, value.knownType != null); + if (value.knownType != null) { + buffer.write(KnownLinkType.NETWORK_TYPE, value.knownType); + } else { + assert value.customType != null; + buffer.write(NetworkBuffer.COMPONENT, value.customType); + } + buffer.write(NetworkBuffer.STRING, value.link); + } + + @Override + public Entry read(@NotNull NetworkBuffer buffer) { + boolean known = buffer.read(NetworkBuffer.BOOLEAN); + if (known) { + return new Entry(buffer.read(KnownLinkType.NETWORK_TYPE), buffer.read(NetworkBuffer.STRING)); + } else { + return new Entry(buffer.read(NetworkBuffer.COMPONENT), buffer.read(NetworkBuffer.STRING)); + } + } + }; + public static final NetworkBuffer.Type> LIST_NETWORK_TYPE = NETWORK_TYPE.list(MAX_ENTRIES); + + public Entry { + Check.argCondition(knownType == null && customType == null, "One of knownType and customType must be present"); + Check.argCondition(knownType != null && customType != null, "Only one of knownType and customType may be present"); + } + + public Entry(@NotNull KnownLinkType type, @NotNull String link) { + this(type, null, link); + } + + public Entry(@NotNull Component type, @NotNull String link) { + this(null, type, link); + } + } + + public enum KnownLinkType { + BUG_REPORT, + COMMUNITY_GUIDELINES, + SUPPORT, + STATUS, + FEEDBACK, + COMMUNITY, + WEBSITE, + FORUMS, + NEWS, + ANNOUNCEMENTS; + + public static final NetworkBuffer.Type NETWORK_TYPE = NetworkBuffer.Enum(KnownLinkType.class); + } +} diff --git a/src/main/java/net/minestom/server/network/packet/server/common/TagsPacket.java b/src/main/java/net/minestom/server/network/packet/server/common/TagsPacket.java index daa06217cad..ec24448d05e 100644 --- a/src/main/java/net/minestom/server/network/packet/server/common/TagsPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/common/TagsPacket.java @@ -35,9 +35,13 @@ public void write(@NotNull NetworkBuffer writer) { final var type = entry.getKey(); final var tags = entry.getValue(); writer.write(STRING, type.getIdentifier()); + if (type.getFunction() == null) { + writer.write(VAR_INT, 0); + continue; + } writer.write(VAR_INT, tags.size()); for (var tag : tags) { - writer.write(STRING, tag.getName().asString()); + writer.write(STRING, tag.name()); final var values = tag.getValues(); writer.write(VAR_INT, values.size()); for (var name : values) { diff --git a/src/main/java/net/minestom/server/network/packet/server/common/TransferPacket.java b/src/main/java/net/minestom/server/network/packet/server/common/TransferPacket.java new file mode 100644 index 00000000000..968e6b725e9 --- /dev/null +++ b/src/main/java/net/minestom/server/network/packet/server/common/TransferPacket.java @@ -0,0 +1,32 @@ +package net.minestom.server.network.packet.server.common; + +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.network.packet.server.ServerPacket; +import net.minestom.server.network.packet.server.ServerPacketIdentifier; +import org.jetbrains.annotations.NotNull; + +public record TransferPacket( + @NotNull String host, + int port +) implements ServerPacket.Configuration, ServerPacket.Play { + + public TransferPacket(@NotNull NetworkBuffer reader) { + this(reader.read(NetworkBuffer.STRING), reader.read(NetworkBuffer.VAR_INT)); + } + + @Override + public void write(@NotNull NetworkBuffer writer) { + writer.write(NetworkBuffer.STRING, host); + writer.write(NetworkBuffer.VAR_INT, port); + } + + @Override + public int configurationId() { + return ServerPacketIdentifier.CONFIGURATION_TRANSFER; + } + + @Override + public int playId() { + return ServerPacketIdentifier.TRANSFER; + } +} diff --git a/src/main/java/net/minestom/server/network/packet/server/configuration/RegistryDataPacket.java b/src/main/java/net/minestom/server/network/packet/server/configuration/RegistryDataPacket.java index 2b0d4d2caf9..cf0fc3eacce 100644 --- a/src/main/java/net/minestom/server/network/packet/server/configuration/RegistryDataPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/configuration/RegistryDataPacket.java @@ -1,25 +1,50 @@ package net.minestom.server.network.packet.server.configuration; +import net.kyori.adventure.nbt.CompoundBinaryTag; import net.minestom.server.network.NetworkBuffer; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import org.jetbrains.annotations.NotNull; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; +import org.jetbrains.annotations.Nullable; + +import java.util.List; import static net.minestom.server.network.NetworkBuffer.NBT; +import static net.minestom.server.network.NetworkBuffer.STRING; + +public record RegistryDataPacket( + @NotNull String registryId, + @NotNull List entries +) implements ServerPacket.Configuration { -public record RegistryDataPacket(@NotNull NBTCompound data) implements ServerPacket.Configuration { public RegistryDataPacket(@NotNull NetworkBuffer buffer) { - this((NBTCompound) buffer.read(NBT)); + this(buffer.read(STRING), buffer.readCollection(Entry::new, Integer.MAX_VALUE)); } @Override public void write(@NotNull NetworkBuffer writer) { - writer.write(NBT, data); + writer.write(STRING, registryId); + writer.writeCollection(entries); } @Override public int configurationId() { return ServerPacketIdentifier.CONFIGURATION_REGISTRY_DATA; } + + public record Entry( + @NotNull String id, + @Nullable CompoundBinaryTag data + ) implements NetworkBuffer.Writer { + + public Entry(@NotNull NetworkBuffer reader) { + this(reader.read(STRING), (CompoundBinaryTag) reader.readOptional(NBT)); + } + + @Override + public void write(@NotNull NetworkBuffer writer) { + writer.write(STRING, id); + writer.writeOptional(NBT, data); + } + } } diff --git a/src/main/java/net/minestom/server/network/packet/server/configuration/ResetChatPacket.java b/src/main/java/net/minestom/server/network/packet/server/configuration/ResetChatPacket.java new file mode 100644 index 00000000000..f460192f005 --- /dev/null +++ b/src/main/java/net/minestom/server/network/packet/server/configuration/ResetChatPacket.java @@ -0,0 +1,23 @@ +package net.minestom.server.network.packet.server.configuration; + +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.network.packet.server.ServerPacket; +import net.minestom.server.network.packet.server.ServerPacketIdentifier; +import org.jetbrains.annotations.NotNull; + +public record ResetChatPacket() implements ServerPacket.Configuration { + + public ResetChatPacket(@NotNull NetworkBuffer reader) { + this(); // No fields + } + + @Override + public void write(@NotNull NetworkBuffer writer) { + // No fields + } + + @Override + public int configurationId() { + return ServerPacketIdentifier.CONFIGURATION_RESET_CHAT; + } +} diff --git a/src/main/java/net/minestom/server/network/packet/server/configuration/SelectKnownPacksPacket.java b/src/main/java/net/minestom/server/network/packet/server/configuration/SelectKnownPacksPacket.java new file mode 100644 index 00000000000..4f07a5f32a5 --- /dev/null +++ b/src/main/java/net/minestom/server/network/packet/server/configuration/SelectKnownPacksPacket.java @@ -0,0 +1,56 @@ +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; +import net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public record SelectKnownPacksPacket( + @NotNull List entries +) 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); + entries = List.copyOf(entries); + } + + public SelectKnownPacksPacket(@NotNull NetworkBuffer reader) { + this(reader.readCollection(Entry::new, MAX_ENTRIES)); + } + + @Override + public void write(@NotNull NetworkBuffer writer) { + writer.writeCollection(entries); + } + + @Override + public int configurationId() { + return ServerPacketIdentifier.CONFIGURATION_SELECT_KNOWN_PACKS; + } + + public record Entry( + @NotNull String namespace, + @NotNull String id, + @NotNull String version + ) implements NetworkBuffer.Writer { + public Entry(@NotNull NetworkBuffer reader) { + this(reader.read(NetworkBuffer.STRING), + reader.read(NetworkBuffer.STRING), + reader.read(NetworkBuffer.STRING)); + } + + @Override + public void write(@NotNull NetworkBuffer writer) { + writer.write(NetworkBuffer.STRING, namespace); + writer.write(NetworkBuffer.STRING, id); + writer.write(NetworkBuffer.STRING, version); + } + } +} diff --git a/src/main/java/net/minestom/server/network/packet/server/configuration/UpdateEnabledFeaturesPacket.java b/src/main/java/net/minestom/server/network/packet/server/configuration/UpdateEnabledFeaturesPacket.java index 226b347525f..d2cc2c52594 100644 --- a/src/main/java/net/minestom/server/network/packet/server/configuration/UpdateEnabledFeaturesPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/configuration/UpdateEnabledFeaturesPacket.java @@ -1,20 +1,20 @@ package net.minestom.server.network.packet.server.configuration; -import net.minestom.server.featureflag.FeatureFlag; import net.minestom.server.network.NetworkBuffer; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; +import net.minestom.server.utils.NamespaceID; import org.jetbrains.annotations.NotNull; import java.util.Set; import static net.minestom.server.network.NetworkBuffer.STRING; -public record UpdateEnabledFeaturesPacket(@NotNull Set features) implements ServerPacket.Configuration { +public record UpdateEnabledFeaturesPacket(@NotNull Set features) implements ServerPacket.Configuration { public static final int MAX_FEATURES = 64; public UpdateEnabledFeaturesPacket(@NotNull NetworkBuffer buffer) { - this(Set.copyOf(buffer.readCollection((b) -> FeatureFlag.fromNamespaceId(b.read(STRING)), MAX_FEATURES))); + this(Set.copyOf(buffer.readCollection((b) -> NamespaceID.from(b.read(STRING)), MAX_FEATURES))); } @Override diff --git a/src/main/java/net/minestom/server/network/packet/server/login/EncryptionRequestPacket.java b/src/main/java/net/minestom/server/network/packet/server/login/EncryptionRequestPacket.java index 3a52c731fba..99d0dd22f15 100644 --- a/src/main/java/net/minestom/server/network/packet/server/login/EncryptionRequestPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/login/EncryptionRequestPacket.java @@ -5,16 +5,20 @@ import net.minestom.server.network.packet.server.ServerPacketIdentifier; import org.jetbrains.annotations.NotNull; -import static net.minestom.server.network.NetworkBuffer.BYTE_ARRAY; -import static net.minestom.server.network.NetworkBuffer.STRING; +import static net.minestom.server.network.NetworkBuffer.*; + +public record EncryptionRequestPacket( + @NotNull String serverId, + byte @NotNull [] publicKey, + byte @NotNull [] verifyToken, + boolean shouldAuthenticate +) implements ServerPacket.Login { -public record EncryptionRequestPacket(@NotNull String serverId, - byte @NotNull [] publicKey, - byte @NotNull [] verifyToken) implements ServerPacket.Login { public EncryptionRequestPacket(@NotNull NetworkBuffer reader) { this(reader.read(STRING), reader.read(BYTE_ARRAY), - reader.read(BYTE_ARRAY)); + reader.read(BYTE_ARRAY), + reader.read(BOOLEAN)); } @Override @@ -22,6 +26,7 @@ public void write(@NotNull NetworkBuffer writer) { writer.write(STRING, serverId); writer.write(BYTE_ARRAY, publicKey); writer.write(BYTE_ARRAY, verifyToken); + writer.write(BOOLEAN, shouldAuthenticate); } @Override diff --git a/src/main/java/net/minestom/server/network/packet/server/login/LoginSuccessPacket.java b/src/main/java/net/minestom/server/network/packet/server/login/LoginSuccessPacket.java index d369c750790..6b645b830f5 100644 --- a/src/main/java/net/minestom/server/network/packet/server/login/LoginSuccessPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/login/LoginSuccessPacket.java @@ -7,13 +7,12 @@ import java.util.UUID; -import static net.minestom.server.network.NetworkBuffer.STRING; -import static net.minestom.server.network.NetworkBuffer.VAR_INT; +import static net.minestom.server.network.NetworkBuffer.*; public record LoginSuccessPacket(@NotNull UUID uuid, @NotNull String username, - int properties) implements ServerPacket.Login { + int properties, boolean strictErrorHandling) implements ServerPacket.Login { public LoginSuccessPacket(@NotNull NetworkBuffer reader) { - this(reader.read(NetworkBuffer.UUID), reader.read(STRING), reader.read(VAR_INT)); + this(reader.read(NetworkBuffer.UUID), reader.read(STRING), reader.read(VAR_INT), reader.read(BOOLEAN)); } @Override @@ -21,6 +20,7 @@ public void write(@NotNull NetworkBuffer writer) { writer.write(NetworkBuffer.UUID, uuid); writer.write(STRING, username); writer.write(VAR_INT, properties); + writer.write(BOOLEAN, strictErrorHandling); } @Override diff --git a/src/main/java/net/minestom/server/network/packet/server/play/AdvancementsPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/AdvancementsPacket.java index e8b4870ae9d..1461344f4f0 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/AdvancementsPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/AdvancementsPacket.java @@ -5,7 +5,6 @@ import net.minestom.server.adventure.ComponentHolder; import net.minestom.server.item.ItemStack; import net.minestom.server.network.NetworkBuffer; -import net.minestom.server.network.packet.server.ServerPacket.ComponentHolding; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import org.jetbrains.annotations.NotNull; @@ -164,7 +163,7 @@ private DisplayData(DisplayData displayData) { private static DisplayData read(@NotNull NetworkBuffer reader) { var title = reader.read(COMPONENT); var description = reader.read(COMPONENT); - var icon = reader.read(ITEM); + var icon = reader.read(ItemStack.NETWORK_TYPE); var frameType = FrameType.values()[reader.read(VAR_INT)]; var flags = reader.read(INT); var backgroundTexture = (flags & 0x1) != 0 ? reader.read(STRING) : null; @@ -180,7 +179,7 @@ private static DisplayData read(@NotNull NetworkBuffer reader) { public void write(@NotNull NetworkBuffer writer) { writer.write(COMPONENT, title); writer.write(COMPONENT, description); - writer.write(ITEM, icon); + writer.write(ItemStack.NETWORK_TYPE, icon); writer.writeEnum(FrameType.class, frameType); writer.write(INT, flags); if ((flags & 0x1) != 0) { diff --git a/src/main/java/net/minestom/server/network/packet/server/play/BlockEntityDataPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/BlockEntityDataPacket.java index f878bfbebeb..e990226a7d7 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/BlockEntityDataPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/BlockEntityDataPacket.java @@ -1,19 +1,19 @@ package net.minestom.server.network.packet.server.play; +import net.kyori.adventure.nbt.CompoundBinaryTag; import net.minestom.server.coordinate.Point; import net.minestom.server.network.NetworkBuffer; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; import static net.minestom.server.network.NetworkBuffer.*; public record BlockEntityDataPacket(@NotNull Point blockPosition, int action, - @Nullable NBTCompound data) implements ServerPacket.Play { + @Nullable CompoundBinaryTag data) implements ServerPacket.Play { public BlockEntityDataPacket(@NotNull NetworkBuffer reader) { - this(reader.read(BLOCK_POSITION), reader.read(VAR_INT), (NBTCompound) reader.read(NBT)); + this(reader.read(BLOCK_POSITION), reader.read(VAR_INT), (CompoundBinaryTag) reader.read(NBT)); } @Override diff --git a/src/main/java/net/minestom/server/network/packet/server/play/DebugSamplePacket.java b/src/main/java/net/minestom/server/network/packet/server/play/DebugSamplePacket.java new file mode 100644 index 00000000000..4d70d018565 --- /dev/null +++ b/src/main/java/net/minestom/server/network/packet/server/play/DebugSamplePacket.java @@ -0,0 +1,29 @@ +package net.minestom.server.network.packet.server.play; + +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.network.packet.server.ServerPacket; +import net.minestom.server.network.packet.server.ServerPacketIdentifier; +import org.jetbrains.annotations.NotNull; + +public record DebugSamplePacket(long @NotNull [] sample, @NotNull Type type) implements ServerPacket.Play { + + public DebugSamplePacket(@NotNull NetworkBuffer buffer) { + this(buffer.read(NetworkBuffer.LONG_ARRAY), buffer.readEnum(Type.class)); + } + + @Override + public void write(@NotNull NetworkBuffer writer) { + writer.write(NetworkBuffer.LONG_ARRAY, sample); + writer.writeEnum(Type.class, type); + } + + @Override + public int playId() { + return ServerPacketIdentifier.DEBUG_SAMPLE; + } + + public enum Type { + TICK_TIME + } + +} diff --git a/src/main/java/net/minestom/server/network/packet/server/play/DeclareRecipesPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/DeclareRecipesPacket.java index d239800dff5..8a06a66001e 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/DeclareRecipesPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/DeclareRecipesPacket.java @@ -5,6 +5,7 @@ import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.recipe.RecipeCategory; +import net.minestom.server.recipe.RecipeType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -23,17 +24,18 @@ public record DeclareRecipesPacket(@NotNull List recipes) implem public DeclareRecipesPacket(@NotNull NetworkBuffer reader) { this(reader.readCollection(r -> { - final String type = r.read(STRING); + final String recipeId = r.read(STRING); + final RecipeType type = r.read(RecipeType.NETWORK_TYPE); return switch (type) { - case "crafting_shapeless" -> new DeclaredShapelessCraftingRecipe(reader); - case "crafting_shaped" -> new DeclaredShapedCraftingRecipe(reader); - case "smelting" -> new DeclaredSmeltingRecipe(reader); - case "blasting" -> new DeclaredBlastingRecipe(reader); - case "smoking" -> new DeclaredSmokingRecipe(reader); - case "campfire_cooking" -> new DeclaredCampfireCookingRecipe(reader); - case "stonecutting" -> new DeclaredStonecutterRecipe(reader); - case "smithing_trim" -> new DeclaredSmithingTrimRecipe(reader); - case "smithing_transform" -> new DeclaredSmithingTransformRecipe(reader); + case RecipeType.SHAPELESS -> new DeclaredShapelessCraftingRecipe(recipeId, reader); + case RecipeType.SHAPED -> new DeclaredShapedCraftingRecipe(recipeId, reader); + case RecipeType.SMELTING -> new DeclaredSmeltingRecipe(recipeId, reader); + case RecipeType.BLASTING -> new DeclaredBlastingRecipe(recipeId, reader); + case RecipeType.SMOKING -> new DeclaredSmokingRecipe(recipeId, reader); + case RecipeType.CAMPFIRE_COOKING -> new DeclaredCampfireCookingRecipe(recipeId, reader); + case RecipeType.STONECUTTING -> new DeclaredStonecutterRecipe(recipeId, reader); + case RecipeType.SMITHING_TRIM -> new DeclaredSmithingTrimRecipe(recipeId, reader); + case RecipeType.SMITHING_TRANSFORM -> new DeclaredSmithingTransformRecipe(recipeId, reader); default -> throw new UnsupportedOperationException("Unrecognized type: " + type); }; }, MAX_RECIPES)); @@ -42,8 +44,8 @@ public DeclareRecipesPacket(@NotNull NetworkBuffer reader) { @Override public void write(@NotNull NetworkBuffer writer) { writer.writeCollection(recipes, (bWriter, recipe) -> { - bWriter.write(STRING, recipe.type()); bWriter.write(STRING, recipe.recipeId()); + bWriter.write(RecipeType.NETWORK_TYPE, recipe.type()); bWriter.write(recipe); }); } @@ -58,7 +60,7 @@ public sealed interface DeclaredRecipe extends NetworkBuffer.Writer DeclaredSmeltingRecipe, DeclaredBlastingRecipe, DeclaredSmokingRecipe, DeclaredCampfireCookingRecipe, DeclaredStonecutterRecipe, DeclaredSmithingTrimRecipe, DeclaredSmithingTransformRecipe { - @NotNull String type(); + @NotNull RecipeType type(); @NotNull String recipeId(); } @@ -67,10 +69,10 @@ public record DeclaredShapelessCraftingRecipe(@NotNull String recipeId, @NotNull @NotNull RecipeCategory.Crafting crafting, @NotNull List ingredients, @NotNull ItemStack result) implements DeclaredRecipe { - private DeclaredShapelessCraftingRecipe(@NotNull NetworkBuffer reader) { - this(reader.read(STRING), reader.read(STRING), + private DeclaredShapelessCraftingRecipe(@NotNull String recipeId, @NotNull NetworkBuffer reader) { + this(recipeId, reader.read(STRING), reader.readEnum(RecipeCategory.Crafting.class), - reader.readCollection(Ingredient::new, MAX_INGREDIENTS), reader.read(ITEM)); + reader.readCollection(Ingredient::new, MAX_INGREDIENTS), reader.read(ItemStack.STRICT_NETWORK_TYPE)); } @Override @@ -78,12 +80,12 @@ public void write(@NotNull NetworkBuffer writer) { writer.write(STRING, group); writer.writeEnum(RecipeCategory.Crafting.class, crafting); writer.writeCollection(ingredients); - writer.write(ITEM, result); + writer.write(ItemStack.STRICT_NETWORK_TYPE, result); } @Override - public @NotNull String type() { - return "crafting_shapeless"; + public @NotNull RecipeType type() { + return RecipeType.SHAPELESS; } } @@ -99,13 +101,11 @@ private DeclaredShapedCraftingRecipe(DeclaredShapedCraftingRecipe packet) { this(packet.recipeId, packet.group, packet.category, packet.width, packet.height, packet.ingredients, packet.result, packet.showNotification); } - public DeclaredShapedCraftingRecipe(@NotNull NetworkBuffer reader) { - this(read(reader)); + public DeclaredShapedCraftingRecipe(@NotNull String recipeId, @NotNull NetworkBuffer reader) { + this(read(recipeId, reader)); } - private static DeclaredShapedCraftingRecipe read(@NotNull NetworkBuffer reader) { - - String recipeId = reader.read(STRING); + private static DeclaredShapedCraftingRecipe read(@NotNull String recipeId, @NotNull NetworkBuffer reader) { String group = reader.read(STRING); RecipeCategory.Crafting category = reader.readEnum(RecipeCategory.Crafting.class); int width = reader.read(VAR_INT); @@ -114,7 +114,7 @@ private static DeclaredShapedCraftingRecipe read(@NotNull NetworkBuffer reader) for (int slot = 0; slot < width * height; slot++) { ingredients.add(new Ingredient(reader)); } - ItemStack result = reader.read(ITEM); + ItemStack result = reader.read(ItemStack.STRICT_NETWORK_TYPE); boolean showNotification = reader.read(BOOLEAN); return new DeclaredShapedCraftingRecipe(recipeId, group, category, width, height, ingredients, result, showNotification); } @@ -128,13 +128,13 @@ public void write(@NotNull NetworkBuffer writer) { for (Ingredient ingredient : ingredients) { ingredient.write(writer); } - writer.write(ITEM, result); + writer.write(ItemStack.STRICT_NETWORK_TYPE, result); writer.write(BOOLEAN, showNotification); } @Override - public @NotNull String type() { - return "crafting_shaped"; + public @NotNull RecipeType type() { + return RecipeType.SHAPED; } } @@ -142,10 +142,10 @@ public record DeclaredSmeltingRecipe(@NotNull String recipeId, @NotNull String g @NotNull RecipeCategory.Cooking category, @NotNull Ingredient ingredient, @NotNull ItemStack result, float experience, int cookingTime) implements DeclaredRecipe { - public DeclaredSmeltingRecipe(@NotNull NetworkBuffer reader) { - this(reader.read(STRING), reader.read(STRING), + public DeclaredSmeltingRecipe(@NotNull String recipeId, @NotNull NetworkBuffer reader) { + this(recipeId, reader.read(STRING), reader.readEnum(RecipeCategory.Cooking.class), - new Ingredient(reader), reader.read(ITEM), + new Ingredient(reader), reader.read(ItemStack.STRICT_NETWORK_TYPE), reader.read(FLOAT), reader.read(VAR_INT)); } @@ -154,14 +154,14 @@ public void write(@NotNull NetworkBuffer writer) { writer.write(STRING, group); writer.writeEnum(RecipeCategory.Cooking.class, category); writer.write(ingredient); - writer.write(ITEM, result); + writer.write(ItemStack.STRICT_NETWORK_TYPE, result); writer.write(FLOAT, experience); writer.write(VAR_INT, cookingTime); } @Override - public @NotNull String type() { - return "smelting"; + public @NotNull RecipeType type() { + return RecipeType.SMELTING; } } @@ -169,10 +169,10 @@ public record DeclaredBlastingRecipe(@NotNull String recipeId, @NotNull String g @NotNull RecipeCategory.Cooking category, @NotNull Ingredient ingredient, @NotNull ItemStack result, float experience, int cookingTime) implements DeclaredRecipe { - public DeclaredBlastingRecipe(@NotNull NetworkBuffer reader) { - this(reader.read(STRING), reader.read(STRING), + public DeclaredBlastingRecipe(@NotNull String recipeId, @NotNull NetworkBuffer reader) { + this(recipeId, reader.read(STRING), reader.readEnum(RecipeCategory.Cooking.class), - new Ingredient(reader), reader.read(ITEM), + new Ingredient(reader), reader.read(ItemStack.STRICT_NETWORK_TYPE), reader.read(FLOAT), reader.read(VAR_INT)); } @@ -181,14 +181,14 @@ public void write(@NotNull NetworkBuffer writer) { writer.write(STRING, group); writer.writeEnum(RecipeCategory.Cooking.class, category); writer.write(ingredient); - writer.write(ITEM, result); + writer.write(ItemStack.STRICT_NETWORK_TYPE, result); writer.write(FLOAT, experience); writer.write(VAR_INT, cookingTime); } @Override - public @NotNull String type() { - return "blasting"; + public @NotNull RecipeType type() { + return RecipeType.BLASTING; } } @@ -196,10 +196,10 @@ public record DeclaredSmokingRecipe(@NotNull String recipeId, @NotNull String gr @NotNull RecipeCategory.Cooking category, @NotNull Ingredient ingredient, @NotNull ItemStack result, float experience, int cookingTime) implements DeclaredRecipe { - public DeclaredSmokingRecipe(@NotNull NetworkBuffer reader) { - this(reader.read(STRING), reader.read(STRING), + public DeclaredSmokingRecipe(@NotNull String recipeId, @NotNull NetworkBuffer reader) { + this(recipeId, reader.read(STRING), reader.readEnum(RecipeCategory.Cooking.class), - new Ingredient(reader), reader.read(ITEM), + new Ingredient(reader), reader.read(ItemStack.STRICT_NETWORK_TYPE), reader.read(FLOAT), reader.read(VAR_INT)); } @@ -208,14 +208,14 @@ public void write(@NotNull NetworkBuffer writer) { writer.write(STRING, group); writer.writeEnum(RecipeCategory.Cooking.class, category); writer.write(ingredient); - writer.write(ITEM, result); + writer.write(ItemStack.STRICT_NETWORK_TYPE, result); writer.write(FLOAT, experience); writer.write(VAR_INT, cookingTime); } @Override - public @NotNull String type() { - return "smoking"; + public @NotNull RecipeType type() { + return RecipeType.SMOKING; } } @@ -223,10 +223,10 @@ public record DeclaredCampfireCookingRecipe(@NotNull String recipeId, @NotNull S @NotNull RecipeCategory.Cooking category, @NotNull Ingredient ingredient, @NotNull ItemStack result, float experience, int cookingTime) implements DeclaredRecipe { - public DeclaredCampfireCookingRecipe(@NotNull NetworkBuffer reader) { - this(reader.read(STRING), reader.read(STRING), + public DeclaredCampfireCookingRecipe(@NotNull String recipeId, @NotNull NetworkBuffer reader) { + this(recipeId, reader.read(STRING), reader.readEnum(RecipeCategory.Cooking.class), - new Ingredient(reader), reader.read(ITEM), + new Ingredient(reader), reader.read(ItemStack.STRICT_NETWORK_TYPE), reader.read(FLOAT), reader.read(VAR_INT)); } @@ -235,42 +235,42 @@ public void write(@NotNull NetworkBuffer writer) { writer.write(STRING, group); writer.writeEnum(RecipeCategory.Cooking.class, category); writer.write(ingredient); - writer.write(ITEM, result); + writer.write(ItemStack.STRICT_NETWORK_TYPE, result); writer.write(FLOAT, experience); writer.write(VAR_INT, cookingTime); } @Override - public @NotNull String type() { - return "campfire_cooking"; + public @NotNull RecipeType type() { + return RecipeType.CAMPFIRE_COOKING; } } public record DeclaredStonecutterRecipe(String recipeId, String group, Ingredient ingredient, ItemStack result) implements DeclaredRecipe { - public DeclaredStonecutterRecipe(@NotNull NetworkBuffer reader) { - this(reader.read(STRING), reader.read(STRING), - new Ingredient(reader), reader.read(ITEM)); + public DeclaredStonecutterRecipe(@NotNull String recipeId, @NotNull NetworkBuffer reader) { + this(recipeId, reader.read(STRING), + new Ingredient(reader), reader.read(ItemStack.STRICT_NETWORK_TYPE)); } @Override public void write(@NotNull NetworkBuffer writer) { writer.write(STRING, group); writer.write(ingredient); - writer.write(ITEM, result); + writer.write(ItemStack.STRICT_NETWORK_TYPE, result); } @Override - public @NotNull String type() { - return "stonecutting"; + public @NotNull RecipeType type() { + return RecipeType.STONECUTTING; } } public record DeclaredSmithingTransformRecipe(String recipeId, Ingredient template, Ingredient base, Ingredient addition, ItemStack result) implements DeclaredRecipe { - public DeclaredSmithingTransformRecipe(@NotNull NetworkBuffer reader) { - this(reader.read(STRING), new Ingredient(reader), new Ingredient(reader), new Ingredient(reader), reader.read(ITEM)); + public DeclaredSmithingTransformRecipe(@NotNull String recipeId, @NotNull NetworkBuffer reader) { + this(recipeId, new Ingredient(reader), new Ingredient(reader), new Ingredient(reader), reader.read(ItemStack.STRICT_NETWORK_TYPE)); } @Override @@ -278,19 +278,19 @@ public void write(@NotNull NetworkBuffer writer) { writer.write(template); writer.write(base); writer.write(addition); - writer.write(ITEM, result); + writer.write(ItemStack.STRICT_NETWORK_TYPE, result); } @Override - public @NotNull String type() { - return "smithing_transform"; + public @NotNull RecipeType type() { + return RecipeType.SMITHING_TRANSFORM; } } public record DeclaredSmithingTrimRecipe(String recipeId, Ingredient template, Ingredient base, Ingredient addition) implements DeclaredRecipe { - public DeclaredSmithingTrimRecipe(@NotNull NetworkBuffer reader) { - this(reader.read(STRING), new Ingredient(reader), new Ingredient(reader), new Ingredient(reader)); + public DeclaredSmithingTrimRecipe(@NotNull String recipeId, @NotNull NetworkBuffer reader) { + this(recipeId, new Ingredient(reader), new Ingredient(reader), new Ingredient(reader)); } @Override @@ -301,8 +301,8 @@ public void write(@NotNull NetworkBuffer writer) { } @Override - public @NotNull String type() { - return "smithing_trim"; + public @NotNull RecipeType type() { + return RecipeType.SMITHING_TRIM; } } @@ -312,11 +312,11 @@ public record Ingredient(@Nullable List items) implements NetworkBuff } public Ingredient(@NotNull NetworkBuffer reader) { - this(reader.readCollection(ITEM, MAX_INGREDIENTS)); + this(reader.readCollection(ItemStack.STRICT_NETWORK_TYPE, MAX_INGREDIENTS)); } public void write(@NotNull NetworkBuffer writer) { - writer.writeCollection(ITEM, items); + writer.writeCollection(ItemStack.STRICT_NETWORK_TYPE, items); } } } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/EntityAttributesPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/EntityAttributesPacket.java new file mode 100644 index 00000000000..f08afb3e90d --- /dev/null +++ b/src/main/java/net/minestom/server/network/packet/server/play/EntityAttributesPacket.java @@ -0,0 +1,58 @@ +package net.minestom.server.network.packet.server.play; + +import net.minestom.server.entity.attribute.Attribute; +import net.minestom.server.entity.attribute.AttributeModifier; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.network.packet.server.ServerPacket; +import net.minestom.server.network.packet.server.ServerPacketIdentifier; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.List; + +import static net.minestom.server.network.NetworkBuffer.DOUBLE; +import static net.minestom.server.network.NetworkBuffer.VAR_INT; + +public record EntityAttributesPacket(int entityId, List properties) implements ServerPacket.Play { + public static final int MAX_ENTRIES = 1024; + + public EntityAttributesPacket { + properties = List.copyOf(properties); + } + + public EntityAttributesPacket(@NotNull NetworkBuffer reader) { + this(reader.read(VAR_INT), reader.readCollection(Property.NETWORK_TYPE, MAX_ENTRIES)); + } + + @Override + public void write(@NotNull NetworkBuffer writer) { + writer.write(VAR_INT, entityId); + writer.writeCollection(Property.NETWORK_TYPE, properties); + } + + @Override + public int playId() { + return ServerPacketIdentifier.ENTITY_ATTRIBUTES; + } + + + public record Property(Attribute attribute, double value, Collection modifiers) { + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, Property value) { + buffer.write(Attribute.NETWORK_TYPE, value.attribute); + buffer.write(DOUBLE, value.value); + buffer.writeCollection(AttributeModifier.NETWORK_TYPE, value.modifiers); + } + + @Override + public Property read(@NotNull NetworkBuffer buffer) { + return new Property(buffer.read(Attribute.NETWORK_TYPE), buffer.read(DOUBLE), buffer.readCollection(AttributeModifier.NETWORK_TYPE, Short.MAX_VALUE)); + } + }; + + public Property { + modifiers = List.copyOf(modifiers); + } + } +} diff --git a/src/main/java/net/minestom/server/network/packet/server/play/EntityEffectPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/EntityEffectPacket.java index 0e0b02bf5e0..3024a943e9b 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/EntityEffectPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/EntityEffectPacket.java @@ -5,23 +5,18 @@ import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.potion.Potion; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; -import static net.minestom.server.network.NetworkBuffer.*; +import static net.minestom.server.network.NetworkBuffer.VAR_INT; -public record EntityEffectPacket(int entityId, @NotNull Potion potion, - @Nullable NBTCompound factorCodec) implements ServerPacket.Play { +public record EntityEffectPacket(int entityId, @NotNull Potion potion) implements ServerPacket.Play { public EntityEffectPacket(@NotNull NetworkBuffer reader) { - this(reader.read(VAR_INT), new Potion(reader), - reader.read(BOOLEAN) ? (NBTCompound) reader.read(NBT) : null); + this(reader.read(VAR_INT), new Potion(reader)); } @Override public void write(@NotNull NetworkBuffer writer) { writer.write(VAR_INT, entityId); writer.write(potion); - writer.writeOptional(NBT, factorCodec); } @Override diff --git a/src/main/java/net/minestom/server/network/packet/server/play/EntityEquipmentPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/EntityEquipmentPacket.java index 9715976bf51..a46d13d77ae 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/EntityEquipmentPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/EntityEquipmentPacket.java @@ -2,9 +2,9 @@ import net.kyori.adventure.text.Component; import net.minestom.server.entity.EquipmentSlot; +import net.minestom.server.item.ItemComponent; import net.minestom.server.item.ItemStack; import net.minestom.server.network.NetworkBuffer; -import net.minestom.server.network.packet.server.ServerPacket.ComponentHolding; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import org.jetbrains.annotations.NotNull; @@ -16,7 +16,8 @@ import java.util.function.UnaryOperator; import java.util.stream.Collectors; -import static net.minestom.server.network.NetworkBuffer.*; +import static net.minestom.server.network.NetworkBuffer.BYTE; +import static net.minestom.server.network.NetworkBuffer.VAR_INT; public record EntityEquipmentPacket(int entityId, @NotNull Map equipments) implements ServerPacket.Play, ServerPacket.ComponentHolding { @@ -39,7 +40,7 @@ public void write(@NotNull NetworkBuffer writer) { byte slotEnum = (byte) entry.getKey().ordinal(); if (!last) slotEnum |= 0x80; writer.write(BYTE, slotEnum); - writer.write(ITEM, entry.getValue()); + writer.write(ItemStack.NETWORK_TYPE, entry.getValue()); } } @@ -52,7 +53,7 @@ public int playId() { public @NotNull Collection components() { return this.equipments.values() .stream() - .map(ItemStack::getDisplayName) + .map(item -> item.get(ItemComponent.CUSTOM_NAME)) .filter(Objects::nonNull) .collect(Collectors.toList()); } @@ -60,7 +61,7 @@ public int playId() { @Override public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { final var map = new EnumMap(EquipmentSlot.class); - this.equipments.forEach((key, value) -> map.put(key, value.withDisplayName(operator))); + this.equipments.forEach((key, value) -> map.put(key, value.with(ItemComponent.CUSTOM_NAME, operator))); return new EntityEquipmentPacket(this.entityId, map); } @@ -70,7 +71,7 @@ private static Map readEquipments(@NotNull NetworkBuff byte slot; do { slot = reader.read(BYTE); - equipments.put(EquipmentSlot.values()[slot & 0x7F], reader.read(ITEM)); + equipments.put(EquipmentSlot.values()[slot & 0x7F], reader.read(ItemStack.NETWORK_TYPE)); } while ((slot & 0x80) == 0x80); return equipments; } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/EntityMetaDataPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/EntityMetaDataPacket.java index 900785f41f9..6dbc825a07b 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/EntityMetaDataPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/EntityMetaDataPacket.java @@ -3,7 +3,6 @@ import net.kyori.adventure.text.Component; import net.minestom.server.entity.Metadata; import net.minestom.server.network.NetworkBuffer; -import net.minestom.server.network.packet.server.ServerPacket.ComponentHolding; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import org.jetbrains.annotations.NotNull; @@ -13,7 +12,8 @@ import java.util.Map; import java.util.function.UnaryOperator; -import static net.minestom.server.network.NetworkBuffer.*; +import static net.minestom.server.network.NetworkBuffer.BYTE; +import static net.minestom.server.network.NetworkBuffer.VAR_INT; public record EntityMetaDataPacket(int entityId, @NotNull Map> entries) implements ServerPacket.Play, ServerPacket.ComponentHolding { @@ -73,12 +73,10 @@ public int playId() { if (v instanceof Component c) { var translated = operator.apply(c); - entries.put(key, t == Metadata.TYPE_OPTCHAT ? Metadata.OptChat(translated) : Metadata.Chat(translated)); + entries.put(key, t == Metadata.TYPE_OPT_CHAT ? Metadata.OptChat(translated) : Metadata.Chat(translated)); } else { entries.put(key, value); } - - entries.put(key, v instanceof Component c ? Metadata.Chat(operator.apply(c)) : value); }); return new EntityMetaDataPacket(this.entityId, entries); diff --git a/src/main/java/net/minestom/server/network/packet/server/play/EntityPropertiesPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/EntityPropertiesPacket.java deleted file mode 100644 index 9cc703dd46b..00000000000 --- a/src/main/java/net/minestom/server/network/packet/server/play/EntityPropertiesPacket.java +++ /dev/null @@ -1,63 +0,0 @@ -package net.minestom.server.network.packet.server.play; - -import net.minestom.server.MinecraftServer; -import net.minestom.server.attribute.*; -import net.minestom.server.network.NetworkBuffer; -import net.minestom.server.network.packet.server.ServerPacket; -import net.minestom.server.network.packet.server.ServerPacketIdentifier; -import org.jetbrains.annotations.NotNull; - -import java.util.Collection; -import java.util.List; - -import static net.minestom.server.network.NetworkBuffer.*; - -public record EntityPropertiesPacket(int entityId, List properties) implements ServerPacket.Play { - public static final int MAX_ENTRIES = 1024; - - public EntityPropertiesPacket { - properties = List.copyOf(properties); - } - - public EntityPropertiesPacket(@NotNull NetworkBuffer reader) { - this(reader.read(VAR_INT), reader.readCollection(r -> { - final Attribute attribute = MinecraftServer.getAttributeManager().getByName(reader.read(STRING)); - final double value = reader.read(DOUBLE); - int modifierCount = reader.read(VAR_INT); - AttributeInstance instance = new AttributeInstance(attribute, null); - for (int i = 0; i < modifierCount; i++) { - AttributeModifier modifier = new AttributeModifier(reader.read(UUID), "", reader.read(DOUBLE), AttributeOperation.fromId(reader.read(BYTE))); - instance.addModifier(modifier); - } - return instance; - }, MAX_ENTRIES)); - } - - @Override - public void write(@NotNull NetworkBuffer writer) { - writer.write(VAR_INT, entityId); - writer.write(VAR_INT, properties.size()); - for (AttributeInstance instance : properties) { - final Attribute attribute = instance.getAttribute(); - - writer.write(STRING, attribute.namespace().toString()); - writer.write(DOUBLE, (double) instance.getBaseValue()); - - { - Collection modifiers = instance.getModifiers(); - writer.write(VAR_INT, modifiers.size()); - - for (var modifier : modifiers) { - writer.write(UUID, modifier.getId()); - writer.write(DOUBLE, modifier.getAmount()); - writer.write(BYTE, (byte) modifier.getOperation().getId()); - } - } - } - } - - @Override - public int playId() { - return ServerPacketIdentifier.ENTITY_PROPERTIES; - } -} diff --git a/src/main/java/net/minestom/server/network/packet/server/play/EntitySoundEffectPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/EntitySoundEffectPacket.java index 25978714838..ed384771a39 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/EntitySoundEffectPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/EntitySoundEffectPacket.java @@ -6,17 +6,12 @@ import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.sound.SoundEvent; -import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import static net.minestom.server.network.NetworkBuffer.*; public record EntitySoundEffectPacket( - // only one of soundEvent and soundName may be present - @Nullable SoundEvent soundEvent, - @Nullable String soundName, - @Nullable Float range, // Only allowed with soundName + @NotNull SoundEvent soundEvent, @NotNull Sound.Source source, int entityId, float volume, @@ -24,64 +19,18 @@ public record EntitySoundEffectPacket( long seed ) implements ServerPacket.Play { - public EntitySoundEffectPacket { - Check.argCondition(soundEvent == null && soundName == null, "soundEvent and soundName cannot both be null"); - Check.argCondition(soundEvent != null && soundName != null, "soundEvent and soundName cannot both be present"); - Check.argCondition(soundName == null && range != null, "range cannot be present if soundName is null"); - } - - public EntitySoundEffectPacket(@NotNull SoundEvent soundEvent, @Nullable Float range, @NotNull Sound.Source source, - int entityId, float volume, float pitch, long seed) { - this(soundEvent, null, range, source, entityId, volume, pitch, seed); - } - - public EntitySoundEffectPacket(@NotNull String soundName, @Nullable Float range, @NotNull Sound.Source source, - int entityId, float volume, float pitch, long seed) { - this(null, soundName, range, source, entityId, volume, pitch, seed); - } - public EntitySoundEffectPacket(@NotNull NetworkBuffer reader) { - this(fromReader(reader)); - } - - private EntitySoundEffectPacket(@NotNull EntitySoundEffectPacket packet) { - this(packet.soundEvent, packet.soundName, packet.range, packet.source, packet.entityId, packet.volume, packet.pitch, packet.seed); - } - - private static @NotNull EntitySoundEffectPacket fromReader(@NotNull NetworkBuffer reader) { - int soundId = reader.read(VAR_INT); - SoundEvent soundEvent; - String soundName; - Float range = null; - if (soundId == 0) { - soundEvent = null; - soundName = reader.read(STRING); - range = reader.readOptional(FLOAT); - } else { - soundEvent = SoundEvent.fromId(soundId - 1); - soundName = null; - } - return new EntitySoundEffectPacket( - soundEvent, - soundName, - range, + this(reader.read(SoundEvent.NETWORK_TYPE), reader.readEnum(Sound.Source.class), reader.read(VAR_INT), reader.read(FLOAT), reader.read(FLOAT), - reader.read(LONG) - ); + reader.read(LONG)); } @Override public void write(@NotNull NetworkBuffer writer) { - if (soundEvent != null) { - writer.write(VAR_INT, soundEvent.id() + 1); - } else { - writer.write(VAR_INT, 0); - writer.write(STRING, soundName); - writer.writeOptional(FLOAT, range); - } + writer.write(SoundEvent.NETWORK_TYPE, soundEvent); writer.write(VAR_INT, AdventurePacketConvertor.getSoundSourceValue(source)); writer.write(VAR_INT, entityId); writer.write(FLOAT, volume); diff --git a/src/main/java/net/minestom/server/network/packet/server/play/ExplosionPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/ExplosionPacket.java index 70c15f6d8bb..4b5dbb42d72 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/ExplosionPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/ExplosionPacket.java @@ -1,5 +1,6 @@ package net.minestom.server.network.packet.server.play; +import net.minestom.server.item.ItemStack; import net.minestom.server.network.NetworkBuffer; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; @@ -16,23 +17,23 @@ public record ExplosionPacket(double x, double y, double z, float radius, @NotNull BlockInteraction blockInteraction, int smallParticleId, byte @NotNull [] smallParticleData, int largeParticleId, byte @NotNull [] largeParticleData, - @NotNull String soundName, boolean hasFixedSoundRange, float soundRange) implements ServerPacket.Play { + @NotNull SoundEvent sound) implements ServerPacket.Play { + public static final SoundEvent DEFAULT_SOUND = SoundEvent.ENTITY_GENERIC_EXPLODE; + private static @NotNull ExplosionPacket fromReader(@NotNull NetworkBuffer reader) { double x = reader.read(DOUBLE), y = reader.read(DOUBLE), z = reader.read(DOUBLE); float radius = reader.read(FLOAT); byte[] records = reader.readBytes(reader.read(VAR_INT) * 3); float playerMotionX = reader.read(FLOAT), playerMotionY = reader.read(FLOAT), playerMotionZ = reader.read(FLOAT); - BlockInteraction blockInteraction = BlockInteraction.values()[reader.read(VAR_INT)]; + BlockInteraction blockInteraction = reader.readEnum(BlockInteraction.class); int smallParticleId = reader.read(VAR_INT); byte[] smallParticleData = readParticleData(reader, Particle.fromId(smallParticleId)); int largeParticleId = reader.read(VAR_INT); byte[] largeParticleData = readParticleData(reader, Particle.fromId(largeParticleId)); - String soundName = reader.read(STRING); - boolean hasFixedSoundRange = reader.read(BOOLEAN); - float soundRange = hasFixedSoundRange ? reader.read(FLOAT) : 0; + SoundEvent sound = reader.read(SoundEvent.NETWORK_TYPE); return new ExplosionPacket(x, y, z, radius, records, playerMotionX, playerMotionY, playerMotionZ, blockInteraction, smallParticleId, smallParticleData, largeParticleId, largeParticleData, - soundName, hasFixedSoundRange, soundRange); + sound); } private static byte @NotNull [] readParticleData(@NotNull NetworkBuffer reader, Particle particle) { @@ -53,7 +54,7 @@ else if (particle.equals(Particle.SCULK_CHARGE)) { return writer.toByteArray(); } else if (particle.equals(Particle.ITEM)) { - writer.writeItemStack(reader.read(ITEM)); + writer.writeItemStack(reader.read(ItemStack.NETWORK_TYPE)); } else if (particle.equals(Particle.DUST_COLOR_TRANSITION)) { for (int i = 0; i < 7; i++) writer.writeFloat(reader.read(FLOAT)); @@ -74,13 +75,13 @@ public ExplosionPacket(double x, double y, double z, float radius, byte @NotNull this(x, y, z, radius, records, playerMotionX, playerMotionY, playerMotionZ, BlockInteraction.DESTROY, Particle.EXPLOSION.id(), new byte[] {}, Particle.EXPLOSION_EMITTER.id(), new byte[] {}, - SoundEvent.ENTITY_GENERIC_EXPLODE.name(), false, 0); + DEFAULT_SOUND); } private ExplosionPacket(@NotNull ExplosionPacket packet) { this(packet.x, packet.y, packet.z, packet.radius, packet.records, packet.playerMotionX, packet.playerMotionY, packet.playerMotionZ, packet.blockInteraction, packet.smallParticleId, packet.smallParticleData, packet.largeParticleId, packet.largeParticleData, - packet.soundName, packet.hasFixedSoundRange, packet.soundRange); + packet.sound); } @Override @@ -99,9 +100,7 @@ public void write(@NotNull NetworkBuffer writer) { writer.write(RAW_BYTES, smallParticleData); writer.write(VAR_INT, largeParticleId); writer.write(RAW_BYTES, largeParticleData); - writer.write(STRING, soundName); - writer.write(BOOLEAN, hasFixedSoundRange); - if (hasFixedSoundRange) writer.write(FLOAT, soundRange); + writer.write(SoundEvent.NETWORK_TYPE, sound); } @Override diff --git a/src/main/java/net/minestom/server/network/packet/server/play/JoinGamePacket.java b/src/main/java/net/minestom/server/network/packet/server/play/JoinGamePacket.java index 36528e1904d..b9811bab4ab 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/JoinGamePacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/JoinGamePacket.java @@ -15,9 +15,10 @@ public record JoinGamePacket( int entityId, boolean isHardcore, List worlds, int maxPlayers, int viewDistance, int simulationDistance, boolean reducedDebugInfo, boolean enableRespawnScreen, - boolean doLimitedCrafting, - String dimensionType, String world, long hashedSeed, GameMode gameMode, GameMode previousGameMode, - boolean isDebug, boolean isFlat, WorldPos deathLocation, int portalCooldown + boolean doLimitedCrafting, int dimensionType, + String world, long hashedSeed, GameMode gameMode, GameMode previousGameMode, + boolean isDebug, boolean isFlat, @Nullable WorldPos deathLocation, int portalCooldown, + boolean enforcesSecureChat ) implements ServerPacket.Play { public static final int MAX_WORLDS = Short.MAX_VALUE; @@ -36,16 +37,16 @@ public JoinGamePacket(@NotNull NetworkBuffer reader) { reader.read(BOOLEAN), reader.read(BOOLEAN), reader.read(BOOLEAN), - - reader.read(STRING), + reader.read(VAR_INT), reader.read(STRING), reader.read(LONG), GameMode.fromId(reader.read(BYTE)), getNullableGameMode(reader.read(BYTE)), reader.read(BOOLEAN), reader.read(BOOLEAN), - reader.read(DEATH_LOCATION), - reader.read(VAR_INT) + reader.readOptional(WorldPos.NETWORK_TYPE), + reader.read(VAR_INT), + reader.read(BOOLEAN) ); } @@ -60,8 +61,7 @@ public void write(@NotNull NetworkBuffer writer) { writer.write(BOOLEAN, reducedDebugInfo); writer.write(BOOLEAN, enableRespawnScreen); writer.write(BOOLEAN, doLimitedCrafting); - - writer.write(STRING, dimensionType); + writer.write(VAR_INT, dimensionType); writer.write(STRING, world); writer.write(LONG, hashedSeed); writer.write(BYTE, gameMode.id()); @@ -72,8 +72,9 @@ public void write(@NotNull NetworkBuffer writer) { } writer.write(BOOLEAN, isDebug); writer.write(BOOLEAN, isFlat); - writer.write(DEATH_LOCATION, deathLocation); + writer.writeOptional(WorldPos.NETWORK_TYPE, deathLocation); writer.write(VAR_INT, portalCooldown); + writer.write(BOOLEAN, enforcesSecureChat); } @Override diff --git a/src/main/java/net/minestom/server/network/packet/server/play/NbtQueryResponsePacket.java b/src/main/java/net/minestom/server/network/packet/server/play/NbtQueryResponsePacket.java index 9040098177e..d48187ec2e2 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/NbtQueryResponsePacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/NbtQueryResponsePacket.java @@ -1,16 +1,17 @@ package net.minestom.server.network.packet.server.play; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minestom.server.network.ConnectionState; import net.minestom.server.network.NetworkBuffer; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import org.jetbrains.annotations.NotNull; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; import static net.minestom.server.network.NetworkBuffer.*; -public record NbtQueryResponsePacket(int transactionId, NBTCompound data) implements ServerPacket.Play { +public record NbtQueryResponsePacket(int transactionId, CompoundBinaryTag data) implements ServerPacket.Play { public NbtQueryResponsePacket(@NotNull NetworkBuffer reader) { - this(reader.read(VAR_INT), (NBTCompound) reader.read(NBT)); + this(reader.read(VAR_INT), (CompoundBinaryTag) reader.read(NBT)); } @Override diff --git a/src/main/java/net/minestom/server/network/packet/server/play/ParticlePacket.java b/src/main/java/net/minestom/server/network/packet/server/play/ParticlePacket.java index 9e77eeeeb1b..efb2a8442c4 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/ParticlePacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/ParticlePacket.java @@ -5,28 +5,23 @@ import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.particle.Particle; -import net.minestom.server.particle.data.ParticleData; -import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; + +import java.util.Objects; import static net.minestom.server.network.NetworkBuffer.*; -public record ParticlePacket(int particleId, boolean longDistance, double x, double y, double z, float offsetX, float offsetY, float offsetZ, float maxSpeed, int particleCount, @Nullable ParticleData data) implements ServerPacket.Play { +public record ParticlePacket(@NotNull Particle particle, boolean longDistance, double x, double y, double z, float offsetX, float offsetY, float offsetZ, float maxSpeed, int particleCount) implements ServerPacket.Play { private ParticlePacket(ParticlePacket copy) { - this(copy.particleId, copy.longDistance, copy.x, copy.y, copy.z, copy.offsetX, copy.offsetY, copy.offsetZ, copy.maxSpeed, copy.particleCount, copy.data); + this(copy.particle, copy.longDistance, copy.x, copy.y, copy.z, copy.offsetX, copy.offsetY, copy.offsetZ, copy.maxSpeed, copy.particleCount); } public ParticlePacket(@NotNull NetworkBuffer reader) { this(readPacket(reader)); } - public ParticlePacket(@NotNull Particle particle, boolean longDistance, double x, double y, double z, float offsetX, float offsetY, float offsetZ, float maxSpeed, int particleCount) { - this(particle.id(), longDistance, x, y, z, offsetX, offsetY, offsetZ, maxSpeed, particleCount, particle.data()); - } - public ParticlePacket(@NotNull Particle particle, double x, double y, double z, float offsetX, float offsetY, float offsetZ, float maxSpeed, int particleCount) { - this(particle.id(), false, x, y, z, offsetX, offsetY, offsetZ, maxSpeed, particleCount, particle.data()); + this(particle, false, x, y, z, offsetX, offsetY, offsetZ, maxSpeed, particleCount); } public ParticlePacket(@NotNull Particle particle, boolean longDistance, @NotNull Point position, @NotNull Point offset, float maxSpeed, int particleCount) { @@ -38,7 +33,6 @@ public ParticlePacket(@NotNull Particle particle, @NotNull Point position, @NotN } private static ParticlePacket readPacket(NetworkBuffer reader) { - int particleId = reader.read(VAR_INT); Boolean longDistance = reader.read(BOOLEAN); Double x = reader.read(DOUBLE); Double y = reader.read(DOUBLE); @@ -48,17 +42,15 @@ private static ParticlePacket readPacket(NetworkBuffer reader) { Float offsetZ = reader.read(FLOAT); Float maxSpeed = reader.read(FLOAT); Integer particleCount = reader.read(INT); - ParticleData data = ParticleData.read(particleId, reader); - return new ParticlePacket(particleId, longDistance, x, y, z, offsetX, offsetY, offsetZ, maxSpeed, particleCount, data); + Particle particle = Particle.fromId(reader.read(VAR_INT)); + Objects.requireNonNull(particle); + + return new ParticlePacket(particle.readData(reader), longDistance, x, y, z, offsetX, offsetY, offsetZ, maxSpeed, particleCount); } @Override public void write(@NotNull NetworkBuffer writer) { - Check.stateCondition(data != null && !data.validate(particleId), "Particle data {0} is not valid for this particle type {1}", data, Particle.fromId(particleId)); - Check.stateCondition(data == null && ParticleData.requiresData(particleId), "Particle data is required for this particle type {0}", Particle.fromId(particleId)); - - writer.write(VAR_INT, particleId); writer.write(BOOLEAN, longDistance); writer.write(DOUBLE, x); writer.write(DOUBLE, y); @@ -68,8 +60,8 @@ public void write(@NotNull NetworkBuffer writer) { writer.write(FLOAT, offsetZ); writer.write(FLOAT, maxSpeed); writer.write(INT, particleCount); - - if (data != null) data.write(writer); + writer.write(VAR_INT, particle.id()); + particle.writeData(writer); } @Override diff --git a/src/main/java/net/minestom/server/network/packet/server/play/ProjectilePowerPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/ProjectilePowerPacket.java new file mode 100644 index 00000000000..262a8d7deca --- /dev/null +++ b/src/main/java/net/minestom/server/network/packet/server/play/ProjectilePowerPacket.java @@ -0,0 +1,27 @@ +package net.minestom.server.network.packet.server.play; + +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.network.packet.server.ServerPacket; +import net.minestom.server.network.packet.server.ServerPacketIdentifier; +import org.jetbrains.annotations.NotNull; + +public record ProjectilePowerPacket( + int entityId, double accelerationPower +) implements ServerPacket.Play { + + public ProjectilePowerPacket(@NotNull NetworkBuffer buffer) { + this(buffer.read(NetworkBuffer.VAR_INT), buffer.read(NetworkBuffer.DOUBLE)); + } + + @Override + public void write(@NotNull NetworkBuffer writer) { + writer.write(NetworkBuffer.VAR_INT, entityId); + writer.write(NetworkBuffer.DOUBLE, accelerationPower); + } + + @Override + public int playId() { + return ServerPacketIdentifier.PROJECTILE_POWER; + } + +} diff --git a/src/main/java/net/minestom/server/network/packet/server/play/RespawnPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/RespawnPacket.java index 12eb01e4eb8..2c96d1c3cbe 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/RespawnPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/RespawnPacket.java @@ -6,13 +6,14 @@ import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.network.packet.server.play.data.WorldPos; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import static net.minestom.server.network.NetworkBuffer.*; public record RespawnPacket( - String dimensionType, String worldName, - long hashedSeed, GameMode gameMode, GameMode previousGameMode, - boolean isDebug, boolean isFlat, WorldPos deathLocation, + int dimensionType, @NotNull String worldName, + long hashedSeed, @NotNull GameMode gameMode, @NotNull GameMode previousGameMode, + boolean isDebug, boolean isFlat, @Nullable WorldPos deathLocation, int portalCooldown, int copyData ) implements ServerPacket.Play { public static final int COPY_NONE = 0x0; @@ -21,24 +22,24 @@ public record RespawnPacket( public static final int COPY_ALL = COPY_ATTRIBUTES | COPY_METADATA; public RespawnPacket(@NotNull NetworkBuffer reader) { - this(reader.read(STRING), reader.read(STRING), + this(reader.read(VAR_INT), reader.read(STRING), reader.read(LONG), GameMode.fromId(reader.read(BYTE)), GameMode.fromId(reader.read(BYTE)), reader.read(BOOLEAN), reader.read(BOOLEAN), - reader.read(DEATH_LOCATION), + reader.readOptional(WorldPos.NETWORK_TYPE), reader.read(VAR_INT), reader.read(BYTE)); } @Override public void write(@NotNull NetworkBuffer writer) { - writer.write(STRING, dimensionType); + writer.write(VAR_INT, dimensionType); writer.write(STRING, worldName); writer.write(LONG, hashedSeed); writer.write(BYTE, gameMode.id()); writer.write(BYTE, previousGameMode.id()); writer.write(BOOLEAN, isDebug); writer.write(BOOLEAN, isFlat); - writer.write(DEATH_LOCATION, deathLocation); + writer.writeOptional(WorldPos.NETWORK_TYPE, deathLocation); writer.write(VAR_INT, portalCooldown); writer.write(BYTE, (byte) copyData); } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/SetSlotPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/SetSlotPacket.java index 1be1adafac7..bf200a91aab 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/SetSlotPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/SetSlotPacket.java @@ -1,9 +1,9 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.text.Component; +import net.minestom.server.item.ItemComponent; import net.minestom.server.item.ItemStack; import net.minestom.server.network.NetworkBuffer; -import net.minestom.server.network.packet.server.ServerPacket.ComponentHolding; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import org.jetbrains.annotations.NotNull; @@ -19,7 +19,7 @@ public record SetSlotPacket(byte windowId, int stateId, short slot, @NotNull ItemStack itemStack) implements ServerPacket.Play, ServerPacket.ComponentHolding { public SetSlotPacket(@NotNull NetworkBuffer reader) { this(reader.read(BYTE), reader.read(VAR_INT), reader.read(SHORT), - reader.read(ITEM)); + reader.read(ItemStack.NETWORK_TYPE)); } @Override @@ -27,7 +27,7 @@ public void write(@NotNull NetworkBuffer writer) { writer.write(BYTE, windowId); writer.write(VAR_INT, stateId); writer.write(SHORT, slot); - writer.write(ITEM, itemStack); + writer.write(ItemStack.NETWORK_TYPE, itemStack); } @Override @@ -37,20 +37,24 @@ public int playId() { @Override public @NotNull Collection components() { - final var components = new ArrayList<>(this.itemStack.getLore()); - final var displayname = this.itemStack.getDisplayName(); - if (displayname != null) components.add(displayname); - + final var components = new ArrayList<>(this.itemStack.get(ItemComponent.LORE, List.of())); + final var displayName = this.itemStack.get(ItemComponent.CUSTOM_NAME); + if (displayName != null) components.add(displayName); + final var itemName = this.itemStack.get(ItemComponent.ITEM_NAME); + if (itemName != null) components.add(itemName); return List.copyOf(components); } @Override public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { - return new SetSlotPacket(this.windowId, this.stateId, this.slot, this.itemStack.withDisplayName(operator).withLore(lines -> { - final var translatedComponents = new ArrayList(); - lines.forEach(component -> translatedComponents.add(operator.apply(component))); - return translatedComponents; - })); + return new SetSlotPacket(this.windowId, this.stateId, this.slot, this.itemStack + .with(ItemComponent.CUSTOM_NAME, operator) + .with(ItemComponent.ITEM_NAME, operator) + .with(ItemComponent.LORE, (UnaryOperator>) lines -> { + final var translatedComponents = new ArrayList(); + lines.forEach(component -> translatedComponents.add(operator.apply(component))); + return translatedComponents; + })); } /** diff --git a/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java index 831c157a42d..48887288823 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java @@ -7,17 +7,12 @@ import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.sound.SoundEvent; -import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import static net.minestom.server.network.NetworkBuffer.*; public record SoundEffectPacket( - // only one of soundEvent and soundName may be present - @Nullable SoundEvent soundEvent, - @Nullable String soundName, - @Nullable Float range, // Only allowed with soundName + @NotNull SoundEvent soundEvent, @NotNull Source source, int x, int y, @@ -27,67 +22,25 @@ public record SoundEffectPacket( long seed ) implements ServerPacket.Play { - public SoundEffectPacket { - Check.argCondition(soundEvent == null && soundName == null, "soundEvent and soundName cannot both be null"); - Check.argCondition(soundEvent != null && soundName != null, "soundEvent and soundName cannot both be present"); - Check.argCondition(soundName == null && range != null, "range cannot be present if soundName is null"); + public SoundEffectPacket(@NotNull SoundEvent soundEvent, @NotNull Source source, @NotNull Point position, float volume, float pitch, long seed) { + this(soundEvent, source, position.blockX(), position.blockY(), position.blockZ(), volume, pitch, seed); } - private static @NotNull SoundEffectPacket fromReader(@NotNull NetworkBuffer reader) { - int soundId = reader.read(VAR_INT); - SoundEvent soundEvent; - String soundName; - Float range = null; - if (soundId == 0) { - soundEvent = null; - soundName = reader.read(STRING); - range = reader.readOptional(FLOAT); - } else { - soundEvent = SoundEvent.fromId(soundId - 1); - soundName = null; - } - return new SoundEffectPacket( - soundEvent, - soundName, - range, + public SoundEffectPacket(@NotNull NetworkBuffer reader) { + this(reader.read(SoundEvent.NETWORK_TYPE), reader.readEnum(Source.class), reader.read(INT) * 8, reader.read(INT) * 8, reader.read(INT) * 8, reader.read(FLOAT), reader.read(FLOAT), - reader.read(LONG) - ); - } - - public SoundEffectPacket(@NotNull SoundEvent soundEvent, @Nullable Float range, @NotNull Source source, - @NotNull Point position, float volume, float pitch, long seed) { - this(soundEvent, null, range, source, position.blockX(), position.blockY(), position.blockZ(), volume, pitch, seed); - } - - public SoundEffectPacket(@NotNull String soundName, @Nullable Float range, @NotNull Source source, - @NotNull Point position, float volume, float pitch, long seed) { - this(null, soundName, range, source, position.blockX(), position.blockY(), position.blockZ(), volume, pitch, seed); + reader.read(LONG)); } - public SoundEffectPacket(@NotNull NetworkBuffer reader) { - this(fromReader(reader)); - } - - private SoundEffectPacket(@NotNull SoundEffectPacket packet) { - this(packet.soundEvent, packet.soundName, packet.range, packet.source, - packet.x, packet.y, packet.z, packet.volume, packet.pitch, packet.seed); - } @Override public void write(@NotNull NetworkBuffer writer) { - if (soundEvent != null) { - writer.write(VAR_INT, soundEvent.id() + 1); - } else { - writer.write(VAR_INT, 0); - writer.write(STRING, soundName); - writer.writeOptional(FLOAT, range); - } + writer.write(SoundEvent.NETWORK_TYPE, soundEvent); writer.write(VAR_INT, AdventurePacketConvertor.getSoundSourceValue(source)); writer.write(INT, x * 8); writer.write(INT, y * 8); diff --git a/src/main/java/net/minestom/server/network/packet/server/play/TradeListPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/TradeListPacket.java index baffa039a43..c21acc41ba6 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/TradeListPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/TradeListPacket.java @@ -45,17 +45,17 @@ public record Trade(ItemStack inputItem1, ItemStack result, int tradeUsesNumber, int maxTradeUsesNumber, int exp, int specialPrice, float priceMultiplier, int demand) implements NetworkBuffer.Writer { public Trade(@NotNull NetworkBuffer reader) { - this(reader.read(ITEM), reader.read(ITEM), - reader.readOptional(ITEM), reader.read(BOOLEAN), + this(reader.read(ItemStack.NETWORK_TYPE), reader.read(ItemStack.NETWORK_TYPE), + reader.readOptional(ItemStack.NETWORK_TYPE), reader.read(BOOLEAN), reader.read(INT), reader.read(INT), reader.read(INT), reader.read(INT), reader.read(FLOAT), reader.read(INT)); } @Override public void write(@NotNull NetworkBuffer writer) { - writer.write(ITEM, inputItem1); - writer.write(ITEM, result); - writer.writeOptional(ITEM, inputItem2); + writer.write(ItemStack.NETWORK_TYPE, inputItem1); + writer.write(ItemStack.NETWORK_TYPE, result); + writer.writeOptional(ItemStack.NETWORK_TYPE, inputItem2); writer.write(BOOLEAN, tradeDisabled); writer.write(INT, tradeUsesNumber); writer.write(INT, maxTradeUsesNumber); diff --git a/src/main/java/net/minestom/server/network/packet/server/play/WindowItemsPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/WindowItemsPacket.java index 21073cbfd3a..6c712c5102d 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/WindowItemsPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/WindowItemsPacket.java @@ -1,9 +1,9 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.text.Component; +import net.minestom.server.item.ItemComponent; import net.minestom.server.item.ItemStack; import net.minestom.server.network.NetworkBuffer; -import net.minestom.server.network.packet.server.ServerPacket.ComponentHolding; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import org.jetbrains.annotations.NotNull; @@ -13,7 +13,8 @@ import java.util.List; import java.util.function.UnaryOperator; -import static net.minestom.server.network.NetworkBuffer.*; +import static net.minestom.server.network.NetworkBuffer.BYTE; +import static net.minestom.server.network.NetworkBuffer.VAR_INT; public record WindowItemsPacket(byte windowId, int stateId, @NotNull List items, @NotNull ItemStack carriedItem) implements ServerPacket.Play, ServerPacket.ComponentHolding { @@ -24,16 +25,16 @@ public record WindowItemsPacket(byte windowId, int stateId, @NotNull List(); list.forEach(itemStack -> { - components.addAll(itemStack.getLore()); + components.addAll(itemStack.get(ItemComponent.LORE, List.of())); - final var displayName = itemStack.getDisplayName(); - if (displayName == null) return; + final var customName = itemStack.get(ItemComponent.CUSTOM_NAME); + if (customName != null) { + components.add(customName); + } - components.add(displayName); + final var itemName = itemStack.get(ItemComponent.ITEM_NAME); + if (itemName != null) { + components.add(itemName); + } }); return components; @@ -62,19 +68,23 @@ public int playId() { @Override public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + UnaryOperator> loreOperator = lines -> { + final var translatedComponents = new ArrayList(); + lines.forEach(component -> translatedComponents.add(operator.apply(component))); + return translatedComponents; + }; return new WindowItemsPacket( this.windowId, this.stateId, - this.items.stream().map(stack -> stack.withDisplayName(operator).withLore(lines -> { - final var translatedComponents = new ArrayList(); - lines.forEach(component -> translatedComponents.add(operator.apply(component))); - return translatedComponents; - })).toList(), - this.carriedItem.withDisplayName(operator).withLore(lines -> { - final var translatedComponents = new ArrayList(); - lines.forEach(component -> translatedComponents.add(operator.apply(component))); - return translatedComponents; - }) + this.items.stream().map(stack -> stack + .with(ItemComponent.ITEM_NAME, operator) + .with(ItemComponent.CUSTOM_NAME, operator) + .with(ItemComponent.LORE, loreOperator)) + .toList(), + this.carriedItem + .with(ItemComponent.ITEM_NAME, operator) + .with(ItemComponent.CUSTOM_NAME, operator) + .with(ItemComponent.LORE, loreOperator) ); } } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/data/ChunkData.java b/src/main/java/net/minestom/server/network/packet/server/play/data/ChunkData.java index c0126205d88..877832732d5 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/data/ChunkData.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/data/ChunkData.java @@ -1,12 +1,12 @@ package net.minestom.server.network.packet.server.play.data; +import net.kyori.adventure.nbt.CompoundBinaryTag; import net.minestom.server.coordinate.Point; import net.minestom.server.instance.block.Block; import net.minestom.server.network.NetworkBuffer; import net.minestom.server.utils.block.BlockUtils; import net.minestom.server.utils.chunk.ChunkUtils; import org.jetbrains.annotations.NotNull; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; import java.util.HashMap; import java.util.Map; @@ -14,7 +14,7 @@ import static net.minestom.server.network.NetworkBuffer.*; -public record ChunkData(@NotNull NBTCompound heightmaps, byte @NotNull [] data, +public record ChunkData(@NotNull CompoundBinaryTag heightmaps, byte @NotNull [] data, @NotNull Map blockEntities) implements NetworkBuffer.Writer { public ChunkData { blockEntities = blockEntities.entrySet() @@ -24,7 +24,7 @@ public record ChunkData(@NotNull NBTCompound heightmaps, byte @NotNull [] data, } public ChunkData(@NotNull NetworkBuffer reader) { - this((NBTCompound) reader.read(NBT), reader.read(BYTE_ARRAY), + this((CompoundBinaryTag) reader.read(NBT), reader.read(BYTE_ARRAY), readBlockEntities(reader)); } @@ -46,7 +46,7 @@ public void write(@NotNull NetworkBuffer writer) { writer.write(SHORT, (short) point.blockY()); // y writer.write(VAR_INT, registry.blockEntityId()); - final NBTCompound nbt = BlockUtils.extractClientNbt(block); + final CompoundBinaryTag nbt = BlockUtils.extractClientNbt(block); assert nbt != null; writer.write(NBT, nbt); // block nbt } @@ -59,7 +59,7 @@ private static Map readBlockEntities(@NotNull NetworkBuffer read final byte xz = reader.read(BYTE); final short y = reader.read(SHORT); final int blockEntityId = reader.read(VAR_INT); - final NBTCompound nbt = (NBTCompound) reader.read(NBT); + final CompoundBinaryTag nbt = (CompoundBinaryTag) reader.read(NBT); // TODO create block object } return blockEntities; diff --git a/src/main/java/net/minestom/server/network/packet/server/play/data/WorldPos.java b/src/main/java/net/minestom/server/network/packet/server/play/data/WorldPos.java index 5122fdbf0f9..42b2d542277 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/data/WorldPos.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/data/WorldPos.java @@ -1,22 +1,51 @@ package net.minestom.server.network.packet.server.play.data; +import net.kyori.adventure.nbt.CompoundBinaryTag; import net.minestom.server.coordinate.Point; import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.nbt.BinaryTagSerializer; import org.jetbrains.annotations.NotNull; import static net.minestom.server.network.NetworkBuffer.BLOCK_POSITION; import static net.minestom.server.network.NetworkBuffer.STRING; -public record WorldPos(@NotNull String dimension, @NotNull Point position) implements NetworkBuffer.Writer { +public record WorldPos(@NotNull String dimension, @NotNull Point blockPosition) implements NetworkBuffer.Writer { + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type() { + @Override + public void write(@NotNull NetworkBuffer buffer, WorldPos value) { + buffer.write(NetworkBuffer.STRING, value.dimension); + buffer.write(NetworkBuffer.BLOCK_POSITION, value.blockPosition); + } + + @Override + public WorldPos read(@NotNull NetworkBuffer buffer) { + return new WorldPos(buffer.read(NetworkBuffer.STRING), buffer.read(NetworkBuffer.BLOCK_POSITION)); + } + }; + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + tag -> new WorldPos(tag.getString("dimension"), BinaryTagSerializer.BLOCK_POSITION.read(tag.get("pos"))), + pos -> CompoundBinaryTag.builder() + .putString("dimension", pos.dimension) + .put("pos", BinaryTagSerializer.BLOCK_POSITION.write(pos.blockPosition)) + .build() + ); public WorldPos(@NotNull NetworkBuffer reader) { this(reader.read(STRING), reader.read(BLOCK_POSITION)); } + public @NotNull WorldPos withDimension(@NotNull String dimension) { + return new WorldPos(dimension, blockPosition); + } + + public @NotNull WorldPos withBlockPosition(@NotNull Point blockPosition) { + return new WorldPos(dimension, blockPosition); + } + @Override public void write(@NotNull NetworkBuffer writer) { writer.write(STRING, dimension); - writer.write(BLOCK_POSITION, position); + writer.write(BLOCK_POSITION, blockPosition); } } \ No newline at end of file diff --git a/src/main/java/net/minestom/server/network/player/GameProfile.java b/src/main/java/net/minestom/server/network/player/GameProfile.java index 3613fe95dea..7ba79c91013 100644 --- a/src/main/java/net/minestom/server/network/player/GameProfile.java +++ b/src/main/java/net/minestom/server/network/player/GameProfile.java @@ -1,7 +1,6 @@ package net.minestom.server.network.player; import net.minestom.server.network.NetworkBuffer; -import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -10,7 +9,6 @@ import static net.minestom.server.network.NetworkBuffer.STRING; -@ApiStatus.Experimental public record GameProfile(@NotNull UUID uuid, @NotNull String name, @NotNull List<@NotNull Property> properties) implements NetworkBuffer.Writer { public static final int MAX_PROPERTIES = 1024; diff --git a/src/main/java/net/minestom/server/network/player/PlayerConnection.java b/src/main/java/net/minestom/server/network/player/PlayerConnection.java index 4d2dc7e7b86..408e47ee89b 100644 --- a/src/main/java/net/minestom/server/network/player/PlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/PlayerConnection.java @@ -6,7 +6,12 @@ import net.minestom.server.entity.Player; import net.minestom.server.network.ConnectionState; import net.minestom.server.network.packet.server.SendablePacket; +import net.minestom.server.network.packet.server.common.CookieRequestPacket; +import net.minestom.server.network.packet.server.common.CookieStorePacket; +import net.minestom.server.network.packet.server.configuration.SelectKnownPacksPacket; import net.minestom.server.network.plugin.LoginPluginMessageProcessor; +import net.minestom.server.utils.NamespaceID; +import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -14,7 +19,10 @@ import java.net.SocketAddress; import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; /** * A PlayerConnection is an object needed for all created {@link Player}. @@ -28,6 +36,10 @@ public abstract class PlayerConnection { private LoginPluginMessageProcessor loginPluginMessageProcessor = new LoginPluginMessageProcessor(this); + private CompletableFuture> knownPacksFuture = null; // Present only when waiting for a response from the client. + + private final Map> pendingCookieRequests = new ConcurrentHashMap<>(); + public PlayerConnection() { this.online = true; this.connectionState = ConnectionState.HANDSHAKE; @@ -53,12 +65,10 @@ public PlayerConnection() { */ public abstract void sendPacket(@NotNull SendablePacket packet); - @ApiStatus.Experimental public void sendPackets(@NotNull Collection packets) { packets.forEach(this::sendPacket); } - @ApiStatus.Experimental public void sendPackets(@NotNull SendablePacket... packets) { sendPackets(List.of(packets)); } @@ -168,6 +178,25 @@ public void setPlayerPublicKey(PlayerPublicKey playerPublicKey) { this.playerPublicKey = playerPublicKey; } + public void storeCookie(@NotNull String key, byte @NotNull [] data) { + sendPacket(new CookieStorePacket(key, data)); + } + + public CompletableFuture fetchCookie(@NotNull String key) { + CompletableFuture future = new CompletableFuture<>(); + pendingCookieRequests.put(NamespaceID.from(key), future); + sendPacket(new CookieRequestPacket(key)); + return future; + } + + @ApiStatus.Internal + public void receiveCookieResponse(@NotNull String key, byte @Nullable [] data) { + CompletableFuture future = pendingCookieRequests.remove(NamespaceID.from(key)); + if (future != null) { + future.complete(data); + } + } + /** * Gets the login plugin message processor, only available during the login state. */ @@ -177,6 +206,22 @@ public void setPlayerPublicKey(PlayerPublicKey playerPublicKey) { "Login plugin message processor is only available during the login state."); } + @ApiStatus.Internal + public @NotNull CompletableFuture> requestKnownPacks(@NotNull List serverPacks) { + Check.stateCondition(knownPacksFuture != null, "Known packs already pending"); + sendPacket(new SelectKnownPacksPacket(serverPacks)); + return knownPacksFuture = new CompletableFuture<>(); + } + + @ApiStatus.Internal + public void receiveKnownPacksResponse(@NotNull List clientPacks) { + final var future = knownPacksFuture; + if (future != null) { + future.complete(clientPacks); + knownPacksFuture = null; + } + } + @Override public String toString() { return "PlayerConnection{" + diff --git a/src/main/java/net/minestom/server/particle/Particle.java b/src/main/java/net/minestom/server/particle/Particle.java index a66130d0d86..ecf08689a5d 100644 --- a/src/main/java/net/minestom/server/particle/Particle.java +++ b/src/main/java/net/minestom/server/particle/Particle.java @@ -1,14 +1,45 @@ package net.minestom.server.particle; -import net.minestom.server.particle.data.ParticleData; +import net.kyori.adventure.nbt.BinaryTagTypes; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.FloatBinaryTag; +import net.kyori.adventure.nbt.ListBinaryTag; +import net.kyori.adventure.util.RGBLike; +import net.minestom.server.color.AlphaColor; +import net.minestom.server.color.Color; +import net.minestom.server.coordinate.Point; +import net.minestom.server.item.ItemStack; +import net.minestom.server.network.NetworkBuffer; import net.minestom.server.registry.StaticProtocolObject; import net.minestom.server.utils.NamespaceID; +import net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; +import java.util.Objects; -public sealed interface Particle extends StaticProtocolObject, Particles permits ParticleImpl { +import static net.minestom.server.network.NetworkBuffer.VAR_INT; + +public sealed interface Particle extends StaticProtocolObject, Particles permits Particle.Block, Particle.BlockMarker, + Particle.Dust, Particle.DustColorTransition, Particle.DustPillar, Particle.EntityEffect, Particle.FallingDust, + Particle.Item, Particle.SculkCharge, Particle.Shriek, Particle.Simple, Particle.Vibration { + + @NotNull NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, Particle value) { + buffer.write(VAR_INT, value.id()); + value.writeData(buffer); + } + + @Override + public Particle read(@NotNull NetworkBuffer buffer) { + final int id = buffer.read(VAR_INT); + final Particle particle = Objects.requireNonNull(fromId(id), () -> "unknown particle id: " + id); + return particle.readData(buffer); + } + }; static @NotNull Collection<@NotNull Particle> values() { return ParticleImpl.values(); @@ -26,6 +57,424 @@ public sealed interface Particle extends StaticProtocolObject, Particles permits return ParticleImpl.getId(id); } - @NotNull Particle withData(@Nullable ParticleData data); - @Nullable ParticleData data(); + @NotNull Particle readData(@NotNull NetworkBuffer reader); + + void writeData(@NotNull NetworkBuffer writer); + + @NotNull CompoundBinaryTag toNbt(); + + record Simple(@NotNull NamespaceID namespace, int id) implements Particle { + @Override + public @NotNull Particle readData(@NotNull NetworkBuffer reader) { + return this; + } + + @Override + public void writeData(@NotNull NetworkBuffer writer) { + } + + @Override + public @NotNull CompoundBinaryTag toNbt() { + return CompoundBinaryTag.builder() + .putString("type", namespace.asString()) + .build(); + } + } + + record Block(@NotNull NamespaceID namespace, int id, @NotNull net.minestom.server.instance.block.Block block) implements Particle { + + @Contract(pure = true) + public @NotNull Block withBlock(@NotNull net.minestom.server.instance.block.Block block) { + return new Block(namespace(), id(), block); + } + + @Override + public @NotNull Block readData(@NotNull NetworkBuffer reader) { + short blockState = reader.read(NetworkBuffer.VAR_INT).shortValue(); + var block = net.minestom.server.instance.block.Block.fromStateId(blockState); + Check.stateCondition(block == null, "Block state " + blockState + " is invalid"); + return this.withBlock(block); + } + + @Override + public void writeData(@NotNull NetworkBuffer writer) { + writer.write(NetworkBuffer.VAR_INT, (int) block.stateId()); + } + + @Override + public @NotNull CompoundBinaryTag toNbt() { + throw new UnsupportedOperationException("Block particle cannot be serialized to NBT"); + } + } + + record BlockMarker(@NotNull NamespaceID namespace, int id, @NotNull net.minestom.server.instance.block.Block block) implements Particle { + + @Contract(pure = true) + public @NotNull BlockMarker withBlock(@NotNull net.minestom.server.instance.block.Block block) { + return new BlockMarker(namespace(), id(), block); + } + + @Override + public @NotNull BlockMarker readData(@NotNull NetworkBuffer reader) { + short blockState = reader.read(NetworkBuffer.VAR_INT).shortValue(); + var block = net.minestom.server.instance.block.Block.fromStateId(blockState); + Check.stateCondition(block == null, "Block state " + blockState + " is invalid"); + return this.withBlock(block); + } + + @Override + public void writeData(@NotNull NetworkBuffer writer) { + writer.write(NetworkBuffer.VAR_INT, (int) block.stateId()); + } + + @Override + public @NotNull CompoundBinaryTag toNbt() { + throw new UnsupportedOperationException("BlockMarker particle cannot be serialized to NBT"); + } + + } + + record Dust(@NotNull NamespaceID namespace, int id, @NotNull RGBLike color, float scale) implements Particle { + + @Contract (pure = true) + public @NotNull Dust withProperties(@NotNull RGBLike color, float scale) { + return new Dust(namespace(), id(), color, scale); + } + + @Contract(pure = true) + public @NotNull Dust withColor(@NotNull RGBLike color) { + return this.withProperties(color, scale); + } + + @Contract(pure = true) + public @NotNull Dust withScale(float scale) { + return this.withProperties(color, scale); + } + + @Override + public @NotNull Dust readData(@NotNull NetworkBuffer reader) { + return this.withProperties(new Color( + (int) (reader.read(NetworkBuffer.FLOAT) * 255), + (int) (reader.read(NetworkBuffer.FLOAT) * 255), + (int) (reader.read(NetworkBuffer.FLOAT) * 255) + ), reader.read(NetworkBuffer.FLOAT)); + } + + @Override + public void writeData(@NotNull NetworkBuffer writer) { + writer.write(NetworkBuffer.FLOAT, color.red() / 255f); + writer.write(NetworkBuffer.FLOAT, color.green() / 255f); + writer.write(NetworkBuffer.FLOAT, color.blue() / 255f); + writer.write(NetworkBuffer.FLOAT, scale); + } + + @Override + public @NotNull CompoundBinaryTag toNbt() { + ListBinaryTag colorTag = ListBinaryTag.builder(BinaryTagTypes.FLOAT) + .add(FloatBinaryTag.floatBinaryTag(color.red() / 255f)) + .add(FloatBinaryTag.floatBinaryTag(color.green() / 255f)) + .add(FloatBinaryTag.floatBinaryTag(color.blue() / 255f)) + .build(); + + return CompoundBinaryTag.builder() + .putString("type", namespace.asString()) + .put("color", colorTag) + .putFloat("scale", scale) + .build(); + } + } + + record DustColorTransition(@NotNull NamespaceID namespace, int id, @NotNull RGBLike color, @NotNull RGBLike transitionColor, float scale) implements Particle { + + @Contract (pure = true) + public @NotNull DustColorTransition withProperties(@NotNull RGBLike color, @NotNull RGBLike transitionColor, float scale) { + return new DustColorTransition(namespace, id, color, transitionColor, scale); + } + + @Contract(pure = true) + public @NotNull DustColorTransition withColor(@NotNull RGBLike color) { + return this.withProperties(color, transitionColor, scale); + } + + @Contract(pure = true) + public @NotNull DustColorTransition withScale(float scale) { + return this.withProperties(color, transitionColor, scale); + } + + @Contract(pure = true) + public @NotNull DustColorTransition withTransitionColor(@NotNull RGBLike transitionColor) { + return this.withProperties(color, transitionColor, scale); + } + + @Override + public @NotNull DustColorTransition readData(@NotNull NetworkBuffer reader) { + return this.withProperties(new Color( + (int) (reader.read(NetworkBuffer.FLOAT) * 255), + (int) (reader.read(NetworkBuffer.FLOAT) * 255), + (int) (reader.read(NetworkBuffer.FLOAT) * 255) + ), new Color( + (int) (reader.read(NetworkBuffer.FLOAT) * 255), + (int) (reader.read(NetworkBuffer.FLOAT) * 255), + (int) (reader.read(NetworkBuffer.FLOAT) * 255) + ), reader.read(NetworkBuffer.FLOAT)); + } + + @Override + public void writeData(@NotNull NetworkBuffer writer) { + writer.write(NetworkBuffer.FLOAT, color.red() / 255f); + writer.write(NetworkBuffer.FLOAT, color.green() / 255f); + writer.write(NetworkBuffer.FLOAT, color.blue() / 255f); + writer.write(NetworkBuffer.FLOAT, transitionColor.red() / 255f); + writer.write(NetworkBuffer.FLOAT, transitionColor.green() / 255f); + writer.write(NetworkBuffer.FLOAT, transitionColor.blue() / 255f); + writer.write(NetworkBuffer.FLOAT, scale); + } + + @Override + public @NotNull CompoundBinaryTag toNbt() { + ListBinaryTag fromColorTag = ListBinaryTag.builder(BinaryTagTypes.FLOAT) + .add(FloatBinaryTag.floatBinaryTag(color.red() / 255f)) + .add(FloatBinaryTag.floatBinaryTag(color.green() / 255f)) + .add(FloatBinaryTag.floatBinaryTag(color.blue() / 255f)) + .build(); + + ListBinaryTag toColorTag = ListBinaryTag.builder(BinaryTagTypes.FLOAT) + .add(FloatBinaryTag.floatBinaryTag(transitionColor.red() / 255f)) + .add(FloatBinaryTag.floatBinaryTag(transitionColor.green() / 255f)) + .add(FloatBinaryTag.floatBinaryTag(transitionColor.blue() / 255f)) + .build(); + + return CompoundBinaryTag.builder() + .putString("type", namespace.asString()) + .putFloat("scale", scale) + .put("from_color", fromColorTag) + .put("to_color", toColorTag) + .build(); + } + } + + record DustPillar(@NotNull NamespaceID namespace, int id, @NotNull net.minestom.server.instance.block.Block block) implements Particle { + + @Contract(pure = true) + public @NotNull DustPillar withBlock(@NotNull net.minestom.server.instance.block.Block block) { + return new DustPillar(namespace(), id(), block); + } + + @Override + public @NotNull DustPillar readData(@NotNull NetworkBuffer reader) { + short blockState = reader.read(NetworkBuffer.VAR_INT).shortValue(); + var block = net.minestom.server.instance.block.Block.fromStateId(blockState); + Check.stateCondition(block == null, "Block state " + blockState + " is invalid"); + return this.withBlock(block); + } + + @Override + public void writeData(@NotNull NetworkBuffer writer) { + writer.write(NetworkBuffer.VAR_INT, (int) block.stateId()); + } + + @Override + public @NotNull CompoundBinaryTag toNbt() { + throw new UnsupportedOperationException("DustPillar particle cannot be serialized to NBT"); + } + + } + + record FallingDust(@NotNull NamespaceID namespace, int id, @NotNull net.minestom.server.instance.block.Block block) implements Particle { + + @Contract(pure = true) + public @NotNull FallingDust withBlock(@NotNull net.minestom.server.instance.block.Block block) { + return new FallingDust(namespace(), id(), block); + } + + @Override + public @NotNull FallingDust readData(@NotNull NetworkBuffer reader) { + short blockState = reader.read(NetworkBuffer.VAR_INT).shortValue(); + var block = net.minestom.server.instance.block.Block.fromStateId(blockState); + Check.stateCondition(block == null, "Block state " + blockState + " is invalid"); + return this.withBlock(block); + } + + @Override + public void writeData(@NotNull NetworkBuffer writer) { + writer.write(NetworkBuffer.VAR_INT, (int) block.stateId()); + } + + @Override + public @NotNull CompoundBinaryTag toNbt() { + throw new UnsupportedOperationException("FallingDust particle cannot be serialized to NBT"); + } + + } + + record Item(@NotNull NamespaceID namespace, int id, @NotNull ItemStack item) implements Particle { + + @Contract(pure = true) + public @NotNull Item withItem(@NotNull ItemStack item) { + return new Item(namespace(), id(), item); + } + + @Override + public @NotNull Item readData(@NotNull NetworkBuffer reader) { + return this.withItem(reader.read(ItemStack.NETWORK_TYPE)); + } + + @Override + public void writeData(@NotNull NetworkBuffer writer) { + writer.write(ItemStack.NETWORK_TYPE, item); + } + + @Override + public @NotNull CompoundBinaryTag toNbt() { + return CompoundBinaryTag.builder() + .putString("type", namespace.asString()) + .put("item", item.toItemNBT()) + .build(); + } + } + + record EntityEffect(@NotNull NamespaceID namespace, int id, @NotNull AlphaColor color) implements Particle { + + @Contract(pure = true) + public @NotNull EntityEffect withColor(@NotNull AlphaColor color) { + return new EntityEffect(namespace(), id(), color); + } + + @Contract(pure = true) + public @NotNull EntityEffect withColor(@NotNull RGBLike color) { + return new EntityEffect(namespace(), id(), new AlphaColor(1, color)); + } + + @Contract(pure = true) + public @NotNull EntityEffect withColor(int alpha, @NotNull RGBLike color) { + return new EntityEffect(namespace(), id(), new AlphaColor(alpha, color)); + } + + @Override + public @NotNull EntityEffect readData(@NotNull NetworkBuffer reader) { + return withColor(reader.read(AlphaColor.NETWORK_TYPE)); + } + + @Override + public void writeData(@NotNull NetworkBuffer writer) { + writer.write(AlphaColor.NETWORK_TYPE, color); + } + + @Override + public @NotNull CompoundBinaryTag toNbt() { + int color = (0xFF << 24) | (color().red() << 16) | (color().green() << 8) | color().blue(); + + return CompoundBinaryTag.builder() + .putString("type", namespace.asString()) + .putInt("color", color) + .build(); + } + } + + record SculkCharge(@NotNull NamespaceID namespace, int id, float roll) implements Particle { + + @Contract(pure = true) + public @NotNull SculkCharge withRoll(float roll) { + return new SculkCharge(namespace(), id(), roll); + } + + @Override + public @NotNull SculkCharge readData(@NotNull NetworkBuffer reader) { + return this.withRoll(reader.read(NetworkBuffer.FLOAT)); + } + + @Override + public void writeData(@NotNull NetworkBuffer writer) { + writer.write(NetworkBuffer.FLOAT, roll); + + } + + @Override + public @NotNull CompoundBinaryTag toNbt() { + return CompoundBinaryTag.builder() + .putString("type", namespace.asString()) + .putFloat("roll", roll) + .build(); + } + } + + record Shriek(@NotNull NamespaceID namespace, int id, int delay) implements Particle { + + @Contract(pure = true) + public @NotNull Shriek withDelay(int delay) { + return new Shriek(namespace(), id(), delay); + } + + @Override + public @NotNull Shriek readData(@NotNull NetworkBuffer reader) { + return this.withDelay(reader.read(NetworkBuffer.VAR_INT)); + } + + @Override + public void writeData(@NotNull NetworkBuffer writer) { + writer.write(NetworkBuffer.VAR_INT, delay); + } + + @Override + public @NotNull CompoundBinaryTag toNbt() { + return CompoundBinaryTag.builder() + .putString("type", namespace.asString()) + .putInt("delay", delay) + .build(); + } + } + + record Vibration(@NotNull NamespaceID namespace, int id, @NotNull SourceType sourceType, @Nullable Point sourceBlockPosition, int sourceEntityId, float sourceEntityEyeHeight, int travelTicks) implements Particle { + + @Contract(pure = true) + public @NotNull Vibration withProperties(@NotNull SourceType sourceType, @Nullable Point sourceBlockPosition, + int sourceEntityId, float sourceEntityEyeHeight, int travelTicks) { + return new Vibration(namespace(), id(), sourceType, sourceBlockPosition, sourceEntityId, sourceEntityEyeHeight, travelTicks); + } + + @Contract(pure = true) + public @NotNull Vibration withSourceBlockPosition(@Nullable Point sourceBlockPosition, int travelTicks) { + return new Vibration(namespace(), id(), SourceType.BLOCK, sourceBlockPosition, sourceEntityId, sourceEntityEyeHeight, travelTicks); + } + + @Contract(pure = true) + public @NotNull Vibration withSourceEntity(int sourceEntityId, float sourceEntityEyeHeight, int travelTicks) { + return new Vibration(namespace(), id(), SourceType.ENTITY, sourceBlockPosition, sourceEntityId, sourceEntityEyeHeight, travelTicks); + } + + @Override + public @NotNull Vibration readData(@NotNull NetworkBuffer reader) { + SourceType type = reader.readEnum(SourceType.class); + if (type == SourceType.BLOCK) { + return this.withSourceBlockPosition(reader.read(NetworkBuffer.BLOCK_POSITION), reader.read(NetworkBuffer.VAR_INT)); + } else { + return this.withSourceEntity(reader.read(NetworkBuffer.VAR_INT), reader.read(NetworkBuffer.FLOAT), reader.read(NetworkBuffer.VAR_INT)); + } + } + + @Override + public void writeData(@NotNull NetworkBuffer writer) { + writer.writeEnum(SourceType.class, sourceType); + if (sourceType == SourceType.BLOCK) { + Objects.requireNonNull(sourceBlockPosition); + writer.write(NetworkBuffer.BLOCK_POSITION, sourceBlockPosition); + writer.write(NetworkBuffer.VAR_INT, travelTicks); + } else { + writer.write(NetworkBuffer.VAR_INT, sourceEntityId); + writer.write(NetworkBuffer.FLOAT, sourceEntityEyeHeight); + writer.write(NetworkBuffer.VAR_INT, travelTicks); + } + + } + + @Override + public @NotNull CompoundBinaryTag toNbt() { + throw new UnsupportedOperationException("Vibration particle cannot be serialized to NBT"); + } + + public enum SourceType { + BLOCK, ENTITY + } + } + } diff --git a/src/main/java/net/minestom/server/particle/ParticleImpl.java b/src/main/java/net/minestom/server/particle/ParticleImpl.java index 344a5ff6086..62e85aa3271 100644 --- a/src/main/java/net/minestom/server/particle/ParticleImpl.java +++ b/src/main/java/net/minestom/server/particle/ParticleImpl.java @@ -1,19 +1,19 @@ package net.minestom.server.particle; -import net.minestom.server.particle.data.ParticleData; +import net.minestom.server.color.AlphaColor; +import net.minestom.server.color.Color; +import net.minestom.server.coordinate.Vec; +import net.minestom.server.instance.block.Block; +import net.minestom.server.item.ItemStack; 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; -record ParticleImpl(NamespaceID namespace, int id, ParticleData data) implements Particle { - private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.PARTICLES, ParticleImpl::createImpl); - - private static Particle createImpl(String namespace, Registry.Properties properties) { - return new ParticleImpl(NamespaceID.from(namespace), properties.getInt("id"), ParticleData.defaultData(namespace)); - } +final class ParticleImpl { + private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.PARTICLES, + (namespace, properties) -> defaultParticle(NamespaceID.from(namespace), properties.getInt("id"))); static Particle get(@NotNull String namespace) { return CONTAINER.get(namespace); @@ -31,17 +31,24 @@ static Collection values() { return CONTAINER.values(); } - public @NotNull Particle withData(@Nullable ParticleData object) { - return new ParticleImpl(namespace, id, object); - } - - @Override - public @Nullable ParticleData data() { - return data; - } - - @Override - public @NotNull String toString() { - return name(); + private static Particle defaultParticle(@NotNull NamespaceID namespace, int id) { + return switch (namespace.asString()) { + case "minecraft:block" -> new Particle.Block(namespace, id, Block.STONE); + case "minecraft:block_marker" -> new Particle.BlockMarker(namespace, id, Block.STONE); + case "minecraft:falling_dust" -> new Particle.FallingDust(namespace, id, Block.STONE); + case "minecraft:dust_pillar" -> new Particle.DustPillar(namespace, id, Block.STONE); + case "minecraft:dust" -> new Particle.Dust(namespace, id, new Color(255, 255, 255), 1); + case "minecraft:dust_color_transition" -> new Particle.DustColorTransition(namespace, id, new Color(255, 255, 255), + new Color(255, 255, 255), 1); + case "minecraft:sculk_charge" -> new Particle.SculkCharge(namespace, id, 0); + case "minecraft:item" -> new Particle.Item(namespace, id, ItemStack.AIR); + case "minecraft:vibration" -> new Particle.Vibration(namespace, id, Particle.Vibration.SourceType.BLOCK, Vec.ZERO, 0, 0, 0); + case "minecraft:shriek" -> new Particle.Shriek(namespace, id, 0); + case "minecraft:entity_effect" -> new Particle.EntityEffect(namespace, id, new AlphaColor(255, 0, 0, 0)); + default -> new Particle.Simple(namespace, id); + }; + } + + private ParticleImpl() { } } diff --git a/src/main/java/net/minestom/server/particle/data/BlockMarkerParticleData.java b/src/main/java/net/minestom/server/particle/data/BlockMarkerParticleData.java deleted file mode 100644 index e64bb0bd797..00000000000 --- a/src/main/java/net/minestom/server/particle/data/BlockMarkerParticleData.java +++ /dev/null @@ -1,34 +0,0 @@ -package net.minestom.server.particle.data; - -import net.minestom.server.instance.block.Block; -import net.minestom.server.network.NetworkBuffer; -import net.minestom.server.particle.Particle; -import net.minestom.server.utils.validate.Check; -import org.jetbrains.annotations.NotNull; - -public record BlockMarkerParticleData(@NotNull Block block) implements ParticleData { - BlockMarkerParticleData(NetworkBuffer reader) { - this(read(reader)); - } - - BlockMarkerParticleData() { - this(Block.STONE); - } - - private static Block read(NetworkBuffer reader) { - short blockState = reader.read(NetworkBuffer.VAR_INT).shortValue(); - Block block = Block.fromStateId(blockState); - Check.stateCondition(block == null, "Block state " + blockState + " is invalid"); - return block; - } - - @Override - public void write(@NotNull NetworkBuffer writer) { - writer.write(NetworkBuffer.VAR_INT, (int) block.stateId()); - } - - @Override - public boolean validate(int particleId) { - return particleId == Particle.BLOCK_MARKER.id(); - } -} diff --git a/src/main/java/net/minestom/server/particle/data/BlockParticleData.java b/src/main/java/net/minestom/server/particle/data/BlockParticleData.java deleted file mode 100644 index 83a414948b6..00000000000 --- a/src/main/java/net/minestom/server/particle/data/BlockParticleData.java +++ /dev/null @@ -1,34 +0,0 @@ -package net.minestom.server.particle.data; - -import net.minestom.server.instance.block.Block; -import net.minestom.server.network.NetworkBuffer; -import net.minestom.server.particle.Particle; -import net.minestom.server.utils.validate.Check; -import org.jetbrains.annotations.NotNull; - -public record BlockParticleData(Block block) implements ParticleData { - BlockParticleData(NetworkBuffer reader) { - this(read(reader)); - } - - BlockParticleData() { - this(Block.STONE); - } - - private static Block read(NetworkBuffer reader) { - short blockState = reader.read(NetworkBuffer.VAR_INT).shortValue(); - Block block = Block.fromStateId(blockState); - Check.stateCondition(block == null, "Block state " + blockState + " is invalid"); - return block; - } - - @Override - public void write(@NotNull NetworkBuffer writer) { - writer.write(NetworkBuffer.VAR_INT, (int) block.stateId()); - } - - @Override - public boolean validate(int particleId) { - return particleId == Particle.BLOCK.id(); - } -} diff --git a/src/main/java/net/minestom/server/particle/data/DustColorTransitionParticleData.java b/src/main/java/net/minestom/server/particle/data/DustColorTransitionParticleData.java deleted file mode 100644 index 75a8eee45e4..00000000000 --- a/src/main/java/net/minestom/server/particle/data/DustColorTransitionParticleData.java +++ /dev/null @@ -1,46 +0,0 @@ -package net.minestom.server.particle.data; - -import net.kyori.adventure.util.RGBLike; -import net.minestom.server.color.Color; -import net.minestom.server.network.NetworkBuffer; -import net.minestom.server.particle.Particle; -import net.minestom.server.utils.validate.Check; -import org.jetbrains.annotations.NotNull; - -public record DustColorTransitionParticleData(@NotNull RGBLike from, float scale, @NotNull RGBLike to) implements ParticleData { - public DustColorTransitionParticleData { - Check.argCondition(scale < 0.01 || scale > 4, "scale must be between 0.01 and 4: was {0}", scale); - } - - DustColorTransitionParticleData() { - this(new Color(255, 255, 255), 1, new Color(255, 255, 255)); - } - - DustColorTransitionParticleData(NetworkBuffer buffer) { - this(new Color( - (int) (buffer.read(NetworkBuffer.FLOAT) * 255), - (int) (buffer.read(NetworkBuffer.FLOAT) * 255), - (int) (buffer.read(NetworkBuffer.FLOAT) * 255) - ), buffer.read(NetworkBuffer.FLOAT), new Color( - (int) (buffer.read(NetworkBuffer.FLOAT) * 255), - (int) (buffer.read(NetworkBuffer.FLOAT) * 255), - (int) (buffer.read(NetworkBuffer.FLOAT) * 255) - )); - } - - @Override - public void write(@NotNull NetworkBuffer writer) { - writer.write(NetworkBuffer.FLOAT, from.red() / 255f); - writer.write(NetworkBuffer.FLOAT, from.green() / 255f); - writer.write(NetworkBuffer.FLOAT, from.blue() / 255f); - writer.write(NetworkBuffer.FLOAT, scale); - writer.write(NetworkBuffer.FLOAT, to.red() / 255f); - writer.write(NetworkBuffer.FLOAT, to.green() / 255f); - writer.write(NetworkBuffer.FLOAT, to.blue() / 255f); - } - - @Override - public boolean validate(int particleId) { - return particleId == Particle.DUST_COLOR_TRANSITION.id(); - } -} diff --git a/src/main/java/net/minestom/server/particle/data/DustParticleData.java b/src/main/java/net/minestom/server/particle/data/DustParticleData.java deleted file mode 100644 index 8fb19b7019d..00000000000 --- a/src/main/java/net/minestom/server/particle/data/DustParticleData.java +++ /dev/null @@ -1,39 +0,0 @@ -package net.minestom.server.particle.data; - -import net.kyori.adventure.util.RGBLike; -import net.minestom.server.color.Color; -import net.minestom.server.network.NetworkBuffer; -import net.minestom.server.particle.Particle; -import net.minestom.server.utils.validate.Check; -import org.jetbrains.annotations.NotNull; - -public record DustParticleData(@NotNull RGBLike color, float scale) implements ParticleData { - public DustParticleData { - Check.argCondition(scale < 0.01 || scale > 4, "scale must be between 0.01 and 4"); - } - - DustParticleData(NetworkBuffer buffer) { - this(new Color( - (int) (buffer.read(NetworkBuffer.FLOAT) * 255), - (int) (buffer.read(NetworkBuffer.FLOAT) * 255), - (int) (buffer.read(NetworkBuffer.FLOAT) * 255) - ), buffer.read(NetworkBuffer.FLOAT)); - } - - DustParticleData() { - this(new Color(255, 255, 255), 1); - } - - @Override - public void write(@NotNull NetworkBuffer writer) { - writer.write(NetworkBuffer.FLOAT, color.red() / 255f); - writer.write(NetworkBuffer.FLOAT, color.green() / 255f); - writer.write(NetworkBuffer.FLOAT, color.blue() / 255f); - writer.write(NetworkBuffer.FLOAT, scale); - } - - @Override - public boolean validate(int particleId) { - return particleId == Particle.DUST.id(); - } -} diff --git a/src/main/java/net/minestom/server/particle/data/FallingDustParticleData.java b/src/main/java/net/minestom/server/particle/data/FallingDustParticleData.java deleted file mode 100644 index 831b1f0dc8a..00000000000 --- a/src/main/java/net/minestom/server/particle/data/FallingDustParticleData.java +++ /dev/null @@ -1,34 +0,0 @@ -package net.minestom.server.particle.data; - -import net.minestom.server.instance.block.Block; -import net.minestom.server.network.NetworkBuffer; -import net.minestom.server.particle.Particle; -import net.minestom.server.utils.validate.Check; -import org.jetbrains.annotations.NotNull; - -public record FallingDustParticleData(Block block) implements ParticleData { - FallingDustParticleData(NetworkBuffer reader) { - this(read(reader)); - } - - FallingDustParticleData() { - this(Block.STONE); - } - - private static Block read(NetworkBuffer reader) { - short blockState = reader.read(NetworkBuffer.VAR_INT).shortValue(); - Block block = Block.fromStateId(blockState); - Check.stateCondition(block == null, "Block state {0} is invalid", blockState); - return block; - } - - @Override - public void write(@NotNull NetworkBuffer writer) { - writer.write(NetworkBuffer.VAR_INT, (int) block.stateId()); - } - - @Override - public boolean validate(int particleId) { - return particleId == Particle.FALLING_DUST.id(); - } -} diff --git a/src/main/java/net/minestom/server/particle/data/ItemParticleData.java b/src/main/java/net/minestom/server/particle/data/ItemParticleData.java deleted file mode 100644 index 58ddfb0d3ae..00000000000 --- a/src/main/java/net/minestom/server/particle/data/ItemParticleData.java +++ /dev/null @@ -1,27 +0,0 @@ -package net.minestom.server.particle.data; - -import net.minestom.server.item.ItemStack; -import net.minestom.server.item.Material; -import net.minestom.server.network.NetworkBuffer; -import net.minestom.server.particle.Particle; -import org.jetbrains.annotations.NotNull; - -public record ItemParticleData(ItemStack item) implements ParticleData { - ItemParticleData(NetworkBuffer reader) { - this(reader.read(NetworkBuffer.ITEM)); - } - - ItemParticleData() { - this(ItemStack.of(Material.STONE)); - } - - @Override - public void write(@NotNull NetworkBuffer writer) { - writer.write(NetworkBuffer.ITEM, item); - } - - @Override - public boolean validate(int particleId) { - return particleId == Particle.ITEM.id(); - } -} diff --git a/src/main/java/net/minestom/server/particle/data/ParticleData.java b/src/main/java/net/minestom/server/particle/data/ParticleData.java deleted file mode 100644 index 4feb802a12b..00000000000 --- a/src/main/java/net/minestom/server/particle/data/ParticleData.java +++ /dev/null @@ -1,51 +0,0 @@ -package net.minestom.server.particle.data; - -import net.minestom.server.network.NetworkBuffer; -import net.minestom.server.particle.Particle; -import org.jetbrains.annotations.NotNull; - -public interface ParticleData { - void write(@NotNull NetworkBuffer writer); - - static ParticleData read(int particleId, NetworkBuffer reader) { - if (particleId == Particle.BLOCK.id()) return new BlockParticleData(reader); - else if (particleId == Particle.BLOCK_MARKER.id()) return new BlockMarkerParticleData(reader); - else if (particleId == Particle.DUST.id()) return new DustParticleData(reader); - else if (particleId == Particle.DUST_COLOR_TRANSITION.id()) return new DustColorTransitionParticleData(reader); - else if (particleId == Particle.FALLING_DUST.id()) return new FallingDustParticleData(reader); - else if (particleId == Particle.SCULK_CHARGE.id()) return new SculkChargeParticleData(reader); - else if (particleId == Particle.ITEM.id()) return new ItemParticleData(reader); - else if (particleId == Particle.VIBRATION.id()) return new VibrationParticleData(reader); - else if (particleId == Particle.SHRIEK.id()) return new ShriekParticleData(reader); - else return null; - } - - boolean validate(int particleId); - - static boolean requiresData(int particleId) { - return particleId == Particle.BLOCK.id() - || particleId == Particle.BLOCK_MARKER.id() - || particleId == Particle.DUST.id() - || particleId == Particle.DUST_COLOR_TRANSITION.id() - || particleId == Particle.FALLING_DUST.id() - || particleId == Particle.SCULK_CHARGE.id() - || particleId == Particle.ITEM.id() - || particleId == Particle.VIBRATION.id() - || particleId == Particle.SHRIEK.id(); - } - - static ParticleData defaultData(String id) { - return switch (id) { - case "minecraft:block" -> new BlockParticleData(); - case "minecraft:block_marker" -> new BlockMarkerParticleData(); - case "minecraft:dust" -> new DustParticleData(); - case "minecraft:dust_color_transition" -> new DustColorTransitionParticleData(); - case "minecraft:falling_dust" -> new FallingDustParticleData(); - case "minecraft:sculk_charge" -> new SculkChargeParticleData(); - case "minecraft:item" -> new ItemParticleData(); - case "minecraft:vibration" -> new VibrationParticleData(); - case "minecraft:shriek" -> new ShriekParticleData(); - default -> null; - }; - } -} diff --git a/src/main/java/net/minestom/server/particle/data/SculkChargeParticleData.java b/src/main/java/net/minestom/server/particle/data/SculkChargeParticleData.java deleted file mode 100644 index 706a2486200..00000000000 --- a/src/main/java/net/minestom/server/particle/data/SculkChargeParticleData.java +++ /dev/null @@ -1,25 +0,0 @@ -package net.minestom.server.particle.data; - -import net.minestom.server.network.NetworkBuffer; -import net.minestom.server.particle.Particle; -import org.jetbrains.annotations.NotNull; - -public record SculkChargeParticleData(float roll) implements ParticleData { - SculkChargeParticleData(NetworkBuffer reader) { - this(reader.read(NetworkBuffer.FLOAT)); - } - - SculkChargeParticleData() { - this(0); - } - - @Override - public void write(@NotNull NetworkBuffer writer) { - writer.write(NetworkBuffer.FLOAT, roll); - } - - @Override - public boolean validate(int particleId) { - return particleId == Particle.SCULK_CHARGE.id(); - } -} diff --git a/src/main/java/net/minestom/server/particle/data/ShriekParticleData.java b/src/main/java/net/minestom/server/particle/data/ShriekParticleData.java deleted file mode 100644 index 518e7acfeb1..00000000000 --- a/src/main/java/net/minestom/server/particle/data/ShriekParticleData.java +++ /dev/null @@ -1,25 +0,0 @@ -package net.minestom.server.particle.data; - -import net.minestom.server.network.NetworkBuffer; -import net.minestom.server.particle.Particle; -import org.jetbrains.annotations.NotNull; - -public record ShriekParticleData(int delay) implements ParticleData { - ShriekParticleData(NetworkBuffer reader) { - this(reader.read(NetworkBuffer.VAR_INT)); - } - - ShriekParticleData() { - this(0); - } - - @Override - public void write(@NotNull NetworkBuffer writer) { - writer.write(NetworkBuffer.VAR_INT, delay); - } - - @Override - public boolean validate(int particleId) { - return particleId == Particle.SHRIEK.id(); - } -} diff --git a/src/main/java/net/minestom/server/particle/data/VibrationParticleData.java b/src/main/java/net/minestom/server/particle/data/VibrationParticleData.java deleted file mode 100644 index 6a7b5f324fb..00000000000 --- a/src/main/java/net/minestom/server/particle/data/VibrationParticleData.java +++ /dev/null @@ -1,55 +0,0 @@ -package net.minestom.server.particle.data; - -import net.minestom.server.coordinate.Point; -import net.minestom.server.coordinate.Vec; -import net.minestom.server.network.NetworkBuffer; -import net.minestom.server.particle.Particle; -import org.jetbrains.annotations.NotNull; - -public record VibrationParticleData(@NotNull VibrationSource type, @NotNull Point source, int entityId, float entityEyeHeight, int ticks) implements ParticleData { - public enum VibrationSource { - BLOCK, - ENTITY - } - - VibrationParticleData(NetworkBuffer buffer) { - this(read(buffer)); - } - - VibrationParticleData() { - this(VibrationSource.BLOCK, Vec.ZERO, 0, 0, 0); - } - - private VibrationParticleData(VibrationParticleData copy) { - this(copy.type, copy.source, copy.entityId, copy.entityEyeHeight, copy.ticks); - } - - private static VibrationParticleData read(NetworkBuffer buffer) { - VibrationSource type = buffer.readEnum(VibrationSource.class); - - if (type == VibrationSource.BLOCK) { - return new VibrationParticleData(type, buffer.read(NetworkBuffer.BLOCK_POSITION), 0, 0, buffer.read(NetworkBuffer.VAR_INT)); - } else { - return new VibrationParticleData(type, Vec.ZERO, buffer.read(NetworkBuffer.VAR_INT), buffer.read(NetworkBuffer.FLOAT), buffer.read(NetworkBuffer.VAR_INT)); - } - } - - @Override - public void write(@NotNull NetworkBuffer writer) { - writer.writeEnum(VibrationSource.class, type); - - if (type == VibrationSource.BLOCK) { - writer.write(NetworkBuffer.BLOCK_POSITION, source); - writer.write(NetworkBuffer.VAR_INT, ticks); - } else { - writer.write(NetworkBuffer.VAR_INT, entityId); - writer.write(NetworkBuffer.FLOAT, entityEyeHeight); - writer.write(NetworkBuffer.VAR_INT, ticks); - } - } - - @Override - public boolean validate(int particleId) { - return particleId == Particle.VIBRATION.id(); - } -} diff --git a/src/main/java/net/minestom/server/permission/Permission.java b/src/main/java/net/minestom/server/permission/Permission.java index 1959e256919..1b798757273 100644 --- a/src/main/java/net/minestom/server/permission/Permission.java +++ b/src/main/java/net/minestom/server/permission/Permission.java @@ -1,23 +1,23 @@ package net.minestom.server.permission; +import net.kyori.adventure.nbt.CompoundBinaryTag; import net.minestom.server.command.CommandSender; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; import java.util.Objects; /** * Representation of a permission granted to a {@link CommandSender}. * Each permission has a string representation used as an identifier, and an optional - * {@link NBTCompound} used to store additional data. + * {@link CompoundBinaryTag} used to store additional data. *

* The class is immutable. */ public class Permission { private final String permissionName; - private final NBTCompound data; + private final CompoundBinaryTag data; /** * Creates a new permission object with optional data. @@ -25,7 +25,7 @@ public class Permission { * @param permissionName the name of the permission * @param data the optional data of the permission */ - public Permission(@NotNull String permissionName, @Nullable NBTCompound data) { + public Permission(@NotNull String permissionName, @Nullable CompoundBinaryTag data) { this.permissionName = permissionName; this.data = data; } @@ -55,7 +55,7 @@ public String getPermissionName() { * @return the nbt data of this permission, can be null if not any */ @Nullable - public NBTCompound getNBTData() { + public CompoundBinaryTag getNBTData() { return data; } diff --git a/src/main/java/net/minestom/server/permission/PermissionHandler.java b/src/main/java/net/minestom/server/permission/PermissionHandler.java index 0683e34977d..a4a028a2c7a 100644 --- a/src/main/java/net/minestom/server/permission/PermissionHandler.java +++ b/src/main/java/net/minestom/server/permission/PermissionHandler.java @@ -2,8 +2,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; -import org.jglrxavpok.hephaistos.parser.SNBTParser; import java.util.Set; import java.util.regex.Pattern; @@ -14,8 +12,7 @@ * Permissions are in-memory only by default. * You have however the capacity to store them persistently as the {@link Permission} object * is serializer-friendly, {@link Permission#getPermissionName()} being a {@link String} - * and {@link Permission#getNBTData()} serializable into a string using {@link NBTCompound#toSNBT()} - * and deserialized back with {@link SNBTParser#parse()}. + * and {@link Permission#getNBTData()} serializable into a string using {@link net.kyori.adventure.nbt.TagStringIO}. */ public interface PermissionHandler { diff --git a/src/main/java/net/minestom/server/permission/PermissionVerifier.java b/src/main/java/net/minestom/server/permission/PermissionVerifier.java index 51323b5d887..99797569c19 100644 --- a/src/main/java/net/minestom/server/permission/PermissionVerifier.java +++ b/src/main/java/net/minestom/server/permission/PermissionVerifier.java @@ -1,10 +1,10 @@ package net.minestom.server.permission; +import net.kyori.adventure.nbt.CompoundBinaryTag; import org.jetbrains.annotations.Nullable; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; /** - * Interface used to check if the {@link NBTCompound nbt data} of a {@link Permission} is correct. + * Interface used to check if the {@link CompoundBinaryTag nbt data} of a {@link Permission} is correct. */ @FunctionalInterface public interface PermissionVerifier { @@ -16,5 +16,5 @@ public interface PermissionVerifier { * @return true if {@link PermissionHandler#hasPermission(String, PermissionVerifier)} * should return true, false otherwise */ - boolean isValid(@Nullable NBTCompound nbtCompound); + boolean isValid(@Nullable CompoundBinaryTag nbtCompound); } diff --git a/src/main/java/net/minestom/server/potion/CustomPotionEffect.java b/src/main/java/net/minestom/server/potion/CustomPotionEffect.java index e9ecad664fa..2ac3495cf9f 100644 --- a/src/main/java/net/minestom/server/potion/CustomPotionEffect.java +++ b/src/main/java/net/minestom/server/potion/CustomPotionEffect.java @@ -1,9 +1,120 @@ package net.minestom.server.potion; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + /** - * Represents a custom effect in {@link net.minestom.server.item.metadata.PotionMeta}. + * Represents a custom effect in {@link net.minestom.server.item.ItemComponent#POTION_CONTENTS}. */ -public record CustomPotionEffect(byte id, byte amplifier, int duration, - boolean isAmbient, boolean showParticles, - boolean showIcon) { +public record CustomPotionEffect(@NotNull PotionEffect id, @NotNull Settings settings) { + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, CustomPotionEffect value) { + buffer.write(NetworkBuffer.VAR_INT, value.id.id()); + buffer.write(Settings.NETWORK_TYPE, value.settings); + } + + @Override + public CustomPotionEffect read(@NotNull NetworkBuffer buffer) { + return new CustomPotionEffect(PotionEffect.fromId(buffer.read(NetworkBuffer.VAR_INT)), buffer.read(Settings.NETWORK_TYPE)); + } + }; + + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.lazy(() -> BinaryTagSerializer.COMPOUND.map( + tag -> new CustomPotionEffect( + PotionEffect.fromNamespaceId(tag.getString("id")), + Settings.NBT_TYPE.read(tag)), + value -> CompoundBinaryTag.builder() + .putString("id", value.id.name()) + .put((CompoundBinaryTag) Settings.NBT_TYPE.write(value.settings)) + .build() + )); + + public CustomPotionEffect(@NotNull PotionEffect id, byte amplifier, int duration, boolean isAmbient, boolean showParticles, boolean showIcon) { + this(id, new Settings(amplifier, duration, isAmbient, showParticles, showIcon, null)); + } + + public byte amplifier() { + return settings.amplifier; + } + + public int duration() { + return settings.duration; + } + + public boolean isAmbient() { + return settings.isAmbient; + } + + public boolean showParticles() { + return settings.showParticles; + } + + public boolean showIcon() { + return settings.showIcon; + } + + public record Settings( + byte amplifier, int duration, + boolean isAmbient, boolean showParticles, boolean showIcon, + @Nullable Settings hiddenEffect + ) { + + public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, Settings value) { + buffer.write(NetworkBuffer.VAR_INT, (int) value.amplifier); + buffer.write(NetworkBuffer.VAR_INT, value.duration); + buffer.write(NetworkBuffer.BOOLEAN, value.isAmbient); + buffer.write(NetworkBuffer.BOOLEAN, value.showParticles); + buffer.write(NetworkBuffer.BOOLEAN, value.showIcon); + buffer.writeOptional(NETWORK_TYPE, value.hiddenEffect); + } + + @Override + public Settings read(@NotNull NetworkBuffer buffer) { + return new Settings( + buffer.read(NetworkBuffer.VAR_INT).byteValue(), + buffer.read(NetworkBuffer.VAR_INT), + buffer.read(NetworkBuffer.BOOLEAN), + buffer.read(NetworkBuffer.BOOLEAN), + buffer.read(NetworkBuffer.BOOLEAN), + buffer.readOptional(NETWORK_TYPE) + ); + } + }; + + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.recursive(self -> BinaryTagSerializer.COMPOUND.map( + tag -> { + byte amplifier = tag.getByte("amplifier"); + int duration = tag.getInt("duration"); + boolean ambient = tag.getBoolean("ambient"); + boolean showParticles = tag.getBoolean("show_particles", true); + boolean showIcon = tag.getBoolean("show_icon", showParticles); + Settings hiddenEffect = null; + if (tag.get("hidden_effect") instanceof CompoundBinaryTag hiddenEffectTag) { + hiddenEffect = self.read(hiddenEffectTag); + } + return new Settings(amplifier, duration, ambient, showParticles, showIcon, hiddenEffect); + }, + value -> { + CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder() + .putByte("amplifier", value.amplifier) + .putInt("duration", value.duration) + .putBoolean("ambient", value.isAmbient) + .putBoolean("show_particles", value.showParticles) + .putBoolean("show_icon", value.showIcon); + if (value.hiddenEffect != null) { + builder.put("hidden_effect", self.write(value.hiddenEffect)); + } + return builder.build(); + } + )); + + } + } diff --git a/src/main/java/net/minestom/server/potion/Potion.java b/src/main/java/net/minestom/server/potion/Potion.java index 6ad326aa0ad..4a44186c22e 100644 --- a/src/main/java/net/minestom/server/potion/Potion.java +++ b/src/main/java/net/minestom/server/potion/Potion.java @@ -48,11 +48,23 @@ public record Potion(@NotNull PotionEffect effect, byte amplifier, */ public static final byte ICON_FLAG = 0x04; + /** + * A flag instructing the client to use its builtin blending effect, only used with the darkness effect currently. + */ + public static final byte BLEND_FLAG = 0x08; + /** * A duration constant which sets a Potion duration to infinite. */ public static final int INFINITE_DURATION = -1; + /** + * @see #Potion(PotionEffect, byte, int, byte) + */ + public Potion(@NotNull PotionEffect effect, byte amplifier, int duration, int flags) { + this(effect, amplifier, duration, (byte) flags); + } + /** * Creates a new Potion with no flags. * @@ -106,6 +118,10 @@ public boolean hasIcon() { return (flags & ICON_FLAG) == ICON_FLAG; } + public boolean hasBlend() { + return (flags & BLEND_FLAG) == BLEND_FLAG; + } + /** * Sends a packet that a potion effect has been applied to the entity. *

@@ -114,7 +130,7 @@ public boolean hasIcon() { * @param entity the entity to add the effect to */ public void sendAddPacket(@NotNull Entity entity) { - entity.sendPacketToViewersAndSelf(new EntityEffectPacket(entity.getEntityId(), this, null)); + entity.sendPacketToViewersAndSelf(new EntityEffectPacket(entity.getEntityId(), this)); } /** diff --git a/src/main/java/net/minestom/server/potion/PotionEffect.java b/src/main/java/net/minestom/server/potion/PotionEffect.java index 0fa9778cecd..69da6a5975a 100644 --- a/src/main/java/net/minestom/server/potion/PotionEffect.java +++ b/src/main/java/net/minestom/server/potion/PotionEffect.java @@ -1,7 +1,8 @@ package net.minestom.server.potion; -import net.minestom.server.registry.StaticProtocolObject; +import net.minestom.server.network.NetworkBuffer; import net.minestom.server.registry.Registry; +import net.minestom.server.registry.StaticProtocolObject; import net.minestom.server.utils.NamespaceID; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -11,6 +12,8 @@ public sealed interface PotionEffect extends StaticProtocolObject, PotionEffects permits PotionEffectImpl { + NetworkBuffer.Type NETWORK_TYPE = NetworkBuffer.VAR_INT.map(PotionEffectImpl::getId, PotionEffect::id); + @Contract(pure = true) @NotNull Registry.PotionEffectEntry registry(); diff --git a/src/main/java/net/minestom/server/recipe/BlastingRecipe.java b/src/main/java/net/minestom/server/recipe/BlastingRecipe.java index 21f43342abb..91148daf009 100644 --- a/src/main/java/net/minestom/server/recipe/BlastingRecipe.java +++ b/src/main/java/net/minestom/server/recipe/BlastingRecipe.java @@ -20,7 +20,7 @@ protected BlastingRecipe( float experience, int cookingTime ) { - super(Type.BLASTING, recipeId); + super(RecipeType.BLASTING, recipeId); this.group = group; this.category = category; this.result = result; diff --git a/src/main/java/net/minestom/server/recipe/CampfireCookingRecipe.java b/src/main/java/net/minestom/server/recipe/CampfireCookingRecipe.java index fa8764145a2..7a9a45b3dfc 100644 --- a/src/main/java/net/minestom/server/recipe/CampfireCookingRecipe.java +++ b/src/main/java/net/minestom/server/recipe/CampfireCookingRecipe.java @@ -20,7 +20,7 @@ protected CampfireCookingRecipe( float experience, int cookingTime ) { - super(Type.CAMPFIRE_COOKING, recipeId); + super(RecipeType.CAMPFIRE_COOKING, recipeId); this.group = group; this.category = category; this.result = result; diff --git a/src/main/java/net/minestom/server/recipe/Recipe.java b/src/main/java/net/minestom/server/recipe/Recipe.java index 75246bd53c2..ad183a5d477 100644 --- a/src/main/java/net/minestom/server/recipe/Recipe.java +++ b/src/main/java/net/minestom/server/recipe/Recipe.java @@ -4,36 +4,22 @@ import org.jetbrains.annotations.NotNull; public abstract class Recipe { - protected final Type recipeType; - protected final String recipeId; + protected final RecipeType type; + protected final String id; - protected Recipe(@NotNull Type recipeType, @NotNull String recipeId) { - this.recipeType = recipeType; - this.recipeId = recipeId; + protected Recipe(@NotNull RecipeType type, @NotNull String id) { + this.type = type; + this.id = id; } public abstract boolean shouldShow(@NotNull Player player); @NotNull - public Type getRecipeType() { - return recipeType; + public RecipeType type() { + return type; } - @NotNull - public String getRecipeId() { - return recipeId; - } - - public enum Type { - SHAPELESS, - SHAPED, - SMELTING, - BLASTING, - SMOKING, - CAMPFIRE_COOKING, - STONECUTTING, - SMITHING_TRANSFORM, - SMITHING_TRIM + public @NotNull String id() { + return id; } - } diff --git a/src/main/java/net/minestom/server/recipe/RecipeConversion.java b/src/main/java/net/minestom/server/recipe/RecipeConversion.java index 102f3926b6b..cbc904d8ca4 100644 --- a/src/main/java/net/minestom/server/recipe/RecipeConversion.java +++ b/src/main/java/net/minestom/server/recipe/RecipeConversion.java @@ -6,7 +6,7 @@ final class RecipeConversion { static @NotNull DeclareRecipesPacket.DeclaredShapelessCraftingRecipe shapeless(@NotNull ShapelessRecipe shapelessRecipe) { - return new DeclareRecipesPacket.DeclaredShapelessCraftingRecipe(shapelessRecipe.getRecipeId(), + return new DeclareRecipesPacket.DeclaredShapelessCraftingRecipe(shapelessRecipe.id(), shapelessRecipe.getGroup(), shapelessRecipe.getCategory(), shapelessRecipe.getIngredients(), @@ -14,7 +14,7 @@ final class RecipeConversion { } static @NotNull DeclareRecipesPacket.DeclaredShapedCraftingRecipe shaped(@NotNull ShapedRecipe shapedRecipe) { - return new DeclareRecipesPacket.DeclaredShapedCraftingRecipe(shapedRecipe.getRecipeId(), + return new DeclareRecipesPacket.DeclaredShapedCraftingRecipe(shapedRecipe.id(), shapedRecipe.getGroup(), shapedRecipe.getCategory(), shapedRecipe.getWidth(), @@ -26,7 +26,7 @@ final class RecipeConversion { static @NotNull DeclareRecipesPacket.DeclaredSmeltingRecipe smelting(@NotNull SmeltingRecipe smeltingRecipe) { return new DeclareRecipesPacket.DeclaredSmeltingRecipe( - smeltingRecipe.getRecipeId(), + smeltingRecipe.id(), smeltingRecipe.getGroup(), smeltingRecipe.getCategory(), smeltingRecipe.getIngredient(), @@ -37,7 +37,7 @@ final class RecipeConversion { static @NotNull DeclareRecipesPacket.DeclaredBlastingRecipe blasting(@NotNull BlastingRecipe blastingRecipe) { return new DeclareRecipesPacket.DeclaredBlastingRecipe( - blastingRecipe.getRecipeId(), + blastingRecipe.id(), blastingRecipe.getGroup(), blastingRecipe.getCategory(), blastingRecipe.getIngredient(), @@ -48,7 +48,7 @@ final class RecipeConversion { static @NotNull DeclareRecipesPacket.DeclaredSmokingRecipe smoking(@NotNull SmokingRecipe smokingRecipe) { return new DeclareRecipesPacket.DeclaredSmokingRecipe( - smokingRecipe.getRecipeId(), + smokingRecipe.id(), smokingRecipe.getGroup(), smokingRecipe.getCategory(), smokingRecipe.getIngredient(), @@ -59,7 +59,7 @@ final class RecipeConversion { static @NotNull DeclareRecipesPacket.DeclaredCampfireCookingRecipe campfire(@NotNull CampfireCookingRecipe campfireCookingRecipe) { return new DeclareRecipesPacket.DeclaredCampfireCookingRecipe( - campfireCookingRecipe.getRecipeId(), + campfireCookingRecipe.id(), campfireCookingRecipe.getGroup(), campfireCookingRecipe.getCategory(), campfireCookingRecipe.getIngredient(), @@ -70,7 +70,7 @@ final class RecipeConversion { static @NotNull DeclareRecipesPacket.DeclaredStonecutterRecipe stonecutter(@NotNull StonecutterRecipe stonecuttingRecipe) { return new DeclareRecipesPacket.DeclaredStonecutterRecipe( - stonecuttingRecipe.getRecipeId(), + stonecuttingRecipe.id(), stonecuttingRecipe.getGroup(), stonecuttingRecipe.getIngredient(), stonecuttingRecipe.getResult()); @@ -78,7 +78,7 @@ final class RecipeConversion { static @NotNull DeclareRecipesPacket.DeclaredSmithingTransformRecipe smithingTransform(@NotNull SmithingTransformRecipe smithingTransformRecipe) { return new DeclareRecipesPacket.DeclaredSmithingTransformRecipe( - smithingTransformRecipe.getRecipeId(), + smithingTransformRecipe.id(), smithingTransformRecipe.getTemplate(), smithingTransformRecipe.getBaseIngredient(), smithingTransformRecipe.getAdditionIngredient(), @@ -87,7 +87,7 @@ final class RecipeConversion { static @NotNull DeclareRecipesPacket.DeclaredSmithingTrimRecipe smithingTrim(@NotNull SmithingTrimRecipe smithingTrimRecipe) { return new DeclareRecipesPacket.DeclaredSmithingTrimRecipe( - smithingTrimRecipe.getRecipeId(), + smithingTrimRecipe.id(), smithingTrimRecipe.getTemplate(), smithingTrimRecipe.getBaseIngredient(), smithingTrimRecipe.getAdditionIngredient()); diff --git a/src/main/java/net/minestom/server/recipe/RecipeManager.java b/src/main/java/net/minestom/server/recipe/RecipeManager.java index 3dac248fb00..38b8cc3fd4d 100644 --- a/src/main/java/net/minestom/server/recipe/RecipeManager.java +++ b/src/main/java/net/minestom/server/recipe/RecipeManager.java @@ -45,7 +45,7 @@ public SendablePacket getDeclareRecipesPacket() { private @NotNull DeclareRecipesPacket createDeclareRecipesPacket() { var entries = new ArrayList(); for (var recipe : recipes) { - entries.add(switch (recipe.recipeType) { + entries.add(switch (recipe.type) { case SHAPELESS -> RecipeConversion.shapeless((ShapelessRecipe) recipe); case SHAPED -> RecipeConversion.shaped((ShapedRecipe) recipe); case SMELTING -> RecipeConversion.smelting((SmeltingRecipe) recipe); @@ -55,6 +55,7 @@ public SendablePacket getDeclareRecipesPacket() { case STONECUTTING -> RecipeConversion.stonecutter((StonecutterRecipe) recipe); case SMITHING_TRANSFORM -> RecipeConversion.smithingTransform((SmithingTransformRecipe) recipe); case SMITHING_TRIM -> RecipeConversion.smithingTrim((SmithingTrimRecipe) recipe); + default -> throw new IllegalStateException("Unhandled recipe type : " + recipe.type); }); } return new DeclareRecipesPacket(entries); diff --git a/src/main/java/net/minestom/server/recipe/ShapedRecipe.java b/src/main/java/net/minestom/server/recipe/ShapedRecipe.java index 5fb9ce2951c..7e195ffaa1d 100644 --- a/src/main/java/net/minestom/server/recipe/ShapedRecipe.java +++ b/src/main/java/net/minestom/server/recipe/ShapedRecipe.java @@ -25,7 +25,7 @@ protected ShapedRecipe(@NotNull String recipeId, @NotNull RecipeCategory.Crafting category, @Nullable List ingredients, @NotNull ItemStack result, boolean showNotification) { - super(Type.SHAPED, recipeId); + super(RecipeType.SHAPED, recipeId); this.width = width; this.height = height; this.group = group; diff --git a/src/main/java/net/minestom/server/recipe/ShapelessRecipe.java b/src/main/java/net/minestom/server/recipe/ShapelessRecipe.java index 343b74ecef3..3910293444e 100644 --- a/src/main/java/net/minestom/server/recipe/ShapelessRecipe.java +++ b/src/main/java/net/minestom/server/recipe/ShapelessRecipe.java @@ -22,7 +22,7 @@ protected ShapelessRecipe( @Nullable List ingredients, @NotNull ItemStack result ) { - super(Type.SHAPELESS, recipeId); + super(RecipeType.SHAPELESS, recipeId); this.group = group; this.category = category; this.ingredients = Objects.requireNonNullElseGet(ingredients, LinkedList::new); diff --git a/src/main/java/net/minestom/server/recipe/SmeltingRecipe.java b/src/main/java/net/minestom/server/recipe/SmeltingRecipe.java index c01aaee524a..35a0a106195 100644 --- a/src/main/java/net/minestom/server/recipe/SmeltingRecipe.java +++ b/src/main/java/net/minestom/server/recipe/SmeltingRecipe.java @@ -20,7 +20,7 @@ protected SmeltingRecipe( float experience, int cookingTime ) { - super(Type.SMELTING, recipeId); + super(RecipeType.SMELTING, recipeId); this.group = group; this.category = category; this.result = result; diff --git a/src/main/java/net/minestom/server/recipe/SmithingTransformRecipe.java b/src/main/java/net/minestom/server/recipe/SmithingTransformRecipe.java index b5eecf824e1..b1ae9bdff65 100644 --- a/src/main/java/net/minestom/server/recipe/SmithingTransformRecipe.java +++ b/src/main/java/net/minestom/server/recipe/SmithingTransformRecipe.java @@ -17,7 +17,7 @@ protected SmithingTransformRecipe( @NotNull DeclareRecipesPacket.Ingredient additionIngredient, @NotNull ItemStack result ) { - super(Type.SMITHING_TRANSFORM, recipeId); + super(RecipeType.SMITHING_TRANSFORM, recipeId); this.template = template; this.baseIngredient = baseIngredient; this.additionIngredient = additionIngredient; diff --git a/src/main/java/net/minestom/server/recipe/SmithingTrimRecipe.java b/src/main/java/net/minestom/server/recipe/SmithingTrimRecipe.java index f1b61dec93e..1259392a762 100644 --- a/src/main/java/net/minestom/server/recipe/SmithingTrimRecipe.java +++ b/src/main/java/net/minestom/server/recipe/SmithingTrimRecipe.java @@ -1,6 +1,5 @@ package net.minestom.server.recipe; -import net.minestom.server.item.ItemStack; import net.minestom.server.network.packet.server.play.DeclareRecipesPacket; import org.jetbrains.annotations.NotNull; @@ -15,7 +14,7 @@ protected SmithingTrimRecipe( @NotNull DeclareRecipesPacket.Ingredient baseIngredient, @NotNull DeclareRecipesPacket.Ingredient additionIngredient ) { - super(Type.SMITHING_TRIM, recipeId); + super(RecipeType.SMITHING_TRIM, recipeId); this.template = template; this.baseIngredient = baseIngredient; this.additionIngredient = additionIngredient; diff --git a/src/main/java/net/minestom/server/recipe/SmokingRecipe.java b/src/main/java/net/minestom/server/recipe/SmokingRecipe.java index 928b61efaf5..ef27f1a58c8 100644 --- a/src/main/java/net/minestom/server/recipe/SmokingRecipe.java +++ b/src/main/java/net/minestom/server/recipe/SmokingRecipe.java @@ -20,7 +20,7 @@ protected SmokingRecipe( float experience, int cookingTime ) { - super(Type.SMOKING, recipeId); + super(RecipeType.SMOKING, recipeId); this.group = group; this.category = category; this.result = result; diff --git a/src/main/java/net/minestom/server/recipe/StonecutterRecipe.java b/src/main/java/net/minestom/server/recipe/StonecutterRecipe.java index a2dce2d45a1..78dc4db84f3 100644 --- a/src/main/java/net/minestom/server/recipe/StonecutterRecipe.java +++ b/src/main/java/net/minestom/server/recipe/StonecutterRecipe.java @@ -15,7 +15,7 @@ protected StonecutterRecipe( @NotNull DeclareRecipesPacket.Ingredient ingredient, @NotNull ItemStack result ) { - super(Type.STONECUTTING, recipeId); + super(RecipeType.STONECUTTING, recipeId); this.group = group; this.ingredient = ingredient; this.result = result; diff --git a/src/main/java/net/minestom/server/registry/DynamicRegistry.java b/src/main/java/net/minestom/server/registry/DynamicRegistry.java new file mode 100644 index 00000000000..456a09214a1 --- /dev/null +++ b/src/main/java/net/minestom/server/registry/DynamicRegistry.java @@ -0,0 +1,222 @@ +package net.minestom.server.registry; + +import net.kyori.adventure.key.Keyed; +import net.minestom.server.entity.Player; +import net.minestom.server.gamedata.DataPack; +import net.minestom.server.network.packet.server.SendablePacket; +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.Comparator; +import java.util.List; + +/** + *

Holds registry data for any of the registries controlled by the server. Entries in registries should be referenced + * using a {@link Key} object as opposed to the record type. For example, a biome should be stored as + * `DynamicRegistry.Key Biome`, as opposed to `Biome` directly.

+ * + *

Builtin registries should be accessed via a {@link Registries} instance (currently implemented by + * {@link net.minestom.server.ServerProcess}, or from {@link net.minestom.server.MinecraftServer} static methods.

+ * + * @param The type of the registry entries + * + * @see Registries + */ +public sealed interface DynamicRegistry permits DynamicRegistryImpl { + + /** + * A key for a {@link ProtocolObject} in a {@link DynamicRegistry}. + * + * @param Unused, except to provide compile-time safety and self documentation. + */ + sealed interface Key extends Keyed permits DynamicRegistryImpl.KeyImpl { + + static @NotNull Key of(@NotNull String namespace) { + return new DynamicRegistryImpl.KeyImpl<>(NamespaceID.from(namespace)); + } + + static @NotNull Key of(@NotNull NamespaceID namespace) { + return new DynamicRegistryImpl.KeyImpl<>(namespace); + } + + @Contract(pure = true) + @NotNull NamespaceID namespace(); + + @Contract(pure = true) + default @NotNull String name() { + return namespace().asString(); + } + + @Override + @Contract(pure = true) + default @NotNull net.kyori.adventure.key.Key key() { + return namespace(); + } + } + + @ApiStatus.Internal + static @NotNull DynamicRegistry create(@NotNull String id) { + return new DynamicRegistryImpl<>(id, null); + } + + /** + * Creates a new empty registry of the given type. Should only be used internally. + * + * @see Registries + */ + @ApiStatus.Internal + static @NotNull DynamicRegistry create( + @NotNull String id, @NotNull BinaryTagSerializer nbtType) { + return new DynamicRegistryImpl<>(id, nbtType); + } + + /** + * Creates a new empty registry of the given type. Should only be used internally. + * + * @see Registries + */ + @ApiStatus.Internal + static @NotNull DynamicRegistry create( + @NotNull String id, @NotNull BinaryTagSerializer nbtType, + @NotNull Registry.Resource resource, @NotNull Registry.Container.Loader loader) { + return create(id, nbtType, resource, loader, null); + } + + /** + * Creates a new empty registry of the given type. Should only be used internally. + * + * @see Registries + */ + @ApiStatus.Internal + static @NotNull DynamicRegistry create( + @NotNull String id, @NotNull BinaryTagSerializer nbtType, + @NotNull Registry.Resource resource, @NotNull Registry.Container.Loader loader, + @Nullable Comparator idComparator) { + final DynamicRegistry registry = new DynamicRegistryImpl<>(id, nbtType); + DynamicRegistryImpl.loadStaticRegistry(registry, resource, loader, idComparator); + return registry; + } + + /** + * Creates a new empty registry of the given type. Should only be used internally. + * + * @see Registries + */ + @ApiStatus.Internal + static @NotNull DynamicRegistry create( + @NotNull String id, @NotNull BinaryTagSerializer nbtType, + @NotNull Registries registries, @NotNull Registry.Resource resource) { + final DynamicRegistryImpl registry = new DynamicRegistryImpl<>(id, nbtType); + DynamicRegistryImpl.loadStaticSnbtRegistry(registries, registry, resource); + return registry; + } + + @NotNull String id(); + + @Nullable T get(int id); + @Nullable T get(@NotNull NamespaceID namespace); + default @Nullable T get(@NotNull Key key) { + return get(key.namespace()); + } + + @Nullable Key getKey(int id); + @Nullable Key getKey(@NotNull T value); + @Nullable NamespaceID getName(int id); + @Nullable DataPack getPack(int id); + default @Nullable DataPack getPack(@NotNull Key key) { + final int id = getId(key); + return id == -1 ? null : getPack(id); + } + + /** + * Returns the protocol ID associated with the given {@link NamespaceID}, or -1 if none is registered. + * + * @see #register(NamespaceID, T) + */ + int getId(@NotNull NamespaceID id); + + /** + * Returns the protocol ID associated with the given {@link Key}, or -1 if none is registered. + * + * @see #register(NamespaceID, T) + */ + default int getId(@NotNull Key key) { + return getId(key.namespace()); + } + + + /** + *

Returns the entries in this registry as an immutable list. The indices in the returned list correspond + * to the protocol ID of each entry.

+ * + *

Note: The returned list is not guaranteed to update with the registry, + * it should be fetched again for updated values.

+ * + * @return An immutable list of the entries in this registry. + */ + @NotNull List values(); + + /** + *

Register an object to this registry, overwriting the previous entry if any is present.

+ * + *

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()}.

+ * + *

WARNING: Updating an existing entry is an inherently unsafe operation as it may cause desync with + * existing structures. This behavior is disabled by default, and must be enabled by setting the system + * property minestom.registry.unsafe-ops to true.

+ * + * @param object The entry to register + * @return The new ID of the registered object + */ + default @NotNull DynamicRegistry.Key register(@NotNull String id, @NotNull T object) { + return register(NamespaceID.from(id), object, null); + } + + default @NotNull DynamicRegistry.Key register(@NotNull NamespaceID id, @NotNull T object) { + return register(id, object, null); + } + + @ApiStatus.Internal + default @NotNull DynamicRegistry.Key register(@NotNull String id, @NotNull T object, @Nullable DataPack pack) { + return register(NamespaceID.from(id), object, pack); + } + + @ApiStatus.Internal + default @NotNull DynamicRegistry.Key register(@NotNull NamespaceID id, @NotNull T object, @Nullable DataPack pack) { + return register(id, object); + } + + /** + *

Removes an object from this registry.

+ * + *

WARNING: 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. This behavior is disabled by default, and must be + * enabled by setting the system property minestom.registry.unsafe-ops to true.

+ * + *

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()}.

+ * + * @param namespaceId The id of the entry to remove + * @return True if the object was removed, false if it was not present + * @throws UnsupportedOperationException If the system property minestom.registry.unsafe-remove is not set to true + */ + boolean remove(@NotNull NamespaceID namespaceId) throws UnsupportedOperationException; + + /** + *

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(NamespaceID, T)}.

+ * + * @param excludeVanilla Whether to exclude vanilla entries + * @return A {@link SendablePacket} containing the registry data + */ + @ApiStatus.Internal + @NotNull SendablePacket registryDataPacket(boolean excludeVanilla); + +} diff --git a/src/main/java/net/minestom/server/registry/DynamicRegistryImpl.java b/src/main/java/net/minestom/server/registry/DynamicRegistryImpl.java new file mode 100644 index 00000000000..48b4e6e1cbe --- /dev/null +++ b/src/main/java/net/minestom/server/registry/DynamicRegistryImpl.java @@ -0,0 +1,235 @@ +package net.minestom.server.registry; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.TagStringIOExt; +import net.minestom.server.MinecraftServer; +import net.minestom.server.ServerFlag; +import net.minestom.server.gamedata.DataPack; +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 net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnknownNullability; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.locks.ReentrantLock; + +@ApiStatus.Internal +final class DynamicRegistryImpl implements DynamicRegistry { + private static final UnsupportedOperationException UNSAFE_REMOVE_EXCEPTION = new UnsupportedOperationException("Unsafe remove is disabled. Enable by setting the system property 'minestom.registry.unsafe-ops' to 'true'"); + + record KeyImpl(@NotNull NamespaceID namespace) implements Key { + + @Override + public String toString() { + return namespace.asString(); + } + + @Override + public int hashCode() { + return namespace.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + KeyImpl key = (KeyImpl) obj; + return namespace.equals(key.namespace); + } + } + + private final CachedPacket vanillaRegistryDataPacket = new CachedPacket(() -> createRegistryDataPacket(true)); + + private final ReentrantLock lock = new ReentrantLock(); // Protects writes + private final List entryById = new CopyOnWriteArrayList<>(); + private final Map entryByName = new ConcurrentHashMap<>(); + private final List idByName = new CopyOnWriteArrayList<>(); + private final List packById = new CopyOnWriteArrayList<>(); + + private final String id; + private final BinaryTagSerializer nbtType; + + DynamicRegistryImpl(@NotNull String id, @Nullable BinaryTagSerializer nbtType) { + this.id = id; + this.nbtType = nbtType; + } + + @Override + public @NotNull String id() { + return id; + } + + public @UnknownNullability BinaryTagSerializer nbtType() { + return nbtType; + } + + @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) { + return entryByName.get(namespace); + } + + @Override + public @Nullable Key getKey(@NotNull T value) { + int index = entryById.indexOf(value); + return index == -1 ? null : getKey(index); + } + + @Override + public @Nullable Key getKey(int id) { + if (id < 0 || id >= entryById.size()) + return null; + return Key.of(idByName.get(id)); + } + + @Override + public @Nullable NamespaceID getName(int id) { + if (id < 0 || id >= entryById.size()) + return null; + return idByName.get(id); + } + + @Override + public @Nullable DataPack getPack(int id) { + if (id < 0 || id >= packById.size()) + return null; + return packById.get(id); + } + + @Override + public int getId(@NotNull NamespaceID id) { + return idByName.indexOf(id); + } + + @Override + public @NotNull List values() { + return Collections.unmodifiableList(entryById); + } + + @Override + public @NotNull DynamicRegistry.Key register(@NotNull NamespaceID namespaceId, @NotNull T object, @Nullable DataPack pack) { + // This check is disabled in tests because we remake server processes over and over. + // todo: re-enable this check +// Check.stateCondition((!DebugUtils.INSIDE_TEST && MinecraftServer.process() != null && !MinecraftServer.isStarted()) && !ServerFlag.REGISTRY_LATE_REGISTER, +// "Registering an object to a dynamic registry ({0}) after the server is started can lead to " + +// "registry desync between the client and server. This is usually unwanted behavior. If you " + +// "know what you're doing and would like this behavior, set the `minestom.registry.late-register` " + +// "system property.", id); + + lock.lock(); + try { + int id = idByName.indexOf(namespaceId); + if (id == -1) id = entryById.size(); + + entryById.add(id, object); + entryByName.put(namespaceId, object); + idByName.add(namespaceId); + packById.add(id, pack); + vanillaRegistryDataPacket.invalidate(); + return Key.of(namespaceId); + } finally { + lock.unlock(); + } + } + + @Override + public boolean remove(@NotNull NamespaceID namespaceId) throws UnsupportedOperationException { + if (!ServerFlag.REGISTRY_UNSAFE_OPS) throw UNSAFE_REMOVE_EXCEPTION; + + lock.lock(); + try { + int id = idByName.indexOf(namespaceId); + if (id == -1) return false; + + entryById.remove(id); + entryByName.remove(namespaceId); + idByName.remove(id); + packById.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) { + Check.notNull(nbtType, "Cannot create registry data packet for server-only registry"); + BinaryTagSerializer.Context context = new BinaryTagSerializer.ContextWithRegistries(MinecraftServer.process(), true); + List entries = new ArrayList<>(entryById.size()); + for (int i = 0; i < entryById.size(); i++) { + 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. + T entry = entryById.get(i); + DataPack pack = packById.get(i); + if (!excludeVanilla || pack != DataPack.MINECRAFT_CORE) { + data = (CompoundBinaryTag) nbtType.write(context, entry); + } + //noinspection DataFlowIssue + entries.add(new RegistryDataPacket.Entry(getKey(i).name(), data)); + } + return new RegistryDataPacket(id, entries); + } + + static void loadStaticRegistry(@NotNull DynamicRegistry registry, @NotNull Registry.Resource resource, @NotNull Registry.Container.Loader loader, @Nullable Comparator idComparator) { + List>> entries = new ArrayList<>(Registry.load(resource).entrySet()); + if (idComparator != null) entries.sort(Map.Entry.comparingByKey(idComparator)); + for (var entry : entries) { + final String namespace = entry.getKey(); + final Registry.Properties properties = Registry.Properties.fromMap(entry.getValue()); + registry.register(namespace, loader.get(namespace, properties), DataPack.MINECRAFT_CORE); + } + } + + static void loadStaticSnbtRegistry(@NotNull Registries registries, @NotNull DynamicRegistryImpl registry, @NotNull Registry.Resource resource) { + Check.argCondition(!resource.fileName().endsWith(".snbt"), "Resource must be an SNBT file: {0}", resource.fileName()); + try (InputStream resourceStream = Registry.class.getClassLoader().getResourceAsStream(resource.fileName())) { + Check.notNull(resourceStream, "Resource {0} does not exist!", resource); + final BinaryTag tag = TagStringIOExt.readTag(new String(resourceStream.readAllBytes(), StandardCharsets.UTF_8)); + if (!(tag instanceof CompoundBinaryTag compound)) { + throw new IllegalStateException("Root tag must be a compound tag"); + } + + final BinaryTagSerializer.Context context = new BinaryTagSerializer.ContextWithRegistries(registries, false); + for (Map.Entry entry : compound) { + final String namespace = entry.getKey(); + final T value = registry.nbtType.read(context, entry.getValue()); + Check.notNull(value, "Failed to read value for namespace {0}", namespace); + registry.register(namespace, value, DataPack.MINECRAFT_CORE); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/net/minestom/server/registry/ObjectSet.java b/src/main/java/net/minestom/server/registry/ObjectSet.java new file mode 100644 index 00000000000..4d2ceaebe1e --- /dev/null +++ b/src/main/java/net/minestom/server/registry/ObjectSet.java @@ -0,0 +1,53 @@ +package net.minestom.server.registry; + +import net.minestom.server.gamedata.tags.Tag; +import net.minestom.server.utils.NamespaceID; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Set; + +/** + * A set of some protocol objects. May contain a single element, multiple elements, or a single tag (which itself contains multiple elements). + * + * @param The type of protocol object represented by this set. + */ +public sealed interface ObjectSet permits ObjectSetImpl { + + static @NotNull ObjectSet empty() { + //noinspection unchecked + return (ObjectSet) ObjectSetImpl.Empty.INSTANCE; + } + + static @NotNull ObjectSet of(@NotNull Collection entries) { + return new ObjectSetImpl.Entries<>(Set.copyOf(entries)); + } + + static @NotNull ObjectSet of(@NotNull Tag tag) { + return new ObjectSetImpl.Tag<>(tag); + } + + static @NotNull BinaryTagSerializer> nbtType(@NotNull Tag.BasicType tagType) { + return new ObjectSetImpl.NbtType<>(tagType); + } + + /** + *

Check if this set contains the given object, tested against its namespace id.

+ * + *

Present for compatibility with non-dynamic registries. Will be removed in the future.

+ * + * @param object The object to check for. + * @return True if this set contains the object, false otherwise. + */ + default boolean contains(@NotNull StaticProtocolObject object) { + return contains(object.namespace()); + } + + default boolean contains(@NotNull DynamicRegistry.Key key) { + return contains(key.namespace()); + } + + boolean contains(@NotNull NamespaceID namespace); + +} diff --git a/src/main/java/net/minestom/server/registry/ObjectSetImpl.java b/src/main/java/net/minestom/server/registry/ObjectSetImpl.java new file mode 100644 index 00000000000..9d42bcd3a68 --- /dev/null +++ b/src/main/java/net/minestom/server/registry/ObjectSetImpl.java @@ -0,0 +1,127 @@ +package net.minestom.server.registry; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.BinaryTagTypes; +import net.kyori.adventure.nbt.ListBinaryTag; +import net.kyori.adventure.nbt.StringBinaryTag; +import net.minestom.server.MinecraftServer; +import net.minestom.server.utils.NamespaceID; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +import org.jetbrains.annotations.NotNull; + +import java.util.HashSet; +import java.util.Set; + +import static net.kyori.adventure.nbt.StringBinaryTag.stringBinaryTag; + +sealed interface ObjectSetImpl extends ObjectSet permits ObjectSetImpl.Empty, ObjectSetImpl.Entries, ObjectSetImpl.Tag { + + record Empty() implements ObjectSetImpl { + static final Empty INSTANCE = new Empty<>(); + + @Override + public boolean contains(@NotNull NamespaceID namespace) { + return false; + } + } + + record Entries(@NotNull Set entries) implements ObjectSetImpl { + + public Entries { + entries = Set.copyOf(entries); + } + + @Override + public boolean contains(@NotNull NamespaceID namespace) { + return entries.contains(namespace); + } + } + + final class Tag implements ObjectSetImpl { + private final net.minestom.server.gamedata.tags.Tag.BasicType tagType; + private final String name; + private volatile Set value = null; + + public Tag(@NotNull net.minestom.server.gamedata.tags.Tag.BasicType tagType, @NotNull String name) { + this.tagType = tagType; + this.name = name; + } + + public Tag(@NotNull net.minestom.server.gamedata.tags.Tag tag) { + this.tagType = null; + this.name = tag.name(); + this.value = Set.copyOf(tag.getValues()); + } + + public @NotNull String name() { + return name; + } + + public Set value() { + if (value == null) { + synchronized (this) { + if (value == null) { + var group = MinecraftServer.getTagManager().getTag(tagType, name); + value = group == null ? Set.of() : Set.copyOf(group.getValues()); + } + } + } + return value; + } + + @Override + public boolean contains(@NotNull NamespaceID namespace) { + return value().contains(namespace); + } + } + + record NbtType( + @NotNull net.minestom.server.gamedata.tags.Tag.BasicType tagType + ) implements BinaryTagSerializer> { + + @Override + public @NotNull ObjectSet read(@NotNull BinaryTag tag) { + return switch (tag) { + case null -> ObjectSet.empty(); + case ListBinaryTag list -> { + if (list.size() == 0) yield ObjectSet.empty(); + + final Set entries = new HashSet<>(list.size()); + for (BinaryTag entryTag : list) { + if (!(entryTag instanceof StringBinaryTag stringTag)) + throw new IllegalArgumentException("Invalid entry type: " + entryTag.type()); + entries.add(NamespaceID.from(stringTag.value())); + } + yield new Entries<>(entries); + } + case StringBinaryTag string -> { + // Could be a tag or a block name depending if it starts with a # + final String value = string.value(); + if (value.startsWith("#")) { + yield new Tag<>(tagType(), value.substring(1)); + } else { + yield new Entries<>(Set.of(NamespaceID.from(value))); + } + } + default -> throw new IllegalArgumentException("Invalid tag type: " + tag.type()); + }; + } + + @Override + public @NotNull BinaryTag write(@NotNull ObjectSet value) { + return switch (value) { + case Empty empty -> ListBinaryTag.empty(); + case Entries entries -> { + if (entries.entries.size() == 1) + yield stringBinaryTag(entries.entries.stream().findFirst().get().asString()); + ListBinaryTag.Builder builder = ListBinaryTag.builder(BinaryTagTypes.STRING); + for (NamespaceID entry : entries.entries) + builder.add(stringBinaryTag(entry.asString())); + yield builder.build(); + } + case Tag tag -> stringBinaryTag("#" + tag.name()); + }; + } + } + +} diff --git a/src/main/java/net/minestom/server/registry/ProtocolObject.java b/src/main/java/net/minestom/server/registry/ProtocolObject.java index e88dba911b9..cb89066f222 100644 --- a/src/main/java/net/minestom/server/registry/ProtocolObject.java +++ b/src/main/java/net/minestom/server/registry/ProtocolObject.java @@ -1,24 +1,10 @@ package net.minestom.server.registry; -import net.kyori.adventure.key.Key; -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 { +public interface ProtocolObject { - @Contract(pure = true) - @NotNull NamespaceID namespace(); - - @Contract(pure = true) - default @NotNull String name() { - return namespace().asString(); - } - - @Override - @Contract(pure = true) - default @NotNull Key key() { - return namespace(); + default @Nullable Object registry() { + return null; } } diff --git a/src/main/java/net/minestom/server/registry/Registries.java b/src/main/java/net/minestom/server/registry/Registries.java new file mode 100644 index 00000000000..246aac1192d --- /dev/null +++ b/src/main/java/net/minestom/server/registry/Registries.java @@ -0,0 +1,57 @@ +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.entity.metadata.other.PaintingMeta; +import net.minestom.server.instance.block.banner.BannerPattern; +import net.minestom.server.instance.block.jukebox.JukeboxSong; +import net.minestom.server.item.armor.TrimMaterial; +import net.minestom.server.item.armor.TrimPattern; +import net.minestom.server.item.enchant.*; +import net.minestom.server.message.ChatType; +import net.minestom.server.utils.nbt.BinaryTagSerializer; +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. {@link net.minestom.server.ServerProcess} is the most relevant + * implementation of this interface.

+ * + * @see net.minestom.server.MinecraftServer for static access to these + */ +public interface Registries { + + @NotNull DynamicRegistry chatType(); + + @NotNull DynamicRegistry dimensionType(); + + @NotNull DynamicRegistry biome(); + + @NotNull DynamicRegistry damageType(); + + @NotNull DynamicRegistry trimMaterial(); + + @NotNull DynamicRegistry trimPattern(); + + @NotNull DynamicRegistry bannerPattern(); + + @NotNull DynamicRegistry wolfVariant(); + + @NotNull DynamicRegistry enchantment(); + + @NotNull DynamicRegistry paintingVariant(); + + @NotNull DynamicRegistry jukeboxSong(); + + // The following are _not_ sent to the client. + + @NotNull DynamicRegistry> enchantmentLevelBasedValues(); + + @NotNull DynamicRegistry> enchantmentValueEffects(); + + @NotNull DynamicRegistry> enchantmentEntityEffects(); + + @NotNull DynamicRegistry> enchantmentLocationEffects(); + +} diff --git a/src/main/java/net/minestom/server/registry/Registry.java b/src/main/java/net/minestom/server/registry/Registry.java index 1f8ee98d8e1..8093e7a87ee 100644 --- a/src/main/java/net/minestom/server/registry/Registry.java +++ b/src/main/java/net/minestom/server/registry/Registry.java @@ -1,21 +1,32 @@ 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; import net.minestom.server.collision.CollisionUtils; import net.minestom.server.collision.Shape; +import net.minestom.server.component.DataComponent; +import net.minestom.server.component.DataComponentMap; import net.minestom.server.entity.EntitySpawnType; import net.minestom.server.entity.EntityType; import net.minestom.server.entity.EquipmentSlot; import net.minestom.server.instance.block.Block; +import net.minestom.server.item.ItemComponent; import net.minestom.server.item.Material; +import net.minestom.server.message.ChatTypeDecoration; import net.minestom.server.sound.SoundEvent; import net.minestom.server.utils.NamespaceID; import net.minestom.server.utils.collection.ObjectArray; +import net.minestom.server.utils.nbt.BinaryTagSerializer; import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -34,14 +45,16 @@ * Use at your own risk. */ public final class Registry { - - private static final String TRANSLATION_KEY = "translationKey"; - @ApiStatus.Internal public static BlockEntry block(String namespace, @NotNull Properties main) { 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); @@ -58,8 +71,8 @@ public static EntityEntry entity(String namespace, @NotNull Properties main) { } @ApiStatus.Internal - public static EnchantmentEntry enchantment(String namespace, @NotNull Properties main) { - return new EnchantmentEntry(namespace, main, null); + public static FeatureFlagEntry featureFlag(String namespace, @NotNull Properties main) { + return new FeatureFlagEntry(namespace, main, null); } @ApiStatus.Internal @@ -88,23 +101,33 @@ public static AttributeEntry attribute(String namespace, @NotNull Properties mai } @ApiStatus.Internal - public static FeatureFlagEntry featureFlag(String namespace, @NotNull Properties main) { - return new FeatureFlagEntry(namespace, main, null); + 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 VillagerProfession villagerProfession(String namespace, @NotNull Properties main) { - return new VillagerProfession(namespace, main, null); + public static EnchantmentEntry enchantment(String namespace, @NotNull Properties main) { + return new EnchantmentEntry(namespace, main, null); } @ApiStatus.Internal - public static VillagerType villagerType(String namespace, @NotNull Properties main) { - return new VillagerType(namespace, main, null); + public static PaintingVariantEntry paintingVariant(String namespace, @NotNull Properties main) { + return new PaintingVariantEntry(namespace, main, null); } @ApiStatus.Internal - public static FluidEntry fluidEntry(String namespace, @NotNull Properties main) { - return new FluidEntry(namespace, main, null); + public static JukeboxSongEntry jukeboxSong(String namespace, @NotNull Properties main) { + return new JukeboxSongEntry(namespace, main, null); } @ApiStatus.Internal @@ -124,7 +147,7 @@ public static Map> load(Resource resource) { } @ApiStatus.Internal - public static Container createStaticContainer(Resource resource, Loader loader) { + public static Container createStaticContainer(Resource resource, Container.Loader loader) { var entries = Registry.load(resource); Map namespaces = new HashMap<>(entries.size()); ObjectArray ids = ObjectArray.singleThread(entries.size()); @@ -184,66 +207,12 @@ public interface Loader { } } - @ApiStatus.Internal - public static DynamicContainer createDynamicContainer(Resource resource, Loader loader) { - var entries = Registry.load(resource); - Map namespaces = new HashMap<>(entries.size()); - for (var entry : entries.entrySet()) { - final String namespace = entry.getKey(); - final Properties properties = Properties.fromMap(entry.getValue()); - final T value = loader.get(namespace, properties); - namespaces.put(value.name(), value); - } - return new DynamicContainer<>(resource, namespaces); - } - - @ApiStatus.Internal - public record DynamicContainer(Resource resource, Map namespaces) { - public DynamicContainer { - namespaces = Map.copyOf(namespaces); - } - - public T get(@NotNull String namespace) { - return namespaces.get(namespace); - } - - public T getSafe(@NotNull String namespace) { - return get(namespace.contains(":") ? namespace : "minecraft:" + namespace); - } - - public Collection values() { - return namespaces.values(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Container container)) return false; - return resource == container.resource; - } - - @Override - public int hashCode() { - return Objects.hash(resource); - } - - public interface Loader { - T get(String namespace, Properties properties); - } - } - - @FunctionalInterface - public interface Loader { - T get(String namespace, Properties properties); - } - @ApiStatus.Internal public enum Resource { BLOCKS("blocks.json"), ITEMS("items.json"), - ATTRIBUTES("attributes.json"), ENTITIES("entities.json"), - ENCHANTMENTS("enchantments.json"), + FEATURE_FLAGS("feature_flags.json"), SOUNDS("sounds.json"), COMMAND_ARGUMENTS("command_arguments.json"), STATISTICS("custom_statistics.json"), @@ -253,71 +222,30 @@ public enum Resource { DAMAGE_TYPES("damage_types.json"), TRIM_MATERIALS("trim_materials.json"), TRIM_PATTERNS("trim_patterns.json"), - BANNER_PATTERNS("banner_patterns.json"), // Microtus - Banner and Shield Meta - BLOCK_TAGS("tags/block_tags.json"), - ENTITY_TYPE_TAGS("tags/entity_type_tags.json"), - FLUID_TAGS("tags/fluid_tags.json"), - GAMEPLAY_TAGS("tags/gameplay_tags.json"), - ITEM_TAGS("tags/item_tags.json"), + BLOCK_TAGS("tags/block.json"), + ENTITY_TYPE_TAGS("tags/entity_type.json"), + FLUID_TAGS("tags/fluid.json"), + GAMEPLAY_TAGS("tags/game_event.json"), + ITEM_TAGS("tags/item.json"), + ENCHANTMENT_TAGS("tags/enchantment.json"), + DIMENSION_TYPES("dimension_types.json"), BIOMES("biomes.json"), - VILLAGER_PROFESSION("villager_professions.json"), - VILLAGER_TYPES("villager_types.json"), - FLUIDS("fluids.json"), - FEATURE_FLAGS("feature_flags.json"), - ; + ATTRIBUTES("attributes.json"), + BANNER_PATTERNS("banner_patterns.json"), + WOLF_VARIANTS("wolf_variants.json"), + CHAT_TYPES("chat_types.json"), + ENCHANTMENTS("enchantments.snbt"), + PAINTING_VARIANTS("painting_variants.json"), + JUKEBOX_SONGS("jukebox_songs.json"); private final String name; Resource(String name) { this.name = name; } - } - public record FeatureFlagEntry( - @NotNull NamespaceID namespace, - @Nullable Properties custom - ) implements Entry { - - public FeatureFlagEntry(String namespace, Properties main, Properties custom) { - this(NamespaceID.from(namespace), custom); - } - } - - public record FluidEntry( - @NotNull NamespaceID namespace, - @NotNull NamespaceID bucketId, - @Nullable Properties custom - ) implements Entry { - - public FluidEntry(String namespace, Properties main, Properties custom) { - this(NamespaceID.from(namespace), - NamespaceID.from(main.getString("bucketId")), - custom - ); - } - } - - public record AttributeEntry( - @NotNull NamespaceID namespace, - int id, - @NotNull String translationKey, - float defaultValue, - boolean clientSync, - float maxValue, - float minValue, - @Nullable Properties custom - ) implements Entry { - - public AttributeEntry(String namespace, Properties main, Properties custom) { - this(NamespaceID.from(namespace), - main.getInt("id"), - main.getString(TRANSLATION_KEY), - (float) main.getDouble("defaultValue"), - main.getBoolean("clientSync"), - (float) main.getDouble("maxValue"), - (float) main.getDouble("minValue"), - custom - ); + public @NotNull String fileName() { + return name; } } @@ -479,6 +407,49 @@ public Properties custom() { } } + 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; @@ -556,24 +527,22 @@ public boolean hasPrecipitation() { public static final class MaterialEntry implements Entry { private final NamespaceID namespace; + private final Properties main; private final int id; private final String translationKey; - private final int maxStackSize; - private final int maxDamage; - private final boolean isFood; private final Supplier blockSupplier; + private DataComponentMap prototype; + private final EquipmentSlot equipmentSlot; private final EntityType entityType; private final Properties custom; private MaterialEntry(String namespace, Properties main, Properties custom) { + this.main = main; this.custom = custom; this.namespace = NamespaceID.from(namespace); this.id = main.getInt("id"); - this.translationKey = main.getString(TRANSLATION_KEY); - this.maxStackSize = main.getInt("maxStackSize", 64); - this.maxDamage = main.getInt("maxDamage", 0); - this.isFood = main.getBoolean("edible", false); + this.translationKey = main.getString("translationKey"); { final String blockNamespace = main.getString("correspondingBlock", null); this.blockSupplier = blockNamespace != null ? () -> Block.fromNamespaceId(blockNamespace) : () -> null; @@ -610,24 +579,34 @@ public int id() { return id; } - public String translationKey() { + public @NotNull String translationKey() { return translationKey; } - public int maxStackSize() { - return maxStackSize; + public @Nullable Block block() { + return blockSupplier.get(); } - public int maxDamage() { - return maxDamage; - } + public @NotNull DataComponentMap prototype() { + if (prototype == null) { + try { + BinaryTagSerializer.Context context = new BinaryTagSerializer.ContextWithRegistries(MinecraftServer.process(), false); + DataComponentMap.Builder builder = DataComponentMap.builder(); + for (Map.Entry entry : main.section("components")) { + //noinspection unchecked + DataComponent component = (DataComponent) ItemComponent.fromNamespaceId(entry.getKey()); + Check.notNull(component, "Unknown component {0} in {1}", entry.getKey(), namespace); - public boolean isFood() { - return isFood; - } + BinaryTag tag = TagStringIOExt.readTag((String) entry.getValue()); + builder.set(component, component.read(context, tag)); + } + this.prototype = builder.build(); + } catch (IOException e) { + throw new RuntimeException("failed to parse material registry: " + namespace, e); + } + } - public @Nullable Block block() { - return blockSupplier.get(); + return prototype; } public boolean isArmor() { @@ -640,6 +619,7 @@ public boolean isArmor() { /** * Gets the entity type this item can spawn. Only present for spawn eggs (e.g. wolf spawn egg, skeleton spawn egg) + * * @return The entity type it can spawn, or null if it is not a spawn egg */ public @Nullable EntityType spawnEntityType() { @@ -652,27 +632,93 @@ public Properties custom() { } } - public record EntityEntry(NamespaceID namespace, int id, - String translationKey, - double width, double height, - double drag, double acceleration, - EntitySpawnType spawnType, - BoundingBox boundingBox, - Properties custom) implements Entry { + public static final class EntityEntry implements Entry { + private final NamespaceID namespace; + private final int id; + private final String translationKey; + private final double drag; + private final double acceleration; + private final EntitySpawnType spawnType; + private final double width; + private final double height; + private final double eyeHeight; + private final BoundingBox boundingBox; + private final Properties custom; + public EntityEntry(String namespace, Properties main, Properties custom) { + this.namespace = NamespaceID.from(namespace); + this.id = main.getInt("id"); + this.translationKey = main.getString("translationKey"); + this.drag = main.getDouble("drag", 0.02); + this.acceleration = main.getDouble("acceleration", 0.08); + this.spawnType = EntitySpawnType.valueOf(main.getString("packetType").toUpperCase(Locale.ROOT)); + + // Dimensions + this.width = main.getDouble("width"); + this.height = main.getDouble("height"); + this.eyeHeight = main.getDouble("eyeHeight"); + this.boundingBox = new BoundingBox(this.width, this.height, this.width); + + // Attachments + Properties attachments = main.section("attachments"); + if (attachments != null) { + //todo + } + + this.custom = custom; + } + + public @NotNull NamespaceID namespace() { + return namespace; + } + + public int id() { + return id; + } + + public String translationKey() { + return translationKey; + } + + public double drag() { + return drag; + } + + public double acceleration() { + return acceleration; + } + + public @NotNull EntitySpawnType spawnType() { + return spawnType; + } + + public double width() { + return width; + } + + public double height() { + return height; + } + + public double eyeHeight() { + return eyeHeight; + } + + public @NotNull BoundingBox boundingBox() { + return boundingBox; + } + + @Override + public Properties custom() { + return custom; + } + } + + public record FeatureFlagEntry(NamespaceID namespace, int id, Properties custom) implements Entry { + public FeatureFlagEntry(String namespace, Properties main, Properties custom) { this(NamespaceID.from(namespace), main.getInt("id"), - main.getString(TRANSLATION_KEY), - main.getDouble("width"), - main.getDouble("height"), - main.getDouble("drag", 0.02), - main.getDouble("acceleration", 0.08), - EntitySpawnType.valueOf(main.getString("packetType").toUpperCase(Locale.ROOT)), - new BoundingBox( - main.getDouble("width"), - main.getDouble("height"), - main.getDouble("width")), - custom + null ); } } @@ -693,11 +739,12 @@ public DamageTypeEntry(String namespace, Properties main, Properties custom) { custom); } } + public record TrimMaterialEntry(@NotNull NamespaceID namespace, @NotNull String assetName, @NotNull Material ingredient, float itemModelIndex, - @NotNull Map overrideArmorMaterials, + @NotNull Map overrideArmorMaterials, @NotNull Component description, Properties custom) implements Entry { public TrimMaterialEntry(@NotNull String namespace, @NotNull Properties main, Properties custom) { @@ -706,7 +753,7 @@ public TrimMaterialEntry(@NotNull String namespace, @NotNull Properties main, Pr main.getString("asset_name"), Objects.requireNonNull(Material.fromNamespaceId(main.getString("ingredient"))), (float) main.getDouble("item_model_index"), - Objects.requireNonNullElse(main.section("override_armor_materials"),new PropertiesMap(Map.of())) + Objects.requireNonNullElse(main.section("override_armor_materials"), new PropertiesMap(Map.of())) .asMap().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> (String) entry.getValue())), JSONComponentSerializer.json().deserialize(main.section("description").toString()), custom @@ -732,57 +779,145 @@ public TrimPatternEntry(@NotNull String namespace, @NotNull Properties main, Pro } } - public record VillagerProfession(NamespaceID namespace, int id, SoundEvent soundEvent, Properties custom) implements Entry { - public VillagerProfession(String namespace, - Properties main, - Properties custom) { + public record PotionEffectEntry(NamespaceID namespace, int id, + String translationKey, + int color, + boolean isInstantaneous, + Properties custom) implements Entry { + public PotionEffectEntry(String namespace, Properties main, Properties custom) { this(NamespaceID.from(namespace), main.getInt("id"), - SoundEvent.fromNamespaceId(main.getString("workSound")), + main.getString("translationKey"), + main.getInt("color"), + main.getBoolean("instantaneous"), custom); } } - public record VillagerType(NamespaceID namespace, int id, Properties custom) implements Entry { - public VillagerType(String namespace, Properties main, Properties custom) { + public record AttributeEntry(NamespaceID namespace, int id, + String translationKey, double defaultValue, + boolean clientSync, + double maxValue, double minValue, + Properties custom) implements Entry { + public AttributeEntry(String namespace, Properties main, Properties custom) { this(NamespaceID.from(namespace), main.getInt("id"), + main.getString("translationKey"), + main.getDouble("defaultValue"), + main.getBoolean("clientSync"), + main.getDouble("maxValue"), + main.getDouble("minValue"), custom); } } - public record EnchantmentEntry(NamespaceID namespace, int id, - String translationKey, - double maxLevel, - boolean isCursed, - boolean isDiscoverable, - boolean isTradeable, - boolean isTreasureOnly, - Properties custom) implements Entry { + 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 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 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 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 record EnchantmentEntry(NamespaceID namespace, String raw, Properties custom) implements Entry { public EnchantmentEntry(String namespace, Properties main, Properties custom) { + this(NamespaceID.from(namespace), main.getString("raw"), custom); + } + } + + public record PaintingVariantEntry(NamespaceID namespace, NamespaceID assetId, int width, int height, Properties custom) implements Entry { + public PaintingVariantEntry(String namespace, Properties main, Properties custom) { this(NamespaceID.from(namespace), - main.getInt("id"), - main.getString(TRANSLATION_KEY), - main.getDouble("maxLevel"), - main.getBoolean("curse", false), - main.getBoolean("discoverable", true), - main.getBoolean("tradeable", true), - main.getBoolean("treasureOnly", false), + NamespaceID.from(main.getString("asset_id")), + main.getInt("width"), + main.getInt("height"), custom); } } - public record PotionEffectEntry(NamespaceID namespace, int id, - String translationKey, - int color, - boolean isInstantaneous, - Properties custom) implements Entry { - public PotionEffectEntry(String namespace, Properties main, Properties custom) { + public record JukeboxSongEntry(NamespaceID namespace, SoundEvent soundEvent, Component description, + float lengthInSeconds, int comparatorOutput, Properties custom) implements Entry { + public JukeboxSongEntry(String namespace, Properties main, Properties custom) { this(NamespaceID.from(namespace), - main.getInt("id"), - main.getString(TRANSLATION_KEY), - main.getInt("color"), - main.getBoolean("instantaneous"), + SoundEvent.fromNamespaceId(main.getString("sound_event")), + GsonComponentSerializer.gson().deserialize(main.section("description").toString()), + (float) main.getDouble("length_in_seconds"), + main.getInt("comparator_output"), custom); } } @@ -849,6 +984,17 @@ public int getInt(String name) { 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); @@ -886,7 +1032,7 @@ private T element(String name) { public String toString() { AtomicReference string = new AtomicReference<>("{ "); this.map.forEach((s, object) -> string.set(string.get() + " , " + "\"" + s + "\"" + " : " + "\"" + object.toString() + "\"")); - return string.updateAndGet(s -> s.replaceFirst(" , ","") + "}"); + return string.updateAndGet(s -> s.replaceFirst(" , ", "") + "}"); } } @@ -908,6 +1054,10 @@ static Properties fromMap(Map map) { int getInt(String name); + float getFloat(String name, float defaultValue); + + float getFloat(String name); + boolean getBoolean(String name, boolean defaultValue); boolean getBoolean(String name); diff --git a/src/main/java/net/minestom/server/registry/StaticProtocolObject.java b/src/main/java/net/minestom/server/registry/StaticProtocolObject.java index e7156e5881b..a1dc1790aba 100644 --- a/src/main/java/net/minestom/server/registry/StaticProtocolObject.java +++ b/src/main/java/net/minestom/server/registry/StaticProtocolObject.java @@ -1,8 +1,26 @@ package net.minestom.server.registry; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.Keyed; +import net.minestom.server.utils.NamespaceID; import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; -public interface StaticProtocolObject extends ProtocolObject { +public interface StaticProtocolObject extends ProtocolObject, Keyed { + + @Contract(pure = true) + @NotNull NamespaceID namespace(); + + @Contract(pure = true) + default @NotNull String name() { + return namespace().asString(); + } + + @Override + @Contract(pure = true) + default @NotNull Key key() { + return namespace(); + } @Contract(pure = true) int id(); diff --git a/src/main/java/net/minestom/server/scoreboard/Sidebar.java b/src/main/java/net/minestom/server/scoreboard/Sidebar.java index 9c0aa45cf13..03778ff4a36 100644 --- a/src/main/java/net/minestom/server/scoreboard/Sidebar.java +++ b/src/main/java/net/minestom/server/scoreboard/Sidebar.java @@ -183,11 +183,11 @@ public void removeLine(@NotNull String id) { @Override public boolean addViewer(@NotNull Player player) { final boolean result = this.viewers.add(player); - - ScoreboardObjectivePacket scoreboardObjectivePacket = this.getCreationObjectivePacket(this.title, ScoreboardObjectivePacket.Type.INTEGER); + if (result) { + ScoreboardObjectivePacket scoreboardObjectivePacket = this.getCreationObjectivePacket(this.title, ScoreboardObjectivePacket.Type.INTEGER); + player.sendPacket(scoreboardObjectivePacket); + } DisplayScoreboardPacket displayScoreboardPacket = this.getDisplayScoreboardPacket((byte) 1); - - player.sendPacket(scoreboardObjectivePacket); // Creative objective player.sendPacket(displayScoreboardPacket); // Show sidebar scoreboard (wait for scores packet) for (ScoreboardLine line : lines) { player.sendPacket(line.sidebarTeam.getCreationPacket()); @@ -199,13 +199,14 @@ public boolean addViewer(@NotNull Player player) { @Override public boolean removeViewer(@NotNull Player player) { final boolean result = this.viewers.remove(player); + if (!result) return false; ScoreboardObjectivePacket scoreboardObjectivePacket = this.getDestructionObjectivePacket(); player.sendPacket(scoreboardObjectivePacket); for (ScoreboardLine line : lines) { player.sendPacket(line.getScoreDestructionPacket(objectiveName)); // Is it necessary? player.sendPacket(line.sidebarTeam.getDestructionPacket()); } - return result; + return true; } @NotNull diff --git a/src/main/java/net/minestom/server/snapshot/ChunkSnapshot.java b/src/main/java/net/minestom/server/snapshot/ChunkSnapshot.java index d3b933a1d0f..ce368272b10 100644 --- a/src/main/java/net/minestom/server/snapshot/ChunkSnapshot.java +++ b/src/main/java/net/minestom/server/snapshot/ChunkSnapshot.java @@ -2,7 +2,7 @@ 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; diff --git a/src/main/java/net/minestom/server/snapshot/InstanceSnapshot.java b/src/main/java/net/minestom/server/snapshot/InstanceSnapshot.java index 16b770d6edf..714a2da6d42 100644 --- a/src/main/java/net/minestom/server/snapshot/InstanceSnapshot.java +++ b/src/main/java/net/minestom/server/snapshot/InstanceSnapshot.java @@ -2,9 +2,10 @@ import net.minestom.server.coordinate.Point; import net.minestom.server.instance.block.Block; +import net.minestom.server.registry.DynamicRegistry; 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; @@ -16,7 +17,7 @@ public sealed interface InstanceSnapshot extends Snapshot, Block.Getter, Biome.Getter, TagReadable permits SnapshotImpl.Instance { - @NotNull DimensionType dimensionType(); + @NotNull DynamicRegistry.Key dimensionType(); long worldAge(); @@ -29,7 +30,7 @@ public sealed interface InstanceSnapshot extends Snapshot, Block.Getter, Biome.G } @Override - default @NotNull Biome getBiome(int x, int y, int z) { + default @NotNull DynamicRegistry.Key getBiome(int x, int y, int z) { ChunkSnapshot chunk = chunk(getChunkCoordinate(x), getChunkCoordinate(z)); return Objects.requireNonNull(chunk).getBiome(x, y, z); } diff --git a/src/main/java/net/minestom/server/snapshot/SnapshotImpl.java b/src/main/java/net/minestom/server/snapshot/SnapshotImpl.java index 9d1e01ea833..839c0e2d916 100644 --- a/src/main/java/net/minestom/server/snapshot/SnapshotImpl.java +++ b/src/main/java/net/minestom/server/snapshot/SnapshotImpl.java @@ -8,12 +8,14 @@ import net.minestom.server.entity.GameMode; import net.minestom.server.instance.Section; import net.minestom.server.instance.block.Block; +import net.minestom.server.registry.DynamicRegistry; import net.minestom.server.tag.Tag; import net.minestom.server.tag.TagReadable; import net.minestom.server.utils.collection.IntMappedArray; import net.minestom.server.utils.collection.MappedCollection; +import net.minestom.server.utils.validate.Check; 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; @@ -44,7 +46,7 @@ public record Server(Collection instances, } public record Instance(AtomicReference serverRef, - DimensionType dimensionType, long worldAge, long time, + DynamicRegistry.Key dimensionType, long worldAge, long time, Map> chunksMap, int[] entitiesIds, TagReadable tagReadable) implements InstanceSnapshot { @@ -99,11 +101,13 @@ public record Chunk(int minSection, int chunkX, int chunkZ, } @Override - public @NotNull Biome getBiome(int x, int y, int z) { + public @NotNull DynamicRegistry.Key getBiome(int x, int y, int z) { 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); + DynamicRegistry.Key key = MinecraftServer.getBiomeRegistry().getKey(id); + Check.notNull(key, "Biome with id {0} is not registered", id); + return key; } @Override diff --git a/src/main/java/net/minestom/server/sound/BuiltinSoundEvent.java b/src/main/java/net/minestom/server/sound/BuiltinSoundEvent.java new file mode 100644 index 00000000000..4ad76088636 --- /dev/null +++ b/src/main/java/net/minestom/server/sound/BuiltinSoundEvent.java @@ -0,0 +1,45 @@ +package net.minestom.server.sound; + +import net.kyori.adventure.key.Key; +import net.minestom.server.registry.Registry; +import net.minestom.server.registry.StaticProtocolObject; +import net.minestom.server.utils.NamespaceID; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +record BuiltinSoundEvent(NamespaceID namespace, int id) implements StaticProtocolObject, SoundEvent { + private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.SOUNDS, + (namespace, properties) -> new BuiltinSoundEvent(NamespaceID.from(namespace), properties.getInt("id"))); + + static SoundEvent get(@NotNull String namespace) { + return CONTAINER.get(namespace); + } + + static SoundEvent getSafe(@NotNull String namespace) { + return CONTAINER.getSafe(namespace); + } + + static SoundEvent getId(int id) { + return CONTAINER.getId(id); + } + + static Collection values() { + return CONTAINER.values(); + } + + @Override + public String toString() { + return name(); + } + + @Override + public @NotNull String name() { + return StaticProtocolObject.super.name(); + } + + @Override + public @NotNull Key key() { + return StaticProtocolObject.super.key(); + } +} diff --git a/src/main/java/net/minestom/server/sound/CustomSoundEvent.java b/src/main/java/net/minestom/server/sound/CustomSoundEvent.java new file mode 100644 index 00000000000..a7c1e9dd2ea --- /dev/null +++ b/src/main/java/net/minestom/server/sound/CustomSoundEvent.java @@ -0,0 +1,8 @@ +package net.minestom.server.sound; + +import net.minestom.server.utils.NamespaceID; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +record CustomSoundEvent(@NotNull NamespaceID namespace, @Nullable Float range) implements SoundEvent { +} diff --git a/src/main/java/net/minestom/server/sound/SoundEvent.java b/src/main/java/net/minestom/server/sound/SoundEvent.java index 842790dc435..6856c6af7cb 100644 --- a/src/main/java/net/minestom/server/sound/SoundEvent.java +++ b/src/main/java/net/minestom/server/sound/SoundEvent.java @@ -1,34 +1,115 @@ package net.minestom.server.sound; import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.Keyed; import net.kyori.adventure.sound.Sound; -import net.minestom.server.registry.StaticProtocolObject; +import net.minestom.server.network.NetworkBuffer; +import net.minestom.server.registry.ProtocolObject; import net.minestom.server.utils.NamespaceID; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; -public sealed interface SoundEvent extends StaticProtocolObject, Sound.Type, SoundEvents permits SoundEventImpl { +/** + * Can represent a builtin/vanilla sound or a custom sound. + */ +public sealed interface SoundEvent extends ProtocolObject, Keyed, Sound.Type, SoundEvents permits BuiltinSoundEvent, CustomSoundEvent { - static @NotNull Collection<@NotNull SoundEvent> values() { - return SoundEventImpl.values(); + @NotNull NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { + @Override + public void write(@NotNull NetworkBuffer buffer, SoundEvent value) { + switch (value) { + case BuiltinSoundEvent soundEvent -> buffer.write(NetworkBuffer.VAR_INT, soundEvent.id() + 1); + case CustomSoundEvent soundEvent -> { + buffer.write(NetworkBuffer.VAR_INT, 0); // Custom sound + buffer.write(NetworkBuffer.STRING, soundEvent.name()); + buffer.writeOptional(NetworkBuffer.FLOAT, soundEvent.range()); + } + } + } + + @Override + public SoundEvent read(@NotNull NetworkBuffer buffer) { + int id = buffer.read(NetworkBuffer.VAR_INT) - 1; + if (id != -1) return BuiltinSoundEvent.getId(id); + + NamespaceID namespace = NamespaceID.from(buffer.read(NetworkBuffer.STRING)); + return new CustomSoundEvent(namespace, buffer.readOptional(NetworkBuffer.FLOAT)); + } + }; + + /** + * Get all the builtin sound events. Resource pack sounds will never be returned from this method. + */ + static @NotNull Collection values() { + return BuiltinSoundEvent.values(); } + /** + * Get a builtin sound event by its namespace ID. Will never return a custom/resource pack sound. + * + * @param namespaceID the namespace ID of the sound event + * @return the sound event, or null if not found + */ static @Nullable SoundEvent fromNamespaceId(@NotNull String namespaceID) { - return SoundEventImpl.getSafe(namespaceID); + return BuiltinSoundEvent.getSafe(namespaceID); } + /** + * Get a builtin sound event by its namespace ID. Will never return a custom/resource pack sound. + * + * @param namespaceID the namespace ID of the sound event + * @return the sound event, or null if not found + */ static @Nullable SoundEvent fromNamespaceId(@NotNull NamespaceID namespaceID) { return fromNamespaceId(namespaceID.asString()); } + /** + * Get a builtin sound event by its protocol ID. Will never return a custom/resource pack sound. + * + * @param id the ID of the sound event + * @return the sound event, or null if not found + */ static @Nullable SoundEvent fromId(int id) { - return SoundEventImpl.getId(id); + return BuiltinSoundEvent.getId(id); + } + + /** + * Create a custom sound event. The namespace should match a sound provided in the resource pack. + * + * @param namespaceID the namespace ID of the custom sound event + * @param range the range of the sound event, or null for (legacy) dynamic range + * @return the custom sound event + */ + static @NotNull SoundEvent of(@NotNull String namespaceID, @Nullable Float range) { + return new CustomSoundEvent(NamespaceID.from(namespaceID), range); + } + + /** + * Create a custom sound event. The {@link NamespaceID} should match a sound provided in the resource pack. + * @param namespaceID the namespace ID of the custom sound event + * @param range the range of the sound event, or null for (legacy) dynamic range + * @return the custom sound event + */ + static @NotNull SoundEvent of(@NotNull NamespaceID namespaceID, @Nullable Float range) { + return new CustomSoundEvent(namespaceID, range); + } + + @Contract(pure = true) + @NotNull NamespaceID namespace(); + + @Contract(pure = true) + default @NotNull String name() { + return namespace().asString(); } @Override + @Contract(pure = true) default @NotNull Key key() { - return StaticProtocolObject.super.key(); + return namespace(); } + } diff --git a/src/main/java/net/minestom/server/sound/SoundEventImpl.java b/src/main/java/net/minestom/server/sound/SoundEventImpl.java deleted file mode 100644 index 7d2a55e6d94..00000000000 --- a/src/main/java/net/minestom/server/sound/SoundEventImpl.java +++ /dev/null @@ -1,36 +0,0 @@ -package net.minestom.server.sound; - -import net.minestom.server.registry.Registry; -import net.minestom.server.utils.NamespaceID; -import org.jetbrains.annotations.NotNull; - -import java.util.Collection; - -record SoundEventImpl(NamespaceID namespace, int id) implements SoundEvent { - private static final Registry.Container CONTAINER = Registry.createStaticContainer(Registry.Resource.SOUNDS, SoundEventImpl::createImpl); - - private static SoundEvent createImpl(String namespace, Registry.Properties properties) { - return new SoundEventImpl(NamespaceID.from(namespace), properties.getInt("id")); - } - - static SoundEvent get(@NotNull String namespace) { - return CONTAINER.get(namespace); - } - - static SoundEvent getSafe(@NotNull String namespace) { - return CONTAINER.getSafe(namespace); - } - - static SoundEvent getId(int id) { - return CONTAINER.getId(id); - } - - static Collection values() { - return CONTAINER.values(); - } - - @Override - public String toString() { - return name(); - } -} diff --git a/src/main/java/net/minestom/server/tag/Serializers.java b/src/main/java/net/minestom/server/tag/Serializers.java index 9d14f112bdb..f26ff11558f 100644 --- a/src/main/java/net/minestom/server/tag/Serializers.java +++ b/src/main/java/net/minestom/server/tag/Serializers.java @@ -1,52 +1,51 @@ package net.minestom.server.tag; +import net.kyori.adventure.nbt.*; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.minestom.server.ServerFlag; import net.minestom.server.item.ItemStack; -import org.jglrxavpok.hephaistos.nbt.*; +import net.minestom.server.utils.UniqueIdUtils; -import java.util.UUID; import java.util.function.Function; /** * Basic serializers for {@link Tag tags}. */ final class Serializers { - static final Entry BYTE = new Entry<>(NBTType.TAG_Byte, NBTByte::getValue, NBT::Byte); - static final Entry BOOLEAN = new Entry<>(NBTType.TAG_Byte, NBTByte::asBoolean, NBT::Boolean); - static final Entry SHORT = new Entry<>(NBTType.TAG_Short, NBTShort::getValue, NBT::Short); - static final Entry INT = new Entry<>(NBTType.TAG_Int, NBTInt::getValue, NBT::Int); - static final Entry LONG = new Entry<>(NBTType.TAG_Long, NBTLong::getValue, NBT::Long); - static final Entry FLOAT = new Entry<>(NBTType.TAG_Float, NBTFloat::getValue, NBT::Float); - static final Entry DOUBLE = new Entry<>(NBTType.TAG_Double, NBTDouble::getValue, NBT::Double); - static final Entry STRING = new Entry<>(NBTType.TAG_String, NBTString::getValue, NBT::String); - static final Entry NBT_ENTRY = new Entry<>(null, Function.identity(), Function.identity()); - - static final Entry UUID = new Entry<>(NBTType.TAG_Int_Array, intArray -> intArrayToUuid(intArray.getValue().copyArray()), - uuid -> NBT.IntArray(uuidToIntArray(uuid))); - static final Entry ITEM = new Entry<>(NBTType.TAG_Compound, ItemStack::fromItemNBT, ItemStack::toItemNBT); - static final Entry COMPONENT = new Entry<>(NBTType.TAG_String, input -> GsonComponentSerializer.gson().deserialize(input.getValue()), - component -> NBT.String(GsonComponentSerializer.gson().serialize(component))); - - static final Entry EMPTY = new Entry<>(NBTType.TAG_Byte, unused -> null, component -> null); - - static Entry fromTagSerializer(TagSerializer serializer) { - return new Serializers.Entry<>(NBTType.TAG_Compound, - (NBTCompound compound) -> { - if ((!ServerFlag.SERIALIZE_EMPTY_COMPOUND) && compound.isEmpty()) return null; + static final Entry BYTE = new Entry<>(BinaryTagTypes.BYTE, ByteBinaryTag::value, ByteBinaryTag::byteBinaryTag); + static final Entry BOOLEAN = new Entry<>(BinaryTagTypes.BYTE, b -> b.value() != 0, b -> b ? ByteBinaryTag.ONE : ByteBinaryTag.ZERO); + static final Entry SHORT = new Entry<>(BinaryTagTypes.SHORT, ShortBinaryTag::value, ShortBinaryTag::shortBinaryTag); + static final Entry INT = new Entry<>(BinaryTagTypes.INT, IntBinaryTag::value, IntBinaryTag::intBinaryTag); + static final Entry LONG = new Entry<>(BinaryTagTypes.LONG, LongBinaryTag::value, LongBinaryTag::longBinaryTag); + static final Entry FLOAT = new Entry<>(BinaryTagTypes.FLOAT, FloatBinaryTag::value, FloatBinaryTag::floatBinaryTag); + static final Entry DOUBLE = new Entry<>(BinaryTagTypes.DOUBLE, DoubleBinaryTag::value, DoubleBinaryTag::doubleBinaryTag); + static final Entry STRING = new Entry<>(BinaryTagTypes.STRING, StringBinaryTag::value, StringBinaryTag::stringBinaryTag); + static final Entry NBT_ENTRY = new Entry<>(null, Function.identity(), Function.identity()); + + static final Entry UUID = new Entry<>(BinaryTagTypes.INT_ARRAY, UniqueIdUtils::fromNbt, UniqueIdUtils::toNbt); + static final Entry ITEM = new Entry<>(BinaryTagTypes.COMPOUND, ItemStack::fromItemNBT, ItemStack::toItemNBT); + static final Entry COMPONENT = new Entry<>(BinaryTagTypes.STRING, input -> GsonComponentSerializer.gson().deserialize(input.value()), + component -> StringBinaryTag.stringBinaryTag(GsonComponentSerializer.gson().serialize(component))); + + static final Entry EMPTY = new Entry<>(BinaryTagTypes.BYTE, unused -> null, component -> null); + + static Entry fromTagSerializer(TagSerializer serializer) { + return new Serializers.Entry<>(BinaryTagTypes.COMPOUND, + (CompoundBinaryTag compound) -> { + if ((!ServerFlag.SERIALIZE_EMPTY_COMPOUND) && compound.size() == 0) return null; return serializer.read(TagHandler.fromCompound(compound)); }, (value) -> { - if (value == null) return NBTCompound.EMPTY; + if (value == null) return CompoundBinaryTag.empty(); TagHandler handler = TagHandler.newHandler(); serializer.write(handler, value); return handler.asCompound(); }); } - record Entry(NBTType nbtType, Function reader, Function writer, boolean isPath) { - Entry(NBTType nbtType, Function reader, Function writer) { + record Entry(BinaryTagType nbtType, Function reader, Function writer, boolean isPath) { + Entry(BinaryTagType nbtType, Function reader, Function writer) { this(nbtType, reader, writer, false); } @@ -58,22 +57,4 @@ N write(T value) { return writer.apply(value); } } - - private static int[] uuidToIntArray(UUID uuid) { - final long uuidMost = uuid.getMostSignificantBits(); - final long uuidLeast = uuid.getLeastSignificantBits(); - return new int[]{ - (int) (uuidMost >> 32), - (int) uuidMost, - (int) (uuidLeast >> 32), - (int) uuidLeast - }; - } - - private static UUID intArrayToUuid(int[] array) { - final long uuidMost = (long) array[0] << 32 | array[1] & 0xFFFFFFFFL; - final long uuidLeast = (long) array[2] << 32 | array[3] & 0xFFFFFFFFL; - - return new UUID(uuidMost, uuidLeast); - } } diff --git a/src/main/java/net/minestom/server/tag/Tag.java b/src/main/java/net/minestom/server/tag/Tag.java index 807cc36784e..597eb81ed39 100644 --- a/src/main/java/net/minestom/server/tag/Tag.java +++ b/src/main/java/net/minestom/server/tag/Tag.java @@ -1,5 +1,6 @@ package net.minestom.server.tag; +import net.kyori.adventure.nbt.*; import net.kyori.adventure.text.Component; import net.minestom.server.item.ItemStack; import net.minestom.server.utils.collection.AutoIncrementMap; @@ -7,11 +8,6 @@ import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jglrxavpok.hephaistos.nbt.NBT; -import org.jglrxavpok.hephaistos.nbt.NBTCompoundLike; -import org.jglrxavpok.hephaistos.nbt.NBTList; -import org.jglrxavpok.hephaistos.nbt.NBTType; -import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound; import java.util.Arrays; import java.util.List; @@ -37,7 +33,7 @@ record PathEntry(String name, int index) { final int index; private final String key; - final Serializers.Entry entry; + final Serializers.Entry entry; private final Supplier defaultValue; final Function readComparator; @@ -48,7 +44,7 @@ record PathEntry(String name, int index) { Tag(int index, String key, Function readComparator, - Serializers.Entry entry, + Serializers.Entry entry, Supplier defaultValue, PathEntry[] path, UnaryOperator copy, int listScope) { assert index == INDEX_MAP.get(key); this.index = index; @@ -61,8 +57,8 @@ record PathEntry(String name, int index) { this.listScope = listScope; } - static Tag tag(@NotNull String key, @NotNull Serializers.Entry entry) { - return new Tag<>(INDEX_MAP.get(key), key, entry.reader(), (Serializers.Entry) entry, + static Tag tag(@NotNull String key, @NotNull Serializers.Entry entry) { + return new Tag<>(INDEX_MAP.get(key), key, entry.reader(), (Serializers.Entry) entry, null, null, null, 0); } @@ -98,11 +94,11 @@ public Tag defaultValue(@NotNull T defaultValue) { public Tag map(@NotNull Function readMap, @NotNull Function writeMap) { var entry = this.entry; - final Function readFunction = entry.reader().andThen(t -> { + final Function readFunction = entry.reader().andThen(t -> { if (t == null) return null; return readMap.apply(t); }); - final Function writeFunction = writeMap.andThen(entry.writer()); + final Function writeFunction = writeMap.andThen(entry.writer()); return new Tag<>(index, key, readMap, new Serializers.Entry<>(entry.nbtType(), readFunction, writeFunction), // Default value @@ -114,24 +110,23 @@ public Tag map(@NotNull Function readMap, path, null, listScope); } - @ApiStatus.Experimental @Contract(value = "-> new", pure = true) public Tag> list() { var entry = this.entry; var readFunction = entry.reader(); var writeFunction = entry.writer(); - var listEntry = new Serializers.Entry, NBTList>( - NBTType.TAG_List, + var listEntry = new Serializers.Entry, ListBinaryTag>( + BinaryTagTypes.LIST, read -> { - if (read.isEmpty()) return List.of(); - return read.asListView().stream().map(readFunction).toList(); + if (read.size() == 0) return List.of(); + return read.stream().map(readFunction).toList(); }, write -> { if (write.isEmpty()) - return NBT.List(NBTType.TAG_String); // String is the default type for lists - final List list = write.stream().map(writeFunction).toList(); - final NBTType type = list.get(0).getID(); - return NBT.List(type, list); + return ListBinaryTag.empty(); + final List list = write.stream().map(writeFunction).toList(); + final BinaryTagType type = list.get(0).type(); + return ListBinaryTag.listBinaryTag(type, list); }); UnaryOperator> co = this.copy != null ? ts -> { final int size = ts.size(); @@ -149,7 +144,6 @@ public Tag> list() { null, path, co, listScope + 1); } - @ApiStatus.Experimental @Contract(value = "_ -> new", pure = true) public Tag path(@NotNull String @Nullable ... path) { if (path == null || path.length == 0) { @@ -165,8 +159,8 @@ public Tag path(@NotNull String @Nullable ... path) { return new Tag<>(index, key, readComparator, entry, defaultValue, pathEntries, copy, listScope); } - public @Nullable T read(@NotNull NBTCompoundLike nbt) { - final NBT readable = isView() ? nbt.toCompound() : nbt.get(key); + public @Nullable T read(@NotNull CompoundBinaryTag nbt) { + final BinaryTag readable = isView() ? nbt : nbt.get(key); final T result; try { if (readable == null || (result = entry.read(readable)) == null) @@ -177,18 +171,20 @@ public Tag path(@NotNull String @Nullable ... path) { } } - public void write(@NotNull MutableNBTCompound nbtCompound, @Nullable T value) { + public void write(@NotNull CompoundBinaryTag.Builder nbtCompound, @Nullable T value) { if (value != null) { - final NBT nbt = entry.write(value); - if (isView()) nbtCompound.copyFrom((NBTCompoundLike) nbt); - else nbtCompound.set(key, nbt); + final BinaryTag nbt = entry.write(value); + if (isView()) nbtCompound.put((CompoundBinaryTag) nbt); + else nbtCompound.put(key, nbt); } else { - if (isView()) nbtCompound.clear(); - else nbtCompound.remove(key); + if (isView()) { + // Adventure compound builder doesn't currently have a clear method. + nbtCompound.build().keySet().forEach(nbtCompound::remove); + } else nbtCompound.remove(key); } } - public void writeUnsafe(@NotNull MutableNBTCompound nbtCompound, @Nullable Object value) { + public void writeUnsafe(@NotNull CompoundBinaryTag.Builder nbtCompound, @Nullable Object value) { //noinspection unchecked write(nbtCompound, (T) value); } @@ -265,7 +261,6 @@ public int hashCode() { return tag(key, Serializers.STRING); } - @ApiStatus.Experimental public static @NotNull Tag UUID(@NotNull String key) { return tag(key, Serializers.UUID); } @@ -279,11 +274,11 @@ public int hashCode() { } /** - * Creates a flexible tag able to read and write any {@link NBT} objects. + * Creates a flexible tag able to read and write any {@link BinaryTag} objects. *

* Specialized tags are recommended if the type is known as conversion will be required both way (read and write). */ - public static @NotNull Tag NBT(@NotNull String key) { + public static @NotNull Tag NBT(@NotNull String key) { return tag(key, Serializers.NBT_ENTRY); } diff --git a/src/main/java/net/minestom/server/tag/TagHandler.java b/src/main/java/net/minestom/server/tag/TagHandler.java index ded8d448744..448fe26699d 100644 --- a/src/main/java/net/minestom/server/tag/TagHandler.java +++ b/src/main/java/net/minestom/server/tag/TagHandler.java @@ -1,12 +1,7 @@ package net.minestom.server.tag; -import org.jetbrains.annotations.ApiStatus; +import net.kyori.adventure.nbt.CompoundBinaryTag; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.UnknownNullability; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; -import org.jglrxavpok.hephaistos.nbt.NBTCompoundLike; - -import java.util.function.UnaryOperator; /** * Represents an element which can read and write {@link Tag tags}. @@ -26,7 +21,7 @@ public interface TagHandler extends TagReadable, TagWritable { /** * Creates a copy of this handler. *

- * Similar to {@link #fromCompound(NBTCompoundLike)} using {@link #asCompound()} + * Similar to {@link #fromCompound(CompoundBinaryTag)} using {@link #asCompound()} * with the advantage that cached objects and adaptive optimizations may be reused. * * @return a copy of this handler @@ -36,43 +31,30 @@ public interface TagHandler extends TagReadable, TagWritable { /** * Updates the content of this handler. *

- * Can be used as a clearing method with {@link NBTCompound#EMPTY}. + * Can be used as a clearing method with {@link CompoundBinaryTag#empty()}. * * @param compound the new content of this handler */ - void updateContent(@NotNull NBTCompoundLike compound); + void updateContent(@NotNull CompoundBinaryTag compound); /** - * Converts the content of this handler into a {@link NBTCompound}. + * Converts the content of this handler into a {@link CompoundBinaryTag}. * * @return a nbt compound representation of this handler */ - @NotNull NBTCompound asCompound(); - - @ApiStatus.Experimental - void updateTag(@NotNull Tag tag, - @NotNull UnaryOperator<@UnknownNullability T> value); - - @ApiStatus.Experimental - @UnknownNullability T updateAndGetTag(@NotNull Tag tag, - @NotNull UnaryOperator<@UnknownNullability T> value); - - @ApiStatus.Experimental - @UnknownNullability T getAndUpdateTag(@NotNull Tag tag, - @NotNull UnaryOperator<@UnknownNullability T> value); + @NotNull CompoundBinaryTag asCompound(); - @ApiStatus.Experimental static @NotNull TagHandler newHandler() { return new TagHandlerImpl(); } /** - * Copy the content of the given {@link NBTCompoundLike} into a new {@link TagHandler}. + * Copy the content of the given {@link CompoundBinaryTag} into a new {@link TagHandler}. * * @param compound the compound to read tags from * @return a new tag handler with the content of the given compound */ - static @NotNull TagHandler fromCompound(@NotNull NBTCompoundLike compound) { + static @NotNull TagHandler fromCompound(@NotNull CompoundBinaryTag compound) { return TagHandlerImpl.fromCompound(compound); } } diff --git a/src/main/java/net/minestom/server/tag/TagHandlerImpl.java b/src/main/java/net/minestom/server/tag/TagHandlerImpl.java index 6f040318279..92a304ccc69 100644 --- a/src/main/java/net/minestom/server/tag/TagHandlerImpl.java +++ b/src/main/java/net/minestom/server/tag/TagHandlerImpl.java @@ -1,22 +1,21 @@ package net.minestom.server.tag; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.BinaryTagType; +import net.kyori.adventure.nbt.BinaryTagTypes; +import net.kyori.adventure.nbt.CompoundBinaryTag; import net.minestom.server.ServerFlag; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.UnknownNullability; -import org.jglrxavpok.hephaistos.nbt.NBT; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; -import org.jglrxavpok.hephaistos.nbt.NBTCompoundLike; -import org.jglrxavpok.hephaistos.nbt.NBTType; -import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound; import java.lang.invoke.VarHandle; import java.util.Map; import java.util.function.UnaryOperator; final class TagHandlerImpl implements TagHandler { - static final Serializers.Entry NODE_SERIALIZER = new Serializers.Entry<>(NBTType.TAG_Compound, entries -> fromCompound(entries).root, Node::compound, true); + static final Serializers.Entry NODE_SERIALIZER = new Serializers.Entry<>(BinaryTagTypes.COMPOUND, entries -> fromCompound(entries).root, Node::compound, true); private final Node root; private volatile Node copy; @@ -29,8 +28,7 @@ final class TagHandlerImpl implements TagHandler { this.root = new Node(); } - static TagHandlerImpl fromCompound(NBTCompoundLike compoundLike) { - final NBTCompound compound = compoundLike.toCompound(); + static TagHandlerImpl fromCompound(CompoundBinaryTag compound) { TagHandlerImpl handler = new TagHandlerImpl(); TagNbtSeparator.separate(compound, entry -> handler.setTag(entry.tag(), entry.value())); handler.root.compound = compound; @@ -50,7 +48,7 @@ public void setTag(@NotNull Tag tag, @Nullable T value) { synchronized (this) { Node syncNode = traversePathWrite(root, tag, value != null); if (syncNode != null) { - syncNode.updateContent(value != null ? (NBTCompound) tag.entry.write(value) : NBTCompound.EMPTY); + syncNode.updateContent(value != null ? (CompoundBinaryTag) tag.entry.write(value) : CompoundBinaryTag.empty()); syncNode.invalidate(); } } @@ -83,6 +81,11 @@ public void setTag(@NotNull Tag tag, @Nullable T value) { node.invalidate(); } + @Override + public @Nullable T getAndSetTag(@NotNull Tag tag, @Nullable T value) { + return updateTag0(tag, t -> value, true); + } + @Override public void updateTag(@NotNull Tag tag, @NotNull UnaryOperator<@UnknownNullability T> value) { updateTag0(tag, value, false); @@ -103,7 +106,7 @@ private synchronized T updateTag0(@NotNull Tag tag, @NotNull UnaryOperato if (tag.isView()) { final T previousValue = tag.read(node.compound()); final T newValue = value.apply(previousValue); - node.updateContent((NBTCompoundLike) tag.entry.write(newValue)); + node.updateContent((CompoundBinaryTag) tag.entry.write(newValue)); node.invalidate(); return returnPrevious ? previousValue : newValue; } @@ -116,7 +119,7 @@ private synchronized T updateTag0(@NotNull Tag tag, @NotNull UnaryOperato if (previousEntry != null) { final Object previousTmp = previousEntry.value; if (previousTmp instanceof Node n) { - final NBTCompound compound = NBT.Compound(Map.of(tag.getKey(), n.compound())); + final CompoundBinaryTag compound = CompoundBinaryTag.from(Map.of(tag.getKey(), n.compound())); previousValue = tag.read(compound); } else { previousValue = (T) previousTmp; @@ -149,12 +152,12 @@ private synchronized T updateTag0(@NotNull Tag tag, @NotNull UnaryOperato } @Override - public synchronized void updateContent(@NotNull NBTCompoundLike compound) { + public synchronized void updateContent(@NotNull CompoundBinaryTag compound) { this.root.updateContent(compound); } @Override - public @NotNull NBTCompound asCompound() { + public @NotNull CompoundBinaryTag asCompound() { VarHandle.fullFence(); return root.compound(); } @@ -200,7 +203,7 @@ private Node traversePathWrite(Node root, Tag tag, // Slow path is taken if the entry comes from a Structure tag, requiring conversion from NBT Node tmp = local; local = new Node(tmp); - if (synEntry != null && synEntry.updatedNbt() instanceof NBTCompound compound) { + if (synEntry != null && synEntry.updatedNbt() instanceof CompoundBinaryTag compound) { local.updateContent(compound); } tmp.entries.put(pathIndex, Entry.makePathEntry(path.name(), local)); @@ -211,8 +214,8 @@ private Node traversePathWrite(Node root, Tag tag, } private Entry valueToEntry(Node parent, Tag tag, @NotNull T value) { - if (value instanceof NBT nbt) { - if (nbt instanceof NBTCompound compound) { + if (value instanceof BinaryTag nbt) { + if (nbt instanceof CompoundBinaryTag compound) { final TagHandlerImpl handler = fromCompound(compound); return Entry.makePathEntry(tag, new Node(parent, handler.root.entries)); } else { @@ -227,7 +230,7 @@ private Entry valueToEntry(Node parent, Tag tag, @NotNull T value) { final class Node implements TagReadable { final Node parent; final StaticIntMap> entries; - NBTCompound compound; + CompoundBinaryTag compound; public Node(Node parent, StaticIntMap> entries) { this.parent = parent; @@ -260,44 +263,43 @@ public Node(Node parent, StaticIntMap> entries) { return (T) entry.value; } // Value must be parsed from nbt if the tag is different - final NBT nbt = entry.updatedNbt(); - final Serializers.Entry serializerEntry = tag.entry; - final NBTType type = serializerEntry.nbtType(); - return type == null || type == nbt.getID() ? serializerEntry.read(nbt) : tag.createDefault(); + final BinaryTag nbt = entry.updatedNbt(); + final Serializers.Entry serializerEntry = tag.entry; + final BinaryTagType type = serializerEntry.nbtType(); + return type == null || type.equals(nbt.type()) ? serializerEntry.read(nbt) : tag.createDefault(); } - void updateContent(@NotNull NBTCompoundLike compoundLike) { - final NBTCompound compound = compoundLike.toCompound(); + void updateContent(@NotNull CompoundBinaryTag compound) { final TagHandlerImpl converted = fromCompound(compound); this.entries.updateContent(converted.root.entries); this.compound = compound; } - NBTCompound compound() { - NBTCompound compound; + CompoundBinaryTag compound() { + CompoundBinaryTag compound; if (!ServerFlag.TAG_HANDLER_CACHE_ENABLED || (compound = this.compound) == null) { - MutableNBTCompound tmp = new MutableNBTCompound(); + CompoundBinaryTag.Builder tmp = CompoundBinaryTag.builder(); this.entries.forValues(entry -> { final Tag tag = entry.tag; - final NBT nbt = entry.updatedNbt(); - if (nbt != null && (!tag.entry.isPath() || (!ServerFlag.SERIALIZE_EMPTY_COMPOUND) && !((NBTCompound) nbt).isEmpty())) { + final BinaryTag nbt = entry.updatedNbt(); + if (nbt != null && (!tag.entry.isPath() || (!ServerFlag.SERIALIZE_EMPTY_COMPOUND) && ((CompoundBinaryTag) nbt).size() > 0)) { tmp.put(tag.getKey(), nbt); } }); - this.compound = compound = tmp.toCompound(); + this.compound = compound = tmp.build(); } return compound; } @Contract("null -> !null") Node copy(Node parent) { - MutableNBTCompound tmp = new MutableNBTCompound(); + CompoundBinaryTag.Builder tmp = CompoundBinaryTag.builder(); Node result = new Node(parent, new StaticIntMap.Array<>()); StaticIntMap> entries = result.entries; this.entries.forValues(entry -> { Tag tag = entry.tag; Object value = entry.value; - NBT nbt; + BinaryTag nbt; if (value instanceof Node node) { Node copy = node.copy(result); if (copy == null) @@ -313,9 +315,10 @@ Node copy(Node parent) { tmp.put(tag.getKey(), nbt); entries.put(tag.index, valueToEntry(result, tag, value)); }); - if ((!ServerFlag.SERIALIZE_EMPTY_COMPOUND) && tmp.isEmpty() && parent != null) + var compound = tmp.build(); + if ((!ServerFlag.SERIALIZE_EMPTY_COMPOUND) && compound.size() == 0 && parent != null) return null; // Empty child node - result.compound = tmp.toCompound(); + result.compound = compound; return result; } @@ -330,7 +333,7 @@ void invalidate() { private static final class Entry { private final Tag tag; T value; - NBT nbt; + BinaryTag nbt; Entry(Tag tag, T value) { this.tag = tag; @@ -345,9 +348,9 @@ static Entry makePathEntry(Tag tag, Node node) { return makePathEntry(tag.getKey(), node); } - NBT updatedNbt() { + BinaryTag updatedNbt() { if (tag.entry.isPath()) return ((Node) value).compound(); - NBT nbt = this.nbt; + BinaryTag nbt = this.nbt; if (nbt == null) this.nbt = nbt = tag.entry.write(value); return nbt; } @@ -360,7 +363,7 @@ void updateValue(T value) { Node toNode() { if (tag.entry.isPath()) return (Node) value; - if (updatedNbt() instanceof NBTCompound compound) { + if (updatedNbt() instanceof CompoundBinaryTag compound) { // Slow path forcing a conversion of the structure to NBTCompound // TODO should the handler be cached inside the entry? return fromCompound(compound).root; diff --git a/src/main/java/net/minestom/server/tag/TagNbtSeparator.java b/src/main/java/net/minestom/server/tag/TagNbtSeparator.java index c8096932733..93584dbb606 100644 --- a/src/main/java/net/minestom/server/tag/TagNbtSeparator.java +++ b/src/main/java/net/minestom/server/tag/TagNbtSeparator.java @@ -1,11 +1,8 @@ package net.minestom.server.tag; +import net.kyori.adventure.nbt.*; import net.minestom.server.MinecraftServer; -import net.minestom.server.exception.ExceptionManager; -import org.jglrxavpok.hephaistos.nbt.NBT; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; -import org.jglrxavpok.hephaistos.nbt.NBTList; -import org.jglrxavpok.hephaistos.nbt.NBTType; +import net.minestom.server.utils.nbt.BinaryTagUtil; import java.util.ArrayList; import java.util.List; @@ -17,30 +14,30 @@ import static java.util.Map.entry; /** - * Handles conversion of {@link NBT} subtypes into one or multiple primitive {@link Tag tags}. + * Handles conversion of {@link BinaryTag} subtypes into one or multiple primitive {@link Tag tags}. */ final class TagNbtSeparator { - static final Map, Function>> SUPPORTED_TYPES = Map.ofEntries( - entry(NBTType.TAG_Byte, Tag::Byte), - entry(NBTType.TAG_Short, Tag::Short), - entry(NBTType.TAG_Int, Tag::Integer), - entry(NBTType.TAG_Long, Tag::Long), - entry(NBTType.TAG_Float, Tag::Float), - entry(NBTType.TAG_Double, Tag::Double), - entry(NBTType.TAG_String, Tag::String)); + static final Map, Function>> SUPPORTED_TYPES = Map.ofEntries( + entry(BinaryTagTypes.BYTE, Tag::Byte), + entry(BinaryTagTypes.SHORT, Tag::Short), + entry(BinaryTagTypes.INT, Tag::Integer), + entry(BinaryTagTypes.LONG, Tag::Long), + entry(BinaryTagTypes.FLOAT, Tag::Float), + entry(BinaryTagTypes.DOUBLE, Tag::Double), + entry(BinaryTagTypes.STRING, Tag::String)); - static void separate(NBTCompound nbtCompound, Consumer consumer) { + static void separate(CompoundBinaryTag nbtCompound, Consumer consumer) { for (var ent : nbtCompound) { convert(new ArrayList<>(), ent.getKey(), ent.getValue(), consumer); } } - static void separate(String key, NBT nbt, Consumer consumer) { + static void separate(String key, BinaryTag nbt, Consumer consumer) { convert(new ArrayList<>(), key, nbt, consumer); } - static Entry separateSingle(String key, NBT nbt) { - assert !(nbt instanceof NBTCompound); + static Entry separateSingle(String key, BinaryTag nbt) { + assert !(nbt instanceof CompoundBinaryTag); AtomicReference> entryRef = new AtomicReference<>(); convert(new ArrayList<>(), key, nbt, entry -> { assert entryRef.getPlain() == null : "Multiple entries found for nbt tag: " + key + " -> " + nbt; @@ -51,28 +48,28 @@ static Entry separateSingle(String key, NBT nbt) { return entry; } - private static void convert(List path, String key, NBT nbt, Consumer consumer) { - var tagFunction = SUPPORTED_TYPES.get(nbt.getID()); + private static void convert(List path, String key, BinaryTag nbt, Consumer consumer) { + var tagFunction = SUPPORTED_TYPES.get(nbt.type()); if (tagFunction != null) { Tag tag = tagFunction.apply(key); - consumer.accept(makeEntry(path, tag, nbt.getValue())); - } else if (nbt instanceof NBTCompound nbtCompound) { + consumer.accept(makeEntry(path, tag, BinaryTagUtil.nbtValueFromTag(nbt))); + } else if (nbt instanceof CompoundBinaryTag nbtCompound) { for (var ent : nbtCompound) { var newPath = new ArrayList<>(path); newPath.add(key); convert(newPath, ent.getKey(), ent.getValue(), consumer); } - } else if (nbt instanceof NBTList nbtList) { - tagFunction = SUPPORTED_TYPES.get(nbtList.getSubtagType()); + } else if (nbt instanceof ListBinaryTag nbtList) { + tagFunction = SUPPORTED_TYPES.get(nbtList.elementType()); if (tagFunction == null) { // Invalid list subtype, fallback to nbt consumer.accept(makeEntry(path, Tag.NBT(key), nbt)); } else { try { var tag = tagFunction.apply(key).list(); - Object[] values = new Object[nbtList.getSize()]; + Object[] values = new Object[nbtList.size()]; for (int i = 0; i < values.length; i++) { - values[i] = nbtList.get(i).getValue(); + values[i] = BinaryTagUtil.nbtValueFromTag(nbtList.get(i)); } consumer.accept(makeEntry(path, Tag.class.cast(tag), List.of(values))); } catch (Exception e) { diff --git a/src/main/java/net/minestom/server/tag/TagRecord.java b/src/main/java/net/minestom/server/tag/TagRecord.java index 3c15d198cec..6cc3d33d757 100644 --- a/src/main/java/net/minestom/server/tag/TagRecord.java +++ b/src/main/java/net/minestom/server/tag/TagRecord.java @@ -1,11 +1,11 @@ package net.minestom.server.tag; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; import net.kyori.adventure.text.Component; import net.minestom.server.item.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jglrxavpok.hephaistos.nbt.NBT; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -44,7 +44,7 @@ protected Serializer computeValue(Class type) { final Tag tag; if (componentType.isRecord()) { tag = Tag.Structure(componentName, serializers.get(componentType)); - } else if (NBT.class.isAssignableFrom(componentType)) { + } else if (BinaryTag.class.isAssignableFrom(componentType)) { tag = Tag.NBT(componentName); } else { final var fun = SUPPORTED_TYPES.get(componentType); @@ -73,7 +73,7 @@ protected Serializer computeValue(Class type) { static final class Serializer implements TagSerializer { final Constructor constructor; final Entry[] entries; - final Serializers.Entry serializerEntry; + final Serializers.Entry serializerEntry; Serializer(Constructor constructor, Entry[] entries) { this.constructor = constructor; diff --git a/src/main/java/net/minestom/server/tag/TagSerializer.java b/src/main/java/net/minestom/server/tag/TagSerializer.java index df3138d4ea4..314a49c2fe3 100644 --- a/src/main/java/net/minestom/server/tag/TagSerializer.java +++ b/src/main/java/net/minestom/server/tag/TagSerializer.java @@ -1,9 +1,9 @@ package net.minestom.server.tag; +import net.kyori.adventure.nbt.CompoundBinaryTag; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; import java.util.function.Function; @@ -31,11 +31,11 @@ public interface TagSerializer { void write(@NotNull TagWritable writer, @NotNull T value); @ApiStatus.Experimental - TagSerializer COMPOUND = TagSerializerImpl.COMPOUND; + TagSerializer COMPOUND = TagSerializerImpl.COMPOUND; @ApiStatus.Experimental - static TagSerializer fromCompound(@NotNull Function reader, - @NotNull Function writer) { + static TagSerializer fromCompound(@NotNull Function reader, + @NotNull Function writer) { return TagSerializerImpl.fromCompound(reader, writer); } } diff --git a/src/main/java/net/minestom/server/tag/TagSerializerImpl.java b/src/main/java/net/minestom/server/tag/TagSerializerImpl.java index 57f8f84beb9..53bc4d4514a 100644 --- a/src/main/java/net/minestom/server/tag/TagSerializerImpl.java +++ b/src/main/java/net/minestom/server/tag/TagSerializerImpl.java @@ -1,35 +1,35 @@ package net.minestom.server.tag; +import net.kyori.adventure.nbt.CompoundBinaryTag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; import java.util.function.Function; final class TagSerializerImpl { - public static final TagSerializer COMPOUND = new TagSerializer<>() { + public static final TagSerializer COMPOUND = new TagSerializer<>() { @Override - public @NotNull NBTCompound read(@NotNull TagReadable reader) { + public @NotNull CompoundBinaryTag read(@NotNull TagReadable reader) { return ((TagHandler) reader).asCompound(); } @Override - public void write(@NotNull TagWritable writer, @NotNull NBTCompound value) { + public void write(@NotNull TagWritable writer, @NotNull CompoundBinaryTag value) { TagNbtSeparator.separate(value, entry -> writer.setTag(entry.tag(), entry.value())); } }; - static TagSerializer fromCompound(Function readFunc, Function writeFunc) { + static TagSerializer fromCompound(Function readFunc, Function writeFunc) { return new TagSerializer<>() { @Override public @Nullable T read(@NotNull TagReadable reader) { - final NBTCompound compound = COMPOUND.read(reader); + final CompoundBinaryTag compound = COMPOUND.read(reader); return readFunc.apply(compound); } @Override public void write(@NotNull TagWritable writer, @NotNull T value) { - final NBTCompound compound = writeFunc.apply(value); + final CompoundBinaryTag compound = writeFunc.apply(value); COMPOUND.write(writer, compound); } }; diff --git a/src/main/java/net/minestom/server/tag/TagWritable.java b/src/main/java/net/minestom/server/tag/TagWritable.java index cd1135d59c1..2649732e32e 100644 --- a/src/main/java/net/minestom/server/tag/TagWritable.java +++ b/src/main/java/net/minestom/server/tag/TagWritable.java @@ -2,11 +2,14 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnknownNullability; + +import java.util.function.UnaryOperator; /** * Represents an element which can write {@link Tag tags}. */ -public interface TagWritable { +public interface TagWritable extends TagReadable { /** * Writes the specified type. @@ -20,4 +23,23 @@ public interface TagWritable { default void removeTag(@NotNull Tag tag) { setTag(tag, null); } + + /** + * Reads the current value, and then write the new one. + * + * @param tag the tag to write + * @param value the tag value, null to remove + * @param the tag type + * @return the previous tag value, null if not present + */ + @Nullable T getAndSetTag(@NotNull Tag tag, @Nullable T value); + + void updateTag(@NotNull Tag tag, + @NotNull UnaryOperator<@UnknownNullability T> value); + + @UnknownNullability T updateAndGetTag(@NotNull Tag tag, + @NotNull UnaryOperator<@UnknownNullability T> value); + + @UnknownNullability T getAndUpdateTag(@NotNull Tag tag, + @NotNull UnaryOperator<@UnknownNullability T> value); } diff --git a/src/main/java/net/minestom/server/tag/Taggable.java b/src/main/java/net/minestom/server/tag/Taggable.java index 39d49ca3b68..0b77502cbd2 100644 --- a/src/main/java/net/minestom/server/tag/Taggable.java +++ b/src/main/java/net/minestom/server/tag/Taggable.java @@ -4,6 +4,8 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.UnknownNullability; +import java.util.function.UnaryOperator; + public interface Taggable extends TagReadable, TagWritable { @NotNull TagHandler tagHandler(); @@ -27,4 +29,24 @@ default void setTag(@NotNull Tag tag, @Nullable T value) { default void removeTag(@NotNull Tag tag) { tagHandler().removeTag(tag); } + + @Override + default @Nullable T getAndSetTag(@NotNull Tag tag, @Nullable T value) { + return tagHandler().getAndSetTag(tag, value); + } + + @Override + default void updateTag(@NotNull Tag tag, @NotNull UnaryOperator<@UnknownNullability T> value) { + tagHandler().updateTag(tag, value); + } + + @Override + default @UnknownNullability T updateAndGetTag(@NotNull Tag tag, @NotNull UnaryOperator<@UnknownNullability T> value) { + return tagHandler().updateAndGetTag(tag, value); + } + + @Override + default @UnknownNullability T getAndUpdateTag(@NotNull Tag tag, @NotNull UnaryOperator<@UnknownNullability T> value) { + return tagHandler().getAndUpdateTag(tag, value); + } } diff --git a/src/main/java/net/minestom/server/terminal/TerminalColorConverter.java b/src/main/java/net/minestom/server/terminal/TerminalColorConverter.java index 845542cd13f..2c9fc094e28 100644 --- a/src/main/java/net/minestom/server/terminal/TerminalColorConverter.java +++ b/src/main/java/net/minestom/server/terminal/TerminalColorConverter.java @@ -3,7 +3,6 @@ import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.minestom.server.ServerFlag; -import net.minestom.server.utils.PropertyUtils; import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/src/main/java/net/minestom/server/thread/AcquirableSource.java b/src/main/java/net/minestom/server/thread/AcquirableSource.java new file mode 100644 index 00000000000..637ac087053 --- /dev/null +++ b/src/main/java/net/minestom/server/thread/AcquirableSource.java @@ -0,0 +1,23 @@ +package net.minestom.server.thread; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Consumer; + +/** + * An object that is a source of {@link Acquirable} objects, and can be synchronized within a {@link ThreadDispatcher}. + * + * @param the type of the acquired object + */ +@ApiStatus.Experimental +public interface AcquirableSource { + /** + * Obtains an {@link Acquirable}. To safely perform operations on this object, the user must call + * {@link Acquirable#sync(Consumer)}, {@link Acquirable#async(Consumer)}, or {@link Acquirable#lock()} (followed by + * a subsequent unlock) on the Acquirable instance. + * + * @return an Acquirable which can be used to synchronize access to this object + */ + @NotNull Acquirable acquirable(); +} diff --git a/src/main/java/net/minestom/server/thread/ThreadDispatcher.java b/src/main/java/net/minestom/server/thread/ThreadDispatcher.java index 6c073eca691..541a2f9ebdb 100644 --- a/src/main/java/net/minestom/server/thread/ThreadDispatcher.java +++ b/src/main/java/net/minestom/server/thread/ThreadDispatcher.java @@ -1,7 +1,6 @@ package net.minestom.server.thread; import net.minestom.server.Tickable; -import net.minestom.server.entity.Entity; import org.jctools.queues.MessagePassingQueue; import org.jctools.queues.MpscUnboundedArrayQueue; import org.jetbrains.annotations.ApiStatus; @@ -10,10 +9,20 @@ import java.util.*; import java.util.concurrent.CountDownLatch; +import java.util.function.IntFunction; /** - * Used to link chunks into multiple groups. - * Then executed into a thread pool. + * ThreadDispatcher can be used to dispatch updates (ticks) across a number of "partitions" (such as chunks) that + * house {@link Tickable} instances (such as entities). The parallelism of such updates is defined when the dispatcher + * is constructed. + *

+ * It is recommended that {@link Tickable}s being added to a dispatcher also implement {@link AcquirableSource}, as + * doing so will allow the user to synchronize external access to them using the {@link Acquirable} API. + *

+ * Instances of this class can be obtained by calling {@link ThreadDispatcher#of(ThreadProvider, int)}, or a similar + * overload. + * @see Acquirable + * @see AcquirableSource */ public final class ThreadDispatcher

{ private final ThreadProvider

provider; @@ -24,29 +33,67 @@ public final class ThreadDispatcher

{ private final Map partitions = new WeakHashMap<>(); // Cache to retrieve the threading context from a tickable element private final Map elements = new WeakHashMap<>(); - // Queue to update chunks linked thread + // Queue to update partition linked thread private final ArrayDeque

partitionUpdateQueue = new ArrayDeque<>(); // Requests consumed at the end of each tick private final MessagePassingQueue> updates = new MpscUnboundedArrayQueue<>(1024); - private ThreadDispatcher(ThreadProvider

provider, int threadCount) { + private ThreadDispatcher(ThreadProvider

provider, int threadCount, + @NotNull IntFunction threadGenerator) { this.provider = provider; TickThread[] threads = new TickThread[threadCount]; - Arrays.setAll(threads, TickThread::new); + Arrays.setAll(threads, threadGenerator); this.threads = List.of(threads); this.threads.forEach(Thread::start); } + /** + * Creates a new ThreadDispatcher using default thread names (ex. Ms-Tick-n). + * + * @param provider the {@link ThreadProvider} instance to be used for defining thread IDs + * @param threadCount the number of threads to create for this dispatcher + * @return a new ThreadDispatcher instance + * @param

the dispatcher partition type + */ public static

@NotNull ThreadDispatcher

of(@NotNull ThreadProvider

provider, int threadCount) { - return new ThreadDispatcher<>(provider, threadCount); + return new ThreadDispatcher<>(provider, threadCount, TickThread::new); + } + + /** + * Creates a new ThreadDispatcher using the caller-provided thread name generator {@code nameGenerator}. This is + * useful to disambiguate custom ThreadDispatcher instances from ones used in core Minestom code. + * + * @param provider the {@link ThreadProvider} instance to be used for defining thread IDs + * @param nameGenerator a function that should return unique names, given a thread index + * @param threadCount the number of threads to create for this dispatcher + * @return a new ThreadDispatcher instance + * @param

the dispatcher partition type + */ + public static

@NotNull ThreadDispatcher

of(@NotNull ThreadProvider

provider, + @NotNull IntFunction nameGenerator, int threadCount) { + return new ThreadDispatcher<>(provider, threadCount, index -> new TickThread(nameGenerator.apply(index))); } + /** + * Creates a single-threaded dispatcher that uses default thread names. + * + * @return a new ThreadDispatcher instance + * @param

the dispatcher partition type + */ public static

@NotNull ThreadDispatcher

singleThread() { return of(ThreadProvider.counter(), 1); } + /** + * Gets the unmodifiable list of TickThreads used to dispatch updates. + *

+ * This method is marked internal to reflect {@link TickThread}s own internal status. + * + * @return the TickThreads used to dispatch updates + */ @Unmodifiable + @ApiStatus.Internal public @NotNull List<@NotNull TickThread> threads() { return threads; } @@ -59,16 +106,16 @@ private ThreadDispatcher(ThreadProvider

provider, int threadCount) { public synchronized void updateAndAwait(long time) { // Update dispatcher this.updates.drain(update -> { - if (update instanceof DispatchUpdate.PartitionLoad

chunkUpdate) { - processLoadedPartition(chunkUpdate.partition()); - } else if (update instanceof DispatchUpdate.PartitionUnload

partitionUnload) { - processUnloadedPartition(partitionUnload.partition()); - } else if (update instanceof DispatchUpdate.ElementUpdate

elementUpdate) { - processUpdatedElement(elementUpdate.tickable(), elementUpdate.partition()); - } else if (update instanceof DispatchUpdate.ElementRemove elementRemove) { - processRemovedElement(elementRemove.tickable()); - } else { - throw new IllegalStateException("Unknown update type: " + update.getClass().getSimpleName()); + switch (update) { + case DispatchUpdate.PartitionLoad

chunkUpdate -> processLoadedPartition(chunkUpdate.partition()); + case DispatchUpdate.PartitionUnload

partitionUnload -> + processUnloadedPartition(partitionUnload.partition()); + case DispatchUpdate.ElementUpdate

elementUpdate -> + processUpdatedElement(elementUpdate.tickable(), elementUpdate.partition()); + case DispatchUpdate.ElementRemove

elementRemove -> processRemovedElement(elementRemove.tickable()); + case null, default -> + throw new IllegalStateException("Unknown update type: " + + (update == null ? "null" : update.getClass().getSimpleName())); } }); // Tick all partitions @@ -82,8 +129,8 @@ public synchronized void updateAndAwait(long time) { } /** - * Called at the end of each tick to clear removed entities, - * refresh the chunk linked to an entity, and chunk threads based on {@link ThreadProvider#findThread(Object)}. + * Called at the end of each tick to clear removed tickables, refresh the partition linked to a tickable, and + * partition threads based on {@link ThreadProvider#findThread(Object)}. * * @param nanoTimeout max time in nanoseconds to update partitions */ @@ -117,23 +164,48 @@ public void refreshThreads(long nanoTimeout) { } } + /** + * Refreshes all thread as per {@link ThreadDispatcher#refreshThreads(long)}, with a timeout of + * {@link Long#MAX_VALUE}. + */ public void refreshThreads() { refreshThreads(Long.MAX_VALUE); } - public void createPartition(P partition) { + /** + * Registers a new partition. + * + * @param partition the partition to register + */ + public void createPartition(@NotNull P partition) { signalUpdate(new DispatchUpdate.PartitionLoad<>(partition)); } - public void deletePartition(P partition) { + /** + * Deletes an existing partition. + * + * @param partition the partition to delete + */ + public void deletePartition(@NotNull P partition) { signalUpdate(new DispatchUpdate.PartitionUnload<>(partition)); } - public void updateElement(Tickable tickable, P partition) { + /** + * Updates a {@link Tickable}, signalling that it is a part of {@code partition}. + * + * @param tickable the Tickable to update + * @param partition the partition the Tickable is part of + */ + public void updateElement(@NotNull Tickable tickable, @NotNull P partition) { signalUpdate(new DispatchUpdate.ElementUpdate<>(tickable, partition)); } - public void removeElement(Tickable tickable) { + /** + * Removes a {@link Tickable}. + * + * @param tickable the Tickable to remove + */ + public void removeElement(@NotNull Tickable tickable) { signalUpdate(new DispatchUpdate.ElementRemove<>(tickable)); } @@ -200,12 +272,15 @@ private void processUpdatedElement(Tickable tickable, P partition) { if (partitionEntry != null) { this.elements.put(tickable, partitionEntry); partitionEntry.elements.add(tickable); - if (tickable instanceof Entity entity) { // TODO support other types - ((AcquirableImpl) entity.getAcquirable()).updateThread(partitionEntry.thread()); + if (tickable instanceof AcquirableSource acquirableSource) { + ((AcquirableImpl) acquirableSource.acquirable()).updateThread(partitionEntry.thread()); } } } + /** + * A data structure which may contain {@link Tickable}s, and is assigned a single {@link TickThread}. + */ public static final class Partition { private TickThread thread; private final List elements = new ArrayList<>(); @@ -214,10 +289,23 @@ private Partition(TickThread thread) { this.thread = thread; } + /** + * The {@link TickThread} used by this partition. + *

+ * This method is marked internal to reflect {@link TickThread}s own internal status. + * + * @return the TickThread used by this partition + */ + @ApiStatus.Internal public @NotNull TickThread thread() { return thread; } + /** + * The {@link Tickable}s assigned to this partition. + * + * @return the tickables assigned to this partition + */ public @NotNull List elements() { return elements; } diff --git a/src/main/java/net/minestom/server/thread/TickThread.java b/src/main/java/net/minestom/server/thread/TickThread.java index 27d6db51d86..21f3edb0fb9 100644 --- a/src/main/java/net/minestom/server/thread/TickThread.java +++ b/src/main/java/net/minestom/server/thread/TickThread.java @@ -34,6 +34,10 @@ public TickThread(int number) { super(MinecraftServer.THREAD_NAME_TICK + "-" + number); } + public TickThread(@NotNull String name) { + super(name); + } + public static @Nullable TickThread current() { if (Thread.currentThread() instanceof TickThread current) return current; diff --git a/src/main/java/net/minestom/server/timer/ExecutionType.java b/src/main/java/net/minestom/server/timer/ExecutionType.java index 5dfc17de1b6..78558fbe4d1 100644 --- a/src/main/java/net/minestom/server/timer/ExecutionType.java +++ b/src/main/java/net/minestom/server/timer/ExecutionType.java @@ -8,16 +8,5 @@ public enum ExecutionType { /** * Schedule tasks to execute at the end of the {@link Schedulable} tick */ - TICK_END, - /** - * @deprecated use {@link ExecutionType#TICK_START} - * to be removed in 1.20.5 - */ - @Deprecated() - SYNC, - /** - * to be removed in 1.20.5 - */ - @Deprecated() - ASYNC + TICK_END } diff --git a/src/main/java/net/minestom/server/timer/SchedulerImpl.java b/src/main/java/net/minestom/server/timer/SchedulerImpl.java index 1064cb5c807..e3bcf4e0c2b 100644 --- a/src/main/java/net/minestom/server/timer/SchedulerImpl.java +++ b/src/main/java/net/minestom/server/timer/SchedulerImpl.java @@ -9,7 +9,6 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executors; -import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -22,7 +21,6 @@ final class SchedulerImpl implements Scheduler { thread.setDaemon(true); return thread; }); - private static final ForkJoinPool EXECUTOR = ForkJoinPool.commonPool(); private final MpscUnboundedArrayQueue tasksToExecute = new MpscUnboundedArrayQueue<>(64); private final MpscUnboundedArrayQueue tickEndTasksToExecute = new MpscUnboundedArrayQueue<>(64); @@ -68,10 +66,7 @@ private void runTasks(MpscUnboundedArrayQueue targetQueue) { if (!targetQueue.isEmpty()) { targetQueue.drain(task -> { if (!task.isAlive()) return; - switch (task.executionType()) { - case TICK_START, TICK_END, SYNC -> handleTask(task); - case ASYNC -> EXECUTOR.submit(() -> handleTask(task)); - } + handleTask(task); }); } } @@ -94,14 +89,8 @@ private void safeExecute(TaskImpl task) { // Prevent the task from being executed in the current thread // By either adding the task to the execution queue or submitting it to the pool switch (task.executionType()) { - case TICK_START, SYNC -> tasksToExecute.offer(task); + case TICK_START -> tasksToExecute.offer(task); case TICK_END -> tickEndTasksToExecute.offer(task); - case ASYNC -> EXECUTOR.submit(() -> { - if (!task.isAlive()) { - return; - } - handleTask(task); - }); } } @@ -121,7 +110,7 @@ private void handleTask(TaskImpl task) { synchronized (this) { final int target = tickState + tickSchedule.tick(); var targetTaskQueue = switch (task.executionType()) { - case TICK_START, SYNC, ASYNC -> tickStartTaskQueue; + case TICK_START -> tickStartTaskQueue; case TICK_END -> tickEndTaskQueue; }; targetTaskQueue.computeIfAbsent(target, i -> new ArrayList<>()).add(task); diff --git a/src/main/java/net/minestom/server/utils/ArrayUtils.java b/src/main/java/net/minestom/server/utils/ArrayUtils.java index 729a480442d..ca21c1a23c1 100644 --- a/src/main/java/net/minestom/server/utils/ArrayUtils.java +++ b/src/main/java/net/minestom/server/utils/ArrayUtils.java @@ -76,4 +76,37 @@ public static Map toMap(@NotNull K[] keys, @NotNull V[] values, int default -> Map.copyOf(new Object2ObjectArrayMap<>(keys, values, length)); }; } + + public static long[] pack(int[] ints, int bitsPerEntry) { + int intsPerLong = (int) Math.floor(64d / bitsPerEntry); + long[] longs = new long[(int) Math.ceil(ints.length / (double) intsPerLong)]; + + long mask = (1L << bitsPerEntry) - 1L; + for (int i = 0; i < longs.length; i++) { + for (int intIndex = 0; intIndex < intsPerLong; intIndex++) { + int bitIndex = intIndex * bitsPerEntry; + int intActualIndex = intIndex + i * intsPerLong; + if (intActualIndex < ints.length) { + longs[i] |= (ints[intActualIndex] & mask) << bitIndex; + } + } + } + + return longs; + } + + public static void unpack(int[] out, long[] in, int bitsPerEntry) { + assert in.length != 0: "unpack input array is zero"; + + var intsPerLong = Math.floor(64d / bitsPerEntry); + var intsPerLongCeil = (int) Math.ceil(intsPerLong); + + long mask = (1L << bitsPerEntry) - 1L; + for (int i = 0; i < out.length; i++) { + int longIndex = i / intsPerLongCeil; + int subIndex = i % intsPerLongCeil; + + out[i] = (int) ((in[longIndex] >>> (bitsPerEntry * subIndex)) & mask); + } + } } diff --git a/src/main/java/net/minestom/server/utils/Direction.java b/src/main/java/net/minestom/server/utils/Direction.java index d24df0d7461..91d3241f0d6 100644 --- a/src/main/java/net/minestom/server/utils/Direction.java +++ b/src/main/java/net/minestom/server/utils/Direction.java @@ -1,5 +1,6 @@ package net.minestom.server.utils; +import net.minestom.server.coordinate.Vec; import org.jetbrains.annotations.NotNull; public enum Direction { @@ -15,11 +16,13 @@ public enum Direction { private final int normalX; private final int normalY; private final int normalZ; + private final Vec normalVec; Direction(int normalX, int normalY, int normalZ) { this.normalX = normalX; this.normalY = normalY; this.normalZ = normalZ; + this.normalVec = new Vec(normalX, normalY, normalZ); } public int normalX() { @@ -34,6 +37,30 @@ public int normalZ() { return normalZ; } + public Vec vec() { + return normalVec; + } + + public Vec mul(double mult) { + return normalVec.mul(mult); + } + + public boolean positive() { + return normalX > 0 || normalY > 0 || normalZ > 0; + } + + public boolean negative() { + return !positive(); + } + + public boolean vertical() { + return this == UP || this == DOWN; + } + + public boolean horizontal() { + return !vertical(); + } + public @NotNull Direction opposite() { return switch (this) { case UP -> DOWN; diff --git a/src/main/java/net/minestom/server/utils/ObjectPool.java b/src/main/java/net/minestom/server/utils/ObjectPool.java index 932b9621fa3..baa5ba0263f 100644 --- a/src/main/java/net/minestom/server/utils/ObjectPool.java +++ b/src/main/java/net/minestom/server/utils/ObjectPool.java @@ -1,7 +1,6 @@ package net.minestom.server.utils; import net.minestom.server.ServerFlag; -import net.minestom.server.network.socket.Server; import net.minestom.server.utils.binary.BinaryBuffer; import org.jctools.queues.MessagePassingQueue; import org.jctools.queues.MpmcUnboundedXaddArrayQueue; @@ -18,7 +17,6 @@ import java.util.function.UnaryOperator; @ApiStatus.Internal -@ApiStatus.Experimental public final class ObjectPool { private static final int QUEUE_SIZE = 32_768; diff --git a/src/main/java/net/minestom/server/utils/PropertyUtils.java b/src/main/java/net/minestom/server/utils/PropertyUtils.java deleted file mode 100644 index 25a796af4ed..00000000000 --- a/src/main/java/net/minestom/server/utils/PropertyUtils.java +++ /dev/null @@ -1,36 +0,0 @@ -package net.minestom.server.utils; - -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -@ApiStatus.Internal -public final class PropertyUtils { - private PropertyUtils() {} - - public static boolean getBoolean(String name, boolean defaultValue) { - boolean result = defaultValue; - try { - final String value = System.getProperty(name); - if (value != null) result = Boolean.parseBoolean(value); - } catch (IllegalArgumentException | NullPointerException ignored) { - } - return result; - } - - @Contract("_, null -> null; _, !null -> !null") - public static String getString(@NotNull String name, @Nullable String defaultValue) { - return System.getProperty(name, defaultValue); - } - - public static Float getFloat(String name, Float defaultValue) { - Float result = defaultValue; - try { - final String value = System.getProperty(name); - if (value != null) result = Float.parseFloat(value); - } catch (IllegalArgumentException | NullPointerException ignored) { - } - return result; - } -} diff --git a/src/main/java/net/minestom/server/utils/UniqueIdUtils.java b/src/main/java/net/minestom/server/utils/UniqueIdUtils.java index da8ccaef50e..93c6d86ea39 100644 --- a/src/main/java/net/minestom/server/utils/UniqueIdUtils.java +++ b/src/main/java/net/minestom/server/utils/UniqueIdUtils.java @@ -1,5 +1,6 @@ package net.minestom.server.utils; +import net.kyori.adventure.nbt.IntArrayBinaryTag; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -27,4 +28,30 @@ private UniqueIdUtils() {} public static boolean isUniqueId(@NotNull String input) { return !input.trim().isEmpty() && UNIQUE_ID_PATTERN.matcher(input).matches(); // Microtus - improve string pattern usage } + + public static @NotNull UUID fromNbt(@NotNull IntArrayBinaryTag tag) { + return intArrayToUuid(tag.value()); + } + + public static @NotNull IntArrayBinaryTag toNbt(@NotNull UUID uuid) { + return IntArrayBinaryTag.intArrayBinaryTag(uuidToIntArray(uuid)); + } + + public static int[] uuidToIntArray(UUID uuid) { + final long uuidMost = uuid.getMostSignificantBits(); + final long uuidLeast = uuid.getLeastSignificantBits(); + return new int[]{ + (int) (uuidMost >> 32), + (int) uuidMost, + (int) (uuidLeast >> 32), + (int) uuidLeast + }; + } + + public static UUID intArrayToUuid(int[] array) { + final long uuidMost = (long) array[0] << 32 | array[1] & 0xFFFFFFFFL; + final long uuidLeast = (long) array[2] << 32 | array[3] & 0xFFFFFFFFL; + + return new UUID(uuidMost, uuidLeast); + } } diff --git a/src/main/java/net/minestom/server/utils/Unit.java b/src/main/java/net/minestom/server/utils/Unit.java new file mode 100644 index 00000000000..3a2902c53be --- /dev/null +++ b/src/main/java/net/minestom/server/utils/Unit.java @@ -0,0 +1,7 @@ +package net.minestom.server.utils; + +public final class Unit { + //todo would rather just support void + + public static final Unit INSTANCE = new Unit(); +} diff --git a/src/main/java/net/minestom/server/utils/binary/BinaryBuffer.java b/src/main/java/net/minestom/server/utils/binary/BinaryBuffer.java index c4f6076280e..ef3e98355a6 100644 --- a/src/main/java/net/minestom/server/utils/binary/BinaryBuffer.java +++ b/src/main/java/net/minestom/server/utils/binary/BinaryBuffer.java @@ -2,8 +2,6 @@ import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; -import org.jglrxavpok.hephaistos.nbt.NBTReader; -import org.jglrxavpok.hephaistos.nbt.NBTWriter; import java.io.IOException; import java.nio.ByteBuffer; @@ -17,8 +15,6 @@ @ApiStatus.Internal public final class BinaryBuffer { private ByteBuffer nioBuffer; // To become a `MemorySegment` once released - private NBTReader nbtReader; - private NBTWriter nbtWriter; private final int capacity; private int readerOffset, writerOffset; diff --git a/src/main/java/net/minestom/server/utils/binary/BinaryReader.java b/src/main/java/net/minestom/server/utils/binary/BinaryReader.java index dbcaef4cb1b..dc85307a1a5 100644 --- a/src/main/java/net/minestom/server/utils/binary/BinaryReader.java +++ b/src/main/java/net/minestom/server/utils/binary/BinaryReader.java @@ -1,5 +1,6 @@ package net.minestom.server.utils.binary; +import net.kyori.adventure.nbt.BinaryTag; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.minestom.server.coordinate.Point; @@ -8,7 +9,6 @@ import net.minestom.server.utils.Either; import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.NotNull; -import org.jglrxavpok.hephaistos.nbt.NBT; import java.io.InputStream; import java.nio.BufferUnderflowException; @@ -187,7 +187,7 @@ public UUID readUuid() { } public ItemStack readItemStack() { - return buffer.read(ITEM); + return buffer.read(ItemStack.NETWORK_TYPE); } public Component readComponent(int maxLength) { @@ -263,7 +263,7 @@ public int available() { return buffer.readableBytes(); } - public NBT readTag() { + public BinaryTag readTag() { return buffer.read(NBT); } diff --git a/src/main/java/net/minestom/server/utils/binary/BinaryWriter.java b/src/main/java/net/minestom/server/utils/binary/BinaryWriter.java index c6ab077aeca..037c12fd4c4 100644 --- a/src/main/java/net/minestom/server/utils/binary/BinaryWriter.java +++ b/src/main/java/net/minestom/server/utils/binary/BinaryWriter.java @@ -1,5 +1,6 @@ package net.minestom.server.utils.binary; +import net.kyori.adventure.nbt.BinaryTag; import net.kyori.adventure.text.Component; import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Vec; @@ -8,7 +9,6 @@ import net.minestom.server.utils.Either; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; -import org.jglrxavpok.hephaistos.nbt.NBT; import java.io.OutputStream; import java.nio.ByteBuffer; @@ -164,10 +164,10 @@ public void writeBlockPosition(int x, int y, int z) { } public void writeItemStack(@NotNull ItemStack itemStack) { - this.buffer.write(ITEM, itemStack); + this.buffer.write(ItemStack.NETWORK_TYPE, itemStack); } - public void writeNBT(@NotNull String name, @NotNull NBT tag) { + public void writeNBT(@NotNull String name, @NotNull BinaryTag tag) { this.buffer.write(NBT, tag); } diff --git a/src/main/java/net/minestom/server/utils/block/BlockIterator.java b/src/main/java/net/minestom/server/utils/block/BlockIterator.java index 86230095855..1569670b3fa 100644 --- a/src/main/java/net/minestom/server/utils/block/BlockIterator.java +++ b/src/main/java/net/minestom/server/utils/block/BlockIterator.java @@ -15,8 +15,8 @@ */ public class BlockIterator implements Iterator { private final short[] signums = new short[3]; - private final Vec end; - private final boolean smooth; + private Vec end; + private boolean smooth; private boolean foundEnd = false; @@ -26,9 +26,9 @@ public class BlockIterator implements Iterator { double sideDistZ; //length of ray from one x or y-side to next x or y-side - private final double deltaDistX; - private final double deltaDistY; - private final double deltaDistZ; + private double deltaDistX; + private double deltaDistY; + private double deltaDistZ; //which box of the map we're in int mapX; @@ -53,6 +53,15 @@ public class BlockIterator implements Iterator { * unloaded chunks. A value of 0 indicates no limit */ public BlockIterator(@NotNull Vec start, @NotNull Vec direction, double yOffset, double maxDistance, boolean smooth) { + reset(start, direction, yOffset, maxDistance, smooth); + } + + public BlockIterator() {} + + public void reset(@NotNull Vec start, @NotNull Vec direction, double yOffset, double maxDistance, boolean smooth) { + extraPoints.clear(); + foundEnd = false; + start = start.add(0, yOffset, 0); if (maxDistance != 0) end = start.add(direction.normalize().mul(maxDistance)); diff --git a/src/main/java/net/minestom/server/utils/block/BlockUtils.java b/src/main/java/net/minestom/server/utils/block/BlockUtils.java index dd66849255e..36034d1e325 100644 --- a/src/main/java/net/minestom/server/utils/block/BlockUtils.java +++ b/src/main/java/net/minestom/server/utils/block/BlockUtils.java @@ -1,16 +1,14 @@ package net.minestom.server.utils.block; import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; +import net.kyori.adventure.nbt.CompoundBinaryTag; import net.minestom.server.coordinate.Point; -import net.minestom.server.instance.Instance; import net.minestom.server.instance.block.Block; import net.minestom.server.instance.block.BlockHandler; import net.minestom.server.tag.Tag; import net.minestom.server.utils.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jglrxavpok.hephaistos.nbt.NBT; -import org.jglrxavpok.hephaistos.nbt.NBTCompound; import java.util.Map; import java.util.Objects; @@ -90,22 +88,22 @@ public static Map parseProperties(String query) { return new Object2ObjectArrayMap<>(keys, values, entryCount); } - public static @Nullable NBTCompound extractClientNbt(@NotNull Block block) { + public static @Nullable CompoundBinaryTag extractClientNbt(@NotNull Block block) { if (!block.registry().isBlockEntity()) return null; // Append handler tags final BlockHandler handler = block.handler(); - final NBTCompound blockNbt = Objects.requireNonNullElseGet(block.nbt(), NBTCompound::new); + final CompoundBinaryTag blockNbt = Objects.requireNonNullElseGet(block.nbt(), CompoundBinaryTag::empty); if (handler != null) { // Extract explicitly defined tags and keep the rest server-side - return NBT.Compound(nbt -> { - for (Tag tag : handler.getBlockEntityTags()) { - final var value = tag.read(blockNbt); - if (value != null) { - // Tag is present and valid - tag.writeUnsafe(nbt, value); - } + var builder = CompoundBinaryTag.builder(); + for (Tag tag : handler.getBlockEntityTags()) { + final var value = tag.read(blockNbt); + if (value != null) { + // Tag is present and valid + tag.writeUnsafe(builder, value); } - }); + } + return builder.build(); } // Complete nbt shall be sent if the block has no handler // Necessary to support all vanilla blocks diff --git a/src/main/java/net/minestom/server/utils/chunk/ChunkUtils.java b/src/main/java/net/minestom/server/utils/chunk/ChunkUtils.java index aaa210ad1f7..9300de8e64f 100644 --- a/src/main/java/net/minestom/server/utils/chunk/ChunkUtils.java +++ b/src/main/java/net/minestom/server/utils/chunk/ChunkUtils.java @@ -1,6 +1,5 @@ package net.minestom.server.utils.chunk; -import net.minestom.server.ServerFlag; import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Vec; import net.minestom.server.instance.Chunk; @@ -289,6 +288,14 @@ public static int toSectionRelativeCoordinate(int xyz) { return xyz & 0xF; } + public static int toRegionCoordinate(int chunkCoordinate) { + return chunkCoordinate >> 5; + } + + public static int toRegionLocal(int chunkCoordinate) { + return chunkCoordinate & 0x1F; + } + public static int floorSection(int coordinate) { return coordinate - (coordinate & 0xF); } diff --git a/src/main/java/net/minestom/server/utils/debug/DebugUtils.java b/src/main/java/net/minestom/server/utils/debug/DebugUtils.java deleted file mode 100644 index 26596b10063..00000000000 --- a/src/main/java/net/minestom/server/utils/debug/DebugUtils.java +++ /dev/null @@ -1,45 +0,0 @@ -package net.minestom.server.utils.debug; - -import net.minestom.server.utils.PropertyUtils; -import org.jetbrains.annotations.ApiStatus; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Utils class useful for debugging purpose. - */ -@ApiStatus.Internal -public final class DebugUtils { - public static boolean INSIDE_TEST = PropertyUtils.getBoolean("minestom.inside-test", false); - - private final static Logger LOGGER = LoggerFactory.getLogger(DebugUtils.class); - - private static final String LINE_SEPARATOR = System.getProperty("line.separator"); - - /** - * Prints the current thread stack trace elements. - */ - public static synchronized void printStackTrace() { - StackTraceElement[] elements = Thread.currentThread().getStackTrace(); - - StringBuilder stringBuilder = new StringBuilder(); - - stringBuilder.append("START STACKTRACE"); - stringBuilder.append(LINE_SEPARATOR); - - for (int i = 0; i < Integer.MAX_VALUE; i++) { - if (i >= elements.length) - break; - - final StackTraceElement element = elements[i]; - final String line = element.getClassName() + "." + element.getMethodName() + " (line:" + element.getLineNumber() + ")"; - stringBuilder.append(line); - stringBuilder.append(LINE_SEPARATOR); - } - - stringBuilder.append("END STACKTRACE"); - - LOGGER.info(stringBuilder.toString()); - } - -} diff --git a/src/main/java/net/minestom/server/utils/entity/EntityFinder.java b/src/main/java/net/minestom/server/utils/entity/EntityFinder.java index 6df9b4fc0de..f319f8d888e 100644 --- a/src/main/java/net/minestom/server/utils/entity/EntityFinder.java +++ b/src/main/java/net/minestom/server/utils/entity/EntityFinder.java @@ -12,7 +12,6 @@ import net.minestom.server.entity.Player; import net.minestom.server.instance.Instance; import net.minestom.server.network.ConnectionManager; -import net.minestom.server.network.ConnectionState; import net.minestom.server.utils.MathUtils; import net.minestom.server.utils.math.IntRange; import net.minestom.server.utils.validate.Check; @@ -135,7 +134,8 @@ public EntityFinder setDifference(float dx, float dy, float dz) { return player != null ? List.of(player) : List.of(); } else if (targetSelector == TargetSelector.MINESTOM_UUID) { Check.notNull(constantUuid, "The UUID should not be null when searching for it"); - final Entity entity = Entity.getEntity(constantUuid); + Check.notNull(instance, "The instance should not be null when searching by UUID"); + final Entity entity = instance.getEntityByUuid(constantUuid); return entity != null ? List.of(entity) : List.of(); } diff --git a/src/main/java/net/minestom/server/utils/instance/InstanceUtils.java b/src/main/java/net/minestom/server/utils/instance/InstanceUtils.java deleted file mode 100644 index cd2942d8dde..00000000000 --- a/src/main/java/net/minestom/server/utils/instance/InstanceUtils.java +++ /dev/null @@ -1,41 +0,0 @@ -package net.minestom.server.utils.instance; - -import net.minestom.server.instance.Instance; -import net.minestom.server.instance.InstanceContainer; -import net.minestom.server.instance.SharedInstance; - -public final class InstanceUtils { - - /** - * Gets if two instances share the same chunks. - * - * @param instance1 the first instance - * @param instance2 the second instance - * @return true if the two instances share the same chunks - */ - public static boolean areLinked(Instance instance1, Instance instance2) { - // SharedInstance check - if (instance1 instanceof InstanceContainer && instance2 instanceof SharedInstance) { - return ((SharedInstance) instance2).getInstanceContainer().equals(instance1); - } else if (instance2 instanceof InstanceContainer && instance1 instanceof SharedInstance) { - return ((SharedInstance) instance1).getInstanceContainer().equals(instance2); - } else if (instance1 instanceof SharedInstance && instance2 instanceof SharedInstance) { - final InstanceContainer container1 = ((SharedInstance) instance1).getInstanceContainer(); - final InstanceContainer container2 = ((SharedInstance) instance2).getInstanceContainer(); - return container1.equals(container2); - } - - // InstanceContainer check (copied from) - if (instance1 instanceof InstanceContainer container1 && instance2 instanceof InstanceContainer container2) { - if (container1.getSrcInstance() != null) { - return container1.getSrcInstance().equals(container2) - && container1.getLastBlockChangeTime() == container2.getLastBlockChangeTime(); - } else if (container2.getSrcInstance() != null) { - return container2.getSrcInstance().equals(container1) - && container2.getLastBlockChangeTime() == container1.getLastBlockChangeTime(); - } - } - return false; - } - -} diff --git a/src/main/java/net/minestom/server/utils/location/RelativeVec.java b/src/main/java/net/minestom/server/utils/location/RelativeVec.java index 4e159bbc05e..28572fe8a00 100644 --- a/src/main/java/net/minestom/server/utils/location/RelativeVec.java +++ b/src/main/java/net/minestom/server/utils/location/RelativeVec.java @@ -5,7 +5,6 @@ import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Player; -import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -42,7 +41,6 @@ public RelativeVec(@NotNull Vec vec, @NotNull CoordinateType coordinateType, boo return coordinateType.convert(vec, origin, relativeX, relativeY, relativeZ); } - @ApiStatus.Experimental public Vec fromView(@Nullable Pos point) { if (!relativeX && !relativeY && !relativeZ) { return vec; @@ -72,7 +70,6 @@ public Vec fromView(@Nullable Pos point) { return from(entityPosition); } - @ApiStatus.Experimental public @NotNull Vec fromView(@Nullable Entity entity) { final var entityPosition = entity != null ? entity.getPosition() : Pos.ZERO; return fromView(entityPosition); diff --git a/src/main/java/net/minestom/server/utils/nbt/BinaryTagReader.java b/src/main/java/net/minestom/server/utils/nbt/BinaryTagReader.java new file mode 100644 index 00000000000..41b73393ce7 --- /dev/null +++ b/src/main/java/net/minestom/server/utils/nbt/BinaryTagReader.java @@ -0,0 +1,36 @@ +package net.minestom.server.utils.nbt; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.BinaryTagType; +import net.kyori.adventure.nbt.BinaryTagTypes; +import org.jetbrains.annotations.NotNull; + +import java.io.DataInput; +import java.io.IOException; +import java.util.Map; + +// Based on net.kyori.adventure.nbt.BinaryTagReaderImpl licensed under the MIT license. +// https://github.com/KyoriPowered/adventure/blob/main/4/nbt/src/main/java/net/kyori/adventure/nbt/BinaryTagReaderImpl.java +public class BinaryTagReader { + + static { + BinaryTagTypes.COMPOUND.id(); // Force initialization + } + + private final DataInput input; + + public BinaryTagReader(@NotNull DataInput input) { + this.input = input; + } + + public @NotNull BinaryTag readNameless() throws IOException { + BinaryTagType type = BinaryTagUtil.nbtTypeFromId(input.readByte()); + return type.read(input); + } + + public @NotNull Map.Entry readNamed() throws IOException { + BinaryTagType type = BinaryTagUtil.nbtTypeFromId(input.readByte()); + String name = input.readUTF(); + return Map.entry(name, type.read(input)); + } +} diff --git a/src/main/java/net/minestom/server/utils/nbt/BinaryTagSerializer.java b/src/main/java/net/minestom/server/utils/nbt/BinaryTagSerializer.java new file mode 100644 index 00000000000..d7f41f1dc1f --- /dev/null +++ b/src/main/java/net/minestom/server/utils/nbt/BinaryTagSerializer.java @@ -0,0 +1,597 @@ +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.coordinate.Point; +import net.minestom.server.coordinate.Vec; +import net.minestom.server.gamedata.DataPack; +import net.minestom.server.item.ItemStack; +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.Registries; +import net.minestom.server.utils.NamespaceID; +import net.minestom.server.utils.UniqueIdUtils; +import net.minestom.server.utils.Unit; +import net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnknownNullability; + +import java.io.IOException; +import java.util.*; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import static net.kyori.adventure.nbt.StringBinaryTag.stringBinaryTag; + +/** + *

API Note: This class and associated types are currently considered an internal api. It is likely there will be + * significant changes in the future, and there will not be backwards compatibility for this. Use at your own risk.

+ */ +@ApiStatus.Internal +public interface BinaryTagSerializer { + + static @NotNull BinaryTagSerializer recursive(@NotNull Function, BinaryTagSerializer> self) { + return new BinaryTagSerializer<>() { + private BinaryTagSerializer serializer = null; + + @Override + public @NotNull BinaryTag write(@NotNull Context context, @NotNull T value) { + return serializer().write(context, value); + } + + @Override + public @NotNull T read(@NotNull Context context, @NotNull BinaryTag tag) { + return serializer().read(context, tag); + } + + private BinaryTagSerializer serializer() { + if (serializer == null) serializer = self.apply(this); + return serializer; + } + }; + } + + static @NotNull BinaryTagSerializer lazy(@NotNull Supplier> self) { + return new BinaryTagSerializer<>() { + private BinaryTagSerializer serializer = null; + + @Override + public @NotNull BinaryTag write(@NotNull Context context, @NotNull T value) { + return serializer().write(context, value); + } + + @Override + public @NotNull T read(@NotNull Context context, @NotNull BinaryTag tag) { + return serializer().read(context, tag); + } + + private BinaryTagSerializer serializer() { + if (serializer == null) serializer = self.get(); + return serializer; + } + }; + } + + static @NotNull BinaryTagSerializer coerced(@NotNull BinaryTagType type) { + return new BinaryTagSerializer<>() { + @Override + public @NotNull BinaryTag write(@NotNull Context context, @NotNull T value) { + return value; + } + + @Override + public @NotNull T read(@NotNull Context context, @NotNull BinaryTag tag) { + if (tag.type() == type) { + //noinspection unchecked + return (T) tag; + } + + if (tag instanceof StringBinaryTag string) { + try { + tag = TagStringIOExt.readTag(string.value()); + if (tag.type() == type) { + //noinspection unchecked + return (T) tag; + } + } catch (IOException e) { + // Ignored, we'll throw a more useful exception below + } + } + + throw new IllegalArgumentException("Expected " + type + " but got " + tag); + } + }; + } + + static > @NotNull BinaryTagSerializer fromEnumStringable(@NotNull Class enumClass) { + final E[] values = enumClass.getEnumConstants(); + final Map nameMap = Arrays.stream(values).collect(Collectors.toMap(e -> e.name().toLowerCase(Locale.ROOT), Function.identity())); + return new BinaryTagSerializer<>() { + @Override + public @NotNull BinaryTag write(@NotNull E value) { + return stringBinaryTag(value.name().toLowerCase(Locale.ROOT)); + } + + @Override + public @NotNull E read(@NotNull BinaryTag tag) { + return switch (tag) { + case IntBinaryTag intBinaryTag -> values[intBinaryTag.value()]; + case StringBinaryTag string -> + nameMap.getOrDefault(string.value().toLowerCase(Locale.ROOT), values[0]); + default -> values[0]; + }; + } + }; + } + + BinaryTagSerializer UNIT = new BinaryTagSerializer<>() { + @Override + public @NotNull BinaryTag write(@NotNull Unit value) { + return CompoundBinaryTag.empty(); + } + + @Override + public @NotNull Unit read(@NotNull BinaryTag tag) { + return Unit.INSTANCE; + } + }; + + BinaryTagSerializer BYTE = new BinaryTagSerializer<>() { + @Override + public @NotNull BinaryTag write(@NotNull Byte value) { + return ByteBinaryTag.byteBinaryTag(value); + } + + @Override + public @NotNull Byte read(@NotNull BinaryTag tag) { + return tag instanceof ByteBinaryTag byteBinaryTag ? byteBinaryTag.value() : 0; + } + }; + + BinaryTagSerializer BOOLEAN = BYTE.map(b -> b != 0, b -> (byte) (b ? 1 : 0)); + + BinaryTagSerializer INT = new BinaryTagSerializer<>() { + @Override + public @NotNull BinaryTag write(@NotNull Integer value) { + return IntBinaryTag.intBinaryTag(value); + } + + @Override + public @NotNull Integer read(@NotNull BinaryTag tag) { + return tag instanceof NumberBinaryTag numberTag ? numberTag.intValue() : 0; + } + }; + + BinaryTagSerializer FLOAT = new BinaryTagSerializer<>() { + @Override + public @NotNull BinaryTag write(@NotNull Float value) { + return FloatBinaryTag.floatBinaryTag(value); + } + + @Override + public @NotNull Float read(@NotNull BinaryTag tag) { + return tag instanceof NumberBinaryTag numberTag ? numberTag.floatValue() : 0f; + } + }; + + BinaryTagSerializer STRING = new BinaryTagSerializer<>() { + @Override + public @NotNull BinaryTag write(@NotNull String value) { + return stringBinaryTag(value); + } + + @Override + public @NotNull String read(@NotNull BinaryTag tag) { + return tag instanceof StringBinaryTag stringBinaryTag ? stringBinaryTag.value() : ""; + } + }; + + BinaryTagSerializer COMPOUND = new BinaryTagSerializer<>() { + @Override + public @NotNull BinaryTag write(@NotNull CompoundBinaryTag value) { + return value; + } + + @Override + public @NotNull CompoundBinaryTag read(@NotNull BinaryTag tag) { + return tag instanceof CompoundBinaryTag compoundBinaryTag ? compoundBinaryTag : CompoundBinaryTag.empty(); + } + }; + BinaryTagSerializer COMPOUND_COERCED = coerced(BinaryTagTypes.COMPOUND); + + BinaryTagSerializer JSON_COMPONENT = STRING.map( + s -> GsonComponentSerializer.gson().deserialize(s), + c -> GsonComponentSerializer.gson().serialize(c) + ); + BinaryTagSerializer 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