Archiviert
13
0

Fix an issue with cloning metadata packets

Also improved some documentation and tests
Dieser Commit ist enthalten in:
Dan Mulloy 2016-03-05 23:28:48 -05:00
Ursprung a39a37ec51
Commit 11ba322b5f
6 geänderte Dateien mit 215 neuen und 101 gelöschten Zeilen

Datei anzeigen

@ -24,6 +24,7 @@ import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.BlockPosition; import com.comphenix.protocol.wrappers.BlockPosition;
import com.comphenix.protocol.wrappers.BukkitConverters; import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.ChunkPosition; import com.comphenix.protocol.wrappers.ChunkPosition;
import com.comphenix.protocol.wrappers.MinecraftKey;
import com.comphenix.protocol.wrappers.WrappedDataWatcher; import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedServerPing; import com.comphenix.protocol.wrappers.WrappedServerPing;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
@ -55,6 +56,11 @@ public class BukkitCloner implements Cloner {
if (MinecraftReflection.isUsingNetty()) { if (MinecraftReflection.isUsingNetty()) {
addClass(4, MinecraftReflection.getServerPingClass()); addClass(4, MinecraftReflection.getServerPingClass());
} }
if (MinecraftReflection.dataWatcherItemExists()) {
addClass(5, MinecraftReflection.getDataWatcherSerializerClass());
addClass(6, MinecraftReflection.getMinecraftKeyClass());
}
} }
private void addClass(int id, Class<?> clazz) { private void addClass(int id, Class<?> clazz) {
@ -102,6 +108,11 @@ public class BukkitCloner implements Cloner {
case 4: case 4:
EquivalentConverter<WrappedServerPing> serverConverter = BukkitConverters.getWrappedServerPingConverter(); EquivalentConverter<WrappedServerPing> serverConverter = BukkitConverters.getWrappedServerPingConverter();
return serverConverter.getGeneric(clonableClasses.get(4), serverConverter.getSpecific(source).deepClone()); 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: default:
throw new IllegalArgumentException("Cannot clone objects of type " + source.getClass()); throw new IllegalArgumentException("Cannot clone objects of type " + source.getClass());
} }

Datei anzeigen

@ -2077,4 +2077,8 @@ public class MinecraftReflection {
throw new RuntimeException("Cannot construct packet serializer.", e); throw new RuntimeException("Cannot construct packet serializer.", e);
} }
} }
public static Class<?> getMinecraftKeyClass() {
return getMinecraftClass("MinecraftKey");
}
} }

Datei anzeigen

