Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -110,6 +111,23 @@ public interface PersistentDataContainerView {
*/
<P, C> C getOrDefault(NamespacedKey key, PersistentDataType<P, C> type, C defaultValue);

/**
* Estimates the {@link PersistentDataType} of the value based on the primitive
* type that is stored for the given key.
* <br>
* <b>WARNING:</b> 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 estimated data type or null if no value is present, the type is
* unknown or an empty list is encountered as value.
*/
@Nullable PersistentDataType<?, ?> getPrimitiveStorageType(Key key);

/**
* Get the set of keys present on this {@link PersistentDataContainer}
* instance.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@
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;
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;
Expand Down Expand Up @@ -73,6 +76,18 @@ public <P, C> C getOrDefault(final NamespacedKey key, final PersistentDataType<P
return c != null ? c : defaultValue;
}

@Override
public @Nullable PersistentDataType<?, ?> getPrimitiveStorageType(final Key key) {
Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");

final Tag value = this.getTag(key.asString());
if (value == null) {
return null;
}

return getTagType(value);
}

@Override
public Set<NamespacedKey> getKeys() {
final Set<String> names = this.toTagCompound().keySet();
Expand Down Expand Up @@ -117,4 +132,62 @@ 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;
}

ListPersistentDataType<?, ?> nestedListType = getListTagType(nestedListTag);
if (nestedListType == null) {
yield null;
}

yield PersistentDataType.LIST.listTypeFrom(nestedListType);
}
default -> null;
};
}
}
Loading