Archiviert
13
0

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:
Kristian S. Stangeland 2013-07-17 05:48:34 +02:00
Ursprung 051a4eda87
Commit e25ab31316
5 geänderte Dateien mit 179 neuen und 91 gelöschten Zeilen

Datei anzeigen

@ -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.

Datei anzeigen

@ -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.

Datei anzeigen

@ -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);
}
} }
} }

Datei anzeigen

@ -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;
} }
} }

Datei anzeigen

@ -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;
} }