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.
|
* Represents an abstract class loader that can only retrieve classes by their canonical name.
|
||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
abstract class ClassSource {
|
public abstract class ClassSource {
|
||||||
/**
|
/**
|
||||||
* Construct a class source from the current class loader.
|
* Construct a class source from the default class loader.
|
||||||
* @return A package source.
|
* @return A class source.
|
||||||
*/
|
*/
|
||||||
public static ClassSource fromClassLoader() {
|
public static ClassSource fromClassLoader() {
|
||||||
return fromClassLoader(ClassSource.class.getClassLoader());
|
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.
|
* Construct a class source from the given class loader.
|
||||||
* @param loader - the class loader.
|
* @param loader - the class loader.
|
||||||
* @return The corresponding package source.
|
* @return The corresponding class source.
|
||||||
*/
|
*/
|
||||||
public static ClassSource fromClassLoader(final ClassLoader loader) {
|
public static ClassSource fromClassLoader(final ClassLoader loader) {
|
||||||
return new ClassSource() {
|
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.
|
* Retrieve a class by name.
|
||||||
* @param canonicalName - the full canonical name of the class.
|
* @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.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.bukkit.plugin.Plugin;
|
|
||||||
|
|
||||||
import net.minecraft.util.io.netty.buffer.ByteBuf;
|
import net.minecraft.util.io.netty.buffer.ByteBuf;
|
||||||
import net.minecraft.util.io.netty.buffer.UnpooledByteBufAllocator;
|
import net.minecraft.util.io.netty.buffer.UnpooledByteBufAllocator;
|
||||||
import net.minecraft.util.io.netty.channel.ChannelHandlerContext;
|
import net.minecraft.util.io.netty.channel.ChannelHandlerContext;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.comphenix.protocol.wrappers;
|
package com.comphenix.protocol.wrappers;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -9,15 +10,75 @@ import java.util.Set;
|
|||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
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.AbstractFuzzyMatcher;
|
||||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers;
|
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.
|
* Wrap a GNU Trove Collection class with an equivalent Java Collection class.
|
||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
public class TroveWrapper {
|
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.
|
* Retrieve a Java wrapper for the corresponding Trove map.
|
||||||
@ -61,19 +122,46 @@ public class TroveWrapper {
|
|||||||
return result;
|
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) {
|
private static Object getDecorated(@Nonnull Object trove) {
|
||||||
if (trove == null)
|
if (trove == null)
|
||||||
throw new IllegalArgumentException("trove instance cannot be non-null.");
|
throw new IllegalArgumentException("trove instance cannot be non-null.");
|
||||||
|
|
||||||
AbstractFuzzyMatcher<Class<?>> match = FuzzyMatchers.matchSuper(trove.getClass());
|
AbstractFuzzyMatcher<Class<?>> match = FuzzyMatchers.matchSuper(trove.getClass());
|
||||||
|
Class<?> decorators = null;
|
||||||
|
|
||||||
if (decorators == null) {
|
|
||||||
try {
|
try {
|
||||||
// Attempt to get decorator class
|
// Attempt to get decorator class
|
||||||
decorators = TroveWrapper.class.getClassLoader().loadClass("gnu.trove.TDecorators");
|
decorators = getClassSource(trove.getClass()).loadClass("TDecorators");
|
||||||
} catch (ClassNotFoundException e) {
|
} 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
|
// 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.FieldAccessException;
|
||||||
import com.comphenix.protocol.reflect.FieldUtils;
|
import com.comphenix.protocol.reflect.FieldUtils;
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
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.utility.MinecraftReflection;
|
||||||
import com.comphenix.protocol.wrappers.collection.ConvertedMap;
|
import com.comphenix.protocol.wrappers.collection.ConvertedMap;
|
||||||
import com.google.common.base.Function;
|
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;
|
private static Map<Class<?>, Integer> TYPE_MAP;
|
||||||
|
|
||||||
|
// Accessors
|
||||||
|
private static FieldAccessor TYPE_MAP_ACCESSOR;
|
||||||
|
private static FieldAccessor VALUE_MAP_ACCESSOR;
|
||||||
|
|
||||||
// Fields
|
// Fields
|
||||||
private static Field VALUE_MAP_FIELD;
|
|
||||||
private static Field READ_WRITE_LOCK_FIELD;
|
private static Field READ_WRITE_LOCK_FIELD;
|
||||||
private static Field ENTITY_FIELD;
|
private static Field ENTITY_FIELD;
|
||||||
|
|
||||||
@ -509,13 +515,8 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected Map<Integer, Object> getWatchableObjectMap() throws FieldAccessException {
|
protected Map<Integer, Object> getWatchableObjectMap() throws FieldAccessException {
|
||||||
if (watchableObjects == null) {
|
if (watchableObjects == null)
|
||||||
try {
|
watchableObjects = (Map<Integer, Object>) VALUE_MAP_ACCESSOR.get(handle);
|
||||||
watchableObjects = (Map<Integer, Object>) FieldUtils.readField(VALUE_MAP_FIELD, handle, true);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new FieldAccessException("Cannot read watchable object field.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return watchableObjects;
|
return watchableObjects;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,17 +562,17 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||||||
for (Field lookup : fuzzy.getFieldListByType(Map.class)) {
|
for (Field lookup : fuzzy.getFieldListByType(Map.class)) {
|
||||||
if (Modifier.isStatic(lookup.getModifiers())) {
|
if (Modifier.isStatic(lookup.getModifiers())) {
|
||||||
// This must be the type map
|
// This must be the type map
|
||||||
try {
|
TYPE_MAP_ACCESSOR = Accessors.getFieldAccessor(lookup, true);
|
||||||
TYPE_MAP = (Map<Class<?>, Integer>) FieldUtils.readStaticField(lookup, true);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new FieldAccessException("Cannot access type map field.", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// If not, then we're probably dealing with the value map
|
// 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 {
|
try {
|
||||||
READ_WRITE_LOCK_FIELD = fuzzy.getFieldByType("readWriteLock", ReadWriteLock.class);
|
READ_WRITE_LOCK_FIELD = fuzzy.getFieldByType("readWriteLock", ReadWriteLock.class);
|
||||||
@ -587,6 +588,32 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||||||
initializeMethods(fuzzy);
|
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) {
|
private static void initializeMethods(FuzzyReflection fuzzy) {
|
||||||
List<Method> candidates = fuzzy.getMethodListByParameters(Void.TYPE,
|
List<Method> candidates = fuzzy.getMethodListByParameters(Void.TYPE,
|
||||||
new Class<?>[] { int.class, Object.class});
|
new Class<?>[] { int.class, Object.class});
|
||||||
|
In neuem Issue referenzieren
Einen Benutzer sperren