From d359f7455f01a57be629e306a46a88f1daff1d52 Mon Sep 17 00:00:00 2001 From: Dan Mulloy Date: Wed, 2 Mar 2016 18:08:01 -0500 Subject: [PATCH] [Breaking] Update data watchers for 1.9 Basically I had to rewrite the classes since Mojang decided to muck the whole thing up. If there are any methods necessary, I'll try to readd them. --- .../protocol/utility/MinecraftReflection.java | 36 +- .../protocol/wrappers/WrappedDataWatcher.java | 871 +++--------------- .../wrappers/WrappedWatchableObject.java | 507 +--------- .../protocol/events/PacketContainerTest.java | 60 +- .../wrappers/WrappedDataWatcherTest.java | 37 + .../wrappers/WrappedWatchableObjectTest.java | 8 +- 6 files changed, 246 insertions(+), 1273 deletions(-) create mode 100644 ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/WrappedDataWatcherTest.java diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java index e5f20495..7985080a 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java @@ -63,7 +63,6 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers; import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; import com.comphenix.protocol.utility.RemappedClassSource.RemapperUnavaibleException; import com.comphenix.protocol.utility.RemappedClassSource.RemapperUnavaibleException.Reason; -import com.comphenix.protocol.wrappers.WrappedDataWatcher; import com.comphenix.protocol.wrappers.nbt.NbtFactory; import com.comphenix.protocol.wrappers.nbt.NbtType; import com.google.common.base.Joiner; @@ -1247,11 +1246,8 @@ public class MinecraftReflection { * @return The ChunkPosition class. */ public static Class getChunkCoordinatesClass() { - try { - return getMinecraftClass("ChunkCoordinates"); - } catch (RuntimeException e) { - return setMinecraftClass("ChunkCoordinates", WrappedDataWatcher.getTypeClass(6)); - } + // TODO Figure out a fallback + return getMinecraftClass("ChunkCoordinates"); } /** @@ -1288,6 +1284,10 @@ public class MinecraftReflection { * @return The WatchableObject class. */ public static Class getWatchableObjectClass() { + if (dataWatcherItemExists()) { + return getDataWatcherItemClass(); + } + try { return getMinecraftClass("WatchableObject", "DataWatcher$WatchableObject"); } catch (RuntimeException e) { @@ -1303,6 +1303,30 @@ public class MinecraftReflection { } } + public static Class getDataWatcherItemClass() { + // TODO Implement a fallback + return getMinecraftClass("DataWatcher$Item"); + } + + public static Class getDataWatcherObjectClass() { + // TODO Implement a fallback + return getMinecraftClass("DataWatcherObject"); + } + + public static Class getDataWatcherSerializerClass() { + // TODO Implement a fallback + return getMinecraftClass("DataWatcherSerializer"); + } + + public static boolean dataWatcherItemExists() { + try { + getDataWatcherItemClass(); + return true; + } catch (RuntimeException e) { + return false; + } + } + /** * Retrieve the ServerConnection abstract class. * @return The ServerConnection class. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java index b7a90fe5..b1be39b0 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java @@ -16,345 +16,92 @@ */ package com.comphenix.protocol.wrappers; -import java.lang.reflect.Constructor; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; -import java.util.HashSet; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.Map.Entry; -import javax.annotation.Nullable; - -import org.bukkit.entity.Entity; import org.bukkit.inventory.ItemStack; -import com.comphenix.protocol.injector.BukkitUnwrapper; import com.comphenix.protocol.reflect.FieldAccessException; -import com.comphenix.protocol.reflect.FieldUtils; import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.accessors.Accessors; import com.comphenix.protocol.reflect.accessors.ConstructorAccessor; -import com.comphenix.protocol.reflect.accessors.FieldAccessor; -import com.comphenix.protocol.reflect.accessors.ReadOnlyFieldAccessor; +import com.comphenix.protocol.reflect.accessors.MethodAccessor; import com.comphenix.protocol.utility.MinecraftReflection; -import com.comphenix.protocol.wrappers.collection.ConvertedMap; -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; -/** - * Wraps a DataWatcher that is used to transmit arbitrary key-value pairs with a given entity. - * - * @author Kristian - */ public class WrappedDataWatcher extends AbstractWrapper implements Iterable { - /** - * Every custom watchable type in the Spigot protocol hack. - * @author Kristian - */ - public enum CustomType { - BYTE_SHORT("org.spigotmc.ProtocolData$ByteShort", 0, short.class), - DUAL_BYTE("org.spigotmc.ProtocolData$DualByte", 0, byte.class, byte.class), - HIDDEN_BYTE("org.spigotmc.ProtocolData$HiddenByte", 0, byte.class), - INT_BYTE("org.spigotmc.ProtocolData$IntByte", 2, int.class, byte.class), - DUAL_INT("org.spigotmc.ProtocolData$DualInt", 2, int.class, int.class); + private static ConstructorAccessor dataWatcherObjectConstructor = null; + private static MethodAccessor getter = null; + private static MethodAccessor setter = null; - private Class spigotClass; - private ConstructorAccessor constructor; - private FieldAccessor secondaryValue; - private int typeId; - - private CustomType(String className, int typeId, Class... parameters) { - try { - this.spigotClass = Class.forName(className); - this.constructor = Accessors.getConstructorAccessor(spigotClass, parameters); - this.secondaryValue = parameters.length > 1 ? Accessors.getFieldAccessor(spigotClass, "value2", true) : null; - } catch (ClassNotFoundException e) { - // ProtocolLibrary.log(Level.WARNING, "Unable to find " + className); - this.spigotClass = null; - } - - this.typeId = typeId; - } - - /** - * Construct a new instance of this Spigot type. - * @param value - the value. Cannot be NULL. - * @return The instance to construct. - */ - Object newInstance(Object value) { - return newInstance(value, null); - } - - /** - * Construct a new instance of this Spigot type. - *

