Attempting to add support for MCPC.

Still have to track down a very elusive bug however.
This commit is contained in:
Kristian S. Stangeland 2013-01-31 06:23:01 +01:00
parent 8b12907dfb
commit e8c615b203
15 changed files with 729 additions and 130 deletions

View File

@ -19,6 +19,7 @@ package com.comphenix.protocol.async;
import java.util.Collection;
import java.util.Iterator;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.concurrent.Semaphore;
@ -67,10 +68,16 @@ class PacketProcessingQueue extends AbstractConcurrentListenerMultimap<AsyncList
public PacketProcessingQueue(PlayerSendingHandler sendingHandler, int initialSize, int maximumSize, int maximumConcurrency) {
super();
this.processingQueue = Synchronization.queue(MinMaxPriorityQueue.
expectedSize(initialSize).
maximumSize(maximumSize).
<PacketEventHolder>create(), null);
try {
this.processingQueue = Synchronization.queue(MinMaxPriorityQueue.
expectedSize(initialSize).
maximumSize(maximumSize).
<PacketEventHolder>create(), null);
} catch (IncompatibleClassChangeError e) {
// It's a Beta class after all
this.processingQueue = Synchronization.queue(
new PriorityQueue<PacketEventHolder>(), null);
}
this.maximumConcurrency = maximumConcurrency;
this.concurrentProcessing = new Semaphore(maximumConcurrency);

View File

@ -164,9 +164,9 @@ class EntityUtilities {
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
Object worldServer = unwrapper.unwrapItem(world);
// We have to rely on the class naming here.
if (entityTrackerField == null)
entityTrackerField = FuzzyReflection.fromObject(worldServer).getFieldByType(".*Tracker");
entityTrackerField = FuzzyReflection.fromObject(worldServer).
getFieldByType("tracker", MinecraftReflection.getEntityTrackerClass());
// Get the tracker
Object tracker = null;
@ -191,7 +191,7 @@ class EntityUtilities {
// The Minecraft field that's NOT filled in by the constructor
trackedEntitiesField = FuzzyReflection.fromObject(tracker, true).
getFieldByType(MinecraftReflection.getMinecraftObjectMatcher(), ignoredTypes);
getFieldByType(MinecraftReflection.getMinecraftObjectRegex(), ignoredTypes);
}
// Read the entity hashmap
@ -250,8 +250,16 @@ class EntityUtilities {
// Handle NULL cases
if (trackerEntry != null) {
if (trackerField == null)
trackerField = trackerEntry.getClass().getField("tracker");
if (trackerField == null) {
try {
trackerField = trackerEntry.getClass().getField("tracker");
} catch (NoSuchFieldException e) {
// Assume it's the first public entity field then
trackerField = FuzzyReflection.fromObject(trackerEntry).getFieldByType(
"tracker", MinecraftReflection.getEntityClass());
}
}
tracker = FieldUtils.readField(trackerField, trackerEntry, true);
}

View File

@ -93,7 +93,7 @@ class PacketInjector {
if (intHashMap == null) {
// We're looking for the first static field with a Minecraft-object. This should be a IntHashMap.
Field intHashMapField = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true).
getFieldByType(MinecraftReflection.getMinecraftObjectMatcher());
getFieldByType(MinecraftReflection.getMinecraftObjectRegex());
try {
intHashMap = FieldUtils.readField(intHashMapField, (Object) null, true);

View File

@ -107,12 +107,10 @@ class InjectedServerConnection {
}
private void injectListenerThread() {
try {
if (listenerThreadField == null)
listenerThreadField = FuzzyReflection.fromObject(minecraftServer).
getFieldByType(".*NetworkListenThread");
if (listenerThreadField == null)
listenerThreadField = FuzzyReflection.fromObject(minecraftServer).
getFieldByType("networkListenThread", MinecraftReflection.getNetworkListenThreadClass());
} catch (RuntimeException e) {
reporter.reportDetailed(this, "Cannot find listener thread in MinecraftServer.", e, minecraftServer);
return;

View File

@ -20,7 +20,8 @@ package com.comphenix.protocol.injector.player;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
@ -43,6 +44,7 @@ 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;
import com.google.common.collect.Maps;
/**
* Represents a player hook into the NetServerHandler class.
@ -91,10 +93,60 @@ public class NetworkServerInjector extends PlayerInjector {
// Get the send packet method!
if (hasInitialized) {
if (sendPacketMethod == null)
sendPacketMethod = FuzzyReflection.fromObject(serverHandler).getMethodByName("sendPacket.*");
if (sendPacketMethod == null) {
try {
sendPacketMethod = FuzzyReflection.fromObject(serverHandler).getMethodByName("sendPacket.*");
} catch (IllegalArgumentException e) {
Map<String, Method> netServer = getMethodList(
MinecraftReflection.getNetServerHandlerClass(), MinecraftReflection.getPacketClass());
Map<String, Method> netHandler = getMethodList(
MinecraftReflection.getNetHandlerClass(), MinecraftReflection.getPacketClass());
// Remove every method in net handler from net server
for (String methodName : netHandler.keySet()) {
netServer.remove(methodName);
}
// The remainder is the send packet method
if (netServer.size() == 1) {
Method[] methods = netServer.values().toArray(new Method[0]);
sendPacketMethod = methods[0];
} else {
throw new IllegalArgumentException("Unable to find the sendPacket method in NetServerHandler/PlayerConnection.");
}
}
}
}
}
/**
* Retrieve a method mapped list of every method with the given signature.
* @param source - class source.
* @param params - parameters.
* @return Method mapped list.
*/
private Map<String, Method> getMethodList(Class<?> source, Class<?>... params) {
return getMappedMethods(
FuzzyReflection.fromClass(source, true).
getMethodListByParameters(Void.TYPE, params)
);
}
/**
* Retrieve every method as a map over names.
* <p>
* Note that overloaded methods will only occur once in the resulting map.
* @param methods - every method.
* @return A map over every given method.
*/
private Map<String, Method> getMappedMethods(List<Method> methods) {
Map<String, Method> map = Maps.newHashMap();
for (Method method : methods) {
map.put(method.getName(), method);
}
return map;
}
@Override
public void sendServerPacket(Object packet, boolean filtered) throws InvocationTargetException {
@ -296,8 +348,22 @@ public class NetworkServerInjector extends PlayerInjector {
}
FieldUtils.writeField(disconnectField, handler, value);
} catch (IllegalArgumentException e) {
reporter.reportDetailed(this, "Unable to find disconnect field. Is ProtocolLib up to date?", e, handler);
} catch (IllegalArgumentException e) {
// Assume it's the first ...
if (disconnectField == null) {
disconnectField = FuzzyReflection.fromObject(handler).getFieldByType("disconnected", boolean.class);
reporter.reportWarning(this, "Unable to find 'disconnected' field. Assuming " + disconnectField);
// Try again
if (disconnectField != null) {
setDisconnect(handler, value);
return;
}
}
// This is really bad
reporter.reportDetailed(this, "Cannot find disconnected field. Is ProtocolLib up to date?", e);
} catch (IllegalAccessException e) {
reporter.reportWarning(this, "Unable to update disconnected field. Player quit event may be sent twice.");
}

View File

@ -167,8 +167,8 @@ abstract class PlayerInjector {
// Next, get the network manager
if (networkManagerField == null)
networkManagerField = FuzzyReflection.fromObject(serverHandler).
getFieldByType(".*" + MinecraftReflection.getNetworkManagerName());
networkManagerField = FuzzyReflection.fromObject(serverHandler).getFieldByType(
"networkManager", MinecraftReflection.getNetworkManagerClass());
initializeNetworkManager(networkManagerField, serverHandler);
}
}
@ -184,7 +184,7 @@ abstract class PlayerInjector {
if (netLoginNetworkField == null)
netLoginNetworkField = FuzzyReflection.fromObject(netLoginHandler).
getFieldByType(".*" + MinecraftReflection.getNetworkManagerName());
getFieldByType("networkManager", MinecraftReflection.getNetworkManagerClass());
initializeNetworkManager(netLoginNetworkField, netLoginHandler);
}
}
@ -290,7 +290,13 @@ abstract class PlayerInjector {
// Execute disconnect on it
if (handler != null) {
if (disconnect == null) {
disconnect = FuzzyReflection.fromObject(handler).getMethodByName("disconnect.*");
try {
disconnect = FuzzyReflection.fromObject(handler).getMethodByName("disconnect.*");
} catch (IllegalArgumentException e) {
// Just assume it's the first String method
disconnect = FuzzyReflection.fromObject(handler).getMethodByParameters("disconnect", String.class);
reporter.reportWarning(this, "Cannot find disconnect method by name. Assuming " + disconnect);
}
// Save the method for later
if (usingNetServer)
@ -380,7 +386,7 @@ abstract class PlayerInjector {
try {
// Well, that sucks. Try just Minecraft objects then.
netHandlerField = FuzzyReflection.fromClass(networkManager.getClass(), true).
getFieldByType(MinecraftReflection.getMinecraftObjectMatcher());
getFieldByType(MinecraftReflection.getMinecraftObjectRegex());
} catch (RuntimeException e2) {
throw new IllegalAccessException("Cannot locate net handler. " + e2.getMessage());

View File

@ -72,4 +72,64 @@ public abstract class AbstractFuzzyMatcher<T> implements Comparable<AbstractFuzz
// No match
return -1;
}
/**
* Create a fuzzy matcher that returns the opposite result of the current matcher.
* @return An inverted fuzzy matcher.
*/
public AbstractFuzzyMatcher<T> inverted() {
return new AbstractFuzzyMatcher<T>() {
@Override
public boolean isMatch(T value, Object parent) {
return !AbstractFuzzyMatcher.this.isMatch(value, parent);
}
@Override
protected int calculateRoundNumber() {
return -2;
}
};
}
/**
* Require that this and the given matcher be TRUE.
* @param other - the other fuzzy matcher.
* @return A combined fuzzy matcher.
*/
public AbstractFuzzyMatcher<T> and(final AbstractFuzzyMatcher<T> other) {
return new AbstractFuzzyMatcher<T>() {
@Override
public boolean isMatch(T value, Object parent) {
// They both have to be true
return AbstractFuzzyMatcher.this.isMatch(value, parent) &&
other.isMatch(value, parent);
}
@Override
protected int calculateRoundNumber() {
return combineRounds(AbstractFuzzyMatcher.this.getRoundNumber(), other.getRoundNumber());
}
};
}
/**
* Require that either this or the other given matcher be TRUE.
* @param other - the other fuzzy matcher.
* @return A combined fuzzy matcher.
*/
public AbstractFuzzyMatcher<T> or(final AbstractFuzzyMatcher<T> other) {
return new AbstractFuzzyMatcher<T>() {
@Override
public boolean isMatch(T value, Object parent) {
// Either can be true
return AbstractFuzzyMatcher.this.isMatch(value, parent) ||
other.isMatch(value, parent);
}
@Override
protected int calculateRoundNumber() {
return combineRounds(AbstractFuzzyMatcher.this.getRoundNumber(), other.getRoundNumber());
}
};
}
}

View File

@ -13,11 +13,11 @@ import javax.annotation.Nonnull;
*/
public abstract class AbstractFuzzyMember<T extends Member> extends AbstractFuzzyMatcher<T> {
// Accessibility matchers
private int modifiersRequired;
private int modifiersBanned;
protected int modifiersRequired;
protected int modifiersBanned;
private Pattern nameRegex;
private AbstractFuzzyMatcher<Class<?>> declaringMatcher = ExactClassMatcher.MATCH_ALL;
protected Pattern nameRegex;
protected AbstractFuzzyMatcher<Class<?>> declaringMatcher = ExactClassMatcher.MATCH_ALL;
/**
* Whether or not this contract can be modified.
@ -89,7 +89,7 @@ public abstract class AbstractFuzzyMember<T extends Member> extends AbstractFuzz
* @return This builder, for chaining.
*/
public Builder<T> declaringClassExactType(Class<?> declaringClass) {
member.declaringMatcher = ExactClassMatcher.matchExact(declaringClass);
member.declaringMatcher = FuzzyMatchers.matchExact(declaringClass);
return this;
}
@ -99,7 +99,7 @@ public abstract class AbstractFuzzyMember<T extends Member> extends AbstractFuzz
* @return This builder, for chaining.
*/
public Builder<T> declaringClassSuperOf(Class<?> declaringClass) {
member.declaringMatcher = ExactClassMatcher.matchSuper(declaringClass);
member.declaringMatcher = FuzzyMatchers.matchSuper(declaringClass);
return this;
}
@ -109,7 +109,7 @@ public abstract class AbstractFuzzyMember<T extends Member> extends AbstractFuzz
* @return This builder, for chaining.
*/
public Builder<T> declaringClassDerivedOf(Class<?> declaringClass) {
member.declaringMatcher = ExactClassMatcher.matchDerived(declaringClass);
member.declaringMatcher = FuzzyMatchers.matchDerived(declaringClass);
return this;
}

View File

@ -34,38 +34,12 @@ class ExactClassMatcher extends AbstractFuzzyMatcher<Class<?>> {
private final Class<?> matcher;
private final Options option;
/**
* Construct a class matcher that matches types exactly.
* @param matcher - the matching class.
*/
public static ExactClassMatcher matchExact(Class<?> matcher) {
return new ExactClassMatcher(matcher, Options.MATCH_EXACT);
}
/**
* Construct a class matcher that matches super types of the given class.
* @param matcher - the matching type must be a super class of this type.
* @return A new class mathcher.
*/
public static ExactClassMatcher matchSuper(Class<?> matcher) {
return new ExactClassMatcher(matcher, Options.MATCH_SUPER);
}
/**
* Construct a class matcher that matches derived types of the given class.
* @param matcher - the matching type must be a derived class of this type.
* @return A new class mathcher.
*/
public static ExactClassMatcher matchDerived(Class<?> matcher) {
return new ExactClassMatcher(matcher, Options.MATCH_DERIVED);
}
/**
* Constructs a new class matcher.
* @param matcher - the matching class, or NULL to represent anything.
* @param option - options specifying the matching rules.
*/
private ExactClassMatcher(Class<?> matcher, Options option) {
ExactClassMatcher(Class<?> matcher, Options option) {
this.matcher = matcher;
this.option = option;
}

View File

@ -1,10 +1,10 @@
package com.comphenix.protocol.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
@ -107,32 +107,6 @@ public class FuzzyClassContract extends AbstractFuzzyMatcher<Class<?>> {
return new Builder();
}
/**
* Match the parent class of a method, field or constructor.
* @return Parent matcher.
*/
public static AbstractFuzzyMatcher<Class<?>> matchParent() {
return new AbstractFuzzyMatcher<Class<?>>() {
@Override
public boolean isMatch(Class<?> value, Object parent) {
if (parent instanceof Member) {
return ((Member) parent).getDeclaringClass().equals(value);
} else if (parent instanceof Class) {
return parent.equals(value);
} else {
// Can't be a match
return false;
}
}
@Override
protected int calculateRoundNumber() {
// We match a very specific type
return -100;
}
};
}
/**
* Constructs a new fuzzy class contract with the given contracts.
* @param fieldContracts - field contracts.
@ -197,7 +171,7 @@ public class FuzzyClassContract extends AbstractFuzzyMatcher<Class<?>> {
@Override
public boolean isMatch(Class<?> value, Object parent) {
FuzzyReflection reflection = FuzzyReflection.fromClass(value);
FuzzyReflection reflection = FuzzyReflection.fromClass(value, true);
// Make sure all the contracts are valid
return processContracts(reflection.getFields(), value, fieldContracts) &&

View File

@ -13,6 +13,11 @@ import javax.annotation.Nonnull;
public class FuzzyFieldContract extends AbstractFuzzyMember<Field> {
private AbstractFuzzyMatcher<Class<?>> typeMatcher = ExactClassMatcher.MATCH_ALL;
/**
* Represents a builder for a field matcher.
*
* @author Kristian
*/
public static class Builder extends AbstractFuzzyMember.Builder<FuzzyFieldContract> {
@Override
public Builder requireModifier(int modifier) {
@ -74,12 +79,22 @@ public class FuzzyFieldContract extends AbstractFuzzyMember<Field> {
}
public Builder typeExact(Class<?> type) {
member.typeMatcher = ExactClassMatcher.matchExact(type);
member.typeMatcher = FuzzyMatchers.matchExact(type);
return this;
}
public Builder typeSuperOf(Class<?> type) {
member.typeMatcher = ExactClassMatcher.matchSuper(type);
member.typeMatcher = FuzzyMatchers.matchSuper(type);
return this;
}
public Builder typeDerivedOf(Class<?> type) {
member.typeMatcher = FuzzyMatchers.matchDerived(type);
return this;
}
public Builder typeMatches(AbstractFuzzyMatcher<Class<?>> matcher) {
member.typeMatcher = matcher;
return this;
}
@ -90,6 +105,10 @@ public class FuzzyFieldContract extends AbstractFuzzyMember<Field> {
}
}
/**
* Return a new fuzzy field contract builder.
* @return New fuzzy field contract builder.
*/
public static Builder newBuilder() {
return new Builder();
}

View File

@ -0,0 +1,100 @@
package com.comphenix.protocol.reflect;
import java.lang.reflect.Member;
import java.util.regex.Pattern;
/**
* Contains factory methods for matching classes.
*
* @author Kristian
*/
public class FuzzyMatchers {
private FuzzyMatchers() {
// Don't make this constructable
}
/**
* Construct a class matcher that matches types exactly.
* @param matcher - the matching class.
*/
public static AbstractFuzzyMatcher<Class<?>> matchExact(Class<?> matcher) {
return new ExactClassMatcher(matcher, ExactClassMatcher.Options.MATCH_EXACT);
}
/**
* Construct a class matcher that matches super types of the given class.
* @param matcher - the matching type must be a super class of this type.
* @return A new class mathcher.
*/
public static AbstractFuzzyMatcher<Class<?>> matchSuper(Class<?> matcher) {
return new ExactClassMatcher(matcher, ExactClassMatcher.Options.MATCH_SUPER);
}
/**
* Construct a class matcher that matches derived types of the given class.
* @param matcher - the matching type must be a derived class of this type.
* @return A new class mathcher.
*/
public static AbstractFuzzyMatcher<Class<?>> matchDerived(Class<?> matcher) {
return new ExactClassMatcher(matcher, ExactClassMatcher.Options.MATCH_DERIVED);
}
/**
* Construct a class matcher based on the canonical names of classes.
* @param regex - regular expression pattern matching class names.
* @param priority - the priority this matcher takes - higher is better.
* @return A fuzzy class matcher based on name.
*/
public static AbstractFuzzyMatcher<Class<?>> matchRegex(final Pattern regex, final int priority) {
return new AbstractFuzzyMatcher<Class<?>>() {
@Override
public boolean isMatch(Class<?> value, Object parent) {
if (value != null)
return regex.matcher(value.getCanonicalName()).matches();
else
return false;
}
@Override
protected int calculateRoundNumber() {
return -priority;
}
};
}
/**
* Construct a class matcher based on the canonical names of classes.
* @param regex - regular expression matching class names.
* @param priority - the priority this matcher takes - higher is better.
* @return A fuzzy class matcher based on name.
*/
public static AbstractFuzzyMatcher<Class<?>> matchRegex(String regex, final int priority) {
return FuzzyMatchers.matchRegex(Pattern.compile(regex), priority);
}
/**
* Match the parent class of a method, field or constructor.
* @return Parent matcher.
*/
public static AbstractFuzzyMatcher<Class<?>> matchParent() {
return new AbstractFuzzyMatcher<Class<?>>() {
@Override
public boolean isMatch(Class<?> value, Object parent) {
if (parent instanceof Member) {
return ((Member) parent).getDeclaringClass().equals(value);
} else if (parent instanceof Class) {
return parent.equals(value);
} else {
// Can't be a match
return false;
}
}
@Override
protected int calculateRoundNumber() {
// We match a very specific type
return -100;
}
};
}
}

View File

@ -85,6 +85,11 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
// Expected parameter count
private Integer paramCount;
/**
* Represents a builder for a fuzzy method contract.
*
* @author Kristian
*/
public static class Builder extends AbstractFuzzyMember.Builder<FuzzyMethodContract> {
public Builder requireModifier(int modifier) {
super.requireModifier(modifier);
@ -145,19 +150,19 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
* @return This builder, for chaining.
*/
public Builder parameterExactType(Class<?> type) {
member.paramMatchers.add(new ParameterClassMatcher(ExactClassMatcher.matchExact(type)));
member.paramMatchers.add(new ParameterClassMatcher(FuzzyMatchers.matchExact(type)));
return this;
}
/**
* Add a new required parameter whose type must be a superlcass of the given type.
* Add a new required parameter whose type must be a superclass of the given type.
* <p>
* 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.
* @return This builder, for chaining.
*/
public Builder parameterSuperOf(Class<?> type) {
member.paramMatchers.add(new ParameterClassMatcher(ExactClassMatcher.matchSuper(type)));
member.paramMatchers.add(new ParameterClassMatcher(FuzzyMatchers.matchSuper(type)));
return this;
}
@ -178,7 +183,7 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
* @return This builder, for chaining.
*/
public Builder parameterExactType(Class<?> type, int index) {
member.paramMatchers.add(new ParameterClassMatcher(ExactClassMatcher.matchExact(type), index));
member.paramMatchers.add(new ParameterClassMatcher(FuzzyMatchers.matchExact(type), index));
return this;
}
@ -191,7 +196,7 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
* @return This builder, for chaining.
*/
public Builder parameterSuperOf(Class<?> type, int index) {
member.paramMatchers.add(new ParameterClassMatcher(ExactClassMatcher.matchSuper(type), index));
member.paramMatchers.add(new ParameterClassMatcher(FuzzyMatchers.matchSuper(type), index));
return this;
}
@ -230,7 +235,7 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
* @return This builder, for chaining.
*/
public Builder returnTypeExact(Class<?> type) {
member.returnMatcher = ExactClassMatcher.matchExact(type);
member.returnMatcher = FuzzyMatchers.matchExact(type);
return this;
}
@ -240,7 +245,7 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
* @return This builder, for chaining.
*/
public Builder returnDerivedOf(Class<?> type) {
member.returnMatcher = ExactClassMatcher.matchDerived(type);
member.returnMatcher = FuzzyMatchers.matchDerived(type);
return this;
}
@ -260,7 +265,7 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
* @return This builder, for chaining.
*/
public Builder exceptionExactType(Class<?> type) {
member.exceptionMatchers.add(new ParameterClassMatcher(ExactClassMatcher.matchExact(type)));
member.exceptionMatchers.add(new ParameterClassMatcher(FuzzyMatchers.matchExact(type)));
return this;
}
@ -270,7 +275,7 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
* @return This builder, for chaining.
*/
public Builder exceptionSuperOf(Class<?> type) {
member.exceptionMatchers.add(new ParameterClassMatcher(ExactClassMatcher.matchSuper(type)));
member.exceptionMatchers.add(new ParameterClassMatcher(FuzzyMatchers.matchSuper(type)));
return this;
}
@ -291,7 +296,7 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
* @return This builder, for chaining.
*/
public Builder exceptionExactType(Class<?> type, int index) {
member.exceptionMatchers.add(new ParameterClassMatcher(ExactClassMatcher.matchExact(type), index));
member.exceptionMatchers.add(new ParameterClassMatcher(FuzzyMatchers.matchExact(type), index));
return this;
}
@ -302,7 +307,7 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
* @return This builder, for chaining.
*/
public Builder exceptionSuperOf(Class<?> type, int index) {
member.exceptionMatchers.add(new ParameterClassMatcher(ExactClassMatcher.matchSuper(type), index));
member.exceptionMatchers.add(new ParameterClassMatcher(FuzzyMatchers.matchSuper(type), index));
return this;
}
@ -330,6 +335,10 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
}
}
/**
* Return a method contract builder.
* @return Method contract builder.
*/
public static Builder newBuilder() {
return new Builder();
}

View File

@ -254,7 +254,6 @@ public class FuzzyReflection {
* @throws IllegalArgumentException If the field cannot be found.
*/
public Field getFieldByName(String nameRegex) {
Pattern match = Pattern.compile(nameRegex);
for (Field field : getFields()) {
@ -276,7 +275,6 @@ public class FuzzyReflection {
* @return The first field with a type that is an instance of the given type.
*/
public Field getFieldByType(String name, Class<?> type) {
List<Field> fields = getFieldListByType(type);
if (fields.size() > 0) {
@ -295,7 +293,6 @@ public class FuzzyReflection {
* @return Every field with a type that is an instance of the given type.
*/
public List<Field> getFieldListByType(Class<?> type) {
List<Field> fields = new ArrayList<Field>();
// Field with a compatible type

View File

@ -17,9 +17,17 @@
package com.comphenix.protocol.utility;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.ServerSocket;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
@ -29,6 +37,13 @@ import org.bukkit.Server;
import org.bukkit.inventory.ItemStack;
import com.comphenix.protocol.injector.BukkitUnwrapper;
import com.comphenix.protocol.reflect.AbstractFuzzyMatcher;
import com.comphenix.protocol.reflect.FuzzyClassContract;
import com.comphenix.protocol.reflect.FuzzyMatchers;
import com.comphenix.protocol.reflect.FuzzyFieldContract;
import com.comphenix.protocol.reflect.FuzzyMethodContract;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.google.common.base.Joiner;
/**
@ -40,7 +55,7 @@ public class MinecraftReflection {
/**
* Regular expression that matches a Minecraft object.
* <p>
* Replaced by the method {@link #getMinecraftObjectMatcher()}.
* Replaced by the method {@link #getMinecraftObjectRegex()}.
*/
@Deprecated
public static final String MINECRAFT_OBJECT = "net\\.minecraft(\\.\\w+)+";
@ -81,12 +96,20 @@ public class MinecraftReflection {
* Retrieve a regular expression that can match Minecraft package objects.
* @return Minecraft package matcher.
*/
public static String getMinecraftObjectMatcher() {
public static String getMinecraftObjectRegex() {
if (DYNAMIC_PACKAGE_MATCHER == null)
getMinecraftPackage();
return DYNAMIC_PACKAGE_MATCHER;
}
/**
* Retrieve a abstract fuzzy class matcher for Minecraft objects.
* @return A matcher for Minecraft objects.
*/
public static AbstractFuzzyMatcher<Class<?>> getMinecraftObjectMatcher() {
return FuzzyMatchers.matchRegex(getMinecraftObjectRegex(), 50);
}
/**
* Retrieve the name of the Minecraft server package.
* @return Full canonical name of the Minecraft server package.
@ -120,6 +143,10 @@ public class MinecraftReflection {
DYNAMIC_PACKAGE_MATCHER =
(MINECRAFT_PREFIX_PACKAGE.length() > 0 ?
Pattern.quote(MINECRAFT_PREFIX_PACKAGE + ".") : "") + "\\w+";
// We'll still accept the default location, however
DYNAMIC_PACKAGE_MATCHER = "(" + DYNAMIC_PACKAGE_MATCHER + ")|(" + MINECRAFT_OBJECT + ")";
} else {
// Use the standard matcher
DYNAMIC_PACKAGE_MATCHER = MINECRAFT_OBJECT;
@ -146,6 +173,9 @@ public class MinecraftReflection {
public static void setMinecraftPackage(String minecraftPackage, String craftBukkitPackage) {
MINECRAFT_FULL_PACKAGE = minecraftPackage;
CRAFTBUKKIT_PACKAGE = craftBukkitPackage;
// Standard matcher
DYNAMIC_PACKAGE_MATCHER = MINECRAFT_OBJECT;
}
/**
@ -325,7 +355,22 @@ public class MinecraftReflection {
* @return The entity class.
*/
public static Class<?> getEntityPlayerClass() {
return getMinecraftClass("EntityPlayer");
try {
return getMinecraftClass("EntityPlayer");
} catch (RuntimeException e) {
try {
// A fairly stable method
Method detect = FuzzyReflection.fromClass(getCraftBukkitClass("CraftServer")).
getMethodByName("detectListNameConflict");
// EntityPlayer is then the first parameter
return detect.getParameterTypes()[0];
} catch (IllegalArgumentException ex) {
// Last resort
return fallbackMethodReturn("EntityPlayer", "entity.CraftPlayer", "getHandle");
}
}
}
/**
@ -333,7 +378,38 @@ public class MinecraftReflection {
* @return The entity class.
*/
public static Class<?> getEntityClass() {
try {
return getMinecraftClass("Entity");
} catch (RuntimeException e) {
return fallbackMethodReturn("Entity", "entity.CraftEntity", "getHandle");
}
}
/**
* Retrieve the WorldServer (NMS) class.
* @return The WorldServer class.
*/
public static Class<?> getWorldServerClass() {
try {
return getMinecraftClass("WorldServer");
} catch (RuntimeException e) {
return fallbackMethodReturn("WorldServer", "CraftWorld", "getHandle");
}
}
/**
* Fallback on the return value of a named method in order to get a NMS class.
* @param nmsClass - the expected name of the Minecraft class.
* @param craftClass - a CraftBukkit class to look at.
* @param methodName - the method we will use.
* @return The return value of this method, which will be saved to the package cache.
*/
private static Class<?> fallbackMethodReturn(String nmsClass, String craftClass, String methodName) {
Class<?> result = FuzzyReflection.fromClass(getCraftBukkitClass(craftClass)).
getMethodByName(methodName).getReturnType();
// Save the result
return setMinecraftClass(nmsClass, result);
}
/**
@ -341,15 +417,89 @@ public class MinecraftReflection {
* @return The packet class.
*/
public static Class<?> getPacketClass() {
return getMinecraftClass("Packet");
try {
return getMinecraftClass("Packet");
} catch (RuntimeException e) {
// What kind of class we're looking for (sanity check)
FuzzyClassContract paketContract =
FuzzyClassContract.newBuilder().
field(FuzzyFieldContract.newBuilder().
typeDerivedOf(Map.class).
requireModifier(Modifier.STATIC)).
field(FuzzyFieldContract.newBuilder().
typeDerivedOf(Set.class).
requireModifier(Modifier.STATIC)).
method(FuzzyMethodContract.newBuilder().
parameterSuperOf(DataInputStream.class).
returnTypeVoid()).
build();
// Select a method with one Minecraft object parameter
Method selected = FuzzyReflection.fromClass(getNetHandlerClass()).
getMethod(FuzzyMethodContract.newBuilder().
parameterMatches(paketContract, 0).
parameterCount(1).
build()
);
// Save and return
Class<?> clazz = getTopmostClass(selected.getParameterTypes()[0]);
return setMinecraftClass("Packet", clazz);
}
}
/**
* Retrieve the least derived class, except Object.
* @return Least derived super class.
*/
private static Class<?> getTopmostClass(Class<?> clazz) {
while (true) {
Class<?> superClass = clazz.getSuperclass();
if (superClass == Object.class || superClass == null)
return clazz;
else
clazz = superClass;
}
}
/**
* Retrieve the MinecraftServer class.
* @return MinecraftServer class.
*/
public static Class<?> getMinecraftServerClass() {
try {
return getMinecraftClass("MinecraftServer");
} catch (RuntimeException e) {
// Get the first constructor that matches CraftServer(MINECRAFT_OBJECT, ANY)
Constructor<?> selected = FuzzyReflection.fromClass(getCraftBukkitClass("CraftServer")).
getConstructor(FuzzyMethodContract.newBuilder().
parameterMatches(getMinecraftObjectMatcher(), 0).
parameterCount(2).
build()
);
Class<?>[] params = selected.getParameterTypes();
// Jackpot - two classes at the same time!
setMinecraftClass("MinecraftServer", params[0]);
setMinecraftClass("ServerConfigurationManager", params[1]);
return params[0];
}
}
/**
* Retrieve the player list class (or ServerConfigurationManager),
* @return The player list class.
*/
public static Class<?> getPlayerListClass() {
return getMinecraftClass("ServerConfigurationManager", "PlayerList");
try {
return getMinecraftClass("ServerConfigurationManager", "PlayerList");
} catch (RuntimeException e) {
// Try again
getMinecraftServerClass();
return getMinecraftClass("ServerConfigurationManager");
}
}
/**
@ -357,7 +507,22 @@ public class MinecraftReflection {
* @return The NetLoginHandler class.
*/
public static Class<?> getNetLoginHandlerClass() {
return getMinecraftClass("NetLoginHandler", "PendingConnection");
try {
return getMinecraftClass("NetLoginHandler", "PendingConnection");
} catch (RuntimeException e) {
Method selected = FuzzyReflection.fromClass(getPlayerListClass()).
getMethod(FuzzyMethodContract.newBuilder().
parameterMatches(
FuzzyMatchers.matchExact(getEntityPlayerClass()).inverted(), 0
).
parameterExactType(String.class, 1).
parameterExactType(String.class, 2).
build()
);
// Save the pending connection reference
return setMinecraftClass("NetLoginHandler", selected.getParameterTypes()[0]);
}
}
/**
@ -365,15 +530,35 @@ public class MinecraftReflection {
* @return The NetServerHandler class.
*/
public static Class<?> getNetServerHandlerClass() {
return getMinecraftClass("NetServerHandler", "PlayerConnection");
try {
return getMinecraftClass("NetServerHandler", "PlayerConnection");
} catch (RuntimeException e) {
// Use the player connection field
return setMinecraftClass("NetLoginHandler",
FuzzyReflection.fromClass(getEntityPlayerClass()).
getFieldByType("playerConnection", getNetHandlerClass()).getType()
);
}
}
/**
* Retrieve the NetworkManager class.
* @return The NetworkManager class.
* Retrieve the NetworkManager class or its interface.
* @return The NetworkManager class or its interface.
*/
public static Class<?> getNetworkManagerClass() {
return getMinecraftClass("NetworkManager");
try {
return getMinecraftClass("INetworkManager", "NetworkManager");
} catch (RuntimeException e) {
Constructor<?> selected = FuzzyReflection.fromClass(getNetServerHandlerClass()).
getConstructor(FuzzyMethodContract.newBuilder().
parameterSuperOf(getMinecraftServerClass(), 0).
parameterSuperOf(getEntityPlayerClass(), 2).
build()
);
// And we're done
return setMinecraftClass("INetworkManager", selected.getParameterTypes()[1]);
}
}
/**
@ -381,7 +566,11 @@ public class MinecraftReflection {
* @return The NetHandler class.
*/
public static Class<?> getNetHandlerClass() {
return getMinecraftClass("NetHandler", "Connection");
try {
return getMinecraftClass("NetHandler", "Connection");
} catch (RuntimeException e) {
return setMinecraftClass("NetHandler", getNetLoginHandlerClass().getSuperclass());
}
}
/**
@ -389,7 +578,13 @@ public class MinecraftReflection {
* @return The ItemStack class.
*/
public static Class<?> getItemStackClass() {
return getMinecraftClass("ItemStack");
try {
return getMinecraftClass("ItemStack");
} catch (RuntimeException e) {
// Use the handle reference
return setMinecraftClass("ItemStack",
FuzzyReflection.fromClass(getCraftItemStackClass(), true).getFieldByName("handle").getType());
}
}
/**
@ -397,15 +592,21 @@ public class MinecraftReflection {
* @return The WorldType class.
*/
public static Class<?> getWorldTypeClass() {
return getMinecraftClass("WorldType");
}
/**
* Retrieve the MinecraftServer class.
* @return MinecraftServer class.
*/
public static Class<?> getMinecraftServerClass() {
return getMinecraftClass("MinecraftServer");
try {
return getMinecraftClass("WorldType");
} catch (RuntimeException e) {
// Get the first constructor that matches CraftServer(MINECRAFT_OBJECT, ANY)
Method selected = FuzzyReflection.fromClass(getMinecraftServerClass(), true).
getMethod(FuzzyMethodContract.newBuilder().
parameterExactType(String.class, 0).
parameterExactType(String.class, 1).
parameterMatches(getMinecraftObjectMatcher()).
parameterExactType(String.class, 4).
parameterCount(5).
build()
);
return setMinecraftClass("WorldType", selected.getParameterTypes()[3]);
}
}
/**
@ -413,7 +614,33 @@ public class MinecraftReflection {
* @return The DataWatcher class.
*/
public static Class<?> getDataWatcherClass() {
return getMinecraftClass("DataWatcher");
try {
return getMinecraftClass("DataWatcher");
} catch (RuntimeException e) {
// Describe the DataWatcher
FuzzyClassContract dataWatcherContract = FuzzyClassContract.newBuilder().
field(FuzzyFieldContract.newBuilder().
requireModifier(Modifier.STATIC).
typeDerivedOf(Map.class)).
field(FuzzyFieldContract.newBuilder().
banModifier(Modifier.STATIC).
typeDerivedOf(Map.class)).
method(FuzzyMethodContract.newBuilder().
parameterExactType(int.class).
parameterExactType(Object.class).
returnTypeVoid()).
build();
FuzzyFieldContract fieldContract = FuzzyFieldContract.newBuilder().
typeMatches(dataWatcherContract).
build();
// Get such a field and save the result
return setMinecraftClass("DataWatcher",
FuzzyReflection.fromClass(getEntityClass(), true).
getField(fieldContract).
getType()
);
}
}
/**
@ -421,7 +648,25 @@ public class MinecraftReflection {
* @return The ChunkPosition class.
*/
public static Class<?> getChunkPositionClass() {
return getMinecraftClass("ChunkPosition");
try {
return getMinecraftClass("ChunkPosition");
} catch (RuntimeException e) {
Class<?> normalChunkGenerator = getCraftBukkitClass("generator.NormalChunkGenerator");
// ChunkPosition a(net.minecraft.server.World world, String string, int i, int i1, int i2) {
FuzzyMethodContract selected = FuzzyMethodContract.newBuilder().
banModifier(Modifier.STATIC).
parameterMatches(getMinecraftObjectMatcher(), 0).
parameterExactType(String.class, 1).
parameterExactType(int.class, 2).
parameterExactType(int.class, 3).
parameterExactType(int.class, 4).
build();
return setMinecraftClass("ChunkPosition",
FuzzyReflection.fromClass(normalChunkGenerator).
getMethod(selected).getReturnType());
}
}
/**
@ -429,7 +674,11 @@ public class MinecraftReflection {
* @return The ChunkPosition class.
*/
public static Class<?> getChunkCoordinatesClass() {
return getMinecraftClass("ChunkCoordinates");
try {
return getMinecraftClass("ChunkCoordinates");
} catch (RuntimeException e) {
return setMinecraftClass("ChunkCoordinates", WrappedDataWatcher.getTypeClass(6));
}
}
/**
@ -437,7 +686,19 @@ public class MinecraftReflection {
* @return The WatchableObject class.
*/
public static Class<?> getWatchableObjectClass() {
return getMinecraftClass("WatchableObject");
try {
return getMinecraftClass("WatchableObject");
} catch (RuntimeException e) {
Method selected = FuzzyReflection.fromClass(getDataWatcherClass(), true).
getMethod(FuzzyMethodContract.newBuilder().
requireModifier(Modifier.STATIC).
parameterSuperOf(DataOutputStream.class, 0).
parameterMatches(getMinecraftObjectMatcher(), 1).
build());
// Use the second parameter
return setMinecraftClass("WatchableObject", selected.getParameterTypes()[1]);
}
}
/**
@ -445,7 +706,26 @@ public class MinecraftReflection {
* @return The ServerConnection class.
*/
public static Class<?> getServerConnectionClass() {
return getMinecraftClass("ServerConnection");
try {
return getMinecraftClass("ServerConnection");
} catch (RuntimeException e) {
FuzzyClassContract serverConnectionContract = FuzzyClassContract.newBuilder().
constructor(FuzzyMethodContract.newBuilder().
parameterExactType(getMinecraftServerClass()).
parameterCount(1)).
method(FuzzyMethodContract.newBuilder().
parameterExactType(getNetServerHandlerClass())).
build();
Method selected = FuzzyReflection.fromClass(getMinecraftServerClass()).
getMethod(FuzzyMethodContract.newBuilder().
requireModifier(Modifier.ABSTRACT).
returnTypeMatches(serverConnectionContract).
build());
// Use the return type
return setMinecraftClass("ServerConnection", selected.getReturnType());
}
}
/**
@ -453,7 +733,95 @@ public class MinecraftReflection {
* @return The NBT base class.
*/
public static Class<?> getNBTBaseClass() {
return getMinecraftClass("NBTBase");
try {
return getMinecraftClass("NBTBase");
} catch (RuntimeException e) {
FuzzyClassContract tagCompoundContract = FuzzyClassContract.newBuilder().
constructor(FuzzyMethodContract.newBuilder().
parameterExactType(String.class).
parameterCount(1)).
field(FuzzyFieldContract.newBuilder().
typeDerivedOf(Map.class)).
build();
Method selected = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).
getMethod(FuzzyMethodContract.newBuilder().
requireModifier(Modifier.STATIC).
parameterSuperOf(DataInputStream.class).
parameterCount(1).
returnTypeMatches(tagCompoundContract).
build()
);
// Use the return type here too
return setMinecraftClass("NBTBase", selected.getReturnType());
}
}
/**
* Retrieve the EntityTracker (NMS) class.
* @return EntityTracker class.
*/
public static Class<?> getEntityTrackerClass() {
try {
return getMinecraftClass("EntityTracker");
} catch (RuntimeException e) {
FuzzyClassContract entityTrackerContract = FuzzyClassContract.newBuilder().
field(FuzzyFieldContract.newBuilder().
typeDerivedOf(Set.class)).
method(FuzzyMethodContract.newBuilder().
parameterSuperOf(MinecraftReflection.getEntityClass()).
parameterCount(1).
returnTypeVoid()).
method(FuzzyMethodContract.newBuilder().
parameterSuperOf(MinecraftReflection.getEntityClass(), 0).
parameterSuperOf(int.class, 1).
parameterSuperOf(int.class, 2).
parameterCount(3).
returnTypeVoid()).
build();
Field selected = FuzzyReflection.fromClass(MinecraftReflection.getWorldServerClass(), true).
getField(FuzzyFieldContract.newBuilder().
typeMatches(entityTrackerContract).
build()
);
// Go by the defined type of this field
return setMinecraftClass("EntityTracker", selected.getType());
}
}
/**
* Retrieve the NetworkListenThread class (NMS).
* <p>
* Note that this class was removed after Minecraft 1.3.1.
* @return NetworkListenThread class.
*/
public static Class<?> getNetworkListenThreadClass() {
try {
return getMinecraftClass("NetworkListenThread");
} catch (RuntimeException e) {
FuzzyClassContract networkListenContract = FuzzyClassContract.newBuilder().
field(FuzzyFieldContract.newBuilder().
typeDerivedOf(ServerSocket.class)).
field(FuzzyFieldContract.newBuilder().
typeDerivedOf(Thread.class)).
field(FuzzyFieldContract.newBuilder().
typeDerivedOf(List.class)).
method(FuzzyMethodContract.newBuilder().
parameterExactType(getNetServerHandlerClass())).
build();
Field selected = FuzzyReflection.fromClass(MinecraftReflection.getMinecraftServerClass(), true).
getField(FuzzyFieldContract.newBuilder().
typeMatches(networkListenContract).
build()
);
// Go by the defined type of this field
return setMinecraftClass("NetworkListenThread", selected.getType());
}
}
/**
@ -619,6 +987,19 @@ public class MinecraftReflection {
return minecraftPackage.getPackageClass(className);
}
/**
* Set the class object for the specific Minecraft class.
* @param className - name of the Minecraft class.
* @param clazz - the new class object.
* @return The provided clazz object.
*/
private static Class<?> setMinecraftClass(String className, Class<?> clazz) {
if (minecraftPackage == null)
minecraftPackage = new CachedPackage(getMinecraftPackage());
minecraftPackage.setPackageClass(className, clazz);
return clazz;
}
/**
* Retrieve the first class that matches a specified Minecraft name.
* @param className - the specific Minecraft class.