Use a separate asynchronous sending queue for every online player.
This ensures that packets intended for player A doesn't have to wait for the packets of player B to be finished processing.
Dieser Commit ist enthalten in:
Ursprung
f8af92eb5b
Commit
7b9d971238
@ -23,6 +23,7 @@ import java.util.Set;
|
|||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
import org.bukkit.scheduler.BukkitScheduler;
|
import org.bukkit.scheduler.BukkitScheduler;
|
||||||
|
|
||||||
@ -52,12 +53,12 @@ public class AsyncFilterManager implements AsynchronousManager {
|
|||||||
private Set<PacketListener> timeoutListeners;
|
private Set<PacketListener> timeoutListeners;
|
||||||
|
|
||||||
private PacketProcessingQueue serverProcessingQueue;
|
private PacketProcessingQueue serverProcessingQueue;
|
||||||
private PacketSendingQueue serverQueue;
|
|
||||||
|
|
||||||
|
|
||||||
private PacketProcessingQueue clientProcessingQueue;
|
private PacketProcessingQueue clientProcessingQueue;
|
||||||
private PacketSendingQueue clientQueue;
|
|
||||||
|
// Sending queues
|
||||||
|
private PlayerSendingHandler playerSendingHandler;
|
||||||
|
|
||||||
|
// Report exceptions
|
||||||
private ErrorReporter reporter;
|
private ErrorReporter reporter;
|
||||||
|
|
||||||
// The likely main thread
|
// The likely main thread
|
||||||
@ -72,38 +73,16 @@ public class AsyncFilterManager implements AsynchronousManager {
|
|||||||
// Current packet index
|
// Current packet index
|
||||||
private AtomicInteger currentSendingIndex = new AtomicInteger();
|
private AtomicInteger currentSendingIndex = new AtomicInteger();
|
||||||
|
|
||||||
// Whether or not we're currently cleaning up
|
|
||||||
private volatile boolean cleaningUp;
|
|
||||||
|
|
||||||
public AsyncFilterManager(ErrorReporter reporter, BukkitScheduler scheduler, ProtocolManager manager) {
|
public AsyncFilterManager(ErrorReporter reporter, BukkitScheduler scheduler, ProtocolManager manager) {
|
||||||
|
|
||||||
// Initialize timeout listeners
|
// Initialize timeout listeners
|
||||||
serverTimeoutListeners = new SortedPacketListenerList();
|
serverTimeoutListeners = new SortedPacketListenerList();
|
||||||
clientTimeoutListeners = new SortedPacketListenerList();
|
clientTimeoutListeners = new SortedPacketListenerList();
|
||||||
timeoutListeners = Sets.newSetFromMap(new ConcurrentHashMap<PacketListener, Boolean>());
|
timeoutListeners = Sets.newSetFromMap(new ConcurrentHashMap<PacketListener, Boolean>());
|
||||||
|
|
||||||
// Server packets are synchronized already
|
this.playerSendingHandler = new PlayerSendingHandler(reporter, serverTimeoutListeners, clientTimeoutListeners);
|
||||||
this.serverQueue = new PacketSendingQueue(false) {
|
this.serverProcessingQueue = new PacketProcessingQueue(playerSendingHandler);
|
||||||
@Override
|
this.clientProcessingQueue = new PacketProcessingQueue(playerSendingHandler);
|
||||||
protected void onPacketTimeout(PacketEvent event) {
|
|
||||||
if (!cleaningUp) {
|
|
||||||
serverTimeoutListeners.invokePacketSending(AsyncFilterManager.this.reporter, event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Client packets must be synchronized
|
|
||||||
this.clientQueue = new PacketSendingQueue(true) {
|
|
||||||
@Override
|
|
||||||
protected void onPacketTimeout(PacketEvent event) {
|
|
||||||
if (!cleaningUp) {
|
|
||||||
clientTimeoutListeners.invokePacketSending(AsyncFilterManager.this.reporter, event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.serverProcessingQueue = new PacketProcessingQueue(serverQueue);
|
|
||||||
this.clientProcessingQueue = new PacketProcessingQueue(clientQueue);
|
|
||||||
|
|
||||||
this.scheduler = scheduler;
|
this.scheduler = scheduler;
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
@ -219,15 +198,12 @@ public class AsyncFilterManager implements AsynchronousManager {
|
|||||||
List<Integer> removed = serverProcessingQueue.removeListener(handler, listener.getSendingWhitelist());
|
List<Integer> removed = serverProcessingQueue.removeListener(handler, listener.getSendingWhitelist());
|
||||||
|
|
||||||
// We're already taking care of this, so don't do anything
|
// We're already taking care of this, so don't do anything
|
||||||
if (!cleaningUp)
|
playerSendingHandler.sendServerPackets(removed, synchronusOK);
|
||||||
serverQueue.signalPacketUpdate(removed, synchronusOK);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasValidWhitelist(listener.getReceivingWhitelist())) {
|
if (hasValidWhitelist(listener.getReceivingWhitelist())) {
|
||||||
List<Integer> removed = clientProcessingQueue.removeListener(handler, listener.getReceivingWhitelist());
|
List<Integer> removed = clientProcessingQueue.removeListener(handler, listener.getReceivingWhitelist());
|
||||||
|
playerSendingHandler.sendClientPackets(removed, synchronusOK);
|
||||||
if (!cleaningUp)
|
|
||||||
clientQueue.signalPacketUpdate(removed, synchronusOK);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,15 +313,14 @@ public class AsyncFilterManager implements AsynchronousManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cleanupAll() {
|
public void cleanupAll() {
|
||||||
cleaningUp = true;
|
|
||||||
serverProcessingQueue.cleanupAll();
|
serverProcessingQueue.cleanupAll();
|
||||||
serverQueue.cleanupAll();
|
playerSendingHandler.cleanupAll();
|
||||||
|
|
||||||
timeoutListeners.clear();
|
timeoutListeners.clear();
|
||||||
|
|
||||||
serverTimeoutListeners = null;
|
serverTimeoutListeners = null;
|
||||||
clientTimeoutListeners = null;
|
clientTimeoutListeners = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void signalPacketTransmission(PacketEvent packet) {
|
public void signalPacketTransmission(PacketEvent packet) {
|
||||||
signalPacketTransmission(packet, onMainThread());
|
signalPacketTransmission(packet, onMainThread());
|
||||||
@ -366,8 +341,12 @@ public class AsyncFilterManager implements AsynchronousManager {
|
|||||||
"A packet must have been queued before it can be transmitted.");
|
"A packet must have been queued before it can be transmitted.");
|
||||||
|
|
||||||
// Only send if the packet is ready
|
// Only send if the packet is ready
|
||||||
if (marker.decrementProcessingDelay() == 0) {
|
if (marker.decrementProcessingDelay() == 0) {
|
||||||
getSendingQueue(packet).signalPacketUpdate(packet, onMainThread);
|
PacketSendingQueue queue = getSendingQueue(packet, false);
|
||||||
|
|
||||||
|
// No need to create a new queue if the player has logged out
|
||||||
|
if (queue != null)
|
||||||
|
queue.signalPacketUpdate(packet, onMainThread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,8 +355,27 @@ public class AsyncFilterManager implements AsynchronousManager {
|
|||||||
* @param packet - the packet.
|
* @param packet - the packet.
|
||||||
* @return The server or client sending queue the packet belongs to.
|
* @return The server or client sending queue the packet belongs to.
|
||||||
*/
|
*/
|
||||||
private PacketSendingQueue getSendingQueue(PacketEvent packet) {
|
public PacketSendingQueue getSendingQueue(PacketEvent packet) {
|
||||||
return packet.isServerPacket() ? serverQueue : clientQueue;
|
return playerSendingHandler.getSendingQueue(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the sending queue this packet belongs to.
|
||||||
|
* @param packet - the packet.
|
||||||
|
* @param createNew - if TRUE, create a new queue if it hasn't already been created.
|
||||||
|
* @return The server or client sending queue the packet belongs to.
|
||||||
|
*/
|
||||||
|
public PacketSendingQueue getSendingQueue(PacketEvent packet, boolean createNew) {
|
||||||
|
return playerSendingHandler.getSendingQueue(packet, createNew);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the processing queue this packet belongs to.
|
||||||
|
* @param packet - the packet.
|
||||||
|
* @return The server or client sending processing the packet belongs to.
|
||||||
|
*/
|
||||||
|
public PacketProcessingQueue getProcessingQueue(PacketEvent packet) {
|
||||||
|
return packet.isServerPacket() ? serverProcessingQueue : clientProcessingQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -388,24 +386,23 @@ public class AsyncFilterManager implements AsynchronousManager {
|
|||||||
getProcessingQueue(packet).signalProcessingDone();
|
getProcessingQueue(packet).signalProcessingDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the processing queue this packet belongs to.
|
|
||||||
* @param packet - the packet.
|
|
||||||
* @return The server or client sending processing the packet belongs to.
|
|
||||||
*/
|
|
||||||
private PacketProcessingQueue getProcessingQueue(PacketEvent packet) {
|
|
||||||
return packet.isServerPacket() ? serverProcessingQueue : clientProcessingQueue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send any due packets, or clean up packets that have expired.
|
* Send any due packets, or clean up packets that have expired.
|
||||||
*/
|
*/
|
||||||
public void sendProcessedPackets(int tickCounter, boolean onMainThread) {
|
public void sendProcessedPackets(int tickCounter, boolean onMainThread) {
|
||||||
// The server queue is unlikely to need checking that often
|
// The server queue is unlikely to need checking that often
|
||||||
if (tickCounter % 10 == 0) {
|
if (tickCounter % 10 == 0) {
|
||||||
serverQueue.trySendPackets(onMainThread);
|
playerSendingHandler.trySendServerPackets(onMainThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
playerSendingHandler.trySendClientPackets(onMainThread);
|
||||||
|
}
|
||||||
|
|
||||||
clientQueue.trySendPackets(onMainThread);
|
/**
|
||||||
|
* Clean up after a given player has logged out.
|
||||||
|
* @param player - the player that has just logged out.
|
||||||
|
*/
|
||||||
|
public void removePlayer(Player player) {
|
||||||
|
playerSendingHandler.removePlayer(player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,13 +58,13 @@ class PacketProcessingQueue extends AbstractConcurrentListenerMultimap<AsyncList
|
|||||||
private Queue<PacketEventHolder> processingQueue;
|
private Queue<PacketEventHolder> processingQueue;
|
||||||
|
|
||||||
// Packets for sending
|
// Packets for sending
|
||||||
private PacketSendingQueue sendingQueue;
|
private PlayerSendingHandler sendingHandler;
|
||||||
|
|
||||||
public PacketProcessingQueue(PacketSendingQueue sendingQueue) {
|
public PacketProcessingQueue(PlayerSendingHandler sendingHandler) {
|
||||||
this(sendingQueue, INITIAL_CAPACITY, DEFAULT_QUEUE_LIMIT, DEFAULT_MAXIMUM_CONCURRENCY);
|
this(sendingHandler, INITIAL_CAPACITY, DEFAULT_QUEUE_LIMIT, DEFAULT_MAXIMUM_CONCURRENCY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PacketProcessingQueue(PacketSendingQueue sendingQueue, int initialSize, int maximumSize, int maximumConcurrency) {
|
public PacketProcessingQueue(PlayerSendingHandler sendingHandler, int initialSize, int maximumSize, int maximumConcurrency) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.processingQueue = Synchronization.queue(MinMaxPriorityQueue.
|
this.processingQueue = Synchronization.queue(MinMaxPriorityQueue.
|
||||||
@ -74,7 +74,7 @@ class PacketProcessingQueue extends AbstractConcurrentListenerMultimap<AsyncList
|
|||||||
|
|
||||||
this.maximumConcurrency = maximumConcurrency;
|
this.maximumConcurrency = maximumConcurrency;
|
||||||
this.concurrentProcessing = new Semaphore(maximumConcurrency);
|
this.concurrentProcessing = new Semaphore(maximumConcurrency);
|
||||||
this.sendingQueue = sendingQueue;
|
this.sendingHandler = sendingHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -131,8 +131,13 @@ class PacketProcessingQueue extends AbstractConcurrentListenerMultimap<AsyncList
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The packet has no further listeners. Just send it.
|
// The packet has no further listeners. Just send it.
|
||||||
if (marker.decrementProcessingDelay() == 0)
|
if (marker.decrementProcessingDelay() == 0) {
|
||||||
sendingQueue.signalPacketUpdate(packet, onMainThread);
|
PacketSendingQueue sendingQueue = sendingHandler.getSendingQueue(packet, false);
|
||||||
|
|
||||||
|
// In case the player has logged out
|
||||||
|
if (sendingQueue != null)
|
||||||
|
sendingQueue.signalPacketUpdate(packet, onMainThread);
|
||||||
|
}
|
||||||
signalProcessingDone();
|
signalProcessingDone();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -168,5 +173,8 @@ class PacketProcessingQueue extends AbstractConcurrentListenerMultimap<AsyncList
|
|||||||
|
|
||||||
// Remove the rest, just in case
|
// Remove the rest, just in case
|
||||||
clearListeners();
|
clearListeners();
|
||||||
|
|
||||||
|
// Remove every packet in the queue
|
||||||
|
processingQueue.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ import com.comphenix.protocol.reflect.FieldAccessException;
|
|||||||
*/
|
*/
|
||||||
abstract class PacketSendingQueue {
|
abstract class PacketSendingQueue {
|
||||||
|
|
||||||
public static final int INITIAL_CAPACITY = 64;
|
public static final int INITIAL_CAPACITY = 10;
|
||||||
|
|
||||||
private PriorityBlockingQueue<PacketEventHolder> sendingQueue;
|
private PriorityBlockingQueue<PacketEventHolder> sendingQueue;
|
||||||
|
|
||||||
|
@ -0,0 +1,217 @@
|
|||||||
|
package com.comphenix.protocol.async;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.error.ErrorReporter;
|
||||||
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
|
import com.comphenix.protocol.injector.SortedPacketListenerList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains every sending queue for every player.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
class PlayerSendingHandler {
|
||||||
|
|
||||||
|
private ErrorReporter reporter;
|
||||||
|
private ConcurrentHashMap<String, QueueContainer> playerSendingQueues;
|
||||||
|
|
||||||
|
// Timeout listeners
|
||||||
|
private SortedPacketListenerList serverTimeoutListeners;
|
||||||
|
private SortedPacketListenerList clientTimeoutListeners;
|
||||||
|
|
||||||
|
// Whether or not we're currently cleaning up
|
||||||
|
private volatile boolean cleaningUp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sending queues for a given player.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
private class QueueContainer {
|
||||||
|
private PacketSendingQueue serverQueue;
|
||||||
|
private PacketSendingQueue clientQueue;
|
||||||
|
|
||||||
|
public QueueContainer() {
|
||||||
|
// Server packets are synchronized already
|
||||||
|
serverQueue = new PacketSendingQueue(false) {
|
||||||
|
@Override
|
||||||
|
protected void onPacketTimeout(PacketEvent event) {
|
||||||
|
if (!cleaningUp) {
|
||||||
|
serverTimeoutListeners.invokePacketSending(reporter, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Client packets must be synchronized
|
||||||
|
clientQueue = new PacketSendingQueue(true) {
|
||||||
|
@Override
|
||||||
|
protected void onPacketTimeout(PacketEvent event) {
|
||||||
|
if (!cleaningUp) {
|
||||||
|
clientTimeoutListeners.invokePacketSending(reporter, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketSendingQueue getServerQueue() {
|
||||||
|
return serverQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketSendingQueue getClientQueue() {
|
||||||
|
return clientQueue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlayerSendingHandler(ErrorReporter reporter,
|
||||||
|
SortedPacketListenerList serverTimeoutListeners, SortedPacketListenerList clientTimeoutListeners) {
|
||||||
|
|
||||||
|
this.reporter = reporter;
|
||||||
|
this.serverTimeoutListeners = serverTimeoutListeners;
|
||||||
|
this.clientTimeoutListeners = clientTimeoutListeners;
|
||||||
|
|
||||||
|
// Initialize storage of queues
|
||||||
|
playerSendingQueues = new ConcurrentHashMap<String, QueueContainer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the sending queue this packet belongs to.
|
||||||
|
* @param packet - the packet.
|
||||||
|
* @return The server or client sending queue the packet belongs to.
|
||||||
|
*/
|
||||||
|
public PacketSendingQueue getSendingQueue(PacketEvent packet) {
|
||||||
|
return getSendingQueue(packet, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the sending queue this packet belongs to.
|
||||||
|
* @param packet - the packet.
|
||||||
|
* @param createNew - if TRUE, create a new queue if it hasn't already been created.
|
||||||
|
* @return The server or client sending queue the packet belongs to.
|
||||||
|
*/
|
||||||
|
public PacketSendingQueue getSendingQueue(PacketEvent packet, boolean createNew) {
|
||||||
|
String name = packet.getPlayer().getName();
|
||||||
|
QueueContainer queues = playerSendingQueues.get(name);
|
||||||
|
|
||||||
|
// Safe concurrent initialization
|
||||||
|
if (queues == null && createNew) {
|
||||||
|
QueueContainer previous = playerSendingQueues.putIfAbsent(name, new QueueContainer());
|
||||||
|
|
||||||
|
if (previous != null)
|
||||||
|
queues = previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for NULL again
|
||||||
|
if (queues != null)
|
||||||
|
return packet.isServerPacket() ? queues.getServerQueue() : queues.getClientQueue();
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send all pending packets.
|
||||||
|
*/
|
||||||
|
public void sendAllPackets() {
|
||||||
|
if (!cleaningUp) {
|
||||||
|
for (QueueContainer queues : playerSendingQueues.values()) {
|
||||||
|
queues.getClientQueue().cleanupAll();
|
||||||
|
queues.getServerQueue().cleanupAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Immediately send every server packet with the given list of IDs.
|
||||||
|
* @param ids - ID of every packet to send immediately.
|
||||||
|
* @param synchronusOK - whether or not we're running on the main thread.
|
||||||
|
*/
|
||||||
|
public void sendServerPackets(List<Integer> ids, boolean synchronusOK) {
|
||||||
|
if (!cleaningUp) {
|
||||||
|
for (QueueContainer queue : playerSendingQueues.values()) {
|
||||||
|
queue.getServerQueue().signalPacketUpdate(ids, synchronusOK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Immediately send every client packet with the given list of IDs.
|
||||||
|
* @param ids - ID of every packet to send immediately.
|
||||||
|
* @param synchronusOK - whether or not we're running on the main thread.
|
||||||
|
*/
|
||||||
|
public void sendClientPackets(List<Integer> ids, boolean synchronusOK) {
|
||||||
|
if (!cleaningUp) {
|
||||||
|
for (QueueContainer queue : playerSendingQueues.values()) {
|
||||||
|
queue.getClientQueue().signalPacketUpdate(ids, synchronusOK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send any outstanding server packets.
|
||||||
|
* @param onMainThread - whether or not this is occuring on the main thread.
|
||||||
|
*/
|
||||||
|
public void trySendServerPackets(boolean onMainThread) {
|
||||||
|
for (QueueContainer queue : playerSendingQueues.values()) {
|
||||||
|
queue.getServerQueue().trySendPackets(onMainThread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send any outstanding server packets.
|
||||||
|
* @param onMainThread - whether or not this is occuring on the main thread.
|
||||||
|
*/
|
||||||
|
public void trySendClientPackets(boolean onMainThread) {
|
||||||
|
for (QueueContainer queue : playerSendingQueues.values()) {
|
||||||
|
queue.getClientQueue().trySendPackets(onMainThread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve every server packet queue for every player.
|
||||||
|
* @return Every sever packet queue.
|
||||||
|
*/
|
||||||
|
public List<PacketSendingQueue> getServerQueues() {
|
||||||
|
List<PacketSendingQueue> result = new ArrayList<PacketSendingQueue>();
|
||||||
|
|
||||||
|
for (QueueContainer queue : playerSendingQueues.values())
|
||||||
|
result.add(queue.getServerQueue());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve every client packet queue for every player.
|
||||||
|
* @return Every client packet queue.
|
||||||
|
*/
|
||||||
|
public List<PacketSendingQueue> getClientQueues() {
|
||||||
|
List<PacketSendingQueue> result = new ArrayList<PacketSendingQueue>();
|
||||||
|
|
||||||
|
for (QueueContainer queue : playerSendingQueues.values())
|
||||||
|
result.add(queue.getClientQueue());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send all pending packets and clean up queues.
|
||||||
|
*/
|
||||||
|
public void cleanupAll() {
|
||||||
|
cleaningUp = true;
|
||||||
|
|
||||||
|
sendAllPackets();
|
||||||
|
playerSendingQueues.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when a player has just logged out.
|
||||||
|
* @param player - the player that just logged out.
|
||||||
|
*/
|
||||||
|
public void removePlayer(Player player) {
|
||||||
|
String name = player.getName();
|
||||||
|
|
||||||
|
// Every packet will be dropped - there's nothing we can do
|
||||||
|
playerSendingQueues.remove(name);
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,7 @@ package com.comphenix.protocol.events;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.EventObject;
|
import java.util.EventObject;
|
||||||
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
@ -32,8 +33,10 @@ public class PacketEvent extends EventObject implements Cancellable {
|
|||||||
* Automatically generated by Eclipse.
|
* Automatically generated by Eclipse.
|
||||||
*/
|
*/
|
||||||
private static final long serialVersionUID = -5360289379097430620L;
|
private static final long serialVersionUID = -5360289379097430620L;
|
||||||
|
|
||||||
private transient Player player;
|
private transient WeakReference<Player> playerReference;
|
||||||
|
private transient Player offlinePlayer;
|
||||||
|
|
||||||
private PacketContainer packet;
|
private PacketContainer packet;
|
||||||
private boolean serverPacket;
|
private boolean serverPacket;
|
||||||
private boolean cancel;
|
private boolean cancel;
|
||||||
@ -52,14 +55,14 @@ public class PacketEvent extends EventObject implements Cancellable {
|
|||||||
private PacketEvent(Object source, PacketContainer packet, Player player, boolean serverPacket) {
|
private PacketEvent(Object source, PacketContainer packet, Player player, boolean serverPacket) {
|
||||||
super(source);
|
super(source);
|
||||||
this.packet = packet;
|
this.packet = packet;
|
||||||
this.player = player;
|
this.playerReference = new WeakReference<Player>(player);
|
||||||
this.serverPacket = serverPacket;
|
this.serverPacket = serverPacket;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PacketEvent(PacketEvent origial, AsyncMarker asyncMarker) {
|
private PacketEvent(PacketEvent origial, AsyncMarker asyncMarker) {
|
||||||
super(origial.source);
|
super(origial.source);
|
||||||
this.packet = origial.packet;
|
this.packet = origial.packet;
|
||||||
this.player = origial.player;
|
this.playerReference = origial.playerReference;
|
||||||
this.cancel = origial.cancel;
|
this.cancel = origial.cancel;
|
||||||
this.serverPacket = origial.serverPacket;
|
this.serverPacket = origial.serverPacket;
|
||||||
this.asyncMarker = asyncMarker;
|
this.asyncMarker = asyncMarker;
|
||||||
@ -143,7 +146,7 @@ public class PacketEvent extends EventObject implements Cancellable {
|
|||||||
* @return The player associated with this event.
|
* @return The player associated with this event.
|
||||||
*/
|
*/
|
||||||
public Player getPlayer() {
|
public Player getPlayer() {
|
||||||
return player;
|
return playerReference.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -197,18 +200,20 @@ public class PacketEvent extends EventObject implements Cancellable {
|
|||||||
output.defaultWriteObject();
|
output.defaultWriteObject();
|
||||||
|
|
||||||
// Write the name of the player (or NULL if it's not set)
|
// Write the name of the player (or NULL if it's not set)
|
||||||
output.writeObject(player != null ? new SerializedOfflinePlayer(player) : null);
|
output.writeObject(playerReference.get() != null ? new SerializedOfflinePlayer(playerReference.get()) : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readObject(ObjectInputStream input) throws ClassNotFoundException, IOException {
|
private void readObject(ObjectInputStream input) throws ClassNotFoundException, IOException {
|
||||||
// Default deserialization
|
// Default deserialization
|
||||||
input.defaultReadObject();
|
input.defaultReadObject();
|
||||||
|
|
||||||
final SerializedOfflinePlayer offlinePlayer = (SerializedOfflinePlayer) input.readObject();
|
final SerializedOfflinePlayer serialized = (SerializedOfflinePlayer) input.readObject();
|
||||||
|
|
||||||
if (offlinePlayer != null) {
|
// Better than nothing
|
||||||
// Better than nothing
|
if (serialized != null) {
|
||||||
player = offlinePlayer.getPlayer();
|
// Store it, to prevent weak reference from cleaning up the reference
|
||||||
|
offlinePlayer = serialized.getPlayer();
|
||||||
|
playerReference = new WeakReference<Player>(offlinePlayer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -598,46 +598,21 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
manager.registerEvents(new Listener() {
|
manager.registerEvents(new Listener() {
|
||||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
public void onPrePlayerJoin(PlayerJoinEvent event) {
|
public void onPrePlayerJoin(PlayerJoinEvent event) {
|
||||||
try {
|
PacketFilterManager.this.onPrePlayerJoin(event);
|
||||||
// Let's clean up the other injection first.
|
|
||||||
playerInjection.uninjectPlayer(event.getPlayer().getAddress());
|
|
||||||
} catch (Exception e) {
|
|
||||||
reporter.reportDetailed(PacketFilterManager.this, "Unable to uninject net handler for player.", e, event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
|
||||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||||
try {
|
PacketFilterManager.this.onPlayerJoin(event);
|
||||||
// This call will be ignored if no listeners are registered
|
|
||||||
playerInjection.injectPlayer(event.getPlayer());
|
|
||||||
} catch (Exception e) {
|
|
||||||
reporter.reportDetailed(PacketFilterManager.this, "Unable to inject player.", e, event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
|
||||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||||
try {
|
PacketFilterManager.this.onPlayerQuit(event);
|
||||||
playerInjection.handleDisconnect(event.getPlayer());
|
|
||||||
playerInjection.uninjectPlayer(event.getPlayer());
|
|
||||||
} catch (Exception e) {
|
|
||||||
reporter.reportDetailed(PacketFilterManager.this, "Unable to uninject logged off player.", e, event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
|
||||||
public void onPluginDisabled(PluginDisableEvent event) {
|
public void onPluginDisabled(PluginDisableEvent event) {
|
||||||
try {
|
PacketFilterManager.this.onPluginDisabled(event, plugin);
|
||||||
// Clean up in case the plugin forgets
|
|
||||||
if (event.getPlugin() != plugin) {
|
|
||||||
removePacketListeners(event.getPlugin());
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
reporter.reportDetailed(PacketFilterManager.this, "Unable handle disabled plugin.", e, event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}, plugin);
|
}, plugin);
|
||||||
@ -648,6 +623,47 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onPrePlayerJoin(PlayerJoinEvent event) {
|
||||||
|
try {
|
||||||
|
// Let's clean up the other injection first.
|
||||||
|
playerInjection.uninjectPlayer(event.getPlayer().getAddress());
|
||||||
|
} catch (Exception e) {
|
||||||
|
reporter.reportDetailed(PacketFilterManager.this, "Unable to uninject net handler for player.", e, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onPlayerJoin(PlayerJoinEvent event) {
|
||||||
|
try {
|
||||||
|
// This call will be ignored if no listeners are registered
|
||||||
|
playerInjection.injectPlayer(event.getPlayer());
|
||||||
|
} catch (Exception e) {
|
||||||
|
reporter.reportDetailed(PacketFilterManager.this, "Unable to inject player.", e, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onPlayerQuit(PlayerQuitEvent event) {
|
||||||
|
try {
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
|
||||||
|
asyncFilterManager.removePlayer(player);
|
||||||
|
playerInjection.handleDisconnect(player);
|
||||||
|
playerInjection.uninjectPlayer(player);
|
||||||
|
} catch (Exception e) {
|
||||||
|
reporter.reportDetailed(PacketFilterManager.this, "Unable to uninject logged off player.", e, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onPluginDisabled(PluginDisableEvent event, Plugin protocolLibrary) {
|
||||||
|
try {
|
||||||
|
// Clean up in case the plugin forgets
|
||||||
|
if (event.getPlugin() != protocolLibrary) {
|
||||||
|
removePacketListeners(event.getPlugin());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
reporter.reportDetailed(PacketFilterManager.this, "Unable handle disabled plugin.", e, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the number of listeners that expect packets during playing.
|
* Retrieve the number of listeners that expect packets during playing.
|
||||||
* @return Number of listeners.
|
* @return Number of listeners.
|
||||||
@ -689,7 +705,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
|
|
||||||
// Yes, this is crazy.
|
// Yes, this is crazy.
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
private void registerOld(PluginManager manager, Plugin plugin) {
|
private void registerOld(PluginManager manager, final Plugin plugin) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ClassLoader loader = manager.getClass().getClassLoader();
|
ClassLoader loader = manager.getClass().getClassLoader();
|
||||||
@ -699,6 +715,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
Class eventPriority = loader.loadClass("org.bukkit.event.Event$Priority");
|
Class eventPriority = loader.loadClass("org.bukkit.event.Event$Priority");
|
||||||
|
|
||||||
// Get the priority
|
// Get the priority
|
||||||
|
Object priorityLowest = Enum.valueOf(eventPriority, "Lowest");
|
||||||
Object priorityMonitor = Enum.valueOf(eventPriority, "Monitor");
|
Object priorityMonitor = Enum.valueOf(eventPriority, "Monitor");
|
||||||
|
|
||||||
// Get event types
|
// Get event types
|
||||||
@ -714,26 +731,40 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
Method registerEvent = FuzzyReflection.fromObject(manager).getMethodByParameters("registerEvent",
|
Method registerEvent = FuzzyReflection.fromObject(manager).getMethodByParameters("registerEvent",
|
||||||
eventTypes, Listener.class, eventPriority, Plugin.class);
|
eventTypes, Listener.class, eventPriority, Plugin.class);
|
||||||
|
|
||||||
|
Enhancer playerLow = new Enhancer();
|
||||||
Enhancer playerEx = new Enhancer();
|
Enhancer playerEx = new Enhancer();
|
||||||
Enhancer serverEx = new Enhancer();
|
Enhancer serverEx = new Enhancer();
|
||||||
|
|
||||||
playerEx.setSuperclass(playerListener);
|
playerLow.setSuperclass(playerListener);
|
||||||
playerEx.setClassLoader(classLoader);
|
playerLow.setClassLoader(classLoader);
|
||||||
playerEx.setCallback(new MethodInterceptor() {
|
playerLow.setCallback(new MethodInterceptor() {
|
||||||
@Override
|
@Override
|
||||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||||
// Must have a parameter
|
// Must have a parameter
|
||||||
|
if (args.length == 1) {
|
||||||
|
Object event = args[0];
|
||||||
|
|
||||||
|
if (event instanceof PlayerJoinEvent) {
|
||||||
|
onPrePlayerJoin((PlayerJoinEvent) event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
playerEx.setSuperclass(playerListener);
|
||||||
|
playerEx.setClassLoader(classLoader);
|
||||||
|
playerEx.setCallback(new MethodInterceptor() {
|
||||||
|
@Override
|
||||||
|
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||||
if (args.length == 1) {
|
if (args.length == 1) {
|
||||||
Object event = args[0];
|
Object event = args[0];
|
||||||
|
|
||||||
// Check for the correct event
|
// Check for the correct event
|
||||||
if (event instanceof PlayerJoinEvent) {
|
if (event instanceof PlayerJoinEvent) {
|
||||||
Player player = ((PlayerJoinEvent) event).getPlayer();
|
onPlayerJoin((PlayerJoinEvent) event);
|
||||||
playerInjection.injectPlayer(player);
|
|
||||||
} else if (event instanceof PlayerQuitEvent) {
|
} else if (event instanceof PlayerQuitEvent) {
|
||||||
Player player = ((PlayerQuitEvent) event).getPlayer();
|
onPlayerQuit((PlayerQuitEvent) event);
|
||||||
playerInjection.handleDisconnect(player);
|
|
||||||
playerInjection.uninjectPlayer(player);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -751,16 +782,18 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
Object event = args[0];
|
Object event = args[0];
|
||||||
|
|
||||||
if (event instanceof PluginDisableEvent)
|
if (event instanceof PluginDisableEvent)
|
||||||
removePacketListeners(((PluginDisableEvent) event).getPlugin());
|
onPluginDisabled((PluginDisableEvent) event, plugin);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create our listener
|
// Create our listener
|
||||||
|
Object playerProxyLow = playerLow.create();
|
||||||
Object playerProxy = playerEx.create();
|
Object playerProxy = playerEx.create();
|
||||||
Object serverProxy = serverEx.create();
|
Object serverProxy = serverEx.create();
|
||||||
|
|
||||||
|
registerEvent.invoke(manager, playerJoinType, playerProxyLow, priorityLowest, plugin);
|
||||||
registerEvent.invoke(manager, playerJoinType, playerProxy, priorityMonitor, plugin);
|
registerEvent.invoke(manager, playerJoinType, playerProxy, priorityMonitor, plugin);
|
||||||
registerEvent.invoke(manager, playerQuitType, playerProxy, priorityMonitor, plugin);
|
registerEvent.invoke(manager, playerQuitType, playerProxy, priorityMonitor, plugin);
|
||||||
registerEvent.invoke(manager, pluginDisabledType, serverProxy, priorityMonitor, plugin);
|
registerEvent.invoke(manager, pluginDisabledType, serverProxy, priorityMonitor, plugin);
|
||||||
|
In neuem Issue referenzieren
Einen Benutzer sperren