feat: joinable server

This commit is contained in:
mworzala 2024-04-10 00:34:30 -04:00
parent e3f4d8a6cf
commit c7a7c790c0
No known key found for this signature in database
GPG Key ID: B148F922E64797C7
90 changed files with 1426 additions and 756 deletions

View File

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

View File

@ -18,11 +18,9 @@ public class Generators {
}
File outputFolder = new File(args[0]);
// Generate DyeColors
new DyeColorGenerator(resource("dye_colors.json"), outputFolder).generate();
var generator = new CodeGenerator(outputFolder);
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");
@ -37,16 +35,11 @@ public class Generators {
generator.generate(resource("damage_types.json"), "net.minestom.server.entity.damage", "DamageType", "DamageTypeImpl", "DamageTypes");
generator.generate(resource("trim_materials.json"), "net.minestom.server.item.armor", "TrimMaterial", "TrimMaterialImpl", "TrimMaterials");
generator.generate(resource("trim_patterns.json"), "net.minestom.server.item.armor", "TrimPattern", "TrimPatternImpl", "TrimPatterns");
generator.generate(resource("attributes.json"), "net.minestom.server.entity.attribute", "Attribute", "AttributeImpl", "Attributes");
// Generate fluids
new FluidGenerator(resource("fluids.json"), outputFolder).generate();
// TODO: Generate attributes
// new AttributeGenerator(
// new File(inputFolder, targetVersion + "_attributes.json"),
// outputFolder
// ).generate();
// TODO: Generate villager professions
// new VillagerProfessionGenerator(
// new File(inputFolder, targetVersion + "_villager_professions.json"),

View File

@ -1,249 +0,0 @@
package net.minestom.codegen.attribute;
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.ArrayList;
import java.util.List;
public final class AttributeGenerator extends MinestomCodeGenerator {
private static final Logger LOGGER = LoggerFactory.getLogger(AttributeGenerator.class);
private final InputStream attributesFile;
private final File outputFolder;
public AttributeGenerator(@Nullable InputStream attributesFile, @NotNull File outputFolder) {
this.attributesFile = attributesFile;
this.outputFolder = outputFolder;
}
@Override
public void generate() {
if (attributesFile == null) {
LOGGER.error("Failed to find attributes.json.");
LOGGER.error("Stopped code generation for attributes.");
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 registryClassName = ClassName.get("net.minestom.server.registry", "Registry");
JsonArray attributes = GSON.fromJson(new InputStreamReader(attributesFile), JsonArray.class);
List<JavaFile> filesToWrite = new ArrayList<>();
ClassName attributeClassName = ClassName.get("net.minestom.server.attribute", "Attribute");
// Attribute
TypeSpec.Builder attributeClass = TypeSpec.classBuilder(attributeClassName)
.addSuperinterface(ClassName.get("net.kyori.adventure.key", "Keyed"))
.addModifiers(Modifier.PUBLIC).addJavadoc("AUTOGENERATED by " + getClass().getSimpleName());
attributeClass.addField(
FieldSpec.builder(namespaceIDClassName, "id")
.addModifiers(Modifier.PRIVATE, Modifier.FINAL).addAnnotation(NotNull.class).build()
);
attributeClass.addField(
FieldSpec.builder(TypeName.DOUBLE, "defaultValue")
.addModifiers(Modifier.PRIVATE, Modifier.FINAL).build()
);
attributeClass.addField(
FieldSpec.builder(TypeName.BOOLEAN, "clientSyncable")
.addModifiers(Modifier.PRIVATE, Modifier.FINAL).build()
);
attributeClass.addMethod(
MethodSpec.constructorBuilder()
.addParameter(ParameterSpec.builder(namespaceIDClassName, "id").addAnnotation(NotNull.class).build())
.addParameter(ParameterSpec.builder(TypeName.BOOLEAN, "clientSyncable").build())
.addParameter(ParameterSpec.builder(TypeName.DOUBLE, "defaultValue").build())
.addStatement("this.id = id")
.addStatement("this.clientSyncable = clientSyncable")
.addStatement("this.defaultValue = defaultValue")
.addModifiers(Modifier.PROTECTED)
.build()
);
// Override key method (adventure)
attributeClass.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
attributeClass.addMethod(
MethodSpec.methodBuilder("getId")
.returns(namespaceIDClassName)
.addAnnotation(NotNull.class)
.addStatement("return this.id")
.addModifiers(Modifier.PUBLIC)
.build()
);
// getDefaultValue
attributeClass.addMethod(
MethodSpec.methodBuilder("getDefaultValue")
.returns(TypeName.DOUBLE)
.addStatement("return this.defaultValue")
.addModifiers(Modifier.PUBLIC)
.build()
);
// isClientSyncable
attributeClass.addMethod(
MethodSpec.methodBuilder("isClientSyncable")
.returns(TypeName.BOOLEAN)
.addStatement("return this.clientSyncable")
.addModifiers(Modifier.PUBLIC)
.build()
);
// isShared
attributeClass.addMethod(
MethodSpec.methodBuilder("isShared")
.addAnnotation(Deprecated.class)
.returns(TypeName.BOOLEAN)
.addStatement("return this.clientSyncable")
.addModifiers(Modifier.PUBLIC)
.build()
);
// values method
attributeClass.addMethod(
MethodSpec.methodBuilder("values")
.addAnnotation(NotNull.class)
.returns(ParameterizedTypeName.get(ClassName.get(List.class), attributeClassName))
.addStatement("return $T.ATTRIBUTE_REGISTRY.values()", registryClassName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.build()
);
// toString method
attributeClass.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()
);
// Creating ClampedAttribute
ClassName clampedAttributeClassName = ClassName.get("net.minestom.server.attribute", "ClampedAttribute");
TypeSpec.Builder clampedAttributeClass = TypeSpec.classBuilder(clampedAttributeClassName)
.superclass(attributeClassName)
.addModifiers(Modifier.PUBLIC).addJavadoc("AUTOGENERATED by " + getClass().getSimpleName());
clampedAttributeClass.addField(
FieldSpec.builder(TypeName.DOUBLE, "minValue")
.addModifiers(Modifier.PRIVATE, Modifier.FINAL).build()
);
clampedAttributeClass.addField(
FieldSpec.builder(TypeName.DOUBLE, "maxValue")
.addModifiers(Modifier.PRIVATE, Modifier.FINAL).build()
);
clampedAttributeClass.addMethod(
MethodSpec.constructorBuilder()
.addParameter(ParameterSpec.builder(namespaceIDClassName, "id").addAnnotation(NotNull.class).build())
.addParameter(ParameterSpec.builder(TypeName.BOOLEAN, "clientSyncable").build())
.addParameter(ParameterSpec.builder(TypeName.DOUBLE, "defaultValue").build())
.addParameter(ParameterSpec.builder(TypeName.DOUBLE, "minValue").build())
.addParameter(ParameterSpec.builder(TypeName.DOUBLE, "maxValue").build())
.addStatement("super(id, clientSyncable, defaultValue)")
.addStatement("this.minValue = minValue")
.addStatement("this.maxValue = maxValue")
.addModifiers(Modifier.PROTECTED)
.build()
);
// getMinValue
clampedAttributeClass.addMethod(
MethodSpec.methodBuilder("getMinValue")
.returns(TypeName.DOUBLE)
.addStatement("return this.minValue")
.addModifiers(Modifier.PUBLIC)
.build()
);
// getMaxValue
clampedAttributeClass.addMethod(
MethodSpec.methodBuilder("getMaxValue")
.returns(TypeName.DOUBLE)
.addStatement("return this.maxValue")
.addModifiers(Modifier.PUBLIC)
.build()
);
CodeBlock.Builder staticBlock = CodeBlock.builder();
// Use data
for (JsonElement a : attributes) {
JsonObject attribute = a.getAsJsonObject();
String attributeName = attribute.get("name").getAsString();
JsonObject range = attribute.getAsJsonObject("range");
if (range == null) {
// Normal attribute
attributeClass.addField(
FieldSpec.builder(
attributeClassName,
attributeName
).initializer(
"new $T($T.from($S), $L, $L)",
attributeClassName,
namespaceIDClassName,
attribute.get("id").getAsString(),
attribute.get("clientSync").getAsBoolean(),
attribute.get("defaultValue").getAsDouble()
).addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).build()
);
} else {
// ClampedAttribute
attributeClass.addField(
FieldSpec.builder(
attributeClassName,
attributeName
).initializer(
"new $T($T.from($S), $L, $L, $L, $L)",
clampedAttributeClassName,
namespaceIDClassName,
attribute.get("id").getAsString(),
attribute.get("clientSync").getAsBoolean(),
attribute.get("defaultValue").getAsDouble(),
range.get("minValue").getAsDouble(),
range.get("maxValue").getAsDouble()
).addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).build()
);
}
// Add to static init.
staticBlock.addStatement("$T.ATTRIBUTE_REGISTRY.register($N)", registryClassName, attributeName);
}
attributeClass.addStaticBlock(staticBlock.build());
filesToWrite.add(
JavaFile.builder("net.minestom.server.attribute", attributeClass.build())
.indent(" ")
.skipJavaLangImports(true)
.build()
);
filesToWrite.add(
JavaFile.builder("net.minestom.server.attribute", clampedAttributeClass.build())
.indent(" ")
.skipJavaLangImports(true)
.build()
);
// Write files to outputFolder
writeFiles(
filesToWrite,
outputFolder
);
}
}

View File

@ -1,9 +1,9 @@
package net.minestom.demo.entity;
import net.minestom.server.attribute.Attribute;
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 class ChickenCreature extends EntityCreature {
// .build()
// );
getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue(0.1f);
getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).setBaseValue(0.1);
}
@Override

View File

@ -3,7 +3,7 @@ metadata.format.version = "1.1"
[versions]
# Important dependencies
data = "1.20.4-rv4"
data = "24w13a-dev"
adventure = "4.16.0"
kotlin = "1.7.22"
dependencyGetter = "v1.0.1"

View File

@ -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");

View File

@ -0,0 +1,51 @@
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 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 GENERIC_MOVEMENT_SPEED = AttributeImpl.get("minecraft:generic.movement_speed");
Attribute GENERIC_SAFE_FALL_DISTANCE = AttributeImpl.get("minecraft:generic.safe_fall_distance");
Attribute GENERIC_SCALE = AttributeImpl.get("minecraft:generic.scale");
Attribute ZOMBIE_SPAWN_REINFORCEMENTS = AttributeImpl.get("minecraft:zombie.spawn_reinforcements");
Attribute GENERIC_STEP_HEIGHT = AttributeImpl.get("minecraft:generic.step_height");
}

View File

@ -33,6 +33,8 @@ interface DamageTypes {
DamageType PLAYER_EXPLOSION = DamageTypeImpl.get("minecraft:player_explosion");
DamageType SPIT = DamageTypeImpl.get("minecraft:spit");
DamageType STING = DamageTypeImpl.get("minecraft:sting");
DamageType UNATTRIBUTED_FIREBALL = DamageTypeImpl.get("minecraft:unattributed_fireball");

View File

@ -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");
}

View File

