And the PacketConstructor class itself.
Dieser Commit ist enthalten in:
Ursprung
fb15ee1b22
Commit
e5beca4ea5
219
ProtocolLib/src/com/comphenix/protocol/injector/PacketConstructor.java
Normale Datei
219
ProtocolLib/src/com/comphenix/protocol/injector/PacketConstructor.java
Normale Datei
@ -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);
|
||||||
|
}
|
||||||
|
}
|
In neuem Issue referenzieren
Einen Benutzer sperren