From 363251abc829ffdaf1f4a0c467d260a240173406 Mon Sep 17 00:00:00 2001 From: Marlon Pohl Date: Tue, 4 Aug 2026 17:17:33 +0200 Subject: [PATCH 1/6] Initial implementation of PersistentDataContainerView.getType This is the initial implementation of PersistentDataContainerView.getType. The method makes it possible for callees to retrieve the data type of a specific persistent data container key. --- .../PersistentDataContainerView.java | 9 +++ .../PaperPersistentDataContainerView.java | 67 +++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/paper-api/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java b/paper-api/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java index fd27d65893a2..b5a45ba6b758 100644 --- a/paper-api/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java +++ b/paper-api/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java @@ -110,6 +110,15 @@ public interface PersistentDataContainerView { */ C getOrDefault(NamespacedKey key, PersistentDataType type, C defaultValue); + /** + * Returns the type of the value that is stored on the + * {@link PersistentDataHolder} instance. + * + * @param key the key to loop up the value type of + * @return the data type or null if no value is present for the given key + */ + @Nullable PersistentDataType getType(NamespacedKey key); + /** * Get the set of keys present on this {@link PersistentDataContainer} * instance. diff --git a/paper-server/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java b/paper-server/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java index 6f78279f4e54..19a3d6639ab7 100644 --- a/paper-server/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java +++ b/paper-server/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java @@ -8,12 +8,14 @@ import java.util.HashSet; import java.util.Set; import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; import net.minecraft.nbt.NbtIo; import net.minecraft.nbt.Tag; import org.bukkit.NamespacedKey; import org.bukkit.craftbukkit.persistence.CraftPersistentDataAdapterContext; import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer; import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry; +import org.bukkit.persistence.ListPersistentDataType; import org.bukkit.persistence.PersistentDataAdapterContext; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; @@ -73,6 +75,18 @@ public C getOrDefault(final NamespacedKey key, final PersistentDataType

getType(final NamespacedKey key) { + Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null"); + + final Tag value = this.getTag(key.toString()); + if (value == null) { + return null; + } + + return getTagType(value); + } + @Override public Set getKeys() { final Set names = this.toTagCompound().keySet(); @@ -117,4 +131,57 @@ public byte[] serializeToBytes() throws IOException { return byteArrayOutput.toByteArray(); } } + + private static @Nullable PersistentDataType getTagType(final Tag value) { + return switch (value.getId()) { + case Tag.TAG_BYTE -> PersistentDataType.BYTE; + case Tag.TAG_SHORT -> PersistentDataType.SHORT; + case Tag.TAG_INT -> PersistentDataType.INTEGER; + case Tag.TAG_LONG -> PersistentDataType.LONG; + case Tag.TAG_FLOAT -> PersistentDataType.FLOAT; + case Tag.TAG_DOUBLE -> PersistentDataType.DOUBLE; + case Tag.TAG_BYTE_ARRAY -> PersistentDataType.BYTE_ARRAY; + case Tag.TAG_STRING -> PersistentDataType.STRING; + case Tag.TAG_COMPOUND -> PersistentDataType.TAG_CONTAINER; + case Tag.TAG_INT_ARRAY -> PersistentDataType.INTEGER_ARRAY; + case Tag.TAG_LONG_ARRAY -> PersistentDataType.LONG_ARRAY; + case Tag.TAG_LIST -> { + if (!(value instanceof ListTag listTag)) { + yield null; + } + + yield getListTagType(listTag); + } + default -> null; + }; + } + + private static @Nullable ListPersistentDataType getListTagType(final ListTag value) { + return switch (value.identifyRawElementType()) { + case Tag.TAG_BYTE -> PersistentDataType.LIST.bytes(); + case Tag.TAG_SHORT -> PersistentDataType.LIST.shorts(); + case Tag.TAG_INT -> PersistentDataType.LIST.integers(); + case Tag.TAG_LONG -> PersistentDataType.LIST.longs(); + case Tag.TAG_FLOAT -> PersistentDataType.LIST.floats(); + case Tag.TAG_DOUBLE -> PersistentDataType.LIST.doubles(); + case Tag.TAG_BYTE_ARRAY -> PersistentDataType.LIST.byteArrays(); + case Tag.TAG_STRING -> PersistentDataType.LIST.strings(); + case Tag.TAG_COMPOUND -> PersistentDataType.LIST.dataContainers(); + case Tag.TAG_INT_ARRAY -> PersistentDataType.LIST.integerArrays(); + case Tag.TAG_LONG_ARRAY -> PersistentDataType.LIST.longArrays(); + case Tag.TAG_LIST -> { + if (value.isEmpty()) { + yield null; + } + + final Tag nestedTag = value.getFirst(); + if (!(nestedTag instanceof ListTag nestedListTag)) { + yield null; + } + + yield getListTagType(nestedListTag); + } + default -> null; + }; + } } From d35df21fd64b8b34c18dffcc3441b201a6f450ff Mon Sep 17 00:00:00 2001 From: Marlon Pohl Date: Wed, 5 Aug 2026 10:50:21 +0200 Subject: [PATCH 2/6] Fix typo --- .../papermc/paper/persistence/PersistentDataContainerView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paper-api/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java b/paper-api/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java index b5a45ba6b758..074013ce024a 100644 --- a/paper-api/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java +++ b/paper-api/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java @@ -114,7 +114,7 @@ public interface PersistentDataContainerView { * Returns the type of the value that is stored on the * {@link PersistentDataHolder} instance. * - * @param key the key to loop up the value type of + * @param key the key to look up the value type of * @return the data type or null if no value is present for the given key */ @Nullable PersistentDataType getType(NamespacedKey key); From fd7deae388c672eebefcb0736dfb3f9654bf4e56 Mon Sep 17 00:00:00 2001 From: Marlon Pohl Date: Wed, 5 Aug 2026 14:56:31 +0200 Subject: [PATCH 3/6] Fix nested lists --- .../persistence/PaperPersistentDataContainerView.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/paper-server/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java b/paper-server/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java index 19a3d6639ab7..ad24998a491a 100644 --- a/paper-server/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java +++ b/paper-server/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java @@ -179,7 +179,12 @@ public byte[] serializeToBytes() throws IOException { yield null; } - yield getListTagType(nestedListTag); + ListPersistentDataType nestedListType = getListTagType(nestedListTag); + if (nestedListType == null) { + yield null; + } + + yield PersistentDataType.LIST.listTypeFrom(nestedListType); } default -> null; }; From c37bd9f17e2ffa65674938aed9814076b96d0b0e Mon Sep 17 00:00:00 2001 From: Marlon Pohl Date: Wed, 5 Aug 2026 14:57:19 +0200 Subject: [PATCH 4/6] Clarify when null will be returned --- .../paper/persistence/PersistentDataContainerView.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/paper-api/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java b/paper-api/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java index 074013ce024a..3b535b2ea7ba 100644 --- a/paper-api/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java +++ b/paper-api/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java @@ -113,9 +113,12 @@ public interface PersistentDataContainerView { /** * Returns the type of the value that is stored on the * {@link PersistentDataHolder} instance. + * If no value is present, the value type is unknown or an empty list is + * encountered, null will be returned. * * @param key the key to look up the value type of - * @return the data type or null if no value is present for the given key + * @return the data type or null if no value is present, the type is unknown + * or an empty list is encountered as value. */ @Nullable PersistentDataType getType(NamespacedKey key); From 47183c2f870a22b39f854616949f414225badbb1 Mon Sep 17 00:00:00 2001 From: Marlon Pohl Date: Wed, 5 Aug 2026 20:05:46 +0200 Subject: [PATCH 5/6] Clarify that the returned data type will be wrong sometimes --- .../PersistentDataContainerView.java | 19 ++++++++++++------- .../PaperPersistentDataContainerView.java | 5 +++-- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/paper-api/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java b/paper-api/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java index 3b535b2ea7ba..150e979ec9db 100644 --- a/paper-api/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java +++ b/paper-api/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java @@ -1,6 +1,7 @@ package io.papermc.paper.persistence; import java.util.Set; +import net.kyori.adventure.key.Key; import org.bukkit.NamespacedKey; import org.bukkit.persistence.PersistentDataAdapterContext; import org.bukkit.persistence.PersistentDataContainer; @@ -111,16 +112,20 @@ public interface PersistentDataContainerView { C getOrDefault(NamespacedKey key, PersistentDataType type, C defaultValue); /** - * Returns the type of the value that is stored on the - * {@link PersistentDataHolder} instance. - * If no value is present, the value type is unknown or an empty list is - * encountered, null will be returned. + * Estimates the {@link PersistentDataType} of the value. + *
+ * WARNING: This method will only return data types + * defined in {@link PersistentDataType} which directly map to a specific + * nbt tag. The return value will be wrong if the data type is plugin + * specific or not represented by a specific nbt tag. + * Use {@link #has(NamespacedKey, PersistentDataType)} if you expect a + * specific data type. * * @param key the key to look up the value type of - * @return the data type or null if no value is present, the type is unknown - * or an empty list is encountered as value. + * @return estimated data type or null if no value is present, the type is + * unknown or an empty list is encountered as value. */ - @Nullable PersistentDataType getType(NamespacedKey key); + @Nullable PersistentDataType estimatePrimitiveDataType(Key key); /** * Get the set of keys present on this {@link PersistentDataContainer} diff --git a/paper-server/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java b/paper-server/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java index ad24998a491a..5dba8fb9ebd1 100644 --- a/paper-server/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java +++ b/paper-server/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java @@ -7,6 +7,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; +import net.kyori.adventure.key.Key; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.NbtIo; @@ -76,10 +77,10 @@ public C getOrDefault(final NamespacedKey key, final PersistentDataType

getType(final NamespacedKey key) { + public @Nullable PersistentDataType estimatePrimitiveDataType(final Key key) { Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null"); - final Tag value = this.getTag(key.toString()); + final Tag value = this.getTag(key.asString()); if (value == null) { return null; } From f495330fed05a2f309cc8ec2451864a2c51e277c Mon Sep 17 00:00:00 2001 From: Marlon Pohl Date: Thu, 6 Aug 2026 10:39:58 +0200 Subject: [PATCH 6/6] Renamed method and updated javadocs --- .../paper/persistence/PersistentDataContainerView.java | 5 +++-- .../paper/persistence/PaperPersistentDataContainerView.java | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/paper-api/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java b/paper-api/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java index 150e979ec9db..544b0f37daa2 100644 --- a/paper-api/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java +++ b/paper-api/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java @@ -112,7 +112,8 @@ public interface PersistentDataContainerView { C getOrDefault(NamespacedKey key, PersistentDataType type, C defaultValue); /** - * Estimates the {@link PersistentDataType} of the value. + * Estimates the {@link PersistentDataType} of the value based on the primitive + * type that is stored for the given key. *
* WARNING: This method will only return data types * defined in {@link PersistentDataType} which directly map to a specific @@ -125,7 +126,7 @@ public interface PersistentDataContainerView { * @return estimated data type or null if no value is present, the type is * unknown or an empty list is encountered as value. */ - @Nullable PersistentDataType estimatePrimitiveDataType(Key key); + @Nullable PersistentDataType getPrimitiveStorageType(Key key); /** * Get the set of keys present on this {@link PersistentDataContainer} diff --git a/paper-server/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java b/paper-server/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java index 5dba8fb9ebd1..614a655555e2 100644 --- a/paper-server/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java +++ b/paper-server/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java @@ -77,7 +77,7 @@ public C getOrDefault(final NamespacedKey key, final PersistentDataType

estimatePrimitiveDataType(final Key key) { + public @Nullable PersistentDataType getPrimitiveStorageType(final Key key) { Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null"); final Tag value = this.getTag(key.asString());