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.
Dieser Commit ist enthalten in:
Ursprung
f81e3262d0
Commit
2bd06922e0
@ -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);
|
||||||
|
|
||||||
@ -319,27 +300,7 @@ public class WrappedDataWatcher {
|
|||||||
* @param update - whether or not to refresh every listening clients.
|
* @param update - whether or not to refresh every listening clients.
|
||||||
* @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,12 +310,22 @@ 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));
|
||||||
}
|
}
|
||||||
} 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();
|
writeLock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private WatchableObject getWatchedObject(int index) throws FieldAccessException {
|
private WatchableObject getWatchedObject(int index) throws FieldAccessException {
|
||||||
// We use the get-method first and foremost
|
// We use the get-method first and foremost
|
||||||
if (getKeyValueMethod != null) {
|
if (getKeyValueMethod != null) {
|
||||||
|
@ -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.
|
||||||
|
In neuem Issue referenzieren
Einen Benutzer sperren