- * The secondary value may be NULL if this custom type does not contain a secondary value. - * @param value - the value. - * @param secondary - optional secondary value. - * @return - */ - Object newInstance(Object value, Object secondary) { - Preconditions.checkNotNull(value, "value cannot be NULL."); - - if (hasSecondary()) { - return constructor.invoke(value, secondary); - } else { - if (secondary != null) { - throw new IllegalArgumentException("Cannot construct " + this + " with a secondary value"); - } - return constructor.invoke(value); - } - } - - /** - * Set the secondary value of a given type. - * @param instance - the instance. - * @param secondary - the secondary value. - */ - void setSecondary(Object instance, Object secondary) { - if (!hasSecondary()) { - throw new IllegalArgumentException(this + " does not have a secondary value."); - } - secondaryValue.set(instance, secondary); - } - - /** - * Get the secondary value of a type. - * @param instance - the instance. - * @return The secondary value. - */ - Object getSecondary(Object instance) { - if (!hasSecondary()) { - throw new IllegalArgumentException(this + " does not have a secondary value."); - } - return secondaryValue.get(instance); - } - - /** - * Determine if this type has a secondary value. - * @return TRUE if it does, FALSE otherwise. - */ - public boolean hasSecondary() { - return secondaryValue != null; - } - - /** - * Underlying Spigot class. - * @return The class. - */ - public Class getSpigotClass() { - return spigotClass; - } - - /** - * The equivalent type ID. - * @return The equivalent ID. - */ - public int getTypeId() { - return typeId; - } - - /** - * Retrieve the custom Spigot type of a value. - * @param value - the value. - * @return The Spigot type, or NULL if not found. - */ - public static CustomType fromValue(Object value) { - for (CustomType type : CustomType.values()) { - if (type.getSpigotClass().isInstance(value)) { - return type; - } - } - return null; - } - } - - /** - * Used to assign integer IDs to given types. - */ - private static Map, Integer> TYPE_MAP; - - /** - * Custom types in the bountiful update. - */ - private static Map, Integer> CUSTOM_MAP; - - // Accessors - private static FieldAccessor TYPE_MAP_ACCESSOR; - private static FieldAccessor VALUE_MAP_ACCESSOR; - - // Fields - private static Field READ_WRITE_LOCK_FIELD; - private static Field ENTITY_FIELD; - - // Methods - private static Method CREATE_KEY_VALUE_METHOD; - private static Method UPDATE_KEY_VALUE_METHOD; - private static Method GET_KEY_VALUE_METHOD; - - // Constructors - private static Constructor CREATE_DATA_WATCHER_CONSTRUCTOR; - - // Entity methods - private volatile static Field ENTITY_DATA_FIELD; - - /** - * Whether or not this class has already been initialized. - */ - private static boolean HAS_INITIALIZED; - - // Lock - private ReadWriteLock readWriteLock; - - // Map of watchable objects - private Map watchableObjects; - - // A map view of all the watchable objects - private Map mapView; - - /** - * Initialize a new data watcher. - * @throws FieldAccessException If we're unable to wrap a DataWatcher. - */ + @Deprecated public WrappedDataWatcher() { super(MinecraftReflection.getDataWatcherClass()); - - // Just create a new watcher - try { - if (MinecraftReflection.isUsingNetty()) { - setHandle(newEntityHandle(null)); - } else { - setHandle(getHandleType().newInstance()); - } - initialize(); - - } catch (Exception e) { - throw new RuntimeException("Unable to construct DataWatcher.", e); - } } - /** - * Create a wrapper for a given data watcher. - * @param handle - the data watcher to wrap. - * @throws FieldAccessException If we're unable to wrap a DataWatcher. - */ public WrappedDataWatcher(Object handle) { - super(MinecraftReflection.getDataWatcherClass()); - setHandle(handle); - initialize(); - } - - /** - * Construct a new data watcher with the given entity. - *

