geforkt von Mirrors/Paper
SPIGOT-4347: Add API to allow storing arbitrary values on ItemStacks
Dieser Commit ist enthalten in:
Ursprung
a4c555b6b1
Commit
38e4c013b6
20
nms-patches/MojangsonParser.patch
Normale Datei
20
nms-patches/MojangsonParser.patch
Normale Datei
@ -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();
|
@ -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);
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren