mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-11-24 11:36:51 +01:00
Adding support for Spigot MCPC 1.2.5.
Very buggy indeed.
This commit is contained in:
parent
505226f8ad
commit
82bb7a7c43
@ -270,7 +270,7 @@ public class CommandFilter extends CommandBase {
|
||||
}
|
||||
|
||||
/*
|
||||
* Description: Adds or removes a simple packet listener.
|
||||
* Description: Adds or removes a simple packet filter.
|
||||
Usage: /<command> add|remove name [packet IDs]
|
||||
*/
|
||||
@Override
|
||||
|
@ -340,6 +340,8 @@ class CommandPacket extends CommandBase {
|
||||
supported.addAll(Packets.Client.getSupported());
|
||||
else if (side.isForServer())
|
||||
supported.addAll(Packets.Server.getSupported());
|
||||
|
||||
System.out.println("Supported for " + side + ": " + supported);
|
||||
return supported;
|
||||
}
|
||||
|
||||
|
@ -229,7 +229,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
||||
reporter.reportWarning(this, "Cannot load server and client packet list.", e);
|
||||
}
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
} catch (FieldAccessException e) {
|
||||
reporter.reportWarning(this, "Unable to initialize packet injector.", e);
|
||||
}
|
||||
}
|
||||
@ -757,7 +757,14 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
||||
if (!MinecraftReflection.isPacketClass(packet))
|
||||
throw new IllegalArgumentException("The given object " + packet + " is not a packet.");
|
||||
|
||||
return PacketRegistry.getPacketToID().get(packet.getClass());
|
||||
Integer id = PacketRegistry.getPacketToID().get(packet.getClass());
|
||||
|
||||
if (id != null) {
|
||||
return id;
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Unable to find associated packet of " + packet + ": Lookup returned NULL.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -8,6 +8,7 @@ import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.PacketFilterManager;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
/**
|
||||
@ -100,9 +101,9 @@ public class PacketInjectorBuilder {
|
||||
* <p>
|
||||
* Note that any non-null builder parameters must be set.
|
||||
* @return The created injector.
|
||||
* @throws IllegalAccessException If anything goes wrong in terms of reflection.
|
||||
* @throws FieldAccessException If anything goes wrong in terms of reflection.
|
||||
*/
|
||||
public PacketInjector buildInjector() throws IllegalAccessException {
|
||||
public PacketInjector buildInjector() throws FieldAccessException {
|
||||
initializeDefaults();
|
||||
return new ProxyPacketInjector(classLoader, invoker, playerInjection, reporter);
|
||||
}
|
||||
|
@ -25,10 +25,15 @@ import java.util.Set;
|
||||
|
||||
import net.sf.cglib.proxy.Factory;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyClassContract;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.TroveWrapper;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
@ -39,6 +44,8 @@ import com.google.common.collect.ImmutableSet;
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class PacketRegistry {
|
||||
private static final int MIN_SERVER_PACKETS = 5;
|
||||
private static final int MIN_CLIENT_PACKETS = 5;
|
||||
|
||||
// Fuzzy reflection
|
||||
private static FuzzyReflection packetRegistry;
|
||||
@ -67,6 +74,14 @@ public class PacketRegistry {
|
||||
try {
|
||||
Field packetsField = getPacketRegistry().getFieldByType("packetsField", Map.class);
|
||||
packetToID = (Map<Class, Integer>) FieldUtils.readStaticField(packetsField, true);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Spigot 1.2.5 MCPC workaround
|
||||
try {
|
||||
packetToID = getSpigotWrapper();
|
||||
} catch (Exception e2) {
|
||||
// Very bad indeed
|
||||
throw new IllegalArgumentException(e.getMessage() + "; Spigot workaround failed.", e2);
|
||||
}
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Unable to retrieve the packetClassToIdMap", e);
|
||||
@ -76,6 +91,40 @@ public class PacketRegistry {
|
||||
return packetToID;
|
||||
}
|
||||
|
||||
private static Map<Class, Integer> getSpigotWrapper() throws IllegalAccessException {
|
||||
// If it talks like a duck, etc.
|
||||
// Perhaps it would be nice to have a proper duck typing library as well
|
||||
FuzzyClassContract mapLike = FuzzyClassContract.newBuilder().
|
||||
method(FuzzyMethodContract.newBuilder().
|
||||
nameExact("size").returnTypeExact(int.class)).
|
||||
method(FuzzyMethodContract.newBuilder().
|
||||
nameExact("put").parameterCount(2)).
|
||||
method(FuzzyMethodContract.newBuilder().
|
||||
nameExact("get").parameterCount(1)).
|
||||
build();
|
||||
|
||||
Field packetsField = getPacketRegistry().getField(
|
||||
FuzzyFieldContract.newBuilder().typeMatches(mapLike).build());
|
||||
Object troveMap = FieldUtils.readStaticField(packetsField, true);
|
||||
|
||||
// Check for stupid no_entry_values
|
||||
try {
|
||||
Field field = FieldUtils.getField(troveMap.getClass(), "no_entry_value", true);
|
||||
Integer value = (Integer) FieldUtils.readField(field, troveMap, true);
|
||||
|
||||
if (value >= 0 && value < 256) {
|
||||
// Someone forgot to set the no entry value. Let's help them.
|
||||
FieldUtils.writeField(field, troveMap, -1);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Whatever
|
||||
ProtocolLibrary.getErrorReporter().reportWarning(PacketRegistry.class, "Unable to correct no entry value.", e);
|
||||
}
|
||||
|
||||
// We'll assume this a Trove map
|
||||
return TroveWrapper.getDecoratedMap(troveMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the cached fuzzy reflection instance allowing access to the packet registry.
|
||||
* @return Reflected packet registry.
|
||||
@ -109,6 +158,10 @@ public class PacketRegistry {
|
||||
*/
|
||||
public static Set<Integer> getServerPackets() throws FieldAccessException {
|
||||
initializeSets();
|
||||
|
||||
// Sanity check. This is impossible!
|
||||
if (serverPackets != null && serverPackets.size() < MIN_SERVER_PACKETS)
|
||||
throw new FieldAccessException("Server packet list is empty. Seems to be unsupported");
|
||||
return serverPackets;
|
||||
}
|
||||
|
||||
@ -119,6 +172,10 @@ public class PacketRegistry {
|
||||
*/
|
||||
public static Set<Integer> getClientPackets() throws FieldAccessException {
|
||||
initializeSets();
|
||||
|
||||
// As above
|
||||
if (clientPackets != null && clientPackets.size() < MIN_CLIENT_PACKETS)
|
||||
throw new FieldAccessException("Client packet list is empty. Seems to be unsupported");
|
||||
return clientPackets;
|
||||
}
|
||||
|
||||
@ -140,6 +197,14 @@ public class PacketRegistry {
|
||||
serverPackets = ImmutableSet.copyOf(serverPacketsRef);
|
||||
clientPackets = ImmutableSet.copyOf(clientPacketsRef);
|
||||
|
||||
// Check sizes
|
||||
if (serverPackets.size() < MIN_SERVER_PACKETS)
|
||||
ProtocolLibrary.getErrorReporter().reportWarning(
|
||||
PacketRegistry.class, "Too few server packets detected: " + serverPackets.size());
|
||||
if (clientPackets.size() < MIN_CLIENT_PACKETS)
|
||||
ProtocolLibrary.getErrorReporter().reportWarning(
|
||||
PacketRegistry.class, "Too few client packets detected: " + clientPackets.size());
|
||||
|
||||
} else {
|
||||
throw new FieldAccessException("Cannot retrieve packet client/server sets.");
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.MethodInfo;
|
||||
@ -49,6 +50,85 @@ import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
* @author Kristian
|
||||
*/
|
||||
class ProxyPacketInjector implements PacketInjector {
|
||||
/**
|
||||
* Represents a way to update the packet ID to class lookup table.
|
||||
* @author Kristian
|
||||
*/
|
||||
private static interface PacketClassLookup {
|
||||
public void setLookup(int packetID, Class<?> clazz);
|
||||
}
|
||||
|
||||
private static class IntHashMapLookup implements PacketClassLookup {
|
||||
// The "put" method that associates a packet ID with a packet class
|
||||
private Method putMethod;
|
||||
private Object intHashMap;
|
||||
|
||||
public IntHashMapLookup() throws IllegalAccessException {
|
||||
initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLookup(int packetID, Class<?> clazz) {
|
||||
try {
|
||||
putMethod.invoke(intHashMap, packetID, clazz);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new RuntimeException("Illegal argument.", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Cannot access method.", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException("Exception occured in IntHashMap.put.", e);
|
||||
}
|
||||
}
|
||||
|
||||
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(MinecraftReflection.getPacketClass(), true).
|
||||
getFieldByType(MinecraftReflection.getMinecraftObjectRegex());
|
||||
|
||||
try {
|
||||
intHashMap = FieldUtils.readField(intHashMapField, (Object) null, true);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new RuntimeException("Minecraft is incompatible.", e);
|
||||
}
|
||||
|
||||
// Now, get the "put" method.
|
||||
putMethod = FuzzyReflection.fromObject(intHashMap).
|
||||
getMethodByParameters("put", int.class, Object.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ArrayLookup implements PacketClassLookup {
|
||||
private Class<?>[] array;
|
||||
|
||||
public ArrayLookup() throws IllegalAccessException {
|
||||
initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLookup(int packetID, Class<?> clazz) {
|
||||
array[packetID] = clazz;
|
||||
}
|
||||
|
||||
private void initialize() throws IllegalAccessException {
|
||||
FuzzyReflection reflection = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass());
|
||||
|
||||
// Is there a Class array with 256 elements instead?
|
||||
for (Field field : reflection.getFieldListByType(Class[].class)) {
|
||||
Class<?>[] test = (Class<?>[]) FieldUtils.readField(field, (Object)null);
|
||||
|
||||
if (test.length == 256) {
|
||||
array = test;
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
"Unable to find an array with the type " + Class[].class +
|
||||
" in " + MinecraftReflection.getPacketClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the readPacketData(DataInputStream) method in Packet.
|
||||
*/
|
||||
@ -58,9 +138,7 @@ class ProxyPacketInjector implements PacketInjector {
|
||||
parameterCount(1).
|
||||
build();
|
||||
|
||||
// The "put" method that associates a packet ID with a packet class
|
||||
private static Method putMethod;
|
||||
private static Object intHashMap;
|
||||
private static PacketClassLookup lookup;
|
||||
|
||||
// The packet filter manager
|
||||
private ListenerInvoker manager;
|
||||
@ -78,7 +156,7 @@ class ProxyPacketInjector implements PacketInjector {
|
||||
private CallbackFilter filter;
|
||||
|
||||
public ProxyPacketInjector(ClassLoader classLoader, ListenerInvoker manager,
|
||||
PlayerInjectionHandler playerInjection, ErrorReporter reporter) throws IllegalAccessException {
|
||||
PlayerInjectionHandler playerInjection, ErrorReporter reporter) throws FieldAccessException {
|
||||
|
||||
this.classLoader = classLoader;
|
||||
this.manager = manager;
|
||||
@ -100,20 +178,21 @@ class ProxyPacketInjector implements 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(MinecraftReflection.getPacketClass(), true).
|
||||
getFieldByType(MinecraftReflection.getMinecraftObjectRegex());
|
||||
|
||||
private void initialize() throws FieldAccessException {
|
||||
if (lookup == null) {
|
||||
try {
|
||||
intHashMap = FieldUtils.readField(intHashMapField, (Object) null, true);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new RuntimeException("Minecraft is incompatible.", e);
|
||||
lookup = new IntHashMapLookup();
|
||||
} catch (Exception e1) {
|
||||
|
||||
try {
|
||||
lookup = new ArrayLookup();
|
||||
} catch (Exception e2) {
|
||||
// Wow
|
||||
throw new FieldAccessException(e1.getMessage() + ". Workaround failed too.", e2);
|
||||
}
|
||||
}
|
||||
|
||||
// Now, get the "put" method.
|
||||
putMethod = FuzzyReflection.fromObject(intHashMap).getMethodByParameters("put", int.class, Object.class);
|
||||
// Should work fine now
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,21 +252,12 @@ class ProxyPacketInjector implements PacketInjector {
|
||||
// Add a static reference
|
||||
Enhancer.registerStaticCallbacks(proxy, new Callback[] { NoOp.INSTANCE, modifierReadPacket, modifierRest });
|
||||
|
||||
try {
|
||||
// Override values
|
||||
previous.put(packetID, old);
|
||||
registry.put(proxy, packetID);
|
||||
overwritten.put(packetID, proxy);
|
||||
putMethod.invoke(intHashMap, packetID, proxy);
|
||||
return true;
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new RuntimeException("Illegal argument.", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Cannot access method.", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException("Exception occured in IntHashMap.put.", e);
|
||||
}
|
||||
// Override values
|
||||
previous.put(packetID, old);
|
||||
registry.put(proxy, packetID);
|
||||
overwritten.put(packetID, proxy);
|
||||
lookup.setLookup(packetID, proxy);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -200,25 +270,14 @@ class ProxyPacketInjector implements PacketInjector {
|
||||
Map<Integer, Class> previous = PacketRegistry.getPreviousPackets();
|
||||
Map<Integer, Class> overwritten = PacketRegistry.getOverwrittenPackets();
|
||||
|
||||
// Use the old class definition
|
||||
try {
|
||||
Class old = previous.get(packetID);
|
||||
Class proxy = PacketRegistry.getPacketClassFromID(packetID);
|
||||
Class old = previous.get(packetID);
|
||||
Class proxy = PacketRegistry.getPacketClassFromID(packetID);
|
||||
|
||||
putMethod.invoke(intHashMap, packetID, old);
|
||||
previous.remove(packetID);
|
||||
registry.remove(proxy);
|
||||
overwritten.remove(packetID);
|
||||
return true;
|
||||
|
||||
// Handle some problems
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Cannot access method.", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException("Exception occured in IntHashMap.put.", e);
|
||||
}
|
||||
lookup.setLookup(packetID, old);
|
||||
previous.remove(packetID);
|
||||
registry.remove(proxy);
|
||||
overwritten.remove(packetID);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,104 @@
|
||||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.fuzzy.AbstractFuzzyMatcher;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers;
|
||||
|
||||
/**
|
||||
* Wrap a GNU Trove Collection class with an equivalent Java Collection class.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class TroveWrapper {
|
||||
private volatile static Class<?> decorators;
|
||||
|
||||
/**
|
||||
* Retrieve a Java wrapper for the corresponding Trove map.
|
||||
* @param troveMap - the trove map to wrap.
|
||||
* @return The wrapped GNU Trove map.
|
||||
* @throws IllegalStateException If GNU Trove cannot be found in the class map.
|
||||
* @throws IllegalArgumentException If troveMap is NULL.
|
||||
* @throws FieldAccessException Error in wrapper method or lack of reflection permissions.
|
||||
*/
|
||||
public static <TKey, TValue> Map<TKey, TValue> getDecoratedMap(@Nonnull Object troveMap) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<TKey, TValue> result = (Map<TKey, TValue>) getDecorated(troveMap);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a Java wrapper for the corresponding Trove set.
|
||||
* @param troveSet - the trove set to wrap.
|
||||
* @return The wrapped GNU Trove set.
|
||||
* @throws IllegalStateException If GNU Trove cannot be found in the class map.
|
||||
* @throws IllegalArgumentException If troveSet is NULL.
|
||||
* @throws FieldAccessException Error in wrapper method or lack of reflection permissions.
|
||||
*/
|
||||
public static <TValue> Set<TValue> getDecoratedSet(@Nonnull Object troveSet) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<TValue> result = (Set<TValue>) getDecorated(troveSet);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a Java wrapper for the corresponding Trove list.
|
||||
* @param troveList - the trove list to wrap.
|
||||
* @return The wrapped GNU Trove list.
|
||||
* @throws IllegalStateException If GNU Trove cannot be found in the class map.
|
||||
* @throws IllegalArgumentException If troveList is NULL.
|
||||
* @throws FieldAccessException Error in wrapper method or lack of reflection permissions.
|
||||
*/
|
||||
public static <TValue> List<TValue> getDecoratedList(@Nonnull Object troveList) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<TValue> result = (List<TValue>) getDecorated(troveList);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Object getDecorated(@Nonnull Object trove) {
|
||||
if (trove == null)
|
||||
throw new IllegalArgumentException("trove instance cannot be non-null.");
|
||||
|
||||
AbstractFuzzyMatcher<Class<?>> match = FuzzyMatchers.matchSuper(trove.getClass());
|
||||
|
||||
if (decorators == null) {
|
||||
try {
|
||||
// Attempt to get decorator class
|
||||
decorators = TroveWrapper.class.getClassLoader().loadClass("gnu.trove.TDecorators");
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalStateException("Cannot find TDecorators in Gnu Trove.", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Find an appropriate wrapper method in TDecorators
|
||||
for (Method method : decorators.getMethods()) {
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
|
||||
if (types.length == 1 && match.isMatch(types[0], null)) {
|
||||
try {
|
||||
Object result = method.invoke(null, trove);
|
||||
|
||||
if (result == null)
|
||||
throw new FieldAccessException("Wrapper returned NULL.");
|
||||
else
|
||||
return result;
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new FieldAccessException("Cannot invoke wrapper method.", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new FieldAccessException("Illegal access.", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new FieldAccessException("Error in invocation.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Cannot find decorator for " + trove + " (" + trove.getClass() + ")");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user