Archiviert
13
0

Added support for "post packet events".

NetworkMarker now contains a list of post listeners that are invoked 
(in no particular order) when a packet has been serialize and sent
to a player, OR, when it has been enqueued for processing by the 
server.

This works for both 1.7.2+ (Netty) and 1.6.4 and earlier, though the
1.6.4 version has a good deal more overhead.
Dieser Commit ist enthalten in:
Kristian S. Stangeland 2014-04-25 02:55:17 +02:00
Ursprung 5185442f35
Commit f7c4fd4ec9
12 geänderte Dateien mit 356 neuen und 51 gelöschten Zeilen

Datei anzeigen

@ -7,15 +7,18 @@ import java.nio.ByteBuffer;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue; import java.util.PriorityQueue;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import com.comphenix.protocol.PacketType; import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.utility.ByteBufferInputStream; import com.comphenix.protocol.utility.ByteBufferInputStream;
import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.StreamSerializer; import com.comphenix.protocol.utility.StreamSerializer;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
/** /**
@ -48,6 +51,8 @@ public abstract class NetworkMarker {
// Custom network handler // Custom network handler
private PriorityQueue<PacketOutputHandler> outputHandlers; private PriorityQueue<PacketOutputHandler> outputHandlers;
// Sent listeners
private List<PacketPostListener> postListeners;
// The input buffer // The input buffer
private ByteBuffer inputBuffer; private ByteBuffer inputBuffer;
@ -151,7 +156,6 @@ public abstract class NetworkMarker {
* <p> * <p>
* The data is exactly the same as in {@link #getInputBuffer()}. * The data is exactly the same as in {@link #getInputBuffer()}.
* @see #getInputBuffer() * @see #getInputBuffer()
* @param excludeHeader - whether or not to exclude the packet ID header.
* @return The incoming serialized packet data as a stream, or NULL if the packet was transmitted locally. * @return The incoming serialized packet data as a stream, or NULL if the packet was transmitted locally.
*/ */
public DataInputStream getInputStream() { public DataInputStream getInputStream() {
@ -252,6 +256,45 @@ public abstract class NetworkMarker {
} }
} }
/**
* Add a listener that is invoked after a packet has been successfully sent to the client, or received
* by the server.
* <p>
* Received packets are not guarenteed to have been fully processed, but packets passed
* to {@link ProtocolManager#recieveClientPacket(Player, PacketContainer)} will be processed after the
* current packet event.
* <p>
* Note that post listeners will be executed asynchronously off the main thread. They are not executed
* in any defined order.
* @param listener - the listener that will be invoked.
* @return TRUE if it was added.
*/
public boolean addPostListener(PacketPostListener listener) {
if (postListeners == null)
postListeners = Lists.newArrayList();
return postListeners.add(listener);
}
/**
* Remove the first instance of the given listener.
* @param listener - listener to remove.
* @return TRUE if it was removed, FALSE otherwise.
*/
public boolean removePostListener(PacketPostListener listener) {
if (postListeners != null) {
return postListeners.remove(listener);
}
return false;
}
/**
* Retrieve an immutable view of all the listeners that will be invoked once the packet has been sent or received.
* @return Every post packet listener. Never NULL.
*/
public List<PacketPostListener> getPostListeners() {
return postListeners != null ? Collections.unmodifiableList(postListeners) : Collections.<PacketPostListener>emptyList();
}
/** /**
* Ensure that the packet event is server side. * Ensure that the packet event is server side.
*/ */
@ -304,6 +347,15 @@ public abstract class NetworkMarker {
return marker != null && !marker.getOutputHandlers().isEmpty(); return marker != null && !marker.getOutputHandlers().isEmpty();
} }
/**
* Determine if the given marker has any post listeners.
* @param marker - the marker to check.
* @return TRUE if it does, FALSE otherwise.
*/
public static boolean hasPostListeners(NetworkMarker marker) {
return marker != null && !marker.getPostListeners().isEmpty();
}
/** /**
* Retrieve the byte buffer stored in the given marker. * Retrieve the byte buffer stored in the given marker.
* @param marker - the marker. * @param marker - the marker.

Datei anzeigen

@ -70,6 +70,7 @@ public class PacketEvent extends EventObject implements Cancellable {
// Whether or not a packet event is read only // Whether or not a packet event is read only
private boolean readOnly; private boolean readOnly;
private boolean filtered;
/** /**
* Use the static constructors to create instances of this event. * Use the static constructors to create instances of this event.
@ -77,18 +78,20 @@ public class PacketEvent extends EventObject implements Cancellable {
*/ */
public PacketEvent(Object source) { public PacketEvent(Object source) {
super(source); super(source);
this.filtered = true;
} }
private PacketEvent(Object source, PacketContainer packet, Player player, boolean serverPacket) { private PacketEvent(Object source, PacketContainer packet, Player player, boolean serverPacket) {
this(source, packet, null, player, serverPacket); this(source, packet, null, player, serverPacket, true);
} }
private PacketEvent(Object source, PacketContainer packet, NetworkMarker marker, Player player, boolean serverPacket) { private PacketEvent(Object source, PacketContainer packet, NetworkMarker marker, Player player, boolean serverPacket, boolean filtered) {
super(source); super(source);
this.packet = packet; this.packet = packet;
this.playerReference = new WeakReference<Player>(player); this.playerReference = new WeakReference<Player>(player);
this.networkMarker = marker; this.networkMarker = marker;
this.serverPacket = serverPacket; this.serverPacket = serverPacket;
this.filtered = filtered;
} }
private PacketEvent(PacketEvent origial, AsyncMarker asyncMarker) { private PacketEvent(PacketEvent origial, AsyncMarker asyncMarker) {
@ -97,6 +100,7 @@ public class PacketEvent extends EventObject implements Cancellable {
this.playerReference = origial.playerReference; this.playerReference = origial.playerReference;
this.cancel = origial.cancel; this.cancel = origial.cancel;
this.serverPacket = origial.serverPacket; this.serverPacket = origial.serverPacket;
this.filtered = origial.filtered;
this.asyncMarker = asyncMarker; this.asyncMarker = asyncMarker;
this.asynchronous = true; this.asynchronous = true;
} }
@ -121,7 +125,22 @@ public class PacketEvent extends EventObject implements Cancellable {
* @return The event. * @return The event.
*/ */
public static PacketEvent fromClient(Object source, PacketContainer packet, NetworkMarker marker, Player client) { public static PacketEvent fromClient(Object source, PacketContainer packet, NetworkMarker marker, Player client) {
return new PacketEvent(source, packet, marker, client, false); return new PacketEvent(source, packet, marker, client, false, true);
}
/**
* Creates an event representing a client packet transmission.
* <p>
* If <i>filtered</i> is FALSE, then this event is only processed by packet monitors.
* @param source - the event source.
* @param packet - the packet.
* @param marker - the network marker.
* @param client - the client that sent the packet.
* @param filtered - whether or not this packet event is processed by every packet listener.
* @return The event.
*/
public static PacketEvent fromClient(Object source, PacketContainer packet, NetworkMarker marker, Player client, boolean filtered) {
return new PacketEvent(source, packet, marker, client, false, filtered);
} }
/** /**
@ -144,7 +163,22 @@ public class PacketEvent extends EventObject implements Cancellable {
* @return The event. * @return The event.
*/ */
public static PacketEvent fromServer(Object source, PacketContainer packet, NetworkMarker marker, Player recipient) { public static PacketEvent fromServer(Object source, PacketContainer packet, NetworkMarker marker, Player recipient) {
return new PacketEvent(source, packet, marker, recipient, true); return new PacketEvent(source, packet, marker, recipient, true, true);
}
/**
* Creates an event representing a server packet transmission.
* <p>
* If <i>filtered</i> is FALSE, then this event is only processed by packet monitors.
* @param source - the event source.
* @param packet - the packet.
* @param marker - the network marker.
* @param recipient - the client that will receieve the packet.
* @param filtered - whether or not this packet event is processed by every packet listener.
* @return The event.
*/
public static PacketEvent fromServer(Object source, PacketContainer packet, NetworkMarker marker, Player recipient, boolean filtered) {
return new PacketEvent(source, packet, marker, recipient, true, filtered);
} }
/** /**
@ -286,6 +320,16 @@ public class PacketEvent extends EventObject implements Cancellable {
return playerReference.get(); return playerReference.get();
} }
/**
* Determine if this packet is filtered by every packet listener.
* <p>
* If not, it will only be intercepted by monitor packets.
* @return TRUE if it is, FALSE otherwise.
*/
public boolean isFiltered() {
return filtered;
}
/** /**
* Whether or not this packet was created by the server. * Whether or not this packet was created by the server.
* <p> * <p>

Datei anzeigen

@ -20,7 +20,7 @@ package com.comphenix.protocol.events;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
/** /**
* Represents a listener that receives notifications when packets are sent or received. * Represents a listener that receives notifications when packets are sending or being received.
* <p> * <p>
* Use {@link PacketAdapter} for a simple wrapper around this interface. * Use {@link PacketAdapter} for a simple wrapper around this interface.
* @author Kristian * @author Kristian

Datei anzeigen

@ -4,7 +4,6 @@ import org.bukkit.plugin.Plugin;
/** /**
* Represents an adapter version of the output handler interface. * Represents an adapter version of the output handler interface.
*
* @author Kristian * @author Kristian
*/ */
public abstract class PacketOutputAdapter implements PacketOutputHandler { public abstract class PacketOutputAdapter implements PacketOutputHandler {

Datei anzeigen

@ -0,0 +1,22 @@
package com.comphenix.protocol.events;
import org.bukkit.plugin.Plugin;
import com.google.common.base.Preconditions;
/**
* Represents an adapter version of a post listener.
* @author Kristian
*/
public abstract class PacketPostAdapter implements PacketPostListener {
private Plugin plugin;
public PacketPostAdapter(Plugin plugin) {
this.plugin = Preconditions.checkNotNull(plugin, "plugin cannot be NULL");
}
@Override
public Plugin getPlugin() {
return plugin;
}
}

Datei anzeigen

@ -0,0 +1,23 @@
package com.comphenix.protocol.events;
import org.bukkit.plugin.Plugin;
/**
* Represents a packet listener that is invoked after a packet has been sent or received.
* @author Kristian
*/
public interface PacketPostListener {
/**
* Retrieve the plugin this listener belongs to.
* @return The assoicated plugin.
*/
public Plugin getPlugin();
/**
* Invoked after a packet has been sent or received.
* <p>
* Note that this is invoked asynchronously.
* @param event - the packet event.
*/
public void onPostEvent(PacketEvent event);
}

Datei anzeigen

@ -0,0 +1,83 @@
package com.comphenix.protocol.injector;
import java.util.PriorityQueue;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketOutputHandler;
import com.comphenix.protocol.events.PacketPostListener;
/**
* Represents a processor for network markers.
* @author Kristian
*/
public class NetworkProcessor {
private ErrorReporter reporter;
/**
* Construct a new network processor.
* @param reporter - the reporter.
*/
public NetworkProcessor(ErrorReporter reporter) {
this.reporter = reporter;
}
/**
* Process the serialized packet byte array with the given network marker.
* @param event - current packet event.
* @param marker - the network marker.
* @param input - the input array.
* @return The output array.
*/
public byte[] processOutput(PacketEvent event, NetworkMarker marker, final byte[] input) {
// Bit of a hack - but we need the performance
PriorityQueue<PacketOutputHandler> handlers = (PriorityQueue<PacketOutputHandler>)
marker.getOutputHandlers();
byte[] output = input;
// Let each handler prepare the actual output
while (!handlers.isEmpty()) {
PacketOutputHandler handler = handlers.poll();
try {
byte[] changed = handler.handle(event, output);
// Don't break just because a plugin returned NULL
if (changed != null) {
output = changed;
} else {
throw new IllegalStateException("Handler cannot return a NULL array.");
}
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (Throwable e) {
reporter.reportMinimal(handler.getPlugin(), "PacketOutputHandler.handle()", e);
}
}
return output;
}
/**
* Invoke the post listeners, if any.
* @param marker - the network marker, or NULL.
*/
public void invokePostListeners(PacketEvent event, NetworkMarker marker) {
if (NetworkMarker.hasPostListeners(marker)) {
// Invoke every sent listener
for (PacketPostListener listener : marker.getPostListeners()) {
try {
listener.onPostEvent(event);
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (Throwable e) {
reporter.reportMinimal(listener.getPlugin(), "SentListener.run()", e);
}
}
}
}
}

Datei anzeigen

@ -782,7 +782,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
// Inform the MONITOR packets // Inform the MONITOR packets
if (!filters) { if (!filters) {
PacketEvent event = PacketEvent.fromServer(this, packet, marker, receiver); PacketEvent event = PacketEvent.fromServer(this, packet, marker, receiver, false);
sendingListeners.invokePacketSending( sendingListeners.invokePacketSending(
reporter, event, ListenerPriority.MONITOR); reporter, event, ListenerPriority.MONITOR);
@ -832,7 +832,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
// Let the monitors know though // Let the monitors know though
recievedListeners.invokePacketSending( recievedListeners.invokePacketSending(
reporter, reporter,
PacketEvent.fromClient(this, packet, marker, sender), PacketEvent.fromClient(this, packet, marker, sender, false),
ListenerPriority.MONITOR); ListenerPriority.MONITOR);
} }

Datei anzeigen

@ -3,7 +3,10 @@ package com.comphenix.protocol.injector.netty;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.net.Socket; import java.net.Socket;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List; import java.util.List;
import java.util.ListIterator;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
@ -13,6 +16,9 @@ import net.minecraft.util.io.netty.buffer.ByteBuf;
import net.minecraft.util.io.netty.channel.Channel; import net.minecraft.util.io.netty.channel.Channel;
import net.minecraft.util.io.netty.channel.ChannelHandler; import net.minecraft.util.io.netty.channel.ChannelHandler;
import net.minecraft.util.io.netty.channel.ChannelHandlerContext; import net.minecraft.util.io.netty.channel.ChannelHandlerContext;
import net.minecraft.util.io.netty.channel.ChannelInboundHandler;
import net.minecraft.util.io.netty.channel.ChannelInboundHandlerAdapter;
import net.minecraft.util.io.netty.channel.ChannelPromise;
import net.minecraft.util.io.netty.channel.socket.SocketChannel; import net.minecraft.util.io.netty.channel.socket.SocketChannel;
import net.minecraft.util.io.netty.handler.codec.ByteToMessageDecoder; import net.minecraft.util.io.netty.handler.codec.ByteToMessageDecoder;
import net.minecraft.util.io.netty.handler.codec.MessageToByteEncoder; import net.minecraft.util.io.netty.handler.codec.MessageToByteEncoder;
@ -31,7 +37,7 @@ import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.ConnectionSide; import com.comphenix.protocol.events.ConnectionSide;
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.injector.NetworkProcessor;
import com.comphenix.protocol.injector.server.SocketInjector; import com.comphenix.protocol.injector.server.SocketInjector;
import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.VolatileField; import com.comphenix.protocol.reflect.VolatileField;
@ -94,6 +100,11 @@ class ChannelInjector extends ByteToMessageDecoder implements Injector {
*/ */
private PacketEvent currentEvent; private PacketEvent currentEvent;
/**
* A packet event that should be processed by the write method.
*/
private PacketEvent finalEvent;
/** /**
* A flag set by the main thread to indiciate that a packet should not be processed. * A flag set by the main thread to indiciate that a packet should not be processed.
*/ */
@ -107,12 +118,17 @@ class ChannelInjector extends ByteToMessageDecoder implements Injector {
private ByteToMessageDecoder vanillaDecoder; private ByteToMessageDecoder vanillaDecoder;
private MessageToByteEncoder<Object> vanillaEncoder; private MessageToByteEncoder<Object> vanillaEncoder;
// Our extra handler // Our extra handlers
private MessageToByteEncoder<Object> protocolEncoder; private MessageToByteEncoder<Object> protocolEncoder;
private ChannelInboundHandler finishHandler;
private Deque<PacketEvent> finishQueue = new ArrayDeque<PacketEvent>();
// The channel listener // The channel listener
private ChannelListener channelListener; private ChannelListener channelListener;
// Processing network markers
private NetworkProcessor processor;
// Closed // Closed
private boolean injected; private boolean injected;
private boolean closed; private boolean closed;
@ -131,6 +147,7 @@ class ChannelInjector extends ByteToMessageDecoder implements Injector {
this.originalChannel = Preconditions.checkNotNull(channel, "channel cannot be NULL"); this.originalChannel = Preconditions.checkNotNull(channel, "channel cannot be NULL");
this.channelListener = Preconditions.checkNotNull(channelListener, "channelListener cannot be NULL"); this.channelListener = Preconditions.checkNotNull(channelListener, "channelListener cannot be NULL");
this.factory = Preconditions.checkNotNull(factory, "factory cannot be NULL"); this.factory = Preconditions.checkNotNull(factory, "factory cannot be NULL");
this.processor = new NetworkProcessor(ProtocolLibrary.getErrorReporter());
// Get the channel field // Get the channel field
this.channelField = new VolatileField( this.channelField = new VolatileField(
@ -178,10 +195,27 @@ class ChannelInjector extends ByteToMessageDecoder implements Injector {
protected void encode(ChannelHandlerContext ctx, Object packet, ByteBuf output) throws Exception { protected void encode(ChannelHandlerContext ctx, Object packet, ByteBuf output) throws Exception {
ChannelInjector.this.encode(ctx, packet, output); ChannelInjector.this.encode(ctx, packet, output);
} }
@Override
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
super.write(ctx, packet, promise);
ChannelInjector.this.finalWrite(ctx, packet, promise);
}
};
// Intercept recieved packets
finishHandler = new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// Execute context first
ctx.fireChannelRead(msg);
ChannelInjector.this.finishRead(ctx, msg);
}
}; };
// Insert our handlers - note that we effectively replace the vanilla encoder/decoder // Insert our handlers - note that we effectively replace the vanilla encoder/decoder
originalChannel.pipeline().addBefore("decoder", "protocol_lib_decoder", this); originalChannel.pipeline().addBefore("decoder", "protocol_lib_decoder", this);
originalChannel.pipeline().addBefore("protocol_lib_decoder", "protocol_lib_finish", finishHandler);
originalChannel.pipeline().addAfter("encoder", "protocol_lib_encoder", protocolEncoder); originalChannel.pipeline().addAfter("encoder", "protocol_lib_encoder", protocolEncoder);
// Intercept all write methods // Intercept all write methods
@ -286,10 +320,10 @@ class ChannelInjector extends ByteToMessageDecoder implements Injector {
* @throws Exception If anything went wrong. * @throws Exception If anything went wrong.
*/ */
protected void encode(ChannelHandlerContext ctx, Object packet, ByteBuf output) throws Exception { protected void encode(ChannelHandlerContext ctx, Object packet, ByteBuf output) throws Exception {
try { NetworkMarker marker = null;
PacketEvent event = currentEvent; PacketEvent event = currentEvent;
NetworkMarker marker = null;
try {
// Skip every kind of non-filtered packet // Skip every kind of non-filtered packet
if (!scheduleProcessPackets.get()) { if (!scheduleProcessPackets.get()) {
return; return;
@ -323,15 +357,16 @@ class ChannelInjector extends ByteToMessageDecoder implements Injector {
if (packet != null && event != null && NetworkMarker.hasOutputHandlers(marker)) { if (packet != null && event != null && NetworkMarker.hasOutputHandlers(marker)) {
ByteBuf packetBuffer = ctx.alloc().buffer(); ByteBuf packetBuffer = ctx.alloc().buffer();
ENCODE_BUFFER.invoke(vanillaEncoder, ctx, packet, packetBuffer); ENCODE_BUFFER.invoke(vanillaEncoder, ctx, packet, packetBuffer);
byte[] data = getBytes(packetBuffer);
for (PacketOutputHandler handler : marker.getOutputHandlers()) { // Let each handler prepare the actual output
data = handler.handle(event, data); byte[] data = processor.processOutput(event, marker, getBytes(packetBuffer));
}
// Write the result // Write the result
output.writeBytes(data); output.writeBytes(data);
packet = null; packet = null;
// Sent listeners?
finalEvent = event;
return; return;
} }
} catch (Exception e) { } catch (Exception e) {
@ -341,10 +376,29 @@ class ChannelInjector extends ByteToMessageDecoder implements Injector {
// Attempt to handle the packet nevertheless // Attempt to handle the packet nevertheless
if (packet != null) { if (packet != null) {
ENCODE_BUFFER.invoke(vanillaEncoder, ctx, packet, output); ENCODE_BUFFER.invoke(vanillaEncoder, ctx, packet, output);
finalEvent = event;
} }
} }
} }
/**
* Invoked when a packet has been written to the channel.
* @param ctx - current context.
* @param packet - the packet that has been written.
* @param promise - a promise.
*/
protected void finalWrite(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) {
PacketEvent event = finalEvent;
if (event != null) {
// Necessary to prevent infinite loops
finalEvent = null;
currentEvent = null;
processor.invokePostListeners(event, NetworkMarker.getNetworkMarker(event));
}
}
private void scheduleMainThread(final Object packetCopy) { private void scheduleMainThread(final Object packetCopy) {
// Don't use BukkitExecutors for this - it has a bit of overhead // Don't use BukkitExecutors for this - it has a bit of overhead
Bukkit.getScheduler().scheduleSyncDelayedTask(factory.getPlugin(), new Runnable() { Bukkit.getScheduler().scheduleSyncDelayedTask(factory.getPlugin(), new Runnable() {
@ -361,8 +415,11 @@ class ChannelInjector extends ByteToMessageDecoder implements Injector {
DECODE_BUFFER.invoke(vanillaDecoder, ctx, byteBuffer, packets); DECODE_BUFFER.invoke(vanillaDecoder, ctx, byteBuffer, packets);
try { try {
if (packets.size() > 0) { // Reset queue
Object input = packets.get(0); finishQueue.clear();
for (ListIterator<Object> it = packets.listIterator(); it.hasNext(); ) {
Object input = it.next();
Class<?> packetClass = input.getClass(); Class<?> packetClass = input.getClass();
NetworkMarker marker = null; NetworkMarker marker = null;
@ -377,10 +434,14 @@ class ChannelInjector extends ByteToMessageDecoder implements Injector {
// Handle packet changes // Handle packet changes
if (output != null) { if (output != null) {
if (output.isCancelled()) if (output.isCancelled()) {
packets.clear(); it.remove();
else if (output.getPacket().getHandle() != input) continue;
packets.set(0, output.getPacket().getHandle()); } else if (output.getPacket().getHandle() != input) {
it.set(output.getPacket().getHandle());
}
finishQueue.addLast(output);
} }
} }
} catch (Exception e) { } catch (Exception e) {
@ -389,6 +450,24 @@ class ChannelInjector extends ByteToMessageDecoder implements Injector {
} }
} }
/**
* Invoked after our decoder.
* @param ctx - current context.
* @param msg - the current packet.
*/
protected void finishRead(ChannelHandlerContext ctx, Object msg) {
// Assume same order
PacketEvent event = finishQueue.pollFirst();
if (event != null) {
NetworkMarker marker = NetworkMarker.getNetworkMarker(event);
if (marker != null) {
processor.invokePostListeners(event, marker);
}
}
}
/** /**
* Invoked when we may need to handle the login packet. * Invoked when we may need to handle the login packet.
* @param packetClass - the packet class. * @param packetClass - the packet class.
@ -608,6 +687,7 @@ class ChannelInjector extends ByteToMessageDecoder implements Injector {
@Override @Override
public Object call() throws Exception { public Object call() throws Exception {
originalChannel.pipeline().remove(ChannelInjector.this); originalChannel.pipeline().remove(ChannelInjector.this);
originalChannel.pipeline().remove(finishHandler);
originalChannel.pipeline().remove(protocolEncoder); originalChannel.pipeline().remove(protocolEncoder);
return null; return null;
} }

Datei anzeigen

@ -28,8 +28,10 @@ import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.error.ErrorReporter; 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.NetworkMarker;
import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.NetworkProcessor;
import com.google.common.collect.MapMaker; import com.google.common.collect.MapMaker;
import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodInterceptor;
@ -47,6 +49,7 @@ class ReadPacketModifier implements MethodInterceptor {
// Report errors // Report errors
private ErrorReporter reporter; private ErrorReporter reporter;
private NetworkProcessor processor;
// If this is a read packet data method // If this is a read packet data method
private boolean isReadPacketDataMethod; private boolean isReadPacketDataMethod;
@ -58,6 +61,7 @@ class ReadPacketModifier implements MethodInterceptor {
this.packetID = packetID; this.packetID = packetID;
this.packetInjector = packetInjector; this.packetInjector = packetInjector;
this.reporter = reporter; this.reporter = reporter;
this.processor = new NetworkProcessor(reporter);
this.isReadPacketDataMethod = isReadPacketDataMethod; this.isReadPacketDataMethod = isReadPacketDataMethod;
} }
@ -152,9 +156,14 @@ class ReadPacketModifier implements MethodInterceptor {
if (event.isCancelled()) { if (event.isCancelled()) {
override.put(thisObj, CANCEL_MARKER); override.put(thisObj, CANCEL_MARKER);
return returnValue;
} else if (!objectEquals(thisObj, result)) { } else if (!objectEquals(thisObj, result)) {
override.put(thisObj, result); override.put(thisObj, result);
} }
// This is fine - received packets are enqueued in any case
NetworkMarker marker = NetworkMarker.getNetworkMarker(event);
processor.invokePostListeners(event, marker);
} }
} catch (OutOfMemoryError e) { } catch (OutOfMemoryError e) {

Datei anzeigen

@ -30,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.comphenix.protocol.injector.NetworkProcessor;
import com.google.common.collect.MapMaker; import com.google.common.collect.MapMaker;
import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodInterceptor;
@ -55,12 +56,14 @@ public class WritePacketModifier implements MethodInterceptor {
// Report errors // Report errors
private final ErrorReporter reporter; private final ErrorReporter reporter;
private final NetworkProcessor processor;
// Whether or not this represents the write method // Whether or not this represents the write method
private boolean isWriteMethod; private boolean isWriteMethod;
public WritePacketModifier(ErrorReporter reporter, boolean isWriteMethod) { public WritePacketModifier(ErrorReporter reporter, boolean isWriteMethod) {
this.reporter = reporter; this.reporter = reporter;
this.processor = new NetworkProcessor(reporter);
this.isWriteMethod = isWriteMethod; this.isWriteMethod = isWriteMethod;
} }
@ -85,39 +88,24 @@ public class WritePacketModifier implements MethodInterceptor {
} }
if (isWriteMethod) { 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 (!information.marker.getOutputHandlers().isEmpty()) {
try { try {
DataOutput output = (DataOutput) args[0]; DataOutput output = (DataOutput) args[0];
// First - we need the initial buffer // First - we need the initial buffer
ByteArrayOutputStream outputBufferStream = new ByteArrayOutputStream(); ByteArrayOutputStream outputBufferStream = new ByteArrayOutputStream();
proxy.invoke(information.proxyObject, new Object[] { new DataOutputStream(outputBufferStream) }); proxy.invoke(information.proxyObject, new Object[] { new DataOutputStream(outputBufferStream) });
byte[] outputBuffer = outputBufferStream.toByteArray();
// Let each handler prepare the actual output // Let each handler prepare the actual output
while (!handlers.isEmpty()) { byte[] outputBuffer = processor.processOutput(information.event, information.marker,
PacketOutputHandler handler = handlers.poll(); outputBufferStream.toByteArray());
try {
byte[] changed = handler.handle(information.event, outputBuffer);
// Don't break just because a plugin returned NULL
if (changed != null) {
outputBuffer = changed;
} else {
throw new IllegalStateException("Handler cannot return a NULL array.");
}
} catch (Exception e) {
reporter.reportMinimal(handler.getPlugin(), "PacketOutputHandler.handle()", e);
}
}
// Write that output to the network stream // Write that output to the network stream
output.write(outputBuffer); output.write(outputBuffer);
// We're done
processor.invokePostListeners(information.event, information.marker);
return null; return null;
} catch (OutOfMemoryError e) { } catch (OutOfMemoryError e) {
@ -131,6 +119,11 @@ public class WritePacketModifier implements MethodInterceptor {
); );
} }
} }
// Invoke this write method first
proxy.invoke(information.proxyObject, args);
processor.invokePostListeners(information.event, information.marker);
return null;
} }
// Default to the super method // Default to the super method

Datei anzeigen

@ -636,7 +636,7 @@ public abstract class PlayerInjector implements SocketInjector {
marker = NetworkMarker.getNetworkMarker(event); marker = NetworkMarker.getNetworkMarker(event);
// See if we need to proxy the write method // See if we need to proxy the write method
if (result != null && NetworkMarker.hasOutputHandlers(marker)) { if (result != null && (NetworkMarker.hasOutputHandlers(marker) || NetworkMarker.hasPostListeners(marker))) {
result = writePacketInterceptor.constructProxy(result, event, marker); result = writePacketInterceptor.constructProxy(result, event, marker);
} }
return result; return result;