From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Wed, 12 Jun 2024 10:29:30 -0700 Subject: [PATCH] Make a PDC view accessible directly from ItemStack diff --git a/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java b/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java new file mode 100644 index 0000000000000000000000000000000000000000..4a4949fde3f03e2ea9d2dde245f7dea119e202bf --- /dev/null +++ b/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java @@ -0,0 +1,167 @@ +package io.papermc.paper.persistence; + +import java.util.Set; +import org.bukkit.NamespacedKey; +import org.bukkit.persistence.PersistentDataAdapterContext; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataHolder; +import org.bukkit.persistence.PersistentDataType; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.ApiStatus; + +/** + * This represents a view of a persistent data container. No + * methods on this interface mutate the container. + * @see PersistentDataContainer + */ +@ApiStatus.NonExtendable +public interface PersistentDataContainerView { + + /** + * Returns if the persistent metadata provider has metadata registered + * matching the provided parameters. + *

+ * This method will only return true if the found value has the same primitive + * data type as the provided key. + *

+ * Storing a value using a custom {@link PersistentDataType} implementation + * will not store the complex data type. Therefore storing a UUID (by + * storing a byte[]) will match has("key" , + * {@link PersistentDataType#BYTE_ARRAY}). Likewise a stored byte[] will + * always match your UUID {@link PersistentDataType} even if it is not 16 + * bytes long. + *

+ * This method is only usable for custom object keys. Overwriting existing + * tags, like 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 the primative stored value has to match + * @param

the generic type of the stored primitive + * @param the generic type of the eventually created complex object + * + * @return if a value with the provided key and type exists + * + * @throws IllegalArgumentException if the key to look up is null + * @throws IllegalArgumentException if the type to cast the found object to is + * null + */ + boolean has(@NonNull NamespacedKey key, @NonNull PersistentDataType type); + + /** + * Returns if the persistent metadata provider has metadata registered matching + * the provided parameters. + *

+ * This method will return true as long as a value with the given key exists, + * regardless of its type. + *

+ * This method is only usable for custom object keys. Overwriting existing tags, + * like the display name, will not work as the values are stored using your + * namespace. + * + * @param key the key the value is stored under + * + * @return if a value with the provided key exists + * + * @throws IllegalArgumentException if the key to look up is null + */ + boolean has(@NonNull NamespacedKey key); + + /** + * Returns the metadata value that is stored on the + * {@link PersistentDataHolder} instance. + * + * @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 IllegalArgumentException if the key to look up is null + * @throws IllegalArgumentException if the type to cast the found object to is + * null + * @throws IllegalArgumentException if a value exists under the given key, + * but cannot be accessed using the given type + * @throws IllegalArgumentException if no suitable adapter was found for + * the {@link + * PersistentDataType#getPrimitiveType()} + */ + @Nullable C get(@NonNull NamespacedKey key, @NonNull PersistentDataType type); + + /** + * Returns the metadata value that is stored on the + * {@link PersistentDataHolder} instance. If the value does not exist in the + * container, the default value provided is returned. + * + * @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 defaultValue the default value to return if no value was found for + * the provided key + * @param

the generic type of the stored primitive + * @param the generic type of the eventually created complex object + * + * @return the value or the default value if no value was mapped under the + * given key + * + * @throws IllegalArgumentException if the key to look up is null + * @throws IllegalArgumentException if the type to cast the found object to is + * null + * @throws IllegalArgumentException if a value exists under the given key, + * but cannot be accessed using the given type + * @throws IllegalArgumentException if no suitable adapter was found for + * the {@link PersistentDataType#getPrimitiveType()} + */ + @NonNull C getOrDefault(@NonNull NamespacedKey key, @NonNull PersistentDataType type, @NonNull C defaultValue); + + /** + * Get the set of keys present on this {@link PersistentDataContainer} + * instance. + * + * Any changes made to the returned set will not be reflected on the + * instance. + * + * @return the key set + */ + @NonNull Set getKeys(); + + /** + * Returns if the container instance is empty, therefore has no entries + * inside it. + * + * @return the boolean + */ + boolean isEmpty(); + + /** + * Copies all values from this {@link PersistentDataContainer} to the provided + * container. + *

+ * This method only copies custom object keys. Existing tags, like the display + * name, will not be copied as the values are stored using your namespace. + * + * @param other the container to copy to + * @param replace whether to replace any matching values in the target container + * + * @throws IllegalArgumentException if the other container is null + */ + void copyTo(@NonNull PersistentDataContainer other, boolean replace); + + /** + * Returns the adapter context this tag container uses. + * + * @return the tag context + */ + @NonNull PersistentDataAdapterContext getAdapterContext(); + + /** + * Serialize this {@link PersistentDataContainer} instance to a + * byte array. + * + * @return a binary representation of this container + * @throws java.io.IOException if we fail to write this container to a byte array + */ + byte @NonNull [] serializeToBytes() throws java.io.IOException; +} diff --git a/src/main/java/io/papermc/paper/persistence/PersistentDataViewHolder.java b/src/main/java/io/papermc/paper/persistence/PersistentDataViewHolder.java new file mode 100644 index 0000000000000000000000000000000000000000..9789916f949374bfb50da535b076180700f7ae73 --- /dev/null +++ b/src/main/java/io/papermc/paper/persistence/PersistentDataViewHolder.java @@ -0,0 +1,23 @@ +package io.papermc.paper.persistence; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.jetbrains.annotations.ApiStatus; + +/** + * The {@link PersistentDataViewHolder} interface defines an object that can view + * custom persistent data on it. + */ +@ApiStatus.NonExtendable +public interface PersistentDataViewHolder { + + /** + * Returns a custom tag container view capable of viewing tags on the object. + *

+ * Note that the tags stored on this container are all stored under their + * own custom namespace therefore modifying default tags using this + * {@link PersistentDataViewHolder} is impossible. + * + * @return the persistent data container view + */ + @NonNull PersistentDataContainerView getPersistentDataContainer(); +} diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java index 4fe68a22576933676271f841a5af0ee45b90216f..b3abe3bde05d4a360e31e490bff8a859dc2bd4a6 100644 --- a/src/main/java/org/bukkit/inventory/ItemStack.java +++ b/src/main/java/org/bukkit/inventory/ItemStack.java @@ -27,7 +27,7 @@ import org.jetbrains.annotations.Nullable; * use this class to encapsulate Materials for which {@link Material#isItem()} * returns false. */ -public class ItemStack implements Cloneable, ConfigurationSerializable, Translatable, net.kyori.adventure.text.event.HoverEventSource, net.kyori.adventure.translation.Translatable { // Paper +public class ItemStack implements Cloneable, ConfigurationSerializable, Translatable, net.kyori.adventure.text.event.HoverEventSource, net.kyori.adventure.translation.Translatable, io.papermc.paper.persistence.PersistentDataViewHolder { // Paper private ItemStack craftDelegate; // Paper - always delegate to server-backed stack private MaterialData data = null; @@ -61,6 +61,13 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat } // Paper end + // Paper start - pdc + @Override + public io.papermc.paper.persistence.@NotNull PersistentDataContainerView getPersistentDataContainer() { + return this.craftDelegate.getPersistentDataContainer(); + } + // Paper end - pdc + @Utility protected ItemStack() {} diff --git a/src/main/java/org/bukkit/persistence/PersistentDataContainer.java b/src/main/java/org/bukkit/persistence/PersistentDataContainer.java index decf3b1949d4653a9fb01684b93ff91048137076..0f40eb52f6e2a5b6932cf9f223c1bbe9ab9eab1b 100644 --- a/src/main/java/org/bukkit/persistence/PersistentDataContainer.java +++ b/src/main/java/org/bukkit/persistence/PersistentDataContainer.java @@ -9,7 +9,7 @@ import org.jetbrains.annotations.Nullable; * This interface represents a map like object, capable of storing custom tags * in it. */ -public interface PersistentDataContainer { +public interface PersistentDataContainer extends io.papermc.paper.persistence.PersistentDataContainerView { // Paper - split up view and mutable /** * Stores a metadata value on the {@link PersistentDataHolder} instance. @@ -33,118 +33,7 @@ public interface PersistentDataContainer { * the {@link PersistentDataType#getPrimitiveType()} */ void set(@NotNull NamespacedKey key, @NotNull PersistentDataType type, @NotNull C value); - - /** - * Returns if the persistent metadata provider has metadata registered - * matching the provided parameters. - *

- * This method will only return true if the found value has the same primitive - * data type as the provided key. - *

- * Storing a value using a custom {@link PersistentDataType} implementation - * will not store the complex data type. Therefore storing a UUID (by - * storing a byte[]) will match has("key" , - * {@link PersistentDataType#BYTE_ARRAY}). Likewise a stored byte[] will - * always match your UUID {@link PersistentDataType} even if it is not 16 - * bytes long. - *

- * This method is only usable for custom object keys. Overwriting existing - * tags, like 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 the primative stored value has to match - * @param

the generic type of the stored primitive - * @param the generic type of the eventually created complex object - * - * @return if a value with the provided key and type exists - * - * @throws IllegalArgumentException if the key to look up is null - * @throws IllegalArgumentException if the type to cast the found object to is - * null - */ - boolean has(@NotNull NamespacedKey key, @NotNull PersistentDataType type); - - /** - * Returns if the persistent metadata provider has metadata registered matching - * the provided parameters. - *

- * This method will return true as long as a value with the given key exists, - * regardless of its type. - *

- * This method is only usable for custom object keys. Overwriting existing tags, - * like the display name, will not work as the values are stored using your - * namespace. - * - * @param key the key the value is stored under - * - * @return if a value with the provided key exists - * - * @throws IllegalArgumentException if the key to look up is null - */ - boolean has(@NotNull NamespacedKey key); - - /** - * Returns the metadata value that is stored on the - * {@link PersistentDataHolder} instance. - * - * @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 IllegalArgumentException if the key to look up is null - * @throws IllegalArgumentException if the type to cast the found object to is - * null - * @throws IllegalArgumentException if a value exists under the given key, - * but cannot be accessed using the given type - * @throws IllegalArgumentException if no suitable adapter was found for - * the {@link - * PersistentDataType#getPrimitiveType()} - */ - @Nullable - C get(@NotNull NamespacedKey key, @NotNull PersistentDataType type); - - /** - * Returns the metadata value that is stored on the - * {@link PersistentDataHolder} instance. If the value does not exist in the - * container, the default value provided is returned. - * - * @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 defaultValue the default value to return if no value was found for - * the provided key - * @param

the generic type of the stored primitive - * @param the generic type of the eventually created complex object - * - * @return the value or the default value if no value was mapped under the - * given key - * - * @throws IllegalArgumentException if the key to look up is null - * @throws IllegalArgumentException if the type to cast the found object to is - * null - * @throws IllegalArgumentException if a value exists under the given key, - * but cannot be accessed using the given type - * @throws IllegalArgumentException if no suitable adapter was found for - * the {@link PersistentDataType#getPrimitiveType()} - */ - @NotNull - C getOrDefault(@NotNull NamespacedKey key, @NotNull PersistentDataType type, @NotNull C defaultValue); - - /** - * Get the set of keys present on this {@link PersistentDataContainer} - * instance. - * - * Any changes made to the returned set will not be reflected on the - * instance. - * - * @return the key set - */ - @NotNull - Set getKeys(); + // Paper - move to PersistentDataContainerView /** * Removes a custom key from the {@link PersistentDataHolder} instance. @@ -154,47 +43,10 @@ public interface PersistentDataContainer { * @throws IllegalArgumentException if the provided key is null */ void remove(@NotNull NamespacedKey key); - - /** - * Returns if the container instance is empty, therefore has no entries - * inside it. - * - * @return the boolean - */ - boolean isEmpty(); - - /** - * Copies all values from this {@link PersistentDataContainer} to the provided - * container. - *

- * This method only copies custom object keys. Existing tags, like the display - * name, will not be copied as the values are stored using your namespace. - * - * @param other the container to copy to - * @param replace whether to replace any matching values in the target container - * - * @throws IllegalArgumentException if the other container is null - */ - void copyTo(@NotNull PersistentDataContainer other, boolean replace); - - /** - * Returns the adapter context this tag container uses. - * - * @return the tag context - */ - @NotNull - PersistentDataAdapterContext getAdapterContext(); + // Paper - move to PersistentDataContainerView // Paper start - byte array serialization - /** - * Serialize this {@link PersistentDataContainer} instance to a - * byte array. - * - * @return a binary representation of this container - * @throws java.io.IOException if we fail to write this container to a byte array - */ - byte @NotNull [] serializeToBytes() throws java.io.IOException; - + // Paper - move to PersistentDataContainerView /** * Read values from a serialised byte array into this * {@link PersistentDataContainer} instance. diff --git a/src/main/java/org/bukkit/persistence/PersistentDataHolder.java b/src/main/java/org/bukkit/persistence/PersistentDataHolder.java index 80b277cc57f092f04fbf7810ac78d250b207b775..71f33c1265bc753ef40108ffce0d4bd8656d2903 100644 --- a/src/main/java/org/bukkit/persistence/PersistentDataHolder.java +++ b/src/main/java/org/bukkit/persistence/PersistentDataHolder.java @@ -5,8 +5,10 @@ import org.jetbrains.annotations.NotNull; /** * The {@link PersistentDataHolder} interface defines an object that can store * custom persistent meta data on it. + *

Prefer using {@link io.papermc.paper.persistence.PersistentDataViewHolder} for read-only operations + * as it covers more types.

*/ -public interface PersistentDataHolder { +public interface PersistentDataHolder extends io.papermc.paper.persistence.PersistentDataViewHolder { // Paper /** * Returns a custom tag container capable of storing tags on the object.