diff --git a/code-generators/src/main/java/net/minestom/codegen/Generators.java b/code-generators/src/main/java/net/minestom/codegen/Generators.java index d54f01540c5..048e7b8d265 100644 --- a/code-generators/src/main/java/net/minestom/codegen/Generators.java +++ b/code-generators/src/main/java/net/minestom/codegen/Generators.java @@ -1,6 +1,5 @@ package net.minestom.codegen; -import net.minestom.codegen.attribute.AttributeGenerator; import net.minestom.codegen.color.DyeColorGenerator; import net.minestom.codegen.feature.FeatureFlagGenerator; import net.minestom.codegen.fluid.FluidGenerator; @@ -41,15 +40,11 @@ public static void main(String[] args) { generator.generate(resource("trim_materials.json"), "net.minestom.server.item.armor", "TrimMaterial", "TrimMaterialImpl", "TrimMaterials"); generator.generate(resource("trim_patterns.json"), "net.minestom.server.item.armor", "TrimPattern", "TrimPatternImpl", "TrimPatterns"); generator.generate(resource("banner_patterns.json"), "net.minestom.server.item.banner", "BannerPattern", "BannerPatternImpl", "BannerPatterns"); // Microtus - Banner and Shield Meta + generator.generate(resource("attributes.json"), "net.minestom.server.attribute", "Attribute", "AttributeImpl", "Attributes"); // Generate fluids new FluidGenerator(resource("fluids.json"), outputFolder).generate(); - - new AttributeGenerator( - resource("attributes.json"), - outputFolder - ).generate(); // TODO: Generate villager professions // new VillagerProfessionGenerator( // new File(inputFolder, targetVersion + "_villager_professions.json"), diff --git a/code-generators/src/main/java/net/minestom/codegen/attribute/AttributeGenerator.java b/code-generators/src/main/java/net/minestom/codegen/attribute/AttributeGenerator.java deleted file mode 100644 index 2bd0ec47837..00000000000 --- a/code-generators/src/main/java/net/minestom/codegen/attribute/AttributeGenerator.java +++ /dev/null @@ -1,127 +0,0 @@ -package net.minestom.codegen.attribute; - -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.HashMap; -import java.util.List; -import java.util.Map; - -import static net.minestom.codegen.util.GenerationHelper.VARIABLE_SETTER; - -public final class AttributeGenerator extends MinestomCodeGenerator { - private static final Logger LOGGER = LoggerFactory.getLogger(AttributeGenerator.class); - private static final String MAX_VALUE = "maxValue"; - private static final String DEFAULT_VALUE = "defaultValue"; - private static final String LITERAL_RETURN = "return this.$L"; - private static final String ATTRIBUTE_KEY = "attribute"; - private final InputStream attributesFile; - private final File outputFolder; - - public AttributeGenerator(@Nullable InputStream attributesFile, @NotNull File outputFolder) { - super("net.minestom.server.attribute"); - 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; - } - - JsonObject attributes = GSON.fromJson(new InputStreamReader(attributesFile), JsonObject.class); - List filesToWrite = new ArrayList<>(); - - ClassName attributeClassName = ClassName.get(packageName, "Attributes"); - ClassName className = ClassName.get(packageName, "Attribute"); - ClassName minecraftServerCn = ClassName.get("net.minestom.server", "MinecraftServer"); - // Attribute - TypeSpec.Builder attributeClass = TypeSpec.enumBuilder(attributeClassName) - .addModifiers(Modifier.PUBLIC).addJavadoc("AUTOGENERATED by " + getClass().getSimpleName()); - - // Fields - attributeClass.addFields( - List.of( - FieldSpec.builder(className, ATTRIBUTE_KEY, Modifier.PRIVATE, Modifier.FINAL).build(), - FieldSpec.builder(ArrayTypeName.of(attributeClassName), "VALUES", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL).initializer("values()").build() // Microtus - Banner and shield meta - ) - ); - - attributeClass.addMethods( - List.of( - // Constructor - MethodSpec.constructorBuilder() - .addParameter( - ParameterSpec.builder(className, ATTRIBUTE_KEY).addAnnotation(NotNull.class).build() - ) - .addStatement(VARIABLE_SETTER, ATTRIBUTE_KEY) - .build(), - MethodSpec.methodBuilder(ATTRIBUTE_KEY) - .addModifiers(Modifier.PUBLIC) - .returns(className.annotated(AnnotationSpec.builder(NotNull.class).build())) - .addStatement(LITERAL_RETURN, ATTRIBUTE_KEY) - .build(), - MethodSpec.methodBuilder("getValue") - .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .addParameter(ParameterSpec.builder(TypeName.INT, "id").build()) - .addAnnotation(Nullable.class) - .returns(className) - .addCode("return VALUES[$L].$L;", "id", ATTRIBUTE_KEY) - .build(), - MethodSpec.methodBuilder("registerAttributes") - .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .beginControlFlow("for (var $L : VALUES)", "v") - .addStatement("$1T.getAttributeManager().register($2L.$3L())", minecraftServerCn, "v", ATTRIBUTE_KEY) - .endControlFlow() - .build() - ) - ); - - Map extractData = new HashMap<>(); - extractData.put("minecraft:", ""); - extractData.put(".", "_"); - - // Use data - for (String key : attributes.keySet()) { - JsonObject attribute = attributes.getAsJsonObject(key); - String attributeName = extractNamespaces(key, extractData); - JsonObject range = attribute.getAsJsonObject("range"); - attributeClass.addEnumConstant(attributeName, TypeSpec.anonymousClassBuilder( - "new $T($S, $Lf, $Lf)", - className, key, attribute.get(DEFAULT_VALUE).getAsFloat(), range.get(MAX_VALUE).getAsFloat() - ).build() - ); - } - - - filesToWrite.add( - JavaFile.builder(packageName, attributeClass.build()) - .indent(DEFAULT_INDENT) - .skipJavaLangImports(true) - .build() - ); - - // Write files to outputFolder - writeFiles( - filesToWrite, - outputFolder - ); - } -} diff --git a/demo/src/main/java/net/minestom/demo/entity/ChickenCreature.java b/demo/src/main/java/net/minestom/demo/entity/ChickenCreature.java index 328a9d855e8..27a3d6be82b 100644 --- a/demo/src/main/java/net/minestom/demo/entity/ChickenCreature.java +++ b/demo/src/main/java/net/minestom/demo/entity/ChickenCreature.java @@ -1,7 +1,6 @@ package net.minestom.demo.entity; -import net.minestom.server.attribute.Attribute; -import net.minestom.server.attribute.Attributes; +import net.minestom.server.attribute.VanillaAttribute; import net.minestom.server.entity.EntityCreature; import net.minestom.server.entity.EntityType; import net.minestom.server.entity.ai.goal.RandomStrollGoal; @@ -36,7 +35,7 @@ public ChickenCreature() { // .build() // ); - getAttribute(Attributes.GENERIC_MOVEMENT_SPEED.attribute()).setBaseValue(0.1f); + getAttribute(VanillaAttribute.GENERIC_MOVEMENT_SPEED).setBaseValue(0.1f); } @Override diff --git a/src/autogenerated/java/net/minestom/server/attribute/Attributes.java b/src/autogenerated/java/net/minestom/server/attribute/Attributes.java index a5edd76b551..b0e0b870ac7 100644 --- a/src/autogenerated/java/net/minestom/server/attribute/Attributes.java +++ b/src/autogenerated/java/net/minestom/server/attribute/Attributes.java @@ -1,61 +1,35 @@ package net.minestom.server.attribute; -import net.minestom.server.MinecraftServer; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - /** - * AUTOGENERATED by AttributeGenerator + * Code autogenerated, do not edit! */ -public enum Attributes { - GENERIC_ARMOR(new Attribute("minecraft:generic.armor", 0.0f, 30.0f)), - - GENERIC_ARMOR_TOUGHNESS(new Attribute("minecraft:generic.armor_toughness", 0.0f, 20.0f)), - - GENERIC_ATTACK_DAMAGE(new Attribute("minecraft:generic.attack_damage", 2.0f, 2048.0f)), - - GENERIC_ATTACK_KNOCKBACK(new Attribute("minecraft:generic.attack_knockback", 0.0f, 5.0f)), - - GENERIC_ATTACK_SPEED(new Attribute("minecraft:generic.attack_speed", 4.0f, 1024.0f)), - - GENERIC_FLYING_SPEED(new Attribute("minecraft:generic.flying_speed", 0.4f, 1024.0f)), - - GENERIC_FOLLOW_RANGE(new Attribute("minecraft:generic.follow_range", 32.0f, 2048.0f)), +@SuppressWarnings("unused") +interface Attributes { + Attribute GENERIC_ARMOR = AttributeImpl.get("minecraft:generic.armor"); - HORSE_JUMP_STRENGTH(new Attribute("minecraft:horse.jump_strength", 0.7f, 2.0f)), + Attribute GENERIC_ARMOR_TOUGHNESS = AttributeImpl.get("minecraft:generic.armor_toughness"); - GENERIC_KNOCKBACK_RESISTANCE(new Attribute("minecraft:generic.knockback_resistance", 0.0f, 1.0f)), + Attribute GENERIC_ATTACK_DAMAGE = AttributeImpl.get("minecraft:generic.attack_damage"); - GENERIC_LUCK(new Attribute("minecraft:generic.luck", 0.0f, 1024.0f)), + Attribute GENERIC_ATTACK_KNOCKBACK = AttributeImpl.get("minecraft:generic.attack_knockback"); - GENERIC_MAX_ABSORPTION(new Attribute("minecraft:generic.max_absorption", 0.0f, 2048.0f)), + Attribute GENERIC_ATTACK_SPEED = AttributeImpl.get("minecraft:generic.attack_speed"); - GENERIC_MAX_HEALTH(new Attribute("minecraft:generic.max_health", 20.0f, 1024.0f)), + Attribute GENERIC_FLYING_SPEED = AttributeImpl.get("minecraft:generic.flying_speed"); - GENERIC_MOVEMENT_SPEED(new Attribute("minecraft:generic.movement_speed", 0.7f, 1024.0f)), + Attribute GENERIC_FOLLOW_RANGE = AttributeImpl.get("minecraft:generic.follow_range"); - ZOMBIE_SPAWN_REINFORCEMENTS(new Attribute("minecraft:zombie.spawn_reinforcements", 0.0f, 1.0f)); + Attribute HORSE_JUMP_STRENGTH = AttributeImpl.get("minecraft:horse.jump_strength"); - private static final Attributes[] VALUES = values(); + Attribute GENERIC_KNOCKBACK_RESISTANCE = AttributeImpl.get("minecraft:generic.knockback_resistance"); - private final Attribute attribute; + Attribute GENERIC_LUCK = AttributeImpl.get("minecraft:generic.luck"); - Attributes(@NotNull Attribute attribute) { - this.attribute = attribute; - } + Attribute GENERIC_MAX_ABSORPTION = AttributeImpl.get("minecraft:generic.max_absorption"); - public @NotNull Attribute attribute() { - return this.attribute; - } + Attribute GENERIC_MAX_HEALTH = AttributeImpl.get("minecraft:generic.max_health"); - @Nullable - public static Attribute getValue(int id) { - return VALUES[id].attribute; - } + Attribute GENERIC_MOVEMENT_SPEED = AttributeImpl.get("minecraft:generic.movement_speed"); - public static void registerAttributes() { - for (var v : VALUES) { - MinecraftServer.getAttributeManager().register(v.attribute()); - } - } + Attribute ZOMBIE_SPAWN_REINFORCEMENTS = AttributeImpl.get("minecraft:zombie.spawn_reinforcements"); } diff --git a/src/main/java/net/minestom/server/ServerProcessImpl.java b/src/main/java/net/minestom/server/ServerProcessImpl.java index 4c1a9de9c88..5eff5ac8daf 100644 --- a/src/main/java/net/minestom/server/ServerProcessImpl.java +++ b/src/main/java/net/minestom/server/ServerProcessImpl.java @@ -10,7 +10,6 @@ import net.minestom.server.advancements.AdvancementManager; import net.minestom.server.adventure.bossbar.BossBarManager; import net.minestom.server.attribute.AttributeManager; -import net.minestom.server.attribute.Attributes; import net.minestom.server.command.CommandManager; import net.minestom.server.entity.Entity; import net.minestom.server.event.EventDispatcher; @@ -258,9 +257,9 @@ public void start(@NotNull SocketAddress socketAddress) { LOGGER.info(MinecraftServer.getBrandName() + " server started successfully."); if (ServerFlag.ATTRIBUTES_ENABLED) { - Attributes.registerAttributes(); + attribute.loadVanillaAttributes(); } - LOGGER.info("Register Attributes({})", attribute.values().size()); + LOGGER.info("Register Attributes({})", attribute.unmodifiableCollection().size()); if (ServerFlag.BIOMES_ENABLED) { biome.loadVanillaBiomes(); diff --git a/src/main/java/net/minestom/server/attribute/Attribute.java b/src/main/java/net/minestom/server/attribute/Attribute.java index 0b1a3ceb6e6..81982b81a8d 100644 --- a/src/main/java/net/minestom/server/attribute/Attribute.java +++ b/src/main/java/net/minestom/server/attribute/Attribute.java @@ -1,13 +1,25 @@ package net.minestom.server.attribute; -/** - * Represents a {@link net.minestom.server.entity.LivingEntity living entity} attribute. - */ -public record Attribute(String key, float defaultValue, float maxValue) { - - public Attribute { - if (defaultValue > maxValue) { - throw new IllegalArgumentException("Default value cannot be greater than the maximum allowed"); - } - } +import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.Registry; +import net.minestom.server.utils.NamespaceID; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public sealed interface Attribute extends ProtocolObject permits AttributeImpl { + @Contract(pure = true) + @Nullable + Registry.AttributeEntry registry(); + + @Override + @NotNull + NamespaceID namespace(); + + float defaultValue(); + float maxValue(); + float mineValue(); + boolean clientSync(); + @NotNull + String translationKey(); } diff --git a/src/main/java/net/minestom/server/attribute/AttributeImpl.java b/src/main/java/net/minestom/server/attribute/AttributeImpl.java new file mode 100644 index 00000000000..791862c8479 --- /dev/null +++ b/src/main/java/net/minestom/server/attribute/AttributeImpl.java @@ -0,0 +1,64 @@ +package net.minestom.server.attribute; + +import net.minestom.server.registry.ProtocolObject; +import net.minestom.server.registry.Registry; +import net.minestom.server.utils.NamespaceID; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +/** + * Represents a {@link net.minestom.server.entity.LivingEntity living entity} attribute. + */ +public record AttributeImpl(Registry.AttributeEntry registry, int id) implements Attribute, ProtocolObject { + + private static final Registry.DynamicContainer CONTAINER = Registry.createDynamicContainer(Registry.Resource.ATTRIBUTES, AttributeImpl::createImpl); + + private static Attribute createImpl(String namespace, Registry.Properties properties) { + Registry.AttributeEntry attributeEntry = Registry.attribute(namespace, properties); + return new AttributeImpl(attributeEntry, attributeEntry.id()); + } + + static Collection values() { + return CONTAINER.values(); + } + + static Attribute get(@NotNull String namespace) { + return CONTAINER.get(namespace); + } + + static Attribute getSafe(@NotNull String namespace) { + return CONTAINER.getSafe(namespace); + } + + @Override + public @NotNull NamespaceID namespace() { + return this.registry.namespace(); + } + + @Override + public float defaultValue() { + return this.registry.defaultValue(); + } + + @Override + public float maxValue() { + return this.registry.maxValue(); + } + + @Override + public float mineValue() { + return this.registry.maxValue(); + } + + @Override + public boolean clientSync() { + return this.registry.clientSync(); + } + + @Override + public @NotNull String translationKey() { + return this.registry.translationKey(); + } +} diff --git a/src/main/java/net/minestom/server/attribute/AttributeManager.java b/src/main/java/net/minestom/server/attribute/AttributeManager.java index e9bdc070c87..703194cb476 100644 --- a/src/main/java/net/minestom/server/attribute/AttributeManager.java +++ b/src/main/java/net/minestom/server/attribute/AttributeManager.java @@ -1,43 +1,89 @@ package net.minestom.server.attribute; -import java.util.Collection; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - +import net.minestom.server.utils.NamespaceID; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + public final class AttributeManager { - private final Map attributes = new ConcurrentHashMap<>(); + private final List attributes = new ArrayList<>(); + private final Map attributesByName = new ConcurrentHashMap<>(); + private final Map idMappings = new ConcurrentHashMap<>(); + /** - * Register this attribute. + * Adds a new Attribute. This does NOT send the new list to players. * - * @see #fromKey(String) - * @see #values() + * @param attribute the attribute to add */ public synchronized void register(Attribute attribute) { - attributes.put(attribute.key(), attribute); + this.attributesByName.put(attribute.namespace(), attribute); + this.idMappings.put(attribute.namespace(), attribute.registry().id()); + this.attributes.add(attribute); + } + + public void loadVanillaAttributes() { + for (Attribute attribute : AttributeImpl.values()) { + if (getByName(attribute.namespace()) == null) + register(attribute); + } } /** - * Retrieves an attribute by its key. + * Removes a attribute. This does NOT send the new list to players. * - * @param key the key of the attribute - * @return the attribute for the key or null if not any + * @param attribute the attribute to remove */ - public synchronized @Nullable Attribute fromKey(@NotNull String key) { - return attributes.get(key); + public void removeBiome(@NotNull Attribute attribute) { + var id = idMappings.get(attribute.namespace()); + if (id != null) { + attributes.remove(id); + attributesByName.remove(attribute.namespace()); + idMappings.remove(attribute.namespace()); + } } /** - * Retrieves all registered attributes. + * Returns an immutable copy of the attribute already registered. * - * @return an array containing all registered attributes + * @return an immutable copy of the attributes already registered + */ + public synchronized Collection unmodifiableCollection() { + return Collections.unmodifiableCollection(attributes); + } + + /** + * Gets a attribute by its id. + * + * @param id the id of the attribute + * @return the {@link Attribute} linked to this id + */ + @Nullable + public synchronized Attribute getById(int id) { + return attributes.get(id); + } + + @Nullable + public Attribute getByName(@NotNull NamespaceID namespaceID) { + return attributesByName.get(namespaceID); + } + + @Nullable + public Attribute getByName(@NotNull String namespaceID) { + NamespaceID namespace = NamespaceID.from(namespaceID); + return getByName(namespace); + } + + /** + * Gets the id of a attribute. + *` + * @param attribute + * @return the id of the attribute, or -1 if the attribute is not registered */ - public synchronized @NotNull Collection<@NotNull Attribute> values() { - return attributes.values(); + public int getId(Attribute attribute) { + return idMappings.getOrDefault(attribute.namespace(), -1); } } diff --git a/src/main/java/net/minestom/server/attribute/VanillaAttribute.java b/src/main/java/net/minestom/server/attribute/VanillaAttribute.java new file mode 100644 index 00000000000..69868b9115b --- /dev/null +++ b/src/main/java/net/minestom/server/attribute/VanillaAttribute.java @@ -0,0 +1,4 @@ +package net.minestom.server.attribute; + +public final class VanillaAttribute implements Attributes { +} diff --git a/src/main/java/net/minestom/server/entity/LivingEntity.java b/src/main/java/net/minestom/server/entity/LivingEntity.java index 459edc248d9..251ec7d3d23 100644 --- a/src/main/java/net/minestom/server/entity/LivingEntity.java +++ b/src/main/java/net/minestom/server/entity/LivingEntity.java @@ -1,9 +1,7 @@ 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.attribute.Attributes; +import net.minestom.server.attribute.*; import net.minestom.server.collision.BoundingBox; import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Vec; @@ -28,6 +26,7 @@ import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.scoreboard.Team; import net.minestom.server.sound.SoundEvent; +import net.minestom.server.utils.NamespaceID; import net.minestom.server.utils.block.BlockIterator; import net.minestom.server.utils.time.Cooldown; import net.minestom.server.utils.time.TimeUnit; @@ -55,7 +54,7 @@ public class LivingEntity extends Entity implements EquipmentHandler { // Bounding box used for items' pickup (see LivingEntity#setBoundingBox) protected BoundingBox expandedBoundingBox; - private final Map attributeModifiers = new ConcurrentHashMap<>(); + private final Map attributeModifiers = new ConcurrentHashMap<>(); // Abilities protected boolean invulnerable; @@ -420,21 +419,21 @@ public void setHealth(float health) { } /** - * Gets the entity max health from {@link #getAttributeValue(Attribute)} {@link Attributes#GENERIC_MAX_HEALTH}. + * Gets the entity max health from {@link #getAttributeValue(Attribute)} {@link VanillaAttribute#GENERIC_MAX_HEALTH}. * * @return the entity max health */ public float getMaxHealth() { - return getAttributeValue(Attributes.GENERIC_MAX_HEALTH.attribute()); + return getAttributeValue(VanillaAttribute.GENERIC_MAX_HEALTH); } /** * Sets the heal of the entity as its max health. *

- * Retrieved from {@link #getAttributeValue(Attribute)} with the attribute {@link Attributes#GENERIC_MAX_HEALTH}. + * Retrieved from {@link #getAttributeValue(Attribute)} with the attribute {@link VanillaAttribute#GENERIC_MAX_HEALTH}. */ public void heal() { - setHealth(getAttributeValue(Attributes.GENERIC_MAX_HEALTH.attribute())); + setHealth(getAttributeValue(VanillaAttribute.GENERIC_MAX_HEALTH)); } /** @@ -444,7 +443,7 @@ public void heal() { * @return the attribute instance */ public @NotNull AttributeInstance getAttribute(@NotNull Attribute attribute) { - return attributeModifiers.computeIfAbsent(attribute.key(), + return attributeModifiers.computeIfAbsent(attribute.namespace(), s -> new AttributeInstance(attribute, this::onAttributeChanged)); } @@ -475,7 +474,7 @@ protected void onAttributeChanged(@NotNull AttributeInstance attributeInstance) * @return the attribute value */ public float getAttributeValue(@NotNull Attribute attribute) { - AttributeInstance instance = attributeModifiers.get(attribute.key()); + AttributeInstance instance = attributeModifiers.get(attribute.namespace()); return (instance != null) ? instance.getValue() : attribute.defaultValue(); } @@ -668,7 +667,7 @@ public void setTeam(@Nullable Team team) { */ @Override public void takeKnockback(float strength, final double x, final double z) { - strength *= 1 - getAttributeValue(Attributes.GENERIC_KNOCKBACK_RESISTANCE.attribute()); + strength *= 1 - getAttributeValue(VanillaAttribute.GENERIC_KNOCKBACK_RESISTANCE); super.takeKnockback(strength, x, z); } } diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 4f780ce9e40..4bb77627151 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -25,7 +25,7 @@ import net.minestom.server.adventure.AdventurePacketConvertor; import net.minestom.server.adventure.Localizable; import net.minestom.server.adventure.audience.Audiences; -import net.minestom.server.attribute.Attributes; +import net.minestom.server.attribute.VanillaAttribute; import net.minestom.server.collision.BoundingBox; import net.minestom.server.command.CommandSender; import net.minestom.server.coordinate.Point; @@ -249,7 +249,7 @@ public Player(@NotNull UUID uuid, @NotNull String username, @NotNull PlayerConne this.gameMode = GameMode.SURVIVAL; this.dimensionType = DimensionType.OVERWORLD; // Default dimension this.levelFlat = true; - getAttribute(Attributes.GENERIC_MOVEMENT_SPEED.attribute()).setBaseValue(0.1f); + getAttribute(VanillaAttribute.GENERIC_MOVEMENT_SPEED).setBaseValue(0.1f); // FakePlayer init its connection there playerConnectionInit(); diff --git a/src/main/java/net/minestom/server/entity/pathfinding/PFPathingEntity.java b/src/main/java/net/minestom/server/entity/pathfinding/PFPathingEntity.java index 7fc994cd76b..f3a0450c292 100644 --- a/src/main/java/net/minestom/server/entity/pathfinding/PFPathingEntity.java +++ b/src/main/java/net/minestom/server/entity/pathfinding/PFPathingEntity.java @@ -5,7 +5,8 @@ import com.extollit.gaming.ai.path.model.Passibility; import com.extollit.linalg.immutable.Vec3d; import net.minestom.server.attribute.Attribute; -import net.minestom.server.attribute.Attributes; +import net.minestom.server.attribute.AttributeImpl; +import net.minestom.server.attribute.VanillaAttribute; import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.Entity; @@ -35,7 +36,7 @@ public PFPathingEntity(Navigator navigator) { this.navigator = navigator; this.entity = navigator.getEntity(); - this.searchRange = getAttributeValue(Attributes.GENERIC_FOLLOW_RANGE.attribute()); + this.searchRange = getAttributeValue(VanillaAttribute.GENERIC_FOLLOW_RANGE); } @Override @@ -139,7 +140,7 @@ public Capabilities capabilities() { return new Capabilities() { @Override public float speed() { - return getAttributeValue(Attributes.GENERIC_MOVEMENT_SPEED.attribute()); + return getAttributeValue(VanillaAttribute.GENERIC_MOVEMENT_SPEED); } @Override @@ -192,7 +193,7 @@ public boolean opensDoors() { @Override public void moveTo(Vec3d position, Passibility passibility, Gravitation gravitation) { final Point targetPosition = new Vec(position.x, position.y, position.z); - this.navigator.moveTowards(targetPosition, getAttributeValue(Attributes.GENERIC_MOVEMENT_SPEED.attribute())); + this.navigator.moveTowards(targetPosition, getAttributeValue(VanillaAttribute.GENERIC_MOVEMENT_SPEED)); final double entityY = entity.getPosition().y() + 0.00001D; // After any negative y movement, entities will always be extremely // slightly below floor level. This +0.00001D is here to offset this // error and stop the entity from permanently jumping. diff --git a/src/main/java/net/minestom/server/item/ItemSerializers.java b/src/main/java/net/minestom/server/item/ItemSerializers.java index 00d79d29790..06640681948 100644 --- a/src/main/java/net/minestom/server/item/ItemSerializers.java +++ b/src/main/java/net/minestom/server/item/ItemSerializers.java @@ -2,6 +2,7 @@ import net.minestom.server.MinecraftServer; import net.minestom.server.attribute.Attribute; +import net.minestom.server.attribute.AttributeImpl; import net.minestom.server.attribute.AttributeOperation; import net.minestom.server.item.attribute.AttributeSlot; import net.minestom.server.item.attribute.ItemAttribute; @@ -58,7 +59,7 @@ public record EnchantmentEntry(Enchantment enchantment, short level) { final int operation = reader.getTag(OPERATION); final String name = reader.getTag(NAME); - final Attribute attribute = MinecraftServer.getAttributeManager().fromKey(attributeName.toLowerCase(Locale.ROOT)); + final Attribute attribute = MinecraftServer.getAttributeManager().getByName(attributeName.toLowerCase(Locale.ROOT)); // Wrong attribute name, stop here if (attribute == null) return null; final AttributeOperation attributeOperation = AttributeOperation.fromId(operation); @@ -80,7 +81,7 @@ public void write(@NotNull TagWritable writer, @NotNull ItemAttribute value) { writer.setTag(ID, value.uuid()); writer.setTag(AMOUNT, value.amount()); writer.setTag(SLOT, value.slot().name().toLowerCase(Locale.ROOT)); - writer.setTag(ATTRIBUTE_NAME, value.attribute().key()); + writer.setTag(ATTRIBUTE_NAME, value.attribute().namespace().toString()); writer.setTag(OPERATION, value.operation().getId()); writer.setTag(NAME, value.name()); } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/EntityPropertiesPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/EntityPropertiesPacket.java index 7ae754bc3d0..9cc703dd46b 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/EntityPropertiesPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/EntityPropertiesPacket.java @@ -1,10 +1,7 @@ package net.minestom.server.network.packet.server.play; import net.minestom.server.MinecraftServer; -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.attribute.*; import net.minestom.server.network.NetworkBuffer; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; @@ -24,7 +21,7 @@ public record EntityPropertiesPacket(int entityId, List prope public EntityPropertiesPacket(@NotNull NetworkBuffer reader) { this(reader.read(VAR_INT), reader.readCollection(r -> { - final Attribute attribute = MinecraftServer.getAttributeManager().fromKey(reader.read(STRING)); + final Attribute attribute = MinecraftServer.getAttributeManager().getByName(reader.read(STRING)); final double value = reader.read(DOUBLE); int modifierCount = reader.read(VAR_INT); AttributeInstance instance = new AttributeInstance(attribute, null); @@ -43,7 +40,7 @@ public void write(@NotNull NetworkBuffer writer) { for (AttributeInstance instance : properties) { final Attribute attribute = instance.getAttribute(); - writer.write(STRING, attribute.key()); + writer.write(STRING, attribute.namespace().toString()); writer.write(DOUBLE, (double) instance.getBaseValue()); { diff --git a/src/main/java/net/minestom/server/registry/Registry.java b/src/main/java/net/minestom/server/registry/Registry.java index 16be476103e..aaf523c192d 100644 --- a/src/main/java/net/minestom/server/registry/Registry.java +++ b/src/main/java/net/minestom/server/registry/Registry.java @@ -33,6 +33,9 @@ * Use at your own risk. */ public final class Registry { + + private static final String TRANSLATION_KEY = "translationKey"; + @ApiStatus.Internal public static BlockEntry block(String namespace, @NotNull Properties main) { return new BlockEntry(namespace, main, null); @@ -78,6 +81,11 @@ public static TrimPatternEntry trimPattern(String namespace, @NotNull Properties 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> load(Resource resource) { Map> map = new HashMap<>(); @@ -212,6 +220,7 @@ public interface Loader { public enum Resource { BLOCKS("blocks.json"), ITEMS("items.json"), + ATTRIBUTES("attributes.json"), ENTITIES("entities.json"), ENCHANTMENTS("enchantments.json"), SOUNDS("sounds.json"), @@ -238,6 +247,30 @@ public enum Resource { } } + public record AttributeEntry( + @NotNull NamespaceID namespace, + int id, + @NotNull String translationKey, + float defaultValue, + boolean clientSync, + float maxValue, + float minValue, + @Nullable Properties custom + ) implements Entry { + + public AttributeEntry(String namespace, Properties main, Properties custom) { + this(NamespaceID.from(namespace), + main.getInt("id"), + main.getString(TRANSLATION_KEY), + (float) main.getDouble("defaultValue"), + main.getBoolean("clientSync"), + (float) main.getDouble("maxValue"), + (float) main.getDouble("minValue"), + custom + ); + } + } + public static final class BlockEntry implements Entry { private final NamespaceID namespace; private final int id; @@ -487,7 +520,7 @@ private MaterialEntry(String namespace, Properties main, Properties custom) { this.custom = custom; this.namespace = NamespaceID.from(namespace); this.id = main.getInt("id"); - this.translationKey = main.getString("translationKey"); + this.translationKey = main.getString(TRANSLATION_KEY); this.maxStackSize = main.getInt("maxStackSize", 64); this.maxDamage = main.getInt("maxDamage", 0); this.isFood = main.getBoolean("edible", false); @@ -579,7 +612,7 @@ public record EntityEntry(NamespaceID namespace, int id, public EntityEntry(String namespace, Properties main, Properties custom) { this(NamespaceID.from(namespace), main.getInt("id"), - main.getString("translationKey"), + main.getString(TRANSLATION_KEY), main.getDouble("width"), main.getDouble("height"), main.getDouble("drag", 0.02), @@ -660,7 +693,7 @@ public record EnchantmentEntry(NamespaceID namespace, int id, public EnchantmentEntry(String namespace, Properties main, Properties custom) { this(NamespaceID.from(namespace), main.getInt("id"), - main.getString("translationKey"), + main.getString(TRANSLATION_KEY), main.getDouble("maxLevel"), main.getBoolean("curse", false), main.getBoolean("discoverable", true), @@ -678,7 +711,7 @@ public record PotionEffectEntry(NamespaceID namespace, int id, public PotionEffectEntry(String namespace, Properties main, Properties custom) { this(NamespaceID.from(namespace), main.getInt("id"), - main.getString("translationKey"), + main.getString(TRANSLATION_KEY), main.getInt("color"), main.getBoolean("instantaneous"), custom); diff --git a/src/main/java/net/minestom/server/utils/ArrayUtils.java b/src/main/java/net/minestom/server/utils/ArrayUtils.java index fb32053f3e8..729a480442d 100644 --- a/src/main/java/net/minestom/server/utils/ArrayUtils.java +++ b/src/main/java/net/minestom/server/utils/ArrayUtils.java @@ -15,7 +15,7 @@ public final class ArrayUtils { private ArrayUtils() { } - public static boolean isEmpty(@Nullable Object @NotNull [] array) { + public static boolean isEmpty(@Nullable Object[] array) { for (Object object : array) { if (object != null) return false; } diff --git a/src/test/java/net/minestom/server/item/ItemAttributeTest.java b/src/test/java/net/minestom/server/item/ItemAttributeTest.java index fc28ae39324..61d3424ce86 100644 --- a/src/test/java/net/minestom/server/item/ItemAttributeTest.java +++ b/src/test/java/net/minestom/server/item/ItemAttributeTest.java @@ -1,7 +1,7 @@ package net.minestom.server.item; import net.minestom.server.attribute.AttributeOperation; -import net.minestom.server.attribute.Attributes; +import net.minestom.server.attribute.VanillaAttribute; import net.minestom.server.item.attribute.AttributeSlot; import net.minestom.server.item.attribute.ItemAttribute; import net.minestom.server.tag.TagHandler; @@ -21,7 +21,7 @@ class ItemAttributeTest { @Test void attribute(Env env) { var attributes = List.of(new ItemAttribute( - new UUID(0, 0), "generic.attack_damage", Attributes.GENERIC_ATTACK_DAMAGE.attribute(), + new UUID(0, 0), "generic.attack_damage", VanillaAttribute.GENERIC_ATTACK_DAMAGE, AttributeOperation.ADDITION, 2, AttributeSlot.MAINHAND)); var item = ItemStack.builder(Material.STICK) .meta(builder -> builder.attributes(attributes)) @@ -31,9 +31,8 @@ void attribute(Env env) { @Test void attributeReader(Env env) { - Attributes.registerAttributes(); var attributes = List.of(new ItemAttribute( - new UUID(0, 0), "generic.attack_damage", Attributes.GENERIC_ATTACK_DAMAGE.attribute(), + new UUID(0, 0), "generic.attack_damage", VanillaAttribute.GENERIC_ATTACK_DAMAGE, AttributeOperation.ADDITION, 2, AttributeSlot.MAINHAND)); TagHandler handler = TagHandler.newHandler(); @@ -48,7 +47,7 @@ void attributeNbt(Env env) { var item = ItemStack.builder(Material.STICK) .meta(builder -> builder.attributes( List.of(new ItemAttribute( - new UUID(0, 0), "generic.attack_damage", Attributes.GENERIC_ATTACK_DAMAGE.attribute(), + new UUID(0, 0), "generic.attack_damage", VanillaAttribute.GENERIC_ATTACK_DAMAGE, AttributeOperation.ADDITION, 2, AttributeSlot.MAINHAND)))) .build(); assertEqualsSNBT("""