From 42f88f72b52d6060f2cd187aceccc983db540d3b Mon Sep 17 00:00:00 2001 From: Bjarne Koll Date: Thu, 25 Apr 2019 14:36:46 +1000 Subject: [PATCH] Replace ItemTag API with new API that also expands to Tiles and Entities --- nms-patches/Entity.patch | 36 +- nms-patches/TileEntity.patch | 44 ++- .../block/CraftBlockEntityState.java | 9 +- .../craftbukkit/entity/CraftEntity.java | 22 ++ .../craftbukkit/inventory/CraftMetaItem.java | 36 +- .../tags/CraftCustomItemTagContainer.java | 133 -------- .../tags/CraftItemTagAdapterContext.java | 24 -- .../tags/DeprecatedContainerTagType.java | 48 +++ .../tags/DeprecatedCustomTagContainer.java | 68 ++++ .../tags/DeprecatedItemAdapterContext.java | 24 ++ .../inventory/tags/DeprecatedItemTagType.java | 34 ++ .../CraftPersistentDataAdapterContext.java | 22 ++ .../CraftPersistentDataContainer.java | 142 ++++++++ .../CraftPersistentDataTypeRegistry.java} | 50 +-- ...=> DeprecatedItemMetaCustomValueTest.java} | 13 +- .../PersistentDataContainerTest.java | 313 ++++++++++++++++++ 16 files changed, 797 insertions(+), 221 deletions(-) delete mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/tags/CraftCustomItemTagContainer.java delete mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/tags/CraftItemTagAdapterContext.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/tags/DeprecatedContainerTagType.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/tags/DeprecatedCustomTagContainer.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/tags/DeprecatedItemAdapterContext.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/tags/DeprecatedItemTagType.java create mode 100644 src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataAdapterContext.java create mode 100644 src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java rename src/main/java/org/bukkit/craftbukkit/{inventory/CraftCustomTagTypeRegistry.java => persistence/CraftPersistentDataTypeRegistry.java} (83%) rename src/test/java/org/bukkit/craftbukkit/inventory/{ItemMetaCustomValueTest.java => DeprecatedItemMetaCustomValueTest.java} (94%) create mode 100644 src/test/java/org/bukkit/craftbukkit/inventory/PersistentDataContainerTest.java diff --git a/nms-patches/Entity.patch b/nms-patches/Entity.patch index 7e5753c529..2443b740ee 100644 --- a/nms-patches/Entity.patch +++ b/nms-patches/Entity.patch @@ -319,7 +319,19 @@ IChatBaseComponent ichatbasecomponent = this.getCustomName(); if (ichatbasecomponent != null) { -@@ -1414,6 +1595,42 @@ +@@ -1331,6 +1512,11 @@ + } + } + ++ // CraftBukkit start - stores eventually existing bukkit values ++ if (this.bukkitEntity != null) { ++ this.bukkitEntity.storeBukkitValues(nbttagcompound); ++ } ++ // CraftBukkit end + return nbttagcompound; + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Saving entity NBT"); +@@ -1414,6 +1600,42 @@ } else { throw new IllegalStateException("Entity has invalid position"); } @@ -362,7 +374,7 @@ } catch (Throwable throwable) { CrashReport crashreport = CrashReport.a(throwable, "Loading entity NBT"); CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity being loaded"); -@@ -1489,9 +1706,22 @@ +@@ -1489,9 +1711,22 @@ } else if (this.world.isClientSide) { return null; } else { @@ -385,7 +397,7 @@ this.world.addEntity(entityitem); return entityitem; } -@@ -1595,7 +1825,7 @@ +@@ -1595,7 +1830,7 @@ } this.vehicle = entity; @@ -394,7 +406,7 @@ return true; } } -@@ -1620,15 +1850,36 @@ +@@ -1620,15 +1855,36 @@ Entity entity = this.vehicle; this.vehicle = null; @@ -433,7 +445,7 @@ if (!this.world.isClientSide && entity instanceof EntityHuman && !(this.getRidingPassenger() instanceof EntityHuman)) { this.passengers.add(0, entity); } else { -@@ -1636,15 +1887,33 @@ +@@ -1636,15 +1892,33 @@ } } @@ -468,7 +480,7 @@ } protected boolean q(Entity entity) { -@@ -1687,7 +1956,7 @@ +@@ -1687,7 +1961,7 @@ int i = this.ab(); if (this.ai) { @@ -477,7 +489,7 @@ this.world.getMethodProfiler().enter("portal"); this.aj = i; this.portalCooldown = this.aW(); -@@ -1771,6 +2040,13 @@ +@@ -1771,6 +2045,13 @@ } public void setSwimming(boolean flag) { @@ -491,7 +503,7 @@ this.setFlag(4, flag); } -@@ -1831,16 +2107,56 @@ +@@ -1831,16 +2112,56 @@ } public void setAirTicks(int i) { @@ -551,7 +563,7 @@ } public void j(boolean flag) { -@@ -1988,20 +2304,33 @@ +@@ -1988,20 +2309,33 @@ @Nullable public Entity a(DimensionManager dimensionmanager) { @@ -588,7 +600,7 @@ if (dimensionmanager1 == DimensionManager.THE_END && dimensionmanager == DimensionManager.OVERWORLD) { blockposition = worldserver1.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING_NO_LEAVES, worldserver1.getSpawn()); } else if (dimensionmanager == DimensionManager.THE_END) { -@@ -2039,6 +2368,25 @@ +@@ -2039,6 +2373,25 @@ vec3d = shapedetector_c.b; f = (float) shapedetector_c.c; } @@ -614,7 +626,7 @@ this.world.getMethodProfiler().exitEnter("reloading"); Entity entity = this.getEntityType().a((World) worldserver1); -@@ -2048,6 +2396,14 @@ +@@ -2048,6 +2401,14 @@ entity.setPositionRotation(blockposition, entity.yaw + f, entity.pitch); entity.setMot(vec3d); worldserver1.addEntityTeleport(entity); @@ -629,7 +641,7 @@ } this.dead = true; -@@ -2239,7 +2595,26 @@ +@@ -2239,7 +2600,26 @@ } public void a(AxisAlignedBB axisalignedbb) { diff --git a/nms-patches/TileEntity.patch b/nms-patches/TileEntity.patch index 0fc1669413..2f11fbf960 100644 --- a/nms-patches/TileEntity.patch +++ b/nms-patches/TileEntity.patch @@ -1,15 +1,47 @@ --- a/net/minecraft/server/TileEntity.java +++ b/net/minecraft/server/TileEntity.java -@@ -4,6 +4,8 @@ +@@ -3,9 +3,18 @@ + import javax.annotation.Nullable; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; ++// CraftBukkit start ++import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer; ++import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry; ++import org.bukkit.inventory.InventoryHolder; ++// CraftBukkit end -+import org.bukkit.inventory.InventoryHolder; // CraftBukkit -+ public abstract class TileEntity { ++ // CraftBukkit start - data containers ++ private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry(); ++ public final CraftPersistentDataContainer persistentDataContainer = new CraftPersistentDataContainer(DATA_TYPE_REGISTRY); ++ // CraftBukkit end private static final Logger LOGGER = LogManager.getLogger(); -@@ -55,8 +57,15 @@ + private final TileEntityTypes b; + @Nullable +@@ -35,6 +44,12 @@ + + public void load(NBTTagCompound nbttagcompound) { + this.position = new BlockPosition(nbttagcompound.getInt("x"), nbttagcompound.getInt("y"), nbttagcompound.getInt("z")); ++ // CraftBukkit start - read container ++ NBTTagCompound persistentDataTag = nbttagcompound.getCompound("PublicBukkitValues"); ++ if (persistentDataTag != null) { ++ this.persistentDataContainer.putAll(persistentDataTag); ++ } ++ // CraftBukkit end + } + + public NBTTagCompound save(NBTTagCompound nbttagcompound) { +@@ -51,12 +66,24 @@ + nbttagcompound.setInt("x", this.position.getX()); + nbttagcompound.setInt("y", this.position.getY()); + nbttagcompound.setInt("z", this.position.getZ()); ++ // CraftBukkit start - store container ++ if (!this.persistentDataContainer.isEmpty()) { ++ nbttagcompound.set("PublicBukkitValues", this.persistentDataContainer.toTagCompound()); ++ } ++ // CraftBukkit end + return nbttagcompound; } } @@ -25,7 +57,7 @@ String s = nbttagcompound.getString("id"); return (TileEntity) IRegistry.BLOCK_ENTITY_TYPE.getOptional(new MinecraftKey(s)).map((tileentitytypes) -> { -@@ -68,6 +77,7 @@ +@@ -68,6 +95,7 @@ } }).map((tileentity) -> { try { @@ -33,7 +65,7 @@ tileentity.load(nbttagcompound); return tileentity; } catch (Throwable throwable) { -@@ -157,4 +167,13 @@ +@@ -157,4 +185,13 @@ public TileEntityTypes q() { return this.b; } diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java index 8617fac6b0..0a28e3c081 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java @@ -6,9 +6,11 @@ import net.minecraft.server.TileEntity; import net.minecraft.server.World; import org.bukkit.Material; import org.bukkit.block.Block; +import org.bukkit.block.TileState; import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.persistence.PersistentDataContainer; -public class CraftBlockEntityState extends CraftBlockState { +public class CraftBlockEntityState extends CraftBlockState implements TileState { private final Class tileEntityClass; private final T tileEntity; @@ -118,4 +120,9 @@ public class CraftBlockEntityState extends CraftBlockState return result; } + + @Override + public PersistentDataContainer getPersistentDataContainer() { + return this.getSnapshot().persistentDataContainer; + } } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java index c0ae544c23..50774710ac 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -146,6 +146,8 @@ import org.bukkit.block.PistonMoveReaction; import org.bukkit.craftbukkit.CraftServer; import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer; +import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry; import org.bukkit.craftbukkit.util.CraftChatMessage; import org.bukkit.craftbukkit.util.CraftVector; import org.bukkit.event.entity.EntityDamageEvent; @@ -163,10 +165,12 @@ import org.bukkit.util.Vector; public abstract class CraftEntity implements org.bukkit.entity.Entity { private static PermissibleBase perm; + private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry(); protected final CraftServer server; protected Entity entity; private EntityDamageEvent lastDamageEvent; + private final CraftPersistentDataContainer persistentDataContainer = new CraftPersistentDataContainer(DATA_TYPE_REGISTRY); public CraftEntity(final CraftServer server, final Entity entity) { this.server = server; @@ -884,6 +888,24 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { return CraftBlock.notchToBlockFace(getHandle().getAdjustedDirection()); } + @Override + public CraftPersistentDataContainer getPersistentDataContainer() { + return persistentDataContainer; + } + + public void storeBukkitValues(NBTTagCompound c) { + if (!this.persistentDataContainer.isEmpty()) { + c.set("BukkitValues", this.persistentDataContainer.toTagCompound()); + } + } + + public void readBukkitValues(NBTTagCompound c) { + NBTTagCompound base = c.getCompound("BukkitValues"); + if (base != null) { + this.persistentDataContainer.putAll(base); + } + } + protected NBTTagCompound save() { NBTTagCompound nbttagcompound = new NBTTagCompound(); diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java index 1dc988f695..d9a3b5467b 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java @@ -62,7 +62,9 @@ import org.bukkit.craftbukkit.attribute.CraftAttributeInstance; import org.bukkit.craftbukkit.attribute.CraftAttributeMap; import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.craftbukkit.inventory.CraftMetaItem.ItemMetaKey.Specific; -import org.bukkit.craftbukkit.inventory.tags.CraftCustomItemTagContainer; +import org.bukkit.craftbukkit.inventory.tags.DeprecatedCustomTagContainer; +import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer; +import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry; import org.bukkit.craftbukkit.util.CraftChatMessage; import org.bukkit.craftbukkit.util.CraftMagicNumbers; import org.bukkit.craftbukkit.util.CraftNBTTagConfigSerializer; @@ -75,6 +77,7 @@ import org.bukkit.inventory.meta.Damageable; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.Repairable; import org.bukkit.inventory.meta.tags.CustomItemTagContainer; +import org.bukkit.persistence.PersistentDataContainer; /** * Children must include the following: @@ -267,11 +270,11 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { private int damage; private static final Set HANDLED_TAGS = Sets.newHashSet(); - private static final CraftCustomTagTypeRegistry TAG_TYPE_REGISTRY = new CraftCustomTagTypeRegistry(); + private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry(); private NBTTagCompound internalTag; private final Map unhandledTags = new HashMap(); - private final CraftCustomItemTagContainer publicItemTagContainer = new CraftCustomItemTagContainer(TAG_TYPE_REGISTRY); + private final CraftPersistentDataContainer persistentDataContainer = new CraftPersistentDataContainer(DATA_TYPE_REGISTRY); private int version = CraftMagicNumbers.INSTANCE.getDataVersion(); // Internal use only @@ -303,7 +306,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { this.unbreakable = meta.unbreakable; this.damage = meta.damage; this.unhandledTags.putAll(meta.unhandledTags); - this.publicItemTagContainer.putAll(meta.publicItemTagContainer.getRaw()); + this.persistentDataContainer.putAll(meta.persistentDataContainer.getRaw()); this.internalTag = meta.internalTag; if (this.internalTag != null) { @@ -375,7 +378,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { NBTTagCompound compound = tag.getCompound(BUKKIT_CUSTOM_TAG.NBT); Set keys = compound.getKeys(); for (String key : keys) { - publicItemTagContainer.put(key, compound.get(key)); + persistentDataContainer.put(key, compound.get(key)); } } @@ -534,7 +537,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { Map nbtMap = SerializableMeta.getObject(Map.class, map, BUKKIT_CUSTOM_TAG.BUKKIT, true); if (nbtMap != null) { - this.publicItemTagContainer.putAll((NBTTagCompound) CraftNBTTagConfigSerializer.deserialize(nbtMap)); + this.persistentDataContainer.putAll((NBTTagCompound) CraftNBTTagConfigSerializer.deserialize(nbtMap)); } } @@ -648,9 +651,9 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { itemTag.set(e.getKey(), e.getValue()); } - if (!publicItemTagContainer.isEmpty()) { + if (!persistentDataContainer.isEmpty()) { NBTTagCompound bukkitCustomCompound = new NBTTagCompound(); - Map rawPublicMap = publicItemTagContainer.getRaw(); + Map rawPublicMap = persistentDataContainer.getRaw(); for (Map.Entry nbtBaseEntry : rawPublicMap.entrySet()) { bukkitCustomCompound.set(nbtBaseEntry.getKey(), nbtBaseEntry.getValue()); @@ -741,7 +744,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { @Overridden boolean isEmpty() { - return !(hasDisplayName() || hasLocalizedName() || hasEnchants() || hasLore() || hasCustomModelData() || hasBlockData() || hasRepairCost() || !unhandledTags.isEmpty() || !publicItemTagContainer.isEmpty() || hideFlag != 0 || isUnbreakable() || hasDamage() || hasAttributeModifiers()); + return !(hasDisplayName() || hasLocalizedName() || hasEnchants() || hasLore() || hasCustomModelData() || hasBlockData() || hasRepairCost() || !unhandledTags.isEmpty() || !persistentDataContainer.isEmpty() || hideFlag != 0 || isUnbreakable() || hasDamage() || hasAttributeModifiers()); } public String getDisplayName() { @@ -1041,7 +1044,12 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { @Override public CustomItemTagContainer getCustomTagContainer() { - return this.publicItemTagContainer; + return new DeprecatedCustomTagContainer(this.getPersistentDataContainer()); + } + + @Override + public PersistentDataContainer getPersistentDataContainer() { + return this.persistentDataContainer; } private static boolean compareModifiers(Multimap first, Multimap second) { @@ -1106,7 +1114,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { && (this.hasRepairCost() ? that.hasRepairCost() && this.repairCost == that.repairCost : !that.hasRepairCost()) && (this.hasAttributeModifiers() ? that.hasAttributeModifiers() && compareModifiers(this.attributeModifiers, that.attributeModifiers) : !that.hasAttributeModifiers()) && (this.unhandledTags.equals(that.unhandledTags)) - && (this.publicItemTagContainer.equals(that.publicItemTagContainer)) + && (this.persistentDataContainer.equals(that.persistentDataContainer)) && (this.hideFlag == that.hideFlag) && (this.isUnbreakable() == that.isUnbreakable()) && (this.hasDamage() ? that.hasDamage() && this.damage == that.damage : !that.hasDamage()) @@ -1139,7 +1147,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { hash = 61 * hash + (hasEnchants() ? this.enchantments.hashCode() : 0); hash = 61 * hash + (hasRepairCost() ? this.repairCost : 0); hash = 61 * hash + unhandledTags.hashCode(); - hash = 61 * hash + (!publicItemTagContainer.isEmpty() ? publicItemTagContainer.hashCode() : 0); + hash = 61 * hash + (!persistentDataContainer.isEmpty() ? persistentDataContainer.hashCode() : 0); hash = 61 * hash + hideFlag; hash = 61 * hash + (isUnbreakable() ? 1231 : 1237); hash = 61 * hash + (hasDamage() ? this.damage : 0); @@ -1240,8 +1248,8 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { } } - if (!publicItemTagContainer.isEmpty()) { // Store custom tags, wrapped in their compound - builder.put(BUKKIT_CUSTOM_TAG.BUKKIT, publicItemTagContainer.serialize()); + if (!persistentDataContainer.isEmpty()) { // Store custom tags, wrapped in their compound + builder.put(BUKKIT_CUSTOM_TAG.BUKKIT, persistentDataContainer.serialize()); } return builder; diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/tags/CraftCustomItemTagContainer.java b/src/main/java/org/bukkit/craftbukkit/inventory/tags/CraftCustomItemTagContainer.java deleted file mode 100644 index fe663bbae5..0000000000 --- a/src/main/java/org/bukkit/craftbukkit/inventory/tags/CraftCustomItemTagContainer.java +++ /dev/null @@ -1,133 +0,0 @@ -package org.bukkit.craftbukkit.inventory.tags; - -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import net.minecraft.server.NBTBase; -import net.minecraft.server.NBTTagCompound; -import org.apache.commons.lang.Validate; -import org.bukkit.NamespacedKey; -import org.bukkit.craftbukkit.inventory.CraftCustomTagTypeRegistry; -import org.bukkit.craftbukkit.util.CraftNBTTagConfigSerializer; -import org.bukkit.inventory.meta.tags.CustomItemTagContainer; -import org.bukkit.inventory.meta.tags.ItemTagAdapterContext; -import org.bukkit.inventory.meta.tags.ItemTagType; - -public final class CraftCustomItemTagContainer implements CustomItemTagContainer { - - private final Map customTags = new HashMap<>(); - private final CraftCustomTagTypeRegistry tagTypeRegistry; - private final CraftItemTagAdapterContext adapterContext; - - public CraftCustomItemTagContainer(Map customTags, CraftCustomTagTypeRegistry tagTypeRegistry) { - this(tagTypeRegistry); - this.customTags.putAll(customTags); - } - - public CraftCustomItemTagContainer(CraftCustomTagTypeRegistry tagTypeRegistry) { - this.tagTypeRegistry = tagTypeRegistry; - this.adapterContext = new CraftItemTagAdapterContext(this.tagTypeRegistry); - } - - @Override - public void setCustomTag(NamespacedKey key, ItemTagType type, Z value) { - Validate.notNull(key, "The provided key for the custom value was null"); - Validate.notNull(type, "The provided type for the custom value was null"); - Validate.notNull(value, "The provided value for the custom value was null"); - - this.customTags.put(key.toString(), tagTypeRegistry.wrap(type.getPrimitiveType(), type.toPrimitive(value, adapterContext))); - } - - @Override - public boolean hasCustomTag(NamespacedKey key, ItemTagType type) { - Validate.notNull(key, "The provided key for the custom value was null"); - Validate.notNull(type, "The provided type for the custom value was null"); - - NBTBase value = this.customTags.get(key.toString()); - if (value == null) { - return false; - } - - return tagTypeRegistry.isInstanceOf(type.getPrimitiveType(), value); - } - - @Override - public Z getCustomTag(NamespacedKey key, ItemTagType type) { - Validate.notNull(key, "The provided key for the custom value was null"); - Validate.notNull(type, "The provided type for the custom value was null"); - - NBTBase value = this.customTags.get(key.toString()); - if (value == null) { - return null; - } - - return type.fromPrimitive(tagTypeRegistry.extract(type.getPrimitiveType(), value), adapterContext); - } - - @Override - public void removeCustomTag(NamespacedKey key) { - Validate.notNull(key, "The provided key for the custom value was null"); - - this.customTags.remove(key.toString()); - } - - @Override - public boolean isEmpty() { - return this.customTags.isEmpty(); - } - - @Override - public ItemTagAdapterContext getAdapterContext() { - return this.adapterContext; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof CraftCustomItemTagContainer)) { - return false; - } - - Map myRawMap = getRaw(); - Map theirRawMap = ((CraftCustomItemTagContainer) obj).getRaw(); - - return Objects.equals(myRawMap, theirRawMap); - } - - public NBTTagCompound toTagCompound() { - NBTTagCompound tag = new NBTTagCompound(); - for (Entry entry : this.customTags.entrySet()) { - tag.set(entry.getKey(), entry.getValue()); - } - return tag; - } - - public void put(String key, NBTBase base) { - this.customTags.put(key, base); - } - - public void putAll(Map map) { - this.customTags.putAll(map); - } - - public void putAll(NBTTagCompound compound) { - for (String key : compound.getKeys()) { - this.customTags.put(key, compound.get(key)); - } - } - - public Map getRaw() { - return this.customTags; - } - - @Override - public int hashCode() { - int hashCode = 3; - hashCode += this.customTags.hashCode(); // We will simply add the maps hashcode - return hashCode; - } - - public Map serialize() { - return (Map) CraftNBTTagConfigSerializer.serialize(toTagCompound()); - } -} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/tags/CraftItemTagAdapterContext.java b/src/main/java/org/bukkit/craftbukkit/inventory/tags/CraftItemTagAdapterContext.java deleted file mode 100644 index 5b81cad1f4..0000000000 --- a/src/main/java/org/bukkit/craftbukkit/inventory/tags/CraftItemTagAdapterContext.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.bukkit.craftbukkit.inventory.tags; - -import org.bukkit.craftbukkit.inventory.CraftCustomTagTypeRegistry; -import org.bukkit.inventory.meta.tags.CustomItemTagContainer; -import org.bukkit.inventory.meta.tags.ItemTagAdapterContext; - -public final class CraftItemTagAdapterContext implements ItemTagAdapterContext { - - private final CraftCustomTagTypeRegistry registry; - - public CraftItemTagAdapterContext(CraftCustomTagTypeRegistry registry) { - this.registry = registry; - } - - /** - * Creates a new and empty tag container instance - * - * @return the fresh container instance - */ - @Override - public CustomItemTagContainer newTagContainer() { - return new CraftCustomItemTagContainer(this.registry); - } -} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/tags/DeprecatedContainerTagType.java b/src/main/java/org/bukkit/craftbukkit/inventory/tags/DeprecatedContainerTagType.java new file mode 100644 index 0000000000..b2d113b483 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/tags/DeprecatedContainerTagType.java @@ -0,0 +1,48 @@ +package org.bukkit.craftbukkit.inventory.tags; + +import org.apache.commons.lang3.Validate; +import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer; +import org.bukkit.inventory.meta.tags.CustomItemTagContainer; +import org.bukkit.inventory.meta.tags.ItemTagType; +import org.bukkit.persistence.PersistentDataAdapterContext; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; + +public final class DeprecatedContainerTagType implements PersistentDataType { + + private final ItemTagType deprecated; + + DeprecatedContainerTagType(ItemTagType deprecated) { + this.deprecated = deprecated; + } + + @Override + public Class getPrimitiveType() { + return PersistentDataContainer.class; + } + + @Override + public Class getComplexType() { + return deprecated.getComplexType(); + } + + @Override + public PersistentDataContainer toPrimitive(Z complex, PersistentDataAdapterContext context) { + CustomItemTagContainer deprecated = this.deprecated.toPrimitive(complex, new DeprecatedItemAdapterContext(context)); + Validate.isInstanceOf(DeprecatedCustomTagContainer.class, deprecated, "Could not wrap deprecated API due to foreign CustomItemTagContainer implementation %s", deprecated.getClass().getSimpleName()); + + DeprecatedCustomTagContainer tagContainer = (DeprecatedCustomTagContainer) deprecated; + PersistentDataContainer wrapped = tagContainer.getWrapped(); + Validate.isInstanceOf(CraftPersistentDataContainer.class, wrapped, "Could not wrap deprecated API due to wrong deprecation wrapper %s", deprecated.getClass().getSimpleName()); + + CraftPersistentDataContainer craftTagContainer = (CraftPersistentDataContainer) wrapped; + return new CraftPersistentDataContainer(craftTagContainer.getRaw(), craftTagContainer.getDataTagTypeRegistry()); + } + + @Override + public Z fromPrimitive(PersistentDataContainer primitive, PersistentDataAdapterContext context) { + Validate.isInstanceOf(CraftPersistentDataContainer.class, primitive, "Could not wrap deprecated API due to foreign PersistentMetadataContainer implementation %s", primitive.getClass().getSimpleName()); + + return this.deprecated.fromPrimitive(new DeprecatedCustomTagContainer(primitive), new DeprecatedItemAdapterContext(context)); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/tags/DeprecatedCustomTagContainer.java b/src/main/java/org/bukkit/craftbukkit/inventory/tags/DeprecatedCustomTagContainer.java new file mode 100644 index 0000000000..3ef217b082 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/tags/DeprecatedCustomTagContainer.java @@ -0,0 +1,68 @@ +package org.bukkit.craftbukkit.inventory.tags; + +import java.util.Objects; +import org.bukkit.NamespacedKey; +import org.bukkit.inventory.meta.tags.CustomItemTagContainer; +import org.bukkit.inventory.meta.tags.ItemTagAdapterContext; +import org.bukkit.inventory.meta.tags.ItemTagType; +import org.bukkit.persistence.PersistentDataContainer; + +/** + * The {@link DeprecatedCustomTagContainer} is a simply wrapper implementation + * that wraps the new api to still be usable with the old api parts. + */ +@SuppressWarnings("unchecked") +public final class DeprecatedCustomTagContainer implements CustomItemTagContainer { + + private final PersistentDataContainer wrapped; + + public DeprecatedCustomTagContainer(PersistentDataContainer wrapped) { + this.wrapped = wrapped; + } + + @Override + public void setCustomTag(NamespacedKey key, ItemTagType type, Z value) { + if (Objects.equals(CustomItemTagContainer.class, type.getPrimitiveType())) { + wrapped.set(key, new DeprecatedContainerTagType<>((ItemTagType) type), value); + } else { + wrapped.set(key, new DeprecatedItemTagType<>(type), value); + } + } + + @Override + public boolean hasCustomTag(NamespacedKey key, ItemTagType type) { + if (Objects.equals(CustomItemTagContainer.class, type.getPrimitiveType())) { + return wrapped.has(key, new DeprecatedContainerTagType<>((ItemTagType) type)); + } else { + return wrapped.has(key, new DeprecatedItemTagType<>(type)); + } + } + + @Override + public Z getCustomTag(NamespacedKey key, ItemTagType type) { + if (Objects.equals(CustomItemTagContainer.class, type.getPrimitiveType())) { + return wrapped.get(key, new DeprecatedContainerTagType<>((ItemTagType) type)); + } else { + return wrapped.get(key, new DeprecatedItemTagType<>(type)); + } + } + + @Override + public void removeCustomTag(NamespacedKey key) { + wrapped.remove(key); + } + + @Override + public boolean isEmpty() { + return wrapped.isEmpty(); + } + + @Override + public ItemTagAdapterContext getAdapterContext() { + return new DeprecatedItemAdapterContext(this.wrapped.getAdapterContext()); + } + + public PersistentDataContainer getWrapped() { + return wrapped; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/tags/DeprecatedItemAdapterContext.java b/src/main/java/org/bukkit/craftbukkit/inventory/tags/DeprecatedItemAdapterContext.java new file mode 100644 index 0000000000..e8bb75e9f4 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/tags/DeprecatedItemAdapterContext.java @@ -0,0 +1,24 @@ +package org.bukkit.craftbukkit.inventory.tags; + +import org.bukkit.inventory.meta.tags.CustomItemTagContainer; +import org.bukkit.inventory.meta.tags.ItemTagAdapterContext; +import org.bukkit.persistence.PersistentDataAdapterContext; + +public final class DeprecatedItemAdapterContext implements ItemTagAdapterContext { + + private final PersistentDataAdapterContext context; + + public DeprecatedItemAdapterContext(PersistentDataAdapterContext context) { + this.context = context; + } + + /** + * Creates a new and empty tag container instance. + * + * @return the fresh container instance + */ + @Override + public CustomItemTagContainer newTagContainer() { + return new DeprecatedCustomTagContainer(context.newPersistentDataContainer()); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/tags/DeprecatedItemTagType.java b/src/main/java/org/bukkit/craftbukkit/inventory/tags/DeprecatedItemTagType.java new file mode 100644 index 0000000000..3e3d3804e7 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/tags/DeprecatedItemTagType.java @@ -0,0 +1,34 @@ +package org.bukkit.craftbukkit.inventory.tags; + +import org.bukkit.inventory.meta.tags.ItemTagType; +import org.bukkit.persistence.PersistentDataAdapterContext; +import org.bukkit.persistence.PersistentDataType; + +public final class DeprecatedItemTagType implements PersistentDataType { + + private final ItemTagType deprecated; + + public DeprecatedItemTagType(ItemTagType deprecated) { + this.deprecated = deprecated; + } + + @Override + public Class getPrimitiveType() { + return deprecated.getPrimitiveType(); + } + + @Override + public Class getComplexType() { + return deprecated.getComplexType(); + } + + @Override + public T toPrimitive(Z complex, PersistentDataAdapterContext context) { + return this.deprecated.toPrimitive(complex, new DeprecatedItemAdapterContext(context)); + } + + @Override + public Z fromPrimitive(T primitive, PersistentDataAdapterContext context) { + return this.deprecated.fromPrimitive(primitive, new DeprecatedItemAdapterContext(context)); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataAdapterContext.java b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataAdapterContext.java new file mode 100644 index 0000000000..6371f6836d --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataAdapterContext.java @@ -0,0 +1,22 @@ +package org.bukkit.craftbukkit.persistence; + +import org.bukkit.persistence.PersistentDataAdapterContext; + +public final class CraftPersistentDataAdapterContext implements PersistentDataAdapterContext { + + private final CraftPersistentDataTypeRegistry registry; + + public CraftPersistentDataAdapterContext(CraftPersistentDataTypeRegistry registry) { + this.registry = registry; + } + + /** + * Creates a new and empty tag container instance + * + * @return the fresh container instance + */ + @Override + public CraftPersistentDataContainer newPersistentDataContainer() { + return new CraftPersistentDataContainer(this.registry); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java new file mode 100644 index 0000000000..6c6540dd3d --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java @@ -0,0 +1,142 @@ +package org.bukkit.craftbukkit.persistence; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import net.minecraft.server.NBTBase; +import net.minecraft.server.NBTTagCompound; +import org.apache.commons.lang.Validate; +import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.util.CraftNBTTagConfigSerializer; +import org.bukkit.persistence.PersistentDataAdapterContext; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; + +public final class CraftPersistentDataContainer implements PersistentDataContainer { + + private final Map customDataTags = new HashMap<>(); + private final CraftPersistentDataTypeRegistry registry; + private final CraftPersistentDataAdapterContext adapterContext; + + public CraftPersistentDataContainer(Map customTags, CraftPersistentDataTypeRegistry registry) { + this(registry); + this.customDataTags.putAll(customTags); + } + + public CraftPersistentDataContainer(CraftPersistentDataTypeRegistry registry) { + this.registry = registry; + this.adapterContext = new CraftPersistentDataAdapterContext(this.registry); + } + + @Override + public void set(NamespacedKey key, PersistentDataType type, Z value) { + Validate.notNull(key, "The provided key for the custom value was null"); + Validate.notNull(type, "The provided type for the custom value was null"); + Validate.notNull(value, "The provided value for the custom value was null"); + + this.customDataTags.put(key.toString(), registry.wrap(type.getPrimitiveType(), type.toPrimitive(value, adapterContext))); + } + + @Override + public boolean has(NamespacedKey key, PersistentDataType type) { + Validate.notNull(key, "The provided key for the custom value was null"); + Validate.notNull(type, "The provided type for the custom value was null"); + + NBTBase value = this.customDataTags.get(key.toString()); + if (value == null) { + return false; + } + + return registry.isInstanceOf(type.getPrimitiveType(), value); + } + + @Override + public Z get(NamespacedKey key, PersistentDataType type) { + Validate.notNull(key, "The provided key for the custom value was null"); + Validate.notNull(type, "The provided type for the custom value was null"); + + NBTBase value = this.customDataTags.get(key.toString()); + if (value == null) { + return null; + } + + return type.fromPrimitive(registry.extract(type.getPrimitiveType(), value), adapterContext); + } + + @Override + public Z getOrDefault(NamespacedKey key, PersistentDataType type, Z defaultValue) { + Z z = get(key, type); + return z != null ? z : defaultValue; + } + + @Override + public void remove(NamespacedKey key) { + Validate.notNull(key, "The provided key for the custom value was null"); + + this.customDataTags.remove(key.toString()); + } + + @Override + public boolean isEmpty() { + return this.customDataTags.isEmpty(); + } + + @Override + public PersistentDataAdapterContext getAdapterContext() { + return this.adapterContext; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof CraftPersistentDataContainer)) { + return false; + } + + Map myRawMap = getRaw(); + Map theirRawMap = ((CraftPersistentDataContainer) obj).getRaw(); + + return Objects.equals(myRawMap, theirRawMap); + } + + public NBTTagCompound toTagCompound() { + NBTTagCompound tag = new NBTTagCompound(); + for (Entry entry : this.customDataTags.entrySet()) { + tag.set(entry.getKey(), entry.getValue()); + } + return tag; + } + + public void put(String key, NBTBase base) { + this.customDataTags.put(key, base); + } + + public void putAll(Map map) { + this.customDataTags.putAll(map); + } + + public void putAll(NBTTagCompound compound) { + for (String key : compound.getKeys()) { + this.customDataTags.put(key, compound.get(key)); + } + } + + public Map getRaw() { + return this.customDataTags; + } + + public CraftPersistentDataTypeRegistry getDataTagTypeRegistry() { + return registry; + } + + @Override + public int hashCode() { + int hashCode = 3; + hashCode += this.customDataTags.hashCode(); // We will simply add the maps hashcode + return hashCode; + } + + public Map serialize() { + return (Map) CraftNBTTagConfigSerializer.serialize(toTagCompound()); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftCustomTagTypeRegistry.java b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataTypeRegistry.java similarity index 83% rename from src/main/java/org/bukkit/craftbukkit/inventory/CraftCustomTagTypeRegistry.java rename to src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataTypeRegistry.java index cd9aa66e31..0bf63e4593 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftCustomTagTypeRegistry.java +++ b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataTypeRegistry.java @@ -1,11 +1,11 @@ -package org.bukkit.craftbukkit.inventory; +package org.bukkit.craftbukkit.persistence; -import com.google.common.primitives.Primitives; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.function.Function; +import com.google.common.primitives.Primitives; import net.minecraft.server.NBTBase; import net.minecraft.server.NBTTagByte; import net.minecraft.server.NBTTagByteArray; @@ -19,17 +19,16 @@ import net.minecraft.server.NBTTagLongArray; import net.minecraft.server.NBTTagShort; import net.minecraft.server.NBTTagString; import org.apache.commons.lang3.Validate; -import org.bukkit.craftbukkit.inventory.tags.CraftCustomItemTagContainer; -import org.bukkit.inventory.meta.tags.CustomItemTagContainer; +import org.bukkit.persistence.PersistentDataContainer; /** * This class represents a registry that contains the used adapters for. */ -public final class CraftCustomTagTypeRegistry { +public final class CraftPersistentDataTypeRegistry { - private final Function CREATE_ADAPTER = this::createAdapter; + private final Function CREATE_ADAPTER = this::createAdapter; - private class CustomTagAdapter { + private class TagAdapter { private final Function builder; private final Function extractor; @@ -37,7 +36,7 @@ public final class CraftCustomTagTypeRegistry { private final Class primitiveType; private final Class nbtBaseType; - public CustomTagAdapter(Class primitiveType, Class nbtBaseType, Function builder, Function extractor) { + public TagAdapter(Class primitiveType, Class nbtBaseType, Function builder, Function extractor) { this.primitiveType = primitiveType; this.nbtBaseType = nbtBaseType; this.builder = builder; @@ -49,7 +48,9 @@ public final class CraftCustomTagTypeRegistry { * the expected primitive type. * * @param base the base to extract from + * * @return the value stored inside of the tag + * * @throws ClassCastException if the passed base is not an instanced of * the defined base type and therefore is not applicable to the * extractor function @@ -63,7 +64,9 @@ public final class CraftCustomTagTypeRegistry { * Builds a tag instance wrapping around the provided value object. * * @param value the value to store inside the created tag + * * @return the new tag instance + * * @throws ClassCastException if the passed value object is not of the * defined primitive type and therefore is not applicable to the builder * function @@ -77,6 +80,7 @@ public final class CraftCustomTagTypeRegistry { * Returns if the tag instance matches the adapters one. * * @param base the base to check + * * @return if the tag was an instance of the set type */ boolean isInstance(NBTBase base) { @@ -84,18 +88,20 @@ public final class CraftCustomTagTypeRegistry { } } - private final Map adapters = new HashMap<>(); + private final Map adapters = new HashMap<>(); /** * Creates a suitable adapter instance for the primitive class type * * @param type the type to create an adapter for * @param the generic type of that class + * * @return the created adapter instance + * * @throws IllegalArgumentException if no suitable tag type adapter for this * type was found */ - private CustomTagAdapter createAdapter(Class type) { + private TagAdapter createAdapter(Class type) { if (!Primitives.isWrapperType(type)) { type = Primitives.wrap(type); //Make sure we will always "switch" over the wrapper types } @@ -143,12 +149,12 @@ public final class CraftCustomTagTypeRegistry { } /* - Note that this will map the interface CustomItemTagContainer directly to the CraftBukkit implementation - Passing any other instance of this form to the tag type registry will throw a ClassCastException as defined in CustomTagAdapter#build + Note that this will map the interface PersistentMetadataContainer directly to the CraftBukkit implementation + Passing any other instance of this form to the tag type registry will throw a ClassCastException as defined in TagAdapter#build */ - if (Objects.equals(CustomItemTagContainer.class, type)) { - return createAdapter(CraftCustomItemTagContainer.class, NBTTagCompound.class, CraftCustomItemTagContainer::toTagCompound, tag -> { - CraftCustomItemTagContainer container = new CraftCustomItemTagContainer(this); + if (Objects.equals(PersistentDataContainer.class, type)) { + return createAdapter(CraftPersistentDataContainer.class, NBTTagCompound.class, CraftPersistentDataContainer::toTagCompound, tag -> { + CraftPersistentDataContainer container = new CraftPersistentDataContainer(this); for (String key : tag.getKeys()) { container.put(key, tag.get(key)); } @@ -156,11 +162,11 @@ public final class CraftCustomTagTypeRegistry { }); } - throw new IllegalArgumentException("Could not find a valid CustomTagAdapter implementation for the requested type " + type.getSimpleName()); + throw new IllegalArgumentException("Could not find a valid TagAdapter implementation for the requested type " + type.getSimpleName()); } - private CustomTagAdapter createAdapter(Class primitiveType, Class nbtBaseType, Function builder, Function extractor) { - return new CustomTagAdapter<>(primitiveType, nbtBaseType, builder, extractor); + private TagAdapter createAdapter(Class primitiveType, Class nbtBaseType, Function builder, Function extractor) { + return new TagAdapter<>(primitiveType, nbtBaseType, builder, extractor); } /** @@ -169,7 +175,9 @@ public final class CraftCustomTagTypeRegistry { * @param type the type of the passed value * @param value the value to be stored in the tag * @param the generic type of the value + * * @return the created tag instance + * * @throws IllegalArgumentException if no suitable tag type adapter for this * type was found */ @@ -183,7 +191,9 @@ public final class CraftCustomTagTypeRegistry { * @param type the type of the primitive value * @param base the base instance to check * @param the generic type of the type + * * @return if the base stores values of the primitive type passed + * * @throws IllegalArgumentException if no suitable tag type adapter for this * type was found */ @@ -197,7 +207,9 @@ public final class CraftCustomTagTypeRegistry { * @param type the type of the value to extract * @param tag the tag to extract the value from * @param the generic type of the value stored inside the tag + * * @return the extracted value + * * @throws IllegalArgumentException if the passed base is not an instanced * of the defined base type and therefore is not applicable to the extractor * function @@ -207,7 +219,7 @@ public final class CraftCustomTagTypeRegistry { * type was found */ public T extract(Class type, NBTBase tag) throws ClassCastException, IllegalArgumentException { - CustomTagAdapter adapter = this.adapters.computeIfAbsent(type, CREATE_ADAPTER); + TagAdapter adapter = this.adapters.computeIfAbsent(type, CREATE_ADAPTER); Validate.isTrue(adapter.isInstance(tag), "`The found tag instance cannot store %s as it is a %s", type.getSimpleName(), tag.getClass().getSimpleName()); Object foundValue = adapter.extract(tag); diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaCustomValueTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/DeprecatedItemMetaCustomValueTest.java similarity index 94% rename from src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaCustomValueTest.java rename to src/test/java/org/bukkit/craftbukkit/inventory/DeprecatedItemMetaCustomValueTest.java index f2962949b7..0ab65ac731 100644 --- a/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaCustomValueTest.java +++ b/src/test/java/org/bukkit/craftbukkit/inventory/DeprecatedItemMetaCustomValueTest.java @@ -4,18 +4,13 @@ import static org.junit.Assert.*; import java.io.StringReader; import java.lang.reflect.Array; import java.nio.ByteBuffer; -import java.util.Arrays; import java.util.Map; import java.util.UUID; -import net.minecraft.server.NBTBase; import net.minecraft.server.NBTTagCompound; -import net.minecraft.server.NBTTagIntArray; -import net.minecraft.server.NBTTagList; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.craftbukkit.inventory.tags.CraftCustomItemTagContainer; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.tags.CustomItemTagContainer; @@ -25,7 +20,7 @@ import org.bukkit.support.AbstractTestingBase; import org.junit.Before; import org.junit.Test; -public class ItemMetaCustomValueTest extends AbstractTestingBase { +public class DeprecatedItemMetaCustomValueTest extends AbstractTestingBase { private static NamespacedKey VALID_KEY; @@ -166,12 +161,6 @@ public class ItemMetaCustomValueTest extends AbstractTestingBase { CustomItemTagContainer innerContainer = itemMeta.getCustomTagContainer().getAdapterContext().newTagContainer(); //Add a inner container innerContainer.setCustomTag(VALID_KEY, ItemTagType.LONG, 5L); itemMeta.getCustomTagContainer().setCustomTag(requestKey("custom-inner-compound"), ItemTagType.TAG_CONTAINER, innerContainer); - - Map rawMap = ((CraftCustomItemTagContainer) itemMeta.getCustomTagContainer()).getRaw(); //Adds a tag list as well (even tho is has no API yet) - NBTTagList nbtList = new NBTTagList(); - nbtList.add(new NBTTagIntArray(Arrays.asList(1, 5, 3))); - nbtList.add(new NBTTagIntArray(Arrays.asList(42, 51))); - rawMap.put("nbttaglist", nbtList); return itemMeta; } diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/PersistentDataContainerTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/PersistentDataContainerTest.java new file mode 100644 index 0000000000..22d8fbd714 --- /dev/null +++ b/src/test/java/org/bukkit/craftbukkit/inventory/PersistentDataContainerTest.java @@ -0,0 +1,313 @@ +package org.bukkit.craftbukkit.inventory; + +import static org.junit.Assert.*; +import java.io.StringReader; +import java.lang.reflect.Array; +import java.nio.ByteBuffer; +import java.util.Map; +import java.util.UUID; +import net.minecraft.server.NBTTagCompound; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.persistence.PersistentDataAdapterContext; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; +import org.bukkit.support.AbstractTestingBase; +import org.junit.Before; +import org.junit.Test; + +public class PersistentDataContainerTest extends AbstractTestingBase { + + private static NamespacedKey VALID_KEY; + + @Before + public void setup() { + VALID_KEY = new NamespacedKey("test", "validkey"); + } + + /* + Sets a test + */ + @Test(expected = IllegalArgumentException.class) + public void testSetNoAdapter() { + ItemMeta itemMeta = createNewItemMeta(); + itemMeta.getPersistentDataContainer().set(VALID_KEY, new PrimitiveTagType<>(boolean.class), true); + } + + /* + Contains a tag + */ + @Test(expected = IllegalArgumentException.class) + public void testHasNoAdapter() { + ItemMeta itemMeta = createNewItemMeta(); + itemMeta.getPersistentDataContainer().set(VALID_KEY, PersistentDataType.INTEGER, 1); // We gotta set this so we at least try to compare it + itemMeta.getPersistentDataContainer().has(VALID_KEY, new PrimitiveTagType<>(boolean.class)); + } + + /* + Getting a tag + */ + @Test(expected = IllegalArgumentException.class) + public void testGetNoAdapter() { + ItemMeta itemMeta = createNewItemMeta(); + itemMeta.getPersistentDataContainer().set(VALID_KEY, PersistentDataType.INTEGER, 1); //We gotta set this so we at least try to compare it + itemMeta.getPersistentDataContainer().get(VALID_KEY, new PrimitiveTagType<>(boolean.class)); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetWrongType() { + ItemMeta itemMeta = createNewItemMeta(); + itemMeta.getPersistentDataContainer().set(VALID_KEY, PersistentDataType.INTEGER, 1); + itemMeta.getPersistentDataContainer().get(VALID_KEY, PersistentDataType.STRING); + } + + @Test + public void testDifferentNamespace() { + NamespacedKey namespacedKeyA = new NamespacedKey("plugin-a", "damage"); + NamespacedKey namespacedKeyB = new NamespacedKey("plugin-b", "damage"); + + ItemMeta meta = createNewItemMeta(); + meta.getPersistentDataContainer().set(namespacedKeyA, PersistentDataType.LONG, 15L); + meta.getPersistentDataContainer().set(namespacedKeyB, PersistentDataType.LONG, 160L); + + assertEquals(15L, (long) meta.getPersistentDataContainer().get(namespacedKeyA, PersistentDataType.LONG)); + assertEquals(160L, (long) meta.getPersistentDataContainer().get(namespacedKeyB, PersistentDataType.LONG)); + } + + private ItemMeta createNewItemMeta() { + return Bukkit.getItemFactory().getItemMeta(Material.DIAMOND_PICKAXE); + } + + private NamespacedKey requestKey(String keyName) { + return new NamespacedKey("test-plugin", keyName.toLowerCase()); + } + + /* + Removing a tag + */ + @Test + public void testNBTTagStoring() { + CraftMetaItem itemMeta = createComplexItemMeta(); + + NBTTagCompound compound = new NBTTagCompound(); + itemMeta.applyToItem(compound); + + assertEquals(itemMeta, new CraftMetaItem(compound)); + } + + @Test + public void testMapStoring() { + CraftMetaItem itemMeta = createComplexItemMeta(); + + Map serialize = itemMeta.serialize(); + assertEquals(itemMeta, new CraftMetaItem(serialize)); + } + + @Test + public void testYAMLStoring() { + ItemStack stack = new ItemStack(Material.DIAMOND); + CraftMetaItem meta = createComplexItemMeta(); + stack.setItemMeta(meta); + + YamlConfiguration configuration = new YamlConfiguration(); + configuration.set("testpath", stack); + + String configValue = configuration.saveToString(); + YamlConfiguration loadedConfig = YamlConfiguration.loadConfiguration(new StringReader(configValue)); + + assertEquals(stack, loadedConfig.getSerializable("testpath", ItemStack.class)); + assertNotEquals(new ItemStack(Material.DIAMOND), loadedConfig.getSerializable("testpath", ItemStack.class)); + } + + @Test + public void testCorrectType() { + ItemStack stack = new ItemStack(Material.DIAMOND); + CraftMetaItem meta = createComplexItemMeta(); + + meta.getPersistentDataContainer().set(requestKey("int"), PersistentDataType.STRING, "1"); + meta.getPersistentDataContainer().set(requestKey("double"), PersistentDataType.STRING, "1.33"); + stack.setItemMeta(meta); + + YamlConfiguration configuration = new YamlConfiguration(); + configuration.set("testpath", stack); + + String configValue = configuration.saveToString(); + YamlConfiguration loadedConfig = YamlConfiguration.loadConfiguration(new StringReader(configValue)); + ItemStack newStack = loadedConfig.getSerializable("testpath", ItemStack.class); + + assertTrue(newStack.getItemMeta().getPersistentDataContainer().has(requestKey("int"), PersistentDataType.STRING)); + assertEquals(newStack.getItemMeta().getPersistentDataContainer().get(requestKey("int"), PersistentDataType.STRING), "1"); + + assertTrue(newStack.getItemMeta().getPersistentDataContainer().has(requestKey("double"), PersistentDataType.STRING)); + assertEquals(newStack.getItemMeta().getPersistentDataContainer().get(requestKey("double"), PersistentDataType.STRING), "1.33"); + } + + private CraftMetaItem createComplexItemMeta() { + CraftMetaItem itemMeta = (CraftMetaItem) createNewItemMeta(); + itemMeta.setDisplayName("Item Display Name"); + + itemMeta.getPersistentDataContainer().set(requestKey("custom-long"), PersistentDataType.LONG, 4L); //Add random primitive values + itemMeta.getPersistentDataContainer().set(requestKey("custom-byte-array"), PersistentDataType.BYTE_ARRAY, new byte[]{ + 0, 1, 2, 10 + }); + itemMeta.getPersistentDataContainer().set(requestKey("custom-string"), PersistentDataType.STRING, "Hello there world"); + itemMeta.getPersistentDataContainer().set(requestKey("custom-int"), PersistentDataType.INTEGER, 3); + itemMeta.getPersistentDataContainer().set(requestKey("custom-double"), PersistentDataType.DOUBLE, 3.123); + + PersistentDataContainer innerContainer = itemMeta.getPersistentDataContainer().getAdapterContext().newPersistentDataContainer(); //Add a inner container + innerContainer.set(VALID_KEY, PersistentDataType.LONG, 5L); + itemMeta.getPersistentDataContainer().set(requestKey("custom-inner-compound"), PersistentDataType.TAG_CONTAINER, innerContainer); + return itemMeta; + } + + /* + Test complex object storage + */ + @Test + public void storeUUIDOnItemTest() { + ItemMeta itemMeta = createNewItemMeta(); + UUIDPersistentDataType uuidPersistentDataType = new UUIDPersistentDataType(); + UUID uuid = UUID.fromString("434eea72-22a6-4c61-b5ef-945874a5c478"); + + itemMeta.getPersistentDataContainer().set(VALID_KEY, uuidPersistentDataType, uuid); + assertTrue(itemMeta.getPersistentDataContainer().has(VALID_KEY, uuidPersistentDataType)); + assertEquals(uuid, itemMeta.getPersistentDataContainer().get(VALID_KEY, uuidPersistentDataType)); + } + + @Test + public void encapsulatedContainers() { + NamespacedKey innerKey = new NamespacedKey("plugin-a", "inner"); + + ItemMeta meta = createNewItemMeta(); + PersistentDataAdapterContext context = meta.getPersistentDataContainer().getAdapterContext(); + + PersistentDataContainer thirdContainer = context.newPersistentDataContainer(); + thirdContainer.set(VALID_KEY, PersistentDataType.LONG, 3L); + + PersistentDataContainer secondContainer = context.newPersistentDataContainer(); + secondContainer.set(VALID_KEY, PersistentDataType.LONG, 2L); + secondContainer.set(innerKey, PersistentDataType.TAG_CONTAINER, thirdContainer); + + meta.getPersistentDataContainer().set(VALID_KEY, PersistentDataType.LONG, 1L); + meta.getPersistentDataContainer().set(innerKey, PersistentDataType.TAG_CONTAINER, secondContainer); + + assertEquals(3L, meta.getPersistentDataContainer() + .get(innerKey, PersistentDataType.TAG_CONTAINER) + .get(innerKey, PersistentDataType.TAG_CONTAINER) + .get(VALID_KEY, PersistentDataType.LONG).longValue()); + + assertEquals(2L, meta.getPersistentDataContainer() + .get(innerKey, PersistentDataType.TAG_CONTAINER) + .get(VALID_KEY, PersistentDataType.LONG).longValue()); + + assertEquals(1L, meta.getPersistentDataContainer() + .get(VALID_KEY, PersistentDataType.LONG).longValue()); + } + + class UUIDPersistentDataType implements PersistentDataType { + + @Override + public Class getPrimitiveType() { + return byte[].class; + } + + @Override + public Class getComplexType() { + return UUID.class; + } + + @Override + public byte[] toPrimitive(UUID complex, PersistentDataAdapterContext context) { + ByteBuffer bb = ByteBuffer.wrap(new byte[16]); + bb.putLong(complex.getMostSignificantBits()); + bb.putLong(complex.getLeastSignificantBits()); + return bb.array(); + } + + @Override + public UUID fromPrimitive(byte[] primitive, PersistentDataAdapterContext context) { + ByteBuffer bb = ByteBuffer.wrap(primitive); + long firstLong = bb.getLong(); + long secondLong = bb.getLong(); + return new UUID(firstLong, secondLong); + } + } + + @Test + public void testPrimitiveCustomTags() { + ItemMeta itemMeta = createNewItemMeta(); + + testPrimitiveCustomTag(itemMeta, PersistentDataType.BYTE, (byte) 1); + testPrimitiveCustomTag(itemMeta, PersistentDataType.SHORT, (short) 1); + testPrimitiveCustomTag(itemMeta, PersistentDataType.INTEGER, 1); + testPrimitiveCustomTag(itemMeta, PersistentDataType.LONG, 1L); + testPrimitiveCustomTag(itemMeta, PersistentDataType.FLOAT, 1.34F); + testPrimitiveCustomTag(itemMeta, PersistentDataType.DOUBLE, 151.123); + + testPrimitiveCustomTag(itemMeta, PersistentDataType.STRING, "test"); + + testPrimitiveCustomTag(itemMeta, PersistentDataType.BYTE_ARRAY, new byte[]{ + 1, 4, 2, Byte.MAX_VALUE + }); + testPrimitiveCustomTag(itemMeta, PersistentDataType.INTEGER_ARRAY, new int[]{ + 1, 4, 2, Integer.MAX_VALUE + }); + testPrimitiveCustomTag(itemMeta, PersistentDataType.LONG_ARRAY, new long[]{ + 1L, 4L, 2L, Long.MAX_VALUE + }); + } + + private void testPrimitiveCustomTag(ItemMeta meta, PersistentDataType type, Z value) { + NamespacedKey tagKey = new NamespacedKey("test", String.valueOf(type.hashCode())); + + meta.getPersistentDataContainer().set(tagKey, type, value); + assertTrue(meta.getPersistentDataContainer().has(tagKey, type)); + + Z foundValue = meta.getPersistentDataContainer().get(tagKey, type); + if (foundValue.getClass().isArray()) { // Compare arrays using reflection access + int length = Array.getLength(foundValue); + int originalLength = Array.getLength(value); + for (int i = 0; i < length && i < originalLength; i++) { + assertEquals(Array.get(value, i), Array.get(foundValue, i)); + } + } else { + assertEquals(foundValue, value); + } + + meta.getPersistentDataContainer().remove(tagKey); + assertFalse(meta.getPersistentDataContainer().has(tagKey, type)); + } + + class PrimitiveTagType implements PersistentDataType { + + private final Class primitiveType; + + PrimitiveTagType(Class primitiveType) { + this.primitiveType = primitiveType; + } + + @Override + public Class getPrimitiveType() { + return primitiveType; + } + + @Override + public Class getComplexType() { + return primitiveType; + } + + @Override + public T toPrimitive(T complex, PersistentDataAdapterContext context) { + return complex; + } + + @Override + public T fromPrimitive(T primitive, PersistentDataAdapterContext context) { + return primitive; + } + } +}