Fix an issue with cloning metadata packets
Also improved some documentation and tests
Dieser Commit ist enthalten in:
Ursprung
a39a37ec51
Commit
11ba322b5f
@ -24,6 +24,7 @@ import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.BlockPosition;
|
||||
import com.comphenix.protocol.wrappers.BukkitConverters;
|
||||
import com.comphenix.protocol.wrappers.ChunkPosition;
|
||||
import com.comphenix.protocol.wrappers.MinecraftKey;
|
||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
|
||||
import com.comphenix.protocol.wrappers.WrappedServerPing;
|
||||
import com.google.common.collect.Maps;
|
||||
@ -55,6 +56,11 @@ public class BukkitCloner implements Cloner {
|
||||
if (MinecraftReflection.isUsingNetty()) {
|
||||
addClass(4, MinecraftReflection.getServerPingClass());
|
||||
}
|
||||
|
||||
if (MinecraftReflection.dataWatcherItemExists()) {
|
||||
addClass(5, MinecraftReflection.getDataWatcherSerializerClass());
|
||||
addClass(6, MinecraftReflection.getMinecraftKeyClass());
|
||||
}
|
||||
}
|
||||
|
||||
private void addClass(int id, Class<?> clazz) {
|
||||
@ -102,6 +108,11 @@ public class BukkitCloner implements Cloner {
|
||||
case 4:
|
||||
EquivalentConverter<WrappedServerPing> serverConverter = BukkitConverters.getWrappedServerPingConverter();
|
||||
return serverConverter.getGeneric(clonableClasses.get(4), serverConverter.getSpecific(source).deepClone());
|
||||
case 5:
|
||||
return source;
|
||||
case 6:
|
||||
EquivalentConverter<MinecraftKey> keyConverter = MinecraftKey.getConverter();
|
||||
return keyConverter.getGeneric(clonableClasses.get(5), keyConverter.getSpecific(source));
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot clone objects of type " + source.getClass());
|
||||
}
|
||||
|
@ -2077,4 +2077,8 @@ public class MinecraftReflection {
|
||||
throw new RuntimeException("Cannot construct packet serializer.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Class<?> getMinecraftKeyClass() {
|
||||
return getMinecraftClass("MinecraftKey");
|
||||
}
|
||||
}
|
@ -16,7 +16,11 @@
|
||||
*/
|
||||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
|
||||
/**
|
||||
* @author dmulloy2
|
||||
@ -59,4 +63,37 @@ public class MinecraftKey {
|
||||
public String getEnumFormat() {
|
||||
return key.toUpperCase().replace(".", "_");
|
||||
}
|
||||
|
||||
private static Constructor<?> constructor = null;
|
||||
|
||||
public static EquivalentConverter<MinecraftKey> getConverter() {
|
||||
return new EquivalentConverter<MinecraftKey>() {
|
||||
@Override
|
||||
public MinecraftKey getSpecific(Object generic) {
|
||||
return MinecraftKey.fromHandle(generic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGeneric(Class<?> genericType, MinecraftKey specific) {
|
||||
if (constructor == null) {
|
||||
try {
|
||||
constructor = MinecraftReflection.getMinecraftKeyClass().getConstructor(String.class, String.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Failed to obtain MinecraftKey constructor", e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return constructor.newInstance(specific.getPrefix(), specific.getKey());
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Failed to create new MinecraftKey", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<MinecraftKey> getSpecificType() {
|
||||
return MinecraftKey.class;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -47,6 +47,7 @@ import com.google.common.base.Optional;
|
||||
|
||||
/**
|
||||
* Represents a DataWatcher in 1.9
|
||||
*
|
||||
* @author dmulloy2
|
||||
*/
|
||||
public class WrappedDataWatcher extends AbstractWrapper implements Iterable<WrappedWatchableObject> {
|
||||
@ -173,6 +174,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
|
||||
/**
|
||||
* Gets the watchable object at a given index.
|
||||
*
|
||||
* @param index Index
|
||||
* @return The watchable object, or null if none exists
|
||||
*/
|
||||
@ -187,6 +189,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
|
||||
/**
|
||||
* Whether or not this DataWatcher has an object at a given index.
|
||||
*
|
||||
* @param index Index
|
||||
* @return True if it does, false if not
|
||||
*/
|
||||
@ -196,111 +199,126 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
|
||||
// ---- Object Getters
|
||||
|
||||
/**
|
||||
* Get a watched byte.
|
||||
* @param index - index of the watched byte.
|
||||
* @return The watched byte, or NULL if this value doesn't exist.
|
||||
* @throws FieldAccessException Cannot read underlying field.
|
||||
*/
|
||||
public Byte getByte(int index) throws FieldAccessException {
|
||||
return (Byte) getObject(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a watched short.
|
||||
* @param index - index of the watched short.
|
||||
* @return The watched short, or NULL if this value doesn't exist.
|
||||
* @throws FieldAccessException Cannot read underlying field.
|
||||
*/
|
||||
public Short getShort(int index) throws FieldAccessException {
|
||||
return (Short) getObject(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a watched integer.
|
||||
* @param index - index of the watched integer.
|
||||
* @return The watched integer, or NULL if this value doesn't exist.
|
||||
* @throws FieldAccessException Cannot read underlying field.
|
||||
*/
|
||||
public Integer getInteger(int index) throws FieldAccessException {
|
||||
return (Integer) getObject(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a watched float.
|
||||
* @param index - index of the watched float.
|
||||
* @return The watched float, or NULL if this value doesn't exist.
|
||||
* @throws FieldAccessException Cannot read underlying field.
|
||||
*/
|
||||
public Float getFloat(int index) throws FieldAccessException {
|
||||
return (Float) getObject(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a watched string.
|
||||
* @param index - index of the watched string.
|
||||
* @return The watched string, or NULL if this value doesn't exist.
|
||||
* @throws FieldAccessException Cannot read underlying field.
|
||||
*/
|
||||
public String getString(int index) throws FieldAccessException {
|
||||
return (String) getObject(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a watched string.
|
||||
* @param index - index of the watched string.
|
||||
* @return The watched string, or NULL if this value doesn't exist.
|
||||
* @throws FieldAccessException Cannot read underlying field.
|
||||
*/
|
||||
public ItemStack getItemStack(int index) throws FieldAccessException {
|
||||
return (ItemStack) getObject(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a watched string.
|
||||
* @param index - index of the watched string.
|
||||
* @return The watched string, or NULL if this value doesn't exist.
|
||||
* @throws FieldAccessException Cannot read underlying field.
|
||||
*/
|
||||
public WrappedChunkCoordinate getChunkCoordinate(int index) throws FieldAccessException {
|
||||
return (WrappedChunkCoordinate) getObject(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a watchable object by index.
|
||||
* @param index - index of the object to retrieve.
|
||||
* @return The watched object.
|
||||
* @throws FieldAccessException Cannot read underlying field.
|
||||
*/
|
||||
public Object getObject(int index) throws FieldAccessException {
|
||||
return WrappedWatchableObject.getWrapped(getWatchedObject(index));
|
||||
}
|
||||
|
||||
private Object getWatchedObject(int index) {
|
||||
return getWatcherObject(new WrappedDataWatcherObject(index, null));
|
||||
/**
|
||||
* Get a watched byte.
|
||||
*
|
||||
* @param index - index of the watched byte.
|
||||
* @return The watched byte, or NULL if this value doesn't exist.
|
||||
* @throws FieldAccessException Cannot read underlying field.
|
||||
*/
|
||||
public Byte getByte(int index) throws FieldAccessException {
|
||||
return (Byte) getObject(index);
|
||||
}
|
||||
|
||||
private Object getWatcherObject(WrappedDataWatcherObject watcherObject) {
|
||||
|
||||
/**
|
||||
* Get a watched short.
|
||||
*
|
||||
* @param index - index of the watched short.
|
||||
* @return The watched short, or NULL if this value doesn't exist.
|
||||
* @throws FieldAccessException Cannot read underlying field.
|
||||
*/
|
||||
public Short getShort(int index) throws FieldAccessException {
|
||||
return (Short) getObject(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a watched integer.
|
||||
*
|
||||
* @param index - index of the watched integer.
|
||||
* @return The watched integer, or NULL if this value doesn't exist.
|
||||
* @throws FieldAccessException Cannot read underlying field.
|
||||
*/
|
||||
public Integer getInteger(int index) throws FieldAccessException {
|
||||
return (Integer) getObject(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a watched float.
|
||||
*
|
||||
* @param index - index of the watched float.
|
||||
* @return The watched float, or NULL if this value doesn't exist.
|
||||
* @throws FieldAccessException Cannot read underlying field.
|
||||
*/
|
||||
public Float getFloat(int index) throws FieldAccessException {
|
||||
return (Float) getObject(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a watched string.
|
||||
*
|
||||
* @param index - index of the watched string.
|
||||
* @return The watched string, or NULL if this value doesn't exist.
|
||||
* @throws FieldAccessException Cannot read underlying field.
|
||||
*/
|
||||
public String getString(int index) throws FieldAccessException {
|
||||
return (String) getObject(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a watched string.
|
||||
*
|
||||
* @param index - index of the watched string.
|
||||
* @return The watched string, or NULL if this value doesn't exist.
|
||||
* @throws FieldAccessException Cannot read underlying field.
|
||||
*/
|
||||
public ItemStack getItemStack(int index) throws FieldAccessException {
|
||||
return (ItemStack) getObject(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a watched string.
|
||||
*
|
||||
* @param index - index of the watched string.
|
||||
* @return The watched string, or NULL if this value doesn't exist.
|
||||
* @throws FieldAccessException Cannot read underlying field.
|
||||
*/
|
||||
public WrappedChunkCoordinate getChunkCoordinate(int index) throws FieldAccessException {
|
||||
return (WrappedChunkCoordinate) getObject(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a watchable object by index.
|
||||
*
|
||||
* @param index - index of the object to retrieve.
|
||||
* @return The watched object.
|
||||
* @throws FieldAccessException Cannot read underlying field.
|
||||
*/
|
||||
public Object getObject(int index) throws FieldAccessException {
|
||||
return getObject(new WrappedDataWatcherObject(index, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a watchable object by watcher object.
|
||||
*
|
||||
* @param object The watcher object
|
||||
* @return The watched object
|
||||
*/
|
||||
public Object getObject(WrappedDataWatcherObject object) {
|
||||
Validate.notNull(object, "Watcher object cannot be null!");
|
||||
|
||||
if (GETTER == null) {
|
||||
GETTER = Accessors.getMethodAccessor(handleType, "get", watcherObject.getHandleType());
|
||||
GETTER = Accessors.getMethodAccessor(handleType, "get", object.getHandleType());
|
||||
}
|
||||
|
||||
return GETTER.invoke(handle, watcherObject.getHandle());
|
||||
}
|
||||
Object value = GETTER.invoke(handle, object.getHandle());
|
||||
return WrappedWatchableObject.getWrapped(value);
|
||||
}
|
||||
|
||||
// ---- Object Setters
|
||||
|
||||
/**
|
||||
* Sets the DataWatcher Item at a given index to a new value.
|
||||
* @param index Index
|
||||
* @param value New value
|
||||
*/
|
||||
/**
|
||||
* Sets the DataWatcher Item at a given index to a new value.
|
||||
*
|
||||
* @param index Index
|
||||
* @param value New value
|
||||
*/
|
||||
public void setObject(int index, Object value) {
|
||||
setObject(new WrappedDataWatcherObject(index, null), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the DataWatcher Item associated with a given watcher object to a new value.
|
||||
*
|
||||
* @param object Associated watcher object
|
||||
* @param value New value
|
||||
*/
|
||||
@ -319,8 +337,6 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
SETTER = Accessors.getMethodAccessor(method);
|
||||
} else if (method.getName().equals("register")) {
|
||||
REGISTER = Accessors.getMethodAccessor(method);
|
||||
} else {
|
||||
System.out.println(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -423,6 +439,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
|
||||
/**
|
||||
* No longer supported in 1.9 due to the removal of a consistent type <-> ID map.
|
||||
*
|
||||
* @param clazz
|
||||
* @return Null
|
||||
*/
|
||||
@ -433,6 +450,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
|
||||
/**
|
||||
* No longer supported in 1.9 due to the removal of a consistent type <-> ID map.
|
||||
*
|
||||
* @param typeID
|
||||
* @return Null
|
||||
*/
|
||||
@ -473,6 +491,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
|
||||
/**
|
||||
* Represents a DataWatcherObject in 1.9.
|
||||
*
|
||||
* @author dmulloy2
|
||||
*/
|
||||
public static class WrappedDataWatcherObject extends AbstractWrapper {
|
||||
@ -484,6 +503,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
|
||||
/**
|
||||
* Creates a new watcher object from a NMS handle
|
||||
*
|
||||
* @param handle NMS handle
|
||||
*/
|
||||
public WrappedDataWatcherObject(Object handle) {
|
||||
@ -495,6 +515,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
|
||||
/**
|
||||
* Creates a new watcher object from an index and serializer
|
||||
*
|
||||
* @param index Index
|
||||
* @param serializer Serializer, see {@link Registry}
|
||||
*/
|
||||
@ -512,17 +533,23 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this watcher object's index
|
||||
* Gets this watcher object's index.
|
||||
*
|
||||
* @return The index
|
||||
*/
|
||||
public int getIndex() {
|
||||
return (int) modifier.read(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this watcher object's serializer.
|
||||
*
|
||||
* @return The serializer
|
||||
*/
|
||||
public Serializer getSerializer() {
|
||||
if (getSerializer == null) {
|
||||
getSerializer = Accessors.getMethodAccessor(FuzzyReflection.fromClass(HANDLE_TYPE, true).getMethodByParameters(
|
||||
"getSerializer", MinecraftReflection.getDataWatcherSerializerClass(), new Class[0]));
|
||||
getSerializer = Accessors.getMethodAccessor(FuzzyReflection.fromClass(HANDLE_TYPE, true)
|
||||
.getMethodByParameters("getSerializer", MinecraftReflection.getDataWatcherSerializerClass(), new Class[0]));
|
||||
}
|
||||
|
||||
Object serializer = getSerializer.invoke(handle);
|
||||
@ -534,10 +561,16 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
return new Serializer(null, serializer, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DataWatcherObject[index=" + getIndex() + ", serializer=" + getSerializer() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a DataWatcherSerializer in 1.9.
|
||||
*
|
||||
* @author dmulloy2
|
||||
*/
|
||||
public static class Serializer extends AbstractWrapper {
|
||||
@ -548,6 +581,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
|
||||
/**
|
||||
* Constructs a new Serializer
|
||||
*
|
||||
* @param type Type it serializes
|
||||
* @param handle NMS handle
|
||||
* @param optional Whether or not it's {@link Optional}
|
||||
@ -562,6 +596,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
|
||||
/**
|
||||
* Gets the type this serializer serializes.
|
||||
*
|
||||
* @return The type
|
||||
*/
|
||||
public Class<?> getType() {
|
||||
@ -569,8 +604,8 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not this serializer is optional, that is whether or not
|
||||
* the return type is wrapped in a {@link Optional}.
|
||||
* Whether or not this serializer is optional, that is whether or not the return type is wrapped in a {@link Optional}.
|
||||
*
|
||||
* @return True if it is, false if not
|
||||
*/
|
||||
public boolean isOptional() {
|
||||
@ -585,6 +620,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
|
||||
/**
|
||||
* Represents a DataWatcherRegistry containing the supported {@link Serializer}s in 1.9.
|
||||
*
|
||||
* @author dmulloy2
|
||||
*/
|
||||
public static class Registry {
|
||||
@ -603,6 +639,11 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
return REGISTRY.get(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the serializer associated with a given NMS handle.
|
||||
* @param handle The NMS handle
|
||||
* @return The serializer, or null if none exists
|
||||
*/
|
||||
public static Serializer fromHandle(Object handle) {
|
||||
initialize();
|
||||
|
||||
@ -623,7 +664,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
}
|
||||
|
||||
List<Field> candidates = FuzzyReflection.fromClass(MinecraftReflection.getMinecraftClass("DataWatcherRegistry"), true)
|
||||
.getFieldListByType(MinecraftReflection.getDataWatcherSerializerClass());
|
||||
.getFieldListByType(MinecraftReflection.getDataWatcherSerializerClass());
|
||||
for (Field candidate : candidates) {
|
||||
Type generic = candidate.getGenericType();
|
||||
if (generic instanceof ParameterizedType) {
|
||||
|
@ -223,4 +223,9 @@ public class WrappedWatchableObject extends AbstractWrapper {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DataWatcherItem[object=" + getWatcherObject() + ", value=" + getValue() + ", dirty=" + getDirtyState() + "]";
|
||||
}
|
||||
}
|
@ -36,6 +36,7 @@ import net.minecraft.server.v1_9_R1.PacketPlayOutUpdateAttributes;
|
||||
import net.minecraft.server.v1_9_R1.PacketPlayOutUpdateAttributes.AttributeSnapshot;
|
||||
|
||||
import org.apache.commons.lang.SerializationUtils;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.DyeColor;
|
||||
import org.bukkit.Material;
|
||||
@ -59,6 +60,8 @@ import com.comphenix.protocol.wrappers.BukkitConverters;
|
||||
import com.comphenix.protocol.wrappers.WrappedBlockData;
|
||||
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
|
||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry;
|
||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
|
||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
|
||||
@ -465,7 +468,7 @@ public class PacketContainerTest {
|
||||
|
||||
private static final List<PacketType> BLACKLISTED = Util.asList(
|
||||
PacketType.Play.Client.CUSTOM_PAYLOAD, PacketType.Play.Server.CUSTOM_PAYLOAD,
|
||||
PacketType.Play.Server.SET_COOLDOWN, PacketType.Play.Server.NAMED_SOUND_EFFECT
|
||||
PacketType.Play.Server.SET_COOLDOWN
|
||||
);
|
||||
|
||||
@Test
|
||||
@ -489,6 +492,15 @@ public class PacketContainerTest {
|
||||
// Initialize default values
|
||||
constructed.getModifier().writeDefaults();
|
||||
|
||||
// Make sure watchable collections can be cloned
|
||||
if (type == PacketType.Play.Server.ENTITY_METADATA) {
|
||||
constructed.getWatchableCollectionModifier().write(0, Util.asList(
|
||||
new WrappedWatchableObject(new WrappedDataWatcherObject(0, Registry.get(Byte.class)), (byte) 1),
|
||||
new WrappedWatchableObject(new WrappedDataWatcherObject(0, Registry.get(String.class)), "String"),
|
||||
new WrappedWatchableObject(new WrappedDataWatcherObject(0, Registry.get(Float.class)), 1.0F)
|
||||
));
|
||||
}
|
||||
|
||||
// Clone the packet
|
||||
PacketContainer cloned = constructed.deepClone();
|
||||
|
||||
@ -549,6 +561,10 @@ public class PacketContainerTest {
|
||||
}
|
||||
}
|
||||
|
||||
if (EqualsBuilder.reflectionEquals(a, b)) {
|
||||
return;
|
||||
}
|
||||
|
||||
assertEquals(a, b);
|
||||
}
|
||||
|
||||
|
In neuem Issue referenzieren
Einen Benutzer sperren