From 2bd06922e055b3b11ec2af6797cadfd04589e809 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Sat, 1 Dec 2012 22:34:46 +0100 Subject: [PATCH] Ensure that the wrapped watchable object returns wrapped objects. In addition, made it possible to construct watchable objects directly. Also fixed a bug preventing data watchers from creating new watchable objects. --- .../protocol/wrappers/WrappedDataWatcher.java | 69 ++++-------- .../wrappers/WrappedWatchableObject.java | 102 +++++++++++++++++- 2 files changed, 120 insertions(+), 51 deletions(-) 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 5f39621e..fd2087ba 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java @@ -1,6 +1,7 @@ package com.comphenix.protocol.wrappers; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; @@ -116,8 +117,7 @@ public class WrappedDataWatcher { */ public static Integer getTypeID(Class clazz) throws FieldAccessException { initialize(); - - return typeMap.get(clazz); + return typeMap.get(WrappedWatchableObject.getUnwrappedType(clazz)); } /** @@ -145,7 +145,7 @@ public class WrappedDataWatcher { * @throws FieldAccessException Cannot read underlying field. */ public Byte getByte(int index) throws FieldAccessException { - return (Byte) getObjectRaw(index); + return (Byte) getObject(index); } /** @@ -155,7 +155,7 @@ public class WrappedDataWatcher { * @throws FieldAccessException Cannot read underlying field. */ public Short getShort(int index) throws FieldAccessException { - return (Short) getObjectRaw(index); + return (Short) getObject(index); } /** @@ -165,7 +165,7 @@ public class WrappedDataWatcher { * @throws FieldAccessException Cannot read underlying field. */ public Integer getInteger(int index) throws FieldAccessException { - return (Integer) getObjectRaw(index); + return (Integer) getObject(index); } /** @@ -175,7 +175,7 @@ public class WrappedDataWatcher { * @throws FieldAccessException Cannot read underlying field. */ public Float getFloat(int index) throws FieldAccessException { - return (Float) getObjectRaw(index); + return (Float) getObject(index); } /** @@ -185,7 +185,7 @@ public class WrappedDataWatcher { * @throws FieldAccessException Cannot read underlying field. */ public String getString(int index) throws FieldAccessException { - return (String) getObjectRaw(index); + return (String) getObject(index); } /** @@ -215,25 +215,6 @@ public class WrappedDataWatcher { * @throws FieldAccessException Cannot read underlying field. */ public Object getObject(int index) throws FieldAccessException { - Object result = getObjectRaw(index); - - // Handle the special cases too - if (result instanceof net.minecraft.server.ItemStack) { - return BukkitConverters.getItemStackConverter().getSpecific(result); - } else if (result instanceof ChunkCoordinates) { - return new WrappedChunkCoordinate((ChunkCoordinates) result); - } else { - return result; - } - } - - /** - * Retrieve a watchable object by index. - * @param index - index of the object to retrieve. - * @return The watched object. - * @throws FieldAccessException Cannot read underlying field. - */ - private Object getObjectRaw(int index) throws FieldAccessException { // The get method will take care of concurrency WatchableObject watchable = getWatchedObject(index); @@ -319,27 +300,7 @@ public class WrappedDataWatcher { * @param update - whether or not to refresh every listening clients. * @throws FieldAccessException Cannot read underlying field. */ - public void setObject(int index, Object newValue, boolean update) throws FieldAccessException { - // Convert special cases - if (newValue instanceof WrappedChunkCoordinate) - newValue = ((WrappedChunkCoordinate) newValue).getHandle(); - if (newValue instanceof ItemStack) - newValue = BukkitConverters.getItemStackConverter().getGeneric( - net.minecraft.server.ItemStack.class, (ItemStack) newValue); - - // Next, set the object - setObjectRaw(index, newValue, update); - } - - /** - * Set a watchable object by index. - * @param index - index of the object to retrieve. - * @param newValue - the new watched value. - * @param update - whether or not to refresh every listening clients. - * @return The watched object. - * @throws FieldAccessException Cannot read underlying field. - */ - private void setObjectRaw(int index, Object newValue, boolean update) throws FieldAccessException { + public void setObject(int index, Object newValue, boolean update) throws FieldAccessException { // Aquire write lock Lock writeLock = getReadWriteLock().writeLock(); writeLock.lock(); @@ -349,12 +310,22 @@ public class WrappedDataWatcher { if (watchable != null) { new WrappedWatchableObject(watchable).setValue(newValue, update); + } else { + createKeyValueMethod.invoke(handle, index, WrappedWatchableObject.getUnwrapped(newValue)); } - } finally { + + // 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(); } } - + private WatchableObject getWatchedObject(int index) throws FieldAccessException { // We use the get-method first and foremost if (getKeyValueMethod != null) { 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 d9862029..2124d2a3 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java @@ -5,6 +5,7 @@ import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.reflect.instances.DefaultInstances; +import net.minecraft.server.ChunkCoordinates; import net.minecraft.server.ItemStack; import net.minecraft.server.WatchableObject; @@ -27,7 +28,35 @@ public class WrappedWatchableObject { // 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(WatchableObject handle) { + 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) { + if (value == null) + throw new IllegalArgumentException("Value cannot be NULL."); + + // Get the correct type ID + Integer typeID = WrappedDataWatcher.getTypeID(value.getClass()); + + if (typeID != null) { + load(new WatchableObject(typeID, index, getUnwrapped(value))); + } else { + throw new IllegalArgumentException("Cannot watch the type " + value.getClass()); + } + } + + // Wrap a NMS object + private void load(WatchableObject handle) { initialize(); this.handle = handle; this.modifier = baseModifier.withTarget(handle); @@ -57,6 +86,15 @@ public class WrappedWatchableObject { * @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()); @@ -131,7 +169,7 @@ public class WrappedWatchableObject { setDirtyState(true); // Use the modifier to set the value - modifier.withType(Object.class).write(0, newValue); + modifier.withType(Object.class).write(0, getUnwrapped(newValue)); } /** @@ -140,7 +178,7 @@ public class WrappedWatchableObject { * @throws FieldAccessException Unable to use reflection. */ public Object getValue() throws FieldAccessException { - return modifier.withType(Object.class).read(0); + return getWrapped(modifier.withType(Object.class).read(0)); } /** @@ -161,6 +199,66 @@ public class WrappedWatchableObject { 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. + */ + static Object getWrapped(Object value) { + // Handle the special cases + if (value instanceof net.minecraft.server.ItemStack) { + return BukkitConverters.getItemStackConverter().getSpecific(value); + } else if (value instanceof ChunkCoordinates) { + return new WrappedChunkCoordinate((ChunkCoordinates) 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(net.minecraft.server.ChunkPosition.class)) + return ChunkPosition.class; + else if (unwrapped.equals(ChunkCoordinates.class)) + return WrappedChunkCoordinate.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 WrappedChunkCoordinate) + return ((WrappedChunkCoordinate) wrapped).getHandle(); + else if (wrapped instanceof ItemStack) + return BukkitConverters.getItemStackConverter().getGeneric( + net.minecraft.server.ItemStack.class, (org.bukkit.inventory.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 net.minecraft.server.ChunkPosition.class; + else if (wrapped.equals(WrappedChunkCoordinate.class)) + return ChunkCoordinates.class; + else + return wrapped; + } + /** * Clone the current wrapped watchable object, along with any contained objects. * @return A deep clone of the current watchable object.