From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Wed, 12 Jun 2024 10:29:40 -0700 Subject: [PATCH] Make a PDC view accessible directly from ItemStack diff --git a/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java b/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java new file mode 100644 index 0000000000000000000000000000000000000000..fac401280d3f3689b00e16c19155ca753faa06e0 --- /dev/null +++ b/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java @@ -0,0 +1,86 @@ +package io.papermc.paper.persistence; + +import com.google.common.base.Preconditions; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import net.minecraft.nbt.CompoundTag; +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.CraftPersistentDataTypeRegistry; +import org.bukkit.persistence.PersistentDataAdapterContext; +import org.bukkit.persistence.PersistentDataType; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.framework.qual.DefaultQualifier; + +@DefaultQualifier(NonNull.class) +public abstract class PaperPersistentDataContainerView implements PersistentDataContainerView { + + protected final CraftPersistentDataTypeRegistry registry; + protected final CraftPersistentDataAdapterContext adapterContext; + + public PaperPersistentDataContainerView(final CraftPersistentDataTypeRegistry registry) { + this.registry = registry; + this.adapterContext = new CraftPersistentDataAdapterContext(this.registry); + } + + public abstract @Nullable Tag getTag(final String key); + + public abstract CompoundTag toTagCompound(); + + @Override + public boolean has(final NamespacedKey key, final PersistentDataType type) { + Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null"); + Preconditions.checkArgument(type != null, "The provided type cannot be null"); + + final @Nullable Tag value = this.getTag(key.toString()); + if (value == null) { + return false; + } + + return this.registry.isInstanceOf(type, value); + } + + @Override + public boolean has(final NamespacedKey key) { + Preconditions.checkArgument(key != null, "The provided key for the custom value was null"); // Paper + return this.getTag(key.toString()) != null; + } + + @Override + public @Nullable C get(final NamespacedKey key, final PersistentDataType type) { + Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null"); + Preconditions.checkArgument(type != null, "The provided type cannot be null"); + + final @Nullable Tag value = this.getTag(key.toString()); + if (value == null) { + return null; + } + + return type.fromPrimitive(this.registry.extract(type, value), this.adapterContext); + } + + @Override + public C getOrDefault(final NamespacedKey key, final PersistentDataType type, final C defaultValue) { + final C c = this.get(key, type); + return c != null ? c : defaultValue; + } + + @Override + public PersistentDataAdapterContext getAdapterContext() { + return this.adapterContext; + } + + @Override + public byte[] serializeToBytes() throws IOException { + final net.minecraft.nbt.CompoundTag root = this.toTagCompound(); + final ByteArrayOutputStream byteArrayOutput = new ByteArrayOutputStream(); + try (final DataOutputStream dataOutput = new DataOutputStream(byteArrayOutput)) { + NbtIo.write(root, dataOutput); + return byteArrayOutput.toByteArray(); + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java index 814e8ece6821e359b504e8c4d140cc38700f2abe..32a41c8b324aad67b9dcf74387aef299e6478a64 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java @@ -480,4 +480,63 @@ public final class CraftItemStack extends ItemStack { return mirrored; } // Paper end + + // Paper start - pdc + private net.minecraft.nbt.CompoundTag getPdcTag() { + if (this.handle == null) { + return new net.minecraft.nbt.CompoundTag(); + } + final net.minecraft.world.item.component.CustomData customData = this.handle.getOrDefault(DataComponents.CUSTOM_DATA, net.minecraft.world.item.component.CustomData.EMPTY); + // getUnsafe is OK here because we are only ever *reading* the data so immutability is preserved + //noinspection deprecation + return customData.getUnsafe().getCompound("PublicBukkitValues"); + } + + private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry(); + private final io.papermc.paper.persistence.PaperPersistentDataContainerView pdcView = new io.papermc.paper.persistence.PaperPersistentDataContainerView(REGISTRY) { + + @Override + public net.minecraft.nbt.CompoundTag toTagCompound() { + return CraftItemStack.this.getPdcTag(); + } + + @Override + public net.minecraft.nbt.Tag getTag(final String key) { + return CraftItemStack.this.getPdcTag().get(key); + } + + @Override + public java.util.Set getKeys() { + java.util.Set keys = new java.util.HashSet<>(); + CraftItemStack.this.getPdcTag().getAllKeys().forEach(key -> { + final String[] keyData = key.split(":", 2); + if (keyData.length == 2) { + keys.add(new org.bukkit.NamespacedKey(keyData[0], keyData[1])); + } + }); + return java.util.Collections.unmodifiableSet(keys); + }; + + @Override + public boolean isEmpty() { + return CraftItemStack.this.getPdcTag().isEmpty(); + } + + @Override + public void copyTo(final org.bukkit.persistence.PersistentDataContainer other, final boolean replace) { + Preconditions.checkArgument(other != null, "The target container cannot be null"); + final org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer target = (org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer) other; + final net.minecraft.nbt.CompoundTag pdcTag = org.bukkit.craftbukkit.inventory.CraftItemStack.this.getPdcTag(); + for (final String key : pdcTag.getAllKeys()) { + if (replace || !target.getRaw().containsKey(key)) { + target.getRaw().put(key, pdcTag.get(key).copy()); + } + } + } + }; + @Override + public io.papermc.paper.persistence.PersistentDataContainerView getPersistentDataContainer() { + return this.pdcView; + } + // Paper end - pdc } diff --git a/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java index f55fdd57ced259ad5a95878840e98ffaa3db2e05..9d867f1659433ea15f281c8b441db7e339013100 100644 --- a/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java +++ b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java @@ -16,11 +16,10 @@ import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.NotNull; -public class CraftPersistentDataContainer implements PersistentDataContainer { +public class CraftPersistentDataContainer extends io.papermc.paper.persistence.PaperPersistentDataContainerView implements PersistentDataContainer { // Paper - split up view and mutable private final Map customDataTags = new HashMap<>(); - private final CraftPersistentDataTypeRegistry registry; - private final CraftPersistentDataAdapterContext adapterContext; + // Paper - move to PersistentDataContainerView public CraftPersistentDataContainer(Map customTags, CraftPersistentDataTypeRegistry registry) { this(registry); @@ -28,10 +27,15 @@ public class CraftPersistentDataContainer implements PersistentDataContainer { } public CraftPersistentDataContainer(CraftPersistentDataTypeRegistry registry) { - this.registry = registry; - this.adapterContext = new CraftPersistentDataAdapterContext(this.registry); + super(registry); // Paper - move to PersistentDataContainerView } + // Paper start + @Override + public Tag getTag(final String key) { + return this.customDataTags.get(key); + } + // Paper end @Override public void set(@NotNull NamespacedKey key, @NotNull PersistentDataType type, @NotNull Z value) { @@ -42,44 +46,7 @@ public class CraftPersistentDataContainer implements PersistentDataContainer { this.customDataTags.put(key.toString(), this.registry.wrap(type, type.toPrimitive(value, this.adapterContext))); } - @Override - public boolean has(@NotNull NamespacedKey key, @NotNull PersistentDataType type) { - Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null"); - Preconditions.checkArgument(type != null, "The provided type cannot be null"); - - Tag value = this.customDataTags.get(key.toString()); - if (value == null) { - return false; - } - - return this.registry.isInstanceOf(type, value); - } - - @Override - public boolean has(NamespacedKey key) { - Preconditions.checkArgument(key != null, "The provided key for the custom value was null"); // Paper - return this.customDataTags.get(key.toString()) != null; - } - - @Override - public Z get(@NotNull NamespacedKey key, @NotNull PersistentDataType type) { - Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null"); - Preconditions.checkArgument(type != null, "The provided type cannot be null"); - - Tag value = this.customDataTags.get(key.toString()); - if (value == null) { - return null; - } - - return type.fromPrimitive(this.registry.extract(type, value), this.adapterContext); - } - - @NotNull - @Override - public Z getOrDefault(@NotNull NamespacedKey key, @NotNull PersistentDataType type, @NotNull Z defaultValue) { - Z z = this.get(key, type); - return z != null ? z : defaultValue; - } + // Paper - move to PersistentDataContainerView @NotNull @Override @@ -186,16 +153,7 @@ public class CraftPersistentDataContainer implements PersistentDataContainer { // Paper end // Paper start - byte array serialization - @Override - public byte[] serializeToBytes() throws java.io.IOException { - final net.minecraft.nbt.CompoundTag root = this.toTagCompound(); - final java.io.ByteArrayOutputStream byteArrayOutput = new java.io.ByteArrayOutputStream(); - try (final java.io.DataOutputStream dataOutput = new java.io.DataOutputStream(byteArrayOutput)) { - net.minecraft.nbt.NbtIo.write(root, dataOutput); - return byteArrayOutput.toByteArray(); - } - } - + // Paper - move to PersistentDataContainerView @Override public void readFromBytes(final byte[] bytes, final boolean clear) throws java.io.IOException { if (clear) {