mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-11-23 19:16:14 +01:00
Improved API and performance.
API changes: * The PacketListener now uses a "ListeningWhitelist" class to report which packet ids it wishes to listen in on for either the client or the server. This makes it possible to use your plugin class as the listener more easily. * Added a priority system similar to Bukkit events. Performance changes: * Create and maintain a separate list of listeners for each packet ID. This uses slightly more memory, but is far more efficient. Especially in light of the priority system. In addition, the listener lists are (hopefully) concurrent. They're optimized for read-access, however. Adding or removing a listener is a O(n) operation.
This commit is contained in:
parent
bb8bec907c
commit
88dcf0cb32
@ -119,11 +119,17 @@ public interface ProtocolManager {
|
||||
public PacketContainer createPacket(int id, boolean skipDefaults);
|
||||
|
||||
/**
|
||||
* Retieves a set of every enabled packet.
|
||||
* @return Every packet filter.
|
||||
* Retrieves a immutable set containing the ID of the sent server packets that will be observed by listeners.
|
||||
* @return Every filtered server packet.
|
||||
*/
|
||||
public Set<Integer> getPacketFilters();
|
||||
|
||||
public Set<Integer> getSendingFilters();
|
||||
|
||||
/**
|
||||
* Retrieves a immutable set containing the ID of the recieved client packets that will be observed by listeners.
|
||||
* @return Every filtered client packet.
|
||||
*/
|
||||
public Set<Integer> getReceivingFilters();
|
||||
|
||||
/**
|
||||
* Determines whether or not this protocol mananger has been disabled.
|
||||
* @return TRUE if it has, FALSE otherwise.
|
||||
|
@ -1,3 +1,20 @@
|
||||
/*
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2012 Kristian S. Stangeland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2012 Kristian S. Stangeland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* Determines which packets will be observed by a listener, and with what priority.
|
||||
|
||||
* @author Kristian
|
||||
*/
|
||||
public class ListeningWhitelist {
|
||||
|
||||
/**
|
||||
* A whitelist with no packets - indicates that the listener shouldn't observe any packets.
|
||||
*/
|
||||
public static ListeningWhitelist EMPTY_WHITELIST = new ListeningWhitelist(ListenerPriority.LOW);
|
||||
|
||||
private ListenerPriority priority;
|
||||
private Set<Integer> whitelist;
|
||||
|
||||
/**
|
||||
* Creates a packet whitelist for a given priority with a set of packet IDs.
|
||||
* @param priority - the listener priority.
|
||||
* @param whitelist - set of IDs to observe/enable.
|
||||
*/
|
||||
public ListeningWhitelist(ListenerPriority priority, Set<Integer> whitelist) {
|
||||
this.priority = priority;
|
||||
this.whitelist = whitelist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet whitelist of a given priority for a list of packets.
|
||||
* @param priority - the listener priority.
|
||||
* @param whitelist - list of packet IDs to observe/enable.
|
||||
*/
|
||||
public ListeningWhitelist(ListenerPriority priority, Integer... whitelist) {
|
||||
this.priority = priority;
|
||||
this.whitelist = Sets.newHashSet(whitelist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not this whitelist has any enabled packets.
|
||||
* @return TRUE if there are any packets, FALSE otherwise.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return whitelist != null || whitelist.size() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the priority in the execution order of the packet listener. Highest priority will be executed last.
|
||||
* @return Execution order in terms of priority.
|
||||
*/
|
||||
public ListenerPriority getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of packets that will be observed by the listeners.
|
||||
* @return Packet whitelist.
|
||||
*/
|
||||
public Set<Integer> getWhitelist() {
|
||||
return whitelist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode(){
|
||||
return Objects.hashCode(priority, whitelist);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj){
|
||||
if(obj instanceof ListeningWhitelist){
|
||||
final ListeningWhitelist other = (ListeningWhitelist) obj;
|
||||
return Objects.equal(priority, other.priority)
|
||||
&& Objects.equal(whitelist, other.whitelist);
|
||||
} else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (this == EMPTY_WHITELIST)
|
||||
return "EMPTY_WHITELIST";
|
||||
else
|
||||
return Objects.toStringHelper(this)
|
||||
.add("priority", priority)
|
||||
.add("packets", whitelist).toString();
|
||||
}
|
||||
|
||||
}
|
@ -17,12 +17,8 @@
|
||||
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* Represents a packet listener with useful constructors.
|
||||
@ -32,10 +28,10 @@ import com.google.common.collect.Sets;
|
||||
public abstract class PacketAdapter implements PacketListener {
|
||||
|
||||
protected Plugin plugin;
|
||||
protected Set<Integer> packetsID;
|
||||
protected ConnectionSide connectionSide;
|
||||
protected ListenerPriority listenerPriority;
|
||||
|
||||
protected ListeningWhitelist receivingWhitelist = ListeningWhitelist.EMPTY_WHITELIST;
|
||||
protected ListeningWhitelist sendingWhitelist = ListeningWhitelist.EMPTY_WHITELIST;
|
||||
|
||||
/**
|
||||
* Initialize a packet listener.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
@ -54,10 +50,23 @@ public abstract class PacketAdapter implements PacketListener {
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
*/
|
||||
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority, Integer... packets) {
|
||||
if (plugin == null)
|
||||
throw new IllegalArgumentException("plugin cannot be null");
|
||||
if (connectionSide == null)
|
||||
throw new IllegalArgumentException("connectionSide cannot be null");
|
||||
if (listenerPriority == null)
|
||||
throw new IllegalArgumentException("listenerPriority cannot be null");
|
||||
if (packets == null)
|
||||
throw new IllegalArgumentException("packets cannot be null");
|
||||
|
||||
// Add whitelists
|
||||
if (connectionSide.isForServer())
|
||||
sendingWhitelist = new ListeningWhitelist(listenerPriority, packets);
|
||||
if (connectionSide.isForClient())
|
||||
receivingWhitelist = new ListeningWhitelist(listenerPriority, packets);
|
||||
|
||||
this.plugin = plugin;
|
||||
this.connectionSide = connectionSide;
|
||||
this.packetsID = Sets.newHashSet(packets);
|
||||
this.listenerPriority = listenerPriority;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -71,13 +80,13 @@ public abstract class PacketAdapter implements PacketListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConnectionSide getConnectionSide() {
|
||||
return connectionSide;
|
||||
public ListeningWhitelist getReceivingWhitelist() {
|
||||
return receivingWhitelist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Integer> getPacketsID() {
|
||||
return packetsID;
|
||||
public ListeningWhitelist getSendingWhitelist() {
|
||||
return sendingWhitelist;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -85,11 +94,6 @@ public abstract class PacketAdapter implements PacketListener {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenerPriority getListenerPriority() {
|
||||
return listenerPriority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the name of the plugin that has been associated with the listener.
|
||||
* @return Name of the associated plugin.
|
||||
@ -113,8 +117,9 @@ public abstract class PacketAdapter implements PacketListener {
|
||||
@Override
|
||||
public String toString() {
|
||||
// This is used by the error reporter
|
||||
return String.format("PacketAdapter[plugin=%s, side=%s, packets=%s]",
|
||||
getPluginName(this), getConnectionSide().name(),
|
||||
Joiner.on(", ").join(packetsID));
|
||||
return String.format("PacketAdapter[plugin=%s, sending=%s, receiving=%s]",
|
||||
getPluginName(this),
|
||||
sendingWhitelist,
|
||||
receivingWhitelist);
|
||||
}
|
||||
}
|
||||
|
@ -17,8 +17,6 @@
|
||||
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
@ -43,22 +41,16 @@ public interface PacketListener {
|
||||
public void onPacketReceiving(PacketEvent event);
|
||||
|
||||
/**
|
||||
* Retrieve whether or not we're listening for client or server packets.
|
||||
* @return The type of packets we expect.
|
||||
* Retrieve which packets sent by the server this listener will observe.
|
||||
* @return List of server packets to observe, along with the priority.
|
||||
*/
|
||||
public ConnectionSide getConnectionSide();
|
||||
public ListeningWhitelist getSendingWhitelist();
|
||||
|
||||
/**
|
||||
* Retrieve the priority in the execution order of this packet listener. Highest priority will be executed last.
|
||||
* @return Execution order in terms of priority.
|
||||
* Retrieve which packets sent by the client this listener will observe.
|
||||
* @return List of server packets to observe, along with the priority.
|
||||
*/
|
||||
public ListenerPriority getListenerPriority();
|
||||
|
||||
/**
|
||||
* Set of packet ids we expect to recieve.
|
||||
* @return Packets IDs.
|
||||
*/
|
||||
public Set<Integer> getPacketsID();
|
||||
public ListeningWhitelist getReceivingWhitelist();
|
||||
|
||||
/**
|
||||
* Retrieve the plugin that created list packet listener.
|
||||
|
@ -1,7 +1,6 @@
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@ -9,10 +8,11 @@ import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.primitives.Ints;
|
||||
|
||||
/**
|
||||
@ -23,36 +23,32 @@ import com.google.common.primitives.Ints;
|
||||
public class ConcurrentListenerMultimap {
|
||||
|
||||
// The core of our map
|
||||
protected ConcurrentMap<Integer, SortedArrayList<PacketListener>> listeners =
|
||||
new ConcurrentHashMap<Integer, SortedArrayList<PacketListener>>();
|
||||
protected ConcurrentMap<Integer, SortedCopyOnWriteArray<PrioritizedListener>> listeners =
|
||||
new ConcurrentHashMap<Integer, SortedCopyOnWriteArray<PrioritizedListener>>();
|
||||
|
||||
/**
|
||||
* Adds a listener to its requested list of packet recievers.
|
||||
* @param listener - listener with a list of packets to recieve notifcations for.
|
||||
*/
|
||||
public void addListener(PacketListener listener) {
|
||||
for (Integer packetID : listener.getPacketsID()) {
|
||||
addListener(packetID, listener);
|
||||
public void addListener(PacketListener listener, ListeningWhitelist whitelist) {
|
||||
|
||||
PrioritizedListener prioritized = new PrioritizedListener(listener, whitelist.getPriority());
|
||||
|
||||
for (Integer packetID : whitelist.getWhitelist()) {
|
||||
addListener(packetID, prioritized);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the listener to a specific packet notifcation list
|
||||
private void addListener(Integer packetID, PacketListener listener) {
|
||||
private void addListener(Integer packetID, PrioritizedListener listener) {
|
||||
|
||||
SortedArrayList<PacketListener> list = listeners.get(packetID);
|
||||
SortedCopyOnWriteArray<PrioritizedListener> list = listeners.get(packetID);
|
||||
|
||||
// We don't want to create this for every lookup
|
||||
if (list == null) {
|
||||
// It would be nice if we could use a PriorityBlockingQueue, but it doesn't preseve iterator order,
|
||||
// which is a essential feature for our purposes.
|
||||
final SortedArrayList<PacketListener> value = new SortedArrayList<PacketListener>(new Comparator<PacketListener>() {
|
||||
@Override
|
||||
public int compare(PacketListener o1, PacketListener o2) {
|
||||
// This ensures that lower priority listeners are executed first
|
||||
return Ints.compare(o1.getListenerPriority().getSlot(),
|
||||
o2.getListenerPriority().getSlot());
|
||||
}
|
||||
});
|
||||
final SortedCopyOnWriteArray<PrioritizedListener> value = new SortedCopyOnWriteArray<PrioritizedListener>();
|
||||
|
||||
list = listeners.putIfAbsent(packetID, value);
|
||||
|
||||
@ -74,21 +70,26 @@ public class ConcurrentListenerMultimap {
|
||||
* @param listener - listener to remove.
|
||||
* @return Every packet ID that was removed due to no listeners.
|
||||
*/
|
||||
public List<Integer> removeListener(PacketListener listener) {
|
||||
public List<Integer> removeListener(PacketListener listener, ListeningWhitelist whitelist) {
|
||||
|
||||
List<Integer> removedPackets = new ArrayList<Integer>();
|
||||
|
||||
// Again, not terribly efficient. But adding or removing listeners should be a rare event.
|
||||
for (Integer packetID : listener.getPacketsID()) {
|
||||
for (Integer packetID : whitelist.getWhitelist()) {
|
||||
|
||||
SortedArrayList<PacketListener> list = listeners.get(packetID);
|
||||
SortedCopyOnWriteArray<PrioritizedListener> list = listeners.get(packetID);
|
||||
|
||||
// Remove any listeners
|
||||
if (list != null) {
|
||||
synchronized(list) {
|
||||
// Don't remove from newly created lists
|
||||
if (list.size() > 0) {
|
||||
list.removeAll(listener);
|
||||
// Remove this listener
|
||||
for (Iterator<PrioritizedListener> it = list.iterator(); it.hasNext(); ) {
|
||||
if (it.next().getListener().equals(list)) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
if (list.size() == 0) {
|
||||
listeners.remove(packetID);
|
||||
@ -110,20 +111,21 @@ public class ConcurrentListenerMultimap {
|
||||
* @param event - the packet event to invoke.
|
||||
*/
|
||||
public void invokePacketRecieving(Logger logger, PacketEvent event) {
|
||||
SortedArrayList<PacketListener> list = listeners.get(event.getPacketID());
|
||||
SortedCopyOnWriteArray<PrioritizedListener> list = listeners.get(event.getPacketID());
|
||||
|
||||
if (list == null)
|
||||
return;
|
||||
|
||||
// We have to be careful. Cannot modify the underlying list when sending notifications.
|
||||
synchronized (list) {
|
||||
for (PacketListener listener : list) {
|
||||
for (PrioritizedListener element : list) {
|
||||
try {
|
||||
listener.onPacketReceiving(event);
|
||||
element.getListener().onPacketReceiving(event);
|
||||
} catch (Exception e) {
|
||||
// Minecraft doesn't want your Exception.
|
||||
logger.log(Level.SEVERE,
|
||||
"Exception occured in onPacketReceiving() for " + PacketAdapter.getPluginName(listener), e);
|
||||
"Exception occured in onPacketReceiving() for " +
|
||||
PacketAdapter.getPluginName(element.getListener()), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -135,75 +137,50 @@ public class ConcurrentListenerMultimap {
|
||||
* @param event - the packet event to invoke.
|
||||
*/
|
||||
public void invokePacketSending(Logger logger, PacketEvent event) {
|
||||
SortedArrayList<PacketListener> list = listeners.get(event.getPacketID());
|
||||
SortedCopyOnWriteArray<PrioritizedListener> list = listeners.get(event.getPacketID());
|
||||
|
||||
if (list == null)
|
||||
return;
|
||||
|
||||
synchronized (list) {
|
||||
for (PacketListener listener : list) {
|
||||
for (PrioritizedListener element : list) {
|
||||
try {
|
||||
listener.onPacketSending(event);
|
||||
element.getListener().onPacketSending(event);
|
||||
} catch (Exception e) {
|
||||
// Minecraft doesn't want your Exception.
|
||||
logger.log(Level.SEVERE,
|
||||
"Exception occured in onPacketReceiving() for " + PacketAdapter.getPluginName(listener), e);
|
||||
"Exception occured in onPacketReceiving() for " +
|
||||
PacketAdapter.getPluginName(element.getListener()), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An implicitly sorted array list that preserves insertion order and maintains duplicates.
|
||||
*
|
||||
* Note that only the {@link insertSorted} method will update the list correctly,
|
||||
* @param <T> - type of the sorted list.
|
||||
* A listener with an associated priority.
|
||||
*/
|
||||
private class SortedArrayList<T> implements Iterable<T> {
|
||||
|
||||
private Comparator<T> comparator;
|
||||
private List<T> list = new ArrayList<T>();
|
||||
private class PrioritizedListener implements Comparable<PrioritizedListener> {
|
||||
private PacketListener listener;
|
||||
private ListenerPriority priority;
|
||||
|
||||
public SortedArrayList(Comparator<T> comparator) {
|
||||
this.comparator = comparator;
|
||||
public PrioritizedListener(PacketListener listener, ListenerPriority priority) {
|
||||
this.listener = listener;
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the given element in the proper location.
|
||||
* @param value - element to insert.
|
||||
*/
|
||||
public void insertSorted(T value) {
|
||||
list.add(value);
|
||||
for (int i = list.size() - 1; i > 0 && comparator.compare(value, list.get(i-1)) < 0; i--) {
|
||||
T tmp = list.get(i);
|
||||
list.set(i, list.get(i-1));
|
||||
list.set(i-1, tmp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes every instance of the given element.
|
||||
* @param element - element to remove.
|
||||
*/
|
||||
public void removeAll(T element) {
|
||||
for (Iterator<T> it = list.iterator(); it.hasNext(); ) {
|
||||
if (Objects.equal(it.next(), element)) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the size of the list.
|
||||
* @return Size of the list.
|
||||
*/
|
||||
public int size() {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return list.iterator();
|
||||
public int compareTo(PrioritizedListener other) {
|
||||
// This ensures that lower priority listeners are executed first
|
||||
return Ints.compare(this.getPriority().getSlot(),
|
||||
other.getPriority().getSlot());
|
||||
}
|
||||
|
||||
public PacketListener getListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
public ListenerPriority getPriority() {
|
||||
return priority;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@ -50,11 +49,12 @@ import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
public final class PacketFilterManager implements ProtocolManager {
|
||||
|
||||
private Set<PacketListener> packetListeners = new CopyOnWriteArraySet<PacketListener>();
|
||||
// Create a concurrent set
|
||||
private Set<PacketListener> packetListeners =
|
||||
Collections.newSetFromMap(new ConcurrentHashMap<PacketListener, Boolean>());
|
||||
|
||||
// Player injection
|
||||
private Map<DataInputStream, Player> connectionLookup = new ConcurrentHashMap<DataInputStream, Player>();
|
||||
@ -111,37 +111,52 @@ public final class PacketFilterManager implements ProtocolManager {
|
||||
public void addPacketListener(PacketListener listener) {
|
||||
if (listener == null)
|
||||
throw new IllegalArgumentException("listener cannot be NULL.");
|
||||
|
||||
ConnectionSide side = listener.getConnectionSide();
|
||||
packetListeners.add(listener);
|
||||
|
||||
// Add listeners
|
||||
if (side.isForServer())
|
||||
sendingListeners.addListener(listener);
|
||||
if (side.isForClient())
|
||||
recievedListeners.addListener(listener);
|
||||
// A listener can only be added once
|
||||
if (packetListeners.contains(listener))
|
||||
return;
|
||||
|
||||
// Inform our injected hooks
|
||||
enablePacketFilters(side, listener.getPacketsID());
|
||||
ListeningWhitelist sending = listener.getSendingWhitelist();
|
||||
ListeningWhitelist receiving = listener.getReceivingWhitelist();
|
||||
boolean hasSending = sending != null && sending.isEnabled();
|
||||
boolean hasReceiving = receiving != null && receiving.isEnabled();
|
||||
|
||||
if (hasSending || hasReceiving) {
|
||||
// Add listeners and hooks
|
||||
if (hasSending) {
|
||||
sendingListeners.addListener(listener, sending);
|
||||
enablePacketFilters(ConnectionSide.SERVER_SIDE, sending.getWhitelist());
|
||||
}
|
||||
if (hasReceiving) {
|
||||
recievedListeners.addListener(listener, receiving);
|
||||
enablePacketFilters(ConnectionSide.CLIENT_SIDE, receiving.getWhitelist());
|
||||
}
|
||||
|
||||
// Inform our injected hooks
|
||||
packetListeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePacketListener(PacketListener listener) {
|
||||
if (listener == null)
|
||||
throw new IllegalArgumentException("listener cannot be NULL");
|
||||
|
||||
ConnectionSide side = listener.getConnectionSide();
|
||||
|
||||
List<Integer> sendingRemoved = null;
|
||||
List<Integer> receivingRemoved = null;
|
||||
|
||||
ListeningWhitelist sending = listener.getSendingWhitelist();
|
||||
ListeningWhitelist receiving = listener.getReceivingWhitelist();
|
||||
|
||||
// Remove from the overal list of listeners
|
||||
packetListeners.remove(listener);
|
||||
if (!packetListeners.remove(listener))
|
||||
return;
|
||||
|
||||
// Add listeners
|
||||
if (side.isForServer())
|
||||
sendingRemoved = sendingListeners.removeListener(listener);
|
||||
if (side.isForClient())
|
||||
receivingRemoved = recievedListeners.removeListener(listener);
|
||||
if (sending != null && sending.isEnabled())
|
||||
sendingRemoved = sendingListeners.removeListener(listener, sending);
|
||||
if (receiving != null && receiving.isEnabled())
|
||||
receivingRemoved = recievedListeners.removeListener(listener, receiving);
|
||||
|
||||
// Remove hooks, if needed
|
||||
if (sendingRemoved != null && sendingRemoved.size() > 0)
|
||||
@ -154,9 +169,7 @@ public final class PacketFilterManager implements ProtocolManager {
|
||||
public void removePacketListeners(Plugin plugin) {
|
||||
|
||||
// Iterate through every packet listener
|
||||
for (Object element : packetListeners.toArray()) {
|
||||
PacketListener listener = (PacketListener) element;
|
||||
|
||||
for (PacketListener listener : packetListeners) {
|
||||
// Remove the listener
|
||||
if (Objects.equal(listener.getPlugin(), plugin)) {
|
||||
removePacketListener(listener);
|
||||
@ -277,11 +290,13 @@ public final class PacketFilterManager implements ProtocolManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Integer> getPacketFilters() {
|
||||
if (packetInjector != null)
|
||||
return Sets.union(sendingFilters, packetInjector.getPacketHandlers());
|
||||
else
|
||||
return sendingFilters;
|
||||
public Set<Integer> getSendingFilters() {
|
||||
return ImmutableSet.copyOf(sendingFilters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Integer> getReceivingFilters() {
|
||||
return ImmutableSet.copyOf(packetInjector.getPacketHandlers());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -362,7 +377,7 @@ public final class PacketFilterManager implements ProtocolManager {
|
||||
// Find the register event method
|
||||
Method registerEvent = FuzzyReflection.fromObject(manager).getMethodByParameters("registerEvent",
|
||||
eventTypes, Listener.class, eventPriority, Plugin.class);
|
||||
|
||||
|
||||
Enhancer ex = new Enhancer();
|
||||
ex.setSuperclass(playerListener);
|
||||
ex.setClassLoader(classLoader);
|
||||
|
@ -0,0 +1,73 @@
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An implicitly sorted array list that preserves insertion order and maintains duplicates.
|
||||
*
|
||||
* Note that only the {@link insertSorted} method will update the list correctly,
|
||||
* @param <T> - type of the sorted list.
|
||||
*/
|
||||
class SortedCopyOnWriteArray<T> implements Iterable<T> {
|
||||
// Prevent reordering
|
||||
private volatile List<T> list;
|
||||
|
||||
public SortedCopyOnWriteArray() {
|
||||
this(new ArrayList<T>());
|
||||
}
|
||||
|
||||
public SortedCopyOnWriteArray(List<T> wrapped) {
|
||||
this.list = wrapped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the given element in the proper location.
|
||||
* @param value - element to insert.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public synchronized void insertSorted(T value) {
|
||||
|
||||
// We use NULL as a special marker, so we don't allow it
|
||||
if (value == null)
|
||||
throw new IllegalArgumentException("value cannot be NULL");
|
||||
|
||||
List<T> copy = new ArrayList<T>();
|
||||
Comparable<T> compare = (Comparable<T>) value;
|
||||
|
||||
for (T element : list) {
|
||||
// If the value is now greater than the current element, it should be placed right before it
|
||||
if (value != null && compare.compareTo(element) < 0) {
|
||||
copy.add(value);
|
||||
value = null;
|
||||
}
|
||||
copy.add(element);
|
||||
}
|
||||
|
||||
// Don't forget to add it
|
||||
if (value != null)
|
||||
copy.add(value);
|
||||
|
||||
list = copy;
|
||||
}
|
||||
|
||||
public T get(int index) {
|
||||
return list.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the size of the list.
|
||||
* @return Size of the list.
|
||||
*/
|
||||
public int size() {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an iterator over the elements in the given list.
|
||||
*/
|
||||
public Iterator<T> iterator() {
|
||||
return list.iterator();
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.google.common.primitives.Ints;
|
||||
|
||||
public class SortedCopyOnWriteArrayTest {
|
||||
|
||||
@Test
|
||||
public void testInsertion() {
|
||||
|
||||
final int MAX_NUMBER = 50;
|
||||
|
||||
SortedCopyOnWriteArray<Integer> test = new SortedCopyOnWriteArray<Integer>();
|
||||
|
||||
// Generate some numbers
|
||||
List<Integer> numbers = new ArrayList<Integer>();
|
||||
|
||||
for (int i = 0; i < MAX_NUMBER; i++) {
|
||||
numbers.add(i);
|
||||
}
|
||||
|
||||
// Random insertion to test it all
|
||||
Collections.shuffle(numbers);
|
||||
|
||||
// O(n^2) of course, so don't use too large numbers
|
||||
for (int i = 0; i < MAX_NUMBER; i++) {
|
||||
test.insertSorted(numbers.get(i));
|
||||
}
|
||||
|
||||
// Check that everything is correct
|
||||
for (int i = 0; i < MAX_NUMBER; i++) {
|
||||
assertEquals((Integer) i, test.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrder() {
|
||||
PriorityStuff a = new PriorityStuff(ListenerPriority.HIGH, 1);
|
||||
PriorityStuff b = new PriorityStuff(ListenerPriority.NORMAL, 2);
|
||||
PriorityStuff c = new PriorityStuff(ListenerPriority.NORMAL, 3);
|
||||
SortedCopyOnWriteArray<PriorityStuff> test = new SortedCopyOnWriteArray<PriorityStuff>();
|
||||
|
||||
test.insertSorted(a);
|
||||
test.insertSorted(b);
|
||||
test.insertSorted(c);
|
||||
|
||||
// Make sure the normal's are in the right order
|
||||
assertEquals(2, test.get(0).id);
|
||||
assertEquals(3, test.get(1).id);
|
||||
}
|
||||
|
||||
private class PriorityStuff implements Comparable<PriorityStuff> {
|
||||
public ListenerPriority priority;
|
||||
public int id;
|
||||
|
||||
public PriorityStuff(ListenerPriority priority, int id) {
|
||||
this.priority = priority;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(PriorityStuff other) {
|
||||
// This ensures that lower priority listeners are executed first
|
||||
return Ints.compare(priority.getSlot(),
|
||||
other.priority.getSlot());
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user