@ -43,7 +43,7 @@ interface Enchantments {
Enchantment LOOTING = EnchantmentImpl.get("minecraft:looting");
Enchantment SWEEPING = EnchantmentImpl.get("minecraft:sweeping");
Enchantment SWEEPING_EDGE = EnchantmentImpl.get("minecraft:sweeping_edge");
Enchantment EFFICIENCY = EnchantmentImpl.get("minecraft:efficiency");
@ -79,6 +79,12 @@ interface Enchantments {
Enchantment PIERCING = EnchantmentImpl.get("minecraft:piercing");
Enchantment DENSITY = EnchantmentImpl.get("minecraft:density");
Enchantment BREACH = EnchantmentImpl.get("minecraft:breach");
Enchantment WIND_BURST = EnchantmentImpl.get("minecraft:wind_burst");
Enchantment MENDING = EnchantmentImpl.get("minecraft:mending");
Enchantment VANISHING_CURSE = EnchantmentImpl.get("minecraft:vanishing_curse");

View File

@ -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,7 +1595,11 @@ 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");
@ -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");
@ -2387,6 +2401,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 +2571,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 +2591,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 +2611,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 +2656,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");
}

View File

@ -5,8 +5,6 @@ package net.minestom.server.particle;
*/
@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");
@ -55,7 +53,11 @@ 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");
@ -67,6 +69,8 @@ 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");
@ -95,6 +99,8 @@ interface Particles {
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");
@ -203,7 +209,17 @@ interface Particles {
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 DUST_PILLAR = 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");
}

View File

@ -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");
}

View File

@ -5,8 +5,6 @@ package net.minestom.server.potion;
*/
@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");
}

View File

