Add support for the latest build of Spigot.
See commit 8b2b731ea5deda5607058849f2ca9ec2e3bf003f in SpigotMC/ Spigot-Server.
Dieser Commit ist enthalten in:
Ursprung
e7273385cf
Commit
74d83b3ed6
@ -0,0 +1,8 @@
|
||||
package com.comphenix.protocol.reflect.accessors;
|
||||
|
||||
public abstract class ReadOnlyFieldAccessor implements FieldAccessor {
|
||||
@Override
|
||||
public final void set(Object instance, Object value) {
|
||||
throw new UnsupportedOperationException("Cannot update the content of a read-only field accessor.");
|
||||
}
|
||||
}
|
@ -6,19 +6,28 @@ import java.util.Map;
|
||||
* Represents an abstract class loader that can only retrieve classes by their canonical name.
|
||||
* @author Kristian
|
||||
*/
|
||||
abstract class ClassSource {
|
||||
public abstract class ClassSource {
|
||||
/**
|
||||
* Construct a class source from the current class loader.
|
||||
* @return A package source.
|
||||
* Construct a class source from the default class loader.
|
||||
* @return A class source.
|
||||
*/
|
||||
public static ClassSource fromClassLoader() {
|
||||
return fromClassLoader(ClassSource.class.getClassLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a class source from the default class loader and package.
|
||||
* @param packageName - the package that is prepended to every lookup.
|
||||
* @return A package source.
|
||||
*/
|
||||
public static ClassSource fromPackage(String packageName) {
|
||||
return fromClassLoader().usingPackage(packageName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a class source from the given class loader.
|
||||
* @param loader - the class loader.
|
||||
* @return The corresponding package source.
|
||||
* @return The corresponding class source.
|
||||
*/
|
||||
public static ClassSource fromClassLoader(final ClassLoader loader) {
|
||||
return new ClassSource() {
|
||||
@ -43,6 +52,57 @@ abstract class ClassSource {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a class source that will retry failed lookups in the given source.
|
||||
* @param other - the other class source.
|
||||
* @return A new class source.
|
||||
*/
|
||||
public ClassSource retry(final ClassSource other) {
|
||||
return new ClassSource() {
|
||||
@Override
|
||||
public Class<?> loadClass(String canonicalName) throws ClassNotFoundException {
|
||||
try {
|
||||
return ClassSource.this.loadClass(canonicalName);
|
||||
} catch (ClassNotFoundException e) {
|
||||
return other.getClass();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a class source that prepends a specific package name to every lookup.
|
||||
* @param packageName - the package name to prepend.
|
||||
* @return The class source.
|
||||
*/
|
||||
public ClassSource usingPackage(final String packageName) {
|
||||
return new ClassSource() {
|
||||
@Override
|
||||
public Class<?> loadClass(String canonicalName) throws ClassNotFoundException {
|
||||
return ClassSource.this.loadClass(append(packageName, canonicalName));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Append to canonical names together.
|
||||
* @param a - the name to the left.
|
||||
* @param b - the name to the right.
|
||||
* @return The full canonical name, with a dot seperator.
|
||||
*/
|
||||
protected static String append(String a, String b) {
|
||||
boolean left = a.endsWith(".");
|
||||
boolean right = b.endsWith(".");
|
||||
|
||||
// Only add a dot if necessary
|
||||
if (left && right)
|
||||
return a.substring(0, a.length() - 1) + b;
|
||||
else if (left != right)
|
||||
return a + b;
|
||||
else
|
||||
return a + "." + b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a class by name.
|
||||
* @param canonicalName - the full canonical name of the class.
|
||||
|
@ -5,8 +5,6 @@ import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import net.minecraft.util.io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.util.io.netty.buffer.UnpooledByteBufAllocator;
|
||||
import net.minecraft.util.io.netty.channel.ChannelHandlerContext;
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
@ -9,15 +10,75 @@ import java.util.Set;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.ReadOnlyFieldAccessor;
|
||||
import com.comphenix.protocol.reflect.fuzzy.AbstractFuzzyMatcher;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers;
|
||||
import com.comphenix.protocol.utility.ClassSource;
|
||||
|
||||
/**
|
||||
* Wrap a GNU Trove Collection class with an equivalent Java Collection class.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class TroveWrapper {
|
||||
private volatile static Class<?> decorators;
|
||||
private static final String[] TROVE_LOCATIONS = new String[] {
|
||||
"net.minecraft.util.gnu.trove", // For Minecraft 1.7.2
|
||||
"gnu.trove" // For an old version of Spigot
|
||||
};
|
||||
|
||||
// The different Trove versions
|
||||
private static final ClassSource[] TROVE_SOURCES = {
|
||||
ClassSource.fromPackage(TROVE_LOCATIONS[0]),
|
||||
ClassSource.fromPackage(TROVE_LOCATIONS[1])
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve a read-only field accessor that automatically wraps the underlying Trove instance.
|
||||
* @param accessor - the accessor.
|
||||
* @return The read only accessor.
|
||||
*/
|
||||
public static ReadOnlyFieldAccessor wrapMapField(final FieldAccessor accessor) {
|
||||
return new ReadOnlyFieldAccessor() {
|
||||
public Object get(Object instance) {
|
||||
return getDecoratedMap(accessor.get(instance));
|
||||
}
|
||||
public Field getField() {
|
||||
return accessor.getField();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read-only field accessor that automatically wraps the underlying Trove instance.
|
||||
* @param accessor - the accessor.
|
||||
* @return The read only accessor.
|
||||
*/
|
||||
public static ReadOnlyFieldAccessor wrapSetField(final FieldAccessor accessor) {
|
||||
return new ReadOnlyFieldAccessor() {
|
||||
public Object get(Object instance) {
|
||||
return getDecoratedSet(accessor.get(instance));
|
||||
}
|
||||
public Field getField() {
|
||||
return accessor.getField();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read-only field accessor that automatically wraps the underlying Trove instance.
|
||||
* @param accessor - the accessor.
|
||||
* @return The read only accessor.
|
||||
*/
|
||||
public static ReadOnlyFieldAccessor wrapListField(final FieldAccessor accessor) {
|
||||
return new ReadOnlyFieldAccessor() {
|
||||
public Object get(Object instance) {
|
||||
return getDecoratedList(accessor.get(instance));
|
||||
}
|
||||
public Field getField() {
|
||||
return accessor.getField();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a Java wrapper for the corresponding Trove map.
|
||||
@ -61,19 +122,46 @@ public class TroveWrapper {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given class is found within gnu.trove.
|
||||
* @param clazz - the clazz.
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
public static boolean isTroveClass(Class<?> clazz) {
|
||||
return getClassSource(clazz) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the correct class source from the given class.
|
||||
* @param clazz - the class source.
|
||||
* @return The class source, or NULL if not found.
|
||||
*/
|
||||
private static ClassSource getClassSource(Class<?> clazz) {
|
||||
for (int i = 0; i < TROVE_LOCATIONS.length; i++) {
|
||||
if (clazz.getCanonicalName().startsWith(TROVE_LOCATIONS[i])) {
|
||||
return TROVE_SOURCES[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the corresponding decorator.
|
||||
* @param trove - the trove class.
|
||||
* @return The wrapped trove class.
|
||||
*/
|
||||
private static Object getDecorated(@Nonnull Object trove) {
|
||||
if (trove == null)
|
||||
throw new IllegalArgumentException("trove instance cannot be non-null.");
|
||||
|
||||
AbstractFuzzyMatcher<Class<?>> match = FuzzyMatchers.matchSuper(trove.getClass());
|
||||
Class<?> decorators = null;
|
||||
|
||||
if (decorators == null) {
|
||||
try {
|
||||
// Attempt to get decorator class
|
||||
decorators = TroveWrapper.class.getClassLoader().loadClass("gnu.trove.TDecorators");
|
||||
decorators = getClassSource(trove.getClass()).loadClass("TDecorators");
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalStateException("Cannot find TDecorators in Gnu Trove.", e);
|
||||
}
|
||||
throw new IllegalStateException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
// Find an appropriate wrapper method in TDecorators
|
||||
|
@ -41,6 +41,9 @@ import com.comphenix.protocol.injector.BukkitUnwrapper;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.ReadOnlyFieldAccessor;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.collection.ConvertedMap;
|
||||
import com.google.common.base.Function;
|
||||
@ -58,8 +61,11 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
*/
|
||||
private static Map<Class<?>, Integer> TYPE_MAP;
|
||||
|
||||
// Accessors
|
||||
private static FieldAccessor TYPE_MAP_ACCESSOR;
|
||||
private static FieldAccessor VALUE_MAP_ACCESSOR;
|
||||
|
||||
// Fields
|
||||
private static Field VALUE_MAP_FIELD;
|
||||
private static Field READ_WRITE_LOCK_FIELD;
|
||||
private static Field ENTITY_FIELD;
|
||||
|
||||
@ -509,13 +515,8 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Map<Integer, Object> getWatchableObjectMap() throws FieldAccessException {
|
||||
if (watchableObjects == null) {
|
||||
try {
|
||||
watchableObjects = (Map<Integer, Object>) FieldUtils.readField(VALUE_MAP_FIELD, handle, true);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new FieldAccessException("Cannot read watchable object field.", e);
|
||||
}
|
||||
}
|
||||
if (watchableObjects == null)
|
||||
watchableObjects = (Map<Integer, Object>) VALUE_MAP_ACCESSOR.get(handle);
|
||||
return watchableObjects;
|
||||
}
|
||||
|
||||
@ -561,17 +562,17 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
for (Field lookup : fuzzy.getFieldListByType(Map.class)) {
|
||||
if (Modifier.isStatic(lookup.getModifiers())) {
|
||||
// This must be the type map
|
||||
try {
|
||||
TYPE_MAP = (Map<Class<?>, Integer>) FieldUtils.readStaticField(lookup, true);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new FieldAccessException("Cannot access type map field.", e);
|
||||
}
|
||||
|
||||
TYPE_MAP_ACCESSOR = Accessors.getFieldAccessor(lookup, true);
|
||||
} else {
|
||||
// If not, then we're probably dealing with the value map
|
||||
VALUE_MAP_FIELD = lookup;
|
||||
VALUE_MAP_ACCESSOR = Accessors.getFieldAccessor(lookup, true);
|
||||
}
|
||||
}
|
||||
// Spigot workaround
|
||||
initializeSpigot(fuzzy);
|
||||
|
||||
// Initialize static type type
|
||||
TYPE_MAP = (Map<Class<?>, Integer>) TYPE_MAP_ACCESSOR.get(null);
|
||||
|
||||
try {
|
||||
READ_WRITE_LOCK_FIELD = fuzzy.getFieldByType("readWriteLock", ReadWriteLock.class);
|
||||
@ -587,6 +588,32 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
||||
initializeMethods(fuzzy);
|
||||
}
|
||||
|
||||
private static void initializeSpigot(FuzzyReflection fuzzy) {
|
||||
// See if the workaround is needed
|
||||
if (TYPE_MAP_ACCESSOR != null && VALUE_MAP_ACCESSOR != null)
|
||||
return;
|
||||
|
||||
for (Field lookup : fuzzy.getFields()) {
|
||||
final Class<?> type = lookup.getType();
|
||||
|
||||
if (TroveWrapper.isTroveClass(type)) {
|
||||
// Create a wrapper accessor
|
||||
final ReadOnlyFieldAccessor accessor = TroveWrapper.wrapMapField(Accessors.getFieldAccessor(lookup, true));
|
||||
|
||||
if (Modifier.isStatic(lookup.getModifiers())) {
|
||||
TYPE_MAP_ACCESSOR = accessor;
|
||||
} else {
|
||||
VALUE_MAP_ACCESSOR = accessor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TYPE_MAP_ACCESSOR == null)
|
||||
throw new IllegalArgumentException("Unable to find static type map.");
|
||||
if (VALUE_MAP_ACCESSOR == null)
|
||||
throw new IllegalArgumentException("Unable to find static value map.");
|
||||
}
|
||||
|
||||
private static void initializeMethods(FuzzyReflection fuzzy) {
|
||||
List<Method> candidates = fuzzy.getMethodListByParameters(Void.TYPE,
|
||||
new Class<?>[] { int.class, Object.class});
|
||||
|
In neuem Issue referenzieren
Einen Benutzer sperren