Archiviert
13
0

And the PacketConstructor class itself.

Dieser Commit ist enthalten in:
Kristian S. Stangeland 2012-09-17 22:59:41 +02:00
Ursprung fb15ee1b22
Commit e5beca4ea5

Datei anzeigen

@ -0,0 +1,219 @@
package com.comphenix.protocol.injector;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import net.minecraft.server.Packet;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
/**
* A packet constructor that uses an internal Minecraft.
* @author Kristian
*
*/
public class PacketConstructor {
/**
* A packet constructor that automatically converts Bukkit types to their NMS conterpart.
* <p>
* Remember to call withPacket().
*/
public static PacketConstructor DEFAUALT = new PacketConstructor(null);
// The constructor method that's actually responsible for creating the packet
private Constructor<?> constructorMethod;
// The packet ID
private int packetID;
// Used to unwrap Bukkit objects
private List<Unwrapper> unwrappers;
private PacketConstructor(Constructor<?> constructorMethod) {
this.constructorMethod = constructorMethod;
this.unwrappers = Lists.newArrayList((Unwrapper) new BukkitUnwrapper());
}
private PacketConstructor(Constructor<?> constructorMethod, List<Unwrapper> unwrappers) {
this.constructorMethod = constructorMethod;
this.unwrappers = unwrappers;
}
public ImmutableList<Unwrapper> getUnwrappers() {
return ImmutableList.copyOf(unwrappers);
}
/**
* Retrieve the id of the packets this constructor creates.
* @return The ID of the packets this constructor will create.
*/
public int getPacketID() {
return packetID;
}
/**
* Return a copy of the current constructor with a different list of unwrappers.
* @param unwrappers - list of unwrappers that convert Bukkit wrappers into the equivalent NMS classes.
* @return
*/
public PacketConstructor withUnwrappers(List<Unwrapper> unwrappers) {
return new PacketConstructor(constructorMethod, unwrappers);
}
/**
* Create a packet constructor that creates packets using the given types.
* @param id - packet ID.
* @param types - types to create.
* @return A packet constructor with these types.
* @throws IllegalArgumentException If no packet constructor could be created with these types.
*/
public PacketConstructor withPacket(int id, Class<?>[] types) {
for (Unwrapper unwrapper : unwrappers) {
for (int i = 0; i < types.length; i++) {
Class<?> result =unwrapper.unwrapType(types[i]);
// Update type we're searching for
if (result != null) {
types[i] = result;
}
}
}
Class<?> packetType = MinecraftRegistry.getPacketClassFromID(id);
if (packetType == null)
throw new IllegalArgumentException("Could not find a packet by the id " + id);
// Find the correct constructor
for (Constructor<?> constructor : packetType.getConstructors()) {
Class<?>[] params = constructor.getParameterTypes();
if (isCompatible(types, params)) {
// Right, we've found our type
return new PacketConstructor(constructor, unwrappers);
}
}
throw new IllegalArgumentException("No suitable constructor could be found.");
}
/**
* Construct a packet using the special builtin Minecraft constructors.
* @param values - values containing Bukkit wrapped items to pass to Minecraft.
* @return The created packet.
* @throws FieldAccessException Failure due to a security limitation.
* @throws IllegalArgumentException Arguments doesn't match the constructor.
* @throws RuntimeException Minecraft threw an exception.
*/
public PacketContainer createPacket(Object... values) throws FieldAccessException {
try {
Packet nmsPacket = (Packet) constructorMethod.newInstance(values);
return new PacketContainer(packetID, nmsPacket);
} catch (IllegalArgumentException e) {
throw e;
} catch (InstantiationException e) {
throw new FieldAccessException("Cannot construct an abstract packet.", e);
} catch (IllegalAccessException e) {
throw new FieldAccessException("Cannot construct packet due to a security limitation.", e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Minecraft error.", e);
}
}
// Determine if a method with the types 'params' can be called with 'types'
private static boolean isCompatible(Class<?>[] types, Class<?>[] params) {
// Determine if the types are similar
if (params.length == types.length) {
for (int i = 0; i < params.length; i++) {
if (!params[i].isAssignableFrom(types[i])) {
return false;
}
}
}
return true;
}
public static class BukkitUnwrapper implements Unwrapper {
private static Map<Class<?>, Method> cache = new ConcurrentHashMap<Class<?>, Method>();
@Override
public Object unwrapItem(Object wrappedObject) {
Class<?> currentClass = wrappedObject.getClass();
Method cachedMethod = initializeCache(currentClass);
try {
// Retrieve the handle
if (cachedMethod != null)
return cachedMethod.invoke(wrappedObject);
else
return null;
} catch (IllegalArgumentException e) {
// Impossible
return null;
} catch (IllegalAccessException e) {
return null;
} catch (InvocationTargetException e) {
// This is REALLY bad
throw new RuntimeException("Minecraft error.", e);
}
}
@Override
public Class<?> unwrapType(Class<?> type) {
if (type == null)
throw new IllegalArgumentException("type cannot be null.");
Method unwrapped = initializeCache(type);
// Determine the unwrapped type
if (unwrapped != null)
return unwrapped.getReturnType();
else
return null;
}
private Method initializeCache(Class<?> type) {
// See if we're already determined this
if (cache.containsKey(type)) {
// We will never remove from the cache, so this ought to be thread safe
return cache.get(type);
}
try {
Method find = type.getMethod("getHandle");
// It's thread safe, as getMethod should return the same handle
cache.put(type, find);
return find;
} catch (SecurityException e) {
return null;
} catch (NoSuchMethodException e) {
return null;
}
}
}
public static interface Unwrapper {
public Object unwrapItem(Object wrappedObject);
public Class<?> unwrapType(Class<?> type);
}
}