@ -16,7 +16,11 @@
*/ */
package com.comphenix.protocol.wrappers; 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.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
/** /**
* @author dmulloy2 * @author dmulloy2
@ -59,4 +63,37 @@ public class MinecraftKey {
public String getEnumFormat() { public String getEnumFormat() {
return key.toUpperCase().replace(".", "_"); 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;
}
};
}
} }

Datei anzeigen

@ -47,6 +47,7 @@ import com.google.common.base.Optional;
/** /**
* Represents a DataWatcher in 1.9 * Represents a DataWatcher in 1.9
*
* @author dmulloy2 * @author dmulloy2
*/ */
public class WrappedDataWatcher extends AbstractWrapper implements Iterable<WrappedWatchableObject> { 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. * Gets the watchable object at a given index.
*
* @param index Index * @param index Index
* @return The watchable object, or null if none exists * @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. * Whether or not this DataWatcher has an object at a given index.
*
* @param index Index * @param index Index
* @return True if it does, false if not * @return True if it does, false if not
*/ */
@ -198,6 +201,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
/** /**
* Get a watched byte. * Get a watched byte.
*
* @param index - index of the watched byte. * @param index - index of the watched byte.
* @return The watched byte, or NULL if this value doesn't exist. * @return The watched byte, or NULL if this value doesn't exist.
* @throws FieldAccessException Cannot read underlying field. * @throws FieldAccessException Cannot read underlying field.
@ -208,6 +212,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
/** /**
* Get a watched short. * Get a watched short.
*
* @param index - index of the watched short. * @param index - index of the watched short.
* @return The watched short, or NULL if this value doesn't exist. * @return The watched short, or NULL if this value doesn't exist.
* @throws FieldAccessException Cannot read underlying field. * @throws FieldAccessException Cannot read underlying field.
@ -218,6 +223,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
/** /**
* Get a watched integer. * Get a watched integer.
*
* @param index - index of the watched integer. * @param index - index of the watched integer.
* @return The watched integer, or NULL if this value doesn't exist. * @return The watched integer, or NULL if this value doesn't exist.
* @throws FieldAccessException Cannot read underlying field. * @throws FieldAccessException Cannot read underlying field.
@ -228,6 +234,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
/** /**
* Get a watched float. * Get a watched float.
*
* @param index - index of the watched float. * @param index - index of the watched float.
* @return The watched float, or NULL if this value doesn't exist. * @return The watched float, or NULL if this value doesn't exist.
* @throws FieldAccessException Cannot read underlying field. * @throws FieldAccessException Cannot read underlying field.
@ -238,6 +245,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
/** /**
* Get a watched string. * Get a watched string.
*
* @param index - index of the watched string. * @param index - index of the watched string.
* @return The watched string, or NULL if this value doesn't exist. * @return The watched string, or NULL if this value doesn't exist.
* @throws FieldAccessException Cannot read underlying field. * @throws FieldAccessException Cannot read underlying field.
@ -248,6 +256,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
/** /**
* Get a watched string. * Get a watched string.
*
* @param index - index of the watched string. * @param index - index of the watched string.
* @return The watched string, or NULL if this value doesn't exist. * @return The watched string, or NULL if this value doesn't exist.
* @throws FieldAccessException Cannot read underlying field. * @throws FieldAccessException Cannot read underlying field.
@ -258,6 +267,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
/** /**
* Get a watched string. * Get a watched string.
*
* @param index - index of the watched string. * @param index - index of the watched string.
* @return The watched string, or NULL if this value doesn't exist. * @return The watched string, or NULL if this value doesn't exist.
* @throws FieldAccessException Cannot read underlying field. * @throws FieldAccessException Cannot read underlying field.
@ -268,30 +278,37 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
/** /**
* Retrieve a watchable object by index. * Retrieve a watchable object by index.
*
* @param index - index of the object to retrieve. * @param index - index of the object to retrieve.
* @return The watched object. * @return The watched object.
* @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 {
return WrappedWatchableObject.getWrapped(getWatchedObject(index)); return getObject(new WrappedDataWatcherObject(index, null));
} }
private Object getWatchedObject(int index) { /**
return getWatcherObject(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!");
private Object getWatcherObject(WrappedDataWatcherObject watcherObject) {
if (GETTER == 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 // ---- Object Setters
/** /**
* Sets the DataWatcher Item at a given index to a new value. * Sets the DataWatcher Item at a given index to a new value.
*
* @param index Index * @param index Index
* @param value New value * @param value New value
*/ */
@ -301,6 +318,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
/** /**
* Sets the DataWatcher Item associated with a given watcher object to a new value. * Sets the DataWatcher Item associated with a given watcher object to a new value.
*
* @param object Associated watcher object * @param object Associated watcher object
* @param value New value * @param value New value
*/ */
@ -319,8 +337,6 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
SETTER = Accessors.getMethodAccessor(method); SETTER = Accessors.getMethodAccessor(method);
} else if (method.getName().equals("register")) { } else if (method.getName().equals("register")) {
REGISTER = Accessors.getMethodAccessor(method); 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. * No longer supported in 1.9 due to the removal of a consistent type <-> ID map.
*
* @param clazz * @param clazz
* @return Null * @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. * No longer supported in 1.9 due to the removal of a consistent type <-> ID map.
*
* @param typeID * @param typeID
* @return Null * @return Null
*/ */
@ -473,6 +491,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
/** /**
* Represents a DataWatcherObject in 1.9. * Represents a DataWatcherObject in 1.9.
*
* @author dmulloy2 * @author dmulloy2
*/ */
public static class WrappedDataWatcherObject extends AbstractWrapper { 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 * Creates a new watcher object from a NMS handle
*
* @param handle NMS handle * @param handle NMS handle
*/ */
public WrappedDataWatcherObject(Object 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 * Creates a new watcher object from an index and serializer
*
* @param index Index * @param index Index
* @param serializer Serializer, see {@link Registry} * @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 * @return The index
*/ */
public int getIndex() { public int getIndex() {
return (int) modifier.read(0); return (int) modifier.read(0);
} }
/**
* Gets this watcher object's serializer.
*
* @return The serializer
*/
public Serializer getSerializer() { public Serializer getSerializer() {
if (getSerializer == null) { if (getSerializer == null) {
getSerializer = Accessors.getMethodAccessor(FuzzyReflection.fromClass(HANDLE_TYPE, true).getMethodByParameters( getSerializer = Accessors.getMethodAccessor(FuzzyReflection.fromClass(HANDLE_TYPE, true)
"getSerializer", MinecraftReflection.getDataWatcherSerializerClass(), new Class[0])); .getMethodByParameters("getSerializer", MinecraftReflection.getDataWatcherSerializerClass(), new Class[0]));
} }
Object serializer = getSerializer.invoke(handle); Object serializer = getSerializer.invoke(handle);
@ -534,10 +561,16 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
return new Serializer(null, serializer, false); return new Serializer(null, serializer, false);
} }
} }
@Override
public String toString() {
return "DataWatcherObject[index=" + getIndex() + ", serializer=" + getSerializer() + "]";
}
} }
/** /**
* Represents a DataWatcherSerializer in 1.9. * Represents a DataWatcherSerializer in 1.9.
*
* @author dmulloy2 * @author dmulloy2
*/ */
public static class Serializer extends AbstractWrapper { public static class Serializer extends AbstractWrapper {
@ -548,6 +581,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
/** /**
* Constructs a new Serializer * Constructs a new Serializer
*
* @param type Type it serializes * @param type Type it serializes
* @param handle NMS handle * @param handle NMS handle
* @param optional Whether or not it's {@link Optional} * @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. * Gets the type this serializer serializes.
*
* @return The type * @return The type
*/ */
public Class<?> getType() { 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 * Whether or not this serializer is optional, that is whether or not the return type is wrapped in a {@link Optional}.
* the return type is wrapped in a {@link Optional}. *
* @return True if it is, false if not * @return True if it is, false if not
*/ */
public boolean isOptional() { 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. * Represents a DataWatcherRegistry containing the supported {@link Serializer}s in 1.9.
*
* @author dmulloy2 * @author dmulloy2
*/ */
public static class Registry { public static class Registry {
@ -603,6 +639,11 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
return REGISTRY.get(clazz); 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) { public static Serializer fromHandle(Object handle) {
initialize(); initialize();

Datei anzeigen

@ -223,4 +223,9 @@ public class WrappedWatchableObject extends AbstractWrapper {
return false; return false;
} }
@Override
public String toString() {
return "DataWatcherItem[object=" + getWatcherObject() + ", value=" + getValue() + ", dirty=" + getDirtyState() + "]";
}
} }

Datei anzeigen

@ -36,6 +36,7 @@ import net.minecraft.server.v1_9_R1.PacketPlayOutUpdateAttributes;
import net.minecraft.server.v1_9_R1.PacketPlayOutUpdateAttributes.AttributeSnapshot; import net.minecraft.server.v1_9_R1.PacketPlayOutUpdateAttributes.AttributeSnapshot;
import org.apache.commons.lang.SerializationUtils; import org.apache.commons.lang.SerializationUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.DyeColor; import org.bukkit.DyeColor;
import org.bukkit.Material; 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.WrappedBlockData;
import com.comphenix.protocol.wrappers.WrappedChatComponent; import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedDataWatcher; 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.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedWatchableObject; import com.comphenix.protocol.wrappers.WrappedWatchableObject;
import com.comphenix.protocol.wrappers.nbt.NbtCompound; import com.comphenix.protocol.wrappers.nbt.NbtCompound;
@ -465,7 +468,7 @@ public class PacketContainerTest {
private static final List<PacketType> BLACKLISTED = Util.asList( private static final List<PacketType> BLACKLISTED = Util.asList(
PacketType.Play.Client.CUSTOM_PAYLOAD, PacketType.Play.Server.CUSTOM_PAYLOAD, 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 @Test
@ -489,6 +492,15 @@ public class PacketContainerTest {
// Initialize default values // Initialize default values
constructed.getModifier().writeDefaults(); 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 // Clone the packet
PacketContainer cloned = constructed.deepClone(); PacketContainer cloned = constructed.deepClone();
@ -549,6 +561,10 @@ public class PacketContainerTest {
} }
} }
if (EqualsBuilder.reflectionEquals(a, b)) {
return;
}
assertEquals(a, b); assertEquals(a, b);
} }