Added support for at least Beta 1.8.

This involved a bunch of reflection magic, along with the removal
of every Apache Commons reference, in addition to every internal
Guava class.
This commit is contained in:
Kristian S. Stangeland 2012-09-13 13:57:22 +02:00
parent f372b247f9
commit 035277bdeb
12 changed files with 344 additions and 56 deletions

View File

@ -4,6 +4,7 @@ import java.lang.reflect.InvocationTargetException;
import java.util.Set;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketListener;
@ -19,20 +20,28 @@ public interface ProtocolManager {
* Retrieves a list of every registered packet listener.
* @return Every registered packet listener.
*/
public abstract ImmutableSet<PacketListener> getPacketListeners();
public ImmutableSet<PacketListener> getPacketListeners();
/**
* Adds a packet listener.
* @param listener - new packet listener.
*/
public abstract void addPacketListener(PacketListener listener);
public void addPacketListener(PacketListener listener);
/**
* Removes a given packet listener.
* @param listener - the packet listener to remove.
*/
public abstract void removePacketListener(PacketListener listener);
public void removePacketListener(PacketListener listener);
/**
* Removes every listener associated with the given plugin.
* <p>
* Note that this only works for listeners that derive from PacketAdapter.
* @param plugin - the plugin to unload.
*/
public void removePacketAdapters(Plugin plugin);
/**
* Send a packet to the given player.
* @param reciever - the reciever.

View File

@ -2,9 +2,9 @@ package com.comphenix.protocol.events;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.plugin.Plugin;
import com.google.common.base.Joiner;
import com.google.common.collect.Sets;
/**
@ -14,7 +14,7 @@ import com.google.common.collect.Sets;
*/
public abstract class PacketAdapter implements PacketListener {
protected JavaPlugin plugin;
protected Plugin plugin;
protected Set<Integer> packetsID;
protected ConnectionSide connectionSide;
@ -24,7 +24,7 @@ public abstract class PacketAdapter implements PacketListener {
* @param connectionSide - the packet type the listener is looking for.
* @param packets - the packet IDs the listener is looking for.
*/
public PacketAdapter(JavaPlugin plugin, ConnectionSide connectionSide, Integer... packets) {
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, Integer... packets) {
this.plugin = plugin;
this.connectionSide = connectionSide;
this.packetsID = Sets.newHashSet(packets);
@ -50,11 +50,28 @@ public abstract class PacketAdapter implements PacketListener {
return packetsID;
}
/**
* Retrieves the plugin associated with this listener.
* @return The associated plugin.
*/
public Plugin getPlugin() {
return plugin;
}
@Override
public String toString() {
String name = "";
// Try to get the plugin name
try {
name = plugin.getName();
} catch (NoSuchMethodError e) {
name = plugin.toString();
}
// This is used by the error reporter
return String.format("PacketAdapter[plugin=%s, side=%s, packets=%s]",
plugin.getName(), getConnectionSide().name(),
StringUtils.join(packetsID, ", "));
name, getConnectionSide().name(),
Joiner.on(", ").join(packetsID));
}
}

View File

@ -1,6 +1,5 @@
package com.comphenix.protocol.events;
import org.apache.commons.lang.NullArgumentException;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.inventory.ItemStack;
@ -48,7 +47,7 @@ public class PacketContainer {
*/
public PacketContainer(int id, Packet handle, StructureModifier<Object> structure) {
if (handle == null)
throw new NullArgumentException("handle");
throw new IllegalArgumentException("handle cannot be null.");
this.id = id;
this.handle = handle;

View File

@ -4,12 +4,11 @@ import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang.ObjectUtils;
import net.minecraft.server.Packet;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.google.common.base.Objects;
/**
* Static registries in Minecraft.
@ -78,7 +77,7 @@ class MinecraftRegistry {
// Will most likely not be used
for (Map.Entry<Class, Integer> entry : getPacketToID().entrySet()) {
if (ObjectUtils.equals(entry.getValue(), packetID)) {
if (Objects.equal(entry.getValue(), packetID)) {
return entry.getKey();
}
}

View File

@ -2,6 +2,7 @@ package com.comphenix.protocol.injector;
import java.io.DataInputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@ -11,8 +12,10 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import net.minecraft.server.Packet;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.apache.commons.lang.NullArgumentException;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@ -24,9 +27,11 @@ import org.bukkit.plugin.PluginManager;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.ConnectionSide;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
@ -47,6 +52,9 @@ public final class PacketFilterManager implements ProtocolManager {
// Whether or not this class has been closed
private boolean hasClosed;
// The default class loader
private ClassLoader classLoader;
// Error logger
private Logger logger;
@ -55,12 +63,13 @@ public final class PacketFilterManager implements ProtocolManager {
*/
public PacketFilterManager(ClassLoader classLoader, Logger logger) {
if (logger == null)
throw new NullArgumentException("logger");
throw new IllegalArgumentException("logger cannot be NULL.");
if (classLoader == null)
throw new NullArgumentException("classLoader");
throw new IllegalArgumentException("classLoader cannot be NULL.");
try {
// Initialize values
this.classLoader = classLoader;
this.logger = logger;
this.packetInjector = new PacketInjector(classLoader, this, connectionLookup);
} catch (IllegalAccessException e) {
@ -80,7 +89,7 @@ public final class PacketFilterManager implements ProtocolManager {
@Override
public void addPacketListener(PacketListener listener) {
if (listener == null)
throw new NullArgumentException("listener");
throw new IllegalArgumentException("listener cannot be NULL.");
packetListeners.add(listener);
enablePacketFilters(listener.getConnectionSide(),
@ -90,13 +99,29 @@ public final class PacketFilterManager implements ProtocolManager {
@Override
public void removePacketListener(PacketListener listener) {
if (listener == null)
throw new NullArgumentException("listener");
throw new IllegalArgumentException("listener cannot be NULL");
packetListeners.remove(listener);
disablePacketFilters(listener.getConnectionSide(),
listener.getPacketsID());
}
@Override
public void removePacketAdapters(Plugin plugin) {
// Iterate through every packet listener
for (Object listener : packetListeners.toArray()) {
if (listener instanceof PacketAdapter) {
PacketAdapter adapter = (PacketAdapter) listener;
// Remove the listener
if (adapter.getPlugin().equals(plugin)) {
packetListeners.remove(listener);
}
}
}
}
/**
* Invokes the given packet event for every registered listener.
* @param event - the packet event to invoke.
@ -150,7 +175,7 @@ public final class PacketFilterManager implements ProtocolManager {
*/
private void enablePacketFilters(ConnectionSide side, Set<Integer> packets) {
if (side == null)
throw new NullArgumentException("side");
throw new IllegalArgumentException("side cannot be NULL.");
for (int packetID : packets) {
if (side.isForServer())
@ -167,7 +192,7 @@ public final class PacketFilterManager implements ProtocolManager {
*/
private void disablePacketFilters(ConnectionSide side, Set<Integer> packets) {
if (side == null)
throw new NullArgumentException("side");
throw new IllegalArgumentException("side cannot be NULL.");
for (int packetID : packets) {
if (side.isForServer())
@ -185,9 +210,9 @@ public final class PacketFilterManager implements ProtocolManager {
@Override
public void sendServerPacket(Player reciever, PacketContainer packet, boolean filters) throws InvocationTargetException {
if (reciever == null)
throw new NullArgumentException("reciever");
throw new IllegalArgumentException("reciever cannot be NULL.");
if (packet == null)
throw new NullArgumentException("packet");
throw new IllegalArgumentException("packet cannot be NULL.");
getInjector(reciever).sendServerPacket(packet.getHandle(), filters);
}
@ -201,9 +226,9 @@ public final class PacketFilterManager implements ProtocolManager {
public void recieveClientPacket(Player sender, PacketContainer packet, boolean filters) throws IllegalAccessException, InvocationTargetException {
if (sender == null)
throw new NullArgumentException("sender");
throw new IllegalArgumentException("sender cannot be NULL.");
if (packet == null)
throw new NullArgumentException("packet");
throw new IllegalArgumentException("packet cannot be NULL.");
PlayerInjector injector = getInjector(sender);
Packet mcPacket = packet.getHandle();
@ -277,18 +302,90 @@ public final class PacketFilterManager implements ProtocolManager {
* @param plugin - the parent plugin.
*/
public void registerEvents(PluginManager manager, Plugin plugin) {
manager.registerEvents(new Listener() {
try {
manager.registerEvents(new Listener() {
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerJoin(PlayerJoinEvent event) {
injectPlayer(event.getPlayer());
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerQuit(PlayerQuitEvent event) {
uninjectPlayer(event.getPlayer());
}
}, plugin);
} catch (NoSuchMethodError e) {
// Oh wow! We're running on 1.0.0 or older.
registerOld(manager, plugin);
}
}
// Yes, this is crazy.
@SuppressWarnings({ "unchecked", "rawtypes" })
private void registerOld(PluginManager manager, Plugin plugin) {
try {
ClassLoader loader = manager.getClass().getClassLoader();
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerJoin(PlayerJoinEvent event) {
injectPlayer(event.getPlayer());
}
// The different enums we are going to need
Class eventTypes = loader.loadClass("org.bukkit.event.Event$Type");
Class eventPriority = loader.loadClass("org.bukkit.event.Event$Priority");
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerQuit(PlayerQuitEvent event) {
uninjectPlayer(event.getPlayer());
}
}, plugin);
// Get the priority
Object priorityNormal = Enum.valueOf(eventPriority, "Normal");
// Get event types
Object playerJoinType = Enum.valueOf(eventTypes, "PLAYER_JOIN");
Object playerQuitType = Enum.valueOf(eventTypes, "PLAYER_QUIT");
// The player listener! Good times.
Class<?> playerListener = loader.loadClass("org.bukkit.event.player.PlayerListener");
// Find the register event method
Method registerEvent = FuzzyReflection.fromObject(manager).getMethodByParameters("registerEvent",
eventTypes, Listener.class, eventPriority, Plugin.class);
Enhancer ex = new Enhancer();
ex.setSuperclass(playerListener);
ex.setClassLoader(classLoader);
ex.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// Must have a parameter
if (args.length == 1) {
Object event = args[0];
// Check for the correct event
if (event instanceof PlayerJoinEvent)
injectPlayer(((PlayerJoinEvent) event).getPlayer());
else if (event instanceof PlayerQuitEvent)
injectPlayer(((PlayerQuitEvent) event).getPlayer());
}
return null;
}
});
// Create our listener
Object proxy = ex.create();
registerEvent.invoke(manager, playerJoinType, proxy, priorityNormal, plugin);
registerEvent.invoke(manager, playerQuitType, proxy, priorityNormal, plugin);
// A lot can go wrong
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
private void uninjectPlayer(Player player) {

View File

@ -15,6 +15,7 @@ import net.sf.cglib.proxy.Enhancer;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
/**
@ -49,10 +50,10 @@ class PacketInjector {
private void initialize() throws IllegalAccessException {
if (intHashMap == null) {
// We're looking for the first static field with a Minecraft-object. This should be a IntHashMap.
Field intHashMapField = FuzzyReflection.fromClass(Packet.class).getFieldByType(FuzzyReflection.MINECRAFT_OBJECT);
Field intHashMapField = FuzzyReflection.fromClass(Packet.class, true).getFieldByType(FuzzyReflection.MINECRAFT_OBJECT);
try {
intHashMap = intHashMapField.get(null);
intHashMap = FieldUtils.readField(intHashMapField, (Object) null, true);
} catch (IllegalArgumentException e) {
throw new RuntimeException("Minecraft is incompatible.", e);
}

View File

@ -2,10 +2,9 @@ package com.comphenix.protocol.injector;
import java.io.DataInputStream;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.WeakHashMap;
import org.apache.commons.lang.ArrayUtils;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
@ -55,7 +54,7 @@ class ReadPacketModifier implements MethodInterceptor {
// Is this a readPacketData method?
if (returnValue == null &&
ArrayUtils.isEquals(method.getParameterTypes(), parameters)) {
Arrays.equals(method.getParameterTypes(), parameters)) {
// We need this in order to get the correct player
DataInputStream input = (DataInputStream) args[0];

View File

@ -6,11 +6,9 @@ import java.util.*;
import javax.annotation.Nullable;
import org.apache.commons.lang.ArrayUtils;
import com.google.common.base.Defaults;
import com.google.common.base.Function;
import com.google.gson.internal.Primitives;
import com.google.common.base.Objects;
/**
* Used to construct default instances of any type.
@ -91,7 +89,7 @@ public class DefaultInstances {
// Note that we don't allow recursive types - that is, types that
// require itself in the constructor.
if (types.length < lastCount) {
if (!ArrayUtils.contains(types, type)) {
if (!contains(types, type)) {
minimum = candidate;
lastCount = types.length;
@ -124,6 +122,15 @@ public class DefaultInstances {
return null;
}
private static <T> boolean contains(T[] elements, T elementToFind) {
// Search for the given element in the array
for (T element : elements) {
if (Objects.equal(elementToFind, element))
return true;
}
return false;
}
/**
* Provides constructors for primtive types, wrappers, arrays and strings.
* @author Kristian
@ -133,10 +140,10 @@ public class DefaultInstances {
@Override
public Object apply(@Nullable Class<?> type) {
if (Primitives.isPrimitive(type)) {
if (PrimitiveUtils.isPrimitive(type)) {
return Defaults.defaultValue(type);
} else if (Primitives.isWrapperType(type)) {
return Defaults.defaultValue(Primitives.unwrap(type));
} else if (PrimitiveUtils.isWrapperType(type)) {
return Defaults.defaultValue(PrimitiveUtils.unwrap(type));
} else if (type.isArray()) {
Class<?> arrayType = type.getComponentType();
return Array.newInstance(arrayType, 0);

View File

@ -21,9 +21,9 @@ import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Iterator;
import org.apache.commons.lang.ClassUtils;
import java.util.List;
/**
* Utilities for working with fields by reflection. Adapted and refactored from
@ -119,11 +119,11 @@ public class FieldUtils {
}
}
// check the public interface case. This must be manually searched for
// incase there is a public supersuperclass field hidden by a
// in case there is a public supersuperclass field hidden by a
// private/package
// superclass field.
Field match = null;
for (Iterator intf = ClassUtils.getAllInterfaces(cls).iterator(); intf.hasNext();) {
for (Iterator intf = getAllInterfaces(cls).iterator(); intf.hasNext();) {
try {
Field test = ((Class) intf.next()).getField(fieldName);
if (match != null) {
@ -138,6 +138,44 @@ public class FieldUtils {
}
return match;
}
/**
* <p>Gets a <code>List</code> of all interfaces implemented by the given
* class and its superclasses.</p>
*
* <p>The order is determined by looking through each interface in turn as
* declared in the source file and following its hierarchy up. Then each
* superclass is considered in the same way. Later duplicates are ignored,
* so the order is maintained.</p>
*
* @param cls the class to look up, may be <code>null</code>
* @return the <code>List</code> of interfaces in order,
* <code>null</code> if null input
*/
private static List getAllInterfaces(Class cls) {
if (cls == null) {
return null;
}
List<Class> list = new ArrayList<Class>();
while (cls != null) {
Class[] interfaces = cls.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
if (list.contains(interfaces[i]) == false) {
list.add(interfaces[i]);
}
List superInterfaces = getAllInterfaces(interfaces[i]);
for (Iterator it = superInterfaces.iterator(); it.hasNext();) {
Class intface = (Class) it.next();
if (list.contains(intface) == false) {
list.add(intface);
}
}
}
cls = cls.getSuperclass();
}
return list;
}
/**
* Read an accessible static Field.

View File

@ -2,12 +2,11 @@ package com.comphenix.protocol.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.regex.Pattern;
import org.apache.commons.lang.ArrayUtils;
/**
* Retrieves fields and methods by signature, not just name.
*
@ -106,7 +105,7 @@ public class FuzzyReflection {
// Find the correct method to call
for (Method method : getMethods()) {
if (ArrayUtils.isEquals(method.getParameterTypes(), args)) {
if (Arrays.equals(method.getParameterTypes(), args)) {
return method;
}
}

View File

@ -0,0 +1,124 @@
package com.comphenix.protocol.reflect;
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Contains static utility methods pertaining to primitive types and their
* corresponding wrapper types.
*
* @author Kevin Bourrillion
*/
public final class PrimitiveUtils {
private PrimitiveUtils() {
}
/** A map from primitive types to their corresponding wrapper types. */
private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_WRAPPER_TYPE;
/** A map from wrapper types to their corresponding primitive types. */
private static final Map<Class<?>, Class<?>> WRAPPER_TO_PRIMITIVE_TYPE;
// Sad that we can't use a BiMap. :(
static {
Map<Class<?>, Class<?>> primToWrap = new HashMap<Class<?>, Class<?>>(16);
Map<Class<?>, Class<?>> wrapToPrim = new HashMap<Class<?>, Class<?>>(16);
add(primToWrap, wrapToPrim, boolean.class, Boolean.class);
add(primToWrap, wrapToPrim, byte.class, Byte.class);
add(primToWrap, wrapToPrim, char.class, Character.class);
add(primToWrap, wrapToPrim, double.class, Double.class);
add(primToWrap, wrapToPrim, float.class, Float.class);
add(primToWrap, wrapToPrim, int.class, Integer.class);
add(primToWrap, wrapToPrim, long.class, Long.class);
add(primToWrap, wrapToPrim, short.class, Short.class);
add(primToWrap, wrapToPrim, void.class, Void.class);
PRIMITIVE_TO_WRAPPER_TYPE = Collections.unmodifiableMap(primToWrap);
WRAPPER_TO_PRIMITIVE_TYPE = Collections.unmodifiableMap(wrapToPrim);
}
private static void add(Map<Class<?>, Class<?>> forward,
Map<Class<?>, Class<?>> backward, Class<?> key, Class<?> value) {
forward.put(key, value);
backward.put(value, key);
}
/**
* Returns true if this type is a primitive.
*/
public static boolean isPrimitive(Type type) {
return PRIMITIVE_TO_WRAPPER_TYPE.containsKey(type);
}
/**
* Returns {@code true} if {@code type} is one of the nine primitive-wrapper
* types, such as {@link Integer}.
*
* @see Class#isPrimitive
*/
public static boolean isWrapperType(Type type) {
return WRAPPER_TO_PRIMITIVE_TYPE.containsKey(checkNotNull(type));
}
/**
* Returns the corresponding wrapper type of {@code type} if it is a
* primitive type; otherwise returns {@code type} itself. Idempotent.
*
* <pre>
* wrap(int.class) == Integer.class
* wrap(Integer.class) == Integer.class
* wrap(String.class) == String.class
* </pre>
*/
public static <T> Class<T> wrap(Class<T> type) {
// cast is safe: long.class and Long.class are both of type Class<Long>
@SuppressWarnings("unchecked")
Class<T> wrapped = (Class<T>) PRIMITIVE_TO_WRAPPER_TYPE
.get(checkNotNull(type));
return (wrapped == null) ? type : wrapped;
}
/**
* Returns the corresponding primitive type of {@code type} if it is a
* wrapper type; otherwise returns {@code type} itself. Idempotent.
*
* <pre>
* unwrap(Integer.class) == int.class
* unwrap(int.class) == int.class
* unwrap(String.class) == String.class
* </pre>
*/
public static <T> Class<T> unwrap(Class<T> type) {
// cast is safe: long.class and Long.class are both of type Class<Long>
@SuppressWarnings("unchecked")
Class<T> unwrapped = (Class<T>) WRAPPER_TO_PRIMITIVE_TYPE
.get(checkNotNull(type));
return (unwrapped == null) ? type : unwrapped;
}
public static <T> T checkNotNull(T obj) {
if (obj == null) {
throw new NullPointerException();
}
return obj;
}
}

View File

@ -11,7 +11,6 @@ import java.util.Set;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.gson.internal.Primitives;
import net.minecraft.server.Packet;
@ -287,7 +286,7 @@ public class StructureModifier<TField> {
Class<?> type = field.getType();
// First, ignore primitive fields
if (!Primitives.isPrimitive(type)) {
if (!PrimitiveUtils.isPrimitive(type)) {
// Next, see if we actually can generate a default value
if (DefaultInstances.getDefault(type) != null) {
// If so, require it