diff --git a/ProtocolLib/src/com/comphenix/protocol/async/AsyncMarker.java b/ProtocolLib/src/com/comphenix/protocol/async/AsyncMarker.java index 51ac432d..be543e9d 100644 --- a/ProtocolLib/src/com/comphenix/protocol/async/AsyncMarker.java +++ b/ProtocolLib/src/com/comphenix/protocol/async/AsyncMarker.java @@ -3,11 +3,17 @@ package com.comphenix.protocol.async; import java.io.IOException; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.Iterator; +import java.util.List; + +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.google.common.primitives.Longs; /** @@ -58,6 +64,10 @@ public class AsyncMarker implements Serializable, Comparable { // Whether or not the asynchronous processing itself should be cancelled private volatile boolean asyncCancelled; + + // Determine if Minecraft processes this packet asynchronously + private static Method isMinecraftAsync; + private static boolean alwaysSync; /** * Create a container for asyncronous packets. @@ -240,6 +250,49 @@ public class AsyncMarker implements Serializable, Comparable { } } + /** + * Determine if Minecraft allows asynchronous processing of this packet. + * @return TRUE if it does, FALSE otherwise. + */ + public boolean isMinecraftAsync(PacketEvent event) throws FieldAccessException { + + if (isMinecraftAsync == null) { + try { + isMinecraftAsync = FuzzyReflection.fromClass(Packet.class).getMethodByName("a_.*"); + } catch (RuntimeException e) { + // This will occur in 1.2.5 (or possibly in later versions) + List methods = FuzzyReflection.fromClass(Packet.class). + getMethodListByParameters(boolean.class, new Class[] {}); + + // Try to look for boolean methods + if (methods.size() == 2) { + isMinecraftAsync = methods.get(1); + } else if (methods.size() == 1) { + // We're in 1.2.5 + alwaysSync = true; + } else { + System.err.println("Cannot determine asynchronous state of packets!"); + alwaysSync = true; + } + } + } + + if (alwaysSync) { + return false; + } else { + try { + // Wrap exceptions + return (Boolean) isMinecraftAsync.invoke(event.getPacket().getHandle()); + } catch (IllegalArgumentException e) { + throw new FieldAccessException("Illegal argument", e); + } catch (IllegalAccessException e) { + throw new FieldAccessException("Unable to reflect method call 'a_', or: isAsyncPacket.", e); + } catch (InvocationTargetException e) { + throw new FieldAccessException("Minecraft error", e); + } + } + } + @Override public int compareTo(AsyncMarker o) { if (o == null) diff --git a/ProtocolLib/src/com/comphenix/protocol/async/PacketSendingQueue.java b/ProtocolLib/src/com/comphenix/protocol/async/PacketSendingQueue.java index 4d25a667..500cf2c3 100644 --- a/ProtocolLib/src/com/comphenix/protocol/async/PacketSendingQueue.java +++ b/ProtocolLib/src/com/comphenix/protocol/async/PacketSendingQueue.java @@ -8,6 +8,7 @@ import java.util.Set; import java.util.concurrent.PriorityBlockingQueue; import com.comphenix.protocol.events.PacketEvent; +import com.comphenix.protocol.reflect.FieldAccessException; import com.google.common.collect.ComparisonChain; /** @@ -84,18 +85,30 @@ class PacketSendingQueue { * @param onMainThread - whether or not this is occuring on the main thread. */ public void trySendPackets(boolean onMainThread) { - - // Abort if we're not on the main thread - if (synchronizeMain && !onMainThread) - return; - + // Transmit as many packets as we can while (true) { PacketEvent current = sendingQueue.peek(); - + if (current != null) { AsyncMarker marker = current.getAsyncMarker(); + // Abort if we're not on the main thread + if (synchronizeMain) { + try { + boolean wantAsync = marker.isMinecraftAsync(current); + boolean wantSync = !wantAsync; + + // Quit if we haven't fulfilled our promise + if ((onMainThread && wantAsync) || (!onMainThread && wantSync)) + return; + + } catch (FieldAccessException e) { + e.printStackTrace(); + return; + } + } + if (marker.isProcessed() || marker.hasExpired()) { if (marker.isProcessed() && !current.isCancelled()) { sendPacket(current); diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/ReadPacketModifier.java b/ProtocolLib/src/com/comphenix/protocol/injector/ReadPacketModifier.java index ba489b96..d2e71c60 100644 --- a/ProtocolLib/src/com/comphenix/protocol/injector/ReadPacketModifier.java +++ b/ProtocolLib/src/com/comphenix/protocol/injector/ReadPacketModifier.java @@ -77,9 +77,14 @@ class ReadPacketModifier implements MethodInterceptor { if (override.containsKey(thisObj)) { Object overridenObject = override.get(thisObj); - // Cancel EVERYTHING, including "processPacket" - if (overridenObject == null) - return null; + // This packet has been cancelled + if (overridenObject == null) { + // 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 { diff --git a/ProtocolLib/src/com/comphenix/protocol/reflect/FuzzyReflection.java b/ProtocolLib/src/com/comphenix/protocol/reflect/FuzzyReflection.java index a8e3eb6b..ae30b604 100644 --- a/ProtocolLib/src/com/comphenix/protocol/reflect/FuzzyReflection.java +++ b/ProtocolLib/src/com/comphenix/protocol/reflect/FuzzyReflection.java @@ -19,8 +19,10 @@ package com.comphenix.protocol.reflect; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashSet; +import java.util.List; import java.util.Set; import java.util.regex.Pattern; @@ -96,6 +98,7 @@ public class FuzzyReflection { * Retrieves a method by looking at its name. * @param nameRegex - regular expression that will match method names. * @return The first method that satisfies the regular expression. + * @throws RuntimeException If the method cannot be found. */ public Method getMethodByName(String nameRegex) { @@ -151,6 +154,26 @@ public class FuzzyReflection { throw new RuntimeException("Unable to find " + name + " in " + source.getName()); } + /** + * Retrieves every method that has the given parameter types and return type. + * @param returnType - return type of the method to find. + * @param args - parameter types of the method to find. + * @return Every method that satisfies the given constraints. + */ + public List getMethodListByParameters(Class returnType, Class[] args) { + + List methods = new ArrayList(); + + // Find the correct method to call + for (Method method : getMethods()) { + if (method.getReturnType().equals(returnType) && Arrays.equals(method.getParameterTypes(), args)) { + methods.add(method); + } + } + + return methods; + } + /** * Retrieves a field by name. * @param nameRegex - regular expression that will match a field name.