diff --git a/pom.xml b/pom.xml index 0ed082f72..0587d5eea 100644 --- a/pom.xml +++ b/pom.xml @@ -120,12 +120,12 @@ - comphenix-snapshots Comphenix Maven Snapshots http://repo.comphenix.net/content/repositories/snapshots/ - --> + @@ -165,13 +165,12 @@ mail 1.5.0-b01 - - + diff --git a/src/main/java/com/comphenix/attribute/AttributeStorage.java b/src/main/java/com/comphenix/attribute/AttributeStorage.java deleted file mode 100644 index 2d9aa5265..000000000 --- a/src/main/java/com/comphenix/attribute/AttributeStorage.java +++ /dev/null @@ -1,144 +0,0 @@ -package com.comphenix.attribute; - -import java.util.UUID; - -import org.bukkit.inventory.ItemStack; - -import com.comphenix.attribute.Attributes.Attribute; -import com.comphenix.attribute.Attributes.AttributeType; -import com.comphenix.attribute.Attributes.Operation; -import com.google.common.base.Objects; -import com.google.common.base.Preconditions; - -/** - * Store meta-data in an ItemStack as attributes. - * - * @author Kristian - */ -public class AttributeStorage { - - private ItemStack target; - private final UUID uniqueKey; - - private AttributeStorage(ItemStack target, UUID uniqueKey) { - this.target = Preconditions.checkNotNull(target, "target cannot be NULL"); - this.uniqueKey = Preconditions.checkNotNull(uniqueKey, "uniqueKey cannot be NULL"); - } - - /** - * Construct a new attribute storage system. - *

