Generate a new class for each packet type, as getID is final.
We can't override the getID method in Packet, as it is marked as final. Instead, we'll just generate a seperate class for each packet ID that needs to be intercepted. We can't inherit from the packet ID of any particular class, as it may get recognized and modified by the sendPacket() method. This could be a problem if this recogition is necessary, but we'll come back to this later.
Dieser Commit ist enthalten in:
Ursprung
051a4eda87
Commit
e25ab31316
@ -18,6 +18,7 @@
|
|||||||
package com.comphenix.protocol.injector;
|
package com.comphenix.protocol.injector;
|
||||||
|
|
||||||
import com.comphenix.protocol.events.PacketEvent;
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
|
import com.comphenix.protocol.injector.packet.InterceptWritePacket;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an object that initiate the packet listeners.
|
* Represents an object that initiate the packet listeners.
|
||||||
@ -45,6 +46,12 @@ public interface ListenerInvoker {
|
|||||||
*/
|
*/
|
||||||
public abstract int getPacketID(Object packet);
|
public abstract int getPacketID(Object packet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the object responsible for intercepting write packets.
|
||||||
|
* @return Object that intercepts write packets.
|
||||||
|
*/
|
||||||
|
public InterceptWritePacket getInterceptWritePacket();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Associate a given class with the given packet ID. Internal method.
|
* Associate a given class with the given packet ID. Internal method.
|
||||||
* @param clazz - class to associate.
|
* @param clazz - class to associate.
|
||||||
|
@ -53,6 +53,7 @@ import com.comphenix.protocol.error.ErrorReporter;
|
|||||||
import com.comphenix.protocol.error.Report;
|
import com.comphenix.protocol.error.Report;
|
||||||
import com.comphenix.protocol.error.ReportType;
|
import com.comphenix.protocol.error.ReportType;
|
||||||
import com.comphenix.protocol.events.*;
|
import com.comphenix.protocol.events.*;
|
||||||
|
import com.comphenix.protocol.injector.packet.InterceptWritePacket;
|
||||||
import com.comphenix.protocol.injector.packet.PacketInjector;
|
import com.comphenix.protocol.injector.packet.PacketInjector;
|
||||||
import com.comphenix.protocol.injector.packet.PacketInjectorBuilder;
|
import com.comphenix.protocol.injector.packet.PacketInjectorBuilder;
|
||||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||||
@ -134,6 +135,9 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
// Different injection types per game phase
|
// Different injection types per game phase
|
||||||
private PlayerInjectionHandler playerInjection;
|
private PlayerInjectionHandler playerInjection;
|
||||||
|
|
||||||
|
// Intercepting write packet methods
|
||||||
|
private InterceptWritePacket interceptWritePacket;
|
||||||
|
|
||||||
// The two listener containers
|
// The two listener containers
|
||||||
private SortedPacketListenerList recievedListeners;
|
private SortedPacketListenerList recievedListeners;
|
||||||
private SortedPacketListenerList sendingListeners;
|
private SortedPacketListenerList sendingListeners;
|
||||||
@ -202,6 +206,9 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
// The plugin verifier
|
// The plugin verifier
|
||||||
this.pluginVerifier = new PluginVerifier(builder.getLibrary());
|
this.pluginVerifier = new PluginVerifier(builder.getLibrary());
|
||||||
|
|
||||||
|
// The write packet interceptor
|
||||||
|
interceptWritePacket = new InterceptWritePacket(classLoader, reporter);
|
||||||
|
|
||||||
// Use the correct injection type
|
// Use the correct injection type
|
||||||
if (builder.isNettyEnabled()) {
|
if (builder.isNettyEnabled()) {
|
||||||
spigotInjector = new SpigotPacketInjector(classLoader, reporter, this, server);
|
spigotInjector = new SpigotPacketInjector(classLoader, reporter, this, server);
|
||||||
@ -274,6 +281,11 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
return ImmutableSet.copyOf(packetListeners);
|
return ImmutableSet.copyOf(packetListeners);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InterceptWritePacket getInterceptWritePacket() {
|
||||||
|
return interceptWritePacket;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Warn of common programming mistakes.
|
* Warn of common programming mistakes.
|
||||||
* @param plugin - plugin to check.
|
* @param plugin - plugin to check.
|
||||||
|
@ -2,11 +2,11 @@ package com.comphenix.protocol.injector.packet;
|
|||||||
|
|
||||||
import java.io.DataOutput;
|
import java.io.DataOutput;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
import net.sf.cglib.proxy.Callback;
|
import net.sf.cglib.proxy.Callback;
|
||||||
import net.sf.cglib.proxy.CallbackFilter;
|
import net.sf.cglib.proxy.CallbackFilter;
|
||||||
import net.sf.cglib.proxy.Enhancer;
|
import net.sf.cglib.proxy.Enhancer;
|
||||||
import net.sf.cglib.proxy.NoOp;
|
|
||||||
|
|
||||||
import com.comphenix.protocol.error.ErrorReporter;
|
import com.comphenix.protocol.error.ErrorReporter;
|
||||||
import com.comphenix.protocol.error.Report;
|
import com.comphenix.protocol.error.Report;
|
||||||
@ -16,6 +16,7 @@ import com.comphenix.protocol.events.PacketEvent;
|
|||||||
import com.comphenix.protocol.reflect.MethodInfo;
|
import com.comphenix.protocol.reflect.MethodInfo;
|
||||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a packet instance that has its write method intercepted.
|
* Retrieve a packet instance that has its write method intercepted.
|
||||||
@ -34,35 +35,41 @@ public class InterceptWritePacket {
|
|||||||
parameterCount(1).
|
parameterCount(1).
|
||||||
build();
|
build();
|
||||||
|
|
||||||
|
private CallbackFilter filter;
|
||||||
|
private boolean writePacketIntercepted;
|
||||||
|
|
||||||
|
private ConcurrentMap<Integer, Class<?>> proxyClasses = Maps.newConcurrentMap();
|
||||||
|
|
||||||
private ClassLoader classLoader;
|
private ClassLoader classLoader;
|
||||||
private ErrorReporter reporter;
|
private ErrorReporter reporter;
|
||||||
|
|
||||||
private CallbackFilter filter;
|
private WritePacketModifier modifierWrite;
|
||||||
private boolean writePacketIntercepted;
|
private WritePacketModifier modifierRest;
|
||||||
|
|
||||||
public InterceptWritePacket(ClassLoader classLoader, ErrorReporter reporter) {
|
public InterceptWritePacket(ClassLoader classLoader, ErrorReporter reporter) {
|
||||||
this.classLoader = classLoader;
|
this.classLoader = classLoader;
|
||||||
this.reporter = reporter;
|
this.reporter = reporter;
|
||||||
|
|
||||||
|
// Initialize modifiers
|
||||||
|
this.modifierWrite = new WritePacketModifier(reporter, true);
|
||||||
|
this.modifierRest = new WritePacketModifier(reporter, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private Class<?> createProxyClass(int packetId) {
|
||||||
* Construct a new instance of the proxy object.
|
|
||||||
* @return New instance of proxy.
|
|
||||||
*/
|
|
||||||
public Object constructProxy(Object proxyObject, PacketEvent event, NetworkMarker marker) {
|
|
||||||
// Construct the proxy object
|
// Construct the proxy object
|
||||||
Enhancer ex = new Enhancer();
|
Enhancer ex = new Enhancer();
|
||||||
|
|
||||||
// Initialize the shared filter
|
// Attempt to share callback filter
|
||||||
if (filter == null) {
|
if (filter == null) {
|
||||||
filter = new CallbackFilter() {
|
filter = new CallbackFilter() {
|
||||||
@Override
|
@Override
|
||||||
public int accept(Method method) {
|
public int accept(Method method) {
|
||||||
// Skip methods defined in Object
|
// Skip methods defined in Object
|
||||||
if (WRITE_PACKET.isMatch(MethodInfo.fromMethod(method), null)) {
|
if (WRITE_PACKET.isMatch(MethodInfo.fromMethod(method), null)) {
|
||||||
return 1;
|
writePacketIntercepted = true;
|
||||||
} else {
|
|
||||||
return 0;
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -71,15 +78,16 @@ public class InterceptWritePacket {
|
|||||||
// Subclass the generic packet class
|
// Subclass the generic packet class
|
||||||
ex.setSuperclass(MinecraftReflection.getPacketClass());
|
ex.setSuperclass(MinecraftReflection.getPacketClass());
|
||||||
ex.setCallbackFilter(filter);
|
ex.setCallbackFilter(filter);
|
||||||
|
ex.setUseCache(false);
|
||||||
|
|
||||||
ex.setClassLoader(classLoader);
|
ex.setClassLoader(classLoader);
|
||||||
ex.setCallbacks(new Callback[] {
|
ex.setCallbackTypes( new Class[] { WritePacketModifier.class, WritePacketModifier.class });
|
||||||
NoOp.INSTANCE,
|
Class<?> proxyClass = ex.createClass();
|
||||||
new WritePacketModifier(reporter, proxyObject, event, marker)
|
|
||||||
});
|
|
||||||
|
|
||||||
Object proxy = ex.create();
|
// Register write modifiers too
|
||||||
|
Enhancer.registerStaticCallbacks(proxyClass, new Callback[] { modifierWrite, modifierRest });
|
||||||
|
|
||||||
if (proxy != null) {
|
if (proxyClass != null) {
|
||||||
// Check that we found the read method
|
// Check that we found the read method
|
||||||
if (!writePacketIntercepted) {
|
if (!writePacketIntercepted) {
|
||||||
reporter.reportWarning(this,
|
reporter.reportWarning(this,
|
||||||
@ -87,6 +95,56 @@ public class InterceptWritePacket {
|
|||||||
messageParam(MinecraftReflection.getPacketClass()));
|
messageParam(MinecraftReflection.getPacketClass()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return proxy;
|
return proxyClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?> getProxyClass(int packetId) {
|
||||||
|
Class<?> stored = proxyClasses.get(packetId);
|
||||||
|
|
||||||
|
// Concurrent pattern
|
||||||
|
if (stored == null) {
|
||||||
|
final Class<?> created = createProxyClass(packetId);
|
||||||
|
stored = proxyClasses.putIfAbsent(packetId, created);
|
||||||
|
|
||||||
|
// We won!
|
||||||
|
if (stored == null) {
|
||||||
|
stored = created;
|
||||||
|
PacketRegistry.getPacketToID().put(stored, packetId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stored;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new instance of the proxy object.
|
||||||
|
* @return New instance of proxy, or NULL if we failed.
|
||||||
|
*/
|
||||||
|
public Object constructProxy(Object proxyObject, PacketEvent event, NetworkMarker marker) {
|
||||||
|
Class<?> proxyClass = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
proxyClass = getProxyClass(event.getPacketID());
|
||||||
|
Object generated = proxyClass.newInstance();
|
||||||
|
|
||||||
|
modifierWrite.register(generated, proxyObject, event, marker);
|
||||||
|
modifierRest.register(generated, proxyObject, event, marker);
|
||||||
|
return generated;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
reporter.reportWarning(this,
|
||||||
|
Report.newBuilder(REPORT_CANNOT_CONSTRUCT_WRITE_PROXY).
|
||||||
|
messageParam(proxyClass));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when the write packet proxy class should be removed.
|
||||||
|
*/
|
||||||
|
public void cleanup() {
|
||||||
|
// Remove all proxy classes from the registry
|
||||||
|
for (Class<?> stored : proxyClasses.values()) {
|
||||||
|
PacketRegistry.getPacketToID().remove(stored);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import java.io.ByteArrayOutputStream;
|
|||||||
import java.io.DataOutput;
|
import java.io.DataOutput;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.PriorityQueue;
|
import java.util.PriorityQueue;
|
||||||
|
|
||||||
import com.comphenix.protocol.error.ErrorReporter;
|
import com.comphenix.protocol.error.ErrorReporter;
|
||||||
@ -29,6 +30,7 @@ import com.comphenix.protocol.error.ReportType;
|
|||||||
import com.comphenix.protocol.events.NetworkMarker;
|
import com.comphenix.protocol.events.NetworkMarker;
|
||||||
import com.comphenix.protocol.events.PacketEvent;
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
import com.comphenix.protocol.events.PacketOutputHandler;
|
import com.comphenix.protocol.events.PacketOutputHandler;
|
||||||
|
import com.google.common.collect.MapMaker;
|
||||||
|
|
||||||
import net.sf.cglib.proxy.MethodInterceptor;
|
import net.sf.cglib.proxy.MethodInterceptor;
|
||||||
import net.sf.cglib.proxy.MethodProxy;
|
import net.sf.cglib.proxy.MethodProxy;
|
||||||
@ -36,24 +38,55 @@ import net.sf.cglib.proxy.MethodProxy;
|
|||||||
public class WritePacketModifier implements MethodInterceptor {
|
public class WritePacketModifier implements MethodInterceptor {
|
||||||
public static final ReportType REPORT_CANNOT_WRITE_SERVER_PACKET = new ReportType("Cannot write server packet.");
|
public static final ReportType REPORT_CANNOT_WRITE_SERVER_PACKET = new ReportType("Cannot write server packet.");
|
||||||
|
|
||||||
// Report errors
|
private static class ProxyInformation {
|
||||||
private final ErrorReporter reporter;
|
|
||||||
|
|
||||||
// Marker that contains custom writers
|
// Marker that contains custom writers
|
||||||
private final Object proxyObject;
|
public final Object proxyObject;
|
||||||
private final PacketEvent event;
|
public final PacketEvent event;
|
||||||
private final NetworkMarker marker;
|
public final NetworkMarker marker;
|
||||||
|
|
||||||
public WritePacketModifier(ErrorReporter reporter, Object proxyObject, PacketEvent event, NetworkMarker marker) {
|
public ProxyInformation(Object proxyObject, PacketEvent event, NetworkMarker marker) {
|
||||||
this.proxyObject = proxyObject;
|
this.proxyObject = proxyObject;
|
||||||
this.event = event;
|
this.event = event;
|
||||||
this.marker = marker;
|
this.marker = marker;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Object, ProxyInformation> proxyLookup = new MapMaker().weakKeys().makeMap();
|
||||||
|
|
||||||
|
// Report errors
|
||||||
|
private final ErrorReporter reporter;
|
||||||
|
|
||||||
|
// Whether or not this represents the write method
|
||||||
|
private boolean isWriteMethod;
|
||||||
|
|
||||||
|
public WritePacketModifier(ErrorReporter reporter, boolean isWriteMethod) {
|
||||||
this.reporter = reporter;
|
this.reporter = reporter;
|
||||||
|
this.isWriteMethod = isWriteMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associate the given generated instance of a class and the given parameteters.
|
||||||
|
* @param generatedClass - the generated class.
|
||||||
|
* @param proxyObject - the object to call from the generated class.
|
||||||
|
* @param event - the packet event.
|
||||||
|
* @param marker - the network marker.
|
||||||
|
*/
|
||||||
|
public void register(Object generatedClass, Object proxyObject, PacketEvent event, NetworkMarker marker) {
|
||||||
|
proxyLookup.put(generatedClass, new ProxyInformation(proxyObject, event, marker));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object intercept(Object thisObj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
public Object intercept(Object thisObj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||||
PriorityQueue<PacketOutputHandler> handlers = (PriorityQueue<PacketOutputHandler>) marker.getOutputHandlers();
|
ProxyInformation information = proxyLookup.get(thisObj);
|
||||||
|
|
||||||
|
if (information == null) {
|
||||||
|
// This is really bad - someone forgot to register the proxy
|
||||||
|
throw new RuntimeException("Cannot find proxy information for " + thisObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isWriteMethod) {
|
||||||
|
PriorityQueue<PacketOutputHandler> handlers = (PriorityQueue<PacketOutputHandler>)
|
||||||
|
information.marker.getOutputHandlers();
|
||||||
|
|
||||||
// If every output handler has been removed - ignore everything
|
// If every output handler has been removed - ignore everything
|
||||||
if (!handlers.isEmpty()) {
|
if (!handlers.isEmpty()) {
|
||||||
@ -62,7 +95,7 @@ public class WritePacketModifier implements MethodInterceptor {
|
|||||||
|
|
||||||
// First - we need the initial buffer
|
// First - we need the initial buffer
|
||||||
ByteArrayOutputStream outputBufferStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream outputBufferStream = new ByteArrayOutputStream();
|
||||||
proxy.invokeSuper(proxyObject, new Object[] { new DataOutputStream(outputBufferStream) });
|
proxy.invoke(information.proxyObject, new Object[] { new DataOutputStream(outputBufferStream) });
|
||||||
byte[] outputBuffer = outputBufferStream.toByteArray();
|
byte[] outputBuffer = outputBufferStream.toByteArray();
|
||||||
|
|
||||||
// Let each handler prepare the actual output
|
// Let each handler prepare the actual output
|
||||||
@ -70,7 +103,7 @@ public class WritePacketModifier implements MethodInterceptor {
|
|||||||
PacketOutputHandler handler = handlers.poll();
|
PacketOutputHandler handler = handlers.poll();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
byte[] changed = handler.handle(event, outputBuffer);
|
byte[] changed = handler.handle(information.event, outputBuffer);
|
||||||
|
|
||||||
// Don't break just because a plugin returned NULL
|
// Don't break just because a plugin returned NULL
|
||||||
if (changed != null) {
|
if (changed != null) {
|
||||||
@ -85,6 +118,7 @@ public class WritePacketModifier implements MethodInterceptor {
|
|||||||
|
|
||||||
// Write that output to the network stream
|
// Write that output to the network stream
|
||||||
output.write(outputBuffer);
|
output.write(outputBuffer);
|
||||||
|
return null;
|
||||||
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
// Minecraft cannot handle this error
|
// Minecraft cannot handle this error
|
||||||
@ -93,32 +127,9 @@ public class WritePacketModifier implements MethodInterceptor {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Default to the super method
|
// Default to the super method
|
||||||
return proxy.invokeSuper(proxyObject, args);
|
return proxy.invoke(information.proxyObject, args);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the proxied Minecraft object.
|
|
||||||
* @return The proxied object.
|
|
||||||
*/
|
|
||||||
public Object getProxyObject() {
|
|
||||||
return proxyObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the associated packet event.
|
|
||||||
* @return The packet event.
|
|
||||||
*/
|
|
||||||
public PacketEvent getEvent() {
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the network marker that is in use.
|
|
||||||
* @return The network marker.
|
|
||||||
*/
|
|
||||||
public NetworkMarker getMarker() {
|
|
||||||
return marker;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,9 +143,7 @@ public abstract class PlayerInjector implements SocketInjector {
|
|||||||
this.reporter = reporter;
|
this.reporter = reporter;
|
||||||
this.player = player;
|
this.player = player;
|
||||||
this.invoker = invoker;
|
this.invoker = invoker;
|
||||||
|
this.writePacketInterceptor = invoker.getInterceptWritePacket();
|
||||||
// Intercept the write method
|
|
||||||
writePacketInterceptor = new InterceptWritePacket(classLoader, reporter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -523,8 +521,10 @@ public abstract class PlayerInjector implements SocketInjector {
|
|||||||
* Remove all hooks and modifications.
|
* Remove all hooks and modifications.
|
||||||
*/
|
*/
|
||||||
public final void cleanupAll() {
|
public final void cleanupAll() {
|
||||||
if (!clean)
|
if (!clean) {
|
||||||
cleanHook();
|
cleanHook();
|
||||||
|
writePacketInterceptor.cleanup();
|
||||||
|
}
|
||||||
clean = true;
|
clean = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
In neuem Issue referenzieren
Einen Benutzer sperren