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.
This commit is contained in:
Kristian S. Stangeland 2012-12-01 22:34:46 +01:00
parent f81e3262d0
commit 2bd06922e0
2 changed files with 120 additions and 51 deletions

View File

@ -1,6 +1,7 @@
package com.comphenix.protocol.wrappers; package com.comphenix.protocol.wrappers;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.ArrayList; import java.util.ArrayList;
@ -116,8 +117,7 @@ public class WrappedDataWatcher {
*/ */
public static Integer getTypeID(Class<?> clazz) throws FieldAccessException { public static Integer getTypeID(Class<?> clazz) throws FieldAccessException {
initialize(); initialize();
return typeMap.get(WrappedWatchableObject.getUnwrappedType(clazz));
return typeMap.get(clazz);
} }
/** /**
@ -145,7 +145,7 @@ public class WrappedDataWatcher {
* @throws FieldAccessException Cannot read underlying field. * @throws FieldAccessException Cannot read underlying field.
*/ */
public Byte getByte(int index) throws FieldAccessException { 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. * @throws FieldAccessException Cannot read underlying field.
*/ */
public Short getShort(int index) throws FieldAccessException { 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. * @throws FieldAccessException Cannot read underlying field.
*/ */
public Integer getInteger(int index) throws FieldAccessException { 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. * @throws FieldAccessException Cannot read underlying field.
*/ */
public Float getFloat(int index) throws FieldAccessException { 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. * @throws FieldAccessException Cannot read underlying field.
*/ */
public String getString(int index) throws FieldAccessException { 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. * @throws FieldAccessException Cannot read underlying field.
*/ */
public Object getObject(int index) throws FieldAccessException { 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 // The get method will take care of concurrency
WatchableObject watchable = getWatchedObject(index); WatchableObject watchable = getWatchedObject(index);
@ -320,26 +301,6 @@ public class WrappedDataWatcher {
* @throws FieldAccessException Cannot read underlying field. * @throws FieldAccessException Cannot read underlying field.
*/ */
public void setObject(int index, Object newValue, boolean update) throws FieldAccessException { 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 {
// Aquire write lock // Aquire write lock
Lock writeLock = getReadWriteLock().writeLock(); Lock writeLock = getReadWriteLock().writeLock();
writeLock.lock(); writeLock.lock();
@ -349,7 +310,17 @@ public class WrappedDataWatcher {
if (watchable != null) { if (watchable != null) {
new WrappedWatchableObject(watchable).setValue(newValue, update); new WrappedWatchableObject(watchable).setValue(newValue, update);
} else {
createKeyValueMethod.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 { } finally {
writeLock.unlock(); writeLock.unlock();
} }

View File

@ -5,6 +5,7 @@ import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.instances.DefaultInstances; import com.comphenix.protocol.reflect.instances.DefaultInstances;
import net.minecraft.server.ChunkCoordinates;
import net.minecraft.server.ItemStack; import net.minecraft.server.ItemStack;
import net.minecraft.server.WatchableObject; import net.minecraft.server.WatchableObject;
@ -27,7 +28,35 @@ public class WrappedWatchableObject {
// Type of the stored value // Type of the stored value
private Class<?> typeClass; private Class<?> typeClass;
/**
* Wrap a given raw Minecraft watchable object.
* @param handle - the raw watchable object to wrap.
*/
public WrappedWatchableObject(WatchableObject handle) { 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(); initialize();
this.handle = handle; this.handle = handle;
this.modifier = baseModifier.withTarget(handle); this.modifier = baseModifier.withTarget(handle);
@ -57,6 +86,15 @@ public class WrappedWatchableObject {
* @throws FieldAccessException Unable to read values. * @throws FieldAccessException Unable to read values.
*/ */
public Class<?> getType() throws FieldAccessException { 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) { if (typeClass == null) {
typeClass = WrappedDataWatcher.getTypeClass(getTypeID()); typeClass = WrappedDataWatcher.getTypeClass(getTypeID());
@ -131,7 +169,7 @@ public class WrappedWatchableObject {
setDirtyState(true); setDirtyState(true);
// Use the modifier to set the value // 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. * @throws FieldAccessException Unable to use reflection.
*/ */
public Object getValue() throws FieldAccessException { 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.<Boolean>withType(boolean.class).read(0); return modifier.<Boolean>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. * Clone the current wrapped watchable object, along with any contained objects.
* @return A deep clone of the current watchable object. * @return A deep clone of the current watchable object.