- * The key must be the same in order to retrieve the same data. - * - * @param target - * - the item stack where the data will be stored. - * @param uniqueKey - * - the unique key used to retrieve the correct data. - */ - public static AttributeStorage newTarget(ItemStack target, UUID uniqueKey) { - return new AttributeStorage(target, uniqueKey); - } - - /** - * Retrieve the data stored in the item's attribute. - * - * @param defaultValue - * - the default value to return if no data can be found. - * @return The stored data, or defaultValue if not found. - */ - public String getData(String defaultValue) { - Attribute current = getAttribute(new Attributes(target), uniqueKey); - return current != null ? current.getName() : defaultValue; - } - - /** - * Determine if we are storing any data. - * - * @return TRUE if we are, FALSE otherwise. - */ - public boolean hasData() { - return getAttribute(new Attributes(target), uniqueKey) != null; - } - - /** - * Set the data stored in the attributes. - * - * @param data - * - the data. - */ - public void setData(String data) { - Attributes attributes = new Attributes(target); - Attribute current = getAttribute(attributes, uniqueKey); - - if (current == null) { - attributes.add(Attribute.newBuilder().name(data).amount(getBaseDamage(target)).uuid(uniqueKey).operation(Operation.ADD_NUMBER).type(AttributeType.GENERIC_ATTACK_DAMAGE).build()); - } else { - current.setName(data); - } - this.target = attributes.getStack(); - } - - /** - * Retrieve the base damage of the given item. - * - * @param stack - * - the stack. - * @return The base damage. - */ - private int getBaseDamage(ItemStack stack) { - // Yes - we have to hard code these values. Cannot use - // Operation.ADD_PERCENTAGE either. - switch (stack.getType()) { - case WOOD_SWORD: - return 4; - case GOLD_SWORD: - return 4; - case STONE_SWORD: - return 5; - case IRON_SWORD: - return 6; - case DIAMOND_SWORD: - return 7; - - case WOOD_AXE: - return 3; - case GOLD_AXE: - return 3; - case STONE_AXE: - return 4; - case IRON_AXE: - return 5; - case DIAMOND_AXE: - return 6; - default: - return 0; - } - } - - /** - * Retrieve the target stack. May have been changed. - * - * @return The target stack. - */ - public ItemStack getTarget() { - return target; - } - - /** - * Retrieve an attribute by UUID. - * - * @param attributes - * - the attribute. - * @param id - * - the UUID to search for. - * @return The first attribute associated with this UUID, or NULL. - */ - private Attribute getAttribute(Attributes attributes, UUID id) { - for (Attribute attribute : attributes.values()) { - if (Objects.equal(attribute.getUUID(), id)) { - return attribute; - } - } - return null; - } -} diff --git a/src/main/java/com/comphenix/attribute/Attributes.java b/src/main/java/com/comphenix/attribute/Attributes.java deleted file mode 100644 index d11afe302..000000000 --- a/src/main/java/com/comphenix/attribute/Attributes.java +++ /dev/null @@ -1,369 +0,0 @@ -package com.comphenix.attribute; - -import java.util.Collections; -import java.util.Iterator; -import java.util.UUID; -import java.util.concurrent.ConcurrentMap; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.bukkit.inventory.ItemStack; - -import com.comphenix.attribute.NbtFactory.NbtCompound; -import com.comphenix.attribute.NbtFactory.NbtList; -import com.google.common.base.Function; -import com.google.common.base.Objects; -import com.google.common.base.Preconditions; -import com.google.common.collect.Iterators; -import com.google.common.collect.Maps; - -public class Attributes { - - public enum Operation { - ADD_NUMBER(0), - MULTIPLY_PERCENTAGE(1), - ADD_PERCENTAGE(2); - - private int id; - - private Operation(int id) { - this.id = id; - } - - public int getId() { - return id; - } - - public static Operation fromId(int id) { - // Linear scan is very fast for small N - for (Operation op : values()) { - if (op.getId() == id) { - return op; - } - } - throw new IllegalArgumentException("Corrupt operation ID " + id + " detected."); - } - } - - public static class AttributeType { - - private static ConcurrentMap LOOKUP = Maps.newConcurrentMap(); - public static final AttributeType GENERIC_MAX_HEALTH = new AttributeType("generic.maxHealth").register(); - public static final AttributeType GENERIC_FOLLOW_RANGE = new AttributeType("generic.followRange").register(); - public static final AttributeType GENERIC_ATTACK_DAMAGE = new AttributeType("generic.attackDamage").register(); - public static final AttributeType GENERIC_MOVEMENT_SPEED = new AttributeType("generic.movementSpeed").register(); - public static final AttributeType GENERIC_KNOCKBACK_RESISTANCE = new AttributeType("generic.knockbackResistance").register(); - - private final String minecraftId; - - /** - * Construct a new attribute type. - *

- * Remember to {@link #register()} the type. - * - * @param minecraftId - * - the ID of the type. - */ - public AttributeType(String minecraftId) { - this.minecraftId = minecraftId; - } - - /** - * Retrieve the associated minecraft ID. - * - * @return The associated ID. - */ - public String getMinecraftId() { - return minecraftId; - } - - /** - * Register the type in the central registry. - * - * @return The registered type. - */ - // Constructors should have no side-effects! - public AttributeType register() { - AttributeType old = LOOKUP.putIfAbsent(minecraftId, this); - return old != null ? old : this; - } - - /** - * Retrieve the attribute type associated with a given ID. - * - * @param minecraftId - * The ID to search for. - * @return The attribute type, or NULL if not found. - */ - public static AttributeType fromId(String minecraftId) { - return LOOKUP.get(minecraftId); - } - - /** - * Retrieve every registered attribute type. - * - * @return Every type. - */ - public static Iterable values() { - return LOOKUP.values(); - } - } - - public static class Attribute { - - private NbtCompound data; - - public Attribute(Builder builder) { - data = NbtFactory.createCompound(); - setAmount(builder.amount); - setOperation(builder.operation); - setAttributeType(builder.type); - setName(builder.name); - setUUID(builder.uuid); - } - - private Attribute(NbtCompound data) { - this.data = data; - } - - public double getAmount() { - return data.getDouble("Amount", 0.0); - } - - public void setAmount(double amount) { - data.put("Amount", amount); - } - - public Operation getOperation() { - return Operation.fromId(data.getInteger("Operation", 0)); - } - - public void setOperation(@Nonnull Operation operation) { - Preconditions.checkNotNull(operation, "operation cannot be NULL."); - data.put("Operation", operation.getId()); - } - - public AttributeType getAttributeType() { - return AttributeType.fromId(data.getString("AttributeName", null)); - } - - public void setAttributeType(@Nonnull AttributeType type) { - Preconditions.checkNotNull(type, "type cannot be NULL."); - data.put("AttributeName", type.getMinecraftId()); - } - - public String getName() { - return data.getString("Name", null); - } - - public void setName(@Nonnull String name) { - Preconditions.checkNotNull(name, "name cannot be NULL."); - data.put("Name", name); - } - - public UUID getUUID() { - return new UUID(data.getLong("UUIDMost", null), data.getLong("UUIDLeast", null)); - } - - public void setUUID(@Nonnull UUID id) { - Preconditions.checkNotNull("id", "id cannot be NULL."); - data.put("UUIDLeast", id.getLeastSignificantBits()); - data.put("UUIDMost", id.getMostSignificantBits()); - } - - /** - * Construct a new attribute builder with a random UUID and default - * operation of adding numbers. - * - * @return The attribute builder. - */ - public static Builder newBuilder() { - return new Builder().uuid(UUID.randomUUID()).operation(Operation.ADD_NUMBER); - } - - // Makes it easier to construct an attribute - public static class Builder { - - private double amount; - private Operation operation = Operation.ADD_NUMBER; - private AttributeType type; - private String name; - private UUID uuid; - - private Builder() { - // Don't make this accessible - } - - public Builder(double amount, Operation operation, - AttributeType type, String name, UUID uuid) { - this.amount = amount; - this.operation = operation; - this.type = type; - this.name = name; - this.uuid = uuid; - } - - public Builder amount(double amount) { - this.amount = amount; - return this; - } - - public Builder operation(Operation operation) { - this.operation = operation; - return this; - } - - public Builder type(AttributeType type) { - this.type = type; - return this; - } - - public Builder name(String name) { - this.name = name; - return this; - } - - public Builder uuid(UUID uuid) { - this.uuid = uuid; - return this; - } - - public Attribute build() { - return new Attribute(this); - } - } - } - - // This may be modified - public ItemStack stack; - private NbtList attributes; - - public Attributes(ItemStack stack) { - // Create a CraftItemStack (under the hood) - this.stack = NbtFactory.getCraftItemStack(stack); - loadAttributes(false); - } - - /** - * Load the NBT list from the TAG compound. - * - * @param createIfMissing - * - create the list if its missing. - */ - private void loadAttributes(boolean createIfMissing) { - if (this.attributes == null) { - NbtCompound nbt = NbtFactory.fromItemTag(this.stack); - this.attributes = nbt.getList("AttributeModifiers", createIfMissing); - } - } - - /** - * Remove the NBT list from the TAG compound. - */ - private void removeAttributes() { - NbtCompound nbt = NbtFactory.fromItemTag(this.stack); - nbt.remove("AttributeModifiers"); - this.attributes = null; - } - - /** - * Retrieve the modified item stack. - * - * @return The modified item stack. - */ - public ItemStack getStack() { - return stack; - } - - /** - * Retrieve the number of attributes. - * - * @return Number of attributes. - */ - public int size() { - return attributes != null ? attributes.size() : 0; - } - - /** - * Add a new attribute to the list. - * - * @param attribute - * - the new attribute. - */ - public void add(Attribute attribute) { - Preconditions.checkNotNull(attribute.getName(), "must specify an attribute name."); - loadAttributes(true); - attributes.add(attribute.data); - } - - /** - * Remove the first instance of the given attribute. - *

- * The attribute will be removed using its UUID. - * - * @param attribute - * - the attribute to remove. - * @return TRUE if the attribute was removed, FALSE otherwise. - */ - public boolean remove(Attribute attribute) { - if (attributes == null) - return false; - UUID uuid = attribute.getUUID(); - - for (Iterator it = values().iterator(); it.hasNext();) { - if (Objects.equal(it.next().getUUID(), uuid)) { - it.remove(); - - // Last removed attribute? - if (size() == 0) { - removeAttributes(); - } - return true; - } - } - return false; - } - - /** - * Remove every attribute. - */ - public void clear() { - removeAttributes(); - } - - /** - * Retrieve the attribute at a given index. - * - * @param index - * - the index to look up. - * @return The attribute at that index. - */ - public Attribute get(int index) { - if (size() == 0) - throw new IllegalStateException("Attribute list is empty."); - return new Attribute((NbtCompound) attributes.get(index)); - } - - // We can't make Attributes itself iterable without splitting it up into - // separate classes - public Iterable values() { - return new Iterable() { - - @Override - public Iterator iterator() { - // Handle the empty case - if (size() == 0) - return Collections. emptyList().iterator(); - - return Iterators.transform(attributes.iterator(), new Function() { - - @Override - public Attribute apply(@Nullable Object element) { - return new Attribute((NbtCompound) element); - } - }); - } - }; - } -} diff --git a/src/main/java/com/comphenix/attribute/NbtFactory.java b/src/main/java/com/comphenix/attribute/NbtFactory.java deleted file mode 100644 index 7fe03cb68..000000000 --- a/src/main/java/com/comphenix/attribute/NbtFactory.java +++ /dev/null @@ -1,1120 +0,0 @@ -package com.comphenix.attribute; - -import java.io.BufferedInputStream; -import java.io.DataInput; -import java.io.DataInputStream; -import java.io.DataOutput; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.AbstractList; -import java.util.AbstractMap; -import java.util.AbstractSet; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentMap; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; - -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.Server; -import org.bukkit.inventory.ItemStack; - -import com.google.common.base.Splitter; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import com.google.common.collect.Lists; -import com.google.common.collect.MapMaker; -import com.google.common.io.Closeables; -import com.google.common.io.Files; -import com.google.common.io.InputSupplier; -import com.google.common.io.OutputSupplier; -import com.google.common.primitives.Primitives; - -public class NbtFactory { - - // Convert between NBT id and the equivalent class in java - private static final BiMap> NBT_CLASS = HashBiMap.create(); - private static final BiMap NBT_ENUM = HashBiMap.create(); - - /** - * Whether or not to enable stream compression. - * - * @author Kristian - */ - public enum StreamOptions { - NO_COMPRESSION, - GZIP_COMPRESSION, - } - - private enum NbtType { - TAG_END(0, Void.class), - TAG_BYTE(1, byte.class), - TAG_SHORT(2, short.class), - TAG_INT(3, int.class), - TAG_LONG(4, long.class), - TAG_FLOAT(5, float.class), - TAG_DOUBLE(6, double.class), - TAG_BYTE_ARRAY(7, byte[].class), - TAG_INT_ARRAY(11, int[].class), - TAG_STRING(8, String.class), - TAG_LIST(9, List.class), - TAG_COMPOUND(10, Map.class); - - // Unique NBT id - public final int id; - - private NbtType(int id, Class type) { - this.id = id; - NBT_CLASS.put(id, type); - NBT_ENUM.put(id, this); - } - - private String getFieldName() { - if (this == TAG_COMPOUND) - return "map"; - else if (this == TAG_LIST) - return "list"; - else return "data"; - } - } - - // The NBT base class - private Class BASE_CLASS; - private Class COMPOUND_CLASS; - private Class STREAM_TOOLS; - private Class READ_LIMITER_CLASS; - private Method NBT_CREATE_TAG; - private Method NBT_GET_TYPE; - private Field NBT_LIST_TYPE; - private final Field[] DATA_FIELD = new Field[12]; - - // CraftItemStack - private Class CRAFT_STACK; - private Field CRAFT_HANDLE; - private Field STACK_TAG; - - // Loading/saving compounds - private LoadCompoundMethod LOAD_COMPOUND; - private Method SAVE_COMPOUND; - - // Shared instance - private static NbtFactory INSTANCE; - - /** - * Represents a root NBT compound. - *

- * All changes to this map will be reflected in the underlying NBT compound. - * Values may only be one of the following: - *

- *

- * See also: - *

- * - * @author Kristian - */ - public final class NbtCompound extends ConvertedMap { - - private NbtCompound(Object handle) { - super(handle, getDataMap(handle)); - } - - // Simplifiying access to each value - public Byte getByte(String key, Byte defaultValue) { - return containsKey(key) ? (Byte) get(key) : defaultValue; - } - - public Short getShort(String key, Short defaultValue) { - return containsKey(key) ? (Short) get(key) : defaultValue; - } - - public Integer getInteger(String key, Integer defaultValue) { - return containsKey(key) ? (Integer) get(key) : defaultValue; - } - - public Long getLong(String key, Long defaultValue) { - return containsKey(key) ? (Long) get(key) : defaultValue; - } - - public Float getFloat(String key, Float defaultValue) { - return containsKey(key) ? (Float) get(key) : defaultValue; - } - - public Double getDouble(String key, Double defaultValue) { - return containsKey(key) ? (Double) get(key) : defaultValue; - } - - public String getString(String key, String defaultValue) { - return containsKey(key) ? (String) get(key) : defaultValue; - } - - public byte[] getByteArray(String key, byte[] defaultValue) { - return containsKey(key) ? (byte[]) get(key) : defaultValue; - } - - public int[] getIntegerArray(String key, int[] defaultValue) { - return containsKey(key) ? (int[]) get(key) : defaultValue; - } - - /** - * Retrieve the list by the given name. - * - * @param key - * - the name of the list. - * @param createNew - * - whether or not to create a new list if its missing. - * @return An existing list, a new list or NULL. - */ - public NbtList getList(String key, boolean createNew) { - NbtList list = (NbtList) get(key); - - if (list == null && createNew) - put(key, list = createList()); - return list; - } - - /** - * Retrieve the map by the given name. - * - * @param key - * - the name of the map. - * @param createNew - * - whether or not to create a new map if its missing. - * @return An existing map, a new map or NULL. - */ - public NbtCompound getMap(String key, boolean createNew) { - return getMap(Arrays.asList(key), createNew); - } - - // Done - - /** - * Set the value of an entry at a given location. - *

- * Every element of the path (except the end) are assumed to be - * compounds, and will be created if they are missing. - * - * @param path - * - the path to the entry. - * @param value - * - the new value of this entry. - * @return This compound, for chaining. - */ - public NbtCompound putPath(String path, Object value) { - List entries = getPathElements(path); - Map map = getMap(entries.subList(0, entries.size() - 1), true); - - map.put(entries.get(entries.size() - 1), value); - return this; - } - - /** - * Retrieve the value of a given entry in the tree. - *

- * Every element of the path (except the end) are assumed to be - * compounds. The retrieval operation will be cancelled if any of them - * are missing. - * - * @param path - * - path to the entry. - * @return The value, or NULL if not found. - */ - @SuppressWarnings("unchecked") - public T getPath(String path) { - List entries = getPathElements(path); - NbtCompound map = getMap(entries.subList(0, entries.size() - 1), false); - - if (map != null) { - return (T) map.get(entries.get(entries.size() - 1)); - } - return null; - } - - /** - * Save the content of a NBT compound to a stream. - *

- * Use {@link Files#newOutputStreamSupplier(java.io.File)} to provide a - * stream supplier to a file. - * - * @param stream - * - the output stream. - * @param option - * - whether or not to compress the output. - * @throws IOException - * If anything went wrong. - */ - public void saveTo(OutputSupplier stream, - StreamOptions option) throws IOException { - saveStream(this, stream, option); - } - - /** - * Retrieve a map from a given path. - * - * @param path - * - path of compounds to look up. - * @param createNew - * - whether or not to create new compounds on the way. - * @return The map at this location. - */ - private NbtCompound getMap(Iterable path, boolean createNew) { - NbtCompound current = this; - - for (String entry : path) { - NbtCompound child = (NbtCompound) current.get(entry); - - if (child == null) { - if (!createNew) - return null; - current.put(entry, child = createCompound()); - } - current = child; - } - return current; - } - - /** - * Split the path into separate elements. - * - * @param path - * - the path to split. - * @return The elements. - */ - private List getPathElements(String path) { - return Lists.newArrayList(Splitter.on(".").omitEmptyStrings().split(path)); - } - } - - /** - * Represents a root NBT list. See also: - *

- * - * @author Kristian - */ - public final class NbtList extends ConvertedList { - - private NbtList(Object handle) { - super(handle, getDataList(handle)); - } - } - - /** - * Represents an object that provides a view of a native NMS class. - * - * @author Kristian - */ - public static interface Wrapper { - - /** - * Retrieve the underlying native NBT tag. - * - * @return The underlying NBT. - */ - public Object getHandle(); - } - - /** - * Retrieve or construct a shared NBT factory. - * - * @return The factory. - */ - private static NbtFactory get() { - if (INSTANCE == null) - INSTANCE = new NbtFactory(); - return INSTANCE; - } - - /** - * Construct an instance of the NBT factory by deducing the class of - * NBTBase. - */ - private NbtFactory() { - if (BASE_CLASS == null) { - try { - // Keep in mind that I do use hard-coded field names - but it's - // okay as long as we're dealing - // with CraftBukkit or its derivatives. This does not work in - // MCPC+ however. - ClassLoader loader = NbtFactory.class.getClassLoader(); - - String packageName = getPackageName(); - Class offlinePlayer = loader.loadClass(packageName + ".CraftOfflinePlayer"); - - // Prepare NBT - COMPOUND_CLASS = getMethod(0, Modifier.STATIC, offlinePlayer, "getData").getReturnType(); - BASE_CLASS = COMPOUND_CLASS.getSuperclass(); - NBT_GET_TYPE = getMethod(0, Modifier.STATIC, BASE_CLASS, "getTypeId"); - NBT_CREATE_TAG = getMethod(Modifier.STATIC, 0, BASE_CLASS, "createTag", byte.class); - - // Prepare CraftItemStack - CRAFT_STACK = loader.loadClass(packageName + ".inventory.CraftItemStack"); - CRAFT_HANDLE = getField(null, CRAFT_STACK, "handle"); - STACK_TAG = getField(null, CRAFT_HANDLE.getType(), "tag"); - - // Loading/saving - String nmsPackage = BASE_CLASS.getPackage().getName(); - initializeNMS(loader, nmsPackage); - - LOAD_COMPOUND = READ_LIMITER_CLASS != null ? new LoadMethodSkinUpdate(STREAM_TOOLS, READ_LIMITER_CLASS) : new LoadMethodWorldUpdate(STREAM_TOOLS); - SAVE_COMPOUND = getMethod(Modifier.STATIC, 0, STREAM_TOOLS, null, BASE_CLASS, DataOutput.class); - - } catch (ClassNotFoundException e) { - throw new IllegalStateException("Unable to find offline player.", e); - } - } - } - - private void initializeNMS(ClassLoader loader, String nmsPackage) { - try { - STREAM_TOOLS = loader.loadClass(nmsPackage + ".NBTCompressedStreamTools"); - READ_LIMITER_CLASS = loader.loadClass(nmsPackage + ".NBTReadLimiter"); - } catch (ClassNotFoundException e) { - // Ignore - we will detect this later - } - } - - private String getPackageName() { - Server server = Bukkit.getServer(); - String name = server != null ? server.getClass().getPackage().getName() : null; - - if (name != null && name.contains("craftbukkit")) { - return name; - } else { - // Fallback - return "org.bukkit.craftbukkit.v1_7_R3"; - } - } - - @SuppressWarnings("unchecked") - private Map getDataMap(Object handle) { - return (Map) getFieldValue(getDataField(NbtType.TAG_COMPOUND, handle), handle); - } - - @SuppressWarnings("unchecked") - private List getDataList(Object handle) { - return (List) getFieldValue(getDataField(NbtType.TAG_LIST, handle), handle); - } - - /** - * Construct a new NBT list of an unspecified type. - * - * @return The NBT list. - */ - public static NbtList createList(Object... content) { - return createList(Arrays.asList(content)); - } - - /** - * Construct a new NBT list of an unspecified type. - * - * @return The NBT list. - */ - public static NbtList createList(Iterable iterable) { - NbtList list = get().new NbtList(INSTANCE.createNbtTag(NbtType.TAG_LIST, null)); - - // Add the content as well - for (Object obj : iterable) - list.add(obj); - return list; - } - - /** - * Construct a new NBT compound. - *

- * Use {@link NbtCompound#asMap()} to modify it. - * - * @return The NBT compound. - */ - public static NbtCompound createCompound() { - return get().new NbtCompound(INSTANCE.createNbtTag(NbtType.TAG_COMPOUND, null)); - } - - /** - * Construct a new NBT wrapper from a list. - * - * @param nmsList - * - the NBT list. - * @return The wrapper. - */ - public static NbtList fromList(Object nmsList) { - return get().new NbtList(nmsList); - } - - /** - * Load the content of a file from a stream. - *

- * Use {@link Files#newInputStreamSupplier(java.io.File)} to provide a - * stream from a file. - * - * @param stream - * - the stream supplier. - * @param option - * - whether or not to decompress the input stream. - * @return The decoded NBT compound. - * @throws IOException - * If anything went wrong. - */ - public static NbtCompound fromStream( - InputSupplier stream, StreamOptions option) - throws IOException { - InputStream input = null; - DataInputStream data = null; - boolean suppress = true; - - try { - input = stream.getInput(); - data = new DataInputStream(new BufferedInputStream(option == StreamOptions.GZIP_COMPRESSION ? new GZIPInputStream(input) : input)); - - NbtCompound result = fromCompound(get().LOAD_COMPOUND.loadNbt(data)); - suppress = false; - return result; - - } finally { - if (data != null) - Closeables.close(data, suppress); - else if (input != null) - Closeables.close(input, suppress); - } - } - - /** - * Save the content of a NBT compound to a stream. - *

- * Use {@link Files#newOutputStreamSupplier(java.io.File)} to provide a - * stream supplier to a file. - * - * @param source - * - the NBT compound to save. - * @param stream - * - the stream. - * @param option - * - whether or not to compress the output. - * @throws IOException - * If anything went wrong. - */ - public static void saveStream(NbtCompound source, - OutputSupplier stream, StreamOptions option) - throws IOException { - OutputStream output = null; - DataOutputStream data = null; - boolean suppress = true; - - try { - output = stream.getOutput(); - data = new DataOutputStream(option == StreamOptions.GZIP_COMPRESSION ? new GZIPOutputStream(output) : output); - - invokeMethod(get().SAVE_COMPOUND, null, source.getHandle(), data); - suppress = false; - - } finally { - if (data != null) - Closeables.close(data, suppress); - else if (output != null) - Closeables.close(output, suppress); - } - } - - /** - * Construct a new NBT wrapper from a compound. - * - * @param nmsCompound - * - the NBT compund. - * @return The wrapper. - */ - public static NbtCompound fromCompound(Object nmsCompound) { - return get().new NbtCompound(nmsCompound); - } - - /** - * Set the NBT compound tag of a given item stack. - *

- * The item stack must be a wrapper for a CraftItemStack. Use - * {@link MinecraftReflection#getBukkitItemStack(ItemStack)} if not. - * - * @param stack - * - the item stack, cannot be air. - * @param compound - * - the new NBT compound, or NULL to remove it. - * @throws IllegalArgumentException - * If the stack is not a CraftItemStack, or it represents air. - */ - public static void setItemTag(ItemStack stack, NbtCompound compound) { - checkItemStack(stack); - Object nms = getFieldValue(get().CRAFT_HANDLE, stack); - - // Now update the tag compound - setFieldValue(get().STACK_TAG, nms, compound.getHandle()); - } - - /** - * Construct a wrapper for an NBT tag stored (in memory) in an item stack. - * This is where auxillary data such as enchanting, name and lore is stored. - * It does not include items material, damage value or count. - *

- * The item stack must be a wrapper for a CraftItemStack. - * - * @param stack - * - the item stack. - * @return A wrapper for its NBT tag. - */ - public static NbtCompound fromItemTag(ItemStack stack) { - checkItemStack(stack); - Object nms = getFieldValue(get().CRAFT_HANDLE, stack); - Object tag = getFieldValue(get().STACK_TAG, nms); - - // Create the tag if it doesn't exist - if (tag == null) { - NbtCompound compound = createCompound(); - setItemTag(stack, compound); - return compound; - } - return fromCompound(tag); - } - - /** - * Retrieve a CraftItemStack version of the stack. - * - * @param stack - * - the stack to convert. - * @return The CraftItemStack version. - */ - public static ItemStack getCraftItemStack(ItemStack stack) { - // Any need to convert? - if (stack == null || get().CRAFT_STACK.isAssignableFrom(stack.getClass())) - return stack; - try { - // Call the private constructor - Constructor caller = INSTANCE.CRAFT_STACK.getDeclaredConstructor(ItemStack.class); - caller.setAccessible(true); - return (ItemStack) caller.newInstance(stack); - } catch (Exception e) { - throw new IllegalStateException("Unable to convert " + stack + " + to a CraftItemStack."); - } - } - - /** - * Ensure that the given stack can store arbitrary NBT information. - * - * @param stack - * - the stack to check. - */ - private static void checkItemStack(ItemStack stack) { - if (stack == null) - throw new IllegalArgumentException("Stack cannot be NULL."); - if (!get().CRAFT_STACK.isAssignableFrom(stack.getClass())) - throw new IllegalArgumentException("Stack must be a CraftItemStack."); - if (stack.getType() == Material.AIR) - throw new IllegalArgumentException("ItemStacks representing air cannot store NMS information."); - } - - /** - * Convert wrapped List and Map objects into their respective NBT - * counterparts. - * - * @param name - * - the name of the NBT element to create. - * @param value - * - the value of the element to create. Can be a List or a Map. - * @return The NBT element. - */ - private Object unwrapValue(Object value) { - if (value == null) - return null; - - if (value instanceof Wrapper) { - return ((Wrapper) value).getHandle(); - - } else if (value instanceof List) { - throw new IllegalArgumentException("Can only insert a WrappedList."); - } else if (value instanceof Map) { - throw new IllegalArgumentException("Can only insert a WrappedCompound."); - - } else { - return createNbtTag(getPrimitiveType(value), value); - } - } - - /** - * Convert a given NBT element to a primitive wrapper or List/Map - * equivalent. - *

- * All changes to any mutable objects will be reflected in the underlying - * NBT element(s). - * - * @param nms - * - the NBT element. - * @return The wrapper equivalent. - */ - private Object wrapNative(Object nms) { - if (nms == null) - return null; - - if (BASE_CLASS.isAssignableFrom(nms.getClass())) { - final NbtType type = getNbtType(nms); - - // Handle the different types - switch (type) { - case TAG_COMPOUND: - return new NbtCompound(nms); - case TAG_LIST: - return new NbtList(nms); - default: - return getFieldValue(getDataField(type, nms), nms); - } - } - throw new IllegalArgumentException("Unexpected type: " + nms); - } - - /** - * Construct a new NMS NBT tag initialized with the given value. - * - * @param type - * - the NBT type. - * @param value - * - the value, or NULL to keep the original value. - * @return The created tag. - */ - private Object createNbtTag(NbtType type, Object value) { - Object tag = invokeMethod(NBT_CREATE_TAG, null, (byte) type.id); - - if (value != null) { - setFieldValue(getDataField(type, tag), tag, value); - } - return tag; - } - - /** - * Retrieve the field where the NBT class stores its value. - * - * @param type - * - the NBT type. - * @param nms - * - the NBT class instance. - * @return The corresponding field. - */ - private Field getDataField(NbtType type, Object nms) { - if (DATA_FIELD[type.id] == null) - DATA_FIELD[type.id] = getField(nms, null, type.getFieldName()); - return DATA_FIELD[type.id]; - } - - /** - * Retrieve the NBT type from a given NMS NBT tag. - * - * @param nms - * - the native NBT tag. - * @return The corresponding type. - */ - private NbtType getNbtType(Object nms) { - int type = (Byte) invokeMethod(NBT_GET_TYPE, nms); - return NBT_ENUM.get(type); - } - - /** - * Retrieve the nearest NBT type for a given primitive type. - * - * @param primitive - * - the primitive type. - * @return The corresponding type. - */ - private NbtType getPrimitiveType(Object primitive) { - NbtType type = NBT_ENUM.get(NBT_CLASS.inverse().get(Primitives.unwrap(primitive.getClass()))); - - // Display the illegal value at least - if (type == null) - throw new IllegalArgumentException(String.format("Illegal type: %s (%s)", primitive.getClass(), primitive)); - return type; - } - - /** - * Invoke a method on the given target instance using the provided - * parameters. - * - * @param method - * - the method to invoke. - * @param target - * - the target. - * @param params - * - the parameters to supply. - * @return The result of the method. - */ - private static Object invokeMethod(Method method, Object target, - Object... params) { - try { - return method.invoke(target, params); - } catch (Exception e) { - throw new RuntimeException("Unable to invoke method " + method + " for " + target, e); - } - } - - private static void setFieldValue(Field field, Object target, Object value) { - try { - field.set(target, value); - } catch (Exception e) { - throw new RuntimeException("Unable to set " + field + " for " + target, e); - } - } - - private static Object getFieldValue(Field field, Object target) { - try { - return field.get(target); - } catch (Exception e) { - throw new RuntimeException("Unable to retrieve " + field + " for " + target, e); - } - } - - /** - * Search for the first publically and privately defined method of the given - * name and parameter count. - * - * @param requireMod - * - modifiers that are required. - * @param bannedMod - * - modifiers that are banned. - * @param clazz - * - a class to start with. - * @param methodName - * - the method name, or NULL to skip. - * @param params - * - the expected parameters. - * @return The first method by this name. - * @throws IllegalStateException - * If we cannot find this method. - */ - private static Method getMethod(int requireMod, int bannedMod, - Class clazz, String methodName, Class... params) { - for (Method method : clazz.getDeclaredMethods()) { - // Limitation: Doesn't handle overloads - if ((method.getModifiers() & requireMod) == requireMod && (method.getModifiers() & bannedMod) == 0 && (methodName == null || method.getName().equals(methodName)) && Arrays.equals(method.getParameterTypes(), params)) { - - method.setAccessible(true); - return method; - } - } - // Search in every superclass - if (clazz.getSuperclass() != null) - return getMethod(requireMod, bannedMod, clazz.getSuperclass(), methodName, params); - throw new IllegalStateException(String.format("Unable to find method %s (%s).", methodName, Arrays.asList(params))); - } - - /** - * Search for the first publically and privately defined field of the given - * name. - * - * @param instance - * - an instance of the class with the field. - * @param clazz - * - an optional class to start with, or NULL to deduce it from - * instance. - * @param fieldName - * - the field name. - * @return The first field by this name. - * @throws IllegalStateException - * If we cannot find this field. - */ - private static Field getField(Object instance, Class clazz, - String fieldName) { - if (clazz == null) - clazz = instance.getClass(); - // Ignore access rules - for (Field field : clazz.getDeclaredFields()) { - if (field.getName().equals(fieldName)) { - field.setAccessible(true); - return field; - } - } - // Recursively fild the correct field - if (clazz.getSuperclass() != null) - return getField(instance, clazz.getSuperclass(), fieldName); - throw new IllegalStateException("Unable to find field " + fieldName + " in " + instance); - } - - /** - * Represents a class for caching wrappers. - * - * @author Kristian - */ - private final class CachedNativeWrapper { - - // Don't recreate wrapper objects - private final ConcurrentMap cache = new MapMaker().weakKeys().makeMap(); - - public Object wrap(Object value) { - Object current = cache.get(value); - - if (current == null) { - current = wrapNative(value); - - // Only cache composite objects - if (current instanceof ConvertedMap || current instanceof ConvertedList) { - cache.put(value, current); - } - } - return current; - } - } - - /** - * Represents a map that wraps another map and automatically converts - * entries of its type and another exposed type. - * - * @author Kristian - */ - private class ConvertedMap extends AbstractMap implements - Wrapper { - - private final Object handle; - private final Map original; - - private final CachedNativeWrapper cache = new CachedNativeWrapper(); - - public ConvertedMap(Object handle, Map original) { - this.handle = handle; - this.original = original; - } - - // For converting back and forth - protected Object wrapOutgoing(Object value) { - return cache.wrap(value); - } - - protected Object unwrapIncoming(Object wrapped) { - return unwrapValue(wrapped); - } - - // Modification - @Override - public Object put(String key, Object value) { - return wrapOutgoing(original.put((String) key, unwrapIncoming(value))); - } - - // Performance - @Override - public Object get(Object key) { - return wrapOutgoing(original.get(key)); - } - - @Override - public Object remove(Object key) { - return wrapOutgoing(original.remove(key)); - } - - @Override - public boolean containsKey(Object key) { - return original.containsKey(key); - } - - @Override - public Set> entrySet() { - return new AbstractSet>() { - - @Override - public boolean add(Entry e) { - String key = e.getKey(); - Object value = e.getValue(); - - original.put(key, unwrapIncoming(value)); - return true; - } - - @Override - public int size() { - return original.size(); - } - - @Override - public Iterator> iterator() { - return ConvertedMap.this.iterator(); - } - }; - } - - private Iterator> iterator() { - final Iterator> proxy = original.entrySet().iterator(); - - return new Iterator>() { - - @Override - public boolean hasNext() { - return proxy.hasNext(); - } - - @Override - public Entry next() { - Entry entry = proxy.next(); - - return new SimpleEntry(entry.getKey(), wrapOutgoing(entry.getValue())); - } - - @Override - public void remove() { - proxy.remove(); - } - }; - } - - @Override - public Object getHandle() { - return handle; - } - } - - /** - * Represents a list that wraps another list and converts elements of its - * type and another exposed type. - * - * @author Kristian - */ - private class ConvertedList extends AbstractList implements Wrapper { - - private final Object handle; - - private final List original; - private final CachedNativeWrapper cache = new CachedNativeWrapper(); - - public ConvertedList(Object handle, List original) { - if (NBT_LIST_TYPE == null) - NBT_LIST_TYPE = getField(handle, null, "type"); - this.handle = handle; - this.original = original; - } - - protected Object wrapOutgoing(Object value) { - return cache.wrap(value); - } - - protected Object unwrapIncoming(Object wrapped) { - return unwrapValue(wrapped); - } - - @Override - public Object get(int index) { - return wrapOutgoing(original.get(index)); - } - - @Override - public int size() { - return original.size(); - } - - @Override - public Object set(int index, Object element) { - return wrapOutgoing(original.set(index, unwrapIncoming(element))); - } - - @Override - public void add(int index, Object element) { - Object nbt = unwrapIncoming(element); - - // Set the list type if its the first element - if (size() == 0) - setFieldValue(NBT_LIST_TYPE, handle, (byte) getNbtType(nbt).id); - original.add(index, nbt); - } - - @Override - public Object remove(int index) { - return wrapOutgoing(original.remove(index)); - } - - @Override - public boolean remove(Object o) { - return original.remove(unwrapIncoming(o)); - } - - @Override - public Object getHandle() { - return handle; - } - } - - /** - * Represents a method for loading an NBT compound. - * - * @author Kristian - */ - private static abstract class LoadCompoundMethod { - - protected Method staticMethod; - - protected void setMethod(Method method) { - this.staticMethod = method; - this.staticMethod.setAccessible(true); - } - - /** - * Load an NBT compound from a given stream. - * - * @param input - * - the input stream. - * @return The loaded NBT compound. - */ - public abstract Object loadNbt(DataInput input); - } - - /** - * Load an NBT compound from the NBTCompressedStreamTools static method in - * 1.7.2 - 1.7.5 - */ - private static class LoadMethodWorldUpdate extends LoadCompoundMethod { - - public LoadMethodWorldUpdate(Class streamClass) { - setMethod(getMethod(Modifier.STATIC, 0, streamClass, null, DataInput.class)); - } - - @Override - public Object loadNbt(DataInput input) { - return invokeMethod(staticMethod, null, input); - } - } - - /** - * Load an NBT compound from the NBTCompressedStreamTools static method in - * 1.7.8 - */ - private static class LoadMethodSkinUpdate extends LoadCompoundMethod { - - private Object readLimiter; - - public LoadMethodSkinUpdate(Class streamClass, - Class readLimiterClass) { - setMethod(getMethod(Modifier.STATIC, 0, streamClass, null, DataInput.class, readLimiterClass)); - - // Find the unlimited read limiter - for (Field field : readLimiterClass.getDeclaredFields()) { - if (readLimiterClass.isAssignableFrom(field.getType())) { - try { - readLimiter = field.get(null); - } catch (Exception e) { - throw new RuntimeException("Cannot retrieve read limiter.", e); - } - } - } - } - - @Override - public Object loadNbt(DataInput input) { - return invokeMethod(staticMethod, null, input, readLimiter); - } - } -} diff --git a/src/main/java/fr/xephi/authme/cache/backup/FileCache.java b/src/main/java/fr/xephi/authme/cache/backup/FileCache.java index be8eef292..948598069 100644 --- a/src/main/java/fr/xephi/authme/cache/backup/FileCache.java +++ b/src/main/java/fr/xephi/authme/cache/backup/FileCache.java @@ -414,8 +414,13 @@ public class FileCache { double amount = Double.parseDouble(args[2]); Operation operation = Operation.fromId(Integer.parseInt(args[3])); UUID uuid = UUID.fromString(args[4]); - Attribute attribute = new Attribute(new Builder(amount, operation, type, name, uuid)); - attributes.add(attribute); + Builder build = Attribute.newBuilder(); + build.amount(amount); + build.operation(operation); + build.type(type); + build.name(name); + build.uuid(uuid); + attributes.add(build.build()); } catch (Exception e) { } } @@ -488,8 +493,13 @@ public class FileCache { double amount = Double.parseDouble(args[2]); Operation operation = Operation.fromId(Integer.parseInt(args[3])); UUID uuid = UUID.fromString(args[4]); - Attribute attribute = new Attribute(new Builder(amount, operation, type, name, uuid)); - attributes.add(attribute); + Builder build = Attribute.newBuilder(); + build.amount(amount); + build.operation(operation); + build.type(type); + build.name(name); + build.uuid(uuid); + attributes.add(build.build()); } catch (Exception e) { } }