diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/cloning/BukkitCloner.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/cloning/BukkitCloner.java index 4a7372e7..c6164f61 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/cloning/BukkitCloner.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/cloning/BukkitCloner.java @@ -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 serverConverter = BukkitConverters.getWrappedServerPingConverter(); return serverConverter.getGeneric(clonableClasses.get(4), serverConverter.getSpecific(source).deepClone()); + case 5: + return source; + case 6: + EquivalentConverter keyConverter = MinecraftKey.getConverter(); + return keyConverter.getGeneric(clonableClasses.get(5), keyConverter.getSpecific(source)); default: throw new IllegalArgumentException("Cannot clone objects of type " + source.getClass()); } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java index b76b310d..74ef8809 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java @@ -2077,4 +2077,8 @@ public class MinecraftReflection { throw new RuntimeException("Cannot construct packet serializer.", e); } } + + public static Class getMinecraftKeyClass() { + return getMinecraftClass("MinecraftKey"); + } } \ No newline at end of file diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/MinecraftKey.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/MinecraftKey.java index 79823572..e8679743 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/MinecraftKey.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/MinecraftKey.java @@ -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 getConverter() { + return new EquivalentConverter() { + @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 getSpecificType() { + return MinecraftKey.class; + } + }; + } } \ No newline at end of file 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 398730b0..f3c08ed7 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java @@ -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 { @@ -173,6 +174,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable ID map. + * * @param clazz * @return Null */ @@ -433,6 +450,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable ID map. + * * @param typeID * @return Null */ @@ -473,6 +491,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable getType() { @@ -569,8 +604,8 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable 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) { 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 570bbae4..a5380737 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java @@ -223,4 +223,9 @@ public class WrappedWatchableObject extends AbstractWrapper { return false; } + + @Override + public String toString() { + return "DataWatcherItem[object=" + getWatcherObject() + ", value=" + getValue() + ", dirty=" + getDirtyState() + "]"; + } } \ No newline at end of file diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java index c4b62a5d..d91eef49 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java @@ -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 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); }