PaperMC/patches/api/0477-Make-a-PDC-view-accessible-directly-from-ItemStack.patch
2024-09-08 22:12:36 +02:00

434 lines
18 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
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.
+ * <p>
+ * This method will only return true if the found value has the same primitive
+ * data type as the provided key.
+ * <p>
+ * 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.
+ * <p>
+ * 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 <P> the generic type of the stored primitive
+ * @param <C> 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
+ */
+ <P, C> boolean has(@NonNull NamespacedKey key, @NonNull PersistentDataType<P, C> type);
+
+ /**
+ * Returns if the persistent metadata provider has metadata registered matching
+ * the provided parameters.
+ * <p>
+ * This method will return true as long as a value with the given key exists,
+ * regardless of its type.
+ * <p>
+ * 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 <P> the generic type of the stored primitive
+ * @param <C> 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()}
+ */
+ <P, C> @Nullable C get(@NonNull NamespacedKey key, @NonNull PersistentDataType<P, C> 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 <P> the generic type of the stored primitive
+ * @param <C> 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()}
+ */
+ <P, C> @NonNull C getOrDefault(@NonNull NamespacedKey key, @NonNull PersistentDataType<P, C> 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<NamespacedKey> 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.
+ * <p>
+ * 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.
+ * <p>
+ * 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.</b>
*/
-public class ItemStack implements Cloneable, ConfigurationSerializable, Translatable, net.kyori.adventure.text.event.HoverEventSource<net.kyori.adventure.text.event.HoverEvent.ShowItem>, net.kyori.adventure.translation.Translatable { // Paper
+public class ItemStack implements Cloneable, ConfigurationSerializable, Translatable, net.kyori.adventure.text.event.HoverEventSource<net.kyori.adventure.text.event.HoverEvent.ShowItem>, 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()}
*/
<P, C> void set(@NotNull NamespacedKey key, @NotNull PersistentDataType<P, C> type, @NotNull C value);
-
- /**
- * Returns if the persistent metadata provider has metadata registered
- * matching the provided parameters.
- * <p>
- * This method will only return true if the found value has the same primitive
- * data type as the provided key.
- * <p>
- * 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.
- * <p>
- * 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 <P> the generic type of the stored primitive
- * @param <C> 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
- */
- <P, C> boolean has(@NotNull NamespacedKey key, @NotNull PersistentDataType<P, C> type);
-
- /**
- * Returns if the persistent metadata provider has metadata registered matching
- * the provided parameters.
- * <p>
- * This method will return true as long as a value with the given key exists,
- * regardless of its type.
- * <p>
- * 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 <P> the generic type of the stored primitive
- * @param <C> 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
- <P, C> C get(@NotNull NamespacedKey key, @NotNull PersistentDataType<P, C> 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 <P> the generic type of the stored primitive
- * @param <C> 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
- <P, C> C getOrDefault(@NotNull NamespacedKey key, @NotNull PersistentDataType<P, C> 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<NamespacedKey> 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.
- * <p>
- * 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.
+ * <p>Prefer using {@link io.papermc.paper.persistence.PersistentDataViewHolder} for read-only operations
+ * as it covers more types.</p>
*/
-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.