From e08005992786cff58eff69abd055a6e76499046e Mon Sep 17 00:00:00 2001 From: Bjarne Koll Date: Sat, 1 Dec 2018 20:25:24 +1100 Subject: SPIGOT-4347: Add API to allow storing arbitrary values on ItemStacks --- .../java/org/bukkit/inventory/meta/ItemMeta.java | 17 +++ .../meta/tags/CustomItemTagContainer.java | 102 +++++++++++++++ .../inventory/meta/tags/ItemTagAdapterContext.java | 15 +++ .../bukkit/inventory/meta/tags/ItemTagType.java | 143 +++++++++++++++++++++ 4 files changed, 277 insertions(+) create mode 100644 src/main/java/org/bukkit/inventory/meta/tags/CustomItemTagContainer.java create mode 100644 src/main/java/org/bukkit/inventory/meta/tags/ItemTagAdapterContext.java create mode 100644 src/main/java/org/bukkit/inventory/meta/tags/ItemTagType.java diff --git a/src/main/java/org/bukkit/inventory/meta/ItemMeta.java b/src/main/java/org/bukkit/inventory/meta/ItemMeta.java index 031400b3..64b68b1a 100644 --- a/src/main/java/org/bukkit/inventory/meta/ItemMeta.java +++ b/src/main/java/org/bukkit/inventory/meta/ItemMeta.java @@ -12,6 +12,7 @@ import org.bukkit.configuration.serialization.ConfigurationSerializable; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.meta.tags.CustomItemTagContainer; /** * This type represents the storage mechanism for auxiliary item data. @@ -314,6 +315,22 @@ public interface ItemMeta extends Cloneable, ConfigurationSerializable { */ boolean removeAttributeModifier(Attribute attribute, AttributeModifier modifier); + /** + * Returns a public custom tag container capable of storing tags on the + * item. + * + * Those tags will be sent to the client with all of their content, so the + * client is capable of reading them. This will result in the player seeing + * a NBT Tag notification on the item. + * + * These tags can also be modified by the client once in creative mode + * + * @return the custom tag container + * @deprecated draft API + */ + @Deprecated + CustomItemTagContainer getCustomTagContainer(); + @SuppressWarnings("javadoc") ItemMeta clone(); } diff --git a/src/main/java/org/bukkit/inventory/meta/tags/CustomItemTagContainer.java b/src/main/java/org/bukkit/inventory/meta/tags/CustomItemTagContainer.java new file mode 100644 index 00000000..f80dd85d --- /dev/null +++ b/src/main/java/org/bukkit/inventory/meta/tags/CustomItemTagContainer.java @@ -0,0 +1,102 @@ +package org.bukkit.inventory.meta.tags; + +import org.bukkit.NamespacedKey; +import org.bukkit.inventory.meta.ItemMeta; + +/** + * This interface represents a map like object, capable of storing custom tags + * in it. + */ +public interface CustomItemTagContainer { + + /** + * Stores a custom value on the {@link ItemMeta}. + * + * This API cannot be used to manipulate minecraft tags, as the values will + * be stored using your namespace. This method will override any existing + * value the meta may have stored under the provided key. + * + * @param key the key this value will be stored under + * @param type the type this item tag uses + * @param value the value stored in the tag + * @param the generic java type of the tag value + * @param the generic type of the object to store + * @throws NullPointerException if the key is null + * @throws NullPointerException if the type is null + * @throws NullPointerException if the value is null. Removing a custom tag + * should be done using {@link #removeCustomTag(org.bukkit.NamespacedKey)} + * @throws IllegalArgumentException if no suitable adapter will be found for + * the {@link ItemTagType#getPrimitiveType()} + */ + void setCustomTag(NamespacedKey key, ItemTagType type, Z value); + + /** + * Returns if the item meta has a custom tag registered matching the + * provided parameters. + * + * This method will only return if the found value has the same primitive + * data type as the provided key. + * + * Storing a value using a custom {@link ItemTagType} implementation will + * not store the complex data type. Therefore storing a UUID (by storing a + * byte[]) will match hasCustomTag("key" , {@link ItemTagType#BYTE_ARRAY}). + * Likewise a stored byte[] will always match your UUID {@link ItemTagType} + * even if it is not 16 bytes long. + * + * This method is only usable for custom object keys. Overwriting existing + * tags, like the the display name, will not work as the values are stored + * using your namespace. + * + * @param key the key the value is stored under + * @param type the type which primitive storage type has to match the value + * @param the generic type of the stored primitive + * @param the generic type of the eventually created complex object + * @return if a value + * @throws NullPointerException if the key to look up is null + * @throws NullPointerException if the type to cast the found object to is + * null + */ + boolean hasCustomTag(NamespacedKey key, ItemTagType type); + + /** + * Returns the custom tag's value that is stored on the item. + * + * @param key the key to look up in the custom tag map + * @param type the type the value must have and will be casted to + * @param the generic type of the stored primitive + * @param the generic type of the eventually created complex object + * @return the value or {@code null} if no value was mapped under the given + * value + * @throws NullPointerException if the key to look up is null + * @throws NullPointerException if the type to cast the found object to is + * null + * @throws IllegalArgumentException if the value exists under the given key, + * but cannot be access using the given type + * @throws IllegalArgumentException if no suitable adapter will be found for + * the {@link ItemTagType#getPrimitiveType()} + */ + Z getCustomTag(NamespacedKey key, ItemTagType type); + + /** + * Removes a custom key from the item meta. + * + * @param key the key + * @throws NullPointerException if the provided key is null + */ + void removeCustomTag(NamespacedKey key); + + /** + * Returns if the container instance is empty, therefore has no entries + * inside it. + * + * @return the boolean + */ + boolean isEmpty(); + + /** + * Returns the adapter context this tag container uses. + * + * @return the tag context + */ + ItemTagAdapterContext getAdapterContext(); +} diff --git a/src/main/java/org/bukkit/inventory/meta/tags/ItemTagAdapterContext.java b/src/main/java/org/bukkit/inventory/meta/tags/ItemTagAdapterContext.java new file mode 100644 index 00000000..348fa65f --- /dev/null +++ b/src/main/java/org/bukkit/inventory/meta/tags/ItemTagAdapterContext.java @@ -0,0 +1,15 @@ +package org.bukkit.inventory.meta.tags; + +/** + * This interface represents the context in which the {@link ItemTagType} can + * serialize and deserialize the passed values. + */ +public interface ItemTagAdapterContext { + + /** + * Creates a new and empty tag container instance. + * + * @return the fresh container instance + */ + CustomItemTagContainer newTagContainer(); +} diff --git a/src/main/java/org/bukkit/inventory/meta/tags/ItemTagType.java b/src/main/java/org/bukkit/inventory/meta/tags/ItemTagType.java new file mode 100644 index 00000000..f8a09010 --- /dev/null +++ b/src/main/java/org/bukkit/inventory/meta/tags/ItemTagType.java @@ -0,0 +1,143 @@ +package org.bukkit.inventory.meta.tags; + +/** + * This class represents an enum with a generic content type. It defines the + * types a custom item tag can have. + *

+ * This interface can be used to create your own custom {@link ItemTagType} with + * different complex types. This may be useful for the likes of a + * UUIDItemTagType: + *

+ * {@code
+ * public class UUIDItemTagType implements ItemTagType {
+ *
+ *         {@literal @Override}
+ *         public Class getPrimitiveType() {
+ *             return byte[].class;
+ *         }
+ *
+ *         {@literal @Override}
+ *         public Class getComplexType() {
+ *             return UUID.class;
+ *         }
+ *
+ *         {@literal @Override}
+ *         public byte[] toPrimitive(UUID complex, ItemTagAdapterContext context) {
+ *             ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
+ *             bb.putLong(complex.getMostSignificantBits());
+ *             bb.putLong(complex.getLeastSignificantBits());
+ *             return bb.array();
+ *         }
+ *
+ *         {@literal @Override}
+ *         public UUID fromPrimitive(byte[] primitive, ItemTagAdapterContext context) {
+ *             ByteBuffer bb = ByteBuffer.wrap(primitive);
+ *             long firstLong = bb.getLong();
+ *             long secondLong = bb.getLong();
+ *             return new UUID(firstLong, secondLong);
+ *         }
+ *     }}
+ * + * @param the primary object type that is stored in the given tag + * @param the retrieved object type when applying this item tag type + */ +public interface ItemTagType { + + /* + The primitive one value types. + */ + ItemTagType BYTE = new PrimitiveTagType<>(Byte.class); + ItemTagType SHORT = new PrimitiveTagType<>(Short.class); + ItemTagType INTEGER = new PrimitiveTagType<>(Integer.class); + ItemTagType LONG = new PrimitiveTagType<>(Long.class); + ItemTagType FLOAT = new PrimitiveTagType<>(Float.class); + ItemTagType DOUBLE = new PrimitiveTagType<>(Double.class); + + /* + String. + */ + ItemTagType STRING = new PrimitiveTagType<>(String.class); + + /* + Primitive Arrays. + */ + ItemTagType BYTE_ARRAY = new PrimitiveTagType<>(byte[].class); + ItemTagType INTEGER_ARRAY = new PrimitiveTagType<>(int[].class); + ItemTagType LONG_ARRAY = new PrimitiveTagType<>(long[].class); + + /* + Nested TagContainer. + */ + ItemTagType TAG_CONTAINER = new PrimitiveTagType<>(CustomItemTagContainer.class); + + /** + * Returns the primitive data type of this tag. + * + * @return the class + */ + Class getPrimitiveType(); + + /** + * Returns the complex object type the primitive value resembles. + * + * @return the class type + */ + Class getComplexType(); + + /** + * Returns the primitive data that resembles the complex object passed to + * this method. + * + * @param complex the complex object instance + * @param context the context this operation is running in + * @return the primitive value + */ + T toPrimitive(Z complex, ItemTagAdapterContext context); + + /** + * Creates a complex object based of the passed primitive value + * + * @param primitive the primitive value + * @param context the context this operation is running in + * @return the complex object instance + */ + Z fromPrimitive(T primitive, ItemTagAdapterContext context); + + /** + * A default implementation that simply exists to pass on the retrieved or + * inserted value to the next layer. + * + * This implementation does not add any kind of logic, but is used to + * provide default implementations for the primitive types. + * + * @param the generic type of the primitive objects + */ + class PrimitiveTagType implements ItemTagType { + + private final Class primitiveType; + + PrimitiveTagType(Class primitiveType) { + this.primitiveType = primitiveType; + } + + @Override + public Class getPrimitiveType() { + return primitiveType; + } + + @Override + public Class getComplexType() { + return primitiveType; + } + + @Override + public T toPrimitive(T complex, ItemTagAdapterContext context) { + return complex; + } + + @Override + public T fromPrimitive(T primitive, ItemTagAdapterContext context) { + return primitive; + } + } +} -- cgit v1.2.3