From 960ca5dac65b1e7449677ee8ed264ad65f14c66f Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Thu, 14 Aug 2025 23:14:49 +0100 Subject: [PATCH 1/7] Initial format updates & modern name usage --- .../entity/EntityAttributeModifiers.java | 101 ++++++------- .../item/ItemAttributeModifiers.java | 136 ++++++++++++++---- 2 files changed, 151 insertions(+), 86 deletions(-) diff --git a/plugin/src/main/java/com/denizenscript/denizen/objects/properties/entity/EntityAttributeModifiers.java b/plugin/src/main/java/com/denizenscript/denizen/objects/properties/entity/EntityAttributeModifiers.java index 79f932d690..626fae7c51 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/objects/properties/entity/EntityAttributeModifiers.java +++ b/plugin/src/main/java/com/denizenscript/denizen/objects/properties/entity/EntityAttributeModifiers.java @@ -3,6 +3,7 @@ import com.denizenscript.denizen.nms.NMSHandler; import com.denizenscript.denizen.nms.NMSVersion; import com.denizenscript.denizen.objects.EntityTag; +import com.denizenscript.denizen.objects.properties.item.ItemAttributeModifiers; import com.denizenscript.denizen.utilities.BukkitImplDeprecations; import com.denizenscript.denizen.utilities.Utilities; import com.denizenscript.denizencore.objects.Mechanism; @@ -16,7 +17,6 @@ import com.denizenscript.denizencore.tags.core.EscapeTagUtil; import com.denizenscript.denizencore.utilities.CoreUtilities; import com.denizenscript.denizencore.utilities.debugging.Debug; -import com.denizenscript.denizencore.utilities.text.StringHolder; import org.bukkit.NamespacedKey; import org.bukkit.attribute.Attributable; import org.bukkit.attribute.Attribute; @@ -26,8 +26,8 @@ import org.bukkit.inventory.EquipmentSlotGroup; import java.util.ArrayList; +import java.util.Collection; import java.util.List; -import java.util.Map; import java.util.UUID; public class EntityAttributeModifiers implements Property { @@ -74,7 +74,7 @@ public ListTag getAttributes() { for (AttributeModifier modifier : instance.getModifiers()) { modifiers.append("/").append(stringify(modifier)); } - list.add(EscapeTagUtil.escape(attribute.name()) + "/" + instance.getBaseValue() + modifiers); + list.add(EscapeTagUtil.escape(ItemAttributeModifiers.legacyAttributeName(attribute)) + "/" + instance.getBaseValue() + modifiers); } return list; } @@ -90,7 +90,7 @@ public static MapTag mapify(AttributeModifier modifier) { else { result.putObject("slot", new ElementTag(modifier.getSlot() == null ? "any" : modifier.getSlot().name())); } - if (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_21)) { + if (ItemAttributeModifiers.MODERN_ATTRIBUTE_FORMAT) { result.putObject("key", new ElementTag(Utilities.namespacedKeyToString(modifier.getKey()), true)); } // TODO: remove/deprecate the UUID key @@ -159,31 +159,28 @@ public static AttributeModifier parseLegacyModifier(Attribute attr, MapTag map, Debug.echoError("Invalid equipment slot group specified: " + slot); return null; } - return new AttributeModifier(idValue, name == null ? attr.name() : name.asString(), amount, operation, group); + return new AttributeModifier(idValue, name == null ? ItemAttributeModifiers.legacyAttributeName(attr) : name.asString(), amount, operation, group); } - return new AttributeModifier(idValue, name == null ? attr.name() : name.toString(), amount, operation, slotValue); + return new AttributeModifier(idValue, name == null ? ItemAttributeModifiers.legacyAttributeName(attr) : name.toString(), amount, operation, slotValue); } public ListTag getAttributeModifierList(AttributeInstance instance) { if (instance == null) { return null; } - ListTag result = new ListTag(); - for (AttributeModifier modifier : instance.getModifiers()) { - result.addObject(mapify(modifier)); - } - if (result.isEmpty()) { + Collection modifiers = instance.getModifiers(); + if (modifiers.isEmpty()) { return null; } - return result; + return new ListTag(modifiers, EntityAttributeModifiers::mapify); } - public MapTag getAttributeModifiers() { + public MapTag getAttributeModifiers(boolean includeDeprecated) { MapTag map = new MapTag(); for (Attribute attribute : Utilities.listTypesRaw(Attribute.class)) { ListTag list = getAttributeModifierList(getAttributable().getAttribute(attribute)); if (list != null) { - map.putObject(attribute.name(), list); + ItemAttributeModifiers.addAttributeToMap(map, attribute, list, includeDeprecated); } } return map; @@ -195,7 +192,7 @@ public Attributable getAttributable() { @Override public String getPropertyString() { - MapTag map = getAttributeModifiers(); + MapTag map = getAttributeModifiers(false); return map.isEmpty() ? null : map.savable(); } @@ -284,7 +281,7 @@ public static void register() { // See also <@link language attribute modifiers>. // --> PropertyParser.registerTag(EntityAttributeModifiers.class, MapTag.class, "attribute_modifiers", (attribute, object) -> { - return object.getAttributeModifiers(); + return object.getAttributeModifiers(true); }); PropertyParser.registerTag(EntityAttributeModifiers.class, ListTag.class, "attributes", (attribute, object) -> { @@ -315,20 +312,17 @@ public void adjust(Mechanism mechanism) { try { MapTag input = mechanism.valueAsType(MapTag.class); Attributable ent = getAttributable(); - for (Map.Entry subValue : input.entrySet()) { - Attribute attr = Attribute.valueOf(subValue.getKey().str.toUpperCase()); - AttributeInstance instance = ent.getAttribute(attr); + ItemAttributeModifiers.parseAttributeModifiers(input, mechanism, attribute -> { + AttributeInstance instance = ent.getAttribute(attribute); if (instance == null) { - mechanism.echoError("Attribute " + attr.name() + " is not applicable to entity of type " + entity.getBukkitEntityType().name()); - continue; + mechanism.echoError("Attribute " + attribute + " is not applicable to entity of type " + entity.getBukkitEntityType().name()); + return null; } for (AttributeModifier modifier : instance.getModifiers()) { instance.removeModifier(modifier); } - for (ObjectTag listValue : CoreUtilities.objectToList(subValue.getValue(), mechanism.context)) { - instance.addModifier(modiferForMap(attr, listValue.asType(MapTag.class, mechanism.context), mechanism.context)); - } - } + return instance; + }, AttributeInstance::addModifier); } catch (Throwable ex) { Debug.echoError(ex); @@ -353,31 +347,29 @@ public void adjust(Mechanism mechanism) { try { MapTag input = mechanism.valueAsType(MapTag.class); Attributable ent = getAttributable(); - for (Map.Entry subValue : input.entrySet()) { - Attribute attr = Attribute.valueOf(subValue.getKey().str.toUpperCase()); - AttributeInstance instance = ent.getAttribute(attr); + ItemAttributeModifiers.parseAttributeModifiers(input, mechanism, attribute -> { + AttributeInstance instance = ent.getAttribute(attribute); if (instance == null) { - mechanism.echoError("Attribute " + attr.name() + " is not applicable to entity of type " + entity.getBukkitEntityType().name()); - continue; + mechanism.echoError("Attribute " + attribute + " is not applicable to entity of type " + entity.getBukkitEntityType().name()); + return null; + } + return instance; + }, (attributeInstance, modifier) -> { + try { + attributeInstance.addModifier(modifier); } - for (ObjectTag listValue : CoreUtilities.objectToList(subValue.getValue(), mechanism.context)) { - AttributeModifier modifier = modiferForMap(attr, listValue.asType(MapTag.class, mechanism.context), mechanism.context); - try { - instance.addModifier(modifier); + catch (IllegalArgumentException ex) { + if (!ex.getMessage().equals("Modifier is already applied on this attribute!")) { + throw ex; } - catch (IllegalArgumentException ex) { - if (!ex.getMessage().equals("Modifier is already applied on this attribute!")) { - throw ex; - } - if (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_21)) { - mechanism.echoError("Cannot add attribute with key '" + modifier.getKey() + "' as the entity already has a modifier with the same key."); - } - else { - mechanism.echoError("Cannot add attribute with ID '" + modifier.getUniqueId() + "' as the entity already has a modifier with the same ID."); - } + if (ItemAttributeModifiers.MODERN_ATTRIBUTE_FORMAT) { + mechanism.echoError("Cannot add attribute with key '" + modifier.getKey() + "' as the entity already has a modifier with the same key."); + } + else { + mechanism.echoError("Cannot add attribute with ID '" + modifier.getUniqueId() + "' as the entity already has a modifier with the same ID."); } } - } + }); } catch (Throwable ex) { Debug.echoError(ex); @@ -402,12 +394,12 @@ public void adjust(Mechanism mechanism) { ArrayList inputList = new ArrayList<>(mechanism.valueAsType(ListTag.class)); Attributable ent = getAttributable(); for (String toRemove : new ArrayList<>(inputList)) { - if (Utilities.matchesEnumlike(new ElementTag(toRemove), Attribute.class)) { + Attribute attr = Utilities.elementToEnumlike(new ElementTag(toRemove, true), Attribute.class); + if (attr != null) { inputList.remove(toRemove); - Attribute attr = Attribute.valueOf(toRemove.toUpperCase()); AttributeInstance instance = ent.getAttribute(attr); if (instance == null) { - mechanism.echoError("Attribute " + attr.name() + " is not applicable to entity of type " + entity.getBukkitEntityType().name()); + mechanism.echoError("Attribute " + attr + " is not applicable to entity of type " + entity.getBukkitEntityType().name()); continue; } for (AttributeModifier modifier : instance.getModifiers()) { @@ -418,8 +410,7 @@ public void adjust(Mechanism mechanism) { for (String toRemove : inputList) { UUID id = null; NamespacedKey key = null; - boolean is1_21 = NMSHandler.getVersion().isAtLeast(NMSVersion.v1_21); - if (is1_21) { + if (ItemAttributeModifiers.MODERN_ATTRIBUTE_FORMAT) { key = Utilities.parseNamespacedKey(toRemove); } else { @@ -430,9 +421,9 @@ public void adjust(Mechanism mechanism) { if (instance == null) { continue; } - for (AttributeModifier modifer : instance.getModifiers()) { - if (is1_21 ? modifer.getKey().equals(key) : modifer.getUniqueId().equals(id)) { - instance.removeModifier(modifer); + for (AttributeModifier modifier : instance.getModifiers()) { + if (ItemAttributeModifiers.MODERN_ATTRIBUTE_FORMAT ? modifier.getKey().equals(key) : modifier.getUniqueId().equals(id)) { + instance.removeModifier(modifier); break; } } @@ -446,10 +437,10 @@ public void adjust(Mechanism mechanism) { ListTag list = mechanism.valueAsType(ListTag.class); for (String str : list) { List subList = CoreUtilities.split(str, '/'); - Attribute attr = Attribute.valueOf(EscapeTagUtil.unEscape(subList.get(0)).toUpperCase()); + Attribute attr = Utilities.elementToEnumlike(new ElementTag(EscapeTagUtil.unEscape(subList.get(0)), true), Attribute.class); AttributeInstance instance = ent.getAttribute(attr); if (instance == null) { - mechanism.echoError("Attribute " + attr.name() + " is not applicable to entity of type " + entity.getBukkitEntityType().name()); + mechanism.echoError("Attribute " + attr + " is not applicable to entity of type " + entity.getBukkitEntityType().name()); continue; } instance.setBaseValue(Double.parseDouble(subList.get(1))); diff --git a/plugin/src/main/java/com/denizenscript/denizen/objects/properties/item/ItemAttributeModifiers.java b/plugin/src/main/java/com/denizenscript/denizen/objects/properties/item/ItemAttributeModifiers.java index fb10189175..bd3f4abc1d 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/objects/properties/item/ItemAttributeModifiers.java +++ b/plugin/src/main/java/com/denizenscript/denizen/objects/properties/item/ItemAttributeModifiers.java @@ -4,6 +4,8 @@ import com.denizenscript.denizen.nms.NMSVersion; import com.denizenscript.denizen.objects.ItemTag; import com.denizenscript.denizen.objects.properties.entity.EntityAttributeModifiers; +import com.denizenscript.denizen.utilities.BukkitImplDeprecations; +import com.denizenscript.denizen.utilities.Settings; import com.denizenscript.denizen.utilities.Utilities; import com.denizenscript.denizencore.objects.Mechanism; import com.denizenscript.denizencore.objects.ObjectTag; @@ -12,18 +14,20 @@ import com.denizenscript.denizencore.objects.core.MapTag; import com.denizenscript.denizencore.objects.properties.PropertyParser; import com.denizenscript.denizencore.utilities.CoreUtilities; +import com.denizenscript.denizencore.utilities.debugging.Debug; import com.denizenscript.denizencore.utilities.text.StringHolder; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; import org.bukkit.NamespacedKey; +import org.bukkit.Registry; import org.bukkit.attribute.AttributeModifier; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Map; -import java.util.UUID; +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.Function; public class ItemAttributeModifiers extends ItemProperty { @@ -41,6 +45,67 @@ public class ItemAttributeModifiers extends ItemProperty { public static boolean describes(ItemTag item) { return true; } + + public static final boolean MODERN_ATTRIBUTE_FORMAT = NMSHandler.getVersion().isAtLeast(NMSVersion.v1_21); + + public static void parseAttributeModifiers(MapTag input, Mechanism mechanism, BiConsumer handler) { + parseAttributeModifiers(input, mechanism, Function.identity(), handler); + } + + public static void parseAttributeModifiers(MapTag input, Mechanism mechanism, Function attrToHolder, BiConsumer<@NotNull T, @NotNull AttributeModifier> handler) { + final Map unappliedLegacyValues = MODERN_ATTRIBUTE_FORMAT && Settings.cache_legacySpigotNamesSupport ? new HashMap<>() : null; + for (Map.Entry mapEntry : input.entrySet()) { + org.bukkit.attribute.Attribute attr; + String keyLow = mapEntry.getKey().low; + if (!MODERN_ATTRIBUTE_FORMAT) { + attr = ElementTag.asEnum(org.bukkit.attribute.Attribute.class, keyLow); + Debug.log("Got old attribute " + attr); + } + else { + attr = Registry.ATTRIBUTE.get(Utilities.parseNamespacedKey(keyLow)); + if (unappliedLegacyValues != null) { + if (attr != null) { + Debug.log("Got modern attribute " + attr); + if (unappliedLegacyValues.remove(attr) != null) { + Debug.log("Cleared legacy attribute " + attr); + } + } + else if (keyLow.startsWith("generic_") || keyLow.startsWith("player_") || keyLow.startsWith("zombie_")) { + org.bukkit.attribute.Attribute attribute = Utilities.elementToEnumlike(new ElementTag(keyLow, true), org.bukkit.attribute.Attribute.class, false); + if (attribute != null) { + unappliedLegacyValues.put(attribute, mapEntry.getValue()); + continue; + } + } + } + } + if (attr == null) { + mechanism.echoError("Invalid attribute specified: " + mapEntry.getKey() + '.'); + continue; + } + T holder = attrToHolder.apply(attr); + if (holder == null) { + continue; + } + for (ObjectTag listValue : CoreUtilities.objectToList(mapEntry.getValue(), mechanism.context)) { + handler.accept(holder, EntityAttributeModifiers.modiferForMap(attr, (MapTag) listValue, mechanism.context)); + } + } + if (unappliedLegacyValues != null && !unappliedLegacyValues.isEmpty()) { + BukkitImplDeprecations.oldSpigotNames.warn(mechanism.context); + for (Map.Entry unappliedEntry : unappliedLegacyValues.entrySet()) { + org.bukkit.attribute.Attribute attribute = unappliedEntry.getKey(); + T holder = attrToHolder.apply(attribute); + if (holder == null) { + continue; + } + Debug.log("Applying legacy attribute " + attribute); + for (ObjectTag listValue : CoreUtilities.objectToList(unappliedEntry.getValue(), mechanism.context)) { + handler.accept(holder, EntityAttributeModifiers.modiferForMap(attribute, (MapTag) listValue, mechanism.context)); + } + } + } + } @Override public boolean isDefaultValue(MapTag map) { @@ -49,23 +114,18 @@ public boolean isDefaultValue(MapTag map) { @Override public MapTag getPropertyValue() { - ItemMeta meta = getItemMeta(); - if (meta == null) { - return null; - } - Multimap metaMap = meta.getAttributeModifiers(); - return getAttributeModifiersFor(metaMap); + return getItemMeta() != null ? getAttributeModifiersFor(getItemMeta().getAttributeModifiers(), false) : null; + } + + @Override + public MapTag getTagValue(com.denizenscript.denizencore.tags.Attribute attribute) { + return getItemMeta() != null ? getAttributeModifiersFor(getItemMeta().getAttributeModifiers(), true) : null; } @Override public void setPropertyValue(MapTag param, Mechanism mechanism) { Multimap metaMap = LinkedHashMultimap.create(); - for (Map.Entry mapEntry : param.entrySet()) { - org.bukkit.attribute.Attribute attr = org.bukkit.attribute.Attribute.valueOf(mapEntry.getKey().str.toUpperCase()); - for (ObjectTag listValue : CoreUtilities.objectToList(mapEntry.getValue(), mechanism.context)) { - metaMap.put(attr, EntityAttributeModifiers.modiferForMap(attr, (MapTag) listValue, mechanism.context)); - } - } + parseAttributeModifiers(param, mechanism, metaMap::put); ItemMeta meta = getItemMeta(); meta.setAttributeModifiers(metaMap); setItemMeta(meta); @@ -76,7 +136,30 @@ public String getPropertyId() { return "attribute_modifiers"; } - public static MapTag getAttributeModifiersFor(Multimap metaMap) { + public static String legacyAttributeName(org.bukkit.attribute.Attribute attribute) { + if (!MODERN_ATTRIBUTE_FORMAT) { + return attribute.toString(); // Enum on older versions, #toString == #name + } + String nameLower = attribute.getKey().getKey(); + return switch (nameLower) { + case "block_interaction_range", "entity_interaction_range", "block_break_speed" -> "player_" + nameLower; + case "spawn_reinforcements" -> "zombie_" + nameLower; + default -> "generic_" + nameLower; + }; + } + + public static void addAttributeToMap(MapTag map, org.bukkit.attribute.Attribute attribute, ListTag value, boolean includeDeprecated) { + if (!MODERN_ATTRIBUTE_FORMAT) { + map.putObject(attribute.toString(), value); + return; + } + if (includeDeprecated && Settings.cache_legacySpigotNamesSupport) { + map.putObject(legacyAttributeName(attribute), value); + } + map.putObject(Utilities.namespacedKeyToString(attribute.getKey()), value); + } + + public static MapTag getAttributeModifiersFor(Multimap metaMap, boolean includeDeprecated) { MapTag map = new MapTag(); if (metaMap == null) { return map; @@ -86,11 +169,7 @@ public static MapTag getAttributeModifiersFor(Multimap PropertyParser.registerMechanism(ItemAttributeModifiers.class, MapTag.class, "add_attribute_modifiers", (prop, mechanism, param) -> { ItemMeta meta = prop.getItemMeta(); - for (Map.Entry subValue : param.entrySet()) { - org.bukkit.attribute.Attribute attr = org.bukkit.attribute.Attribute.valueOf(subValue.getKey().str.toUpperCase()); - for (ObjectTag listValue : CoreUtilities.objectToList(subValue.getValue(), mechanism.context)) { - meta.addAttributeModifier(attr, EntityAttributeModifiers.modiferForMap(attr, (MapTag) listValue, mechanism.context)); - } - } + parseAttributeModifiers(param, mechanism, meta::addAttributeModifier); prop.setItemMeta(meta); }); @@ -154,9 +228,9 @@ public static void register() { ItemMeta meta = prop.getItemMeta(); ArrayList inputList = new ArrayList<>(param); for (String toRemove : new ArrayList<>(inputList)) { - if (Utilities.matchesEnumlike(new ElementTag(toRemove), org.bukkit.attribute.Attribute.class)) { + org.bukkit.attribute.Attribute attr = Utilities.elementToEnumlike(new ElementTag(toRemove, true), org.bukkit.attribute.Attribute.class); + if (attr != null) { inputList.remove(toRemove); - org.bukkit.attribute.Attribute attr = org.bukkit.attribute.Attribute.valueOf(toRemove.toUpperCase()); meta.removeAttributeModifier(attr); } } From d7bbf9daa4e743e9dd26003055803f70d5123e61 Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Fri, 15 Aug 2025 00:59:43 +0100 Subject: [PATCH 2/7] Move attribute utils into a separate util class --- .../entity/EntityAttributeModifiers.java | 181 +----------- .../item/ItemAttributeModifiers.java | 111 +------- .../denizen/utilities/AttributeUtil.java | 265 ++++++++++++++++++ 3 files changed, 284 insertions(+), 273 deletions(-) create mode 100644 plugin/src/main/java/com/denizenscript/denizen/utilities/AttributeUtil.java diff --git a/plugin/src/main/java/com/denizenscript/denizen/objects/properties/entity/EntityAttributeModifiers.java b/plugin/src/main/java/com/denizenscript/denizen/objects/properties/entity/EntityAttributeModifiers.java index 626fae7c51..fd7902e0bd 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/objects/properties/entity/EntityAttributeModifiers.java +++ b/plugin/src/main/java/com/denizenscript/denizen/objects/properties/entity/EntityAttributeModifiers.java @@ -1,9 +1,7 @@ package com.denizenscript.denizen.objects.properties.entity; -import com.denizenscript.denizen.nms.NMSHandler; -import com.denizenscript.denizen.nms.NMSVersion; import com.denizenscript.denizen.objects.EntityTag; -import com.denizenscript.denizen.objects.properties.item.ItemAttributeModifiers; +import com.denizenscript.denizen.utilities.AttributeUtil; import com.denizenscript.denizen.utilities.BukkitImplDeprecations; import com.denizenscript.denizen.utilities.Utilities; import com.denizenscript.denizencore.objects.Mechanism; @@ -13,7 +11,6 @@ import com.denizenscript.denizencore.objects.core.MapTag; import com.denizenscript.denizencore.objects.properties.Property; import com.denizenscript.denizencore.objects.properties.PropertyParser; -import com.denizenscript.denizencore.tags.TagContext; import com.denizenscript.denizencore.tags.core.EscapeTagUtil; import com.denizenscript.denizencore.utilities.CoreUtilities; import com.denizenscript.denizencore.utilities.debugging.Debug; @@ -23,7 +20,6 @@ import org.bukkit.attribute.AttributeInstance; import org.bukkit.attribute.AttributeModifier; import org.bukkit.inventory.EquipmentSlot; -import org.bukkit.inventory.EquipmentSlotGroup; import java.util.ArrayList; import java.util.Collection; @@ -56,12 +52,6 @@ public EntityAttributeModifiers(EntityTag entity) { EntityTag entity; - @Deprecated - public static String stringify(AttributeModifier modifier) { - return EscapeTagUtil.escape(modifier.getName()) + "/" + modifier.getAmount() + "/" + modifier.getOperation().name() - + "/" + (modifier.getSlot() == null ? "any" : modifier.getSlot().name()); - } - @Deprecated public ListTag getAttributes() { ListTag list = new ListTag(); @@ -72,98 +62,13 @@ public ListTag getAttributes() { } StringBuilder modifiers = new StringBuilder(); for (AttributeModifier modifier : instance.getModifiers()) { - modifiers.append("/").append(stringify(modifier)); + modifiers.append("/").append(AttributeUtil.modifierToLegacyString(modifier)); } - list.add(EscapeTagUtil.escape(ItemAttributeModifiers.legacyAttributeName(attribute)) + "/" + instance.getBaseValue() + modifiers); + list.add(EscapeTagUtil.escape(AttributeUtil.legacyName(attribute)) + "/" + instance.getBaseValue() + modifiers); } return list; } - public static MapTag mapify(AttributeModifier modifier) { - MapTag result = new MapTag(); - result.putObject("name", new ElementTag(modifier.getName())); - result.putObject("amount", new ElementTag(modifier.getAmount())); - result.putObject("operation", new ElementTag(modifier.getOperation())); - if (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_20)) { - result.putObject("slot", new ElementTag(modifier.getSlotGroup().toString(), true)); - } - else { - result.putObject("slot", new ElementTag(modifier.getSlot() == null ? "any" : modifier.getSlot().name())); - } - if (ItemAttributeModifiers.MODERN_ATTRIBUTE_FORMAT) { - result.putObject("key", new ElementTag(Utilities.namespacedKeyToString(modifier.getKey()), true)); - } - // TODO: remove/deprecate the UUID key - result.putObject("id", new ElementTag(modifier.getUniqueId().toString())); - return result; - } - - public static AttributeModifier modiferForMap(Attribute attr, MapTag map, TagContext context) { - ElementTag amount = map.getElement("amount"); - ElementTag operation = map.getElement("operation"); - double amountValue; - AttributeModifier.Operation operationValue = operation.asEnum(AttributeModifier.Operation.class); - if (operationValue == null) { - Debug.echoError("Attribute modifier operation '" + operation + "' does not exist."); - return null; - } - try { - amountValue = Double.parseDouble(amount.toString()); - } - catch (NumberFormatException ex) { - Debug.echoError("Attribute modifier amount '" + amount + "' is not a valid decimal number."); - return null; - } - if (!NMSHandler.getVersion().isAtLeast(NMSVersion.v1_21)) { - return parseLegacyModifier(attr, map, amountValue, operationValue); - } - ElementTag key = map.getElement("key"); - if (key == null && map.size() >= 2) { - BukkitImplDeprecations.pre1_21AttributeFormat.warn(context); - return parseLegacyModifier(attr, map, amountValue, operationValue); - } - if (key == null) { - Debug.echoError("Must specify a key."); - return null; - } - String slotGroupName = map.getElement("slot", "any").asString(); - EquipmentSlotGroup group = EquipmentSlotGroup.getByName(slotGroupName); - if (group == null) { - EquipmentSlot slot = ElementTag.asEnum(EquipmentSlot.class, slotGroupName); - if (slot == null) { - Debug.echoError("Invalid equipment slot group specified: " + slotGroupName); - return null; - } - group = slot.getGroup(); - } - return new AttributeModifier(Utilities.parseNamespacedKey(key.asString()), amountValue, operationValue, group); - } - - @Deprecated(forRemoval = true) - public static AttributeModifier parseLegacyModifier(Attribute attr, MapTag map, double amount, AttributeModifier.Operation operation) { - ElementTag name = map.getElement("name"); - ElementTag slot = map.getElement("slot", "any"); - ElementTag id = map.getElement("id"); - UUID idValue; - try { - idValue = id == null ? UUID.randomUUID() : UUID.fromString(id.toString()); - } - catch (IllegalArgumentException ex) { - Debug.echoError("Attribute modifier ID '" + id + "' is not a valid UUID."); - return null; - } - EquipmentSlot slotValue = CoreUtilities.equalsIgnoreCase(slot.toString(), "any") ? null : slot.asEnum(EquipmentSlot.class); - if (slotValue == null && NMSHandler.getVersion().isAtLeast(NMSVersion.v1_20)) { - EquipmentSlotGroup group = EquipmentSlotGroup.getByName(slot.asString()); - if (group == null) { - Debug.echoError("Invalid equipment slot group specified: " + slot); - return null; - } - return new AttributeModifier(idValue, name == null ? ItemAttributeModifiers.legacyAttributeName(attr) : name.asString(), amount, operation, group); - } - return new AttributeModifier(idValue, name == null ? ItemAttributeModifiers.legacyAttributeName(attr) : name.toString(), amount, operation, slotValue); - } - public ListTag getAttributeModifierList(AttributeInstance instance) { if (instance == null) { return null; @@ -172,7 +77,7 @@ public ListTag getAttributeModifierList(AttributeInstance instance) { if (modifiers.isEmpty()) { return null; } - return new ListTag(modifiers, EntityAttributeModifiers::mapify); + return new ListTag(modifiers, AttributeUtil::modifierToMap); } public MapTag getAttributeModifiers(boolean includeDeprecated) { @@ -180,7 +85,7 @@ public MapTag getAttributeModifiers(boolean includeDeprecated) { for (Attribute attribute : Utilities.listTypesRaw(Attribute.class)) { ListTag list = getAttributeModifierList(getAttributable().getAttribute(attribute)); if (list != null) { - ItemAttributeModifiers.addAttributeToMap(map, attribute, list, includeDeprecated); + AttributeUtil.addToMap(map, attribute, list, includeDeprecated); } } return map; @@ -201,72 +106,6 @@ public String getPropertyId() { return "attribute_modifiers"; } - - // <--[language] - // @name Attribute Modifiers - // @group Properties - // @description - // In minecraft, the "attributes" system defined certain core numerical values on entities, such as max health or attack damage. - // The value of an "attribute" is determined by its "base value" modified mathematically by each of its "attribute modififers". - // "Attribute modifiers" can be added either directly to the entity, or onto items - when on an item, an entity can equip it into the correct slot to automatically apply the modifier. - // - // These can be read via such tags as <@link tag EntityTag.attribute_modifiers>, <@link tag ItemTag.attribute_modifiers>, - // <@link tag EntityTag.has_attribute>, <@link tag EntityTag.attribute_value>, <@link tag EntityTag.attribute_base_value>, <@link tag EntityTag.attribute_default_value>, ... - // - // These can be modified by such mechanisms as <@link mechanism EntityTag.attribute_base_values>, <@link mechanism EntityTag.attribute_modifiers>, <@link mechanism EntityTag.add_attribute_modifiers>, - // <@link mechanism EntityTag.remove_attribute_modifiers>, <@link mechanism ItemTag.attribute_modifiers>, <@link mechanism ItemTag.add_attribute_modifiers>, <@link mechanism ItemTag.remove_attribute_modifiers>, ... - // - // The input format of each of the 'add' and set mechanisms is slightly complicated: a MapTag where the keys are attribute names, and values are a ListTag of modifiers, - // where each modifier is itself a MapTag with required keys 'operation' and 'amount', and additionally: - // Before MC 1.21: optional 'name', 'slot', and 'id' keys. - // The default ID will be randomly generated, the default name will be the attribute name. - // After MC 1.21: required 'key' key, and optional 'slot'. - // The 'key' is the attribute's name/identifier in a "namespace:key" format (defaulting to the "minecraft" namespace), which has to be distinct to other modifiers of the same type on the object. - // - // Valid operations: ADD_NUMBER, ADD_SCALAR, and MULTIPLY_SCALAR_1 - // Valid slots (used up to MC 1.20.6): HAND, OFF_HAND, FEET, LEGS, CHEST, HEAD, ANY - // Valid slot groups (used on MC 1.20.6+): <@link url https://hub.spigotmc.org/javadocs/spigot/org/bukkit/inventory/EquipmentSlotGroup.html> - // Valid attribute names are listed at <@link url https://hub.spigotmc.org/javadocs/spigot/org/bukkit/attribute/Attribute.html> - // The default slot/slot group is "any". - // - // Operation names are based on the Bukkit enum. - // ADD_NUMBER corresponds to Mojang "ADDITION" - adds on top of the base value. - // ADD_SCALAR corresponds to Mojang "MULTIPLY_BASE" - adds to the total, multiplied by the base value. - // MULTIPLY_SCALAR_1 corresponds to Mojang "MULTIPLY_TOTAL", multiplies the final value (after both "add_number" and "add_scaler") by the amount given plus one. - // - // They are combined like (pseudo-code): - // - // - define x <[base_value]> - // - foreach : - // - define x:+:<[value]> - // - define y <[x]> - // - foreach : - // - define y:+:<[x].mul[<[value]>]> - // - foreach : - // - define y:*:<[value].add[1]> - // - determine <[y]> - // - // - // See also <@link url https://minecraft.wiki/w/Attribute#Modifiers> - // - // For a quick and dirty in-line input, you can do for example: [generic_max_health=]>] - // - // For more clean/proper input, instead do something like: - // - // - definemap attributes: - // generic_max_health: - // 1: - // key: my_project:add_health - // operation: ADD_NUMBER - // amount: 20 - // slot: head - // - inventory adjust slot:head add_attribute_modifiers:<[attributes]> - // - // - // When pre-defining a custom item, instead of this, simply use an item script: <@link language item script containers>. That page shows an example of valid attribute modifiers on an item script. - // - // --> - public static void register() { // <--[tag] @@ -312,7 +151,7 @@ public void adjust(Mechanism mechanism) { try { MapTag input = mechanism.valueAsType(MapTag.class); Attributable ent = getAttributable(); - ItemAttributeModifiers.parseAttributeModifiers(input, mechanism, attribute -> { + AttributeUtil.parseModifiers(input, mechanism, attribute -> { AttributeInstance instance = ent.getAttribute(attribute); if (instance == null) { mechanism.echoError("Attribute " + attribute + " is not applicable to entity of type " + entity.getBukkitEntityType().name()); @@ -347,7 +186,7 @@ public void adjust(Mechanism mechanism) { try { MapTag input = mechanism.valueAsType(MapTag.class); Attributable ent = getAttributable(); - ItemAttributeModifiers.parseAttributeModifiers(input, mechanism, attribute -> { + AttributeUtil.parseModifiers(input, mechanism, attribute -> { AttributeInstance instance = ent.getAttribute(attribute); if (instance == null) { mechanism.echoError("Attribute " + attribute + " is not applicable to entity of type " + entity.getBukkitEntityType().name()); @@ -362,7 +201,7 @@ public void adjust(Mechanism mechanism) { if (!ex.getMessage().equals("Modifier is already applied on this attribute!")) { throw ex; } - if (ItemAttributeModifiers.MODERN_ATTRIBUTE_FORMAT) { + if (AttributeUtil.MODERN_ATTRIBUTE_FORMAT) { mechanism.echoError("Cannot add attribute with key '" + modifier.getKey() + "' as the entity already has a modifier with the same key."); } else { @@ -410,7 +249,7 @@ public void adjust(Mechanism mechanism) { for (String toRemove : inputList) { UUID id = null; NamespacedKey key = null; - if (ItemAttributeModifiers.MODERN_ATTRIBUTE_FORMAT) { + if (AttributeUtil.MODERN_ATTRIBUTE_FORMAT) { key = Utilities.parseNamespacedKey(toRemove); } else { @@ -422,7 +261,7 @@ public void adjust(Mechanism mechanism) { continue; } for (AttributeModifier modifier : instance.getModifiers()) { - if (ItemAttributeModifiers.MODERN_ATTRIBUTE_FORMAT ? modifier.getKey().equals(key) : modifier.getUniqueId().equals(id)) { + if (AttributeUtil.MODERN_ATTRIBUTE_FORMAT ? modifier.getKey().equals(key) : modifier.getUniqueId().equals(id)) { instance.removeModifier(modifier); break; } diff --git a/plugin/src/main/java/com/denizenscript/denizen/objects/properties/item/ItemAttributeModifiers.java b/plugin/src/main/java/com/denizenscript/denizen/objects/properties/item/ItemAttributeModifiers.java index bd3f4abc1d..33c0a9d3e4 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/objects/properties/item/ItemAttributeModifiers.java +++ b/plugin/src/main/java/com/denizenscript/denizen/objects/properties/item/ItemAttributeModifiers.java @@ -3,31 +3,23 @@ import com.denizenscript.denizen.nms.NMSHandler; import com.denizenscript.denizen.nms.NMSVersion; import com.denizenscript.denizen.objects.ItemTag; -import com.denizenscript.denizen.objects.properties.entity.EntityAttributeModifiers; -import com.denizenscript.denizen.utilities.BukkitImplDeprecations; -import com.denizenscript.denizen.utilities.Settings; +import com.denizenscript.denizen.utilities.AttributeUtil; import com.denizenscript.denizen.utilities.Utilities; import com.denizenscript.denizencore.objects.Mechanism; -import com.denizenscript.denizencore.objects.ObjectTag; import com.denizenscript.denizencore.objects.core.ElementTag; import com.denizenscript.denizencore.objects.core.ListTag; import com.denizenscript.denizencore.objects.core.MapTag; import com.denizenscript.denizencore.objects.properties.PropertyParser; -import com.denizenscript.denizencore.utilities.CoreUtilities; -import com.denizenscript.denizencore.utilities.debugging.Debug; -import com.denizenscript.denizencore.utilities.text.StringHolder; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; import org.bukkit.NamespacedKey; -import org.bukkit.Registry; import org.bukkit.attribute.AttributeModifier; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.meta.ItemMeta; -import org.jetbrains.annotations.NotNull; -import java.util.*; -import java.util.function.BiConsumer; -import java.util.function.Function; +import java.util.ArrayList; +import java.util.Collection; +import java.util.UUID; public class ItemAttributeModifiers extends ItemProperty { @@ -45,67 +37,6 @@ public class ItemAttributeModifiers extends ItemProperty { public static boolean describes(ItemTag item) { return true; } - - public static final boolean MODERN_ATTRIBUTE_FORMAT = NMSHandler.getVersion().isAtLeast(NMSVersion.v1_21); - - public static void parseAttributeModifiers(MapTag input, Mechanism mechanism, BiConsumer handler) { - parseAttributeModifiers(input, mechanism, Function.identity(), handler); - } - - public static void parseAttributeModifiers(MapTag input, Mechanism mechanism, Function attrToHolder, BiConsumer<@NotNull T, @NotNull AttributeModifier> handler) { - final Map unappliedLegacyValues = MODERN_ATTRIBUTE_FORMAT && Settings.cache_legacySpigotNamesSupport ? new HashMap<>() : null; - for (Map.Entry mapEntry : input.entrySet()) { - org.bukkit.attribute.Attribute attr; - String keyLow = mapEntry.getKey().low; - if (!MODERN_ATTRIBUTE_FORMAT) { - attr = ElementTag.asEnum(org.bukkit.attribute.Attribute.class, keyLow); - Debug.log("Got old attribute " + attr); - } - else { - attr = Registry.ATTRIBUTE.get(Utilities.parseNamespacedKey(keyLow)); - if (unappliedLegacyValues != null) { - if (attr != null) { - Debug.log("Got modern attribute " + attr); - if (unappliedLegacyValues.remove(attr) != null) { - Debug.log("Cleared legacy attribute " + attr); - } - } - else if (keyLow.startsWith("generic_") || keyLow.startsWith("player_") || keyLow.startsWith("zombie_")) { - org.bukkit.attribute.Attribute attribute = Utilities.elementToEnumlike(new ElementTag(keyLow, true), org.bukkit.attribute.Attribute.class, false); - if (attribute != null) { - unappliedLegacyValues.put(attribute, mapEntry.getValue()); - continue; - } - } - } - } - if (attr == null) { - mechanism.echoError("Invalid attribute specified: " + mapEntry.getKey() + '.'); - continue; - } - T holder = attrToHolder.apply(attr); - if (holder == null) { - continue; - } - for (ObjectTag listValue : CoreUtilities.objectToList(mapEntry.getValue(), mechanism.context)) { - handler.accept(holder, EntityAttributeModifiers.modiferForMap(attr, (MapTag) listValue, mechanism.context)); - } - } - if (unappliedLegacyValues != null && !unappliedLegacyValues.isEmpty()) { - BukkitImplDeprecations.oldSpigotNames.warn(mechanism.context); - for (Map.Entry unappliedEntry : unappliedLegacyValues.entrySet()) { - org.bukkit.attribute.Attribute attribute = unappliedEntry.getKey(); - T holder = attrToHolder.apply(attribute); - if (holder == null) { - continue; - } - Debug.log("Applying legacy attribute " + attribute); - for (ObjectTag listValue : CoreUtilities.objectToList(unappliedEntry.getValue(), mechanism.context)) { - handler.accept(holder, EntityAttributeModifiers.modiferForMap(attribute, (MapTag) listValue, mechanism.context)); - } - } - } - } @Override public boolean isDefaultValue(MapTag map) { @@ -125,7 +56,7 @@ public MapTag getTagValue(com.denizenscript.denizencore.tags.Attribute attribute @Override public void setPropertyValue(MapTag param, Mechanism mechanism) { Multimap metaMap = LinkedHashMultimap.create(); - parseAttributeModifiers(param, mechanism, metaMap::put); + AttributeUtil.parseModifiers(param, mechanism, metaMap::put); ItemMeta meta = getItemMeta(); meta.setAttributeModifiers(metaMap); setItemMeta(meta); @@ -136,29 +67,6 @@ public String getPropertyId() { return "attribute_modifiers"; } - public static String legacyAttributeName(org.bukkit.attribute.Attribute attribute) { - if (!MODERN_ATTRIBUTE_FORMAT) { - return attribute.toString(); // Enum on older versions, #toString == #name - } - String nameLower = attribute.getKey().getKey(); - return switch (nameLower) { - case "block_interaction_range", "entity_interaction_range", "block_break_speed" -> "player_" + nameLower; - case "spawn_reinforcements" -> "zombie_" + nameLower; - default -> "generic_" + nameLower; - }; - } - - public static void addAttributeToMap(MapTag map, org.bukkit.attribute.Attribute attribute, ListTag value, boolean includeDeprecated) { - if (!MODERN_ATTRIBUTE_FORMAT) { - map.putObject(attribute.toString(), value); - return; - } - if (includeDeprecated && Settings.cache_legacySpigotNamesSupport) { - map.putObject(legacyAttributeName(attribute), value); - } - map.putObject(Utilities.namespacedKeyToString(attribute.getKey()), value); - } - public static MapTag getAttributeModifiersFor(Multimap metaMap, boolean includeDeprecated) { MapTag map = new MapTag(); if (metaMap == null) { @@ -169,7 +77,7 @@ public static MapTag getAttributeModifiersFor(Multimap PropertyParser.registerMechanism(ItemAttributeModifiers.class, MapTag.class, "add_attribute_modifiers", (prop, mechanism, param) -> { ItemMeta meta = prop.getItemMeta(); - parseAttributeModifiers(param, mechanism, meta::addAttributeModifier); + AttributeUtil.parseModifiers(param, mechanism, meta::addAttributeModifier); prop.setItemMeta(meta); }); @@ -237,8 +145,7 @@ public static void register() { for (String toRemove : inputList) { UUID id = null; NamespacedKey key = null; - boolean is1_21 = NMSHandler.getVersion().isAtLeast(NMSVersion.v1_21); - if (is1_21) { + if (AttributeUtil.MODERN_ATTRIBUTE_FORMAT) { key = Utilities.parseNamespacedKey(toRemove); } else { @@ -247,7 +154,7 @@ public static void register() { Multimap metaMap = meta.getAttributeModifiers(); for (org.bukkit.attribute.Attribute attribute : metaMap.keys()) { for (AttributeModifier modifer : metaMap.get(attribute)) { - if (is1_21 ? modifer.getKey().equals(key) : modifer.getUniqueId().equals(id)) { + if (AttributeUtil.MODERN_ATTRIBUTE_FORMAT ? modifer.getKey().equals(key) : modifer.getUniqueId().equals(id)) { meta.removeAttributeModifier(attribute, modifer); break; } diff --git a/plugin/src/main/java/com/denizenscript/denizen/utilities/AttributeUtil.java b/plugin/src/main/java/com/denizenscript/denizen/utilities/AttributeUtil.java new file mode 100644 index 0000000000..451253c0bb --- /dev/null +++ b/plugin/src/main/java/com/denizenscript/denizen/utilities/AttributeUtil.java @@ -0,0 +1,265 @@ +package com.denizenscript.denizen.utilities; + +import com.denizenscript.denizen.nms.NMSHandler; +import com.denizenscript.denizen.nms.NMSVersion; +import com.denizenscript.denizencore.objects.Mechanism; +import com.denizenscript.denizencore.objects.ObjectTag; +import com.denizenscript.denizencore.objects.core.ElementTag; +import com.denizenscript.denizencore.objects.core.ListTag; +import com.denizenscript.denizencore.objects.core.MapTag; +import com.denizenscript.denizencore.tags.TagContext; +import com.denizenscript.denizencore.tags.core.EscapeTagUtil; +import com.denizenscript.denizencore.utilities.CoreUtilities; +import com.denizenscript.denizencore.utilities.debugging.Debug; +import com.denizenscript.denizencore.utilities.text.StringHolder; +import org.bukkit.Registry; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeModifier; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.EquipmentSlotGroup; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.function.BiConsumer; +import java.util.function.Function; + +public class AttributeUtil { + + + // <--[language] + // @name Attribute Modifiers + // @group Properties + // @description + // In minecraft, the "attributes" system defined certain core numerical values on entities, such as max health or attack damage. + // The value of an "attribute" is determined by its "base value" modified mathematically by each of its "attribute modififers". + // "Attribute modifiers" can be added either directly to the entity, or onto items - when on an item, an entity can equip it into the correct slot to automatically apply the modifier. + // + // These can be read via such tags as <@link tag EntityTag.attribute_modifiers>, <@link tag ItemTag.attribute_modifiers>, + // <@link tag EntityTag.has_attribute>, <@link tag EntityTag.attribute_value>, <@link tag EntityTag.attribute_base_value>, <@link tag EntityTag.attribute_default_value>, ... + // + // These can be modified by such mechanisms as <@link mechanism EntityTag.attribute_base_values>, <@link mechanism EntityTag.attribute_modifiers>, <@link mechanism EntityTag.add_attribute_modifiers>, + // <@link mechanism EntityTag.remove_attribute_modifiers>, <@link mechanism ItemTag.attribute_modifiers>, <@link mechanism ItemTag.add_attribute_modifiers>, <@link mechanism ItemTag.remove_attribute_modifiers>, ... + // + // The input format of each of the 'add' and set mechanisms is slightly complicated: a MapTag where the keys are attribute names, and values are a ListTag of modifiers, + // where each modifier is itself a MapTag with required keys 'operation' and 'amount', and additionally: + // Before MC 1.21: optional 'name', 'slot', and 'id' keys. + // The default ID will be randomly generated, the default name will be the attribute name. + // After MC 1.21: required 'key' key, and optional 'slot'. + // The 'key' is the attribute's name/identifier in a "namespace:key" format (defaulting to the "minecraft" namespace), which has to be distinct to other modifiers of the same type on the object. + // + // Valid operations: ADD_NUMBER, ADD_SCALAR, and MULTIPLY_SCALAR_1 + // Valid slots (used up to MC 1.20.6): HAND, OFF_HAND, FEET, LEGS, CHEST, HEAD, ANY + // Valid slot groups (used on MC 1.20.6+): <@link url https://hub.spigotmc.org/javadocs/spigot/org/bukkit/inventory/EquipmentSlotGroup.html> + // Valid attribute names are listed at <@link url https://hub.spigotmc.org/javadocs/spigot/org/bukkit/attribute/Attribute.html> + // The default slot/slot group is "any". + // + // Operation names are based on the Bukkit enum. + // ADD_NUMBER corresponds to Mojang "ADDITION" - adds on top of the base value. + // ADD_SCALAR corresponds to Mojang "MULTIPLY_BASE" - adds to the total, multiplied by the base value. + // MULTIPLY_SCALAR_1 corresponds to Mojang "MULTIPLY_TOTAL", multiplies the final value (after both "add_number" and "add_scaler") by the amount given plus one. + // + // They are combined like (pseudo-code): + // + // - define x <[base_value]> + // - foreach : + // - define x:+:<[value]> + // - define y <[x]> + // - foreach : + // - define y:+:<[x].mul[<[value]>]> + // - foreach : + // - define y:*:<[value].add[1]> + // - determine <[y]> + // + // + // See also <@link url https://minecraft.wiki/w/Attribute#Modifiers> + // + // For a quick and dirty in-line input, you can do for example: [generic_max_health=]>] + // + // For more clean/proper input, instead do something like: + // + // - definemap attributes: + // generic_max_health: + // 1: + // key: my_project:add_health + // operation: ADD_NUMBER + // amount: 20 + // slot: head + // - inventory adjust slot:head add_attribute_modifiers:<[attributes]> + // + // + // When pre-defining a custom item, instead of this, simply use an item script: <@link language item script containers>. That page shows an example of valid attribute modifiers on an item script. + // + // --> + + public static final boolean MODERN_ATTRIBUTE_FORMAT = NMSHandler.getVersion().isAtLeast(NMSVersion.v1_21); + + public static String legacyName(Attribute attribute) { + if (!MODERN_ATTRIBUTE_FORMAT) { + return String.valueOf(attribute); // Enum on older versions, #toString == #name + } + String nameLower = attribute.getKey().getKey(); + return switch (nameLower) { + case "block_interaction_range", "entity_interaction_range", "block_break_speed" -> "player_" + nameLower; + case "spawn_reinforcements" -> "zombie_" + nameLower; + default -> "generic_" + nameLower; + }; + } + + public static void addToMap(MapTag map, Attribute attribute, ListTag value, boolean includeDeprecated) { + if (!MODERN_ATTRIBUTE_FORMAT) { + map.putObject(String.valueOf(attribute), value); + return; + } + if (includeDeprecated && Settings.cache_legacySpigotNamesSupport) { + map.putObject(legacyName(attribute), value); + } + map.putObject(Utilities.namespacedKeyToString(attribute.getKey()), value); + } + + public static void parseModifiers(MapTag input, Mechanism mechanism, BiConsumer handler) { + parseModifiers(input, mechanism, Function.identity(), handler); + } + + public static void parseModifiers(MapTag input, Mechanism mechanism, Function attrToHolder, BiConsumer<@NotNull T, @NotNull AttributeModifier> handler) { + final Map unappliedLegacyValues = MODERN_ATTRIBUTE_FORMAT && Settings.cache_legacySpigotNamesSupport ? new HashMap<>() : null; + for (Map.Entry mapEntry : input.entrySet()) { + Attribute attr; + String keyLow = mapEntry.getKey().low; + if (!MODERN_ATTRIBUTE_FORMAT) { + attr = ElementTag.asEnum(Attribute.class, keyLow); + } + else { + attr = Registry.ATTRIBUTE.get(Utilities.parseNamespacedKey(keyLow)); + if (unappliedLegacyValues != null) { + if (attr != null) { + unappliedLegacyValues.remove(attr); + } + else if (keyLow.startsWith("generic_") || keyLow.startsWith("player_") || keyLow.startsWith("zombie_")) { + Attribute attribute = Utilities.elementToEnumlike(new ElementTag(keyLow, true), Attribute.class, false); + if (attribute != null) { + unappliedLegacyValues.put(attribute, mapEntry.getValue()); + continue; + } + } + } + } + if (attr == null) { + mechanism.echoError("Invalid attribute specified: " + mapEntry.getKey() + '.'); + continue; + } + T holder = attrToHolder.apply(attr); + if (holder == null) { + continue; + } + for (ObjectTag listValue : CoreUtilities.objectToList(mapEntry.getValue(), mechanism.context)) { + handler.accept(holder, parseModifier(attr, (MapTag) listValue, mechanism.context)); + } + } + if (unappliedLegacyValues != null && !unappliedLegacyValues.isEmpty()) { + BukkitImplDeprecations.oldSpigotNames.warn(mechanism.context); + for (Map.Entry unappliedEntry : unappliedLegacyValues.entrySet()) { + Attribute attribute = unappliedEntry.getKey(); + T holder = attrToHolder.apply(attribute); + if (holder == null) { + continue; + } + for (ObjectTag listValue : CoreUtilities.objectToList(unappliedEntry.getValue(), mechanism.context)) { + handler.accept(holder, parseModifier(attribute, (MapTag) listValue, mechanism.context)); + } + } + } + } + + public static MapTag modifierToMap(AttributeModifier modifier) { + MapTag result = new MapTag(); + result.putObject("name", new ElementTag(modifier.getName())); + result.putObject("amount", new ElementTag(modifier.getAmount())); + result.putObject("operation", new ElementTag(modifier.getOperation())); + if (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_20)) { + result.putObject("slot", new ElementTag(modifier.getSlotGroup().toString(), true)); + } + else { + result.putObject("slot", new ElementTag(modifier.getSlot() == null ? "any" : modifier.getSlot().name())); + } + if (AttributeUtil.MODERN_ATTRIBUTE_FORMAT) { + result.putObject("key", new ElementTag(Utilities.namespacedKeyToString(modifier.getKey()), true)); + } + // TODO: remove/deprecate the UUID key + result.putObject("id", new ElementTag(modifier.getUniqueId().toString())); + return result; + } + + public static AttributeModifier parseModifier(Attribute attr, MapTag map, TagContext context) { + ElementTag amount = map.getElement("amount"); + ElementTag operation = map.getElement("operation"); + double amountValue; + AttributeModifier.Operation operationValue = operation.asEnum(AttributeModifier.Operation.class); + if (operationValue == null) { + Debug.echoError("Attribute modifier operation '" + operation + "' does not exist."); + return null; + } + try { + amountValue = Double.parseDouble(amount.toString()); + } + catch (NumberFormatException ex) { + Debug.echoError("Attribute modifier amount '" + amount + "' is not a valid decimal number."); + return null; + } + if (!NMSHandler.getVersion().isAtLeast(NMSVersion.v1_21)) { + return parseLegacyModifier(attr, map, amountValue, operationValue); + } + ElementTag key = map.getElement("key"); + if (key == null && map.size() >= 2) { + BukkitImplDeprecations.pre1_21AttributeFormat.warn(context); + return parseLegacyModifier(attr, map, amountValue, operationValue); + } + if (key == null) { + Debug.echoError("Must specify a key."); + return null; + } + String slotGroupName = map.getElement("slot", "any").asString(); + EquipmentSlotGroup group = EquipmentSlotGroup.getByName(slotGroupName); + if (group == null) { + EquipmentSlot slot = ElementTag.asEnum(EquipmentSlot.class, slotGroupName); + if (slot == null) { + Debug.echoError("Invalid equipment slot group specified: " + slotGroupName); + return null; + } + group = slot.getGroup(); + } + return new AttributeModifier(Utilities.parseNamespacedKey(key.asString()), amountValue, operationValue, group); + } + + @Deprecated(forRemoval = true) + public static String modifierToLegacyString(AttributeModifier modifier) { + return EscapeTagUtil.escape(modifier.getName()) + "/" + modifier.getAmount() + "/" + modifier.getOperation().name() + + "/" + (modifier.getSlot() == null ? "any" : modifier.getSlot().name()); + } + + @Deprecated(forRemoval = true) + public static AttributeModifier parseLegacyModifier(Attribute attr, MapTag map, double amount, AttributeModifier.Operation operation) { + ElementTag name = map.getElement("name"); + ElementTag slot = map.getElement("slot", "any"); + ElementTag id = map.getElement("id"); + UUID idValue; + try { + idValue = id == null ? UUID.randomUUID() : UUID.fromString(id.toString()); + } + catch (IllegalArgumentException ex) { + Debug.echoError("Attribute modifier ID '" + id + "' is not a valid UUID."); + return null; + } + EquipmentSlot slotValue = CoreUtilities.equalsIgnoreCase(slot.toString(), "any") ? null : slot.asEnum(EquipmentSlot.class); + if (slotValue == null && NMSHandler.getVersion().isAtLeast(NMSVersion.v1_20)) { + EquipmentSlotGroup group = EquipmentSlotGroup.getByName(slot.asString()); + if (group == null) { + Debug.echoError("Invalid equipment slot group specified: " + slot); + return null; + } + return new AttributeModifier(idValue, name == null ? AttributeUtil.legacyName(attr) : name.asString(), amount, operation, group); + } + return new AttributeModifier(idValue, name == null ? AttributeUtil.legacyName(attr) : name.toString(), amount, operation, slotValue); + } +} From 1bec6b00c98f0eb3dc5347d33d4936c993fca3b0 Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Fri, 15 Aug 2025 17:46:43 +0100 Subject: [PATCH 3/7] `id` & `name` under `includeDeprecated` --- .../entity/EntityAttributeModifiers.java | 6 +++--- .../properties/item/ItemAttributeModifiers.java | 2 +- .../denizen/utilities/AttributeUtil.java | 14 ++++++++------ 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/plugin/src/main/java/com/denizenscript/denizen/objects/properties/entity/EntityAttributeModifiers.java b/plugin/src/main/java/com/denizenscript/denizen/objects/properties/entity/EntityAttributeModifiers.java index fd7902e0bd..7584f314f4 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/objects/properties/entity/EntityAttributeModifiers.java +++ b/plugin/src/main/java/com/denizenscript/denizen/objects/properties/entity/EntityAttributeModifiers.java @@ -69,7 +69,7 @@ public ListTag getAttributes() { return list; } - public ListTag getAttributeModifierList(AttributeInstance instance) { + public ListTag getAttributeModifierList(AttributeInstance instance, boolean includeDeprecated) { if (instance == null) { return null; } @@ -77,13 +77,13 @@ public ListTag getAttributeModifierList(AttributeInstance instance) { if (modifiers.isEmpty()) { return null; } - return new ListTag(modifiers, AttributeUtil::modifierToMap); + return new ListTag(modifiers, modifier -> AttributeUtil.modifierToMap(modifier, includeDeprecated)); } public MapTag getAttributeModifiers(boolean includeDeprecated) { MapTag map = new MapTag(); for (Attribute attribute : Utilities.listTypesRaw(Attribute.class)) { - ListTag list = getAttributeModifierList(getAttributable().getAttribute(attribute)); + ListTag list = getAttributeModifierList(getAttributable().getAttribute(attribute), includeDeprecated); if (list != null) { AttributeUtil.addToMap(map, attribute, list, includeDeprecated); } diff --git a/plugin/src/main/java/com/denizenscript/denizen/objects/properties/item/ItemAttributeModifiers.java b/plugin/src/main/java/com/denizenscript/denizen/objects/properties/item/ItemAttributeModifiers.java index 33c0a9d3e4..670c526bc7 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/objects/properties/item/ItemAttributeModifiers.java +++ b/plugin/src/main/java/com/denizenscript/denizen/objects/properties/item/ItemAttributeModifiers.java @@ -77,7 +77,7 @@ public static MapTag getAttributeModifiersFor(Multimap AttributeUtil.modifierToMap(modifier, includeDeprecated)), includeDeprecated); } return map; } diff --git a/plugin/src/main/java/com/denizenscript/denizen/utilities/AttributeUtil.java b/plugin/src/main/java/com/denizenscript/denizen/utilities/AttributeUtil.java index 451253c0bb..cff852ed27 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/utilities/AttributeUtil.java +++ b/plugin/src/main/java/com/denizenscript/denizen/utilities/AttributeUtil.java @@ -172,22 +172,24 @@ else if (keyLow.startsWith("generic_") || keyLow.startsWith("player_") || keyLow } } - public static MapTag modifierToMap(AttributeModifier modifier) { + public static MapTag modifierToMap(AttributeModifier modifier, boolean includeDeprecated) { MapTag result = new MapTag(); - result.putObject("name", new ElementTag(modifier.getName())); result.putObject("amount", new ElementTag(modifier.getAmount())); result.putObject("operation", new ElementTag(modifier.getOperation())); if (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_20)) { result.putObject("slot", new ElementTag(modifier.getSlotGroup().toString(), true)); } else { - result.putObject("slot", new ElementTag(modifier.getSlot() == null ? "any" : modifier.getSlot().name())); + result.putObject("slot", new ElementTag(modifier.getSlot() == null ? "any" : modifier.getSlot().name(), true)); } - if (AttributeUtil.MODERN_ATTRIBUTE_FORMAT) { + if (MODERN_ATTRIBUTE_FORMAT) { result.putObject("key", new ElementTag(Utilities.namespacedKeyToString(modifier.getKey()), true)); } - // TODO: remove/deprecate the UUID key - result.putObject("id", new ElementTag(modifier.getUniqueId().toString())); + // TODO: remove/deprecate these + if (!MODERN_ATTRIBUTE_FORMAT || includeDeprecated) { + result.putObject("id", new ElementTag(modifier.getUniqueId().toString(), true)); + result.putObject("name", new ElementTag(modifier.getName(), true)); + } return result; } From b99c91ca48a66ff5cc91aae266b5d2be2ccae468 Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Mon, 18 Aug 2025 15:13:25 +0100 Subject: [PATCH 4/7] Meta fixes --- .../properties/entity/EntityAttributeModifiers.java | 5 ++--- .../denizen/utilities/AttributeUtil.java | 12 ++++++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/plugin/src/main/java/com/denizenscript/denizen/objects/properties/entity/EntityAttributeModifiers.java b/plugin/src/main/java/com/denizenscript/denizen/objects/properties/entity/EntityAttributeModifiers.java index 7584f314f4..57eba6cd00 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/objects/properties/entity/EntityAttributeModifiers.java +++ b/plugin/src/main/java/com/denizenscript/denizen/objects/properties/entity/EntityAttributeModifiers.java @@ -114,10 +114,9 @@ public static void register() { // @mechanism EntityTag.attribute_modifiers // @group properties // @description - // Returns a map of all attribute modifiers on the entity, with key as the attribute name and value as a list of modifiers, - // where each modifier is a MapTag containing keys 'name', 'amount', 'slot', 'operation', and 'id'. + // Returns a map of all attribute modifiers on the entity, with keys as attribute names and values as a list of modifiers, + // see <@link language attribute modifiers> for how modifiers are formatted. // This is formatted in a way that can be sent back into the 'attribute_modifiers' mechanism. - // See also <@link language attribute modifiers>. // --> PropertyParser.registerTag(EntityAttributeModifiers.class, MapTag.class, "attribute_modifiers", (attribute, object) -> { return object.getAttributeModifiers(true); diff --git a/plugin/src/main/java/com/denizenscript/denizen/utilities/AttributeUtil.java b/plugin/src/main/java/com/denizenscript/denizen/utilities/AttributeUtil.java index cff852ed27..b21cde0d46 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/utilities/AttributeUtil.java +++ b/plugin/src/main/java/com/denizenscript/denizen/utilities/AttributeUtil.java @@ -42,8 +42,8 @@ public class AttributeUtil { // These can be modified by such mechanisms as <@link mechanism EntityTag.attribute_base_values>, <@link mechanism EntityTag.attribute_modifiers>, <@link mechanism EntityTag.add_attribute_modifiers>, // <@link mechanism EntityTag.remove_attribute_modifiers>, <@link mechanism ItemTag.attribute_modifiers>, <@link mechanism ItemTag.add_attribute_modifiers>, <@link mechanism ItemTag.remove_attribute_modifiers>, ... // - // The input format of each of the 'add' and set mechanisms is slightly complicated: a MapTag where the keys are attribute names, and values are a ListTag of modifiers, - // where each modifier is itself a MapTag with required keys 'operation' and 'amount', and additionally: + // The input format of each of the 'add' and 'set' mechanisms is slightly complicated: a MapTag where the keys are attribute names, and values are a ListTag of modifiers. + // Modifiers are MapTags with required keys 'operation' and 'amount', and additionally: // Before MC 1.21: optional 'name', 'slot', and 'id' keys. // The default ID will be randomly generated, the default name will be the attribute name. // After MC 1.21: required 'key' key, and optional 'slot'. @@ -73,14 +73,14 @@ public class AttributeUtil { // - determine <[y]> // // - // See also <@link url https://minecraft.wiki/w/Attribute#Modifiers> + // See also <@link url https://minecraft.wiki/w/Attribute#Modifiers>. // - // For a quick and dirty in-line input, you can do for example: [generic_max_health=]>] + // For a quick and dirty in-line input, you can do for example: [max_health=]>] // // For more clean/proper input, instead do something like: // // - definemap attributes: - // generic_max_health: + // max_health: // 1: // key: my_project:add_health // operation: ADD_NUMBER @@ -209,7 +209,7 @@ public static AttributeModifier parseModifier(Attribute attr, MapTag map, TagCon Debug.echoError("Attribute modifier amount '" + amount + "' is not a valid decimal number."); return null; } - if (!NMSHandler.getVersion().isAtLeast(NMSVersion.v1_21)) { + if (!MODERN_ATTRIBUTE_FORMAT) { return parseLegacyModifier(attr, map, amountValue, operationValue); } ElementTag key = map.getElement("key"); From 6afb4900e643e8e8c27935deff8e567d4bd97ad9 Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Mon, 18 Aug 2025 23:19:05 +0100 Subject: [PATCH 5/7] Cleanup `addToMap` logic --- .../entity/EntityAttributeModifiers.java | 18 +++--------------- .../item/ItemAttributeModifiers.java | 7 +------ .../denizen/utilities/AttributeUtil.java | 7 ++++++- 3 files changed, 10 insertions(+), 22 deletions(-) diff --git a/plugin/src/main/java/com/denizenscript/denizen/objects/properties/entity/EntityAttributeModifiers.java b/plugin/src/main/java/com/denizenscript/denizen/objects/properties/entity/EntityAttributeModifiers.java index 57eba6cd00..738c4a1796 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/objects/properties/entity/EntityAttributeModifiers.java +++ b/plugin/src/main/java/com/denizenscript/denizen/objects/properties/entity/EntityAttributeModifiers.java @@ -22,7 +22,6 @@ import org.bukkit.inventory.EquipmentSlot; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.UUID; @@ -69,23 +68,12 @@ public ListTag getAttributes() { return list; } - public ListTag getAttributeModifierList(AttributeInstance instance, boolean includeDeprecated) { - if (instance == null) { - return null; - } - Collection modifiers = instance.getModifiers(); - if (modifiers.isEmpty()) { - return null; - } - return new ListTag(modifiers, modifier -> AttributeUtil.modifierToMap(modifier, includeDeprecated)); - } - public MapTag getAttributeModifiers(boolean includeDeprecated) { MapTag map = new MapTag(); for (Attribute attribute : Utilities.listTypesRaw(Attribute.class)) { - ListTag list = getAttributeModifierList(getAttributable().getAttribute(attribute), includeDeprecated); - if (list != null) { - AttributeUtil.addToMap(map, attribute, list, includeDeprecated); + AttributeInstance attributeInstance = getAttributable().getAttribute(attribute); + if (attributeInstance != null) { + AttributeUtil.addToMap(map, attribute, attributeInstance.getModifiers(), includeDeprecated); } } return map; diff --git a/plugin/src/main/java/com/denizenscript/denizen/objects/properties/item/ItemAttributeModifiers.java b/plugin/src/main/java/com/denizenscript/denizen/objects/properties/item/ItemAttributeModifiers.java index 670c526bc7..af8f859106 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/objects/properties/item/ItemAttributeModifiers.java +++ b/plugin/src/main/java/com/denizenscript/denizen/objects/properties/item/ItemAttributeModifiers.java @@ -18,7 +18,6 @@ import org.bukkit.inventory.meta.ItemMeta; import java.util.ArrayList; -import java.util.Collection; import java.util.UUID; public class ItemAttributeModifiers extends ItemProperty { @@ -73,11 +72,7 @@ public static MapTag getAttributeModifiersFor(Multimap modifiers = metaMap.get(attribute); - if (modifiers.isEmpty()) { - continue; - } - AttributeUtil.addToMap(map, attribute, new ListTag(modifiers, modifier -> AttributeUtil.modifierToMap(modifier, includeDeprecated)), includeDeprecated); + AttributeUtil.addToMap(map, attribute, metaMap.get(attribute), includeDeprecated); } return map; } diff --git a/plugin/src/main/java/com/denizenscript/denizen/utilities/AttributeUtil.java b/plugin/src/main/java/com/denizenscript/denizen/utilities/AttributeUtil.java index b21cde0d46..9d25b24dcb 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/utilities/AttributeUtil.java +++ b/plugin/src/main/java/com/denizenscript/denizen/utilities/AttributeUtil.java @@ -19,6 +19,7 @@ import org.bukkit.inventory.EquipmentSlotGroup; import org.jetbrains.annotations.NotNull; +import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -107,7 +108,11 @@ public static String legacyName(Attribute attribute) { }; } - public static void addToMap(MapTag map, Attribute attribute, ListTag value, boolean includeDeprecated) { + public static void addToMap(MapTag map, Attribute attribute, Collection modifiers, boolean includeDeprecated) { + if (modifiers.isEmpty()) { + return; + } + ListTag value = new ListTag(modifiers, modifier -> modifierToMap(modifier, includeDeprecated)); if (!MODERN_ATTRIBUTE_FORMAT) { map.putObject(String.valueOf(attribute), value); return; From e1db90ac7da3e230729cf37b13f1a4fc54da0aed Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Tue, 19 Aug 2025 00:26:07 +0100 Subject: [PATCH 6/7] Meta fixes --- .../objects/properties/item/ItemAttributeModifiers.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugin/src/main/java/com/denizenscript/denizen/objects/properties/item/ItemAttributeModifiers.java b/plugin/src/main/java/com/denizenscript/denizen/objects/properties/item/ItemAttributeModifiers.java index af8f859106..fa386370a9 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/objects/properties/item/ItemAttributeModifiers.java +++ b/plugin/src/main/java/com/denizenscript/denizen/objects/properties/item/ItemAttributeModifiers.java @@ -27,10 +27,10 @@ public class ItemAttributeModifiers extends ItemProperty { // @name attribute_modifiers // @input MapTag // @description - // Controls the attribute modifiers of an item, with key as the attribute name and value as a list of modifiers, - // where each modifier is a MapTag containing keys 'name', 'amount', 'slot', 'operation', and 'id'. - // For use as a mechanism, this is a SET operation, meaning pre-existing modifiers are removed. - // For format details, refer to <@link language attribute modifiers>. + // Controls the attribute modifiers of an item, with keys as the attribute names and values as a list of modifiers, + // see <@link language attribute modifiers> for how modifiers are formatted. + // @mechanism + // This is a SET operation, meaning pre-existing modifiers are removed. // --> public static boolean describes(ItemTag item) { From cfa488569d887bd98cc06d15f68e2289edca9b16 Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Tue, 19 Aug 2025 02:26:51 +0100 Subject: [PATCH 7/7] Link to Minecraft Wiki --- .../java/com/denizenscript/denizen/utilities/AttributeUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/main/java/com/denizenscript/denizen/utilities/AttributeUtil.java b/plugin/src/main/java/com/denizenscript/denizen/utilities/AttributeUtil.java index 9d25b24dcb..cc9c12f75c 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/utilities/AttributeUtil.java +++ b/plugin/src/main/java/com/denizenscript/denizen/utilities/AttributeUtil.java @@ -53,7 +53,7 @@ public class AttributeUtil { // Valid operations: ADD_NUMBER, ADD_SCALAR, and MULTIPLY_SCALAR_1 // Valid slots (used up to MC 1.20.6): HAND, OFF_HAND, FEET, LEGS, CHEST, HEAD, ANY // Valid slot groups (used on MC 1.20.6+): <@link url https://hub.spigotmc.org/javadocs/spigot/org/bukkit/inventory/EquipmentSlotGroup.html> - // Valid attribute names are listed at <@link url https://hub.spigotmc.org/javadocs/spigot/org/bukkit/attribute/Attribute.html> + // Valid attribute names are listed at <@link url https://minecraft.wiki/w/Attribute#Attributes> // The default slot/slot group is "any". // // Operation names are based on the Bukkit enum.