diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/CleanupStaticMembers.java b/ProtocolLib/src/main/java/com/comphenix/protocol/CleanupStaticMembers.java index 91e86148..1d6fa6da 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/CleanupStaticMembers.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/CleanupStaticMembers.java @@ -19,8 +19,10 @@ import com.comphenix.protocol.reflect.compiler.StructureCompiler; import com.comphenix.protocol.reflect.instances.CollectionGenerator; import com.comphenix.protocol.reflect.instances.DefaultInstances; import com.comphenix.protocol.reflect.instances.PrimitiveGenerator; +import com.comphenix.protocol.wrappers.BukkitConverters; import com.comphenix.protocol.wrappers.ChunkPosition; import com.comphenix.protocol.wrappers.WrappedDataWatcher; +import com.comphenix.protocol.wrappers.WrappedWatchableObject; /** * Used to fix ClassLoader leaks that may lead to filling up the permanent generation. @@ -48,7 +50,8 @@ class CleanupStaticMembers { PrimitiveGenerator.class, FuzzyReflection.class, MethodUtils.class, BackgroundCompiler.class, StructureCompiler.class, ObjectCloner.class, Packets.Server.class, Packets.Client.class, - ChunkPosition.class, WrappedDataWatcher.class + ChunkPosition.class, WrappedDataWatcher.class, WrappedWatchableObject.class, + BukkitConverters.class }; String[] internalClasses = { @@ -65,8 +68,7 @@ class CleanupStaticMembers { "com.comphenix.protocol.injector.ReadPacketModifier", "com.comphenix.protocol.injector.StructureCache", "com.comphenix.protocol.reflect.compiler.BoxingHelper", - "com.comphenix.protocol.reflect.compiler.MethodDescriptor", - "com.comphenix.protocol.wrappers.WrappedWatchableObject" + "com.comphenix.protocol.reflect.compiler.MethodDescriptor" }; resetClasses(publicClasses); 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 5267b4dd..b5451328 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java @@ -3,6 +3,7 @@ package com.comphenix.protocol.wrappers; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -10,6 +11,7 @@ import java.util.Set; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; +import org.bukkit.entity.Entity; import org.bukkit.inventory.ItemStack; import com.comphenix.protocol.reflect.FieldAccessException; @@ -42,6 +44,9 @@ public class WrappedDataWatcher { private static Method updateKeyValueMethod; private static Method getKeyValueMethod; + // Entity methods + private static Field entityDataField; + /** * Whether or not this class has already been initialized. */ @@ -80,6 +85,20 @@ public class WrappedDataWatcher { } } + /** + * Create a new data watcher from a list of watchable objects. + * @param watchableObjects - list of watchable objects that will be copied. + * @throws FieldAccessException Unable to read watchable objects. + */ + public WrappedDataWatcher(List watchableObjects) throws FieldAccessException { + this(); + + // Fill the underlying map + for (WrappedWatchableObject watched : watchableObjects) { + setObject(watched.getIndex(), watched.getValue()); + } + } + /** * Retrieves the underlying data watcher. * @return The underlying data watcher. @@ -223,6 +242,32 @@ public class WrappedDataWatcher { } } + /** + * Retrieve every watchable object in this watcher. + * @return Every watchable object. + * @throws FieldAccessException If reflection failed. + */ + public List getWatchableObjects() throws FieldAccessException { + try { + getReadWriteLock().readLock().lock(); + + List result = new ArrayList(); + + // Add each watchable object to the list + for (Object watchable : getWatchableObjectMap().values()) { + if (watchable != null) { + result.add(new WrappedWatchableObject((WatchableObject) watchable)); + } else { + result.add(null); + } + } + return result; + + } finally { + getReadWriteLock().readLock().unlock(); + } + } + /** * Retrieve a copy of every index associated with a watched object. * @return Every watched object index. @@ -361,6 +406,29 @@ public class WrappedDataWatcher { 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 (entityDataField == null) + entityDataField = FuzzyReflection.fromClass(Entity.class, true).getFieldByType("datawatcher", DataWatcher.class); + + try { + Object nsmWatcher = FieldUtils.readField(entityDataField, entity, true); + + if (nsmWatcher != null) + return new WrappedDataWatcher((DataWatcher) nsmWatcher); + else + return null; + + } catch (IllegalAccessException e) { + throw new FieldAccessException("Cannot access DataWatcher field.", e); + } + } + /** * Invoked when a data watcher is first used. */ 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 d9895f9a..7323f273 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java @@ -53,7 +53,7 @@ public class WrappedWatchableObject { * @return Super type. * @throws FieldAccessException Unable to read values. */ - public Class getValueType() throws FieldAccessException { + public Class getType() throws FieldAccessException { if (typeClass == null) { typeClass = WrappedDataWatcher.getTypeClass(getTypeID()); @@ -120,8 +120,8 @@ public class WrappedWatchableObject { // Verify a few quick things if (newValue == null) throw new IllegalArgumentException("Cannot watch a NULL value."); - if (!getValueType().isAssignableFrom(newValue.getClass())) - throw new IllegalArgumentException("Object " + newValue + " must be of type " + getValueType().getName()); + 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)