From 253605061c85ce3a93c397d828cddbdaf6da7f1f Mon Sep 17 00:00:00 2001 From: Bukkit/Spigot Date: Fri, 21 Sep 2018 20:41:38 +1000 Subject: [PATCH] SPIGOT-1916: Attribute modifiers for ItemStacks By: Senmori --- .../bukkit/attribute/AttributeModifier.java | 49 ++++++- .../org/bukkit/inventory/meta/ItemMeta.java | 122 ++++++++++++++++++ 2 files changed, 168 insertions(+), 3 deletions(-) diff --git a/paper-api/src/main/java/org/bukkit/attribute/AttributeModifier.java b/paper-api/src/main/java/org/bukkit/attribute/AttributeModifier.java index 3c0c4bc5c8..a3a69ffa49 100644 --- a/paper-api/src/main/java/org/bukkit/attribute/AttributeModifier.java +++ b/paper-api/src/main/java/org/bukkit/attribute/AttributeModifier.java @@ -5,6 +5,7 @@ import java.util.Map; import java.util.UUID; import org.apache.commons.lang.Validate; import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.util.NumberConversions; /** @@ -16,20 +17,25 @@ public class AttributeModifier implements ConfigurationSerializable { private final String name; private final double amount; private final Operation operation; + private final EquipmentSlot slot; public AttributeModifier(String name, double amount, Operation operation) { this(UUID.randomUUID(), name, amount, operation); } public AttributeModifier(UUID uuid, String name, double amount, Operation operation) { - Validate.notNull(uuid, "uuid"); - Validate.notEmpty(name, "Name cannot be empty"); - Validate.notNull(operation, "operation"); + this(uuid, name, amount, operation, null); + } + public AttributeModifier(UUID uuid, String name, double amount, Operation operation, EquipmentSlot slot) { + Validate.notNull(uuid, "UUID cannot be null"); + Validate.notEmpty(name, "Name cannot be empty"); + Validate.notNull(operation, "Operation cannot be null"); this.uuid = uuid; this.name = name; this.amount = amount; this.operation = operation; + this.slot = slot; } /** @@ -68,6 +74,16 @@ public class AttributeModifier implements ConfigurationSerializable { return operation; } + /** + * Get the {@link EquipmentSlot} this AttributeModifier is active on, + * or null if this modifier is applicable for any slot. + * + * @return the slot + */ + public EquipmentSlot getSlot() { + return slot; + } + @Override public Map serialize() { Map data = new HashMap(); @@ -75,10 +91,37 @@ public class AttributeModifier implements ConfigurationSerializable { data.put("name", name); data.put("operation", operation.ordinal()); data.put("amount", amount); + if (slot != null) { + data.put("slot", slot.name()); + } return data; } + @Override + public boolean equals(Object other) { + if (!(other instanceof AttributeModifier)) { + return false; + } + AttributeModifier mod = (AttributeModifier) other; + boolean slots = (this.slot != null ? (this.slot == mod.slot) : mod.slot != null); + return this.uuid.equals(mod.uuid) && this.name.equals(mod.name) && this.amount == mod.amount && this.operation == mod.operation && slots; + } + + @Override + public String toString() { + return "AttributeModifier{" + + "uuid=" + this.uuid.toString() + + ", name=" + this.name + + ", operation=" + this.operation.name() + + ", amount=" + this.amount + + ", slot=" + (this.slot != null ? this.slot.name() : "") + + "}"; + } + public static AttributeModifier deserialize(Map args) { + if (args.containsKey("slot")) { + return new AttributeModifier(UUID.fromString((String) args.get("uuid")), (String) args.get("name"), NumberConversions.toDouble(args.get("amount")), Operation.values()[NumberConversions.toInt(args.get("operation"))], EquipmentSlot.valueOf((args.get("slot").toString().toUpperCase()))); + } return new AttributeModifier(UUID.fromString((String) args.get("uuid")), (String) args.get("name"), NumberConversions.toDouble(args.get("amount")), Operation.values()[NumberConversions.toInt(args.get("operation"))]); } diff --git a/paper-api/src/main/java/org/bukkit/inventory/meta/ItemMeta.java b/paper-api/src/main/java/org/bukkit/inventory/meta/ItemMeta.java index 76d43266dd..73bdcebdf5 100644 --- a/paper-api/src/main/java/org/bukkit/inventory/meta/ItemMeta.java +++ b/paper-api/src/main/java/org/bukkit/inventory/meta/ItemMeta.java @@ -1,11 +1,16 @@ package org.bukkit.inventory.meta; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; +import com.google.common.collect.Multimap; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeModifier; import org.bukkit.configuration.serialization.ConfigurationSerializable; import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemFlag; /** @@ -13,6 +18,8 @@ import org.bukkit.inventory.ItemFlag; *

* An implementation will handle the creation and application for ItemMeta. * This class should not be implemented by a plugin in a live environment. + *

+ * Attribute related APIs are draft API */ public interface ItemMeta extends Cloneable, ConfigurationSerializable { @@ -194,6 +201,121 @@ public interface ItemMeta extends Cloneable, ConfigurationSerializable { */ void setUnbreakable(boolean unbreakable); + /** + * Checks for the existence of any AttributeModifiers. + * + * @return true if any AttributeModifiers exist + */ + boolean hasAttributeModifiers(); + + /** + * Return an immutable copy of all Attributes and + * their modifiers in this ItemMeta.
+ * Returns null if none exist. + * + * @return an immutable {@link Multimap} of Attributes + * and their AttributeModifiers, or null if none exist + */ + Multimap getAttributeModifiers(); + + /** + * Return an immutable copy of all {@link Attribute}s and their + * {@link AttributeModifier}s for a given {@link EquipmentSlot}.
+ * Any {@link AttributeModifier} that does have have a given + * {@link EquipmentSlot} will be returned. This is because + * AttributeModifiers without a slot are active in any slot.
+ * If there are no attributes set for the given slot, an empty map + * will be returned. + * + * @param slot the {@link EquipmentSlot} to check + * @return the immutable {@link Multimap} with the + * respective Attributes and modifiers, or an empty map + * if no attributes are set. + */ + Multimap getAttributeModifiers(EquipmentSlot slot); + + /** + * Return an immutable copy of all {@link AttributeModifier}s + * for a given {@link Attribute} + * + * @param attribute the {@link Attribute} + * @return an immutable collection of {@link AttributeModifier}s + * or null if no AttributeModifiers exist for the Attribute. + * @throws NullPointerException if Attribute is null + */ + Collection getAttributeModifiers(Attribute attribute); + + /** + * Add an Attribute and it's Modifier. + * AttributeModifiers can now support {@link EquipmentSlot}s. + * If not set, the {@link AttributeModifier} will be active in ALL slots. + *
+ * Two {@link AttributeModifier}s that have the same {@link java.util.UUID} + * cannot exist on the same Attribute. + * + * @param attribute the {@link Attribute} to modify + * @param modifier the {@link AttributeModifier} specifying the modification + * @return true if the Attribute and AttributeModifier were + * successfully added + * @throws NullPointerException if Attribute is null + * @throws NullPointerException if AttributeModifier is null + * @throws IllegalArgumentException if AttributeModifier already exists + */ + boolean addAttributeModifier(Attribute attribute, AttributeModifier modifier); + + /** + * Set all {@link Attribute}s and their {@link AttributeModifier}s. + * To clear all currently set Attributes and AttributeModifiers use + * null or an empty Multimap. + * If not null nor empty, this will filter all entries that are not-null + * and add them to the ItemStack. + * + * @param attributeModifiers the new Multimap containing the Attributes + * and their AttributeModifiers + */ + void setAttributeModifiers(Multimap attributeModifiers); + + /** + * Remove all {@link AttributeModifier}s associated with the given + * {@link Attribute}. + * This will return false if nothing was removed. + * + * @param attribute attribute to remove + * @return true if all modifiers were removed from a given + * Attribute. Returns false if no attributes were + * removed. + * @throws NullPointerException if Attribute is null + */ + boolean removeAttributeModifier(Attribute attribute); + + /** + * Remove all {@link Attribute}s and {@link AttributeModifier}s for a + * given {@link EquipmentSlot}.
+ * If the given {@link EquipmentSlot} is null, this will remove all + * {@link AttributeModifier}s that do not have an EquipmentSlot set. + * + * @param slot the {@link EquipmentSlot} to clear all Attributes and + * their modifiers for + * @return true if all modifiers were removed that match the given + * EquipmentSlot. + */ + boolean removeAttributeModifier(EquipmentSlot slot); + + /** + * Remove a specific {@link Attribute} and {@link AttributeModifier}. + * AttributeModifiers are matched according to their {@link java.util.UUID}. + * + * @param attribute the {@link Attribute} to remove + * @param modifier the {@link AttributeModifier} to remove + * @return if any attribute modifiers were remove + * + * @see AttributeModifier#getUniqueId() + * + * @throws NullPointerException if the Attribute is null + * @throws NullPointerException if the AttributeModifier is null + */ + boolean removeAttributeModifier(Attribute attribute, AttributeModifier modifier); + @SuppressWarnings("javadoc") ItemMeta clone(); }