13
0
geforkt von Mirrors/Paper

SPIGOT-4347: Add API to allow storing arbitrary values on ItemStacks

Dieser Commit ist enthalten in:
Bjarne Koll 2018-12-01 20:26:23 +11:00 committet von md_5
Ursprung a4c555b6b1
Commit 38e4c013b6
7 geänderte Dateien mit 818 neuen und 1 gelöschten Zeilen

Datei anzeigen

@ -0,0 +1,20 @@
--- a/net/minecraft/server/MojangsonParser.java
+++ b/net/minecraft/server/MojangsonParser.java
@@ -82,7 +82,7 @@
}
}
- private NBTBase b(String s) {
+ public NBTBase b(String s) { // PAIL
try {
if (MojangsonParser.i.matcher(s).matches()) {
return new NBTTagFloat(Float.parseFloat(s.substring(0, s.length() - 1)));
@@ -207,7 +207,7 @@
}
}
- private NBTBase h() throws CommandSyntaxException {
+ public NBTBase h() throws CommandSyntaxException { // PAIL
this.a('[');
int i = this.n.getCursor();
char c0 = this.n.read();

Datei anzeigen

@ -0,0 +1,217 @@
package org.bukkit.craftbukkit.inventory;
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 net.minecraft.server.NBTBase;
import net.minecraft.server.NBTTagByte;
import net.minecraft.server.NBTTagByteArray;
import net.minecraft.server.NBTTagCompound;
import net.minecraft.server.NBTTagDouble;
import net.minecraft.server.NBTTagFloat;
import net.minecraft.server.NBTTagInt;
import net.minecraft.server.NBTTagIntArray;
import net.minecraft.server.NBTTagLong;
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;
/**
* This class represents a registry that contains the used adapters for.
*/
public final class CraftCustomTagTypeRegistry {
private final Function<Class, CustomTagAdapter> CREATE_ADAPTER = this::createAdapter;
private class CustomTagAdapter<T, Z extends NBTBase> {
private final Function<T, Z> builder;
private final Function<Z, T> extractor;
private final Class<T> primitiveType;
private final Class<Z> nbtBaseType;
public CustomTagAdapter(Class<T> primitiveType, Class<Z> nbtBaseType, Function<T, Z> builder, Function<Z, T> extractor) {
this.primitiveType = primitiveType;
this.nbtBaseType = nbtBaseType;
this.builder = builder;
this.extractor = extractor;
}
/**
* This method will extract the value stored in the tag, according to
* 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
*/
T extract(NBTBase base) {
Validate.isInstanceOf(nbtBaseType, base, "The provided NBTBase was of the type %s. Expected type %s", base.getClass().getSimpleName(), nbtBaseType.getSimpleName());
return this.extractor.apply(nbtBaseType.cast(base));
}
/**
* 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
*/
Z build(Object value) {
Validate.isInstanceOf(primitiveType, value, "The provided value was of the type %s. Expected type %s", value.getClass().getSimpleName(), primitiveType.getSimpleName());
return this.builder.apply(primitiveType.cast(value));
}
/**
* 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) {
return this.nbtBaseType.isInstance(base);
}
}
private final Map<Class, CustomTagAdapter> adapters = new HashMap<>();
/**
* Creates a suitable adapter instance for the primitive class type
*
* @param type the type to create an adapter for
* @param <T> 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 <T> CustomTagAdapter createAdapter(Class<T> type) {
if (!Primitives.isWrapperType(type)) {
type = Primitives.wrap(type); //Make sure we will always "switch" over the wrapper types
}
/*
Primitives
*/
if (Objects.equals(Byte.class, type)) {
return createAdapter(Byte.class, NBTTagByte.class, NBTTagByte::new, NBTTagByte::g); // PAIL: rename asByte
}
if (Objects.equals(Short.class, type)) {
return createAdapter(Short.class, NBTTagShort.class, NBTTagShort::new, NBTTagShort::f); // PAIL: rename asShort
}
if (Objects.equals(Integer.class, type)) {
return createAdapter(Integer.class, NBTTagInt.class, NBTTagInt::new, NBTTagInt::e); // PAIL: rename asInteger
}
if (Objects.equals(Long.class, type)) {
return createAdapter(Long.class, NBTTagLong.class, NBTTagLong::new, NBTTagLong::d); // PAIL: rename asLong
}
if (Objects.equals(Float.class, type)) {
return createAdapter(Float.class, NBTTagFloat.class, NBTTagFloat::new, NBTTagFloat::i); // PAIL: rename asFloat
}
if (Objects.equals(Double.class, type)) {
return createAdapter(Double.class, NBTTagDouble.class, NBTTagDouble::new, NBTTagDouble::asDouble);
}
/*
String
*/
if (Objects.equals(String.class, type)) {
return createAdapter(String.class, NBTTagString.class, NBTTagString::new, NBTTagString::b_); // PAIL: rename getString
}
/*
Primitive Arrays
*/
if (Objects.equals(byte[].class, type)) {
return createAdapter(byte[].class, NBTTagByteArray.class, array -> new NBTTagByteArray(Arrays.copyOf(array, array.length)), n -> Arrays.copyOf(n.c(), n.size())); // PAIL: rename getByteArray
}
if (Objects.equals(int[].class, type)) {
return createAdapter(int[].class, NBTTagIntArray.class, array -> new NBTTagIntArray(Arrays.copyOf(array, array.length)), n -> Arrays.copyOf(n.d(), n.size())); // PAIL: rename getIntegerArray
}
if (Objects.equals(long[].class, type)) {
return createAdapter(long[].class, NBTTagLongArray.class, array -> new NBTTagLongArray(Arrays.copyOf(array, array.length)), n -> Arrays.copyOf(n.d(), n.size())); // PAIL: rename getLongArray
}
/*
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
*/
if (Objects.equals(CustomItemTagContainer.class, type)) {
return createAdapter(CraftCustomItemTagContainer.class, NBTTagCompound.class, CraftCustomItemTagContainer::toTagCompound, tag -> {
CraftCustomItemTagContainer container = new CraftCustomItemTagContainer(this);
for (String key : tag.getKeys()) {
container.put(key, tag.get(key));
}
return container;
});
}
throw new IllegalArgumentException("Could not find a valid CustomTagAdapter implementation for the requested type " + type.getSimpleName());
}
private <T, Z extends NBTBase> CustomTagAdapter<T, Z> createAdapter(Class<T> primitiveType, Class<Z> nbtBaseType, Function<T, Z> builder, Function<Z, T> extractor) {
return new CustomTagAdapter<>(primitiveType, nbtBaseType, builder, extractor);
}
/**
* Wraps the passed value into a tag instance.
*
* @param type the type of the passed value
* @param value the value to be stored in the tag
* @param <T> the generic type of the value
* @return the created tag instance
* @throws IllegalArgumentException if no suitable tag type adapter for this
* type was found
*/
public <T> NBTBase wrap(Class<T> type, T value) {
return this.adapters.computeIfAbsent(type, CREATE_ADAPTER).build(value);
}
/**
* Returns if the tag instance matches the provided primitive type.
*
* @param type the type of the primitive value
* @param base the base instance to check
* @param <T> 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
*/
public <T> boolean isInstanceOf(Class<T> type, NBTBase base) {
return this.adapters.computeIfAbsent(type, CREATE_ADAPTER).isInstance(base);
}
/**
* Extracts the value out of the provided tag.
*
* @param type the type of the value to extract
* @param tag the tag to extract the value from
* @param <T> 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
* @throws IllegalArgumentException if the found object is not of type
* passed
* @throws IllegalArgumentException if no suitable tag type adapter for this
* type was found
*/
public <T> T extract(Class<T> type, NBTBase tag) throws ClassCastException, IllegalArgumentException {
CustomTagAdapter 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);
Validate.isInstanceOf(type, foundValue, "The found object is of the type %s. Expected type %s", foundValue.getClass().getSimpleName(), type.getSimpleName());
return type.cast(foundValue);
}
}

Datei anzeigen

@ -42,8 +42,10 @@ import org.bukkit.craftbukkit.Overridden;
import org.bukkit.craftbukkit.attribute.CraftAttributeInstance;
import org.bukkit.craftbukkit.attribute.CraftAttributeMap;
import org.bukkit.craftbukkit.inventory.CraftMetaItem.ItemMetaKey.Specific;
import org.bukkit.craftbukkit.inventory.tags.CraftCustomItemTagContainer;
import org.bukkit.craftbukkit.util.CraftChatMessage;
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
import org.bukkit.craftbukkit.util.CraftNBTTagConfigSerializer;
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.EquipmentSlot;
@ -51,6 +53,7 @@ import org.bukkit.inventory.ItemFlag;
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 com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@ -244,6 +247,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable {
static final ItemMetaKey UNBREAKABLE = new ItemMetaKey("Unbreakable");
@Specific(Specific.To.NBT)
static final ItemMetaKey DAMAGE = new ItemMetaKey("Damage");
static final ItemMetaKey BUKKIT_CUSTOM_TAG = new ItemMetaKey("PublicBukkitValues");
private IChatBaseComponent displayName;
private IChatBaseComponent locName;
@ -256,9 +260,11 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable {
private int damage;
private static final Set<String> HANDLED_TAGS = Sets.newHashSet();
private static final CraftCustomTagTypeRegistry TAG_TYPE_REGISTRY = new CraftCustomTagTypeRegistry();
private NBTTagCompound internalTag;
private final Map<String, NBTBase> unhandledTags = new HashMap<String, NBTBase>();
private final CraftCustomItemTagContainer publicItemTagContainer = new CraftCustomItemTagContainer(TAG_TYPE_REGISTRY);
CraftMetaItem(CraftMetaItem meta) {
if (meta == null) {
@ -285,6 +291,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable {
this.unbreakable = meta.unbreakable;
this.damage = meta.damage;
this.unhandledTags.putAll(meta.unhandledTags);
this.publicItemTagContainer.putAll(meta.publicItemTagContainer.getRaw());
this.internalTag = meta.internalTag;
if (this.internalTag != null) {
@ -339,6 +346,13 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable {
if (tag.hasKey(DAMAGE.NBT)) {
damage = tag.getInt(DAMAGE.NBT);
}
if (tag.hasKey(BUKKIT_CUSTOM_TAG.NBT)) {
NBTTagCompound compound = tag.getCompound(BUKKIT_CUSTOM_TAG.NBT);
Set<String> keys = compound.getKeys();
for (String key : keys) {
publicItemTagContainer.put(key, compound.get(key));
}
}
Set<String> keys = tag.getKeys();
for (String key : keys) {
@ -476,6 +490,11 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable {
Logger.getLogger(CraftMetaItem.class.getName()).log(Level.SEVERE, null, ex);
}
}
Map nbtMap = SerializableMeta.getObject(Map.class, map, BUKKIT_CUSTOM_TAG.BUKKIT, true);
if (nbtMap != null) {
this.publicItemTagContainer.putAll((NBTTagCompound) CraftNBTTagConfigSerializer.deserialize(nbtMap));
}
}
void deserializeInternal(NBTTagCompound tag, Object context) {
@ -575,6 +594,16 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable {
for (Map.Entry<String, NBTBase> e : unhandledTags.entrySet()) {
itemTag.set(e.getKey(), e.getValue());
}
if (!publicItemTagContainer.isEmpty()) {
NBTTagCompound bukkitCustomCompound = new NBTTagCompound();
Map<String, NBTBase> rawPublicMap = publicItemTagContainer.getRaw();
for (Map.Entry<String, NBTBase> nbtBaseEntry : rawPublicMap.entrySet()) {
bukkitCustomCompound.set(nbtBaseEntry.getKey(), nbtBaseEntry.getValue());
}
itemTag.set(BUKKIT_CUSTOM_TAG.NBT, bukkitCustomCompound);
}
}
static NBTTagList createStringList(List<String> list) {
@ -659,7 +688,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable {
@Overridden
boolean isEmpty() {
return !(hasDisplayName() || hasLocalizedName() || hasEnchants() || hasLore() || hasRepairCost() || !unhandledTags.isEmpty() || hideFlag != 0 || isUnbreakable() || hasDamage() || hasAttributeModifiers());
return !(hasDisplayName() || hasLocalizedName() || hasEnchants() || hasLore() || hasRepairCost() || !unhandledTags.isEmpty() || !publicItemTagContainer.isEmpty() || hideFlag != 0 || isUnbreakable() || hasDamage() || hasAttributeModifiers());
}
public String getDisplayName() {
@ -926,6 +955,11 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable {
return removed > 0;
}
@Override
public CustomItemTagContainer getCustomTagContainer() {
return this.publicItemTagContainer;
}
private static boolean compareModifiers(Multimap<Attribute, AttributeModifier> first, Multimap<Attribute, AttributeModifier> second) {
if (first == null || second == null) {
return false;
@ -986,6 +1020,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable {
&& (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.hideFlag == that.hideFlag)
&& (this.isUnbreakable() == that.isUnbreakable())
&& (this.hasDamage() ? that.hasDamage() && this.damage == that.damage : !that.hasDamage());
@ -1015,6 +1050,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable {
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 + hideFlag;
hash = 61 * hash + (isUnbreakable() ? 1231 : 1237);
hash = 61 * hash + (hasDamage() ? this.damage : 0);
@ -1104,6 +1140,10 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable {
}
}
if (!publicItemTagContainer.isEmpty()) { // Store custom tags, wrapped in their compound
builder.put(BUKKIT_CUSTOM_TAG.BUKKIT, publicItemTagContainer.serialize());
}
return builder;
}
@ -1199,6 +1239,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable {
HIDEFLAGS.NBT,
UNBREAKABLE.NBT,
DAMAGE.NBT,
BUKKIT_CUSTOM_TAG.NBT,
ATTRIBUTES.NBT,
ATTRIBUTES_IDENTIFIER.NBT,
ATTRIBUTES_NAME.NBT,

Datei anzeigen

@ -0,0 +1,133 @@
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<String, NBTBase> customTags = new HashMap<>();
private final CraftCustomTagTypeRegistry tagTypeRegistry;
private final CraftItemTagAdapterContext adapterContext;
public CraftCustomItemTagContainer(Map<String, NBTBase> customTags, CraftCustomTagTypeRegistry tagTypeRegistry) {
this(tagTypeRegistry);
this.customTags.putAll(customTags);
}
public CraftCustomItemTagContainer(CraftCustomTagTypeRegistry tagTypeRegistry) {
this.tagTypeRegistry = tagTypeRegistry;
this.adapterContext = new CraftItemTagAdapterContext(this.tagTypeRegistry);
}
@Override
public <T, Z> void setCustomTag(NamespacedKey key, ItemTagType<T, Z> 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 <T, Z> boolean hasCustomTag(NamespacedKey key, ItemTagType<T, Z> 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 <T, Z> Z getCustomTag(NamespacedKey key, ItemTagType<T, Z> 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<String, NBTBase> myRawMap = getRaw();
Map<String, NBTBase> theirRawMap = ((CraftCustomItemTagContainer) obj).getRaw();
return Objects.equals(myRawMap, theirRawMap);
}
public NBTTagCompound toTagCompound() {
NBTTagCompound tag = new NBTTagCompound();
for (Entry<String, NBTBase> 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<String, NBTBase> map) {
this.customTags.putAll(map);
}
public void putAll(NBTTagCompound compound) {
for (String key : compound.getKeys()) {
this.customTags.put(key, compound.get(key));
}
}
public Map<String, NBTBase> 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<String, Object> serialize() {
return (Map<String, Object>) CraftNBTTagConfigSerializer.serialize(toTagCompound());
}
}

Datei anzeigen

@ -0,0 +1,24 @@
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);
}
}

Datei anzeigen

@ -0,0 +1,80 @@
package org.bukkit.craftbukkit.util;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import net.minecraft.server.MojangsonParser;
import net.minecraft.server.NBTBase;
import net.minecraft.server.NBTList;
import net.minecraft.server.NBTTagCompound;
import net.minecraft.server.NBTTagList;
import net.minecraft.server.NBTTagString;
public class CraftNBTTagConfigSerializer {
private static final Pattern ARRAY = Pattern.compile("^\\[.*]");
private static final MojangsonParser MOJANGSON_PARSER = new MojangsonParser(new StringReader(""));
public static Object serialize(NBTBase base) {
if (base instanceof NBTTagCompound) {
Map<String, Object> innerMap = new HashMap<>();
for (String key : ((NBTTagCompound) base).getKeys()) {
innerMap.put(key, serialize(((NBTTagCompound) base).get(key)));
}
return innerMap;
} else if (base instanceof NBTTagList) {
List<Object> baseList = new ArrayList<>();
for (int i = 0; i < ((NBTList) base).size(); i++) {
baseList.add(serialize(((NBTList) base).get(i)));
}
return baseList;
} else if (base instanceof NBTTagString) {
return base.b_(); //PAIL Rename getString
}
return base.toString();
}
public static NBTBase deserialize(Object object) {
if (object instanceof Map) {
NBTTagCompound compound = new NBTTagCompound();
for (Map.Entry<String, Object> entry : ((Map<String, Object>) object).entrySet()) {
compound.set(entry.getKey(), deserialize(entry.getValue()));
}
return compound;
} else if (object instanceof List) {
List<Object> list = (List<Object>) object;
if (list.isEmpty()) {
return new NBTTagList(); // Default
}
NBTTagList tagList = new NBTTagList();
for (Object tag : list) {
tagList.add(deserialize(tag));
}
return tagList;
} else if (object instanceof String) {
String string = (String) object;
if (ARRAY.matcher(string).matches()) {
try {
return new MojangsonParser(new StringReader(string)).h(); // PAIL Rename parseTagList
} catch (CommandSyntaxException e) {
throw new RuntimeException("Could not deserialize found list ", e);
}
} else {
return MOJANGSON_PARSER.b(string); // PAIL Rename parse tagBase
}
}
throw new RuntimeException("Could not deserialize NBTBase");
}
}

Datei anzeigen

@ -0,0 +1,302 @@
package org.bukkit.craftbukkit.inventory;
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;
import org.bukkit.inventory.meta.tags.ItemTagAdapterContext;
import org.bukkit.inventory.meta.tags.ItemTagType;
import org.bukkit.support.AbstractTestingBase;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
public class ItemMetaCustomValueTest 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.getCustomTagContainer().setCustomTag(VALID_KEY, new PrimitiveTagType<>(boolean.class), true);
}
/*
Contains a tag
*/
@Test(expected = IllegalArgumentException.class)
public void testHasNoAdapter() {
ItemMeta itemMeta = createNewItemMeta();
itemMeta.getCustomTagContainer().setCustomTag(VALID_KEY, ItemTagType.INTEGER, 1); // We gotta set this so we at least try to compare it
itemMeta.getCustomTagContainer().hasCustomTag(VALID_KEY, new PrimitiveTagType<>(boolean.class));
}
/*
Getting a tag
*/
@Test(expected = IllegalArgumentException.class)
public void testGetNoAdapter() {
ItemMeta itemMeta = createNewItemMeta();
itemMeta.getCustomTagContainer().setCustomTag(VALID_KEY, ItemTagType.INTEGER, 1); //We gotta set this so we at least try to compare it
itemMeta.getCustomTagContainer().getCustomTag(VALID_KEY, new PrimitiveTagType<>(boolean.class));
}
@Test(expected = IllegalArgumentException.class)
public void testGetWrongType() {
ItemMeta itemMeta = createNewItemMeta();
itemMeta.getCustomTagContainer().setCustomTag(VALID_KEY, ItemTagType.INTEGER, 1);
itemMeta.getCustomTagContainer().getCustomTag(VALID_KEY, ItemTagType.STRING);
}
@Test
public void testDifferentNamespace() {
NamespacedKey namespacedKeyA = new NamespacedKey("plugin-a", "damage");
NamespacedKey namespacedKeyB = new NamespacedKey("plugin-b", "damage");
ItemMeta meta = createNewItemMeta();
meta.getCustomTagContainer().setCustomTag(namespacedKeyA, ItemTagType.LONG, 15L);
meta.getCustomTagContainer().setCustomTag(namespacedKeyB, ItemTagType.LONG, 160L);
assertEquals(15L, (long) meta.getCustomTagContainer().getCustomTag(namespacedKeyA, ItemTagType.LONG));
assertEquals(160L, (long) meta.getCustomTagContainer().getCustomTag(namespacedKeyB, ItemTagType.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<String, Object> 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));
}
private CraftMetaItem createComplexItemMeta() {
CraftMetaItem itemMeta = (CraftMetaItem) createNewItemMeta();
itemMeta.setDisplayName("Item Display Name");
itemMeta.getCustomTagContainer().setCustomTag(requestKey("custom-long"), ItemTagType.LONG, 4L); //Add random primitive values
itemMeta.getCustomTagContainer().setCustomTag(requestKey("custom-byte-array"), ItemTagType.BYTE_ARRAY, new byte[]{
0, 1, 2, 10
});
itemMeta.getCustomTagContainer().setCustomTag(requestKey("custom-string"), ItemTagType.STRING, "Hello there world");
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<String, NBTBase> 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;
}
/*
Test complex object storage
*/
@Test
public void storeUUIDOnItemTest() {
ItemMeta itemMeta = createNewItemMeta();
UUIDItemTagType uuidItemTagType = new UUIDItemTagType();
UUID uuid = UUID.fromString("434eea72-22a6-4c61-b5ef-945874a5c478");
itemMeta.getCustomTagContainer().setCustomTag(VALID_KEY, uuidItemTagType, uuid);
assertTrue(itemMeta.getCustomTagContainer().hasCustomTag(VALID_KEY, uuidItemTagType));
assertEquals(uuid, itemMeta.getCustomTagContainer().getCustomTag(VALID_KEY, uuidItemTagType));
}
@Test
public void encapsulatedContainers() {
NamespacedKey innerKey = new NamespacedKey("plugin-a", "inner");
ItemMeta meta = createNewItemMeta();
ItemTagAdapterContext context = meta.getCustomTagContainer().getAdapterContext();
CustomItemTagContainer thirdContainer = context.newTagContainer();
thirdContainer.setCustomTag(VALID_KEY, ItemTagType.LONG, 3L);
CustomItemTagContainer secondContainer = context.newTagContainer();
secondContainer.setCustomTag(VALID_KEY, ItemTagType.LONG, 2L);
secondContainer.setCustomTag(innerKey, ItemTagType.TAG_CONTAINER, thirdContainer);
meta.getCustomTagContainer().setCustomTag(VALID_KEY, ItemTagType.LONG, 1L);
meta.getCustomTagContainer().setCustomTag(innerKey, ItemTagType.TAG_CONTAINER, secondContainer);
assertEquals(3L, meta.getCustomTagContainer()
.getCustomTag(innerKey, ItemTagType.TAG_CONTAINER)
.getCustomTag(innerKey, ItemTagType.TAG_CONTAINER)
.getCustomTag(VALID_KEY, ItemTagType.LONG).longValue());
assertEquals(2L, meta.getCustomTagContainer()
.getCustomTag(innerKey, ItemTagType.TAG_CONTAINER)
.getCustomTag(VALID_KEY, ItemTagType.LONG).longValue());
assertEquals(1L, meta.getCustomTagContainer()
.getCustomTag(VALID_KEY, ItemTagType.LONG).longValue());
}
class UUIDItemTagType implements ItemTagType<byte[], UUID> {
@Override
public Class<byte[]> getPrimitiveType() {
return byte[].class;
}
@Override
public Class<UUID> getComplexType() {
return UUID.class;
}
@Override
public byte[] toPrimitive(UUID complex, ItemTagAdapterContext 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, ItemTagAdapterContext 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, ItemTagType.BYTE, (byte) 1);
testPrimitiveCustomTag(itemMeta, ItemTagType.SHORT, (short) 1);
testPrimitiveCustomTag(itemMeta, ItemTagType.INTEGER, 1);
testPrimitiveCustomTag(itemMeta, ItemTagType.LONG, 1L);
testPrimitiveCustomTag(itemMeta, ItemTagType.FLOAT, 1.34F);
testPrimitiveCustomTag(itemMeta, ItemTagType.DOUBLE, 151.123);
testPrimitiveCustomTag(itemMeta, ItemTagType.STRING, "test");
testPrimitiveCustomTag(itemMeta, ItemTagType.BYTE_ARRAY, new byte[]{
1, 4, 2, Byte.MAX_VALUE
});
testPrimitiveCustomTag(itemMeta, ItemTagType.INTEGER_ARRAY, new int[]{
1, 4, 2, Integer.MAX_VALUE
});
testPrimitiveCustomTag(itemMeta, ItemTagType.LONG_ARRAY, new long[]{
1L, 4L, 2L, Long.MAX_VALUE
});
}
private <T, Z> void testPrimitiveCustomTag(ItemMeta meta, ItemTagType<T, Z> type, Z value) {
NamespacedKey tagKey = new NamespacedKey("test", String.valueOf(type.hashCode()));
meta.getCustomTagContainer().setCustomTag(tagKey, type, value);
assertTrue(meta.getCustomTagContainer().hasCustomTag(tagKey, type));
Z foundValue = meta.getCustomTagContainer().getCustomTag(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.getCustomTagContainer().removeCustomTag(tagKey);
assertFalse(meta.getCustomTagContainer().hasCustomTag(tagKey, type));
}
class PrimitiveTagType<T> implements ItemTagType<T, T> {
private final Class<T> primitiveType;
PrimitiveTagType(Class<T> primitiveType) {
this.primitiveType = primitiveType;
}
@Override
public Class<T> getPrimitiveType() {
return primitiveType;
}
@Override
public Class<T> getComplexType() {
return primitiveType;
}
@Override
public T toPrimitive(T complex, ItemTagAdapterContext context) {
return complex;
}
@Override
public T fromPrimitive(T primitive, ItemTagAdapterContext context) {
return primitive;
}
}
}