diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/packet/ProxyPacketInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/packet/ProxyPacketInjector.java index a6744fb0..800f264e 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/packet/ProxyPacketInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/packet/ProxyPacketInjector.java @@ -17,6 +17,7 @@ package com.comphenix.protocol.injector.packet; +import java.io.DataInput; import java.io.DataInputStream; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -33,6 +34,8 @@ import net.sf.cglib.proxy.NoOp; import com.comphenix.protocol.Packets; import com.comphenix.protocol.error.ErrorReporter; +import com.comphenix.protocol.error.Report; +import com.comphenix.protocol.error.ReportType; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.injector.ListenerInvoker; @@ -50,6 +53,8 @@ import com.comphenix.protocol.utility.MinecraftReflection; * @author Kristian */ class ProxyPacketInjector implements PacketInjector { + public static final ReportType REPORT_CANNOT_FIND_READ_PACKET_METHOD = new ReportType("Cannot find read packet method for ID %s."); + /** * Represents a way to update the packet ID to class lookup table. * @author Kristian @@ -134,7 +139,7 @@ class ProxyPacketInjector implements PacketInjector { */ private static FuzzyMethodContract readPacket = FuzzyMethodContract.newBuilder(). returnTypeVoid(). - parameterExactType(DataInputStream.class). + parameterDerivedOf(DataInput.class). parameterCount(1). build(); @@ -154,6 +159,9 @@ class ProxyPacketInjector implements PacketInjector { // Share callback filter private CallbackFilter filter; + + // Determine if the read packet method was found + private boolean readPacketIntercepted = false; public ProxyPacketInjector(ClassLoader classLoader, ListenerInvoker manager, PlayerInjectionHandler playerInjection, ErrorReporter reporter) throws FieldAccessException { @@ -224,16 +232,20 @@ class ProxyPacketInjector implements PacketInjector { } if (filter == null) { + readPacketIntercepted = false; + filter = new CallbackFilter() { @Override public int accept(Method method) { // Skip methods defined in Object - if (method.getDeclaringClass().equals(Object.class)) + if (method.getDeclaringClass().equals(Object.class)) { return 0; - else if (readPacket.isMatch(MethodInfo.fromMethod(method), null)) + } else if (readPacket.isMatch(MethodInfo.fromMethod(method), null)) { + readPacketIntercepted = true; return 1; - else + } else { return 2; + } } }; } @@ -252,6 +264,12 @@ class ProxyPacketInjector implements PacketInjector { // Add a static reference Enhancer.registerStaticCallbacks(proxy, new Callback[] { NoOp.INSTANCE, modifierReadPacket, modifierRest }); + // Check that we found the read method + if (!readPacketIntercepted) { + reporter.reportWarning(this, + Report.newBuilder(REPORT_CANNOT_FIND_READ_PACKET_METHOD).messageParam(packetID)); + } + // Override values previous.put(packetID, old); registry.put(proxy, packetID); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/packet/ReadPacketModifier.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/packet/ReadPacketModifier.java index d88d8282..0c8ace3f 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/packet/ReadPacketModifier.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/packet/ReadPacketModifier.java @@ -1,140 +1,140 @@ -/* - * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. - * Copyright (C) 2012 Kristian S. Stangeland - * - * This program is free software; you can redistribute it and/or modify it under the terms of the - * GNU General Public License as published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program; - * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307 USA - */ - -package com.comphenix.protocol.injector.packet; - -import java.io.DataInputStream; -import java.lang.reflect.Method; -import java.util.Map; - -import com.comphenix.protocol.error.ErrorReporter; -import com.comphenix.protocol.error.Report; -import com.comphenix.protocol.error.ReportType; -import com.comphenix.protocol.events.PacketContainer; -import com.comphenix.protocol.events.PacketEvent; -import com.google.common.collect.MapMaker; - -import net.sf.cglib.proxy.MethodInterceptor; -import net.sf.cglib.proxy.MethodProxy; - -class ReadPacketModifier implements MethodInterceptor { - public static final ReportType REPORT_CANNOT_HANDLE_CLIENT_PACKET = new ReportType("Cannot handle client packet."); - - // A cancel marker - private static final Object CANCEL_MARKER = new Object(); - - // Common for all packets of the same type - private ProxyPacketInjector packetInjector; - private int packetID; - - // Report errors - private ErrorReporter reporter; - - // If this is a read packet data method - private boolean isReadPacketDataMethod; - - // Whether or not a packet has been cancelled - private static Map override = new MapMaker().weakKeys().makeMap(); - - public ReadPacketModifier(int packetID, ProxyPacketInjector packetInjector, ErrorReporter reporter, boolean isReadPacketDataMethod) { - this.packetID = packetID; - this.packetInjector = packetInjector; - this.reporter = reporter; - this.isReadPacketDataMethod = isReadPacketDataMethod; - } - - /** - * Remove any packet overrides. - * @param packet - the packet to rever - */ - public static void removeOverride(Object packet) { - override.remove(packet); - } - - /** - * Retrieve the packet that overrides the methods of the given packet. - * @param packet - the given packet. - * @return Overriden object. - */ - public static Object getOverride(Object packet) { - return override.get(packet); - } - - /** - * Determine if the given packet has been cancelled before. - * @param packet - the packet to check. - * @return TRUE if it has been cancelled, FALSE otherwise. - */ - public static boolean hasCancelled(Object packet) { - return getOverride(packet) == CANCEL_MARKER; - } - - @Override - public Object intercept(Object thisObj, Method method, Object[] args, MethodProxy proxy) throws Throwable { - // Atomic retrieval - Object overridenObject = override.get(thisObj); - Object returnValue = null; - - if (overridenObject != null) { - // This packet has been cancelled - if (overridenObject == CANCEL_MARKER) { - // So, cancel all void methods - if (method.getReturnType().equals(Void.TYPE)) - return null; - else // Revert to normal for everything else - overridenObject = thisObj; - } - - returnValue = proxy.invokeSuper(overridenObject, args); - } else { - returnValue = proxy.invokeSuper(thisObj, args); - } - - // Is this a readPacketData method? - if (isReadPacketDataMethod) { - try { - // We need this in order to get the correct player - DataInputStream input = (DataInputStream) args[0]; - - // Let the people know - PacketContainer container = new PacketContainer(packetID, thisObj); - PacketEvent event = packetInjector.packetRecieved(container, input); - - // Handle override - if (event != null) { - Object result = event.getPacket().getHandle(); - - if (event.isCancelled()) { - override.put(thisObj, CANCEL_MARKER); - } else if (!objectEquals(thisObj, result)) { - override.put(thisObj, result); - } - } - } catch (Throwable e) { - // Minecraft cannot handle this error - reporter.reportDetailed(this, - Report.newBuilder(REPORT_CANNOT_HANDLE_CLIENT_PACKET).callerParam(args[0]).error(e) - ); - } - } - return returnValue; - } - - private boolean objectEquals(Object a, Object b) { - return System.identityHashCode(a) != System.identityHashCode(b); - } -} +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +package com.comphenix.protocol.injector.packet; + +import java.io.DataInputStream; +import java.lang.reflect.Method; +import java.util.Map; + +import com.comphenix.protocol.error.ErrorReporter; +import com.comphenix.protocol.error.Report; +import com.comphenix.protocol.error.ReportType; +import com.comphenix.protocol.events.PacketContainer; +import com.comphenix.protocol.events.PacketEvent; +import com.google.common.collect.MapMaker; + +import net.sf.cglib.proxy.MethodInterceptor; +import net.sf.cglib.proxy.MethodProxy; + +class ReadPacketModifier implements MethodInterceptor { + public static final ReportType REPORT_CANNOT_HANDLE_CLIENT_PACKET = new ReportType("Cannot handle client packet."); + + // A cancel marker + private static final Object CANCEL_MARKER = new Object(); + + // Common for all packets of the same type + private ProxyPacketInjector packetInjector; + private int packetID; + + // Report errors + private ErrorReporter reporter; + + // If this is a read packet data method + private boolean isReadPacketDataMethod; + + // Whether or not a packet has been cancelled + private static Map override = new MapMaker().weakKeys().makeMap(); + + public ReadPacketModifier(int packetID, ProxyPacketInjector packetInjector, ErrorReporter reporter, boolean isReadPacketDataMethod) { + this.packetID = packetID; + this.packetInjector = packetInjector; + this.reporter = reporter; + this.isReadPacketDataMethod = isReadPacketDataMethod; + } + + /** + * Remove any packet overrides. + * @param packet - the packet to rever + */ + public static void removeOverride(Object packet) { + override.remove(packet); + } + + /** + * Retrieve the packet that overrides the methods of the given packet. + * @param packet - the given packet. + * @return Overriden object. + */ + public static Object getOverride(Object packet) { + return override.get(packet); + } + + /** + * Determine if the given packet has been cancelled before. + * @param packet - the packet to check. + * @return TRUE if it has been cancelled, FALSE otherwise. + */ + public static boolean hasCancelled(Object packet) { + return getOverride(packet) == CANCEL_MARKER; + } + + @Override + public Object intercept(Object thisObj, Method method, Object[] args, MethodProxy proxy) throws Throwable { + // Atomic retrieval + Object overridenObject = override.get(thisObj); + Object returnValue = null; + + if (overridenObject != null) { + // This packet has been cancelled + if (overridenObject == CANCEL_MARKER) { + // So, cancel all void methods + if (method.getReturnType().equals(Void.TYPE)) + return null; + else // Revert to normal for everything else + overridenObject = thisObj; + } + + returnValue = proxy.invokeSuper(overridenObject, args); + } else { + returnValue = proxy.invokeSuper(thisObj, args); + } + + // Is this a readPacketData method? + if (isReadPacketDataMethod) { + try { + // We need this in order to get the correct player + DataInputStream input = (DataInputStream) args[0]; + + // Let the people know + PacketContainer container = new PacketContainer(packetID, thisObj); + PacketEvent event = packetInjector.packetRecieved(container, input); + + // Handle override + if (event != null) { + Object result = event.getPacket().getHandle(); + + if (event.isCancelled()) { + override.put(thisObj, CANCEL_MARKER); + } else if (!objectEquals(thisObj, result)) { + override.put(thisObj, result); + } + } + } catch (Throwable e) { + // Minecraft cannot handle this error + reporter.reportDetailed(this, + Report.newBuilder(REPORT_CANNOT_HANDLE_CLIENT_PACKET).callerParam(args[0]).error(e) + ); + } + } + return returnValue; + } + + private boolean objectEquals(Object a, Object b) { + return System.identityHashCode(a) != System.identityHashCode(b); + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/FuzzyMethodContract.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/FuzzyMethodContract.java index 2fe0da85..35b92d9a 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/FuzzyMethodContract.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/FuzzyMethodContract.java @@ -161,14 +161,26 @@ public class FuzzyMethodContract extends AbstractFuzzyMember { /** * Add a new required parameter whose type must be a superclass of the given type. *

- * If a parameter is of type Number, any derived class (Integer, Long, etc.) will match it. - * @param type - a type or derived type of the matching parameter. + * If a method parameter is of type Number, then any derived class here (Integer, Long, etc.) will match it. + * @param type - a type or less derived type of the matching parameter. * @return This builder, for chaining. */ public Builder parameterSuperOf(Class type) { member.paramMatchers.add(new ParameterClassMatcher(FuzzyMatchers.matchSuper(type))); return this; } + + /** + * Add a new required parameter whose type must be a derived class of the given class. + *

+ * If the method parameter has the type Integer, then the class Number here will match it. + * @param type - a type or more derived type of the matching parameter. + * @return This builder, for chaining. + */ + public Builder parameterDerivedOf(Class type) { + member.paramMatchers.add(new ParameterClassMatcher(FuzzyMatchers.matchDerived(type))); + return this; + } /** * Add a new required parameter whose type must match the given class matcher. @@ -204,6 +216,19 @@ public class FuzzyMethodContract extends AbstractFuzzyMember { return this; } + /** + * Add a new required parameter whose type must be a derived class of the given class. + *

+ * If the method parameter has the type Integer, then the class Number here will match it. + * @param type - a type or more derived type of the matching parameter. + * @param index - the expected position in the parameter list. + * @return This builder, for chaining. + */ + public Builder parameterDerivedOf(Class type, int index) { + member.paramMatchers.add(new ParameterClassMatcher(FuzzyMatchers.matchDerived(type), index)); + return this; + } + /** * Add a new required parameter whose type must match the given class matcher and index. * @param classMatcher - the class matcher.