From 733ecdd59bd6fc77ff8dbcc059379fb1135fb37a Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Tue, 4 Dec 2012 14:49:59 +0100 Subject: [PATCH 01/17] Increment to 1.7.2-SNAPSHOT for development towards the next version. --- ProtocolLib/pom.xml | 2 +- ProtocolLib/src/main/resources/plugin.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ProtocolLib/pom.xml b/ProtocolLib/pom.xml index 1b769aac..00aca124 100644 --- a/ProtocolLib/pom.xml +++ b/ProtocolLib/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.comphenix.protocol ProtocolLib - 1.7.1 + 1.7.2-SNAPSHOT jar Provides read/write access to the Minecraft protocol. diff --git a/ProtocolLib/src/main/resources/plugin.yml b/ProtocolLib/src/main/resources/plugin.yml index 69f13146..7bac3e23 100644 --- a/ProtocolLib/src/main/resources/plugin.yml +++ b/ProtocolLib/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: ProtocolLib -version: 1.7.1 +version: 1.7.2-SNAPSHOT description: Provides read/write access to the Minecraft protocol. author: Comphenix website: http://www.comphenix.net/ProtocolLib From 86d346170069f200ca77d71371fd8b0f6102ebdc Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Tue, 4 Dec 2012 14:59:08 +0100 Subject: [PATCH 02/17] Ensure that we don't return enhancer classes as vanilla classes. --- .../protocol/injector/MinecraftRegistry.java | 22 +++++++++++++++++-- .../injector/player/InjectedArrayList.java | 3 ++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/MinecraftRegistry.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/MinecraftRegistry.java index 34f7a341..e433604e 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/MinecraftRegistry.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/MinecraftRegistry.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.Set; import net.minecraft.server.Packet; +import net.sf.cglib.proxy.Factory; import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FieldUtils; @@ -166,7 +167,7 @@ class MinecraftRegistry { // Optimized lookup if (lookup.containsKey(packetID)) { - return lookup.get(packetID); + return removeEnhancer(lookup.get(packetID), forceVanilla); } // Will most likely not be used @@ -174,10 +175,27 @@ class MinecraftRegistry { if (Objects.equal(entry.getValue(), packetID)) { // Attempt to get the vanilla class here too if (!forceVanilla || entry.getKey().getName().startsWith("net.minecraft.server")) - return entry.getKey(); + return removeEnhancer(entry.getKey(), forceVanilla); } } throw new IllegalArgumentException("The packet ID " + packetID + " is not registered."); } + + /** + * Find the first superclass that is not a CBLib proxy object. + * @param clazz - the class whose hierachy we're going to search through. + * @param remove - whether or not to skip enhanced (proxy) classes. + * @return If remove is TRUE, the first superclass that is not a proxy. + */ + private static Class removeEnhancer(Class clazz, boolean remove) { + if (remove) { + // Get the underlying vanilla class + while (Factory.class.isAssignableFrom(clazz) && !clazz.equals(Object.class)) { + clazz = clazz.getSuperclass(); + } + } + + return clazz; + } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedArrayList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedArrayList.java index fd5a0b80..d92d4803 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedArrayList.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedArrayList.java @@ -27,6 +27,7 @@ import com.comphenix.protocol.injector.player.NetworkFieldInjector.FakePacket; import net.minecraft.server.Packet; import net.sf.cglib.proxy.Enhancer; +import net.sf.cglib.proxy.Factory; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; @@ -95,7 +96,7 @@ class InjectedArrayList extends ArrayList { int packetID = invoker.getPacketID(source); Class type = invoker.getPacketClassFromID(packetID, true); - + // We want to subtract the byte amount that were added to the running // total of outstanding packets. Otherwise, cancelling too many packets // might cause a "disconnect.overflow" error. From ede60970b933ff6e5c2070abceae9b1df3bb42e6 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Tue, 4 Dec 2012 16:21:37 +0100 Subject: [PATCH 03/17] Ensure that we don't cache wrong methods in the serialization. --- ProtocolLib/dependency-reduced-pom.xml | 2 +- .../protocol/events/PacketContainer.java | 60 ++++++++++++++----- .../injector/player/InjectedArrayList.java | 1 - 3 files changed, 46 insertions(+), 17 deletions(-) diff --git a/ProtocolLib/dependency-reduced-pom.xml b/ProtocolLib/dependency-reduced-pom.xml index 8bf4ad99..7f3c8e0c 100644 --- a/ProtocolLib/dependency-reduced-pom.xml +++ b/ProtocolLib/dependency-reduced-pom.xml @@ -4,7 +4,7 @@ com.comphenix.protocol ProtocolLib ProtocolLib - 1.7.1 + 1.7.2-SNAPSHOT Provides read/write access to the Minecraft protocol. http://dev.bukkit.org/server-mods/protocollib/ diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java b/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java index 2ff67143..6f424874 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java @@ -29,6 +29,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collection; import java.util.List; +import java.util.concurrent.ConcurrentMap; import org.bukkit.World; import org.bukkit.WorldType; @@ -43,6 +44,7 @@ import com.comphenix.protocol.wrappers.BukkitConverters; import com.comphenix.protocol.wrappers.ChunkPosition; import com.comphenix.protocol.wrappers.WrappedDataWatcher; import com.comphenix.protocol.wrappers.WrappedWatchableObject; +import com.google.common.collect.Maps; import net.minecraft.server.Packet; @@ -63,11 +65,11 @@ public class PacketContainer implements Serializable { // Current structure modifier protected transient StructureModifier structureModifier; - + // Support for serialization - private static Method writeMethod; - private static Method readMethod; - + private static ConcurrentMap, Method> writeMethods = Maps.newConcurrentMap(); + private static ConcurrentMap, Method> readMethods = Maps.newConcurrentMap(); + /** * Creates a packet container for a new packet. * @param id - ID of the packet to create. @@ -100,6 +102,12 @@ public class PacketContainer implements Serializable { this.structureModifier = structure; } + /** + * For serialization. + */ + protected PacketContainer() { + } + /** * Retrieves the underlying Minecraft packet. * @return Underlying Minecraft packet. @@ -399,14 +407,12 @@ public class PacketContainer implements Serializable { // We'll take care of NULL packets as well output.writeBoolean(handle != null); - - // Retrieve the write method by reflection - if (writeMethod == null) - writeMethod = FuzzyReflection.fromObject(handle).getMethodByParameters("write", DataOutputStream.class); - + try { // Call the write-method - writeMethod.invoke(handle, new DataOutputStream(output)); + getMethodLazily(writeMethods, handle.getClass(), "write", DataOutputStream.class). + invoke(handle, new DataOutputStream(output)); + } catch (IllegalArgumentException e) { throw new IOException("Minecraft packet doesn't support DataOutputStream", e); } catch (IllegalAccessException e) { @@ -429,13 +435,11 @@ public class PacketContainer implements Serializable { // Create a default instance of the packet handle = StructureCache.newPacket(id); - // Retrieve the read method by reflection - if (readMethod == null) - readMethod = FuzzyReflection.fromObject(handle).getMethodByParameters("read", DataInputStream.class); - // Call the read method try { - readMethod.invoke(handle, new DataInputStream(input)); + getMethodLazily(readMethods, handle.getClass(), "read", DataInputStream.class). + invoke(handle, new DataInputStream(input)); + } catch (IllegalArgumentException e) { throw new IOException("Minecraft packet doesn't support DataInputStream", e); } catch (IllegalAccessException e) { @@ -448,4 +452,30 @@ public class PacketContainer implements Serializable { structureModifier = structureModifier.withTarget(handle); } } + + /** + * Retrieve the cached method concurrently. + * @param lookup - a lazy lookup cache. + * @param handleClass - class type of the current packet. + * @param methodName - name of method to retrieve. + * @param parameterClass - the one parameter type in the method. + * @return Reflected method. + */ + private Method getMethodLazily(ConcurrentMap, Method> lookup, + Class handleClass, String methodName, Class parameterClass) { + Method method = lookup.get(handleClass); + + // Atomic operation + if (method == null) { + Method initialized = FuzzyReflection.fromClass(handleClass).getMethodByParameters(methodName, parameterClass); + method = lookup.putIfAbsent(handleClass, initialized); + + // Use our version if we succeeded + if (method == null) { + method = initialized; + } + } + + return method; + } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedArrayList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedArrayList.java index d92d4803..2f8ee829 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedArrayList.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedArrayList.java @@ -27,7 +27,6 @@ import com.comphenix.protocol.injector.player.NetworkFieldInjector.FakePacket; import net.minecraft.server.Packet; import net.sf.cglib.proxy.Enhancer; -import net.sf.cglib.proxy.Factory; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; From 25d41ed51c2f0583a7e9ca0a43ae7a67d8feb2a7 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Wed, 5 Dec 2012 03:32:13 +0100 Subject: [PATCH 04/17] Ensure that fake cancellation packets can be created. --- .../injector/player/InjectedArrayList.java | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedArrayList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedArrayList.java index 2f8ee829..b2fc3855 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedArrayList.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedArrayList.java @@ -26,6 +26,7 @@ import com.comphenix.protocol.injector.ListenerInvoker; import com.comphenix.protocol.injector.player.NetworkFieldInjector.FakePacket; import net.minecraft.server.Packet; +import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; @@ -46,10 +47,13 @@ class InjectedArrayList extends ArrayList { private transient Set ignoredPackets; private transient ClassLoader classLoader; + private transient InvertedIntegerCallback callback; + public InjectedArrayList(ClassLoader classLoader, PlayerInjector injector, Set ignoredPackets) { this.classLoader = classLoader; this.injector = injector; this.ignoredPackets = ignoredPackets; + this.callback = new InvertedIntegerCallback(); } @Override @@ -96,6 +100,8 @@ class InjectedArrayList extends ArrayList { int packetID = invoker.getPacketID(source); Class type = invoker.getPacketClassFromID(packetID, true); + System.out.println(type.getName()); + // We want to subtract the byte amount that were added to the running // total of outstanding packets. Otherwise, cancelling too many packets // might cause a "disconnect.overflow" error. @@ -122,15 +128,20 @@ class InjectedArrayList extends ArrayList { ex.setCallbackType(InvertedIntegerCallback.class); Class proxyClass = ex.createClass(); - - // Temporarily associate the fake packet class - invoker.registerPacketClass(proxyClass, packetID); - - Packet fake = (Packet) Enhancer.create(proxyClass, new InvertedIntegerCallback()); + Enhancer.registerCallbacks(proxyClass, new Callback[] { callback }); - // Remove this association - invoker.unregisterPacketClass(proxyClass); - return fake; + try { + // Temporarily associate the fake packet class + invoker.registerPacketClass(proxyClass, packetID); + return (Packet) proxyClass.newInstance(); + + } catch (Exception e) { + // Don't pollute the throws tree + throw new RuntimeException("Cannot create fake class.", e); + } finally { + // Remove this association + invoker.unregisterPacketClass(proxyClass); + } } /** From 88009292bb488d5be333113d43ff482836ac4e90 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Wed, 5 Dec 2012 23:47:36 +0100 Subject: [PATCH 05/17] Update to the new CraftBukkit snapshot. --- ProtocolLib/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProtocolLib/pom.xml b/ProtocolLib/pom.xml index 00aca124..2f5c28a1 100644 --- a/ProtocolLib/pom.xml +++ b/ProtocolLib/pom.xml @@ -202,7 +202,7 @@ org.bukkit craftbukkit - 1.3.2-R1.0 + 1.4.5-R0.3-SNAPSHOT provided From 45c28d47c89fbdf9f0be73b3185498d74ac90e7d Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Wed, 5 Dec 2012 23:47:55 +0100 Subject: [PATCH 06/17] Create a new dedicated Minecraft reflection class. --- .../protocol/injector/EntityUtilities.java | 3 ++- .../comphenix/protocol/injector/PacketInjector.java | 4 +++- .../protocol/injector/player/PlayerInjector.java | 8 +++----- .../comphenix/protocol/reflect/FuzzyReflection.java | 5 ----- .../protocol/utility/MinecraftReflection.java | 13 +++++++++++++ 5 files changed, 21 insertions(+), 12 deletions(-) create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java index ef9c9e8c..806c03e1 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java @@ -39,6 +39,7 @@ import org.bukkit.entity.Player; import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FieldUtils; import com.comphenix.protocol.reflect.FuzzyReflection; +import com.comphenix.protocol.utility.MinecraftReflection; import com.google.common.collect.Lists; /** @@ -193,7 +194,7 @@ class EntityUtilities { // The Minecraft field that's NOT filled in by the constructor trackedEntitiesField = FuzzyReflection.fromObject(tracker, true). - getFieldByType(FuzzyReflection.MINECRAFT_OBJECT, ignoredTypes); + getFieldByType(MinecraftReflection.MINECRAFT_OBJECT, ignoredTypes); } // Read the entity hashmap diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketInjector.java index 8d3374dc..6f6fb19a 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketInjector.java @@ -37,6 +37,7 @@ import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.injector.player.PlayerInjectionHandler; import com.comphenix.protocol.reflect.FieldUtils; import com.comphenix.protocol.reflect.FuzzyReflection; +import com.comphenix.protocol.utility.MinecraftReflection; /** * This class is responsible for adding or removing proxy objects that intercepts recieved packets. @@ -92,7 +93,8 @@ class PacketInjector { private void initialize() throws IllegalAccessException { if (intHashMap == null) { // We're looking for the first static field with a Minecraft-object. This should be a IntHashMap. - Field intHashMapField = FuzzyReflection.fromClass(Packet.class, true).getFieldByType(FuzzyReflection.MINECRAFT_OBJECT); + Field intHashMapField = FuzzyReflection.fromClass(Packet.class, true). + getFieldByType(MinecraftReflection.MINECRAFT_OBJECT); try { intHashMap = FieldUtils.readField(intHashMapField, (Object) null, true); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java index 6eda9848..7739b68f 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java @@ -25,9 +25,6 @@ import java.lang.reflect.Method; import java.net.Socket; import java.net.SocketAddress; -import net.minecraft.server.EntityPlayer; -import net.minecraft.server.NetLoginHandler; -import net.minecraft.server.Packet; import net.sf.cglib.proxy.Factory; import org.bukkit.craftbukkit.entity.CraftPlayer; @@ -45,6 +42,7 @@ import com.comphenix.protocol.reflect.FieldUtils; import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.reflect.VolatileField; +import com.comphenix.protocol.utility.MinecraftReflection; abstract class PlayerInjector { @@ -122,7 +120,7 @@ abstract class PlayerInjector { * @param player - the player to retrieve. * @return Notch player object. */ - protected EntityPlayer getEntityPlayer(Player player) { + protected Object getEntityPlayer(Player player) { CraftPlayer craft = (CraftPlayer) player; return craft.getHandle(); } @@ -379,7 +377,7 @@ abstract class PlayerInjector { try { // Well, that sucks. Try just Minecraft objects then. netHandlerField = FuzzyReflection.fromClass(networkManager.getClass(), true). - getFieldByType(FuzzyReflection.MINECRAFT_OBJECT); + getFieldByType(MinecraftReflection.MINECRAFT_OBJECT); } catch (RuntimeException e2) { throw new IllegalAccessException("Cannot locate net handler. " + e2.getMessage()); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java index ae986fdb..4c8cd1bf 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java @@ -32,11 +32,6 @@ import java.util.regex.Pattern; * @author Kristian */ public class FuzzyReflection { - - /** - * Matches a Minecraft object. - */ - public static final String MINECRAFT_OBJECT = "net\\.minecraft(\\.\\w+)+"; // The class we're actually representing private Class source; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java new file mode 100644 index 00000000..80f73190 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java @@ -0,0 +1,13 @@ +package com.comphenix.protocol.utility; + +/** + * Methods and constants specifically used in conjuction with reflecting Minecraft object. + * @author Kristian + * + */ +public class MinecraftReflection { + /** + * Matches a Minecraft object. + */ + public static final String MINECRAFT_OBJECT = "net\\.minecraft(\\.\\w+)+"; +} From 0745fba8815efae4ca87b48e439dc2e2ca6a2705 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Thu, 6 Dec 2012 03:37:20 +0100 Subject: [PATCH 07/17] Massive update that ensures ProtocolLib is compatible with 1.4.5-R3.0 Due to 8f12382e8efc8c39a919af9180dd884caf3720ff, CraftBukkit has now moved the net.minecraft.server package and org.bukkit.craftbukkit to versioned package names, breaking all plugins that rely on these directly. ProtocolLib was never intended to be updated specifcally for new versions of Minecraft (hence the index-based system of accessing packets instead of making wrapper classes), so we will use reflection to "ignore" this hack entirely. Luckily, ProtocolLib was initially designed for this in mind, so we don't have to refactor it entirely. --- ProtocolLib/dependency-reduced-pom.xml | 2 +- .../com/comphenix/protocol/CommandPacket.java | 6 +- .../comphenix/protocol/async/AsyncMarker.java | 7 +- .../protocol/events/PacketContainer.java | 33 +- .../protocol/injector/EntityUtilities.java | 23 +- .../protocol/injector/ListenerInvoker.java | 4 +- .../protocol/injector/MinecraftRegistry.java | 4 +- .../protocol/injector/PacketConstructor.java | 4 +- .../injector/PacketFilterManager.java | 8 +- .../protocol/injector/PacketInjector.java | 5 +- .../protocol/injector/ReadPacketModifier.java | 11 +- .../protocol/injector/StructureCache.java | 9 +- .../injector/player/InjectedArrayList.java | 15 +- .../player/InjectedServerConnection.java | 6 +- .../injector/player/NetworkFieldInjector.java | 18 +- .../player/NetworkObjectInjector.java | 5 +- .../player/NetworkServerInjector.java | 9 +- .../player/PlayerInjectionHandler.java | 4 +- .../injector/player/PlayerInjector.java | 31 +- .../protocol/utility/CachedPackage.java | 46 ++ .../protocol/utility/MinecraftReflection.java | 444 +++++++++++++++++- .../protocol/wrappers/BukkitConverters.java | 64 +-- .../protocol/wrappers/ChunkPosition.java | 41 +- .../wrappers/WrappedChunkCoordinate.java | 58 ++- .../protocol/wrappers/WrappedDataWatcher.java | 50 +- .../wrappers/WrappedWatchableObject.java | 67 ++- .../reflect/StructureModifierTest.java | 26 - 27 files changed, 757 insertions(+), 243 deletions(-) create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/utility/CachedPackage.java delete mode 100644 ProtocolLib/src/test/java/com/comphenix/protocol/reflect/StructureModifierTest.java diff --git a/ProtocolLib/dependency-reduced-pom.xml b/ProtocolLib/dependency-reduced-pom.xml index 7f3c8e0c..f877ad9e 100644 --- a/ProtocolLib/dependency-reduced-pom.xml +++ b/ProtocolLib/dependency-reduced-pom.xml @@ -139,7 +139,7 @@ org.bukkit craftbukkit - 1.3.2-R1.0 + 1.4.5-R0.3-SNAPSHOT provided diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/CommandPacket.java b/ProtocolLib/src/main/java/com/comphenix/protocol/CommandPacket.java index 3b8245c9..5719a097 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/CommandPacket.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/CommandPacket.java @@ -10,7 +10,6 @@ import java.util.WeakHashMap; import java.util.logging.Level; import java.util.logging.Logger; -import net.minecraft.server.Packet; import net.sf.cglib.proxy.Factory; import org.bukkit.ChatColor; @@ -29,6 +28,7 @@ import com.comphenix.protocol.injector.GamePhase; import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.PrettyPrinter; import com.comphenix.protocol.utility.ChatExtensions; +import com.comphenix.protocol.utility.MinecraftReflection; import com.google.common.collect.DiscreteDomains; import com.google.common.collect.Range; import com.google.common.collect.Ranges; @@ -394,7 +394,7 @@ class CommandPacket extends CommandBase { // Detailed will print the packet's content too if (detailed) { try { - Packet packet = event.getPacket().getHandle(); + Object packet = event.getPacket().getHandle(); Class clazz = packet.getClass(); // Get the first Minecraft super class @@ -404,7 +404,7 @@ class CommandPacket extends CommandBase { } logger.info(shortDescription + ":\n" + - PrettyPrinter.printObject(packet, clazz, Packet.class) + PrettyPrinter.printObject(packet, clazz, MinecraftReflection.getPacketClass()) ); } catch (IllegalAccessException e) { diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/async/AsyncMarker.java b/ProtocolLib/src/main/java/com/comphenix/protocol/async/AsyncMarker.java index 7b6b038b..5b0e6a1c 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/async/AsyncMarker.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/async/AsyncMarker.java @@ -25,13 +25,12 @@ import java.util.Iterator; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; -import net.minecraft.server.Packet; - import com.comphenix.protocol.PacketStream; import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.injector.PrioritizedListener; import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FuzzyReflection; +import com.comphenix.protocol.utility.MinecraftReflection; import com.google.common.primitives.Longs; /** @@ -403,10 +402,10 @@ public class AsyncMarker implements Serializable, Comparable { if (isMinecraftAsync == null && !alwaysSync) { try { - isMinecraftAsync = FuzzyReflection.fromClass(Packet.class).getMethodByName("a_.*"); + isMinecraftAsync = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethodByName("a_.*"); } catch (RuntimeException e) { // This will occur in 1.2.5 (or possibly in later versions) - List methods = FuzzyReflection.fromClass(Packet.class). + List methods = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()). getMethodListByParameters(boolean.class, new Class[] {}); // Try to look for boolean methods diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java b/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java index 6f424874..a7b4a265 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java @@ -40,14 +40,13 @@ import com.comphenix.protocol.injector.StructureCache; import com.comphenix.protocol.reflect.EquivalentConverter; import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.StructureModifier; +import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.wrappers.BukkitConverters; import com.comphenix.protocol.wrappers.ChunkPosition; import com.comphenix.protocol.wrappers.WrappedDataWatcher; import com.comphenix.protocol.wrappers.WrappedWatchableObject; import com.google.common.collect.Maps; -import net.minecraft.server.Packet; - /** * Represents a Minecraft packet indirectly. * @@ -61,7 +60,7 @@ public class PacketContainer implements Serializable { private static final long serialVersionUID = 2074805748222377230L; protected int id; - protected transient Packet handle; + protected transient Object handle; // Current structure modifier protected transient StructureModifier structureModifier; @@ -83,7 +82,7 @@ public class PacketContainer implements Serializable { * @param id - ID of the given packet. * @param handle - contained packet. */ - public PacketContainer(int id, Packet handle) { + public PacketContainer(int id, Object handle) { this(id, handle, StructureCache.getStructure(id).withTarget(handle)); } @@ -93,7 +92,7 @@ public class PacketContainer implements Serializable { * @param handle - contained packet. * @param structure - structure modifier. */ - public PacketContainer(int id, Packet handle, StructureModifier structure) { + public PacketContainer(int id, Object handle, StructureModifier structure) { if (handle == null) throw new IllegalArgumentException("handle cannot be null."); @@ -112,7 +111,7 @@ public class PacketContainer implements Serializable { * Retrieves the underlying Minecraft packet. * @return Underlying Minecraft packet. */ - public Packet getHandle() { + public Object getHandle() { return handle; } @@ -222,7 +221,7 @@ public class PacketContainer implements Serializable { public StructureModifier getItemModifier() { // Convert to and from the Bukkit wrapper return structureModifier.withType( - net.minecraft.server.ItemStack.class, BukkitConverters.getItemStackConverter()); + MinecraftReflection.getItemStackClass(), BukkitConverters.getItemStackConverter()); } /** @@ -238,23 +237,23 @@ public class PacketContainer implements Serializable { // Convert to and from the Bukkit wrapper return structureModifier.withType( - net.minecraft.server.ItemStack[].class, + MinecraftReflection.getItemStackArrayClass(), BukkitConverters.getIgnoreNull(new EquivalentConverter() { public Object getGeneric(ClassgenericType, ItemStack[] specific) { - net.minecraft.server.ItemStack[] result = new net.minecraft.server.ItemStack[specific.length]; + Object[] result = new Object[specific.length]; // Unwrap every item for (int i = 0; i < result.length; i++) { - result[i] = (net.minecraft.server.ItemStack) stackConverter.getGeneric( - net.minecraft.server.ItemStack.class, specific[i]); + result[i] = stackConverter.getGeneric( + MinecraftReflection.getItemStackClass(), specific[i]); } return result; } @Override public ItemStack[] getSpecific(Object generic) { - net.minecraft.server.ItemStack[] input = (net.minecraft.server.ItemStack[]) generic; + Object[] input = (Object[]) generic; ItemStack[] result = new ItemStack[input.length]; // Add the wrapper @@ -281,7 +280,7 @@ public class PacketContainer implements Serializable { public StructureModifier getWorldTypeModifier() { // Convert to and from the Bukkit wrapper return structureModifier.withType( - net.minecraft.server.WorldType.class, + MinecraftReflection.getWorldTypeClass(), BukkitConverters.getWorldTypeConverter()); } @@ -292,7 +291,7 @@ public class PacketContainer implements Serializable { public StructureModifier getDataWatcherModifier() { // Convert to and from the Bukkit wrapper return structureModifier.withType( - net.minecraft.server.DataWatcher.class, + MinecraftReflection.getDataWatcherClass(), BukkitConverters.getDataWatcherConverter()); } @@ -319,7 +318,7 @@ public class PacketContainer implements Serializable { public StructureModifier getPositionModifier() { // Convert to and from the Bukkit wrapper return structureModifier.withType( - net.minecraft.server.ChunkPosition.class, + MinecraftReflection.getChunkPositionClass(), ChunkPosition.getConverter()); } @@ -335,7 +334,7 @@ public class PacketContainer implements Serializable { return structureModifier.withType( Collection.class, BukkitConverters.getListConverter( - net.minecraft.server.ChunkPosition.class, + MinecraftReflection.getChunkPositionClass(), ChunkPosition.getConverter()) ); } @@ -352,7 +351,7 @@ public class PacketContainer implements Serializable { return structureModifier.withType( Collection.class, BukkitConverters.getListConverter( - net.minecraft.server.WatchableObject.class, + MinecraftReflection.getWatchableObjectClass(), BukkitConverters.getWatchableObjectConverter()) ); } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java index 806c03e1..9927230a 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java @@ -28,11 +28,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import net.minecraft.server.EntityPlayer; -import net.minecraft.server.EntityTrackerEntry; - import org.bukkit.World; -import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; @@ -52,6 +48,7 @@ class EntityUtilities { private static Field entityTrackerField; private static Field trackedEntitiesField; private static Field trackedPlayersField; + private static Field trackerField; private static Method hashGetMethod; private static Method scanPlayersMethod; @@ -143,9 +140,8 @@ class EntityUtilities { // Wrap every player - we also ensure that the underlying tracker list is immutable for (Object tracker : trackedPlayers) { - if (tracker instanceof EntityPlayer) { - EntityPlayer nmsPlayer = (EntityPlayer) tracker; - result.add(nmsPlayer.getBukkitEntity()); + if (MinecraftReflection.isMinecraftPlayer(tracker)) { + result.add((Player) MinecraftReflection.getBukkitEntity(tracker)); } } return result; @@ -165,7 +161,8 @@ class EntityUtilities { * @throws FieldAccessException */ private static Object getEntityTrackerEntry(World world, int entityID) throws FieldAccessException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { - Object worldServer = ((CraftWorld) world).getHandle(); + BukkitUnwrapper unwrapper = new BukkitUnwrapper(); + Object worldServer = unwrapper.unwrapItem(world); // We have to rely on the class naming here. if (entityTrackerField == null) @@ -248,11 +245,15 @@ class EntityUtilities { */ public static Entity getEntityFromID(World world, int entityID) throws FieldAccessException { try { - EntityTrackerEntry trackerEntry = (EntityTrackerEntry) getEntityTrackerEntry(world, entityID); + Object trackerEntry = getEntityTrackerEntry(world, entityID); + + if (trackerField == null) + trackerField = trackerEntry.getClass().getField("tracker"); + Object tracker = FieldUtils.readField(trackerField, trackerEntry, true); // Handle NULL cases - if (trackerEntry != null && trackerEntry.tracker != null) { - return trackerEntry.tracker.getBukkitEntity(); + if (trackerEntry != null && tracker != null) { + return (Entity) MinecraftReflection.getBukkitEntity(tracker); } else { return null; } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/ListenerInvoker.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/ListenerInvoker.java index 52c52e7d..f27b64e5 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/ListenerInvoker.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/ListenerInvoker.java @@ -17,8 +17,6 @@ package com.comphenix.protocol.injector; -import net.minecraft.server.Packet; - import com.comphenix.protocol.events.PacketEvent; /** @@ -45,7 +43,7 @@ public interface ListenerInvoker { * @param packet - the packet. * @return The packet ID. */ - public abstract int getPacketID(Packet packet); + public abstract int getPacketID(Object packet); /** * Associate a given class with the given packet ID. Internal method. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/MinecraftRegistry.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/MinecraftRegistry.java index e433604e..5cdddd7c 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/MinecraftRegistry.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/MinecraftRegistry.java @@ -23,12 +23,12 @@ import java.util.List; import java.util.Map; import java.util.Set; -import net.minecraft.server.Packet; import net.sf.cglib.proxy.Factory; import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FieldUtils; import com.comphenix.protocol.reflect.FuzzyReflection; +import com.comphenix.protocol.utility.MinecraftReflection; import com.google.common.base.Objects; import com.google.common.collect.ImmutableSet; @@ -78,7 +78,7 @@ class MinecraftRegistry { */ private static FuzzyReflection getPacketRegistry() { if (packetRegistry == null) - packetRegistry = FuzzyReflection.fromClass(Packet.class, true); + packetRegistry = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true); return packetRegistry; } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketConstructor.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketConstructor.java index 7c1f85e6..3d138a13 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketConstructor.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketConstructor.java @@ -21,8 +21,6 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.List; -import net.minecraft.server.Packet; - import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.reflect.FieldAccessException; import com.google.common.collect.ImmutableList; @@ -157,7 +155,7 @@ public class PacketConstructor { } } - Packet nmsPacket = (Packet) constructorMethod.newInstance(values); + Object nmsPacket = constructorMethod.newInstance(values); return new PacketContainer(packetID, nmsPacket); } catch (IllegalArgumentException e) { diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java index 27f7c7c0..e43921ef 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java @@ -28,7 +28,6 @@ import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.Nullable; -import net.minecraft.server.Packet; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; @@ -55,6 +54,7 @@ import com.comphenix.protocol.events.*; import com.comphenix.protocol.injector.player.PlayerInjectionHandler; import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FuzzyReflection; +import com.comphenix.protocol.utility.MinecraftReflection; import com.google.common.base.Objects; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableSet; @@ -508,7 +508,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok if (packetCreation.compareAndSet(false, true)) incrementPhases(GamePhase.PLAYING); - Packet mcPacket = packet.getHandle(); + Object mcPacket = packet.getHandle(); // Make sure the packet isn't cancelled packetInjector.undoCancel(packet.getID(), mcPacket); @@ -686,9 +686,11 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok } @Override - public int getPacketID(Packet packet) { + public int getPacketID(Object packet) { if (packet == null) throw new IllegalArgumentException("Packet cannot be NULL."); + if (!MinecraftReflection.isPacketClass(packet)) + throw new IllegalArgumentException("The given object " + packet + " is not a packet."); return MinecraftRegistry.getPacketToID().get(packet.getClass()); } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketInjector.java index 6f6fb19a..0a83c8ec 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketInjector.java @@ -27,7 +27,6 @@ import java.util.concurrent.ConcurrentHashMap; import org.bukkit.entity.Player; -import net.minecraft.server.Packet; import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.Enhancer; @@ -81,7 +80,7 @@ class PacketInjector { * @param id - the id of the packet. * @param packet - packet to uncancel. */ - public void undoCancel(Integer id, Packet packet) { + public void undoCancel(Integer id, Object packet) { ReadPacketModifier modifier = readModifier.get(id); // See if this packet has been cancelled before @@ -93,7 +92,7 @@ class PacketInjector { private void initialize() throws IllegalAccessException { if (intHashMap == null) { // We're looking for the first static field with a Minecraft-object. This should be a IntHashMap. - Field intHashMapField = FuzzyReflection.fromClass(Packet.class, true). + Field intHashMapField = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true). getFieldByType(MinecraftReflection.MINECRAFT_OBJECT); try { diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/ReadPacketModifier.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/ReadPacketModifier.java index ee32ddc2..ceb702c6 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/ReadPacketModifier.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/ReadPacketModifier.java @@ -29,7 +29,6 @@ import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketEvent; -import net.minecraft.server.Packet; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; @@ -61,7 +60,7 @@ class ReadPacketModifier implements MethodInterceptor { * Remove any packet overrides. * @param packet - the packet to rever */ - public void removeOverride(Packet packet) { + public void removeOverride(Object packet) { override.remove(packet); } @@ -70,7 +69,7 @@ class ReadPacketModifier implements MethodInterceptor { * @param packet - the given packet. * @return Overriden object. */ - public Object getOverride(Packet packet) { + public Object getOverride(Object packet) { return override.get(packet); } @@ -79,7 +78,7 @@ class ReadPacketModifier implements MethodInterceptor { * @param packet - the packet to check. * @return TRUE if it has been cancelled, FALSE otherwise. */ - public boolean hasCancelled(Packet packet) { + public boolean hasCancelled(Object packet) { return getOverride(packet) == CANCEL_MARKER; } @@ -121,12 +120,12 @@ class ReadPacketModifier implements MethodInterceptor { DataInputStream input = (DataInputStream) args[0]; // Let the people know - PacketContainer container = new PacketContainer(packetID, (Packet) thisObj); + PacketContainer container = new PacketContainer(packetID, thisObj); PacketEvent event = packetInjector.packetRecieved(container, input); // Handle override if (event != null) { - Packet result = event.getPacket().getHandle(); + Object result = event.getPacket().getHandle(); if (event.isCancelled()) { override.put(thisObj, CANCEL_MARKER); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/StructureCache.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/StructureCache.java index aeedc16a..bc4f7aa2 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/StructureCache.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/StructureCache.java @@ -22,12 +22,11 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import net.minecraft.server.Packet; - import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.reflect.compiler.BackgroundCompiler; import com.comphenix.protocol.reflect.compiler.CompileListener; import com.comphenix.protocol.reflect.compiler.CompiledStructureModifier; +import com.comphenix.protocol.utility.MinecraftReflection; /** * Caches structure modifiers. @@ -45,9 +44,9 @@ public class StructureCache { * @param id - packet ID. * @return Created packet. */ - public static Packet newPacket(int id) { + public static Object newPacket(int id) { try { - return (Packet) MinecraftRegistry.getPacketClassFromID(id, true).newInstance(); + return MinecraftRegistry.getPacketClassFromID(id, true).newInstance(); } catch (InstantiationException e) { return null; } catch (IllegalAccessException e) { @@ -79,7 +78,7 @@ public class StructureCache { if (result == null) { // Use the vanilla class definition final StructureModifier value = new StructureModifier( - MinecraftRegistry.getPacketClassFromID(id, true), Packet.class, true); + MinecraftRegistry.getPacketClassFromID(id, true), MinecraftReflection.getPacketClass(), true); result = structureModifiers.putIfAbsent(id, value); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedArrayList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedArrayList.java index b2fc3855..60f570b5 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedArrayList.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedArrayList.java @@ -25,7 +25,6 @@ import java.util.Set; import com.comphenix.protocol.injector.ListenerInvoker; import com.comphenix.protocol.injector.player.NetworkFieldInjector.FakePacket; -import net.minecraft.server.Packet; import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; @@ -36,7 +35,7 @@ import net.sf.cglib.proxy.MethodProxy; * * @author Kristian */ -class InjectedArrayList extends ArrayList { +class InjectedArrayList extends ArrayList { /** * Silly Eclipse. @@ -44,12 +43,12 @@ class InjectedArrayList extends ArrayList { private static final long serialVersionUID = -1173865905404280990L; private transient PlayerInjector injector; - private transient Set ignoredPackets; + private transient Set ignoredPackets; private transient ClassLoader classLoader; private transient InvertedIntegerCallback callback; - public InjectedArrayList(ClassLoader classLoader, PlayerInjector injector, Set ignoredPackets) { + public InjectedArrayList(ClassLoader classLoader, PlayerInjector injector, Set ignoredPackets) { this.classLoader = classLoader; this.injector = injector; this.ignoredPackets = ignoredPackets; @@ -57,9 +56,9 @@ class InjectedArrayList extends ArrayList { } @Override - public boolean add(Packet packet) { + public boolean add(Object packet) { - Packet result = null; + Object result = null; // Check for fake packets and ignored packets if (packet instanceof FakePacket) { @@ -94,7 +93,7 @@ class InjectedArrayList extends ArrayList { * @param source - packet to invert. * @return The inverted packet. */ - Packet createNegativePacket(Packet source) { + Object createNegativePacket(Object source) { ListenerInvoker invoker = injector.getInvoker(); int packetID = invoker.getPacketID(source); @@ -133,7 +132,7 @@ class InjectedArrayList extends ArrayList { try { // Temporarily associate the fake packet class invoker.registerPacketClass(proxyClass, packetID); - return (Packet) proxyClass.newInstance(); + return proxyClass.newInstance(); } catch (Exception e) { // Don't pollute the throws tree diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedServerConnection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedServerConnection.java index c9ae47ff..1d94a9c9 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedServerConnection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedServerConnection.java @@ -22,7 +22,6 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; -import net.minecraft.server.NetLoginHandler; import net.sf.cglib.proxy.Factory; import org.bukkit.Server; @@ -32,6 +31,7 @@ import com.comphenix.protocol.reflect.FieldUtils; import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.ObjectCloner; import com.comphenix.protocol.reflect.VolatileField; +import com.comphenix.protocol.utility.MinecraftReflection; /** * Used to ensure that the 1.3 server is referencing the correct server handler. @@ -229,7 +229,7 @@ class InjectedServerConnection { @Override protected void onInserting(Object inserting) { // Ready for some login handler injection? - if (inserting instanceof NetLoginHandler) { + if (MinecraftReflection.isLoginHandler(inserting)) { Object replaced = netLoginInjector.onNetLoginCreated(inserting); // Only replace if it has changed @@ -241,7 +241,7 @@ class InjectedServerConnection { @Override protected void onRemoved(Object removing) { // Clean up? - if (removing instanceof NetLoginHandler) { + if (MinecraftReflection.isLoginHandler(removing)) { netLoginInjector.cleanup(removing); } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkFieldInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkFieldInjector.java index dd65fa17..ffb6ca3d 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkFieldInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkFieldInjector.java @@ -40,8 +40,6 @@ import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.reflect.VolatileField; import com.google.common.collect.Sets; -import net.minecraft.server.Packet; - /** * Injection hook that overrides the packet queue lists in NetworkHandler. * @@ -58,7 +56,7 @@ class NetworkFieldInjector extends PlayerInjector { } // Packets to ignore - private Set ignoredPackets = Sets.newSetFromMap(new ConcurrentHashMap()); + private Set ignoredPackets = Sets.newSetFromMap(new ConcurrentHashMap()); // Overridden fields private List overridenLists = new ArrayList(); @@ -99,7 +97,7 @@ class NetworkFieldInjector extends PlayerInjector { } @Override - public void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException { + public void sendServerPacket(Object packet, boolean filtered) throws InvocationTargetException { if (networkManager != null) { try { @@ -147,14 +145,14 @@ class NetworkFieldInjector extends PlayerInjector { VolatileField overwriter = new VolatileField(field, networkManager, true); @SuppressWarnings("unchecked") - List minecraftList = (List) overwriter.getOldValue(); + List minecraftList = (List) overwriter.getOldValue(); synchronized(syncObject) { // The list we'll be inserting - List hackedList = new InjectedArrayList(classLoader, this, ignoredPackets); + List hackedList = new InjectedArrayList(classLoader, this, ignoredPackets); // Add every previously stored packet - for (Packet packet : minecraftList) { + for (Object packet : minecraftList) { hackedList.add(packet); } @@ -172,8 +170,8 @@ class NetworkFieldInjector extends PlayerInjector { protected void cleanHook() { // Clean up for (VolatileField overriden : overridenLists) { - List minecraftList = (List) overriden.getOldValue(); - List hacketList = (List) overriden.getValue(); + List minecraftList = (List) overriden.getOldValue(); + List hacketList = (List) overriden.getValue(); if (minecraftList == hacketList) { return; @@ -183,7 +181,7 @@ class NetworkFieldInjector extends PlayerInjector { synchronized(syncObject) { try { // Copy over current packets - for (Packet packet : (List) overriden.getValue()) { + for (Object packet : (List) overriden.getValue()) { minecraftList.add(packet); } } finally { diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkObjectInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkObjectInjector.java index e391d832..951c4b23 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkObjectInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkObjectInjector.java @@ -19,7 +19,6 @@ package com.comphenix.protocol.injector.player; import java.lang.reflect.InvocationTargetException; -import net.minecraft.server.Packet; import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.CallbackFilter; import net.sf.cglib.proxy.Enhancer; @@ -67,7 +66,7 @@ class NetworkObjectInjector extends PlayerInjector { } @Override - public void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException { + public void sendServerPacket(Object packet, boolean filtered) throws InvocationTargetException { Object networkDelegate = filtered ? networkManagerRef.getValue() : networkManagerRef.getOldValue(); if (networkDelegate != null) { @@ -114,7 +113,7 @@ class NetworkObjectInjector extends PlayerInjector { Callback queueFilter = new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { - Packet packet = (Packet) args[0]; + Object packet = args[0]; if (packet != null) { packet = handlePacketSending(packet); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkServerInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkServerInjector.java index 3a346dfd..a7b42de2 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkServerInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkServerInjector.java @@ -21,7 +21,6 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import net.minecraft.server.Packet; import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.CallbackFilter; import net.sf.cglib.proxy.Enhancer; @@ -43,6 +42,7 @@ import com.comphenix.protocol.reflect.ObjectCloner; import com.comphenix.protocol.reflect.VolatileField; import com.comphenix.protocol.reflect.instances.DefaultInstances; import com.comphenix.protocol.reflect.instances.ExistingGenerator; +import com.comphenix.protocol.utility.MinecraftReflection; /** * Represents a player hook into the NetServerHandler class. @@ -94,7 +94,7 @@ public class NetworkServerInjector extends PlayerInjector { } @Override - public void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException { + public void sendServerPacket(Object packet, boolean filtered) throws InvocationTargetException { Object serverDeleage = filtered ? serverHandlerRef.getValue() : serverHandlerRef.getOldValue(); if (serverDeleage != null) { @@ -152,8 +152,7 @@ public class NetworkServerInjector extends PlayerInjector { Callback sendPacketCallback = new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { - - Packet packet = (Packet) args[0]; + Object packet = args[0]; if (packet != null) { packet = handlePacketSending(packet); @@ -237,7 +236,7 @@ public class NetworkServerInjector extends PlayerInjector { } private Class getFirstMinecraftSuperClass(Class clazz) { - if (clazz.getName().startsWith("net.minecraft.server.")) + if (clazz.getName().startsWith(MinecraftReflection.getMinecraftPackage())) return clazz; else if (clazz.equals(Object.class)) return clazz; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjectionHandler.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjectionHandler.java index c5f832b9..3722e4f5 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjectionHandler.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjectionHandler.java @@ -26,8 +26,6 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; -import net.minecraft.server.Packet; - import org.bukkit.Server; import org.bukkit.entity.Player; @@ -501,7 +499,7 @@ public class PlayerInjectionHandler { * @throws IllegalAccessException If the reflection machinery failed. * @throws InvocationTargetException If the underlying method caused an error. */ - public void processPacket(Player player, Packet mcPacket) throws IllegalAccessException, InvocationTargetException { + public void processPacket(Player player, Object mcPacket) throws IllegalAccessException, InvocationTargetException { PlayerInjector injector = getInjector(player); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java index 7739b68f..f4f8d871 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java @@ -27,14 +27,15 @@ import java.net.SocketAddress; import net.sf.cglib.proxy.Factory; -import org.bukkit.craftbukkit.entity.CraftPlayer; import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitWorker; import com.comphenix.protocol.Packets; import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.events.PacketListener; +import com.comphenix.protocol.injector.BukkitUnwrapper; import com.comphenix.protocol.injector.GamePhase; import com.comphenix.protocol.injector.ListenerInvoker; import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks; @@ -121,8 +122,8 @@ abstract class PlayerInjector { * @return Notch player object. */ protected Object getEntityPlayer(Player player) { - CraftPlayer craft = (CraftPlayer) player; - return craft.getHandle(); + BukkitUnwrapper unwrapper = new BukkitUnwrapper(); + return unwrapper.unwrapItem(player); } /** @@ -136,7 +137,7 @@ abstract class PlayerInjector { //Dispatch to the correct injection method if (injectionSource instanceof Player) initializePlayer(injectionSource); - else if (injectionSource instanceof NetLoginHandler) + else if (MinecraftReflection.isLoginHandler(injectionSource)) initializeLogin(injectionSource); else throw new IllegalArgumentException("Cannot initialize a player hook using a " + injectionSource.getClass().getName()); @@ -148,7 +149,7 @@ abstract class PlayerInjector { */ public void initializePlayer(Object player) { - EntityPlayer notchEntity = getEntityPlayer((Player) player); + Object notchEntity = getEntityPlayer((Player) player); if (!hasInitialized) { // Do this first, in case we encounter an exception @@ -202,7 +203,7 @@ abstract class PlayerInjector { // And the queue method if (queueMethod == null) queueMethod = FuzzyReflection.fromClass(reference.getType()). - getMethodByParameters("queue", Packet.class ); + getMethodByParameters("queue", MinecraftReflection.getPacketClass()); // And the data input stream that we'll use to identify a player if (inputField == null) @@ -321,7 +322,7 @@ abstract class PlayerInjector { } } - private Field getProxyField(EntityPlayer notchEntity, Field serverField) { + private Field getProxyField(Object notchEntity, Field serverField) { try { Object handler = FieldUtils.readField(serverHandlerField, notchEntity, true); @@ -396,10 +397,10 @@ abstract class PlayerInjector { * @return The stored entity player. * @throws IllegalAccessException If the reflection failed. */ - private EntityPlayer getEntityPlayer(Object netHandler) throws IllegalAccessException { + private Object getEntityPlayer(Object netHandler) throws IllegalAccessException { if (entityPlayerField == null) entityPlayerField = FuzzyReflection.fromObject(netHandler).getFieldByType(".*EntityPlayer"); - return (EntityPlayer) FieldUtils.readField(entityPlayerField, netHandler); + return FieldUtils.readField(entityPlayerField, netHandler); } /** @@ -408,15 +409,15 @@ abstract class PlayerInjector { * @throws IllegalAccessException If the reflection machinery failed. * @throws InvocationTargetException If the underlying method caused an error. */ - public void processPacket(Packet packet) throws IllegalAccessException, InvocationTargetException { + public void processPacket(Object packet) throws IllegalAccessException, InvocationTargetException { Object netHandler = getNetHandler(); // Get the process method if (processMethod == null) { try { - processMethod = FuzzyReflection.fromClass(Packet.class). - getMethodByParameters("processPacket", netHandlerField.getType()); + processMethod = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()). + getMethodByParameters("processPacket", netHandlerField.getType()); } catch (RuntimeException e) { throw new IllegalArgumentException("Cannot locate process packet method: " + e.getMessage()); } @@ -438,7 +439,7 @@ abstract class PlayerInjector { * @param filtered - whether or not the packet will be filtered by our listeners. * @param InvocationTargetException If an error occured when sending the packet. */ - public abstract void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException; + public abstract void sendServerPacket(Object packet, boolean filtered) throws InvocationTargetException; /** * Inject a hook to catch packets sent to the current player. @@ -499,7 +500,7 @@ abstract class PlayerInjector { * @param packet - packet to sent. * @return The given packet, or the packet replaced by the listeners. */ - public Packet handlePacketSending(Packet packet) { + public Object handlePacketSending(Object packet) { try { // Get the packet ID too Integer id = invoker.getPacketID(packet); @@ -514,7 +515,7 @@ abstract class PlayerInjector { if (updateOnLogin) { if (id == Packets.Server.LOGIN) { try { - updatedPlayer = getEntityPlayer(getNetHandler()).getBukkitEntity(); + updatedPlayer = (Player) MinecraftReflection.getBukkitEntity(getEntityPlayer(getNetHandler())); } catch (IllegalAccessException e) { reporter.reportDetailed(this, "Cannot update player in PlayerEvent.", e, packet); } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/CachedPackage.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/CachedPackage.java new file mode 100644 index 00000000..e69f956c --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/CachedPackage.java @@ -0,0 +1,46 @@ +package com.comphenix.protocol.utility; + +import java.util.Map; + +import com.google.common.collect.Maps; + +/** + * Represents a dynamic package and an arbitrary number of cached classes. + * + * @author Kristian + */ +class CachedPackage { + private Map> cache; + private String packageName; + + public CachedPackage(String packageName) { + this.packageName = packageName; + this.cache = Maps.newConcurrentMap(); + } + + /** + * Retrieve the class object of a specific class in the current package. + * @param className - the specific class. + * @return Class object. + * @throws RuntimeException If we are unable to find the given class. + */ + @SuppressWarnings("rawtypes") + public Class getPackageClass(String className) { + try { + Class result = cache.get(className); + + // Concurrency is not a problem - we don't care if we look up a class twice + if (result == null) { + // Look up the class dynamically + result = CachedPackage.class.getClassLoader(). + loadClass(packageName + "." + className); + cache.put(className, result); + } + + return result; + + } catch (ClassNotFoundException e) { + throw new RuntimeException("Cannot find class " + className, e); + } + } +} 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 80f73190..1a62fcae 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java @@ -1,13 +1,453 @@ package com.comphenix.protocol.utility; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +import javax.annotation.Nonnull; + +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.inventory.ItemStack; + +import com.comphenix.protocol.injector.BukkitUnwrapper; + /** * Methods and constants specifically used in conjuction with reflecting Minecraft object. + * * @author Kristian - * */ public class MinecraftReflection { /** - * Matches a Minecraft object. + * Regular expression that matches a Minecraft object. */ public static final String MINECRAFT_OBJECT = "net\\.minecraft(\\.\\w+)+"; + + /** + * The package name of all the classes that belongs to the native code in Minecraft. + */ + private static String MINECRAFT_SERVER_PACKAGE = "net.minecraft.server"; + + private static String MINECRAFT_FULL_PACKAGE = null; + private static String CRAFTBUKKIT_PACKAGE = null; + + private static CachedPackage minecraftPackage; + private static CachedPackage craftbukkitPackage; + + // org.bukkit.craftbukkit + private static Class craftItemStackClass; + private static Constructor craftNMSConstructor; + private static Constructor craftBukkitConstructor; + + // net.minecraft.server + private static Class itemStackArrayClass; + + /** + * Retrieve the name of the Minecraft server package. + * @return Full canonical name of the Minecraft server package. + */ + public static String getMinecraftPackage() { + // Speed things up + if (MINECRAFT_FULL_PACKAGE != null) + return MINECRAFT_FULL_PACKAGE; + + Server craftServer = Bukkit.getServer(); + + // This server should have a "getHandle" method that we can use + if (craftServer != null) { + try { + Class craftClass = craftServer.getClass(); + Method getHandle = craftClass.getMethod("getHandle"); + + Class returnType = getHandle.getReturnType(); + String returnName = returnType.getCanonicalName(); + + // The return type will tell us the full package, regardless of formating + CRAFTBUKKIT_PACKAGE = getPackage(craftClass.getCanonicalName()); + MINECRAFT_FULL_PACKAGE = getPackage(returnName); + return MINECRAFT_FULL_PACKAGE; + + } catch (SecurityException e) { + throw new RuntimeException("Security violation. Cannot get handle method.", e); + } catch (NoSuchMethodException e) { + throw new IllegalStateException("Cannot find getHandle() method on server. Is this a modified CraftBukkit version?", e); + } + + } else { + throw new IllegalStateException("Cannot find Bukkit. Is it running?"); + } + } + + /** + * Retrieve the name of the root CraftBukkit package. + * @return Full canonical name of the root CraftBukkit package. + */ + public static String getCraftBukkitPackage() { + // Ensure it has been initialized + getMinecraftPackage(); + return CRAFTBUKKIT_PACKAGE; + } + + /** + * Retrieve the package name from a given canonical Java class name. + * @param fullName - full Java class name. + * @return The package name. + */ + private static String getPackage(String fullName) { + return fullName.substring(0, fullName.lastIndexOf(".")); + } + + /** + * Determine if a given object can be found within the package net.minecraft.server. + * @param obj - the object to test. + * @return TRUE if it can, FALSE otherwise. + */ + public static boolean isMinecraftObject(@Nonnull Object obj) { + if (obj == null) + throw new IllegalArgumentException("Cannot determine the type of a null object."); + + // Doesn't matter if we don't check for the version here + return obj.getClass().getName().startsWith(MINECRAFT_SERVER_PACKAGE); + } + + /** + * Determine if a given object is found in net.minecraft.server, and has the given name. + * @param obj - the object to test. + * @param className - the class name to test. + * @return TRUE if it can, FALSE otherwise. + */ + public static boolean isMinecraftObject(@Nonnull Object obj, String className) { + if (obj == null) + throw new IllegalArgumentException("Cannot determine the type of a null object."); + + String javaName = obj.getClass().getName(); + return javaName.startsWith(MINECRAFT_SERVER_PACKAGE) && javaName.endsWith(className); + } + + /** + * Dynamically retrieve the Bukkit entity from a given entity. + * @param nmsObject - the NMS entity. + * @return A bukkit entity. + * @throws RuntimeException If we were unable to retrieve the Bukkit entity. + */ + public static Object getBukkitEntity(Object nmsObject) { + if (nmsObject == null) + return null; + + // We will have to do this dynamically, unfortunately + try { + return nmsObject.getClass().getMethod("getBukkitEntity").invoke(nmsObject); + } catch (Exception e) { + throw new RuntimeException("Cannot get Bukkit entity from " + nmsObject, e); + } + } + + /** + * Determine if a given object is a ChunkPosition. + * @param obj - the object to test. + * @return TRUE if it can, FALSE otherwise. + */ + @SuppressWarnings("unchecked") + public static boolean isChunkPosition(Object obj) { + return getChunkPositionClass().isAssignableFrom(obj.getClass()); + } + + /** + * Determine if a given object is a ChunkCoordinate. + * @param obj - the object to test. + * @return TRUE if it can, FALSE otherwise. + */ + @SuppressWarnings("unchecked") + public static boolean isChunkCoordinates(Object obj) { + return getChunkCoordinatesClass().isAssignableFrom(obj.getClass()); + } + + /** + * Determine if the given object is actually a Minecraft packet. + * @param obj - the given object. + * @return TRUE if it is, FALSE otherwise. + */ + @SuppressWarnings("unchecked") + public static boolean isPacketClass(Object obj) { + return getPacketClass().isAssignableFrom(obj.getClass()); + } + + /** + * Determine if the given object is a NetLoginHandler. + * @param obj - the given object. + * @return TRUE if it is, FALSE otherwise. + */ + @SuppressWarnings("unchecked") + public static boolean isLoginHandler(Object obj) { + return getNetLoginHandlerClass().isAssignableFrom(obj.getClass()); + } + + /** + * Determine if the given object is actually a Minecraft packet. + * @param obj - the given object. + * @return TRUE if it is, FALSE otherwise. + */ + @SuppressWarnings("unchecked") + public static boolean isMinecraftEntity(Object obj) { + return getEntityClass().isAssignableFrom(obj.getClass()); + } + + /** + * Determine if the given object is a NMS ItemStack. + * @param obj - the given object. + * @return TRUE if it is, FALSE otherwise. + */ + @SuppressWarnings("unchecked") + public static boolean isItemStack(Object value) { + return getItemStackClass().isAssignableFrom(value.getClass()); + } + + /** + * Determine if the given object is a Minecraft player entity. + * @param obj - the given object. + * @return TRUE if it is, FALSE otherwise. + */ + @SuppressWarnings("unchecked") + public static boolean isMinecraftPlayer(Object obj) { + return getEntityPlayerClass().isAssignableFrom(obj.getClass()); + } + + /** + * Determine if the given object is a watchable object. + * @param obj - the given object. + * @return TRUE if it is, FALSE otherwise. + */ + @SuppressWarnings("unchecked") + public static boolean isWatchableObject(Object obj) { + return getWatchableObjectClass().isAssignableFrom(obj.getClass()); + } + + /** + * Determine if the given object is a data watcher object. + * @param obj - the given object. + * @return TRUE if it is, FALSE otherwise. + */ + @SuppressWarnings("unchecked") + public static boolean isDataWatcher(Object obj) { + return getDataWatcherClass().isAssignableFrom(obj.getClass()); + } + + /** + * Determine if the given object is a CraftItemStack instancey. + * @param obj - the given object. + * @return TRUE if it is, FALSE otherwise. + */ + @SuppressWarnings("unchecked") + public static boolean isCraftItemStack(Object obj) { + return getCraftItemStackClass().isAssignableFrom(obj.getClass()); + } + + /** + * Retrieve the EntityPlayer (NMS) class. + * @return The entity class. + */ + @SuppressWarnings("rawtypes") + public static Class getEntityPlayerClass() { + return getMinecraftClass("EntityPlayer"); + } + + /** + * Retrieve the entity (NMS) class. + * @return The entity class. + */ + @SuppressWarnings("rawtypes") + public static Class getEntityClass() { + return getMinecraftClass("Entity"); + } + + /** + * Retrieve the packet class. + * @return The packet class. + */ + @SuppressWarnings("rawtypes") + public static Class getPacketClass() { + return getMinecraftClass("Packet"); + } + + /** + * Retrieve the NetLoginHandler class. + * @return The NetLoginHandler class. + */ + @SuppressWarnings("rawtypes") + public static Class getNetLoginHandlerClass() { + return getMinecraftClass("NetLoginHandler"); + } + + /** + * Retrieve the NetLoginHandler class. + * @return The NetLoginHandler class. + */ + @SuppressWarnings("rawtypes") + public static Class getItemStackClass() { + return getMinecraftClass("ItemStack"); + } + + /** + * Retrieve the WorldType class. + * @return The WorldType class. + */ + @SuppressWarnings("rawtypes") + public static Class getWorldTypeClass() { + return getMinecraftClass("WorldType"); + } + + /** + * Retrieve the DataWatcher class. + * @return The DataWatcher class. + */ + @SuppressWarnings("rawtypes") + public static Class getDataWatcherClass() { + return getMinecraftClass("DataWatcher"); + } + + /** + * Retrieve the ChunkPosition class. + * @return The ChunkPosition class. + */ + @SuppressWarnings("rawtypes") + public static Class getChunkPositionClass() { + return getMinecraftClass("ChunkPosition"); + } + + /** + * Retrieve the ChunkPosition class. + * @return The ChunkPosition class. + */ + @SuppressWarnings("rawtypes") + public static Class getChunkCoordinatesClass() { + return getMinecraftClass("ChunkCoordinates"); + } + + /** + * Retrieve the WatchableObject class. + * @return The WatchableObject class. + */ + @SuppressWarnings("rawtypes") + public static Class getWatchableObjectClass() { + return getMinecraftClass("WatchableObject"); + } + + /** + * Retrieve the ItemStack[] class. + * @return The ItemStack[] class. + */ + @SuppressWarnings("rawtypes") + public static Class getItemStackArrayClass() { + if (itemStackArrayClass == null) + itemStackArrayClass = getArrayClass(getItemStackClass()); + return itemStackArrayClass; + } + + /** + * Retrieve the array class of a given component type. + * @param componentType - type of each element in the array. + * @return The class of the array. + */ + @SuppressWarnings("rawtypes") + public static Class getArrayClass(Class componentType) { + // Bit of a hack, but it works + return Array.newInstance(componentType, 0).getClass(); + } + + /** + * Retrieve the CraftItemStack class. + * @return The CraftItemStack class. + */ + @SuppressWarnings("rawtypes") + public static Class getCraftItemStackClass() { + if (craftItemStackClass == null) + craftItemStackClass = getCraftBukkitClass("inventory.CraftItemStack"); + return craftItemStackClass; + } + + /** + * Retrieve a CraftItemStack from a given ItemStack. + * @param bukkitItemStack - the Bukkit ItemStack to convert. + * @return A CraftItemStack as an ItemStack. + */ + @SuppressWarnings("unchecked") + public static ItemStack getBukkitItemStack(ItemStack bukkitItemStack) { + if (craftBukkitConstructor == null) { + try { + craftBukkitConstructor = getCraftItemStackClass().getConstructor(ItemStack.class); + } catch (Exception e) { + throw new RuntimeException("Cannot find CraftItemStack(org.bukkit.inventory.ItemStack).", e); + } + } + + // Try to create the CraftItemStack + try { + return (ItemStack) craftBukkitConstructor.newInstance(bukkitItemStack); + } catch (Exception e) { + throw new RuntimeException("Cannot construct CraftItemStack.", e); + } + } + + /** + * Retrieve the Bukkit ItemStack from a given net.minecraft.server ItemStack. + * @param minecraftItemStack - the NMS ItemStack to wrap. + * @return The wrapped ItemStack. + */ + @SuppressWarnings("unchecked") + public static ItemStack getBukkitItemStack(Object minecraftItemStack) { + if (craftNMSConstructor == null) { + try { + craftNMSConstructor = getCraftItemStackClass().getConstructor(minecraftItemStack.getClass()); + } catch (Exception e) { + throw new RuntimeException("Cannot find CraftItemStack(net.mineraft.server.ItemStack).", e); + } + } + + // Try to create the CraftItemStack + try { + return (ItemStack) craftNMSConstructor.newInstance(minecraftItemStack); + } catch (Exception e) { + throw new RuntimeException("Cannot construct CraftItemStack.", e); + } + } + + /** + * Retrieve the net.minecraft.server ItemStack from a Bukkit ItemStack. + * @param stack - the Bukkit ItemStack to convert. + * @return The NMS ItemStack. + */ + public static Object getMinecraftItemStack(ItemStack stack) { + // Make sure this is a CraftItemStack + if (!isCraftItemStack(stack)) + stack = getBukkitItemStack(stack); + + BukkitUnwrapper unwrapper = new BukkitUnwrapper(); + return unwrapper.unwrapItem(stack); + } + + /** + * Retrieve the class object of a specific CraftBukkit class. + * @param className - the specific CraftBukkit class. + * @return Class object. + * @throws RuntimeException If we are unable to find the given class. + */ + @SuppressWarnings("rawtypes") + public static Class getCraftBukkitClass(String className) { + if (craftbukkitPackage == null) + craftbukkitPackage = new CachedPackage(getCraftBukkitPackage()); + return craftbukkitPackage.getPackageClass(className); + } + + /** + * Retrieve the class object of a specific Minecraft class. + * @param className - the specific Minecraft class. + * @return Class object. + * @throws RuntimeException If we are unable to find the given class. + */ + @SuppressWarnings("rawtypes") + public static Class getMinecraftClass(String className) { + if (minecraftPackage == null) + minecraftPackage = new CachedPackage(getMinecraftPackage()); + return minecraftPackage.getPackageClass(className); + } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java index ce2af3e8..7b9bfe49 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java @@ -1,16 +1,13 @@ package com.comphenix.protocol.wrappers; import java.lang.ref.WeakReference; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import net.minecraft.server.DataWatcher; -import net.minecraft.server.WatchableObject; - import org.bukkit.World; import org.bukkit.WorldType; -import org.bukkit.craftbukkit.inventory.CraftItemStack; import org.bukkit.entity.Entity; import org.bukkit.inventory.ItemStack; @@ -19,6 +16,7 @@ import com.comphenix.protocol.ProtocolManager; import com.comphenix.protocol.reflect.EquivalentConverter; import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.instances.DefaultInstances; +import com.comphenix.protocol.utility.MinecraftReflection; /** * Contains several useful equivalent converters for normal Bukkit types. @@ -29,9 +27,13 @@ public class BukkitConverters { // Check whether or not certain classes exists private static boolean hasWorldType = false; + // Used to access the world type + private static Method worldTypeName; + private static Method worldTypeGetType; + static { try { - Class.forName("net.minecraft.server.WorldType"); + Class.forName(MinecraftReflection.getMinecraftPackage() + ".WorldType"); hasWorldType = true; } catch (ClassNotFoundException e) { } @@ -100,8 +102,8 @@ public class BukkitConverters { } public WrappedWatchableObject getSpecific(Object generic) { - if (generic instanceof WatchableObject) - return new WrappedWatchableObject((WatchableObject) generic); + if (MinecraftReflection.isWatchableObject(generic)) + return new WrappedWatchableObject(generic); else if (generic instanceof WrappedWatchableObject) return (WrappedWatchableObject) generic; else @@ -128,8 +130,8 @@ public class BukkitConverters { @Override public WrappedDataWatcher getSpecific(Object generic) { - if (generic instanceof DataWatcher) - return new WrappedDataWatcher((DataWatcher) generic); + if (MinecraftReflection.isDataWatcher(generic)) + return new WrappedDataWatcher(generic); else if (generic instanceof WrappedDataWatcher) return (WrappedDataWatcher) generic; else @@ -153,15 +155,35 @@ public class BukkitConverters { return null; return getIgnoreNull(new EquivalentConverter() { + @SuppressWarnings("unchecked") @Override public Object getGeneric(Class genericType, WorldType specific) { - return net.minecraft.server.WorldType.getType(specific.getName()); + try { + if (worldTypeGetType == null) + worldTypeGetType = MinecraftReflection.getWorldTypeClass().getMethod("getType", String.class); + + // Convert to the Bukkit world type + return worldTypeGetType.invoke(this, specific.getName()); + + } catch (Exception e) { + throw new FieldAccessException("Cannot find the WorldType.getType() method.", e); + } } + @SuppressWarnings("unchecked") @Override public WorldType getSpecific(Object generic) { - net.minecraft.server.WorldType type = (net.minecraft.server.WorldType) generic; - return WorldType.getByName(type.name()); + try { + if (worldTypeName == null) + worldTypeName = MinecraftReflection.getWorldTypeClass().getMethod("name"); + + // Dynamically call the namne method + String name = (String) worldTypeName.invoke(generic); + return WorldType.getByName(name); + + } catch (Exception e) { + throw new FieldAccessException("Cannot call the name method in WorldType.", e); + } } @Override @@ -221,12 +243,12 @@ public class BukkitConverters { public static EquivalentConverter getItemStackConverter() { return getIgnoreNull(new EquivalentConverter() { public Object getGeneric(Class genericType, ItemStack specific) { - return toStackNMS(specific); + return MinecraftReflection.getMinecraftItemStack(specific); } @Override public ItemStack getSpecific(Object generic) { - return new CraftItemStack((net.minecraft.server.ItemStack) generic); + return MinecraftReflection.getBukkitItemStack(generic); } @Override @@ -236,20 +258,6 @@ public class BukkitConverters { }); } - /** - * Convert an item stack to the NMS equivalent. - * @param stack - Bukkit stack to convert. - * @return A bukkit stack. - */ - private static net.minecraft.server.ItemStack toStackNMS(ItemStack stack) { - // We must be prepared for an object that simply implements ItemStcak - if (stack instanceof CraftItemStack) { - return ((CraftItemStack) stack).getHandle(); - } else { - return (new CraftItemStack(stack)).getHandle(); - } - } - /** * Wraps a given equivalent converter in NULL checks, ensuring that such values are ignored. * @param delegate - the underlying equivalent converter. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/ChunkPosition.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/ChunkPosition.java index a00a139a..fd7bc679 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/ChunkPosition.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/ChunkPosition.java @@ -1,10 +1,13 @@ package com.comphenix.protocol.wrappers; +import java.lang.reflect.Constructor; + import org.bukkit.util.Vector; import com.comphenix.protocol.reflect.EquivalentConverter; import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.StructureModifier; +import com.comphenix.protocol.utility.MinecraftReflection; import com.google.common.base.Objects; /** @@ -19,6 +22,8 @@ public class ChunkPosition { */ public static ChunkPosition ORIGIN = new ChunkPosition(0, 0, 0); + private static Constructor chunkPositionConstructor; + // Use protected members, like Bukkit protected final int x; protected final int y; @@ -128,27 +133,35 @@ public class ChunkPosition { */ public static EquivalentConverter getConverter() { return new EquivalentConverter() { + @SuppressWarnings("unchecked") @Override public Object getGeneric(Class genericType, ChunkPosition specific) { - return new net.minecraft.server.ChunkPosition(specific.x, specific.y, specific.z); + if (chunkPositionConstructor == null) { + try { + chunkPositionConstructor = MinecraftReflection.getChunkPositionClass(). + getConstructor(int.class, int.class, int.class); + } catch (Exception e) { + throw new RuntimeException("Cannot find chunk position constructor.", e); + } + } + + // Construct the underlying ChunkPosition + try { + return chunkPositionConstructor.newInstance(specific.x, specific.y, specific.z); + } catch (Exception e) { + throw new RuntimeException("Cannot construct ChunkPosition.", e); + } } @Override public ChunkPosition getSpecific(Object generic) { - if (generic instanceof net.minecraft.server.ChunkPosition) { - net.minecraft.server.ChunkPosition other = (net.minecraft.server.ChunkPosition) generic; + if (MinecraftReflection.isChunkPosition(generic)) { + // Use a structure modifier + intModifier = new StructureModifier(generic.getClass(), null, false).withType(int.class); - try { - if (intModifier == null) - return new ChunkPosition(other.x, other.y, other.z); - } catch (LinkageError e) { - // It could happen. If it does, use a structure modifier instead - intModifier = new StructureModifier(other.getClass(), null, false).withType(int.class); - - // Damn it all - if (intModifier.size() < 3) { - throw new IllegalStateException("Cannot read class " + other.getClass() + " for its integer fields."); - } + // Damn it all + if (intModifier.size() < 3) { + throw new IllegalStateException("Cannot read class " + generic.getClass() + " for its integer fields."); } if (intModifier.size() >= 3) { diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedChunkCoordinate.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedChunkCoordinate.java index 8385770d..2b6bb244 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedChunkCoordinate.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedChunkCoordinate.java @@ -1,9 +1,9 @@ package com.comphenix.protocol.wrappers; +import com.comphenix.protocol.reflect.StructureModifier; +import com.comphenix.protocol.utility.MinecraftReflection; import com.google.common.base.Objects; -import net.minecraft.server.ChunkCoordinates; - /** * Allows access to a chunk coordinate. * @@ -16,23 +16,42 @@ public class WrappedChunkCoordinate implements Comparable intModifier; + /** * Create a new empty wrapper. */ + @SuppressWarnings("rawtypes") public WrappedChunkCoordinate() { - this(new ChunkCoordinates()); + try { + this.handle = (Comparable) MinecraftReflection.getChunkCoordinatesClass().newInstance(); + initializeModifier(); + } catch (Exception e) { + throw new RuntimeException("Cannot construct chunk coordinate."); + } } /** * Create a wrapper for a specific chunk coordinates. * @param handle - the NMS chunk coordinates. */ - public WrappedChunkCoordinate(ChunkCoordinates handle) { + @SuppressWarnings("rawtypes") + public WrappedChunkCoordinate(Comparable handle) { if (handle == null) throw new IllegalArgumentException("handle cannot be NULL"); this.handle = handle; + initializeModifier(); + } + + // Ensure that the structure modifier is initialized + private void initializeModifier() { + if (intModifier == null) { + intModifier = new StructureModifier(handle.getClass(), null, false).withType(int.class); + } } /** @@ -56,7 +75,7 @@ public class WrappedChunkCoordinate implements Comparable baseModifier; - protected WatchableObject handle; + // Used to create new watchable objects + private static Constructor watchableConstructor; + + protected Object handle; protected StructureModifier modifier; // Type of the stored value @@ -32,7 +36,7 @@ public class WrappedWatchableObject { * Wrap a given raw Minecraft watchable object. * @param handle - the raw watchable object to wrap. */ - public WrappedWatchableObject(WatchableObject handle) { + public WrappedWatchableObject(Object handle) { load(handle); } @@ -41,6 +45,7 @@ public class WrappedWatchableObject { * @param index - the index. * @param value - non-null value of specific types. */ + @SuppressWarnings("unchecked") public WrappedWatchableObject(int index, Object value) { if (value == null) throw new IllegalArgumentException("Value cannot be NULL."); @@ -49,14 +54,28 @@ public class WrappedWatchableObject { Integer typeID = WrappedDataWatcher.getTypeID(value.getClass()); if (typeID != null) { - load(new WatchableObject(typeID, index, getUnwrapped(value))); + if (watchableConstructor == null) { + try { + watchableConstructor = MinecraftReflection.getWatchableObjectClass(). + getConstructor(int.class, int.class, Object.class); + } catch (Exception e) { + throw new RuntimeException("Cannot get the WatchableObject(int, int, Object) constructor.", e); + } + } + + // Create the object + try { + load(watchableConstructor.newInstance(typeID, index, getUnwrapped(value))); + } catch (Exception e) { + throw new RuntimeException("Cannot construct underlying WatchableObject.", e); + } } else { throw new IllegalArgumentException("Cannot watch the type " + value.getClass()); } } // Wrap a NMS object - private void load(WatchableObject handle) { + private void load(Object handle) { initialize(); this.handle = handle; this.modifier = baseModifier.withTarget(handle); @@ -66,7 +85,7 @@ public class WrappedWatchableObject { * Retrieves the underlying watchable object. * @return The underlying watchable object. */ - public WatchableObject getHandle() { + public Object getHandle() { return handle; } @@ -76,7 +95,7 @@ public class WrappedWatchableObject { private static void initialize() { if (!hasInitialized) { hasInitialized = true; - baseModifier = new StructureModifier(WatchableObject.class, null, false); + baseModifier = new StructureModifier(MinecraftReflection.getWatchableObjectClass(), null, false); } } @@ -204,12 +223,13 @@ public class WrappedWatchableObject { * @param value - the raw NMS object to wrap. * @return The wrapped object. */ + @SuppressWarnings("rawtypes") static Object getWrapped(Object value) { // Handle the special cases - if (value instanceof net.minecraft.server.ItemStack) { + if (MinecraftReflection.isItemStack(value)) { return BukkitConverters.getItemStackConverter().getSpecific(value); - } else if (value instanceof ChunkCoordinates) { - return new WrappedChunkCoordinate((ChunkCoordinates) value); + } else if (MinecraftReflection.isChunkCoordinates(value)) { + return new WrappedChunkCoordinate((Comparable) value); } else { return value; } @@ -221,9 +241,9 @@ public class WrappedWatchableObject { * @return The wrapped class type. */ static Class getWrappedType(Class unwrapped) { - if (unwrapped.equals(net.minecraft.server.ChunkPosition.class)) + if (unwrapped.equals(MinecraftReflection.getChunkPositionClass())) return ChunkPosition.class; - else if (unwrapped.equals(ChunkCoordinates.class)) + else if (unwrapped.equals(MinecraftReflection.getChunkCoordinatesClass())) return WrappedChunkCoordinate.class; else return unwrapped; @@ -240,7 +260,7 @@ public class WrappedWatchableObject { return ((WrappedChunkCoordinate) wrapped).getHandle(); else if (wrapped instanceof ItemStack) return BukkitConverters.getItemStackConverter().getGeneric( - net.minecraft.server.ItemStack.class, (org.bukkit.inventory.ItemStack) wrapped); + MinecraftReflection.getItemStackClass(), (ItemStack) wrapped); else return wrapped; } @@ -252,9 +272,9 @@ public class WrappedWatchableObject { */ static Class getUnwrappedType(Class wrapped) { if (wrapped.equals(ChunkPosition.class)) - return net.minecraft.server.ChunkPosition.class; + return MinecraftReflection.getChunkPositionClass(); else if (wrapped.equals(WrappedChunkCoordinate.class)) - return ChunkCoordinates.class; + return MinecraftReflection.getChunkCoordinatesClass(); else return wrapped; } @@ -265,7 +285,8 @@ public class WrappedWatchableObject { * @throws FieldAccessException If we're unable to use reflection. */ public WrappedWatchableObject deepClone() throws FieldAccessException { - WrappedWatchableObject clone = new WrappedWatchableObject(DefaultInstances.DEFAULT.getDefault(WatchableObject.class)); + @SuppressWarnings("unchecked") + WrappedWatchableObject clone = new WrappedWatchableObject(DefaultInstances.DEFAULT.getDefault(MinecraftReflection.getWatchableObjectClass())); clone.setDirtyState(getDirtyState()); clone.setIndex(getIndex()); @@ -279,11 +300,11 @@ public class WrappedWatchableObject { Object value = getValue(); // Only a limited set of references types are supported - if (value instanceof net.minecraft.server.ChunkPosition) { + if (MinecraftReflection.isChunkPosition(value)) { EquivalentConverter converter = ChunkPosition.getConverter(); - return converter.getGeneric(net.minecraft.server.ChunkPosition.class, converter.getSpecific(value)); - } else if (value instanceof ItemStack) { - return ((ItemStack) value).cloneItemStack(); + return converter.getGeneric(MinecraftReflection.getChunkPositionClass(), converter.getSpecific(value)); + } else if (MinecraftReflection.isItemStack(value)) { + return MinecraftReflection.getMinecraftItemStack(MinecraftReflection.getBukkitItemStack(value).clone()); } else { // A string or primitive wrapper, which are all immutable. return value; diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/reflect/StructureModifierTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/reflect/StructureModifierTest.java deleted file mode 100644 index af570e39..00000000 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/reflect/StructureModifierTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.comphenix.protocol.reflect; - -import static org.junit.Assert.*; - -import net.minecraft.server.Packet103SetSlot; - -import org.junit.Test; - -import com.avaje.ebeaninternal.server.cluster.Packet; -import com.comphenix.protocol.reflect.StructureModifier; - -public class StructureModifierTest { - - @Test - public void test() throws FieldAccessException { - - Packet103SetSlot move = new Packet103SetSlot(); - StructureModifier modifier = new StructureModifier( - Packet103SetSlot.class, Packet.class, true); - - move.a = 1; - int value = (Integer) modifier.withTarget(move).withType(int.class).read(0); - - assertEquals(1, value); - } -} From 6530db23b7de7e699dedae6dc44ef6c7c953be97 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Thu, 6 Dec 2012 03:54:25 +0100 Subject: [PATCH 08/17] Last commit contains breaking API changes, so increment version. --- ProtocolLib/pom.xml | 2 +- ProtocolLib/src/main/resources/plugin.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ProtocolLib/pom.xml b/ProtocolLib/pom.xml index 2f5c28a1..f01c02d6 100644 --- a/ProtocolLib/pom.xml +++ b/ProtocolLib/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.comphenix.protocol ProtocolLib - 1.7.2-SNAPSHOT + 1.8.0-SNAPSHOT jar Provides read/write access to the Minecraft protocol. diff --git a/ProtocolLib/src/main/resources/plugin.yml b/ProtocolLib/src/main/resources/plugin.yml index 7bac3e23..f56352fe 100644 --- a/ProtocolLib/src/main/resources/plugin.yml +++ b/ProtocolLib/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: ProtocolLib -version: 1.7.2-SNAPSHOT +version: 1.8.0-SNAPSHOT description: Provides read/write access to the Minecraft protocol. author: Comphenix website: http://www.comphenix.net/ProtocolLib From af546e7f1d470eb55f173929aafee7d02678e355 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Thu, 6 Dec 2012 04:04:07 +0100 Subject: [PATCH 09/17] Ignore depcreciation - we're using the method correctly. --- ProtocolLib/dependency-reduced-pom.xml | 2 +- .../java/com/comphenix/protocol/CommandProtocol.java | 2 ++ .../comphenix/protocol/async/AsyncListenerHandler.java | 9 +++++++-- .../protocol/injector/player/PlayerInjector.java | 1 - .../java/com/comphenix/protocol/metrics/Metrics.java | 3 ++- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/ProtocolLib/dependency-reduced-pom.xml b/ProtocolLib/dependency-reduced-pom.xml index f877ad9e..4830e8fd 100644 --- a/ProtocolLib/dependency-reduced-pom.xml +++ b/ProtocolLib/dependency-reduced-pom.xml @@ -4,7 +4,7 @@ com.comphenix.protocol ProtocolLib ProtocolLib - 1.7.2-SNAPSHOT + 1.8.0-SNAPSHOT Provides read/write access to the Minecraft protocol. http://dev.bukkit.org/server-mods/protocollib/ diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/CommandProtocol.java b/ProtocolLib/src/main/java/com/comphenix/protocol/CommandProtocol.java index fa20d46d..d4c6aa05 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/CommandProtocol.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/CommandProtocol.java @@ -47,6 +47,7 @@ class CommandProtocol extends CommandBase { return true; } + @SuppressWarnings("deprecation") public void checkVersion(final CommandSender sender) { // Perform on an async thread plugin.getServer().getScheduler().scheduleAsyncDelayedTask(plugin, new Runnable() { @@ -64,6 +65,7 @@ class CommandProtocol extends CommandBase { updateFinished(); } + @SuppressWarnings("deprecation") public void updateVersion(final CommandSender sender) { // Perform on an async thread plugin.getServer().getScheduler().scheduleAsyncDelayedTask(plugin, new Runnable() { diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/async/AsyncListenerHandler.java b/ProtocolLib/src/main/java/com/comphenix/protocol/async/AsyncListenerHandler.java index f37511f8..cefd8a0a 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/async/AsyncListenerHandler.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/async/AsyncListenerHandler.java @@ -242,7 +242,7 @@ public class AsyncListenerHandler { final AsyncRunnable listenerLoop = getListenerLoop(); - filterManager.getScheduler().scheduleAsyncDelayedTask(listener.getPlugin(), new Runnable() { + scheduleAsync(new Runnable() { @Override public void run() { Thread thread = Thread.currentThread(); @@ -290,7 +290,7 @@ public class AsyncListenerHandler { final AsyncRunnable listenerLoop = getListenerLoop(); final Function delegateCopy = executor; - filterManager.getScheduler().scheduleAsyncDelayedTask(listener.getPlugin(), new Runnable() { + scheduleAsync(new Runnable() { @Override public void run() { delegateCopy.apply(listenerLoop); @@ -298,6 +298,11 @@ public class AsyncListenerHandler { }); } + @SuppressWarnings("deprecation") + private void scheduleAsync(Runnable runnable) { + filterManager.getScheduler().scheduleAsyncDelayedTask(listener.getPlugin(), runnable); + } + /** * Create a friendly thread name using the following convention: *

diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java index f4f8d871..239f6b7a 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java @@ -28,7 +28,6 @@ import java.net.SocketAddress; import net.sf.cglib.proxy.Factory; import org.bukkit.entity.Player; -import org.bukkit.scheduler.BukkitWorker; import com.comphenix.protocol.Packets; import com.comphenix.protocol.error.ErrorReporter; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/metrics/Metrics.java b/ProtocolLib/src/main/java/com/comphenix/protocol/metrics/Metrics.java index 493bbeaf..3ee7d6e9 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/metrics/Metrics.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/metrics/Metrics.java @@ -234,7 +234,8 @@ class Metrics { * * @return True if statistics measuring is running, otherwise false. */ - public boolean start() { + @SuppressWarnings("deprecation") + public boolean start() { synchronized (optOutLock) { // Did we opt out? if (isOptOut()) { From 6b5ecd7309052a0156875f2e9f19b69ad3a7ca0c Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Sat, 8 Dec 2012 14:22:07 +0100 Subject: [PATCH 10/17] Improve the functionality of wrapped DataWatcher and WatchableObject. --- .../protocol/utility/MinecraftReflection.java | 10 +++ .../protocol/wrappers/WrappedDataWatcher.java | 84 +++++++++++++++++-- .../wrappers/WrappedWatchableObject.java | 43 +++++++++- 3 files changed, 128 insertions(+), 9 deletions(-) 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 1a62fcae..af3d5e3d 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java @@ -78,6 +78,16 @@ public class MinecraftReflection { } } + /** + * Used during debugging and testing. + * @param minecraftPackage - the current Minecraft package. + * @param craftBukkitPackage - the current CraftBukkit package. + */ + public static void setMinecraftPackage(String minecraftPackage, String craftBukkitPackage) { + MINECRAFT_FULL_PACKAGE = minecraftPackage; + CRAFTBUKKIT_PACKAGE = craftBukkitPackage; + } + /** * Retrieve the name of the root CraftBukkit package. * @return Full canonical name of the root CraftBukkit package. 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 accde9d8..306ac18e 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java @@ -6,6 +6,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -13,6 +14,8 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; +import javax.annotation.Nullable; + import org.bukkit.entity.Entity; import org.bukkit.inventory.ItemStack; @@ -21,14 +24,16 @@ import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FieldUtils; import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.utility.MinecraftReflection; +import com.google.common.base.Function; import com.google.common.base.Objects; +import com.google.common.collect.Iterators; /** * Wraps a DataWatcher that is used to transmit arbitrary key-value pairs with a given entity. * * @author Kristian */ -public class WrappedDataWatcher { +public class WrappedDataWatcher implements Iterable { /** * Used to assign integer IDs to given types. @@ -92,16 +97,31 @@ public class WrappedDataWatcher { } /** - * Create a new data watcher from a list of watchable objects. + * Create a new data watcher for a list of watchable objects. + *

+ * Note that the watchable objects are not cloned, and will be modified in place. Use "deepClone" if + * that is not desirable. + *

+ * The {@link #removeObject(int)} method will not modify the given list, however. + * * @param watchableObjects - list of watchable objects that will be copied. * @throws FieldAccessException Unable to read watchable objects. */ public WrappedDataWatcher(List watchableObjects) throws FieldAccessException { this(); + + Lock writeLock = getReadWriteLock().writeLock(); + Map map = getWatchableObjectMap(); - // Fill the underlying map - for (WrappedWatchableObject watched : watchableObjects) { - setObject(watched.getIndex(), watched.getValue()); + writeLock.lock(); + + try { + // Add the watchable objects by reference + for (WrappedWatchableObject watched : watchableObjects) { + map.put(watched.getIndex(), watched.handle); + } + } finally { + writeLock.unlock(); } } @@ -234,9 +254,10 @@ public class WrappedDataWatcher { * @throws FieldAccessException If reflection failed. */ public List getWatchableObjects() throws FieldAccessException { + Lock readLock = getReadWriteLock().readLock(); + readLock.lock(); + try { - getReadWriteLock().readLock().lock(); - List result = new ArrayList(); // Add each watchable object to the list @@ -250,7 +271,7 @@ public class WrappedDataWatcher { return result; } finally { - getReadWriteLock().readLock().unlock(); + readLock.unlock(); } } @@ -270,6 +291,20 @@ public class WrappedDataWatcher { } } + /** + * Clone the content of the current DataWatcher. + * @return A cloned data watcher. + */ + public WrappedDataWatcher deepClone() { + WrappedDataWatcher clone = new WrappedDataWatcher(); + + // Make a new copy instead + for (WrappedWatchableObject watchable : this) { + clone.setObject(watchable.getIndex(), watchable.getValue()); + } + return clone; + } + /** * Retrieve the number of watched objects. * @return Watched object count. @@ -286,6 +321,23 @@ public class WrappedDataWatcher { } } + /** + * Remove a given object from the underlying DataWatcher. + * @param index - index of the object to remove. + * @return The watchable object that was removed, or NULL If none could be found. + */ + public WrappedWatchableObject removeObject(int index) { + Lock writeLock = getReadWriteLock().writeLock(); + writeLock.lock(); + + try { + Object removed = getWatchableObjectMap().remove(index); + return removed != null ? new WrappedWatchableObject(removed) : null; + } finally { + writeLock.unlock(); + } + } + /** * Set a watched byte. * @param index - index of the watched byte. @@ -480,4 +532,20 @@ public class WrappedDataWatcher { // Use fallback method } } + + @Override + public Iterator iterator() { + // We'll wrap the iterator instead of creating a new list every time + return Iterators.transform(getWatchableObjectMap().values().iterator(), + new Function() { + + @Override + public WrappedWatchableObject apply(@Nullable Object item) { + if (item != null) + return new WrappedWatchableObject(item); + else + return null; + } + }); + } } 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 dac76e4d..33207cf1 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java @@ -9,6 +9,7 @@ import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.reflect.instances.DefaultInstances; import com.comphenix.protocol.utility.MinecraftReflection; +import com.google.common.base.Objects; /** * Represents a watchable object. @@ -26,6 +27,9 @@ public class WrappedWatchableObject { // Used to create new watchable objects private static Constructor watchableConstructor; + // The watchable object class type + private static Class watchableObjectClass; + protected Object handle; protected StructureModifier modifier; @@ -79,6 +83,12 @@ public class WrappedWatchableObject { initialize(); this.handle = handle; this.modifier = baseModifier.withTarget(handle); + + // Make sure the type is correct + if (!watchableObjectClass.isAssignableFrom(handle.getClass())) { + throw new ClassCastException("Cannot cast the class " + handle.getClass().getName() + + " to " + watchableObjectClass.getName()); + } } /** @@ -95,7 +105,8 @@ public class WrappedWatchableObject { private static void initialize() { if (!hasInitialized) { hasInitialized = true; - baseModifier = new StructureModifier(MinecraftReflection.getWatchableObjectClass(), null, false); + watchableObjectClass = MinecraftReflection.getWatchableObjectClass(); + baseModifier = new StructureModifier(watchableObjectClass, null, false); } } @@ -310,4 +321,34 @@ public class WrappedWatchableObject { return value; } } + + @Override + public boolean equals(Object obj) { + // Quick checks + if (obj == this) + return true; + if (obj == null) + return false; + + if (obj instanceof WrappedWatchableObject) { + WrappedWatchableObject other = (WrappedWatchableObject) obj; + + return Objects.equal(getIndex(), other.getIndex()) && + Objects.equal(getTypeID(), other.getTypeID()) && + Objects.equal(getValue(), other.getValue()); + } + + // No, this is not equivalent + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(getIndex(), getTypeID(), getValue()); + } + + @Override + public String toString() { + return String.format("[%s: %s (%s)]", getIndex(), getValue(), getType().getSimpleName()); + } } From 7ca3e916a575779a35b443b95bd04b403139cd43 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Sun, 9 Dec 2012 04:23:26 +0100 Subject: [PATCH 11/17] Add a warning message when ProtocolLib is started on untested version. May decide to simply stop ProtocolLib unless this feature is specifically disabled. --- .../comphenix/protocol/MinecraftVersion.java | 153 ++++++++++++++++++ .../comphenix/protocol/ProtocolConfig.java | 22 +++ .../comphenix/protocol/ProtocolLibrary.java | 47 ++++-- .../protocol/utility/MinecraftReflection.java | 10 +- .../protocol/MinecraftVersionTest.java | 27 ++++ 5 files changed, 239 insertions(+), 20 deletions(-) create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/MinecraftVersion.java create mode 100644 ProtocolLib/src/test/java/com/comphenix/protocol/MinecraftVersionTest.java diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/MinecraftVersion.java b/ProtocolLib/src/main/java/com/comphenix/protocol/MinecraftVersion.java new file mode 100644 index 00000000..78ff9e0c --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/MinecraftVersion.java @@ -0,0 +1,153 @@ +package com.comphenix.protocol; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.bukkit.Server; + +import com.google.common.base.Objects; +import com.google.common.collect.ComparisonChain; + +/** + * Determine the current Minecraft version. + * + * @author Kristian + */ +class MinecraftVersion implements Comparable { + /** + * Regular expression used to parse version strings. + */ + private static final String VERSION_PATTERN = ".*\\(MC:\\s*((?:\\d+\\.)*\\d)\\s*\\)"; + + private final int major; + private final int minor; + private final int build; + + /** + * Determine the current Minecraft version. + * @param server - the Bukkit server that will be used to examine the MC version. + */ + public MinecraftVersion(Server server) { + this(extractVersion(server.getVersion())); + } + + /** + * Construct a version object from the format major.minor.build. + * @param versionOnly - the version in text form. + */ + public MinecraftVersion(String versionOnly) { + int[] numbers = parseVersion(versionOnly); + + this.major = numbers[0]; + this.minor = numbers[1]; + this.build = numbers[2]; + } + + /** + * Construct a version object directly. + * @param major - major version number. + * @param minor - minor version number. + * @param build - build version number. + */ + public MinecraftVersion(int major, int minor, int build) { + this.major = major; + this.minor = minor; + this.build = build; + } + + private int[] parseVersion(String version) { + String[] elements = version.split("\\."); + int[] numbers = new int[3]; + + // Make sure it's even a valid version + if (elements.length < 1) + throw new IllegalStateException("Corrupt MC version: " + version); + + // The String 1 or 1.2 is interpreted as 1.0.0 and 1.2.0 respectively. + for (int i = 0; i < Math.min(numbers.length, elements.length); i++) + numbers[i] = Integer.parseInt(elements[i].trim()); + return numbers; + } + + /** + * Major version number + * @return Current major version number. + */ + public int getMajor() { + return major; + } + + /** + * Minor version number + * @return Current minor version number. + */ + public int getMinor() { + return minor; + } + + /** + * Build version number + * @return Current build version number. + */ + public int getBuild() { + return build; + } + + @Override + public int compareTo(MinecraftVersion o) { + if (o == null) + return 1; + + return ComparisonChain.start(). + compare(major, o.major). + compare(minor, o.minor). + compare(build, o.build). + result(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) + return false; + if (obj == this) + return true; + + if (obj instanceof MinecraftVersion) { + MinecraftVersion other = (MinecraftVersion) obj; + + return major == other.major && + minor == other.minor && + build == other.build; + } + + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(major, minor, build); + } + + @Override + public String toString() { + // Convert to a String that we can parse back again + return String.format("(MC: %s.%s.%s)", major, minor, build); + } + + /** + * Extract the Minecraft version from CraftBukkit itself. + * @param server - the server object representing CraftBukkit. + * @return The underlying MC version. + * @throws IllegalStateException If we could not parse the version string. + */ + public static String extractVersion(String text) { + Pattern versionPattern = Pattern.compile(VERSION_PATTERN); + Matcher version = versionPattern.matcher(text); + + if (version.matches() && version.group(1) != null) { + return version.group(1); + } else { + throw new IllegalStateException("Cannot parse version String '" + text + "'"); + } + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolConfig.java b/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolConfig.java index 7e771e0b..f135fa97 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolConfig.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolConfig.java @@ -18,6 +18,8 @@ class ProtocolConfig { private static final String METRICS_ENABLED = "metrics"; + private static final String IGNORE_VERSION_CHECK = "ignore version check"; + private static final String BACKGROUND_COMPILER_ENABLED = "background compiler"; private static final String UPDATER_NOTIFY = "notify"; @@ -148,6 +150,26 @@ class ProtocolConfig { public long getAutoLastTime() { return updater.getLong(UPDATER_LAST_TIME, 0); } + + /** + * The version of Minecraft to ignore the built-in safety feature. + * @return The version to ignore ProtocolLib's satefy. + */ + public String getIgnoreVersionCheck() { + return global.getString(IGNORE_VERSION_CHECK, ""); + } + + /** + * Sets under which version of Minecraft the version safety feature will be ignored. + *

+ * This is useful if a server operator has tested and verified that a version of ProtocolLib works, + * but doesn't want or can't upgrade to a newer version. + * + * @param ignoreVersion - the version of Minecraft where the satefy will be disabled. + */ + public void setIgnoreVersionCheck(String ignoreVersion) { + global.set(IGNORE_VERSION_CHECK, ignoreVersion); + } /** * Retrieve whether or not metrics is enabled. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java b/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java index e3f8e09d..e9c887c4 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java @@ -43,6 +43,15 @@ import com.comphenix.protocol.reflect.compiler.BackgroundCompiler; * @author Kristian */ public class ProtocolLibrary extends JavaPlugin { + /** + * The minimum version ProtocolLib has been tested with. + */ + private static final String MINIMUM_MINECRAFT_VERSION = "1.0.0"; + + /** + * The maximum version ProtocolLib has been tested with, + */ + private static final String MAXIMUM_MINECRAFT_VERSION = "1.4.5"; /** * The number of milliseconds per second. @@ -188,13 +197,13 @@ public class ProtocolLibrary extends JavaPlugin { logger.info("Structure compiler thread has been disabled."); } + // Handle unexpected Minecraft versions + verifyMinecraftVersion(); + // Set up command handlers registerCommand(CommandProtocol.NAME, commandProtocol); registerCommand(CommandPacket.NAME, commandPacket); - // Notify server managers of incompatible plugins - checkForIncompatibility(manager); - // Player login and logout events protocolManager.registerEvents(manager, this); @@ -220,6 +229,26 @@ public class ProtocolLibrary extends JavaPlugin { } } + // Used to check Minecraft version + private void verifyMinecraftVersion() { + try { + MinecraftVersion minimum = new MinecraftVersion(MINIMUM_MINECRAFT_VERSION); + MinecraftVersion maximum = new MinecraftVersion(MAXIMUM_MINECRAFT_VERSION); + MinecraftVersion current = new MinecraftVersion(getServer()); + + // Skip certain versions + if (!config.getIgnoreVersionCheck().equals(current.toString())) { + // We'll just warn the user for now + if (current.compareTo(minimum) < 0) + reporter.reportWarning(this, "Version " + current + " is lower than the minimum " + minimum); + if (current.compareTo(maximum) > 0) + reporter.reportWarning(this, "Version " + current + " has not yet been tested! Proceed with caution."); + } + } catch (Exception e) { + reporter.reportWarning(this, "Unable to retrieve current Minecraft version.", e); + } + } + private void registerCommand(String name, CommandExecutor executor) { try { if (executor == null) @@ -296,18 +325,6 @@ public class ProtocolLibrary extends JavaPlugin { } } - private void checkForIncompatibility(PluginManager manager) { - // Plugin authors: Notify me to remove these - String[] incompatiblePlugins = {}; - - for (String plugin : incompatiblePlugins) { - if (manager.getPlugin(plugin) != null) { - // Check for versions, ect. - reporter.reportWarning(this, "Detected incompatible plugin: " + plugin); - } - } - } - @Override public void onDisable() { // Disable compiler 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 af3d5e3d..ded29674 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java @@ -26,8 +26,8 @@ public class MinecraftReflection { /** * The package name of all the classes that belongs to the native code in Minecraft. */ - private static String MINECRAFT_SERVER_PACKAGE = "net.minecraft.server"; - + private static String MINECRAFT_PREFIX_PACKAGE = "net.minecraft.server"; + private static String MINECRAFT_FULL_PACKAGE = null; private static String CRAFTBUKKIT_PACKAGE = null; @@ -74,7 +74,7 @@ public class MinecraftReflection { } } else { - throw new IllegalStateException("Cannot find Bukkit. Is it running?"); + throw new IllegalStateException("Could not find Bukkit. Is it running?"); } } @@ -117,7 +117,7 @@ public class MinecraftReflection { throw new IllegalArgumentException("Cannot determine the type of a null object."); // Doesn't matter if we don't check for the version here - return obj.getClass().getName().startsWith(MINECRAFT_SERVER_PACKAGE); + return obj.getClass().getName().startsWith(MINECRAFT_PREFIX_PACKAGE); } /** @@ -131,7 +131,7 @@ public class MinecraftReflection { throw new IllegalArgumentException("Cannot determine the type of a null object."); String javaName = obj.getClass().getName(); - return javaName.startsWith(MINECRAFT_SERVER_PACKAGE) && javaName.endsWith(className); + return javaName.startsWith(MINECRAFT_PREFIX_PACKAGE) && javaName.endsWith(className); } /** diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/MinecraftVersionTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/MinecraftVersionTest.java new file mode 100644 index 00000000..b69c29fd --- /dev/null +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/MinecraftVersionTest.java @@ -0,0 +1,27 @@ +package com.comphenix.protocol; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class MinecraftVersionTest { + + @Test + public void testComparision() { + MinecraftVersion within = new MinecraftVersion(1, 2, 5); + MinecraftVersion outside = new MinecraftVersion(1, 7, 0); + + MinecraftVersion lower = new MinecraftVersion(1, 0, 0); + MinecraftVersion highest = new MinecraftVersion(1, 4, 5); + + // Make sure this is valid + assertTrue(lower.compareTo(within) < 0 && within.compareTo(highest) < 0); + assertFalse(outside.compareTo(within) < 0 && outside.compareTo(highest) < 0); + } + + public void testParsing() { + assertEquals(MinecraftVersion.extractVersion("CraftBukkit R3.0 (MC: 1.4.3)"), "1.4.3"); + assertEquals(MinecraftVersion.extractVersion("CraftBukkit Test Beta 1 (MC: 1.10.01 )"), "1.10.01"); + assertEquals(MinecraftVersion.extractVersion("Hello (MC: 2.3.4 ) "), "2.3.4"); + } +} From 83f5a7e5b7f3dd4a8ca30dc7d67315778545f59c Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Sun, 9 Dec 2012 04:51:11 +0100 Subject: [PATCH 12/17] Add packet information to the default packet error reporter. --- .../protocol/error/DetailedErrorReporter.java | 15 +++++++++++ .../protocol/error/ErrorReporter.java | 9 +++++++ .../protocol/injector/EntityUtilities.java | 25 +++++++++++-------- .../injector/SortedPacketListenerList.java | 6 +++-- 4 files changed, 42 insertions(+), 13 deletions(-) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/error/DetailedErrorReporter.java b/ProtocolLib/src/main/java/com/comphenix/protocol/error/DetailedErrorReporter.java index 2257cbca..9b6ef329 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/error/DetailedErrorReporter.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/error/DetailedErrorReporter.java @@ -95,6 +95,21 @@ public class DetailedErrorReporter implements ErrorReporter { this.logger = logger; } + @Override + public void reportMinimal(Plugin sender, String methodName, Throwable error, Object... parameters) { + reportMinimal(sender, methodName, error); + + // Print parameters, if they are given + if (parameters != null && parameters.length > 0) { + logger.log(Level.SEVERE, " Parameters:"); + + // Print each parameter + for (Object parameter : parameters) { + logger.log(Level.SEVERE, " " + getStringDescription(parameter)); + } + } + } + @Override public void reportMinimal(Plugin sender, String methodName, Throwable error) { logger.log(Level.SEVERE, "[" + PLUGIN_NAME + "] Unhandled exception occured in " + methodName + " for " + diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/error/ErrorReporter.java b/ProtocolLib/src/main/java/com/comphenix/protocol/error/ErrorReporter.java index de6e1e36..54b45f48 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/error/ErrorReporter.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/error/ErrorReporter.java @@ -12,6 +12,15 @@ public interface ErrorReporter { */ public abstract void reportMinimal(Plugin sender, String methodName, Throwable error); + /** + * Prints a small minimal error report about an exception from another plugin. + * @param sender - the other plugin. + * @param methodName - name of the caller method. + * @param error - the exception itself. + * @param parameters - any relevant parameters to print. + */ + public abstract void reportMinimal(Plugin sender, String methodName, Throwable error, Object... parameters); + /** * Prints a warning message from the current plugin. * @param sender - the object containing the caller method. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java index 9927230a..2362ed55 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java @@ -52,7 +52,7 @@ class EntityUtilities { private static Method hashGetMethod; private static Method scanPlayersMethod; - + /* * While this function may look pretty bad, it's essentially just a reflection-warped * version of the following: @@ -246,20 +246,23 @@ class EntityUtilities { public static Entity getEntityFromID(World world, int entityID) throws FieldAccessException { try { Object trackerEntry = getEntityTrackerEntry(world, entityID); - - if (trackerField == null) - trackerField = trackerEntry.getClass().getField("tracker"); - Object tracker = FieldUtils.readField(trackerField, trackerEntry, true); - + Object tracker = null; + // Handle NULL cases - if (trackerEntry != null && tracker != null) { - return (Entity) MinecraftReflection.getBukkitEntity(tracker); - } else { - return null; + if (trackerEntry != null) { + if (trackerField == null) + trackerField = trackerEntry.getClass().getField("tracker"); + tracker = FieldUtils.readField(trackerField, trackerEntry, true); } + // If the tracker is NULL, we'll just assume this entity doesn't exist + if (tracker != null) + return (Entity) MinecraftReflection.getBukkitEntity(tracker); + else + return null; + } catch (Exception e) { - throw new FieldAccessException("Cannot find entity from ID.", e); + throw new FieldAccessException("Cannot find entity from ID " + entityID + ".", e); } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/SortedPacketListenerList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/SortedPacketListenerList.java index d3184b10..9c895e28 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/SortedPacketListenerList.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/SortedPacketListenerList.java @@ -48,7 +48,8 @@ public final class SortedPacketListenerList extends AbstractConcurrentListenerMu element.getListener().onPacketReceiving(event); } catch (Throwable e) { // Minecraft doesn't want your Exception. - reporter.reportMinimal(element.getListener().getPlugin(), "onPacketReceiving()", e); + reporter.reportMinimal(element.getListener().getPlugin(), "onPacketReceiving(PacketEvent)", e, + event.getPacket().getHandle()); } } } @@ -69,7 +70,8 @@ public final class SortedPacketListenerList extends AbstractConcurrentListenerMu element.getListener().onPacketSending(event); } catch (Throwable e) { // Minecraft doesn't want your Exception. - reporter.reportMinimal(element.getListener().getPlugin(), "onPacketSending()", e); + reporter.reportMinimal(element.getListener().getPlugin(), "onPacketSending(PacketEvent)", e, + event.getPacket().getHandle()); } } } From 7845a844d475e5be2be1f589710b250bb3df1245 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Sun, 9 Dec 2012 15:35:01 +0100 Subject: [PATCH 13/17] Make ProtocolLib load on startup. No need to wait for the world to load --- .../src/main/java/com/comphenix/protocol/ProtocolLibrary.java | 4 ++-- ProtocolLib/src/main/resources/plugin.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java b/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java index e9c887c4..2d753368 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java @@ -240,9 +240,9 @@ public class ProtocolLibrary extends JavaPlugin { if (!config.getIgnoreVersionCheck().equals(current.toString())) { // We'll just warn the user for now if (current.compareTo(minimum) < 0) - reporter.reportWarning(this, "Version " + current + " is lower than the minimum " + minimum); + logger.warning("Version " + current + " is lower than the minimum " + minimum); if (current.compareTo(maximum) > 0) - reporter.reportWarning(this, "Version " + current + " has not yet been tested! Proceed with caution."); + logger.warning("Version " + current + " has not yet been tested! Proceed with caution."); } } catch (Exception e) { reporter.reportWarning(this, "Unable to retrieve current Minecraft version.", e); diff --git a/ProtocolLib/src/main/resources/plugin.yml b/ProtocolLib/src/main/resources/plugin.yml index f56352fe..84bff7e3 100644 --- a/ProtocolLib/src/main/resources/plugin.yml +++ b/ProtocolLib/src/main/resources/plugin.yml @@ -3,7 +3,7 @@ version: 1.8.0-SNAPSHOT description: Provides read/write access to the Minecraft protocol. author: Comphenix website: http://www.comphenix.net/ProtocolLib - +load: startup main: com.comphenix.protocol.ProtocolLibrary database: false From b135bf86c308a9287dca73b14e036dc4a9bedc9c Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Sun, 9 Dec 2012 17:09:24 +0100 Subject: [PATCH 14/17] Add the ability to skip the version check for a specific MC version. --- .../java/com/comphenix/protocol/MinecraftVersion.java | 10 +++++++++- .../java/com/comphenix/protocol/ProtocolLibrary.java | 4 ++-- ProtocolLib/src/main/resources/config.yml | 5 ++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/MinecraftVersion.java b/ProtocolLib/src/main/java/com/comphenix/protocol/MinecraftVersion.java index 78ff9e0c..6544ac11 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/MinecraftVersion.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/MinecraftVersion.java @@ -93,6 +93,14 @@ class MinecraftVersion implements Comparable { return build; } + /** + * Retrieve the version String (major.minor.build) only. + * @return A normal version string. + */ + public String getVersion() { + return String.format("%s.%s.%s", major, minor, build); + } + @Override public int compareTo(MinecraftVersion o) { if (o == null) @@ -131,7 +139,7 @@ class MinecraftVersion implements Comparable { @Override public String toString() { // Convert to a String that we can parse back again - return String.format("(MC: %s.%s.%s)", major, minor, build); + return String.format("(MC: %s)", getVersion()); } /** diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java b/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java index 2d753368..f0563828 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java @@ -235,9 +235,9 @@ public class ProtocolLibrary extends JavaPlugin { MinecraftVersion minimum = new MinecraftVersion(MINIMUM_MINECRAFT_VERSION); MinecraftVersion maximum = new MinecraftVersion(MAXIMUM_MINECRAFT_VERSION); MinecraftVersion current = new MinecraftVersion(getServer()); - + // Skip certain versions - if (!config.getIgnoreVersionCheck().equals(current.toString())) { + if (!config.getIgnoreVersionCheck().equals(current.getVersion())) { // We'll just warn the user for now if (current.compareTo(minimum) < 0) logger.warning("Version " + current + " is lower than the minimum " + minimum); diff --git a/ProtocolLib/src/main/resources/config.yml b/ProtocolLib/src/main/resources/config.yml index 15828ee1..0b5a4f64 100644 --- a/ProtocolLib/src/main/resources/config.yml +++ b/ProtocolLib/src/main/resources/config.yml @@ -12,4 +12,7 @@ global: metrics: true # Automatically compile structure modifiers - background compiler: true \ No newline at end of file + background compiler: true + + # Disable version checking for the given Minecraft version. Backup your world first! + ignore version check: \ No newline at end of file From f6f8c9f80f8b853dafdf26cf63d38e598778bb91 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Mon, 10 Dec 2012 05:50:18 +0100 Subject: [PATCH 15/17] Only broadcast a notifcation message in the event of a warning. --- .../java/com/comphenix/protocol/ProtocolLibrary.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java b/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java index f0563828..2f5b4774 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java @@ -19,6 +19,7 @@ package com.comphenix.protocol; import java.io.IOException; import java.util.logging.Handler; +import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; @@ -129,7 +130,7 @@ public class ProtocolLibrary extends JavaPlugin { commandPacket = new CommandPacket(detailedReporter, this, logger, protocolManager); // Send logging information to player listeners too - broadcastUsers(PERMISSION_INFO); + setupBroadcastUsers(PERMISSION_INFO); } catch (Throwable e) { detailedReporter.reportDetailed(this, "Cannot load ProtocolLib.", e, protocolManager); @@ -151,7 +152,7 @@ public class ProtocolLibrary extends JavaPlugin { } } - private void broadcastUsers(final String permission) { + private void setupBroadcastUsers(final String permission) { // Guard against multiple calls if (redirectHandler != null) return; @@ -160,7 +161,10 @@ public class ProtocolLibrary extends JavaPlugin { redirectHandler = new Handler() { @Override public void publish(LogRecord record) { - commandPacket.broadcastMessageSilently(record.getMessage(), permission); + // Only display warnings and above + if (record.getLevel().intValue() >= Level.WARNING.intValue()) { + commandPacket.broadcastMessageSilently(record.getMessage(), permission); + } } @Override From 40345f25ff2186a93826e051ad6733b35b831444 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Mon, 10 Dec 2012 16:10:56 +0100 Subject: [PATCH 16/17] Bumping to version 1.8.0 --- ProtocolLib/pom.xml | 2 +- ProtocolLib/src/main/resources/plugin.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ProtocolLib/pom.xml b/ProtocolLib/pom.xml index f01c02d6..421b5e48 100644 --- a/ProtocolLib/pom.xml +++ b/ProtocolLib/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.comphenix.protocol ProtocolLib - 1.8.0-SNAPSHOT + 1.8.0 jar Provides read/write access to the Minecraft protocol. diff --git a/ProtocolLib/src/main/resources/plugin.yml b/ProtocolLib/src/main/resources/plugin.yml index 84bff7e3..cd537899 100644 --- a/ProtocolLib/src/main/resources/plugin.yml +++ b/ProtocolLib/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: ProtocolLib -version: 1.8.0-SNAPSHOT +version: 1.8.0 description: Provides read/write access to the Minecraft protocol. author: Comphenix website: http://www.comphenix.net/ProtocolLib From afdb0d03247f2c43e1a137c21ef606caf2abd3ef Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Mon, 10 Dec 2012 16:30:48 +0100 Subject: [PATCH 17/17] Remember to perform deep cloning. --- ProtocolLib/dependency-reduced-pom.xml | 2 +- .../com/comphenix/protocol/wrappers/WrappedDataWatcher.java | 2 +- .../com/comphenix/protocol/wrappers/WrappedWatchableObject.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ProtocolLib/dependency-reduced-pom.xml b/ProtocolLib/dependency-reduced-pom.xml index 4830e8fd..00d4c784 100644 --- a/ProtocolLib/dependency-reduced-pom.xml +++ b/ProtocolLib/dependency-reduced-pom.xml @@ -4,7 +4,7 @@ com.comphenix.protocol ProtocolLib ProtocolLib - 1.8.0-SNAPSHOT + 1.8.0 Provides read/write access to the Minecraft protocol. http://dev.bukkit.org/server-mods/protocollib/ 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 306ac18e..90eeb283 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java @@ -300,7 +300,7 @@ public class WrappedDataWatcher implements Iterable { // Make a new copy instead for (WrappedWatchableObject watchable : this) { - clone.setObject(watchable.getIndex(), watchable.getValue()); + clone.setObject(watchable.getIndex(), watchable.getClonedValue()); } return clone; } 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 33207cf1..c3af7039 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java @@ -307,7 +307,7 @@ public class WrappedWatchableObject { } // Helper - private Object getClonedValue() throws FieldAccessException { + Object getClonedValue() throws FieldAccessException { Object value = getValue(); // Only a limited set of references types are supported