Another diffcult to track down bug.
Dieser Commit ist enthalten in:
Ursprung
1c41f83305
Commit
6063f437fd
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
In neuem Issue referenzieren
Einen Benutzer sperren