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:
Ursprung
5185442f35
Commit
f7c4fd4ec9
@ -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.
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
@ -250,7 +284,7 @@ class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process a given message on the packet listeners.
|
* Process a given message on the packet listeners.
|
||||||
* @param message - the message/packet.
|
* @param message - the message/packet.
|
||||||
@ -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 {
|
||||||
|
NetworkMarker marker = null;
|
||||||
|
PacketEvent event = currentEvent;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
PacketEvent event = currentEvent;
|
|
||||||
NetworkMarker marker = null;
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
// Let each handler prepare the actual output
|
||||||
for (PacketOutputHandler handler : marker.getOutputHandlers()) {
|
byte[] data = processor.processOutput(event, marker, getBytes(packetBuffer));
|
||||||
data = handler.handle(event, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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() {
|
||||||
@ -360,9 +414,12 @@ class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
|||||||
byteBuffer.markReaderIndex();
|
byteBuffer.markReaderIndex();
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
In neuem Issue referenzieren
Einen Benutzer sperren