Archiviert
13
0

Add support for the latest build of Spigot.

See commit 8b2b731ea5deda5607058849f2ca9ec2e3bf003f in SpigotMC/
Spigot-Server.
Dieser Commit ist enthalten in:
Kristian S. Stangeland 2013-12-13 20:35:00 +01:00
Ursprung e7273385cf
Commit 74d83b3ed6
5 geänderte Dateien mit 210 neuen und 29 gelöschten Zeilen

Datei anzeigen

@ -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.");
}
}

Datei anzeigen

@ -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.

Datei anzeigen

@ -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;

Datei anzeigen

@ -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

Datei anzeigen

@ -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});