- * In 1.6.4 and ealier, this will fall back to using {@link #WrappedDataWatcher()}. - * @param entity - the entity. - * @return The wrapped data watcher. - */ - public static WrappedDataWatcher newWithEntity(Entity entity) { - // Use the old constructor - if (!MinecraftReflection.isUsingNetty()) - return new WrappedDataWatcher(); - return new WrappedDataWatcher(newEntityHandle(entity)); - } - - /** - * Construct a new native DataWatcher with the given entity. - *

- * Warning: This is only supported in 1.7.2 and above. - * @param entity - the entity, or NULL. - * @return The data watcher. - */ - private static Object newEntityHandle(Entity entity) { - Class dataWatcher = MinecraftReflection.getDataWatcherClass(); - - try { - if (CREATE_DATA_WATCHER_CONSTRUCTOR == null) - CREATE_DATA_WATCHER_CONSTRUCTOR = dataWatcher.getConstructor(MinecraftReflection.getEntityClass()); - - return CREATE_DATA_WATCHER_CONSTRUCTOR.newInstance( - BukkitUnwrapper.getInstance().unwrapItem(entity) - ); - } catch (Exception e) { - throw new RuntimeException("Cannot construct data watcher.", e); - } - } - - /** - * Create a new data watcher for a list of watchable objects. - *

- * Note that the watchable objects are not cloned, and will be modified in place. Use "deepClone" if - * that is not desirable. - *

- * The {@link #removeObject(int)} method will not modify the given list, however. - * - * @param watchableObjects - list of watchable objects that will be copied. - * @throws FieldAccessException Unable to read watchable objects. - */ - public WrappedDataWatcher(List watchableObjects) throws FieldAccessException { this(); + setHandle(handle); + } - Lock writeLock = getReadWriteLock().writeLock(); - Map map = getWatchableObjectMap(); + @Override + public Iterator iterator() { + return getWatchableObjects().iterator(); + } - writeLock.lock(); + public List getWatchableObjects() { + return new ArrayList<>(asMap().values()); + } + + @SuppressWarnings("unchecked") + public Map asMap() { + FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType, true); + List candidates = fuzzy.getFieldListByType(Map.class); + + Field match = null; + for (Field candidate : candidates) { + if (Modifier.isStatic(candidate.getModifiers())) { + // This is the entity class to current index map, which we really don't have a use for + } else { + // This is the map we're looking for + match = candidate; + } + } + + if (match == null) { + throw new FieldAccessException("Could not find index -> object map."); + } + + Map map = null; try { - // Add the watchable objects by reference - for (WrappedWatchableObject watched : watchableObjects) { - map.put(watched.getIndex(), watched.handle); - } - } finally { - writeLock.unlock(); - } - } - - /** - * Retrieve the ID of a given type, if it's allowed to be watched. - * @param clazz - Class to check for. - * @return The ID, or NULL if it cannot be watched. - * @throws FieldAccessException If we cannot initialize the reflection machinery. - */ - public static Integer getTypeID(Class clazz) throws FieldAccessException { - initialize(); - Integer result = TYPE_MAP.get(WrappedWatchableObject.getUnwrappedType(clazz)); - - if (result == null) { - result = CUSTOM_MAP.get(clazz); - } - return result; - } - - /** - * Retrieve the type of a given ID, if it's allowed to be watched. - * @param id - id to get type for - * @return The type using a given ID, or NULL if it cannot be watched. - * @throws FieldAccessException If we cannot initialize the reflection machinery. - */ - public static Class getTypeClass(int id) throws FieldAccessException { - initialize(); - - for (Map.Entry, Integer> entry : TYPE_MAP.entrySet()) { - if (Objects.equal(entry.getValue(), id)) { - return entry.getKey(); - } + match.setAccessible(true); + map = (Map) match.get(handle); + } catch (IllegalArgumentException e) { + throw new FieldAccessException(e); + } catch (IllegalAccessException e) { + throw new FieldAccessException(e); } - // Unknown class type - return null; + Map ret = new HashMap<>(); + for (Entry entry : map.entrySet()) { + ret.put(entry.getKey(), new WrappedWatchableObject(entry.getValue())); + } + + return ret; } + public int size() { + return asMap().size(); + } + + // ---- Object Getters + /** * Get a watched byte. * @param index - index of the watched byte. @@ -432,49 +179,60 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable getWatchableObjects() throws FieldAccessException { - Lock readLock = getReadWriteLock().readLock(); - readLock.lock(); + private Object getWatchedObject(int index) { + if (dataWatcherObjectConstructor == null) { + dataWatcherObjectConstructor = Accessors.getConstructorAccessor(MinecraftReflection.getDataWatcherObjectClass().getConstructors()[0]); + } - try { - List result = new ArrayList(); + Object object = dataWatcherObjectConstructor.invoke(index, null); - // Add each watchable object to the list - for (Object watchable : getWatchableObjectMap().values()) { - if (watchable != null) { - result.add(new WrappedWatchableObject(watchable)); - } else { - result.add(null); - } - } - return result; + if (getter == null) { + getter = Accessors.getMethodAccessor(handleType, "get", object.getClass()); + } - } finally { - readLock.unlock(); - } - } + return getter.invoke(handle, object); + } + + // ---- Object Setters + + public void setObject(int index, Object value) { + if (dataWatcherObjectConstructor == null) { + dataWatcherObjectConstructor = Accessors.getConstructorAccessor(MinecraftReflection.getDataWatcherObjectClass().getConstructors()[0]); + } + + Object object = dataWatcherObjectConstructor.invoke(index, null); + + if (setter == null) { + setter = Accessors.getMethodAccessor(handleType, "set", object.getClass(), Object.class); + } + + setter.invoke(handle, object, value); + } + + public WrappedDataWatcher deepClone() { + // TODO This + return null; + } + + @Deprecated + @SuppressWarnings("unused") + public static Integer getTypeID(Class clazz) { + return null; + } + + @Deprecated + @SuppressWarnings("unused") + public static Class getTypeClass(int typeID) { + return null; + } @Override public boolean equals(Object obj) { - // Quick checks - if (obj == this) - return true; - if (obj == null) - return false; + if (obj == this) return true; + if (obj == null) return false; if (obj instanceof WrappedDataWatcher) { WrappedDataWatcher other = (WrappedDataWatcher) obj; @@ -484,13 +242,15 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable indexSet() throws FieldAccessException { - Lock readLock = getReadWriteLock().readLock(); - readLock.lock(); - - try { - return new HashSet(getWatchableObjectMap().keySet()); - } finally { - readLock.unlock(); - } - } - - /** - * Clone the content of the current DataWatcher. - * @return A cloned data watcher. - */ - public WrappedDataWatcher deepClone() { - WrappedDataWatcher clone = new WrappedDataWatcher(); - - // Make a new copy instead - for (WrappedWatchableObject watchable : this) { - clone.setObject(watchable.getIndex(), watchable.getClonedValue()); - } - return clone; - } - - /** - * Retrieve the number of watched objects. - * @return Watched object count. - * @throws FieldAccessException If we're unable to read the underlying object. - */ - public int size() throws FieldAccessException { - Lock readLock = getReadWriteLock().readLock(); - readLock.lock(); - - try { - return getWatchableObjectMap().size(); - } finally { - readLock.unlock(); - } - } - - /** - * Remove a given object from the underlying DataWatcher. - * @param index - index of the object to remove. - * @return The watchable object that was removed, or NULL If none could be found. - */ - public WrappedWatchableObject removeObject(int index) { - Lock writeLock = getReadWriteLock().writeLock(); - writeLock.lock(); - - try { - Object removed = getWatchableObjectMap().remove(index); - return removed != null ? new WrappedWatchableObject(removed) : null; - } finally { - writeLock.unlock(); - } - } - - /** - * Set a watched byte. - * @param index - index of the watched byte. - * @param newValue - the new watched value. - * @throws FieldAccessException Cannot read underlying field. - */ - public void setObject(int index, Object newValue) throws FieldAccessException { - setObject(index, newValue, true); - } - - /** - * Set a watched byte. - * @param index - index of the watched byte. - * @param newValue - the new watched value. - * @param update - whether or not to refresh every listening client. - * @throws FieldAccessException Cannot read underlying field. - */ - public void setObject(int index, Object newValue, boolean update) throws FieldAccessException { - // Aquire write lock - Lock writeLock = getReadWriteLock().writeLock(); - writeLock.lock(); - - try { - Object watchable = getWatchedObject(index); - - if (watchable != null) { - new WrappedWatchableObject(watchable).setValue(newValue, update); - } else { - CREATE_KEY_VALUE_METHOD.invoke(handle, index, WrappedWatchableObject.getUnwrapped(newValue)); - } - - // Handle invoking the method - } catch (IllegalArgumentException e) { - throw new FieldAccessException("Cannot convert arguments.", e); - } catch (IllegalAccessException e) { - throw new FieldAccessException("Illegal access.", e); - } catch (InvocationTargetException e) { - throw new FieldAccessException("Checked exception in Minecraft.", e); - } finally { - writeLock.unlock(); - } - } - - /** - * Set a watched byte with an optional secondary value. - * @param index - index of the watched byte. - * @param newValue - the new watched value. - * @param secondary - optional secondary value. - * @param update - whether or not to refresh every listening client. - * @param type - custom type. - * @throws FieldAccessException If we cannot read the underlying field. - */ - public void setObject(int index, Object newValue, Object secondary, boolean update, CustomType type) throws FieldAccessException { - Object created = type.newInstance(newValue, secondary); - - // Now update the watcher - setObject(index, created, update); - } - - private Object getWatchedObject(int index) throws FieldAccessException { - // We use the get-method first and foremost - if (GET_KEY_VALUE_METHOD != null) { - try { - return GET_KEY_VALUE_METHOD.invoke(handle, index); - } catch (Exception e) { - throw new FieldAccessException("Cannot invoke get key method for index " + index, e); - } - } else { - try { - getReadWriteLock().readLock().lock(); - return getWatchableObjectMap().get(index); - - } finally { - getReadWriteLock().readLock().unlock(); - } - } - } - - /** - * Retrieve the current read write lock. - * @return Current read write lock. - * @throws FieldAccessException If we're unable to read the underlying field. - */ - protected ReadWriteLock getReadWriteLock() throws FieldAccessException { - try { - // Cache the read write lock - if (readWriteLock != null) - return readWriteLock; - else if (READ_WRITE_LOCK_FIELD != null) - return readWriteLock = (ReadWriteLock) FieldUtils.readField(READ_WRITE_LOCK_FIELD, handle, true); - else - return readWriteLock = new ReentrantReadWriteLock(); - } catch (IllegalAccessException e) { - throw new FieldAccessException("Unable to read lock field.", e); - } - } - - /** - * Retrieve the underlying map of key values that stores watchable objects. - * @return A map of watchable objects. - * @throws FieldAccessException If we don't have permission to perform reflection. - */ - @SuppressWarnings("unchecked") - protected Map getWatchableObjectMap() throws FieldAccessException { - if (watchableObjects == null) - watchableObjects = (Map) VALUE_MAP_ACCESSOR.get(handle); - return watchableObjects; - } - - /** - * Retrieve the data watcher associated with an entity. - * @param entity - the entity to read from. - * @return Associated data watcher. - * @throws FieldAccessException Reflection failed. - */ - public static WrappedDataWatcher getEntityWatcher(Entity entity) throws FieldAccessException { - if (ENTITY_DATA_FIELD == null) - ENTITY_DATA_FIELD = FuzzyReflection.fromClass(MinecraftReflection.getEntityClass(), true). - getFieldByType("datawatcher", MinecraftReflection.getDataWatcherClass()); - - BukkitUnwrapper unwrapper = new BukkitUnwrapper(); - - try { - Object nsmWatcher = FieldUtils.readField(ENTITY_DATA_FIELD, unwrapper.unwrapItem(entity), true); - - if (nsmWatcher != null) - return new WrappedDataWatcher(nsmWatcher); - else - return null; - - } catch (IllegalAccessException e) { - throw new FieldAccessException("Cannot access DataWatcher field.", e); - } - } - - /** - * Invoked when a data watcher is first used. - */ - @SuppressWarnings("unchecked") - private static void initialize() throws FieldAccessException { - // This method should only be run once, even if an exception is thrown - if (!HAS_INITIALIZED) - HAS_INITIALIZED = true; - else - return; - - FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getDataWatcherClass(), true); - - for (Field lookup : fuzzy.getFieldListByType(Map.class)) { - if (Modifier.isStatic(lookup.getModifiers())) { - // This must be the type map - TYPE_MAP_ACCESSOR = Accessors.getFieldAccessor(lookup, true); - } else { - // If not, then we're probably dealing with the value map - VALUE_MAP_ACCESSOR = Accessors.getFieldAccessor(lookup, true); - } - } - // Spigot workaround (not necessary - initializeSpigot(fuzzy); - - // Any custom types - CUSTOM_MAP = initializeCustom(); - - // Initialize static type type - TYPE_MAP = (Map, Integer>) TYPE_MAP_ACCESSOR.get(null); - - try { - READ_WRITE_LOCK_FIELD = fuzzy.getFieldByType("readWriteLock", ReadWriteLock.class); - } catch (IllegalArgumentException e) { - // It's not a big deal - } - - // Check for the entity field as well - if (MinecraftReflection.isUsingNetty()) { - ENTITY_FIELD = fuzzy.getFieldByType("entity", MinecraftReflection.getEntityClass()); - ENTITY_FIELD.setAccessible(true); - } - initializeMethods(fuzzy); - } - - // For Spigot's bountiful update patch - private static Map, Integer> initializeCustom() { - Map, Integer> map = Maps.newHashMap(); - - for (CustomType type : CustomType.values()) { - if (type.getSpigotClass() != null) { - map.put(type.getSpigotClass(), type.getTypeId()); - } - } - return map; - } - - // TODO: Remove, as this was fixed in build #1189 of Spigot - private static void initializeSpigot(FuzzyReflection fuzzy) { - // See if the workaround is needed - if (TYPE_MAP_ACCESSOR != null && VALUE_MAP_ACCESSOR != null) - return; - - for (Field lookup : fuzzy.getFields()) { - final Class type = lookup.getType(); - - if (TroveWrapper.isTroveClass(type)) { - // Create a wrapper accessor - final ReadOnlyFieldAccessor accessor = TroveWrapper.wrapMapField( - Accessors.getFieldAccessor(lookup, true), new Function() { - @Override - public Integer apply(@Nullable Integer value) { - // Do not use zero for no entry value - if (value == 0) - return -1; - return value; - } - }); - - if (Modifier.isStatic(lookup.getModifiers())) { - TYPE_MAP_ACCESSOR = accessor; - } else { - VALUE_MAP_ACCESSOR = accessor; - } - } - } - - if (TYPE_MAP_ACCESSOR == null) - throw new IllegalArgumentException("Unable to find static type map."); - if (VALUE_MAP_ACCESSOR == null) - throw new IllegalArgumentException("Unable to find static value map."); - } - - private static void initializeMethods(FuzzyReflection fuzzy) { - List candidates = fuzzy.getMethodListByParameters(Void.TYPE, - new Class[] { int.class, Object.class}); - - // Load the get-method - try { - GET_KEY_VALUE_METHOD = fuzzy.getMethodByParameters( - "getWatchableObject", MinecraftReflection.getWatchableObjectClass(), new Class[] { int.class }); - GET_KEY_VALUE_METHOD.setAccessible(true); - - } catch (IllegalArgumentException e) { - // Use the fallback method - } - - for (Method method : candidates) { - if (!method.getName().startsWith("watch")) { - CREATE_KEY_VALUE_METHOD = method; - } else { - UPDATE_KEY_VALUE_METHOD = method; - } - } - - // Did we succeed? - if (UPDATE_KEY_VALUE_METHOD == null || CREATE_KEY_VALUE_METHOD == null) { - // Go by index instead - if (candidates.size() > 1) { - CREATE_KEY_VALUE_METHOD = candidates.get(0); - UPDATE_KEY_VALUE_METHOD = candidates.get(1); - } else { - //throw new IllegalStateException("Unable to find create and update watchable object. Update ProtocolLib."); - } - - // Be a little scientist - see if this in fact IS the right way around - try { - WrappedDataWatcher watcher = new WrappedDataWatcher(); - watcher.setObject(0, 0); - watcher.setObject(0, 1); - - if (watcher.getInteger(0) != 1) { - throw new IllegalStateException("This cannot be!"); - } - } catch (Exception e) { - // Nope - UPDATE_KEY_VALUE_METHOD = candidates.get(0); - CREATE_KEY_VALUE_METHOD = candidates.get(1); - } - } - } - - @Override - public Iterator iterator() { - // We'll wrap the iterator instead of creating a new list every time - return Iterators.transform(getWatchableObjectMap().values().iterator(), - new Function() { - - @Override - public WrappedWatchableObject apply(@Nullable Object item) { - if (item != null) - return new WrappedWatchableObject(item); - else - return null; - } - }); - } - - /** - * Retrieve a view of this DataWatcher as a map. - *

- * Any changes to the map will be reflected in this DataWatcher, and vice versa. - * @return A view of the data watcher as a map. - */ - public Map asMap() { - // Construct corresponding map - if (mapView == null) { - mapView = new ConvertedMap(getWatchableObjectMap()) { - @Override - protected Object toInner(WrappedWatchableObject outer) { - if (outer == null) - return null; - return outer.getHandle(); - } - - @Override - protected WrappedWatchableObject toOuter(Object inner) { - if (inner == null) - return null; - return new WrappedWatchableObject(inner); - } - }; - } - return mapView; - } - - @Override - public String toString() { - return asMap().toString(); - } - - /** - * Retrieve the entity associated with this data watcher. - *

- * Warning: This is only supported on 1.7.2 and above. - * @return The entity, or NULL. - */ - public Entity getEntity() { - if (!MinecraftReflection.isUsingNetty()) - throw new IllegalStateException("This method is only supported on 1.7.2 and above."); - - try { - return (Entity) MinecraftReflection.getBukkitEntity(ENTITY_FIELD.get(handle)); - } catch (Exception e) { - throw new RuntimeException("Unable to retrieve entity.", e); - } - } - - /** - * Set the entity associated with this data watcher. - *

- * Warning: This is only supported on 1.7.2 and above. - * @param entity - the new entity. - */ - public void setEntity(Entity entity) { - if (!MinecraftReflection.isUsingNetty()) - throw new IllegalStateException("This method is only supported on 1.7.2 and above."); - - try { - ENTITY_FIELD.set(handle, BukkitUnwrapper.getInstance().unwrapItem(entity)); - } catch (Exception e) { - throw new RuntimeException("Unable to set entity.", e); - } - } } \ No newline at end of file diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java index 55e3caeb..16941fc2 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java @@ -1,467 +1,42 @@ -/* - * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. - * Copyright (C) 2012 Kristian S. Stangeland - * - * This program is free software; you can redistribute it and/or modify it under the terms of the - * GNU General Public License as published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program; - * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307 USA - */ - -package com.comphenix.protocol.wrappers; - -import java.lang.reflect.Constructor; - -import org.bukkit.inventory.ItemStack; - -import com.comphenix.protocol.reflect.EquivalentConverter; -import com.comphenix.protocol.reflect.FieldAccessException; -import com.comphenix.protocol.reflect.StructureModifier; -import com.comphenix.protocol.reflect.instances.DefaultInstances; -import com.comphenix.protocol.utility.MinecraftReflection; -import com.google.common.base.Objects; - -/** - * Represents a watchable object. - * - * @author Kristian - */ -public class WrappedWatchableObject extends AbstractWrapper { - // Whether or not the reflection machinery has been initialized - private static boolean hasInitialized; - - // The field containing the value itself - private static StructureModifier baseModifier; - - // Used to create new watchable objects - private static Constructor watchableConstructor; - - // The watchable object class type - private static Class watchableObjectClass; - - protected StructureModifier modifier; - - // Type of the stored value - private Class typeClass; - - /** - * Wrap a given raw Minecraft watchable object. - * @param handle - the raw watchable object to wrap. - */ - public WrappedWatchableObject(Object handle) { - super(MinecraftReflection.getWatchableObjectClass()); - load(handle); - } - - /** - * Construct a watchable object from an index and a given value. - * @param index - the index. - * @param value - non-null value of specific types. - */ - public WrappedWatchableObject(int index, Object value) { - super(MinecraftReflection.getWatchableObjectClass()); - - if (value == null) - throw new IllegalArgumentException("Value cannot be NULL."); - - // Get the correct type ID - Integer typeID = WrappedDataWatcher.getTypeID(value.getClass()); - - if (typeID != null) { - if (watchableConstructor == null) { - try { - watchableConstructor = MinecraftReflection.getWatchableObjectClass(). - getConstructor(int.class, int.class, Object.class); - } catch (Exception e) { - throw new RuntimeException("Cannot get the WatchableObject(int, int, Object) constructor.", e); - } - } - - // Create the object - try { - load(watchableConstructor.newInstance(typeID, index, getUnwrapped(value))); - } catch (Exception e) { - throw new RuntimeException("Cannot construct underlying WatchableObject.", e); - } - } else { - throw new IllegalArgumentException("Cannot watch the type " + value.getClass()); - } - } - -// /** -// * Construct a custom watchable object from an index, value and custom type. -// * @param index - the index. -// * @param primary - non-null value of specific types. -// * @param secondary - optional secondary value, if the type can store it. -// * @param type - the custom Spigot type. -// */ -// public WrappedWatchableObject(int index, Object value, Object secondary, CustomType type) { -// this(index, type.newInstance(value, secondary)); -// } - - // Wrap a NMS object - private void load(Object handle) { - initialize(); - this.handle = handle; - this.modifier = baseModifier.withTarget(handle); - - // Make sure the type is correct - if (!watchableObjectClass.isAssignableFrom(handle.getClass())) { - throw new ClassCastException("Cannot cast the class " + handle.getClass().getName() + - " to " + watchableObjectClass.getName()); - } - } - - /** - * Initialize reflection machinery. - */ - private static void initialize() { - if (!hasInitialized) { - hasInitialized = true; - watchableObjectClass = MinecraftReflection.getWatchableObjectClass(); - baseModifier = new StructureModifier(watchableObjectClass, null, false); - } - } - -// /** -// * Retrieve the custom type of this object. -// * @return The custom type, or NULL if not applicable. -// */ -// public CustomType getCustomType() { -// return CustomType.fromValue(getValue()); -// } - - /** - * Retrieve the correct super type of the current value. - * @return Super type. - * @throws FieldAccessException Unable to read values. - */ - public Class getType() throws FieldAccessException { - return getWrappedType(getTypeRaw()); - } - - /** - * Retrieve the correct super type of the current value, given the raw NMS object. - * @return Super type. - * @throws FieldAccessException Unable to read values. - */ - private Class getTypeRaw() throws FieldAccessException { - if (typeClass == null) { - typeClass = WrappedDataWatcher.getTypeClass(getTypeID()); - - if (typeClass == null) { - throw new IllegalStateException("Unrecognized data type: " + getTypeID()); - } - } - - return typeClass; - } - - /** - * Retrieve the index of this watchable object. This is used to identify a value. - * @return Object index. - * @throws FieldAccessException Reflection failed. - */ - public int getIndex() throws FieldAccessException { - return modifier.withType(int.class).read(1); - } - - /** - * Set the the index of this watchable object. - * @param index - the new object index. - * @throws FieldAccessException Reflection failed. - */ - public void setIndex(int index) throws FieldAccessException { - modifier.withType(int.class).write(1, index); - } - - /** - * Retrieve the type ID of a watchable object. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Type to Value
Type IDData Type
0Byte
1Short
2Int
3Float
4{@link String}
5{@link org.bukkit.inventory.ItemStack ItemStack}
6*{@link com.comphenix.protocol.wrappers.ChunkPosition ChunkPosition}
- * @return Type ID that identifies the type of the value. - * @throws FieldAccessException Reflection failed. - */ - public int getTypeID() throws FieldAccessException { - return modifier.withType(int.class).read(0); - } - - /** - * Set the type ID of a watchable object. - * @param id - the new ID. - * @throws FieldAccessException Reflection failed. - * @see #getTypeID() - */ - public void setTypeID(int id) throws FieldAccessException { - modifier.withType(int.class).write(0, id); - } - - /** - * Update the value field. - * @param newValue - new value. - * @throws FieldAccessException Unable to use reflection. - */ - public void setValue(Object newValue) throws FieldAccessException { - setValue(newValue, true); - } - - /** - * Update the value field. - * @param newValue - new value. - * @param updateClient - whether or not to update listening clients. - * @throws FieldAccessException Unable to use reflection. - */ - public void setValue(Object newValue, boolean updateClient) throws FieldAccessException { - // Verify a few quick things - if (newValue == null) - throw new IllegalArgumentException("Cannot watch a NULL value."); - if (!getType().isAssignableFrom(newValue.getClass())) - throw new IllegalArgumentException("Object " + newValue + " must be of type " + getType().getName()); - - // See if we should update the client to - if (updateClient) - setDirtyState(true); - - // Use the modifier to set the value - modifier.withType(Object.class).write(0, getUnwrapped(newValue)); - } - - /** - * Read the underlying value field. - * @return The underlying value. - */ - private Object getNMSValue() { - return modifier.withType(Object.class).read(0); - } - - /** - * Read the value field. - * @return The watched value. - * @throws FieldAccessException Unable to use reflection. - */ - public Object getValue() throws FieldAccessException { - return getWrapped(modifier.withType(Object.class).read(0)); - } - -// /** -// * Retrieve the secondary value associated with this watchable object. -// *

-// * This is only applicable for certain {@link CustomType}. -// * @return The secondary value, or NULL if not found. -// */ -// public Object getSecondaryValue() { -// CustomType type = getCustomType(); -// return type != null ? type.getSecondary(getValue()) : null; -// } -// -// /** -// * Set the secondary value. -// * @param secondary - the secondary value. -// * @throws IllegalStateException If this watchable object does not have a secondary value. -// */ -// public void setSecondaryValue(Object secondary) { -// CustomType type = getCustomType(); -// -// if (type == null) { -// throw new IllegalStateException(this + " does not have a custom type."); -// } -// type.setSecondary(getValue(), secondary); -// } - - /** - * Set whether or not the value must be synchronized with the client. - * @param dirty - TRUE if the value should be synchronized, FALSE otherwise. - * @throws FieldAccessException Unable to use reflection. - */ - public void setDirtyState(boolean dirty) throws FieldAccessException { - modifier.withType(boolean.class).write(0, dirty); - } - - /** - * Retrieve whether or not the value must be synchronized with the client. - * @return TRUE if the value should be synchronized, FALSE otherwise. - * @throws FieldAccessException Unable to use reflection. - */ - public boolean getDirtyState() throws FieldAccessException { - return modifier.withType(boolean.class).read(0); - } - - /** - * Retrieve the wrapped object value, if needed. - * @param value - the raw NMS object to wrap. - * @return The wrapped object. - */ - @SuppressWarnings("rawtypes") - static Object getWrapped(Object value) { - // Handle the special cases - if (MinecraftReflection.isItemStack(value)) { - return BukkitConverters.getItemStackConverter().getSpecific(value); - } else if (MinecraftReflection.isChunkCoordinates(value)) { - return new WrappedChunkCoordinate((Comparable) value); - } else { - return value; - } - } - - /** - * Retrieve the wrapped type, if needed. - * @param wrapped - the wrapped class type. - * @return The wrapped class type. - */ - static Class getWrappedType(Class unwrapped) { - if (unwrapped.equals(MinecraftReflection.getChunkPositionClass())) - return ChunkPosition.class; - else if (unwrapped.equals(MinecraftReflection.getBlockPositionClass())) - return BlockPosition.class; - else if (unwrapped.equals(MinecraftReflection.getChunkCoordinatesClass())) - return WrappedChunkCoordinate.class; - else if (unwrapped.equals(MinecraftReflection.getItemStackClass())) - return ItemStack.class; - else - return unwrapped; - } - - /** - * Retrieve the raw NMS value. - * @param wrapped - the wrapped position. - * @return The raw NMS object. - */ - static Object getUnwrapped(Object wrapped) { - // Convert special cases - if (wrapped instanceof ChunkPosition) - return ChunkPosition.getConverter().getGeneric(MinecraftReflection.getChunkPositionClass(), (ChunkPosition) wrapped); - else if (wrapped instanceof BlockPosition) - return BlockPosition.getConverter().getGeneric(MinecraftReflection.getBlockPositionClass(), (BlockPosition) wrapped); - else if (wrapped instanceof WrappedChunkCoordinate) - return ((WrappedChunkCoordinate) wrapped).getHandle(); - else if (wrapped instanceof ItemStack) - return BukkitConverters.getItemStackConverter().getGeneric(MinecraftReflection.getItemStackClass(), (ItemStack) wrapped); - else - return wrapped; - } - - /** - * Retrieve the unwrapped type, if needed. - * @param wrapped - the unwrapped class type. - * @return The unwrapped class type. - */ - static Class getUnwrappedType(Class wrapped) { - if (wrapped.equals(ChunkPosition.class)) - return MinecraftReflection.getChunkPositionClass(); - else if (wrapped.equals(BlockPosition.class)) - return MinecraftReflection.getBlockPositionClass(); - else if (wrapped.equals(WrappedChunkCoordinate.class)) - return MinecraftReflection.getChunkCoordinatesClass(); - else if (ItemStack.class.isAssignableFrom(wrapped)) - return MinecraftReflection.getItemStackClass(); - else - return wrapped; - } - - /** - * Clone the current wrapped watchable object, along with any contained objects. - * @return A deep clone of the current watchable object. - * @throws FieldAccessException If we're unable to use reflection. - */ - public WrappedWatchableObject deepClone() throws FieldAccessException { - WrappedWatchableObject clone = new WrappedWatchableObject( - DefaultInstances.DEFAULT.getDefault(MinecraftReflection.getWatchableObjectClass())); - - clone.setDirtyState(getDirtyState()); - clone.setIndex(getIndex()); - clone.setTypeID(getTypeID()); - clone.setValue(getClonedValue(), false); - return clone; - } - - // Helper - Object getClonedValue() throws FieldAccessException { - Object value = getNMSValue(); - - // Only a limited set of references types are supported - if (MinecraftReflection.isBlockPosition(value)) { - EquivalentConverter converter = BlockPosition.getConverter(); - return converter.getGeneric(MinecraftReflection.getBlockPositionClass(), converter.getSpecific(value)); - } else if (MinecraftReflection.isChunkPosition(value)) { - EquivalentConverter converter = ChunkPosition.getConverter(); - return converter.getGeneric(MinecraftReflection.getChunkPositionClass(), converter.getSpecific(value)); - } else if (MinecraftReflection.isItemStack(value)) { - return MinecraftReflection.getMinecraftItemStack(MinecraftReflection.getBukkitItemStack(value).clone()); - } else { - // A string or primitive wrapper, which are all immutable. - return value; - } - } - - @Override - public boolean equals(Object obj) { - // Quick checks - if (obj == this) - return true; - if (obj == null) - return false; - - if (obj instanceof WrappedWatchableObject) { - WrappedWatchableObject other = (WrappedWatchableObject) obj; - - return Objects.equal(getIndex(), other.getIndex()) && - Objects.equal(getTypeID(), other.getTypeID()) && - Objects.equal(getValue(), other.getValue()); - } - - // No, this is not equivalent - return false; - } - - @Override - public int hashCode() { - return Objects.hashCode(getIndex(), getTypeID(), getValue()); - } - - @Override - public String toString() { - return String.format("[%s: %s (%s)]", getIndex(), getValue(), getType().getSimpleName()); - } +/** + * (c) 2016 dmulloy2 + */ +package com.comphenix.protocol.wrappers; + +import com.comphenix.protocol.reflect.StructureModifier; +import com.comphenix.protocol.utility.MinecraftReflection; + +/** + * @author dmulloy2 + */ + +public class WrappedWatchableObject extends AbstractWrapper { + + private WrappedWatchableObject() { + super(MinecraftReflection.getDataWatcherItemClass()); + } + + public WrappedWatchableObject(Object handle) { + this(); + setHandle(handle); + } + + public Object getValue() { + return new StructureModifier(handleType).withTarget(this).read(1); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null) return false; + + if (obj instanceof WrappedWatchableObject) { + WrappedWatchableObject other = (WrappedWatchableObject) obj; + return other.handle.equals(handle); + } + + return false; + } + + // TODO Flesh out this class } \ No newline at end of file diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java index 4d26d8ee..1e25c88b 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java @@ -26,6 +26,9 @@ import java.util.List; import java.util.UUID; import net.minecraft.server.v1_9_R1.AttributeModifier; +import net.minecraft.server.v1_9_R1.DataWatcher; +import net.minecraft.server.v1_9_R1.Entity; +import net.minecraft.server.v1_9_R1.EntityLightning; import net.minecraft.server.v1_9_R1.PacketPlayOutUpdateAttributes; import net.minecraft.server.v1_9_R1.PacketPlayOutUpdateAttributes.AttributeSnapshot; @@ -50,7 +53,9 @@ import com.comphenix.protocol.utility.Util; import com.comphenix.protocol.wrappers.BlockPosition; import com.comphenix.protocol.wrappers.WrappedBlockData; import com.comphenix.protocol.wrappers.WrappedChatComponent; +import com.comphenix.protocol.wrappers.WrappedDataWatcher; import com.comphenix.protocol.wrappers.WrappedGameProfile; +import com.comphenix.protocol.wrappers.WrappedWatchableObject; import com.comphenix.protocol.wrappers.nbt.NbtCompound; import com.comphenix.protocol.wrappers.nbt.NbtFactory; import com.google.common.collect.Lists; @@ -61,8 +66,8 @@ import com.google.common.collect.Lists; //@PrepareForTest(CraftItemFactory.class) public class PacketContainerTest { // Helper converters - //private EquivalentConverter watchConvert = BukkitConverters.getDataWatcherConverter(); - //private EquivalentConverter itemConvert = BukkitConverters.getItemStackConverter(); + // private EquivalentConverter watchConvert = BukkitConverters.getDataWatcherConverter(); + // private EquivalentConverter itemConvert = BukkitConverters.getItemStackConverter(); @BeforeClass public static void initializeBukkit() throws IllegalAccessException { @@ -167,6 +172,7 @@ public class PacketContainerTest { } // TODO Find a packet with integer arrays + /*@Test public void testGetIntegerArrays() { // Contains a byte array we will test @@ -278,21 +284,26 @@ public class PacketContainerTest { assertEquals(compound.getList("ages"), result.getList("ages")); } - /*@Test + @Test public void testGetDataWatcherModifier() { PacketContainer mobSpawnPacket = new PacketContainer(PacketType.Play.Server.SPAWN_ENTITY_LIVING); StructureModifier watcherAccessor = mobSpawnPacket.getDataWatcherModifier(); - WrappedDataWatcher dataWatcher = new WrappedDataWatcher(); - dataWatcher.setObject(1, 100); - dataWatcher.setObject(2, 125); + Entity entity = new EntityLightning(null, 0, 0, 0, true); + DataWatcher watcher = entity.getDataWatcher(); + + WrappedDataWatcher dataWatcher = new WrappedDataWatcher(watcher); + dataWatcher.setObject(1, (byte) 1); + dataWatcher.setObject(2, 301); + dataWatcher.setObject(3, true); + dataWatcher.setObject(4, "Lorem"); assertNull(watcherAccessor.read(0)); // Insert and read back watcherAccessor.write(0, dataWatcher); assertEquals(dataWatcher, watcherAccessor.read(0)); - }*/ + } // Unfortunately, it might be too difficult to mock this one // @@ -322,7 +333,7 @@ public class PacketContainerTest { assertEquals(positions, cloned); } - /*@Test + @Test public void testGetWatchableCollectionModifier() { PacketContainer entityMetadata = new PacketContainer(PacketType.Play.Server.ENTITY_METADATA); StructureModifier> watchableAccessor = @@ -330,16 +341,16 @@ public class PacketContainerTest { assertNull(watchableAccessor.read(0)); - WrappedDataWatcher watcher = new WrappedDataWatcher(); - watcher.setObject(1, 10); - watcher.setObject(8, 10); + Entity entity = new EntityLightning(null, 0, 0, 0, true); + DataWatcher watcher = entity.getDataWatcher(); - List list = watcher.getWatchableObjects(); + WrappedDataWatcher wrapper = new WrappedDataWatcher(watcher); + List list = wrapper.getWatchableObjects(); // Insert and read back watchableAccessor.write(0, list); assertEquals(list, watchableAccessor.read(0)); - }*/ + } @Test public void testGameProfiles() { @@ -350,32 +361,25 @@ public class PacketContainerTest { assertEquals(profile, spawnEntity.getGameProfiles().read(0)); } - /*@Test + @Test public void testChatComponents() { PacketContainer chatPacket = new PacketContainer(PacketType.Play.Server.CHAT); chatPacket.getChatComponents().write(0, WrappedChatComponent.fromChatMessage("You shall not " + ChatColor.ITALIC + "pass!")[0]); - assertEquals("{\"extra\":[\"You shall not \",{\"italic\":true,\"text\":\"pass!\"}],\"text\":\"\"}", + assertEquals("{\"extra\":[{\"text\":\"You shall not \"},{\"italic\":true,\"text\":\"pass!\"}],\"text\":\"\"}", chatPacket.getChatComponents().read(0).getJson()); - }*/ + } @Test public void testSerialization() { - try { - PacketContainer chat = new PacketContainer(PacketType.Play.Client.CHAT); - chat.getStrings().write(0, "Test"); + PacketContainer chat = new PacketContainer(PacketType.Play.Client.CHAT); + chat.getStrings().write(0, "Test"); - PacketContainer copy = (PacketContainer) SerializationUtils.clone(chat); + PacketContainer copy = (PacketContainer) SerializationUtils.clone(chat); - assertEquals(PacketType.Play.Client.CHAT, copy.getType()); - assertEquals("Test", copy.getStrings().read(0)); - } catch (Throwable ex) { - // This occurs intermittently on Java 6, for the time being just log it and move on - // TODO: Possibly find a solution to this - System.err.println("Failed to serialize packet:"); - ex.printStackTrace(); - } + assertEquals(PacketType.Play.Client.CHAT, copy.getType()); + assertEquals("Test", copy.getStrings().read(0)); } @Test diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/WrappedDataWatcherTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/WrappedDataWatcherTest.java new file mode 100644 index 00000000..facac713 --- /dev/null +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/WrappedDataWatcherTest.java @@ -0,0 +1,37 @@ +/** + * (c) 2016 dmulloy2 + */ +package com.comphenix.protocol.wrappers; + +import static org.junit.Assert.assertTrue; +import net.minecraft.server.v1_9_R1.DataWatcher; +import net.minecraft.server.v1_9_R1.Entity; +import net.minecraft.server.v1_9_R1.EntityLightning; + +import org.junit.BeforeClass; +import org.junit.Test; + +import com.comphenix.protocol.BukkitInitialization; + +/** + * @author dmulloy2 + */ + +public class WrappedDataWatcherTest { + + @BeforeClass + public static void prepare() { + BukkitInitialization.initializePackage(); + } + + @Test + public void test() { + Entity entity = new EntityLightning(null, 0, 0, 0, true); + DataWatcher handle = entity.getDataWatcher(); + + WrappedDataWatcher wrapper = new WrappedDataWatcher(handle); + + wrapper.setObject(0, (byte) 1); + assertTrue(wrapper.getByte(0) == 1); + } +} \ No newline at end of file diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/WrappedWatchableObjectTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/WrappedWatchableObjectTest.java index 01078a66..1c82ec64 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/WrappedWatchableObjectTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/WrappedWatchableObjectTest.java @@ -1,9 +1,5 @@ package com.comphenix.protocol.wrappers; -import static org.junit.Assert.assertEquals; - -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; import org.junit.runner.RunWith; import org.powermock.core.classloader.annotations.PowerMockIgnore; @@ -19,11 +15,11 @@ public class WrappedWatchableObjectTest { } //@Test - public void testItemStack() { + /* public void testItemStack() { final ItemStack stack = new ItemStack(Material.GOLD_AXE); final WrappedWatchableObject test = new WrappedWatchableObject(0, stack); ItemStack value = (ItemStack) test.getValue(); assertEquals(value.getType(), stack.getType()); - } + } */ }