mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-11-24 03:25:29 +01:00
Ensure that hook method #3 receieves Packet0KeepAlive.
This commit is contained in:
parent
9770257a74
commit
3ad24921d9
@ -53,8 +53,7 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
@Override
|
||||
public void onLoad() {
|
||||
logger = getLoggerSafely();
|
||||
protocolManager = new PacketFilterManager(
|
||||
getClassLoader(), getServer().getScheduler(), logger);
|
||||
protocolManager = new PacketFilterManager(getClassLoader(), getServer(), logger);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,189 @@
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.bukkit.Server;
|
||||
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.VolatileField;
|
||||
|
||||
/**
|
||||
* Used to ensure that the 1.3 server is referencing the correct server handler.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class InjectedServerConnection {
|
||||
|
||||
private static Field listenerThreadField;
|
||||
private static Field minecraftServerField;
|
||||
private static Method serverConnectionMethod;
|
||||
private static Field listField;
|
||||
|
||||
private List<VolatileField> listFields;
|
||||
private List<ReplacedArrayList<Object>> replacedLists;
|
||||
|
||||
private Server server;
|
||||
private Logger logger;
|
||||
private boolean hasAttempted;
|
||||
private boolean hasSuccess;
|
||||
|
||||
private Object minecraftServer = null;
|
||||
|
||||
public InjectedServerConnection(Logger logger, Server server) {
|
||||
this.listFields = new ArrayList<VolatileField>();
|
||||
this.replacedLists = new ArrayList<ReplacedArrayList<Object>>();
|
||||
this.logger = logger;
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
public void injectList() {
|
||||
|
||||
// Only execute this method once
|
||||
if (!hasAttempted)
|
||||
hasAttempted = true;
|
||||
else
|
||||
return;
|
||||
|
||||
if (minecraftServerField == null)
|
||||
minecraftServerField = FuzzyReflection.fromObject(server, true).getFieldByType(".*MinecraftServer");
|
||||
|
||||
try {
|
||||
minecraftServer = FieldUtils.readField(minecraftServerField, server, true);
|
||||
} catch (IllegalAccessException e1) {
|
||||
logger.log(Level.WARNING, "Cannot extract minecraft server from Bukkit.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (serverConnectionMethod == null)
|
||||
serverConnectionMethod = FuzzyReflection.fromClass(minecraftServerField.getType()).
|
||||
getMethodByParameters("getServerConnection", ".*ServerConnection", new String[] {});
|
||||
// We're using Minecraft 1.3.1
|
||||
injectServerConnection();
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
|
||||
// Minecraft 1.2.5 or lower
|
||||
injectListenerThread();
|
||||
}
|
||||
}
|
||||
|
||||
private void injectListenerThread() {
|
||||
|
||||
try {
|
||||
|
||||
if (listenerThreadField == null)
|
||||
listenerThreadField = FuzzyReflection.fromClass(minecraftServerField.getType()).
|
||||
getFieldByType(".*NetworkListenThread");
|
||||
} catch (RuntimeException e) {
|
||||
logger.log(Level.SEVERE, "Cannot find listener thread in MinecraftServer.");
|
||||
return;
|
||||
}
|
||||
|
||||
Object listenerThread = null;
|
||||
|
||||
// Attempt to get the thread
|
||||
try {
|
||||
listenerThread = listenerThreadField.get(minecraftServer);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "Unable to read the listener thread.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Ok, great. Get every list field
|
||||
List<Field> lists = FuzzyReflection.fromClass(listenerThreadField.getType()).getFieldListByType(List.class);
|
||||
|
||||
for (Field list : lists) {
|
||||
injectIntoList(listenerThread, list);
|
||||
}
|
||||
|
||||
hasSuccess = true;
|
||||
}
|
||||
|
||||
private void injectServerConnection() {
|
||||
|
||||
Object serverConnection = null;
|
||||
|
||||
// Careful - we might fail
|
||||
try {
|
||||
serverConnection = serverConnectionMethod.invoke(minecraftServer);
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.WARNING, "Unable to retrieve server connection", ex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (listField == null)
|
||||
listField = FuzzyReflection.fromClass(serverConnectionMethod.getReturnType(), true).
|
||||
getFieldByType("serverConnection", List.class);
|
||||
injectIntoList(serverConnection, listField);
|
||||
hasSuccess = true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void injectIntoList(Object instance, Field field) {
|
||||
VolatileField listFieldRef = new VolatileField(listField, instance, true);
|
||||
List<Object> list = (List<Object>) listFieldRef.getValue();
|
||||
|
||||
// Careful not to inject twice
|
||||
if (list instanceof ReplacedArrayList) {
|
||||
replacedLists.add((ReplacedArrayList<Object>) list);
|
||||
} else {
|
||||
replacedLists.add(new ReplacedArrayList<Object>(list));
|
||||
listFieldRef.setValue(replacedLists.get(0));
|
||||
listFields.add(listFieldRef);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the server handler instance kept by the "keep alive" object.
|
||||
* @param oldHandler - old server handler.
|
||||
* @param newHandler - new, proxied server handler.
|
||||
*/
|
||||
public void replaceServerHandler(Object oldHandler, Object newHandler) {
|
||||
if (!hasAttempted) {
|
||||
injectList();
|
||||
}
|
||||
|
||||
if (hasSuccess) {
|
||||
for (ReplacedArrayList<Object> replacedList : replacedLists) {
|
||||
replacedList.addMapping(oldHandler, newHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Revert to the old vanilla server handler, if it has been replaced.
|
||||
* @param oldHandler - old vanilla server handler.
|
||||
*/
|
||||
public void revertServerHandler(Object oldHandler) {
|
||||
if (hasSuccess) {
|
||||
for (ReplacedArrayList<Object> replacedList : replacedLists) {
|
||||
replacedList.removeMapping(oldHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Undoes everything.
|
||||
*/
|
||||
public void cleanupAll() {
|
||||
if (replacedLists.size() > 0) {
|
||||
// Repair the underlying lists
|
||||
for (ReplacedArrayList<Object> replacedList : replacedLists) {
|
||||
replacedList.revertAll();
|
||||
}
|
||||
for (VolatileField field : listFields) {
|
||||
field.revertValue();
|
||||
}
|
||||
|
||||
listFields.clear();
|
||||
replacedLists.clear();
|
||||
}
|
||||
}
|
||||
}
|
@ -49,7 +49,7 @@ class MinecraftRegistry {
|
||||
// Initialize it, if we haven't already
|
||||
if (packetToID == null) {
|
||||
try {
|
||||
Field packetsField = FuzzyReflection.fromClass(Packet.class, true).getFieldByType("java\\.util\\.Map");
|
||||
Field packetsField = FuzzyReflection.fromClass(Packet.class, true).getFieldByType("packetsField", Map.class);
|
||||
packetToID = (Map<Class, Integer>) FieldUtils.readStaticField(packetsField, true);
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
|
@ -13,8 +13,10 @@ import net.sf.cglib.proxy.MethodProxy;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.instances.CollectionGenerator;
|
||||
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
||||
import com.comphenix.protocol.reflect.instances.ExistingGenerator;
|
||||
@ -28,9 +30,14 @@ import com.comphenix.protocol.reflect.instances.PrimitiveGenerator;
|
||||
public class NetworkServerInjector extends PlayerInjector {
|
||||
|
||||
private static Method sendPacketMethod;
|
||||
|
||||
private StructureModifier<Object> serverHandlerModifier;
|
||||
private InjectedServerConnection serverInjection;
|
||||
|
||||
public NetworkServerInjector(Player player, PacketFilterManager manager, Set<Integer> sendingFilters) throws IllegalAccessException {
|
||||
public NetworkServerInjector(Player player, PacketFilterManager manager,
|
||||
Set<Integer> sendingFilters, InjectedServerConnection serverInjection) throws IllegalAccessException {
|
||||
super(player, manager, sendingFilters);
|
||||
this.serverInjection = serverInjection;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -41,6 +48,8 @@ public class NetworkServerInjector extends PlayerInjector {
|
||||
if (hasInitialized) {
|
||||
if (sendPacketMethod == null)
|
||||
sendPacketMethod = FuzzyReflection.fromObject(serverHandler).getMethodByName("sendPacket.*");
|
||||
if (serverHandlerModifier == null)
|
||||
serverHandlerModifier = new StructureModifier<Object>(serverHandler.getClass(), null, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,12 +107,8 @@ public class NetworkServerInjector extends PlayerInjector {
|
||||
}
|
||||
}
|
||||
|
||||
// Delegate to our underlying class
|
||||
try {
|
||||
return method.invoke(serverHandler, args);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw e.getCause();
|
||||
}
|
||||
// Call the method directly
|
||||
return proxy.invokeSuper(obj, args);
|
||||
}
|
||||
});
|
||||
|
||||
@ -114,22 +119,46 @@ public class NetworkServerInjector extends PlayerInjector {
|
||||
CollectionGenerator.INSTANCE);
|
||||
|
||||
Object proxyObject = serverInstances.forEnhancer(ex).getDefault(serverClass);
|
||||
|
||||
serverInjection.replaceServerHandler(serverHandler, proxyObject);
|
||||
|
||||
// Inject it now
|
||||
if (proxyObject != null) {
|
||||
copyTo(serverHandler, proxyObject);
|
||||
serverHandlerRef.setValue(proxyObject);
|
||||
} else {
|
||||
throw new RuntimeException(
|
||||
"Cannot hook player: Unable to find a valid constructor for the NetServerHandler object.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copy every field in server handler A to server handler B.
|
||||
* @param source - fields to copy.
|
||||
* @param destination - fields to copy to.
|
||||
*/
|
||||
private void copyTo(Object source, Object destination) {
|
||||
StructureModifier<Object> modifierSource = serverHandlerModifier.withTarget(source);
|
||||
StructureModifier<Object> modifierDest = serverHandlerModifier.withTarget(destination);
|
||||
|
||||
// Copy every field
|
||||
try {
|
||||
for (int i = 0; i < modifierSource.size(); i++) {
|
||||
modifierDest.write(i, modifierSource.read(i));
|
||||
}
|
||||
} catch (FieldAccessException e) {
|
||||
throw new RuntimeException("Unable to copy fields from NetServerHandler.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanupAll() {
|
||||
if (serverHandlerRef != null) {
|
||||
if (serverHandlerRef != null && serverHandlerRef.isCurrentSet()) {
|
||||
copyTo(serverHandlerRef.getValue(), serverHandlerRef.getOldValue());
|
||||
serverHandlerRef.revertValue();
|
||||
}
|
||||
|
||||
serverInjection.revertServerHandler(serverHandler);
|
||||
|
||||
try {
|
||||
if (getNetHandler() != null) {
|
||||
// Restore packet listener
|
||||
|
@ -34,6 +34,7 @@ import net.sf.cglib.proxy.Enhancer;
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
import net.sf.cglib.proxy.MethodProxy;
|
||||
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@ -44,7 +45,6 @@ import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.event.server.PluginDisableEvent;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
|
||||
import com.comphenix.protocol.AsynchronousManager;
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
@ -92,11 +92,14 @@ public final class PacketFilterManager implements ProtocolManager {
|
||||
private Map<Player, PlayerInjector> playerInjection = new HashMap<Player, PlayerInjector>();
|
||||
|
||||
// Player injection type
|
||||
private PlayerInjectHooks playerHook = PlayerInjectHooks.NETWORK_SERVER_OBJECT;
|
||||
private PlayerInjectHooks playerHook = PlayerInjectHooks.NETWORK_HANDLER_FIELDS;
|
||||
|
||||
// Packet injection
|
||||
private PacketInjector packetInjector;
|
||||
|
||||
// Server connection injection
|
||||
private InjectedServerConnection serverInjection;
|
||||
|
||||
// Enabled packet filters
|
||||
private Set<Integer> sendingFilters = Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>());
|
||||
|
||||
@ -122,7 +125,7 @@ public final class PacketFilterManager implements ProtocolManager {
|
||||
/**
|
||||
* Only create instances of this class if protocol lib is disabled.
|
||||
*/
|
||||
public PacketFilterManager(ClassLoader classLoader, BukkitScheduler scheduler, Logger logger) {
|
||||
public PacketFilterManager(ClassLoader classLoader, Server server, Logger logger) {
|
||||
if (logger == null)
|
||||
throw new IllegalArgumentException("logger cannot be NULL.");
|
||||
if (classLoader == null)
|
||||
@ -133,7 +136,8 @@ public final class PacketFilterManager implements ProtocolManager {
|
||||
this.classLoader = classLoader;
|
||||
this.logger = logger;
|
||||
this.packetInjector = new PacketInjector(classLoader, this, connectionLookup);
|
||||
this.asyncFilterManager = new AsyncFilterManager(logger, scheduler, this);
|
||||
this.asyncFilterManager = new AsyncFilterManager(logger, server.getScheduler(), this);
|
||||
this.serverInjection = new InjectedServerConnection(logger, server);
|
||||
} catch (IllegalAccessException e) {
|
||||
logger.log(Level.SEVERE, "Unable to initialize packet injector.", e);
|
||||
}
|
||||
@ -474,7 +478,7 @@ public final class PacketFilterManager implements ProtocolManager {
|
||||
case NETWORK_MANAGER_OBJECT:
|
||||
return new NetworkObjectInjector(player, this, sendingFilters);
|
||||
case NETWORK_SERVER_OBJECT:
|
||||
return new NetworkServerInjector(player, this, sendingFilters);
|
||||
return new NetworkServerInjector(player, this, sendingFilters, serverInjection);
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot construct a player injector.");
|
||||
}
|
||||
@ -714,6 +718,8 @@ public final class PacketFilterManager implements ProtocolManager {
|
||||
if (packetInjector != null)
|
||||
packetInjector.cleanupAll();
|
||||
|
||||
// Remove server handler
|
||||
serverInjection.cleanupAll();
|
||||
hasClosed = true;
|
||||
|
||||
// Remove listeners
|
||||
|
@ -221,6 +221,7 @@ abstract class PlayerInjector {
|
||||
Packet handlePacketRecieved(Packet packet) {
|
||||
// Get the packet ID too
|
||||
Integer id = MinecraftRegistry.getPacketToID().get(packet.getClass());
|
||||
System.out.println(id);
|
||||
|
||||
// Make sure we're listening
|
||||
if (id != null && sendingFilters.contains(id)) {
|
||||
|
@ -0,0 +1,136 @@
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.ForwardingList;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
|
||||
/**
|
||||
* Represents an array list that wraps another list, while automatically replacing one element with another.
|
||||
* <p>
|
||||
* The replaced elements can be recovered.
|
||||
*
|
||||
* @author Kristian
|
||||
* @param <TKey> - type of the elements we're replacing.
|
||||
*/
|
||||
class ReplacedArrayList<TKey> extends ForwardingList<TKey> {
|
||||
private BiMap<TKey, TKey> replaceMap = HashBiMap.create();
|
||||
private List<TKey> underlyingList;
|
||||
|
||||
public ReplacedArrayList(List<TKey> underlyingList) {
|
||||
this.underlyingList = underlyingList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(TKey element) {
|
||||
if (replaceMap.containsKey(element)) {
|
||||
return super.add(replaceMap.get(element));
|
||||
} else {
|
||||
return super.add(element);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, TKey element) {
|
||||
if (replaceMap.containsKey(element)) {
|
||||
super.add(index, replaceMap.get(element));
|
||||
} else {
|
||||
super.add(index, element);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends TKey> collection) {
|
||||
int oldSize = size();
|
||||
|
||||
for (TKey element : collection)
|
||||
add(element);
|
||||
return size() != oldSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends TKey> elements) {
|
||||
int oldSize = size();
|
||||
|
||||
for (TKey element : elements)
|
||||
add(index++, element);
|
||||
return size() != oldSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TKey> delegate() {
|
||||
return underlyingList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a replace rule.
|
||||
* <p>
|
||||
* This automatically replaces every existing element.
|
||||
* @param target - instance to find.
|
||||
* @param replacement - instance to replace with.
|
||||
*/
|
||||
public synchronized void addMapping(TKey target, TKey replacement) {
|
||||
replaceMap.put(target, replacement);
|
||||
|
||||
// Replace existing elements
|
||||
replaceAll(target, replacement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revert the given mapping.
|
||||
* @param target - the instance we replaced.
|
||||
*/
|
||||
public synchronized void removeMapping(TKey target) {
|
||||
// Make sure the mapping exist
|
||||
if (replaceMap.containsKey(target)) {
|
||||
TKey replacement = replaceMap.get(target);
|
||||
replaceMap.remove(target);
|
||||
|
||||
// Revert existing elements
|
||||
replaceAll(replacement, target);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace all instances of the given object.
|
||||
* @param find - object to find.
|
||||
* @param replace - object to replace it with.
|
||||
*/
|
||||
public synchronized void replaceAll(TKey find, TKey replace) {
|
||||
for (int i = 0; i < underlyingList.size(); i++) {
|
||||
if (Objects.equal(underlyingList.get(i), find))
|
||||
underlyingList.set(i, replace);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo all replacements.
|
||||
*/
|
||||
public synchronized void revertAll() {
|
||||
|
||||
// No need to do anything else
|
||||
if (replaceMap.size() < 1)
|
||||
return;
|
||||
|
||||
BiMap<TKey, TKey> inverse = replaceMap.inverse();
|
||||
|
||||
for (int i = 0; i < underlyingList.size(); i++) {
|
||||
TKey replaced = underlyingList.get(i);
|
||||
|
||||
if (inverse.containsKey(replaced)) {
|
||||
underlyingList.set(i, inverse.get(replaced));
|
||||
}
|
||||
}
|
||||
|
||||
replaceMap.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
revertAll();
|
||||
super.finalize();
|
||||
}
|
||||
}
|
@ -142,11 +142,38 @@ public class FuzzyReflection {
|
||||
* @return The first method that satisfies the parameter types.
|
||||
*/
|
||||
public Method getMethodByParameters(String name, Class<?> returnType, Class<?>[] args) {
|
||||
// Find the correct method to call
|
||||
List<Method> methods = getMethodListByParameters(returnType, args);
|
||||
|
||||
if (methods.size() > 0) {
|
||||
return methods.get(0);
|
||||
} else {
|
||||
// That sucks
|
||||
throw new RuntimeException("Unable to find " + name + " in " + source.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a method by looking at the parameter types and return type only.
|
||||
* @param name - potential name of the method. Only used by the error mechanism.
|
||||
* @param returnType - regular expression matching the return type of the method to find.
|
||||
* @param args - regular expressions of the matching parameter types.
|
||||
* @return The first method that satisfies the parameter types.
|
||||
*/
|
||||
public Method getMethodByParameters(String name, String returnTypeRegex, String[] argsRegex) {
|
||||
|
||||
Pattern match = Pattern.compile(returnTypeRegex);
|
||||
Pattern[] argMatch = new Pattern[argsRegex.length];
|
||||
|
||||
for (int i = 0; i < argsRegex.length; i++) {
|
||||
argMatch[i] = Pattern.compile(argsRegex[i]);
|
||||
}
|
||||
|
||||
// Find the correct method to call
|
||||
for (Method method : getMethods()) {
|
||||
if (method.getReturnType().equals(returnType) && Arrays.equals(method.getParameterTypes(), args)) {
|
||||
return method;
|
||||
if (match.matcher(method.getReturnType().getName()).matches()) {
|
||||
if (matchParameters(argMatch, method.getParameterTypes()))
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,6 +181,19 @@ public class FuzzyReflection {
|
||||
throw new RuntimeException("Unable to find " + name + " in " + source.getName());
|
||||
}
|
||||
|
||||
private boolean matchParameters(Pattern[] parameterMatchers, Class<?>[] argTypes) {
|
||||
if (parameterMatchers.length != argTypes.length)
|
||||
throw new IllegalArgumentException("Arrays must have the same cardinality.");
|
||||
|
||||
// Check types against the regular expressions
|
||||
for (int i = 0; i < argTypes.length; i++) {
|
||||
if (!parameterMatchers[i].matcher(argTypes[i].getName()).matches())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves every method that has the given parameter types and return type.
|
||||
* @param returnType - return type of the method to find.
|
||||
@ -195,6 +235,46 @@ public class FuzzyReflection {
|
||||
nameRegex + " in " + source.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the first field with a type equal to or more specific to the given type.
|
||||
* @param name - name the field probably is given. This will only be used in the error message.
|
||||
* @param type - type of the field to find.
|
||||
* @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) {
|
||||
return fields.get(0);
|
||||
} else {
|
||||
// Looks like we're outdated. Too bad.
|
||||
throw new RuntimeException(String.format("Unable to find a field %s with the type %s in %s",
|
||||
name, type.getName(), source.getName())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves every field with a type equal to or more specific to the given type.
|
||||
* @param type - type of the fields to find.
|
||||
* @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
|
||||
for (Field field : getFields()) {
|
||||
// A assignable from B -> B instanceOf A
|
||||
if (type.isAssignableFrom(field.getType())) {
|
||||
fields.add(field);
|
||||
}
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a field by type.
|
||||
* <p>
|
||||
|
@ -155,6 +155,13 @@ public class VolatileField {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not we'll need to revert the value.
|
||||
*/
|
||||
public boolean isCurrentSet() {
|
||||
return currentSet;
|
||||
}
|
||||
|
||||
private void ensureLoaded() {
|
||||
// Load the value if we haven't already
|
||||
if (!previousLoaded) {
|
||||
|
Loading…
Reference in New Issue
Block a user