@ -113,6 +113,32 @@ interface SoundEvents {
SoundEvent BLOCK_ANVIL_USE = SoundEventImpl.get("minecraft:block.anvil.use");
SoundEvent ENTITY_ARMADILLO_EAT = SoundEventImpl.get("minecraft:entity.armadillo.eat");
SoundEvent ENTITY_ARMADILLO_HURT = SoundEventImpl.get("minecraft:entity.armadillo.hurt");
SoundEvent ENTITY_ARMADILLO_HURT_REDUCED = SoundEventImpl.get("minecraft:entity.armadillo.hurt_reduced");
SoundEvent ENTITY_ARMADILLO_AMBIENT = SoundEventImpl.get("minecraft:entity.armadillo.ambient");
SoundEvent ENTITY_ARMADILLO_STEP = SoundEventImpl.get("minecraft:entity.armadillo.step");
SoundEvent ENTITY_ARMADILLO_DEATH = SoundEventImpl.get("minecraft:entity.armadillo.death");
SoundEvent ENTITY_ARMADILLO_ROLL = SoundEventImpl.get("minecraft:entity.armadillo.roll");
SoundEvent ENTITY_ARMADILLO_LAND = SoundEventImpl.get("minecraft:entity.armadillo.land");
SoundEvent ENTITY_ARMADILLO_SCUTE_DROP = SoundEventImpl.get("minecraft:entity.armadillo.scute_drop");
SoundEvent ENTITY_ARMADILLO_UNROLL_FINISH = SoundEventImpl.get("minecraft:entity.armadillo.unroll_finish");
SoundEvent ENTITY_ARMADILLO_PEEK = SoundEventImpl.get("minecraft:entity.armadillo.peek");
SoundEvent ENTITY_ARMADILLO_UNROLL_START = SoundEventImpl.get("minecraft:entity.armadillo.unroll_start");
SoundEvent ENTITY_ARMADILLO_BRUSH = SoundEventImpl.get("minecraft:entity.armadillo.brush");
SoundEvent ITEM_ARMOR_EQUIP_CHAIN = SoundEventImpl.get("minecraft:item.armor.equip_chain");
SoundEvent ITEM_ARMOR_EQUIP_DIAMOND = SoundEventImpl.get("minecraft:item.armor.equip_diamond");
@ -131,6 +157,10 @@ interface SoundEvents {
SoundEvent ITEM_ARMOR_EQUIP_TURTLE = SoundEventImpl.get("minecraft:item.armor.equip_turtle");
SoundEvent ITEM_ARMOR_EQUIP_WOLF = SoundEventImpl.get("minecraft:item.armor.equip_wolf");
SoundEvent ITEM_ARMOR_UNEQUIP_WOLF = SoundEventImpl.get("minecraft:item.armor.unequip_wolf");
SoundEvent ENTITY_ARMOR_STAND_BREAK = SoundEventImpl.get("minecraft:entity.armor_stand.break");
SoundEvent ENTITY_ARMOR_STAND_FALL = SoundEventImpl.get("minecraft:entity.armor_stand.fall");
@ -313,6 +343,16 @@ interface SoundEvents {
SoundEvent ENTITY_BOAT_PADDLE_WATER = SoundEventImpl.get("minecraft:entity.boat.paddle_water");
SoundEvent ENTITY_BOGGED_AMBIENT = SoundEventImpl.get("minecraft:entity.bogged.ambient");
SoundEvent ENTITY_BOGGED_DEATH = SoundEventImpl.get("minecraft:entity.bogged.death");
SoundEvent ENTITY_BOGGED_HURT = SoundEventImpl.get("minecraft:entity.bogged.hurt");
SoundEvent ENTITY_BOGGED_SHEAR = SoundEventImpl.get("minecraft:entity.bogged.shear");
SoundEvent ENTITY_BOGGED_STEP = SoundEventImpl.get("minecraft:entity.bogged.step");
SoundEvent BLOCK_BONE_BLOCK_BREAK = SoundEventImpl.get("minecraft:block.bone_block.break");
SoundEvent BLOCK_BONE_BLOCK_FALL = SoundEventImpl.get("minecraft:block.bone_block.fall");
@ -337,6 +377,10 @@ interface SoundEvents {
SoundEvent ITEM_BOTTLE_FILL_DRAGONBREATH = SoundEventImpl.get("minecraft:item.bottle.fill_dragonbreath");
SoundEvent ENTITY_BREEZE_CHARGE = SoundEventImpl.get("minecraft:entity.breeze.charge");
SoundEvent ENTITY_BREEZE_DEFLECT = SoundEventImpl.get("minecraft:entity.breeze.deflect");
SoundEvent ENTITY_BREEZE_INHALE = SoundEventImpl.get("minecraft:entity.breeze.inhale");
SoundEvent ENTITY_BREEZE_IDLE_GROUND = SoundEventImpl.get("minecraft:entity.breeze.idle_ground");
@ -355,6 +399,10 @@ interface SoundEvents {
SoundEvent ENTITY_BREEZE_HURT = SoundEventImpl.get("minecraft:entity.breeze.hurt");
SoundEvent ENTITY_BREEZE_WHIRL = SoundEventImpl.get("minecraft:entity.breeze.whirl");
SoundEvent ENTITY_BREEZE_WIND_BURST = SoundEventImpl.get("minecraft:entity.breeze.wind_burst");
SoundEvent BLOCK_BREWING_STAND_BREW = SoundEventImpl.get("minecraft:block.brewing_stand.brew");
SoundEvent ITEM_BRUSH_BRUSHING_GENERIC = SoundEventImpl.get("minecraft:item.brush.brushing.generic");
@ -597,6 +645,16 @@ interface SoundEvents {
SoundEvent ITEM_CHORUS_FRUIT_TELEPORT = SoundEventImpl.get("minecraft:item.chorus_fruit.teleport");
SoundEvent BLOCK_COBWEB_BREAK = SoundEventImpl.get("minecraft:block.cobweb.break");
SoundEvent BLOCK_COBWEB_STEP = SoundEventImpl.get("minecraft:block.cobweb.step");
SoundEvent BLOCK_COBWEB_PLACE = SoundEventImpl.get("minecraft:block.cobweb.place");
SoundEvent BLOCK_COBWEB_HIT = SoundEventImpl.get("minecraft:block.cobweb.hit");
SoundEvent BLOCK_COBWEB_FALL = SoundEventImpl.get("minecraft:block.cobweb.fall");
SoundEvent ENTITY_COD_AMBIENT = SoundEventImpl.get("minecraft:entity.cod.ambient");
SoundEvent ENTITY_COD_DEATH = SoundEventImpl.get("minecraft:entity.cod.death");
@ -801,6 +859,8 @@ interface SoundEvents {
SoundEvent ENTITY_DONKEY_HURT = SoundEventImpl.get("minecraft:entity.donkey.hurt");
SoundEvent ENTITY_DONKEY_JUMP = SoundEventImpl.get("minecraft:entity.donkey.jump");
SoundEvent BLOCK_DRIPSTONE_BLOCK_BREAK = SoundEventImpl.get("minecraft:block.dripstone_block.break");
SoundEvent BLOCK_DRIPSTONE_BLOCK_STEP = SoundEventImpl.get("minecraft:block.dripstone_block.step");
@ -1261,6 +1321,16 @@ interface SoundEvents {
SoundEvent BLOCK_HANGING_SIGN_PLACE = SoundEventImpl.get("minecraft:block.hanging_sign.place");
SoundEvent BLOCK_HEAVY_CORE_BREAK = SoundEventImpl.get("minecraft:block.heavy_core.break");
SoundEvent BLOCK_HEAVY_CORE_FALL = SoundEventImpl.get("minecraft:block.heavy_core.fall");
SoundEvent BLOCK_HEAVY_CORE_HIT = SoundEventImpl.get("minecraft:block.heavy_core.hit");
SoundEvent BLOCK_HEAVY_CORE_PLACE = SoundEventImpl.get("minecraft:block.heavy_core.place");
SoundEvent BLOCK_HEAVY_CORE_STEP = SoundEventImpl.get("minecraft:block.heavy_core.step");
SoundEvent BLOCK_NETHER_WOOD_HANGING_SIGN_STEP = SoundEventImpl.get("minecraft:block.nether_wood_hanging_sign.step");
SoundEvent BLOCK_NETHER_WOOD_HANGING_SIGN_BREAK = SoundEventImpl.get("minecraft:block.nether_wood_hanging_sign.break");
@ -1293,10 +1363,20 @@ interface SoundEvents {
SoundEvent BLOCK_TRIAL_SPAWNER_SPAWN_MOB = SoundEventImpl.get("minecraft:block.trial_spawner.spawn_mob");
SoundEvent BLOCK_TRIAL_SPAWNER_ABOUT_TO_SPAWN_ITEM = SoundEventImpl.get("minecraft:block.trial_spawner.about_to_spawn_item");
SoundEvent BLOCK_TRIAL_SPAWNER_SPAWN_ITEM = SoundEventImpl.get("minecraft:block.trial_spawner.spawn_item");
SoundEvent BLOCK_TRIAL_SPAWNER_SPAWN_ITEM_BEGIN = SoundEventImpl.get("minecraft:block.trial_spawner.spawn_item_begin");
SoundEvent BLOCK_TRIAL_SPAWNER_DETECT_PLAYER = SoundEventImpl.get("minecraft:block.trial_spawner.detect_player");
SoundEvent BLOCK_TRIAL_SPAWNER_CHARGE_ACTIVATE = SoundEventImpl.get("minecraft:block.trial_spawner.charge_activate");
SoundEvent BLOCK_TRIAL_SPAWNER_AMBIENT = SoundEventImpl.get("minecraft:block.trial_spawner.ambient");
SoundEvent BLOCK_TRIAL_SPAWNER_AMBIENT_CHARGED = SoundEventImpl.get("minecraft:block.trial_spawner.ambient_charged");
SoundEvent BLOCK_TRIAL_SPAWNER_OPEN_SHUTTER = SoundEventImpl.get("minecraft:block.trial_spawner.open_shutter");
SoundEvent BLOCK_TRIAL_SPAWNER_CLOSE_SHUTTER = SoundEventImpl.get("minecraft:block.trial_spawner.close_shutter");
@ -1525,6 +1605,12 @@ interface SoundEvents {
SoundEvent ITEM_LODESTONE_COMPASS_LOCK = SoundEventImpl.get("minecraft:item.lodestone_compass.lock");
SoundEvent ITEM_MACE_SMASH_AIR = SoundEventImpl.get("minecraft:item.mace.smash_air");
SoundEvent ITEM_MACE_SMASH_GROUND = SoundEventImpl.get("minecraft:item.mace.smash_ground");
SoundEvent ITEM_MACE_SMASH_GROUND_HEAVY = SoundEventImpl.get("minecraft:item.mace.smash_ground_heavy");
SoundEvent ENTITY_MAGMA_CUBE_DEATH = SoundEventImpl.get("minecraft:entity.magma_cube.death");
SoundEvent ENTITY_MAGMA_CUBE_HURT = SoundEventImpl.get("minecraft:entity.magma_cube.hurt");
@ -1653,6 +1739,8 @@ interface SoundEvents {
SoundEvent ENTITY_MULE_HURT = SoundEventImpl.get("minecraft:entity.mule.hurt");
SoundEvent ENTITY_MULE_JUMP = SoundEventImpl.get("minecraft:entity.mule.jump");
SoundEvent MUSIC_CREATIVE = SoundEventImpl.get("minecraft:music.creative");
SoundEvent MUSIC_CREDITS = SoundEventImpl.get("minecraft:music.credits");
@ -1933,6 +2021,8 @@ interface SoundEvents {
SoundEvent ENTITY_OCELOT_DEATH = SoundEventImpl.get("minecraft:entity.ocelot.death");
SoundEvent ITEM_OMINOUS_BOTTLE_DISPOSE = SoundEventImpl.get("minecraft:item.ominous_bottle.dispose");
SoundEvent ENTITY_PAINTING_BREAK = SoundEventImpl.get("minecraft:entity.painting.break");
SoundEvent ENTITY_PAINTING_PLACE = SoundEventImpl.get("minecraft:entity.painting.place");
@ -1971,6 +2061,8 @@ interface SoundEvents {
SoundEvent ENTITY_PARROT_IMITATE_BLAZE = SoundEventImpl.get("minecraft:entity.parrot.imitate.blaze");
SoundEvent ENTITY_PARROT_IMITATE_BOGGED = SoundEventImpl.get("minecraft:entity.parrot.imitate.bogged");
SoundEvent ENTITY_PARROT_IMITATE_BREEZE = SoundEventImpl.get("minecraft:entity.parrot.imitate.breeze");
SoundEvent ENTITY_PARROT_IMITATE_CREEPER = SoundEventImpl.get("minecraft:entity.parrot.imitate.creeper");
@ -2779,6 +2871,32 @@ interface SoundEvents {
SoundEvent UI_TOAST_OUT = SoundEventImpl.get("minecraft:ui.toast.out");
SoundEvent BLOCK_VAULT_ACTIVATE = SoundEventImpl.get("minecraft:block.vault.activate");
SoundEvent BLOCK_VAULT_AMBIENT = SoundEventImpl.get("minecraft:block.vault.ambient");
SoundEvent BLOCK_VAULT_BREAK = SoundEventImpl.get("minecraft:block.vault.break");
SoundEvent BLOCK_VAULT_CLOSE_SHUTTER = SoundEventImpl.get("minecraft:block.vault.close_shutter");
SoundEvent BLOCK_VAULT_DEACTIVATE = SoundEventImpl.get("minecraft:block.vault.deactivate");
SoundEvent BLOCK_VAULT_EJECT_ITEM = SoundEventImpl.get("minecraft:block.vault.eject_item");
SoundEvent BLOCK_VAULT_FALL = SoundEventImpl.get("minecraft:block.vault.fall");
SoundEvent BLOCK_VAULT_HIT = SoundEventImpl.get("minecraft:block.vault.hit");
SoundEvent BLOCK_VAULT_INSERT_ITEM = SoundEventImpl.get("minecraft:block.vault.insert_item");
SoundEvent BLOCK_VAULT_INSERT_ITEM_FAIL = SoundEventImpl.get("minecraft:block.vault.insert_item_fail");
SoundEvent BLOCK_VAULT_OPEN_SHUTTER = SoundEventImpl.get("minecraft:block.vault.open_shutter");
SoundEvent BLOCK_VAULT_PLACE = SoundEventImpl.get("minecraft:block.vault.place");
SoundEvent BLOCK_VAULT_STEP = SoundEventImpl.get("minecraft:block.vault.step");
SoundEvent ENTITY_VEX_AMBIENT = SoundEventImpl.get("minecraft:entity.vex.ambient");
SoundEvent ENTITY_VEX_CHARGE = SoundEventImpl.get("minecraft:entity.vex.charge");
@ -2929,6 +3047,8 @@ interface SoundEvents {
SoundEvent BLOCK_WET_SPONGE_BREAK = SoundEventImpl.get("minecraft:block.wet_sponge.break");
SoundEvent BLOCK_WET_SPONGE_DRIES = SoundEventImpl.get("minecraft:block.wet_sponge.dries");
SoundEvent BLOCK_WET_SPONGE_FALL = SoundEventImpl.get("minecraft:block.wet_sponge.fall");
SoundEvent BLOCK_WET_SPONGE_HIT = SoundEventImpl.get("minecraft:block.wet_sponge.hit");
@ -2937,7 +3057,9 @@ interface SoundEvents {
SoundEvent BLOCK_WET_SPONGE_STEP = SoundEventImpl.get("minecraft:block.wet_sponge.step");
SoundEvent ENTITY_GENERIC_WIND_BURST = SoundEventImpl.get("minecraft:entity.generic.wind_burst");
SoundEvent ENTITY_WIND_CHARGE_WIND_BURST = SoundEventImpl.get("minecraft:entity.wind_charge.wind_burst");
SoundEvent ENTITY_WIND_CHARGE_THROW = SoundEventImpl.get("minecraft:entity.wind_charge.throw");
SoundEvent ENTITY_WITCH_AMBIENT = SoundEventImpl.get("minecraft:entity.witch.ambient");
@ -2971,6 +3093,14 @@ interface SoundEvents {
SoundEvent ENTITY_WITHER_SPAWN = SoundEventImpl.get("minecraft:entity.wither.spawn");
SoundEvent ITEM_WOLF_ARMOR_BREAK = SoundEventImpl.get("minecraft:item.wolf_armor.break");
SoundEvent ITEM_WOLF_ARMOR_CRACK = SoundEventImpl.get("minecraft:item.wolf_armor.crack");
SoundEvent ITEM_WOLF_ARMOR_DAMAGE = SoundEventImpl.get("minecraft:item.wolf_armor.damage");
SoundEvent ITEM_WOLF_ARMOR_REPAIR = SoundEventImpl.get("minecraft:item.wolf_armor.repair");
SoundEvent ENTITY_WOLF_AMBIENT = SoundEventImpl.get("minecraft:entity.wolf.ambient");
SoundEvent ENTITY_WOLF_DEATH = SoundEventImpl.get("minecraft:entity.wolf.death");
@ -3082,4 +3212,10 @@ interface SoundEvents {
SoundEvent ENTITY_ZOMBIE_VILLAGER_HURT = SoundEventImpl.get("minecraft:entity.zombie_villager.hurt");
SoundEvent ENTITY_ZOMBIE_VILLAGER_STEP = SoundEventImpl.get("minecraft:entity.zombie_villager.step");
SoundEvent EVENT_MOB_EFFECT_BAD_OMEN = SoundEventImpl.get("minecraft:event.mob_effect.bad_omen");
SoundEvent EVENT_MOB_EFFECT_TRIAL_OMEN = SoundEventImpl.get("minecraft:event.mob_effect.trial_omen");
SoundEvent EVENT_MOB_EFFECT_RAID_OMEN = SoundEventImpl.get("minecraft:event.mob_effect.raid_omen");
}

View File

@ -44,8 +44,8 @@ public final class MinecraftServer {
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;
public static final String VERSION_NAME = "24w14a";
public static final int PROTOCOL_VERSION = 1073742008;
// Threads
public static final String THREAD_NAME_BENCHMARK = "Ms-Benchmark";

View File

@ -1,66 +0,0 @@
package net.minestom.server.attribute;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Represents a {@link net.minestom.server.entity.LivingEntity living entity} attribute.
*/
public record Attribute(String key, float defaultValue, float maxValue) {
private static final Map<String, Attribute> ATTRIBUTES = new ConcurrentHashMap<>();
public static final Attribute MAX_HEALTH = (new Attribute("generic.max_health", 20, 1024)).register();
public static final Attribute FOLLOW_RANGE = (new Attribute("generic.follow_range", 32, 2048)).register();
public static final Attribute KNOCKBACK_RESISTANCE = (new Attribute("generic.knockback_resistance", 0, 1)).register();
public static final Attribute MOVEMENT_SPEED = (new Attribute("generic.movement_speed", 0.25f, 1024)).register();
public static final Attribute ATTACK_DAMAGE = (new Attribute("generic.attack_damage", 2, 2048)).register();
public static final Attribute ATTACK_SPEED = (new Attribute("generic.attack_speed", 4, 1024)).register();
public static final Attribute FLYING_SPEED = (new Attribute("generic.flying_speed", 0.4f, 1024)).register();
public static final Attribute ARMOR = (new Attribute("generic.armor", 0, 30)).register();
public static final Attribute ARMOR_TOUGHNESS = (new Attribute("generic.armor_toughness", 0, 20)).register();
public static final Attribute ATTACK_KNOCKBACK = (new Attribute("generic.attack_knockback", 0, 5)).register();
public static final Attribute LUCK = (new Attribute("generic.luck", 0, 1024)).register();
public static final Attribute HORSE_JUMP_STRENGTH = (new Attribute("horse.jump_strength", 0.7f, 2)).register();
public static final Attribute ZOMBIE_SPAWN_REINFORCEMENTS = (new Attribute("zombie.spawn_reinforcements", 0, 1)).register();
public Attribute {
if (defaultValue > maxValue) {
throw new IllegalArgumentException("Default value cannot be greater than the maximum allowed");
}
}
/**
* Register this attribute.
*
* @return this attribute
* @see #fromKey(String)
* @see #values()
*/
public @NotNull Attribute register() {
ATTRIBUTES.put(key, this);
return this;
}
/**
* Retrieves an attribute by its key.
*
* @param key the key of the attribute
* @return the attribute for the key or null if not any
*/
public static @Nullable Attribute fromKey(@NotNull String key) {
return ATTRIBUTES.get(key);
}
/**
* Retrieves all registered attributes.
*
* @return an array containing all registered attributes
*/
public static @NotNull Collection<@NotNull Attribute> values() {
return ATTRIBUTES.values();
}
}

View File

@ -1,11 +1,11 @@
package net.minestom.server.entity;
import net.kyori.adventure.sound.Sound.Source;
import net.minestom.server.attribute.Attribute;
import net.minestom.server.attribute.AttributeInstance;
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.damage.Damage;
import net.minestom.server.entity.damage.DamageType;
import net.minestom.server.entity.metadata.LivingEntityMeta;
@ -22,7 +22,7 @@ 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.EntityAttributesPacket;
import net.minestom.server.network.packet.server.play.SoundEffectPacket;
import net.minestom.server.network.player.PlayerConnection;
import net.minestom.server.scoreboard.Team;
@ -419,21 +419,21 @@ public class LivingEntity extends Entity implements EquipmentHandler {
}
/**
* Gets the entity max health from {@link #getAttributeValue(Attribute)} {@link Attribute#MAX_HEALTH}.
* Gets the entity max health from {@link #getAttributeValue(Attribute)} {@link Attribute#GENERIC_MAX_HEALTH}.
*
* @return the entity max health
*/
public float getMaxHealth() {
return getAttributeValue(Attribute.MAX_HEALTH);
return (float) getAttributeValue(Attribute.GENERIC_MAX_HEALTH);
}
/**
* Sets the heal of the entity as its max health.
* <p>
* Retrieved from {@link #getAttributeValue(Attribute)} with the attribute {@link Attribute#MAX_HEALTH}.
* Retrieved from {@link #getAttributeValue(Attribute)} with the attribute {@link Attribute#GENERIC_MAX_HEALTH}.
*/
public void heal() {
setHealth(getAttributeValue(Attribute.MAX_HEALTH));
setHealth((float) getAttributeValue(Attribute.GENERIC_MAX_HEALTH));
}
/**
@ -443,7 +443,7 @@ public class LivingEntity extends Entity implements EquipmentHandler {
* @return the attribute instance
*/
public @NotNull AttributeInstance getAttribute(@NotNull Attribute attribute) {
return attributeModifiers.computeIfAbsent(attribute.key(),
return attributeModifiers.computeIfAbsent(attribute.name(),
s -> new AttributeInstance(attribute, this::onAttributeChanged));
}
@ -459,7 +459,7 @@ public class LivingEntity extends Entity implements EquipmentHandler {
// 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(attributeInstance));
if (self) {
sendPacketToViewersAndSelf(propertiesPacket);
} else {
@ -473,8 +473,8 @@ public class LivingEntity extends Entity implements EquipmentHandler {
* @param attribute the attribute value to get
* @return the attribute value
*/
public float getAttributeValue(@NotNull Attribute attribute) {
AttributeInstance instance = attributeModifiers.get(attribute.key());
public double getAttributeValue(@NotNull Attribute attribute) {
AttributeInstance instance = attributeModifiers.get(attribute.name());
return (instance != null) ? instance.getValue() : attribute.defaultValue();
}
@ -566,12 +566,12 @@ public class LivingEntity extends Entity implements EquipmentHandler {
}
/**
* 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()));
protected @NotNull EntityAttributesPacket getPropertiesPacket() {
return new EntityAttributesPacket(getEntityId(), List.copyOf(attributeModifiers.values()));
}
/**
@ -667,7 +667,7 @@ public class LivingEntity extends Entity implements EquipmentHandler {
*/
@Override
public void takeKnockback(float strength, final double x, final double z) {
strength *= 1 - getAttributeValue(Attribute.KNOCKBACK_RESISTANCE);
strength *= (float) (1 - getAttributeValue(Attribute.GENERIC_KNOCKBACK_RESISTANCE));
super.takeKnockback(strength, x, z);
}
}

View File

@ -3,6 +3,7 @@ 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;
@ -19,10 +20,8 @@ import org.jetbrains.annotations.UnknownNullability;
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.UUID;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
public final class Metadata {
public static Entry<Byte> Byte(byte value) {
@ -50,11 +49,11 @@ public final class Metadata {
}
public static Entry<Component> 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<ItemStack> Slot(@NotNull ItemStack value) {
return new MetadataImpl.EntryImpl<>(TYPE_SLOT, value, NetworkBuffer.ITEM);
public static Entry<ItemStack> ItemStack(@NotNull ItemStack value) {
return new MetadataImpl.EntryImpl<>(TYPE_ITEM_STACK, value, NetworkBuffer.ITEM);
}
public static Entry<Boolean> Boolean(boolean value) {
@ -65,12 +64,12 @@ public final class Metadata {
return new MetadataImpl.EntryImpl<>(TYPE_ROTATION, value, NetworkBuffer.VECTOR3);
}
public static Entry<Point> Position(@NotNull Point value) {
return new MetadataImpl.EntryImpl<>(TYPE_POSITION, value, NetworkBuffer.BLOCK_POSITION);
public static Entry<Point> BlockPosition(@NotNull Point value) {
return new MetadataImpl.EntryImpl<>(TYPE_BLOCK_POSITION, value, NetworkBuffer.BLOCK_POSITION);
}
public static Entry<Point> OptPosition(@Nullable Point value) {
return new MetadataImpl.EntryImpl<>(TYPE_OPTPOSITION, value, NetworkBuffer.OPT_BLOCK_POSITION);
public static Entry<Point> OptBlockPosition(@Nullable Point value) {
return new MetadataImpl.EntryImpl<>(TYPE_OPT_BLOCK_POSITION, value, NetworkBuffer.OPT_BLOCK_POSITION);
}
public static Entry<Direction> Direction(@NotNull Direction value) {
@ -78,7 +77,7 @@ public final class Metadata {
}
public static Entry<UUID> 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<Integer> BlockState(@Nullable Integer value) {
@ -86,22 +85,38 @@ public final class Metadata {
}
public static Entry<Integer> OptBlockState(@Nullable Integer value) {
return new MetadataImpl.EntryImpl<>(TYPE_OPTBLOCKSTATE, value, NetworkBuffer.OPT_BLOCK_STATE);
return new MetadataImpl.EntryImpl<>(TYPE_OPT_BLOCKSTATE, value, NetworkBuffer.OPT_BLOCK_STATE);
}
public static Entry<BinaryTag> NBT(@NotNull BinaryTag nbt) {
return new MetadataImpl.EntryImpl<>(TYPE_NBT, nbt, NetworkBuffer.NBT);
}
public static Entry<int[]> VillagerData(int villagerType,
int villagerProfession,
int level) {
public static Entry<Particle> Particle(@NotNull Particle particle) {
return new MetadataImpl.EntryImpl<>(TYPE_PARTICLE, particle, NetworkBuffer.PARTICLE);
}
public static Entry<List<Particle>> ParticleList(@NotNull List<Particle> particles) {
return new MetadataImpl.EntryImpl<>(TYPE_PARTICLE_LIST, particles, new NetworkBuffer.Type<>() {
@Override
public void write(@NotNull NetworkBuffer buffer, List<Particle> value) {
buffer.writeCollection(NetworkBuffer.PARTICLE, value);
}
@Override
public List<Particle> read(@NotNull NetworkBuffer buffer) {
return buffer.readCollection(NetworkBuffer.PARTICLE, Integer.MAX_VALUE);
}
});
}
public static Entry<int[]> VillagerData(int villagerType, int villagerProfession, int level) {
return new MetadataImpl.EntryImpl<>(TYPE_VILLAGERDATA, new int[]{villagerType, villagerProfession, level},
NetworkBuffer.VILLAGER_DATA);
}
public static Entry<Integer> 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);
@ -123,18 +138,24 @@ public final class Metadata {
return new MetadataImpl.EntryImpl<>(TYPE_CAT_VARIANT, value, NetworkBuffer.CAT_VARIANT);
}
// WOLF VARIANT
public static Entry<FrogMeta.Variant> FrogVariant(@NotNull FrogMeta.Variant value) {
return new MetadataImpl.EntryImpl<>(TYPE_FROG_VARIANT, value, NetworkBuffer.FROG_VARIANT);
}
public static Entry<PaintingMeta.Variant> PaintingVariant(@NotNull PaintingMeta.Variant value) {
return new MetadataImpl.EntryImpl<>(TYPE_PAINTINGVARIANT, value, NetworkBuffer.PAINTING_VARIANT);
return new MetadataImpl.EntryImpl<>(TYPE_PAINTING_VARIANT, value, NetworkBuffer.PAINTING_VARIANT);
}
public static Entry<SnifferMeta.State> SnifferState(@NotNull SnifferMeta.State value) {
return new MetadataImpl.EntryImpl<>(TYPE_SNIFFER_STATE, value, NetworkBuffer.SNIFFER_STATE);
}
public static Entry<ArmadilloMeta.State> ArmadilloState(@NotNull ArmadilloMeta.State value) {
return new MetadataImpl.EntryImpl<>(TYPE_ARMADILLO_STATE, value, NetworkBuffer.ARMADILLO_STATE);
}
public static Entry<Point> Vector3(@NotNull Point value) {
return new MetadataImpl.EntryImpl<>(TYPE_VECTOR3, value, NetworkBuffer.VECTOR3);
}
@ -143,41 +164,46 @@ public final class Metadata {
return new MetadataImpl.EntryImpl<>(TYPE_QUATERNION, value, NetworkBuffer.QUATERNION);
}
public static Entry<Particle> Particle(@NotNull Particle particle) {
return new MetadataImpl.EntryImpl<>(TYPE_PARTICLE, particle, NetworkBuffer.PARTICLE);
}
private static final AtomicInteger NEXT_ID = new AtomicInteger(0);
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;
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 byte nextId() {
return (byte) NEXT_ID.getAndIncrement();
}
private static final VarHandle NOTIFIED_CHANGES;
static {

View File

@ -3,6 +3,7 @@ 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;
@ -10,11 +11,14 @@ 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 java.util.List;
import static net.minestom.server.entity.Metadata.*;
import static net.minestom.server.network.NetworkBuffer.VAR_INT;
@ -28,26 +32,29 @@ final class MetadataImpl {
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_OPT_UUID, OptUUID(null));
EMPTY_VALUES.set(TYPE_BLOCKSTATE, BlockState(Block.AIR.id()));
EMPTY_VALUES.set(TYPE_OPTBLOCKSTATE, OptBlockState(null));
EMPTY_VALUES.set(TYPE_OPT_BLOCKSTATE, OptBlockState(null));
EMPTY_VALUES.set(TYPE_NBT, NBT(EndBinaryTag.endBinaryTag()));
//EMPTY_VALUES.set(TYPE_PARTICLE -> throw new UnsupportedOperationException();
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));
// WolfVariant
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();

View File

@ -25,13 +25,13 @@ import net.minestom.server.advancements.AdvancementTab;
import net.minestom.server.adventure.AdventurePacketConvertor;
import net.minestom.server.adventure.Localizable;
import net.minestom.server.adventure.audience.Audiences;
import net.minestom.server.attribute.Attribute;
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;
@ -251,7 +251,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
this.gameMode = GameMode.SURVIVAL;
this.dimensionType = DimensionType.OVERWORLD; // Default dimension
this.levelFlat = true;
getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue(0.1f);
getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).setBaseValue(0.1);
// FakePlayer init its connection there
playerConnectionInit();
@ -293,8 +293,8 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
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, dimensionType.getId(), spawnInstance.getDimensionName(),
0, gameMode, null, false, levelFlat, deathLocation, portalCooldown, true);
sendPacket(joinGamePacket);
// Difficulty
@ -523,7 +523,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
setOnFire(false);
refreshHealth();
sendPacket(new RespawnPacket(getDimensionType().toString(), instance.getDimensionName(),
sendPacket(new RespawnPacket(getDimensionType().getId(), instance.getDimensionName(),
0, gameMode, gameMode, false, levelFlat, deathLocation, portalCooldown, RespawnPacket.COPY_ALL));
refreshClientStateAfterRespawn();
@ -1202,7 +1202,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
final PlayerInfoRemovePacket removePlayerPacket = getRemovePlayerToList();
final PlayerInfoUpdatePacket addPlayerPacket = getAddPlayerToList();
RespawnPacket respawnPacket = new RespawnPacket(getDimensionType().toString(), instance.getDimensionName(),
RespawnPacket respawnPacket = new RespawnPacket(getDimensionType().getId(), instance.getDimensionName(),
0, gameMode, gameMode, false, levelFlat, deathLocation, portalCooldown, RespawnPacket.COPY_ALL);
sendPacket(removePlayerPacket);
@ -1653,7 +1653,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
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,
sendPacket(new RespawnPacket(dimensionType.getId(), dimensionName,
0, gameMode, gameMode, false, levelFlat,
deathLocation, portalCooldown, RespawnPacket.COPY_ALL));
refreshClientStateAfterRespawn();

View File

@ -0,0 +1,59 @@
package net.minestom.server.entity.attribute;
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 Attribute extends StaticProtocolObject, Attributes permits AttributeImpl {
@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);
}
}

View File

@ -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<Attribute> 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<Attribute> values() {
return CONTAINER.values();
}
@Override
public String toString() {
return name();
}
}

View File

@ -1,4 +1,4 @@
package net.minestom.server.attribute;
package net.minestom.server.entity.attribute;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -16,8 +16,8 @@ public final class AttributeInstance {
private final Attribute attribute;
private final Map<UUID, AttributeModifier> modifiers = new HashMap<>();
private final Consumer<AttributeInstance> propertyChangeListener;
private float baseValue;
private float cachedValue = 0.0f;
private double baseValue;
private double cachedValue = 0.0f;
public AttributeInstance(@NotNull Attribute attribute, @Nullable Consumer<AttributeInstance> listener) {
this.attribute = attribute;
@ -39,9 +39,9 @@ public final class AttributeInstance {
* The base value of this instance without modifiers
*
* @return the instance base value
* @see #setBaseValue(float)
* @see #setBaseValue(double)
*/
public float getBaseValue() {
public double getBaseValue() {
return baseValue;
}
@ -51,7 +51,7 @@ public final class AttributeInstance {
* @param baseValue the new base value
* @see #getBaseValue()
*/
public void setBaseValue(float baseValue) {
public void setBaseValue(double baseValue) {
if (this.baseValue != baseValue) {
this.baseValue = baseValue;
refreshCachedValue();
@ -104,7 +104,7 @@ public final class AttributeInstance {
*
* @return the attribute value
*/
public float getValue() {
public double getValue() {
return cachedValue;
}
@ -113,13 +113,13 @@ public final class AttributeInstance {
*/
private void refreshCachedValue() {
final Collection<AttributeModifier> modifiers = getModifiers();
float base = getBaseValue();
double base = getBaseValue();
for (var modifier : modifiers.stream().filter(mod -> mod.getOperation() == AttributeOperation.ADDITION).toArray(AttributeModifier[]::new)) {
base += modifier.getAmount();
}
float result = base;
double result = base;
for (var modifier : modifiers.stream().filter(mod -> mod.getOperation() == AttributeOperation.MULTIPLY_BASE).toArray(AttributeModifier[]::new)) {
result += (base * modifier.getAmount());

View File

@ -1,4 +1,4 @@
package net.minestom.server.attribute;
package net.minestom.server.entity.attribute;
import org.jetbrains.annotations.NotNull;

View File

@ -1,4 +1,4 @@
package net.minestom.server.attribute;
package net.minestom.server.entity.attribute;
import org.jetbrains.annotations.Nullable;

View File

@ -1,6 +1,6 @@
package net.minestom.server.entity.damage;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.network.packet.server.configuration.RegistryDataPacket;
import net.minestom.server.registry.Registry;
import net.minestom.server.registry.StaticProtocolObject;
import net.minestom.server.utils.NamespaceID;
@ -36,7 +36,7 @@ public sealed interface DamageType extends StaticProtocolObject, DamageTypes per
return registry().scaling();
}
CompoundBinaryTag asNBT();
@NotNull RegistryDataPacket.Entry toRegistryEntry();
static @NotNull Collection<@NotNull DamageType> values() {
return DamageTypeImpl.values();
@ -54,7 +54,7 @@ public sealed interface DamageType extends StaticProtocolObject, DamageTypes per
return DamageTypeImpl.getId(id);
}
static CompoundBinaryTag getNBT() {
return DamageTypeImpl.getNBT();
static @NotNull RegistryDataPacket registryDataPacket() {
return DamageTypeImpl.registryDataPacket();
}
}

View File

@ -1,8 +1,7 @@
package net.minestom.server.entity.damage;
import net.kyori.adventure.nbt.BinaryTagTypes;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.ListBinaryTag;
import net.minestom.server.network.packet.server.configuration.RegistryDataPacket;
import net.minestom.server.registry.Registry;
import org.jetbrains.annotations.NotNull;
@ -30,13 +29,14 @@ record DamageTypeImpl(Registry.DamageTypeEntry registry, int id) implements Dama
return CONTAINER.getId(id);
}
@Override
public CompoundBinaryTag asNBT() {
return CompoundBinaryTag.builder()
public @NotNull RegistryDataPacket.Entry toRegistryEntry() {
return new RegistryDataPacket.Entry(name(), CompoundBinaryTag.builder()
.putFloat("exhaustion", registry.exhaustion())
.putString("message_id", registry.messageId())
.putString("scaling", registry.scaling())
.build();
.build());
}
static Collection<DamageType> values() {
@ -53,24 +53,15 @@ record DamageTypeImpl(Registry.DamageTypeEntry registry, int id) implements Dama
return id;
}
private static CompoundBinaryTag lazyNbt = null;
private static RegistryDataPacket lazyRegistryDataPacket = null;
static CompoundBinaryTag getNBT() {
if (lazyNbt == null) {
var entries = ListBinaryTag.builder(BinaryTagTypes.COMPOUND);
for (var damageType : values()) {
entries.add(CompoundBinaryTag.builder()
.putInt("id", damageType.id())
.putString("name", damageType.name())
.put("element", damageType.asNBT())
.build());
}
lazyNbt = CompoundBinaryTag.builder()
.putString("type", "minecraft:damage_type")
.put("value", entries.build())
.build();
}
return lazyNbt;
static @NotNull RegistryDataPacket registryDataPacket() {
if (lazyRegistryDataPacket != null) return lazyRegistryDataPacket;
return lazyRegistryDataPacket = new RegistryDataPacket(
"minecraft:damage_type",
values().stream()
.map(DamageType::toRegistryEntry)
.toList()
);
}
}

View File

@ -4,9 +4,12 @@ import net.minestom.server.coordinate.Point;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.Metadata;
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;
@ -44,58 +47,28 @@ public class LivingEntityMeta extends EntityMeta {
setMaskBit(OFFSET, IS_IN_SPIN_ATTACK_BIT, value);
}
public float getHealth() {
return super.metadata.getIndex(OFFSET + 1, 1F);
public @NotNull List<Particle> getEffectParticles() {
return super.metadata.getIndex(OFFSET + 1, List.of());
}
public void setHealth(float value) {
super.metadata.setIndex(OFFSET + 1, Metadata.Float(value));
}
public int getPotionEffectColor() {
return super.metadata.getIndex(OFFSET + 2, 0);
}
public void setPotionEffectColor(int value) {
super.metadata.setIndex(OFFSET + 2, Metadata.VarInt(value));
public void setEffectParticles(@NotNull List<Particle> value) {
super.metadata.setIndex(OFFSET + 1, Metadata.ParticleList(value));
}
public boolean isPotionEffectAmbient() {
return super.metadata.getIndex(OFFSET + 3, false);
return super.metadata.getIndex(OFFSET + 2, false);
}
public void setPotionEffectAmbient(boolean value) {
super.metadata.setIndex(OFFSET + 3, Metadata.Boolean(value));
super.metadata.setIndex(OFFSET + 2, Metadata.Boolean(value));
}
public int getArrowCount() {
return super.metadata.getIndex(OFFSET + 4, 0);
return super.metadata.getIndex(OFFSET + 3, 0);
}
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));
super.metadata.setIndex(OFFSET + 3, Metadata.VarInt(value));
}
/**
@ -103,7 +76,7 @@ public class LivingEntityMeta extends EntityMeta {
* @return The amount of bee stingers
*/
public int getBeeStingerCount() {
return super.metadata.getIndex(OFFSET + 5, 0);
return super.metadata.getIndex(OFFSET + 4, 0);
}
/**
@ -111,7 +84,15 @@ public class LivingEntityMeta extends EntityMeta {
* @param value The amount of bee stingers to set, use 0 to clear all stingers
*/
public void setBeeStingerCount(int value) {
super.metadata.setIndex(OFFSET + 5, Metadata.VarInt(value));
super.metadata.setIndex(OFFSET + 4, Metadata.VarInt(value));
}
public float getHealth() {
return super.metadata.getIndex(OFFSET + 5, 1F);
}
public void setHealth(float value) {
super.metadata.setIndex(OFFSET + 5, Metadata.Float(value));
}
@Nullable
@ -120,7 +101,7 @@ public class LivingEntityMeta extends EntityMeta {
}
public void setBedInWhichSleepingPosition(@Nullable Point value) {
super.metadata.setIndex(OFFSET + 6, Metadata.OptPosition(value));
super.metadata.setIndex(OFFSET + 6, Metadata.OptBlockPosition(value));
}
}

View File

@ -0,0 +1,32 @@
package net.minestom.server.entity.metadata.animal;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.Metadata;
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 Metadata 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;
private static final State[] VALUES = values();
}
}

View File

@ -19,7 +19,7 @@ public class TurtleMeta extends AnimalMeta {
}
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 +43,7 @@ public class TurtleMeta extends AnimalMeta {
}
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() {

View File

@ -12,6 +12,8 @@ public class WolfMeta extends TameableAnimalMeta {
super(entity, metadata);
}
//todo variant
public boolean isBegging() {
return super.metadata.getIndex(OFFSET, false);
}

View File

@ -18,7 +18,7 @@ public class ItemDisplayMeta extends AbstractDisplayMeta {
}
public void setItemStack(@NotNull ItemStack value) {
super.metadata.setIndex(OFFSET, Metadata.Slot(value));
super.metadata.setIndex(OFFSET, Metadata.ItemStack(value));
}
public @NotNull DisplayContext getDisplayContext() {

View File

@ -24,7 +24,7 @@ class ItemContainingMeta extends EntityMeta {
}
public void setItem(@NotNull ItemStack item) {
super.metadata.setIndex(OFFSET, Metadata.Slot(item));
super.metadata.setIndex(OFFSET, Metadata.ItemStack(item));
}
}

View File

@ -0,0 +1,14 @@
package net.minestom.server.entity.metadata.monster.skeleton;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.Metadata;
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 + 0;
public BoggedMeta(@NotNull Entity entity, @NotNull Metadata metadata) {
super(entity, metadata);
}
}

View File

@ -20,7 +20,7 @@ public class EndCrystalMeta extends EntityMeta {
}
public void setBeamTarget(@Nullable Point value) {
super.metadata.setIndex(OFFSET, Metadata.OptPosition(value));
super.metadata.setIndex(OFFSET, Metadata.OptBlockPosition(value));
}
public boolean isShowingBottom() {

View File

@ -24,7 +24,7 @@ public class FallingBlockMeta extends EntityMeta implements ObjectDataProvider {
}
public void setSpawnPosition(Point value) {
super.metadata.setIndex(OFFSET, Metadata.Position(value));
super.metadata.setIndex(OFFSET, Metadata.BlockPosition(value));
}
@NotNull

View File

@ -25,7 +25,7 @@ public class ItemFrameMeta extends EntityMeta implements ObjectDataProvider {
}
public void setItem(@NotNull ItemStack value) {
super.metadata.setIndex(OFFSET, Metadata.Slot(value));
super.metadata.setIndex(OFFSET, Metadata.ItemStack(value));
}
@NotNull

View File

@ -0,0 +1,25 @@
package net.minestom.server.entity.metadata.other;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.Metadata;
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 Metadata 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));
}
}

View File

@ -3,7 +3,6 @@ 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.projectile.ProjectileMeta;
import net.minestom.server.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -24,7 +23,7 @@ public class FireworkRocketMeta extends EntityMeta implements ProjectileMeta {
}
public void setFireworkInfo(@NotNull ItemStack value) {
super.metadata.setIndex(OFFSET, Metadata.Slot(value));
super.metadata.setIndex(OFFSET, Metadata.ItemStack(value));
}
@Override

View File

@ -20,7 +20,7 @@ public class DolphinMeta extends WaterAnimalMeta {
}
public void setTreasurePosition(@NotNull Point value) {
super.metadata.setIndex(OFFSET, Metadata.Position(value));
super.metadata.setIndex(OFFSET, Metadata.BlockPosition(value));
}
public boolean isHasFish() {

View File

@ -4,11 +4,11 @@ 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.coordinate.Point;
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 org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
@ -34,7 +34,7 @@ public final class PFPathingEntity implements IPathingEntity {
this.navigator = navigator;
this.entity = navigator.getEntity();
this.searchRange = getAttributeValue(Attribute.FOLLOW_RANGE);
this.searchRange = (float) getAttributeValue(Attribute.GENERIC_FOLLOW_RANGE);
}
@Override
@ -138,7 +138,7 @@ public final class PFPathingEntity implements IPathingEntity {
return new Capabilities() {
@Override
public float speed() {
return getAttributeValue(Attribute.MOVEMENT_SPEED);
return (float) getAttributeValue(Attribute.GENERIC_MOVEMENT_SPEED);
}
@Override
@ -191,7 +191,7 @@ public final class PFPathingEntity implements IPathingEntity {
@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(Attribute.MOVEMENT_SPEED));
this.navigator.moveTowards(targetPosition, getAttributeValue(Attribute.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.
@ -217,7 +217,7 @@ public final class PFPathingEntity implements IPathingEntity {
return (float) entity.getBoundingBox().height();
}
private float getAttributeValue(@NotNull Attribute attribute) {
private double getAttributeValue(@NotNull Attribute attribute) {
if (entity instanceof LivingEntity) {
return ((LivingEntity) entity).getAttributeValue(attribute);
}

View File

@ -1,7 +1,7 @@
package net.minestom.server.item;
import net.minestom.server.attribute.Attribute;
import net.minestom.server.attribute.AttributeOperation;
import net.minestom.server.entity.attribute.Attribute;
import net.minestom.server.entity.attribute.AttributeOperation;
import net.minestom.server.item.attribute.AttributeSlot;
import net.minestom.server.item.attribute.ItemAttribute;
import net.minestom.server.tag.Tag;
@ -57,7 +57,7 @@ public final class ItemSerializers {
final int operation = reader.getTag(OPERATION);
final String name = reader.getTag(NAME);
final Attribute attribute = Attribute.fromKey(attributeName.toLowerCase(Locale.ROOT));
final Attribute attribute = Attribute.fromNamespaceId(attributeName.toLowerCase(Locale.ROOT));
// Wrong attribute name, stop here
if (attribute == null) return null;
final AttributeOperation attributeOperation = AttributeOperation.fromId(operation);
@ -79,7 +79,7 @@ public final class ItemSerializers {
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().key());
writer.setTag(ATTRIBUTE_NAME, value.attribute().name());
writer.setTag(OPERATION, value.operation().getId());
writer.setTag(NAME, value.name());
}

View File

@ -1,8 +1,8 @@
package net.minestom.server.item;
import net.minestom.server.instance.block.Block;
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.Contract;
import org.jetbrains.annotations.NotNull;
@ -10,6 +10,38 @@ import org.jetbrains.annotations.Nullable;
import java.util.Collection;
/**
*
* Component notes
* todo delete me
*
* ItemComponent.DYED_ITEM -> record DyedItemComponent.class
*
* itemStack.with(ItemComponent.DYED_ITEM, new DyedItemComponent(Color.RED))
* itemStack.get(ItemComponent.DYED_ITEM)
* itemStack.getOrDefault(ItemComponent.DYED_ITEM, new DyedItemComponent(Color.RED))
*
* material.prototype() -> some component list?
*
*
*
* // NEW WIRE FORMAT
* count | varint
* material id | varint
* components | SEE BELOW
*
* DataComponentPatch
* additions | varint
* removals | varint
* for each addition
* varint | data component id
* data component | data component (depends)
* for each removal
* varint | data component id
*
*
*/
public sealed interface Material extends StaticProtocolObject, Materials permits MaterialImpl {
/**

View File

@ -1,7 +1,7 @@
package net.minestom.server.item.attribute;
import net.minestom.server.attribute.Attribute;
import net.minestom.server.attribute.AttributeOperation;
import net.minestom.server.entity.attribute.Attribute;
import net.minestom.server.entity.attribute.AttributeOperation;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;

View File

@ -0,0 +1,23 @@
package net.minestom.server.item.component;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.tag.Tag;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
public record CustomData(@NotNull NBTCompound nbt) implements ItemComponent {
static final Tag<CustomData> TAG = Tag.Structure("ab", CustomData.class);
static final NetworkBuffer.Type<CustomData> 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((NBTCompound) buffer.read(NetworkBuffer.NBT));
}
};
}

View File

@ -0,0 +1,20 @@
package net.minestom.server.item.component;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.tag.Tag;
import org.jetbrains.annotations.NotNull;
public sealed interface ItemComponent permits CustomData {
ItemComponent.Type<CustomData> CUSTOM_DATA = new ItemComponent.Type<>("custom_data", CustomData.NETWORK_TYPE, CustomData.TAG);
record Type<T extends ItemComponent>(
@NotNull String name,
@NotNull NetworkBuffer.Type<T> network,
@NotNull Tag<T> tag
) {
}
}

View File

@ -13,7 +13,7 @@ import org.jetbrains.annotations.UnknownNullability;
import java.util.List;
public record PotionMeta(TagReadable readable) implements ItemMetaView<PotionMeta.Builder> {
private static final Tag<PotionType> POTION_TYPE = Tag.String("Potion").map(PotionType::fromNamespaceId, StaticProtocolObject::name).defaultValue(PotionType.EMPTY);
private static final Tag<PotionType> POTION_TYPE = Tag.String("Potion").map(PotionType::fromNamespaceId, StaticProtocolObject::name).defaultValue(PotionType.WATER);
private static final Tag<List<CustomPotionEffect>> CUSTOM_POTION_EFFECTS = Tag.Structure("CustomPotionEffects", new TagSerializer<CustomPotionEffect>() {
@Override
public @Nullable CustomPotionEffect read(@NotNull TagReadable reader) {

View File

@ -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 final class HandshakeListener {
}
}
}
case TRANSFER -> {
throw new UnsupportedOperationException("Transfer intent is not supported in HandshakeListener");
}
default -> {
// Unexpected error
}

View File

@ -75,7 +75,7 @@ public final class LoginListener {
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

View File

@ -5,6 +5,7 @@ import net.kyori.adventure.nbt.TagStringIO;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.entity.Player;
import net.minestom.server.network.packet.server.configuration.RegistryDataPacket;
import net.minestom.server.network.packet.server.play.SystemChatPacket;
import net.minestom.server.utils.PacketUtils;
import org.jetbrains.annotations.NotNull;
@ -12,6 +13,7 @@ import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
@ -26,44 +28,36 @@ 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 CompoundBinaryTag CHAT_REGISTRY;
private static final RegistryDataPacket REGISTRY_DATA_PACKET;
static {
try {
CHAT_REGISTRY = TagStringIO.get().asCompound(
"""
{
"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"
]
}
}
} ]
}"""
);
REGISTRY_DATA_PACKET = new RegistryDataPacket(
"minecraft:chat_type", List.of(
new RegistryDataPacket.Entry(
"minecraft:chat",
TagStringIO.get().asCompound(
"""
{
"chat":{
"translation_key":"chat.type.text",
"parameters":["sender", "content"]
},
"narration":{
"translation_key":"chat.type.text.narrate",
"parameters":["sender", "content"]
}
}"""
)
)
));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static @NotNull CompoundBinaryTag chatRegistry() {
return CHAT_REGISTRY;
public static @NotNull RegistryDataPacket registryDataPacket() {
return REGISTRY_DATA_PACKET;
}
/**

View File

@ -1,6 +1,5 @@
package net.minestom.server.network;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.MinecraftServer;
@ -18,7 +17,6 @@ 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.login.LoginSuccessPacket;
import net.minestom.server.network.packet.server.play.StartConfigurationPacket;
import net.minestom.server.network.player.PlayerConnection;
@ -33,8 +31,6 @@ import org.jctools.queues.MpscUnboundedArrayQueue;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@ -282,15 +278,15 @@ public final class ConnectionManager {
// Registry data (if it should be sent)
if (event.willSendRegistryData()) {
var registryCompound = CompoundBinaryTag.builder()
.put("minecraft:chat_type", Messenger.chatRegistry())
.put("minecraft:dimension_type", MinecraftServer.getDimensionTypeManager().toNBT())
.put("minecraft:worldgen/biome", MinecraftServer.getBiomeManager().toNBT())
.put("minecraft:damage_type", DamageType.getNBT())
// .put("minecraft:trim_material", MinecraftServer.getTrimManager().getTrimMaterialNBT())
// .put("minecraft:trim_pattern", MinecraftServer.getTrimManager().getTrimPatternNBT())
.build();
player.sendPacket(new RegistryDataPacket(registryCompound));
// minecraft:trim_pattern, minecraft:trim_material, minecraft:wolf_variant, and minecraft:banner_pattern.
player.sendPacket(Messenger.registryDataPacket());
player.sendPacket(MinecraftServer.getDimensionTypeManager().registryDataPacket());
player.sendPacket(MinecraftServer.getBiomeManager().registryDataPacket());
player.sendPacket(DamageType.registryDataPacket());
// registry.put("minecraft:trim_material", MinecraftServer.getTrimManager().getTrimMaterialNBT());
// registry.put("minecraft:trim_pattern", MinecraftServer.getTrimManager().getTrimPatternNBT());
player.sendPacket(TagsPacket.DEFAULT_TAGS);
}

View File

@ -4,6 +4,7 @@ import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.text.Component;
import net.minestom.server.coordinate.Point;
import net.minestom.server.entity.Entity;
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;
@ -73,6 +74,7 @@ public final class NetworkBuffer {
public static final Type<FrogMeta.Variant> FROG_VARIANT = NetworkBufferTypeImpl.fromEnum(FrogMeta.Variant.class);
public static final Type<PaintingMeta.Variant> PAINTING_VARIANT = NetworkBufferTypeImpl.fromEnum(PaintingMeta.Variant.class);
public static final Type<SnifferMeta.State> SNIFFER_STATE = NetworkBufferTypeImpl.fromEnum(SnifferMeta.State.class);
public static final Type<ArmadilloMeta.State> ARMADILLO_STATE = NetworkBufferTypeImpl.fromEnum(ArmadilloMeta.State.class);
ByteBuffer nioBuffer;
@ -301,6 +303,11 @@ public final class NetworkBuffer {
void write(@NotNull NetworkBuffer writer);
}
@FunctionalInterface
public interface Reader<T> {
@NotNull T read(@NotNull NetworkBuffer reader);
}
public static byte[] makeArray(@NotNull Consumer<@NotNull NetworkBuffer> writing) {
NetworkBuffer writer = new NetworkBuffer();
writing.accept(writer);

View File

@ -399,32 +399,28 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
@Override
public void write(@NotNull NetworkBuffer buffer, ItemStack value) {
if (value.isAir()) {
buffer.write(BOOLEAN, false);
buffer.write(VAR_INT, 0); // 0 count always
return;
}
buffer.write(BOOLEAN, true);
buffer.write(VAR_INT, value.amount());
buffer.write(VAR_INT, value.material().id());
buffer.write(BYTE, (byte) value.amount());
// Vanilla does not write an empty object, just an end tag.
CompoundBinaryTag nbt = value.meta().toNBT();
buffer.write(NBT, nbt.size() == 0 ? EndBinaryTag.endBinaryTag() : nbt);
buffer.write(VAR_INT, 0); // Added components
buffer.write(VAR_INT, 0); // Removed components
}
@Override
public ItemStack read(@NotNull NetworkBuffer buffer) {
final boolean present = buffer.read(BOOLEAN);
if (!present) return ItemStack.AIR;
int count = buffer.read(VAR_INT);
if (count <= 0) 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 BinaryTag nbt = buffer.read(NBT);
if (!(nbt instanceof CompoundBinaryTag compound)) {
return ItemStack.of(material, amount);
}
return ItemStack.fromNBT(material, compound, amount);
buffer.read(VAR_INT); // Added components
buffer.read(VAR_INT); // Removed components
return ItemStack.fromNBT(material, new NBTCompound(), count);
}
}

View File

@ -3,6 +3,7 @@ package net.minestom.server.network.packet.client;
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 net.minestom.server.utils.collection.ObjectArray;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.UnknownNullability;
import java.util.function.Function;
/**
* Contains registered packets and a way to instantiate them.
* <p>
* 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<Function<NetworkBuffer, ClientPacket>> suppliers = ObjectArray.singleThread(0x10);
private final ObjectArray<NetworkBuffer.Reader<ClientPacket>> 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<ClientPacket> packetSupplier) {
this.suppliers.set(id, packetSupplier);
}
public @UnknownNullability ClientPacket create(int packetId, @NotNull NetworkBuffer reader) {
final Function<NetworkBuffer, ClientPacket> supplier = suppliers.get(packetId);
final NetworkBuffer.Reader<ClientPacket> 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 sealed class ClientPacketsHandler permits ClientPacketsHandler.Status, Cl
register(nextId(), ClientEncryptionResponsePacket::new);
register(nextId(), ClientLoginPluginResponsePacket::new);
register(nextId(), ClientLoginAcknowledgedPacket::new);
register(nextId(), ClientCookieResponsePacket::new);
}
}
@ -71,11 +71,13 @@ public sealed class ClientPacketsHandler permits ClientPacketsHandler.Status, Cl
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 sealed class ClientPacketsHandler permits ClientPacketsHandler.Status, Cl
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 sealed class ClientPacketsHandler permits ClientPacketsHandler.Status, Cl
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);

View File

@ -0,0 +1,35 @@
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 void write(@NotNull NetworkBuffer writer) {
writer.write(NetworkBuffer.STRING, key);
writer.writeOptional(NetworkBuffer.BYTE_ARRAY, value);
}
}

View File

@ -0,0 +1,29 @@
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<SelectKnownPacksPacket.Entry> 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);
}
public ClientSelectKnownPacksPacket(@NotNull NetworkBuffer reader) {
this(reader.readCollection(SelectKnownPacksPacket.Entry::new, MAX_ENTRIES));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.writeCollection(entries);
}
}

View File

@ -8,7 +8,7 @@ import org.jetbrains.annotations.NotNull;
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,7 @@ 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), Intent.fromId(reader.read(VAR_INT)));
}
@Override
@ -30,7 +30,7 @@ public record ClientHandshakePacket(int protocolVersion, @NotNull String serverA
}
writer.write(STRING, serverAddress);
writer.write(UNSIGNED_SHORT, serverPort);
writer.write(VAR_INT, intent);
writer.write(VAR_INT, intent.id());
}
private static int getMaxHandshakeLength() {
@ -38,4 +38,23 @@ public record ClientHandshakePacket(int protocolVersion, @NotNull String serverA
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;
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -3,27 +3,36 @@ package net.minestom.server.network.packet.server;
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 BUNDLE = nextPlayId();
public static final int SPAWN_ENTITY = nextPlayId();
@ -47,10 +56,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 +141,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 +149,25 @@ 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();
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();

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -20,7 +20,7 @@ public record ResourcePackPopPacket(@Nullable UUID id) implements ServerPacket.C
@Override
public int configurationId() {
return ServerPacketIdentifier.CONFIGURATION_RESOURCE_PACK_POP_PACKET;
return ServerPacketIdentifier.CONFIGURATION_RESOURCE_PACK_POP;
}
@Override

View File

@ -42,7 +42,7 @@ public record ResourcePackPushPacket(
@Override
public int configurationId() {
return ServerPacketIdentifier.CONFIGURATION_RESOURCE_PACK_PUSH_PACKET;
return ServerPacketIdentifier.CONFIGURATION_RESOURCE_PACK_PUSH;
}
@Override

View File

@ -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;
}
}

View File

@ -5,21 +5,47 @@ 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 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<Entry> entries
) implements ServerPacket.Configuration {
public record RegistryDataPacket(@NotNull CompoundBinaryTag data) implements ServerPacket.Configuration {
public RegistryDataPacket(@NotNull NetworkBuffer buffer) {
this((CompoundBinaryTag) 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);
}
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,52 @@
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 net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public record SelectKnownPacksPacket(
@NotNull List<Entry> entries
) implements ServerPacket.Configuration {
private static final int MAX_ENTRIES = 64;
public SelectKnownPacksPacket {
Check.argCondition(entries.size() > MAX_ENTRIES, "Too many known packs: {0} > {1}", entries.size(), MAX_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);
}
}
}

View File

@ -5,16 +5,20 @@ import net.minestom.server.network.packet.server.ServerPacket;
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 record EncryptionRequestPacket(@NotNull String serverId,
writer.write(STRING, serverId);
writer.write(BYTE_ARRAY, publicKey);
writer.write(BYTE_ARRAY, verifyToken);
writer.write(BOOLEAN, shouldAuthenticate);
}
@Override

View File

@ -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
}
}

View File

@ -42,8 +42,8 @@ public record DeclareRecipesPacket(@NotNull List<DeclaredRecipe> recipes) implem
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.writeCollection(recipes, (bWriter, recipe) -> {
bWriter.write(STRING, recipe.type());
bWriter.write(STRING, recipe.recipeId());
bWriter.write(STRING, recipe.type());
bWriter.write(recipe);
});
}

View File

@ -1,12 +1,13 @@
package net.minestom.server.network.packet.server.play;
import net.minestom.server.attribute.Attribute;
import net.minestom.server.attribute.AttributeInstance;
import net.minestom.server.attribute.AttributeModifier;
import net.minestom.server.attribute.AttributeOperation;
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.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.Collection;
@ -14,16 +15,19 @@ import java.util.List;
import static net.minestom.server.network.NetworkBuffer.*;
public record EntityPropertiesPacket(int entityId, List<AttributeInstance> properties) implements ServerPacket.Play {
public record EntityAttributesPacket(int entityId, List<AttributeInstance> properties) implements ServerPacket.Play {
public static final int MAX_ENTRIES = 1024;
public EntityPropertiesPacket {
public EntityAttributesPacket {
properties = List.copyOf(properties);
}
public EntityPropertiesPacket(@NotNull NetworkBuffer reader) {
public EntityAttributesPacket(@NotNull NetworkBuffer reader) {
this(reader.read(VAR_INT), reader.readCollection(r -> {
final Attribute attribute = Attribute.fromKey(reader.read(STRING));
int id = reader.read(VAR_INT);
final Attribute attribute = Attribute.fromId(id);
Check.notNull(attribute, "Unknown attribute id: " + id);
final double value = reader.read(DOUBLE);
int modifierCount = reader.read(VAR_INT);
AttributeInstance instance = new AttributeInstance(attribute, null);
@ -42,8 +46,8 @@ public record EntityPropertiesPacket(int entityId, List<AttributeInstance> prope
for (AttributeInstance instance : properties) {
final Attribute attribute = instance.getAttribute();
writer.write(STRING, attribute.key());
writer.write(DOUBLE, (double) instance.getBaseValue());
writer.write(VAR_INT, attribute.id());
writer.write(DOUBLE, instance.getBaseValue());
{
Collection<AttributeModifier> modifiers = instance.getModifiers();
@ -60,6 +64,6 @@ public record EntityPropertiesPacket(int entityId, List<AttributeInstance> prope
@Override
public int playId() {
return ServerPacketIdentifier.ENTITY_PROPERTIES;
return ServerPacketIdentifier.ENTITY_ATTRIBUTES;
}
}

View File

@ -1,27 +1,22 @@
package net.minestom.server.network.packet.server.play;
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 net.minestom.server.potion.Potion;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
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 CompoundBinaryTag 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) ? (CompoundBinaryTag) 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

View File

@ -3,7 +3,6 @@ package net.minestom.server.network.packet.server.play;
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.HashMap;
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<Integer, Metadata.Entry<?>> entries) implements ServerPacket.Play, ServerPacket.ComponentHolding {
@ -73,7 +73,7 @@ public record EntityMetaDataPacket(int entityId,
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);
}

View File

@ -15,9 +15,10 @@ import static net.minestom.server.network.NetworkBuffer.*;
public record JoinGamePacket(
int entityId, boolean isHardcore, List<String> 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,8 +37,7 @@ public record JoinGamePacket(
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)),
@ -45,7 +45,8 @@ public record JoinGamePacket(
reader.read(BOOLEAN),
reader.read(BOOLEAN),
reader.read(DEATH_LOCATION),
reader.read(VAR_INT)
reader.read(VAR_INT),
reader.read(BOOLEAN)
);
}
@ -60,8 +61,7 @@ public record JoinGamePacket(
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());
@ -74,6 +74,7 @@ public record JoinGamePacket(
writer.write(BOOLEAN, isFlat);
writer.write(DEATH_LOCATION, deathLocation);
writer.write(VAR_INT, portalCooldown);
writer.write(BOOLEAN, enforcesSecureChat);
}
@Override

View File

@ -0,0 +1,28 @@
package net.minestom.server.network.packet.server.play;
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;
public record ProjectilePowerPacket(
int entityId, @NotNull Point power
) implements ServerPacket.Play {
public ProjectilePowerPacket(@NotNull NetworkBuffer buffer) {
this(buffer.read(NetworkBuffer.VAR_INT), buffer.read(NetworkBuffer.VECTOR3D));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(NetworkBuffer.VAR_INT, entityId);
writer.write(NetworkBuffer.VECTOR3D, power);
}
@Override
public int playId() {
return ServerPacketIdentifier.PROJECTILE_POWER;
}
}

View File

@ -10,7 +10,7 @@ import org.jetbrains.annotations.NotNull;
import static net.minestom.server.network.NetworkBuffer.*;
public record RespawnPacket(
String dimensionType, String worldName,
int dimensionType, String worldName,
long hashedSeed, GameMode gameMode, GameMode previousGameMode,
boolean isDebug, boolean isFlat, WorldPos deathLocation,
int portalCooldown, int copyData
@ -21,7 +21,7 @@ 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),
@ -31,7 +31,7 @@ public record RespawnPacket(
@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());

View File

@ -48,6 +48,11 @@ 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.
*/
@ -106,6 +111,10 @@ public record Potion(@NotNull PotionEffect effect, byte amplifier,
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.
* <p>
@ -114,7 +123,7 @@ public record Potion(@NotNull PotionEffect effect, byte amplifier,
* @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));
}
/**

View File

@ -57,7 +57,7 @@ public class RecipeManager {
case SMITHING_TRIM -> RecipeConversion.smithingTrim((SmithingTrimRecipe) recipe);
});
}
return new DeclareRecipesPacket(entries);
return new DeclareRecipesPacket(List.of());
}
}

View File

@ -78,6 +78,11 @@ public final class Registry {
return new TrimPatternEntry(namespace, main, null);
}
@ApiStatus.Internal
public static AttributeEntry attribute(String namespace, @NotNull Properties main) {
return new AttributeEntry(namespace, main, null);
}
@ApiStatus.Internal
public static Map<String, Map<String, Object>> load(Resource resource) {
Map<String, Map<String, Object>> map = new HashMap<>();
@ -223,7 +228,8 @@ public final class Registry {
FLUID_TAGS("tags/fluid_tags.json"),
GAMEPLAY_TAGS("tags/gameplay_tags.json"),
ITEM_TAGS("tags/item_tags.json"),
BIOMES("biomes.json");
BIOMES("biomes.json"),
ATTRIBUTES("attributes.json");
private final String name;
@ -679,6 +685,23 @@ public final class Registry {
}
}
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 interface Entry {
@ApiStatus.Experimental
Properties custom();

View File

@ -1,6 +1,7 @@
package net.minestom.server.world;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.network.packet.server.configuration.RegistryDataPacket;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -109,13 +110,8 @@ public class DimensionType {
.build();
}
@NotNull
public CompoundBinaryTag toIndexedNBT() {
return CompoundBinaryTag.builder()
.putString("name", name.toString())
.putInt("id", id)
.put("element", toNBT())
.build();
public @NotNull RegistryDataPacket.Entry toRegistryEntry() {
return new RegistryDataPacket.Entry(name.toString(), toNBT());
}
@NotNull

View File

@ -1,8 +1,8 @@
package net.minestom.server.world;
import net.kyori.adventure.nbt.BinaryTagTypes;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.ListBinaryTag;
import net.minestom.server.network.packet.server.CachedPacket;
import net.minestom.server.network.packet.server.SendablePacket;
import net.minestom.server.network.packet.server.configuration.RegistryDataPacket;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -18,6 +18,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
*/
public final class DimensionTypeManager {
private final CachedPacket registryDataPacket = new CachedPacket(this::createRegistryDataPacket);
private final List<DimensionType> dimensionTypes = new CopyOnWriteArrayList<>();
public DimensionTypeManager() {
@ -32,6 +33,7 @@ public final class DimensionTypeManager {
public void addDimension(@NotNull DimensionType dimensionType) {
dimensionType.registered = true;
this.dimensionTypes.add(dimensionType);
registryDataPacket.invalidate();
}
/**
@ -42,7 +44,9 @@ public final class DimensionTypeManager {
*/
public boolean removeDimension(@NotNull DimensionType dimensionType) {
dimensionType.registered = false;
return dimensionTypes.remove(dimensionType);
boolean removed = dimensionTypes.remove(dimensionType);
if (removed) registryDataPacket.invalidate();
return removed;
}
/**
@ -80,20 +84,16 @@ public final class DimensionTypeManager {
return Collections.unmodifiableList(dimensionTypes);
}
/**
* Creates the {@link CompoundBinaryTag} containing all the registered dimensions.
* <p>
* Used when a player connects.
*
* @return an nbt compound containing the registered dimensions
*/
public @NotNull CompoundBinaryTag toNBT() {
ListBinaryTag.Builder<CompoundBinaryTag> entries = ListBinaryTag.builder(BinaryTagTypes.COMPOUND);
for (DimensionType dimensionType : dimensionTypes)
entries.add(dimensionType.toIndexedNBT());
return CompoundBinaryTag.builder()
.putString("type", "minecraft:dimension_type")
.put("value", entries.build())
.build();
public @NotNull SendablePacket registryDataPacket() {
return registryDataPacket;
}
private @NotNull RegistryDataPacket createRegistryDataPacket() {
return new RegistryDataPacket(
"minecraft:dimension_type",
dimensionTypes.stream()
.map(DimensionType::toRegistryEntry)
.toList()
);
}
}

View File

@ -2,6 +2,7 @@ package net.minestom.server.world.biomes;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.coordinate.Point;
import net.minestom.server.network.packet.server.configuration.RegistryDataPacket;
import net.minestom.server.registry.ProtocolObject;
import net.minestom.server.registry.Registry;
import net.minestom.server.utils.NamespaceID;
@ -53,6 +54,10 @@ sealed public interface Biome extends ProtocolObject permits BiomeImpl {
}
}
default @NotNull RegistryDataPacket.Entry toRegistryEntry() {
return new RegistryDataPacket.Entry(namespace().toString(), toNbt());
}
default @NotNull CompoundBinaryTag toNbt() {
Check.notNull(name(), "The biome namespace cannot be null");
Check.notNull(effects(), "The biome effects cannot be null");

View File

@ -1,8 +1,8 @@
package net.minestom.server.world.biomes;
import net.kyori.adventure.nbt.BinaryTagTypes;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.ListBinaryTag;
import net.minestom.server.network.packet.server.CachedPacket;
import net.minestom.server.network.packet.server.SendablePacket;
import net.minestom.server.network.packet.server.configuration.RegistryDataPacket;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
@ -10,9 +10,10 @@ import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.CopyOnWriteArrayList;
/**
@ -20,12 +21,13 @@ import java.util.concurrent.atomic.AtomicInteger;
* <p>
*/
public final class BiomeManager {
private final Map<Integer, Biome> biomes = new ConcurrentHashMap<>();
private final CachedPacket registryDataPacket = new CachedPacket(this::createRegistryDataPacket);
private final List<Biome> biomes = new CopyOnWriteArrayList<>();
private final Map<NamespaceID, Biome> biomesByName = new ConcurrentHashMap<>();
private final Map<NamespaceID, Integer> idMappings = new ConcurrentHashMap<>();
private final AtomicInteger ID_COUNTER = new AtomicInteger(0);
private CompoundBinaryTag nbtCache = null;
public BiomeManager() {
// Need to register plains for the client to work properly
@ -48,27 +50,28 @@ public final class BiomeManager {
public void addBiome(@NotNull Biome biome) {
Check.stateCondition(getByName(biome.namespace()) != null, "The biome " + biome.namespace() + " has already been registered");
var id = ID_COUNTER.getAndIncrement();
this.biomes.put(id, biome);
var id = this.biomes.size();
this.biomes.add(biome);
this.biomesByName.put(biome.namespace(), biome);
this.idMappings.put(biome.namespace(), id);
nbtCache = null;
registryDataPacket.invalidate();
}
/**
* Removes a biome. This does NOT send the new list to players.
*
* @param biome the biome to remove
*/
public void removeBiome(@NotNull Biome biome) {
var id = idMappings.get(biome.namespace());
if (id != null) {
biomes.remove(id);
biomesByName.remove(biome.namespace());
idMappings.remove(biome.namespace());
nbtCache = null;
}
}
//todo supporting remove is probably challenging at best since we no longer send ids explicitly, so you cannot skip an ID.
// /**
// * Removes a biome. This does NOT send the new list to players.
// *
// * @param biome the biome to remove
// */
// public void removeBiome(@NotNull Biome biome) {
// var id = idMappings.get(biome.namespace());
// if (id != null) {
// biomes.remove(id.intValue());
// biomesByName.remove(biome.namespace());
// idMappings.remove(biome.namespace());
// registryDataPacket.invalidate();
// }
// }
/**
* Returns an immutable copy of the biomes already registered.
@ -76,7 +79,7 @@ public final class BiomeManager {
* @return an immutable copy of the biomes already registered
*/
public Collection<Biome> unmodifiableCollection() {
return Collections.unmodifiableCollection(biomes.values());
return Collections.unmodifiableCollection(biomes);
}
/**
@ -101,24 +104,6 @@ public final class BiomeManager {
return getByName(namespace);
}
public @NotNull CompoundBinaryTag toNBT() {
if (nbtCache != null) return nbtCache;
ListBinaryTag.Builder<CompoundBinaryTag> entries = ListBinaryTag.builder(BinaryTagTypes.COMPOUND);
for (Biome biome : biomes.values()) {
entries.add(CompoundBinaryTag.builder()
.putInt("id", getId(biome))
.putString("name", biome.namespace().toString())
.put("element", biome.toNbt())
.build());
}
nbtCache = CompoundBinaryTag.builder()
.putString("type", "minecraft:worldgen/biome")
.put("value", entries.build())
.build();
return nbtCache;
}
/**
* Gets the id of a biome.
*`
@ -128,4 +113,32 @@ public final class BiomeManager {
public int getId(Biome biome) {
return idMappings.getOrDefault(biome.namespace(), -1);
}
//
// public @NotNull NBTCompound toNBT() {
// if (nbtCache != null) return nbtCache;
// nbtCache = NBT.Compound(Map.of(
// "type", NBT.String("minecraft:worldgen/biome"),
// "value", NBT.List(NBTType.TAG_Compound, biomes.values().stream().map(biome -> {
// return NBT.Compound(Map.of(
// "id", NBT.Int(getId(biome)),
// "name", NBT.String(biome.namespace().toString()),
// "element", biome.toNbt()
// ));
// }).toList())));
//
// return nbtCache;
// }
public @NotNull SendablePacket registryDataPacket() {
return registryDataPacket;
}
private @NotNull RegistryDataPacket createRegistryDataPacket() {
return new RegistryDataPacket(
"minecraft:worldgen/biome",
biomes.stream()
.map(Biome::toRegistryEntry)
.toList()
);
}
}

View File

@ -1,10 +1,10 @@
package net.minestom.server.entity;
import net.kyori.adventure.text.Component;
import net.minestom.testing.Env;
import net.minestom.testing.EnvTest;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.network.packet.server.play.EntityMetaDataPacket;
import net.minestom.testing.Env;
import net.minestom.testing.EnvTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@ -105,7 +105,7 @@ public class EntityMetaIntegrationTest {
//This is first test, and it is not related to "custom name" bug. Therefore, it should work.
var packets = incomingPackets.collect();
validMetaDataPackets(packets, entity.getEntityId(), entry -> {
if (entry.type() != Metadata.TYPE_OPTCHAT) return;
if (entry.type() != Metadata.TYPE_OPT_CHAT) return;
assertEquals(Component.text("Custom Name"), entry.value());
});
@ -127,7 +127,7 @@ public class EntityMetaIntegrationTest {
//Listen packets to check if entity name is "Custom Name 2".
packets = incomingPackets.collect();
validMetaDataPackets(packets, entity.getEntityId(), entry -> {
if (entry.type() != Metadata.TYPE_OPTCHAT) return;
if (entry.type() != Metadata.TYPE_OPT_CHAT) return;
assertEquals(Component.text("Custom Name 2"), entry.value());
});
}

View File

@ -23,7 +23,6 @@ import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import static net.minestom.server.entity.Player.*;
import static org.junit.jupiter.api.Assertions.*;
@EnvTest
@ -119,7 +118,7 @@ public class PlayerIntegrationTest {
final var packets = List.of(
JoinGamePacket.class, ServerDifficultyPacket.class, SpawnPositionPacket.class,
DeclareCommandsPacket.class, EntityPropertiesPacket.class, EntityStatusPacket.class,
DeclareCommandsPacket.class, EntityAttributesPacket.class, EntityStatusPacket.class,
UpdateHealthPacket.class, PlayerAbilitiesPacket.class
);
final List<Collector<?>> trackers = new ArrayList<>();

View File

@ -1,13 +1,10 @@
package net.minestom.server.item;
import net.minestom.server.attribute.Attribute;
import net.minestom.server.attribute.AttributeOperation;
import net.minestom.server.entity.attribute.AttributeOperation;
import net.minestom.server.item.attribute.AttributeSlot;
import net.minestom.server.item.attribute.ItemAttribute;
import net.minestom.server.tag.TagHandler;
import net.minestom.server.tag.TagWritable;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.junit.jupiter.api.Test;
import java.util.List;

View File

@ -1,12 +1,8 @@
package net.minestom.server.network;
import com.google.gson.JsonObject;
import java.io.PrintStream;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component;
import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.EquipmentSlot;
import net.minestom.server.entity.GameMode;
@ -140,7 +136,7 @@ public class PacketWriteReadTest {
SERVER_PACKETS.add(new EntityMetaDataPacket(5, Map.of(1, Metadata.VarInt(5))));
SERVER_PACKETS.add(new EntityPositionAndRotationPacket(5, (short) 0, (short) 0, (short) 0, 45f, 45f, false));
SERVER_PACKETS.add(new EntityPositionPacket(5, (short) 0, (short) 0, (short) 0, true));
SERVER_PACKETS.add(new EntityPropertiesPacket(5, List.of()));
SERVER_PACKETS.add(new EntityAttributesPacket(5, List.of()));
SERVER_PACKETS.add(new EntityRotationPacket(5, 45f, 45f, false));
final PlayerSkin skin = new PlayerSkin("hh", "hh");