Another diffcult to track down bug.

This commit is contained in:
Kristian S. Stangeland 2012-09-30 04:24:27 +02:00
parent 1c41f83305
commit 6063f437fd
4 changed files with 103 additions and 9 deletions

View File

@ -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<AsyncMarker> {
// 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<AsyncMarker> {
}
}
/**
* 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<Method> 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)

View File

@ -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);

View File

@ -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 {

View File

@ -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<Method> getMethodListByParameters(Class<?> returnType, Class<?>[] args) {
List<Method> methods = new ArrayList<Method>();
// 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.