From e8c615b2032b71daf283728c0778540ef656352b Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Thu, 31 Jan 2013 06:23:01 +0100 Subject: [PATCH] Attempting to add support for MCPC. Still have to track down a very elusive bug however. --- .../protocol/async/PacketProcessingQueue.java | 15 +- .../protocol/injector/EntityUtilities.java | 18 +- .../protocol/injector/PacketInjector.java | 2 +- .../player/InjectedServerConnection.java | 8 +- .../player/NetworkServerInjector.java | 76 ++- .../injector/player/PlayerInjector.java | 16 +- .../reflect/AbstractFuzzyMatcher.java | 60 +++ .../protocol/reflect/AbstractFuzzyMember.java | 14 +- .../protocol/reflect/ExactClassMatcher.java | 28 +- .../protocol/reflect/FuzzyClassContract.java | 30 +- .../protocol/reflect/FuzzyFieldContract.java | 23 +- .../protocol/reflect/FuzzyMatchers.java | 100 ++++ .../protocol/reflect/FuzzyMethodContract.java | 31 +- .../protocol/reflect/FuzzyReflection.java | 3 - .../protocol/utility/MinecraftReflection.java | 435 ++++++++++++++++-- 15 files changed, 729 insertions(+), 130 deletions(-) create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyMatchers.java diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/async/PacketProcessingQueue.java b/ProtocolLib/src/main/java/com/comphenix/protocol/async/PacketProcessingQueue.java index 364b6c1f..06385c0e 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/async/PacketProcessingQueue.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/async/PacketProcessingQueue.java @@ -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 AbstractConcurrentListenerMultimapcreate(), null); + try { + this.processingQueue = Synchronization.queue(MinMaxPriorityQueue. + expectedSize(initialSize). + maximumSize(maximumSize). + create(), null); + } catch (IncompatibleClassChangeError e) { + // It's a Beta class after all + this.processingQueue = Synchronization.queue( + new PriorityQueue(), null); + } this.maximumConcurrency = maximumConcurrency; this.concurrentProcessing = new Semaphore(maximumConcurrency); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java index c773f4aa..31152925 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java @@ -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); } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketInjector.java index 2ac36e95..32bac7eb 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketInjector.java @@ -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); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedServerConnection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedServerConnection.java index b9560cdf..c6860ab6 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedServerConnection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedServerConnection.java @@ -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; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkServerInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkServerInjector.java index 74a0a3e6..d6c6e592 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkServerInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkServerInjector.java @@ -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 netServer = getMethodList( + MinecraftReflection.getNetServerHandlerClass(), MinecraftReflection.getPacketClass()); + Map 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 getMethodList(Class source, Class... params) { + return getMappedMethods( + FuzzyReflection.fromClass(source, true). + getMethodListByParameters(Void.TYPE, params) + ); + } + + /** + * Retrieve every method as a map over names. + *

+ * 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 getMappedMethods(List methods) { + Map 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."); } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java index 79f90c6a..da1e1834 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java @@ -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()); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/AbstractFuzzyMatcher.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/AbstractFuzzyMatcher.java index c95fa3e1..1d4d4e26 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/AbstractFuzzyMatcher.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/AbstractFuzzyMatcher.java @@ -72,4 +72,64 @@ public abstract class AbstractFuzzyMatcher implements Comparable inverted() { + return new AbstractFuzzyMatcher() { + @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 and(final AbstractFuzzyMatcher other) { + return new AbstractFuzzyMatcher() { + @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 or(final AbstractFuzzyMatcher other) { + return new AbstractFuzzyMatcher() { + @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()); + } + }; + } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/AbstractFuzzyMember.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/AbstractFuzzyMember.java index 3d77a24b..b0ab4950 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/AbstractFuzzyMember.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/AbstractFuzzyMember.java @@ -13,11 +13,11 @@ import javax.annotation.Nonnull; */ public abstract class AbstractFuzzyMember extends AbstractFuzzyMatcher { // Accessibility matchers - private int modifiersRequired; - private int modifiersBanned; + protected int modifiersRequired; + protected int modifiersBanned; - private Pattern nameRegex; - private AbstractFuzzyMatcher> declaringMatcher = ExactClassMatcher.MATCH_ALL; + protected Pattern nameRegex; + protected AbstractFuzzyMatcher> declaringMatcher = ExactClassMatcher.MATCH_ALL; /** * Whether or not this contract can be modified. @@ -89,7 +89,7 @@ public abstract class AbstractFuzzyMember extends AbstractFuzz * @return This builder, for chaining. */ public Builder declaringClassExactType(Class declaringClass) { - member.declaringMatcher = ExactClassMatcher.matchExact(declaringClass); + member.declaringMatcher = FuzzyMatchers.matchExact(declaringClass); return this; } @@ -99,7 +99,7 @@ public abstract class AbstractFuzzyMember extends AbstractFuzz * @return This builder, for chaining. */ public Builder declaringClassSuperOf(Class declaringClass) { - member.declaringMatcher = ExactClassMatcher.matchSuper(declaringClass); + member.declaringMatcher = FuzzyMatchers.matchSuper(declaringClass); return this; } @@ -109,7 +109,7 @@ public abstract class AbstractFuzzyMember extends AbstractFuzz * @return This builder, for chaining. */ public Builder declaringClassDerivedOf(Class declaringClass) { - member.declaringMatcher = ExactClassMatcher.matchDerived(declaringClass); + member.declaringMatcher = FuzzyMatchers.matchDerived(declaringClass); return this; } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/ExactClassMatcher.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/ExactClassMatcher.java index 3e7a34e4..51b22d04 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/ExactClassMatcher.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/ExactClassMatcher.java @@ -34,38 +34,12 @@ class ExactClassMatcher extends AbstractFuzzyMatcher> { 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; } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyClassContract.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyClassContract.java index 0ae1b1a5..d0c81e0f 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyClassContract.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyClassContract.java @@ -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> { return new Builder(); } - /** - * Match the parent class of a method, field or constructor. - * @return Parent matcher. - */ - public static AbstractFuzzyMatcher> matchParent() { - return new AbstractFuzzyMatcher>() { - @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> { @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) && diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyFieldContract.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyFieldContract.java index 040ec89f..2293a5e8 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyFieldContract.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyFieldContract.java @@ -13,6 +13,11 @@ import javax.annotation.Nonnull; public class FuzzyFieldContract extends AbstractFuzzyMember { private AbstractFuzzyMatcher> typeMatcher = ExactClassMatcher.MATCH_ALL; + /** + * Represents a builder for a field matcher. + * + * @author Kristian + */ public static class Builder extends AbstractFuzzyMember.Builder { @Override public Builder requireModifier(int modifier) { @@ -74,12 +79,22 @@ public class FuzzyFieldContract extends AbstractFuzzyMember { } 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> matcher) { + member.typeMatcher = matcher; return this; } @@ -90,6 +105,10 @@ public class FuzzyFieldContract extends AbstractFuzzyMember { } } + /** + * Return a new fuzzy field contract builder. + * @return New fuzzy field contract builder. + */ public static Builder newBuilder() { return new Builder(); } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyMatchers.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyMatchers.java new file mode 100644 index 00000000..1fee2439 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyMatchers.java @@ -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> 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> 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> 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> matchRegex(final Pattern regex, final int priority) { + return new AbstractFuzzyMatcher>() { + @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> 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> matchParent() { + return new AbstractFuzzyMatcher>() { + @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; + } + }; + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyMethodContract.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyMethodContract.java index c477d08b..a53358df 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyMethodContract.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyMethodContract.java @@ -85,6 +85,11 @@ public class FuzzyMethodContract extends AbstractFuzzyMember { // Expected parameter count private Integer paramCount; + /** + * Represents a builder for a fuzzy method contract. + * + * @author Kristian + */ public static class Builder extends AbstractFuzzyMember.Builder { public Builder requireModifier(int modifier) { super.requireModifier(modifier); @@ -145,19 +150,19 @@ public class FuzzyMethodContract extends AbstractFuzzyMember { * @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. *

* 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 { * @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 { * @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 { * @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 { * @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 { * @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 { * @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 { * @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 { * @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 { } } + /** + * Return a method contract builder. + * @return Method contract builder. + */ public static Builder newBuilder() { return new Builder(); } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java index 3f22efdf..07067262 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java @@ -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 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 getFieldListByType(Class type) { - List fields = new ArrayList(); // Field with a compatible type diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java index 89119886..b59e3522 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java @@ -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. *

- * 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> 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). + *

+ * 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.