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.
Dieser Commit ist enthalten in:
Ursprung
bb8bec907c
Commit
88dcf0cb32
@ -119,10 +119,16 @@ public interface ProtocolManager {
|
|||||||
public PacketContainer createPacket(int id, boolean skipDefaults);
|
public PacketContainer createPacket(int id, boolean skipDefaults);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retieves a set of every enabled packet.
|
* Retrieves a immutable set containing the ID of the sent server packets that will be observed by listeners.
|
||||||
* @return Every packet filter.
|
* @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.
|
* Determines whether or not this protocol mananger has been disabled.
|
||||||
|
@ -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;
|
package com.comphenix.protocol.events;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
110
ProtocolLib/src/com/comphenix/protocol/events/ListeningWhitelist.java
Normale Datei
110
ProtocolLib/src/com/comphenix/protocol/events/ListeningWhitelist.java
Normale Datei
@ -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;
|
package com.comphenix.protocol.events;
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
import com.google.common.base.Joiner;
|
|
||||||
import com.google.common.collect.Sets;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a packet listener with useful constructors.
|
* Represents a packet listener with useful constructors.
|
||||||
@ -32,9 +28,9 @@ import com.google.common.collect.Sets;
|
|||||||
public abstract class PacketAdapter implements PacketListener {
|
public abstract class PacketAdapter implements PacketListener {
|
||||||
|
|
||||||
protected Plugin plugin;
|
protected Plugin plugin;
|
||||||
protected Set<Integer> packetsID;
|
|
||||||
protected ConnectionSide connectionSide;
|
protected ConnectionSide connectionSide;
|
||||||
protected ListenerPriority listenerPriority;
|
protected ListeningWhitelist receivingWhitelist = ListeningWhitelist.EMPTY_WHITELIST;
|
||||||
|
protected ListeningWhitelist sendingWhitelist = ListeningWhitelist.EMPTY_WHITELIST;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize a packet listener.
|
* Initialize a packet listener.
|
||||||
@ -54,10 +50,23 @@ public abstract class PacketAdapter implements PacketListener {
|
|||||||
* @param packets - the packet IDs the listener is looking for.
|
* @param packets - the packet IDs the listener is looking for.
|
||||||
*/
|
*/
|
||||||
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority, Integer... packets) {
|
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.plugin = plugin;
|
||||||
this.connectionSide = connectionSide;
|
this.connectionSide = connectionSide;
|
||||||
this.packetsID = Sets.newHashSet(packets);
|
|
||||||
this.listenerPriority = listenerPriority;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -71,13 +80,13 @@ public abstract class PacketAdapter implements PacketListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConnectionSide getConnectionSide() {
|
public ListeningWhitelist getReceivingWhitelist() {
|
||||||
return connectionSide;
|
return receivingWhitelist;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Integer> getPacketsID() {
|
public ListeningWhitelist getSendingWhitelist() {
|
||||||
return packetsID;
|
return sendingWhitelist;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -85,11 +94,6 @@ public abstract class PacketAdapter implements PacketListener {
|
|||||||
return plugin;
|
return plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ListenerPriority getListenerPriority() {
|
|
||||||
return listenerPriority;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the name of the plugin that has been associated with the listener.
|
* Retrieves the name of the plugin that has been associated with the listener.
|
||||||
* @return Name of the associated plugin.
|
* @return Name of the associated plugin.
|
||||||
@ -113,8 +117,9 @@ public abstract class PacketAdapter implements PacketListener {
|
|||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
// This is used by the error reporter
|
// This is used by the error reporter
|
||||||
return String.format("PacketAdapter[plugin=%s, side=%s, packets=%s]",
|
return String.format("PacketAdapter[plugin=%s, sending=%s, receiving=%s]",
|
||||||
getPluginName(this), getConnectionSide().name(),
|
getPluginName(this),
|
||||||
Joiner.on(", ").join(packetsID));
|
sendingWhitelist,
|
||||||
|
receivingWhitelist);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,6 @@
|
|||||||
|
|
||||||
package com.comphenix.protocol.events;
|
package com.comphenix.protocol.events;
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -43,22 +41,16 @@ public interface PacketListener {
|
|||||||
public void onPacketReceiving(PacketEvent event);
|
public void onPacketReceiving(PacketEvent event);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve whether or not we're listening for client or server packets.
|
* Retrieve which packets sent by the server this listener will observe.
|
||||||
* @return The type of packets we expect.
|
* @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.
|
* Retrieve which packets sent by the client this listener will observe.
|
||||||
* @return Execution order in terms of priority.
|
* @return List of server packets to observe, along with the priority.
|
||||||
*/
|
*/
|
||||||
public ListenerPriority getListenerPriority();
|
public ListeningWhitelist getReceivingWhitelist();
|
||||||
|
|
||||||
/**
|
|
||||||
* Set of packet ids we expect to recieve.
|
|
||||||
* @return Packets IDs.
|
|
||||||
*/
|
|
||||||
public Set<Integer> getPacketsID();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the plugin that created list packet listener.
|
* Retrieve the plugin that created list packet listener.
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.comphenix.protocol.injector;
|
package com.comphenix.protocol.injector;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
@ -9,10 +8,11 @@ import java.util.concurrent.ConcurrentMap;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
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.PacketAdapter;
|
||||||
import com.comphenix.protocol.events.PacketEvent;
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
import com.comphenix.protocol.events.PacketListener;
|
import com.comphenix.protocol.events.PacketListener;
|
||||||
import com.google.common.base.Objects;
|
|
||||||
import com.google.common.primitives.Ints;
|
import com.google.common.primitives.Ints;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -23,36 +23,32 @@ import com.google.common.primitives.Ints;
|
|||||||
public class ConcurrentListenerMultimap {
|
public class ConcurrentListenerMultimap {
|
||||||
|
|
||||||
// The core of our map
|
// The core of our map
|
||||||
protected ConcurrentMap<Integer, SortedArrayList<PacketListener>> listeners =
|
protected ConcurrentMap<Integer, SortedCopyOnWriteArray<PrioritizedListener>> listeners =
|
||||||
new ConcurrentHashMap<Integer, SortedArrayList<PacketListener>>();
|
new ConcurrentHashMap<Integer, SortedCopyOnWriteArray<PrioritizedListener>>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a listener to its requested list of packet recievers.
|
* Adds a listener to its requested list of packet recievers.
|
||||||
* @param listener - listener with a list of packets to recieve notifcations for.
|
* @param listener - listener with a list of packets to recieve notifcations for.
|
||||||
*/
|
*/
|
||||||
public void addListener(PacketListener listener) {
|
public void addListener(PacketListener listener, ListeningWhitelist whitelist) {
|
||||||
for (Integer packetID : listener.getPacketsID()) {
|
|
||||||
addListener(packetID, listener);
|
PrioritizedListener prioritized = new PrioritizedListener(listener, whitelist.getPriority());
|
||||||
|
|
||||||
|
for (Integer packetID : whitelist.getWhitelist()) {
|
||||||
|
addListener(packetID, prioritized);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the listener to a specific packet notifcation list
|
// 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
|
// We don't want to create this for every lookup
|
||||||
if (list == null) {
|
if (list == null) {
|
||||||
// It would be nice if we could use a PriorityBlockingQueue, but it doesn't preseve iterator order,
|
// 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.
|
// which is a essential feature for our purposes.
|
||||||
final SortedArrayList<PacketListener> value = new SortedArrayList<PacketListener>(new Comparator<PacketListener>() {
|
final SortedCopyOnWriteArray<PrioritizedListener> value = new SortedCopyOnWriteArray<PrioritizedListener>();
|
||||||
@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());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
list = listeners.putIfAbsent(packetID, value);
|
list = listeners.putIfAbsent(packetID, value);
|
||||||
|
|
||||||
@ -74,21 +70,26 @@ public class ConcurrentListenerMultimap {
|
|||||||
* @param listener - listener to remove.
|
* @param listener - listener to remove.
|
||||||
* @return Every packet ID that was removed due to no listeners.
|
* @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>();
|
List<Integer> removedPackets = new ArrayList<Integer>();
|
||||||
|
|
||||||
// Again, not terribly efficient. But adding or removing listeners should be a rare event.
|
// 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
|
// Remove any listeners
|
||||||
if (list != null) {
|
if (list != null) {
|
||||||
synchronized(list) {
|
synchronized(list) {
|
||||||
// Don't remove from newly created lists
|
// Don't remove from newly created lists
|
||||||
if (list.size() > 0) {
|
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) {
|
if (list.size() == 0) {
|
||||||
listeners.remove(packetID);
|
listeners.remove(packetID);
|
||||||
@ -110,20 +111,21 @@ public class ConcurrentListenerMultimap {
|
|||||||
* @param event - the packet event to invoke.
|
* @param event - the packet event to invoke.
|
||||||
*/
|
*/
|
||||||
public void invokePacketRecieving(Logger logger, PacketEvent event) {
|
public void invokePacketRecieving(Logger logger, PacketEvent event) {
|
||||||
SortedArrayList<PacketListener> list = listeners.get(event.getPacketID());
|
SortedCopyOnWriteArray<PrioritizedListener> list = listeners.get(event.getPacketID());
|
||||||
|
|
||||||
if (list == null)
|
if (list == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// We have to be careful. Cannot modify the underlying list when sending notifications.
|
// We have to be careful. Cannot modify the underlying list when sending notifications.
|
||||||
synchronized (list) {
|
synchronized (list) {
|
||||||
for (PacketListener listener : list) {
|
for (PrioritizedListener element : list) {
|
||||||
try {
|
try {
|
||||||
listener.onPacketReceiving(event);
|
element.getListener().onPacketReceiving(event);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Minecraft doesn't want your Exception.
|
// Minecraft doesn't want your Exception.
|
||||||
logger.log(Level.SEVERE,
|
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.
|
* @param event - the packet event to invoke.
|
||||||
*/
|
*/
|
||||||
public void invokePacketSending(Logger logger, PacketEvent event) {
|
public void invokePacketSending(Logger logger, PacketEvent event) {
|
||||||
SortedArrayList<PacketListener> list = listeners.get(event.getPacketID());
|
SortedCopyOnWriteArray<PrioritizedListener> list = listeners.get(event.getPacketID());
|
||||||
|
|
||||||
if (list == null)
|
if (list == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
synchronized (list) {
|
synchronized (list) {
|
||||||
for (PacketListener listener : list) {
|
for (PrioritizedListener element : list) {
|
||||||
try {
|
try {
|
||||||
listener.onPacketSending(event);
|
element.getListener().onPacketSending(event);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Minecraft doesn't want your Exception.
|
// Minecraft doesn't want your Exception.
|
||||||
logger.log(Level.SEVERE,
|
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.
|
* A listener with an associated priority.
|
||||||
*
|
|
||||||
* Note that only the {@link insertSorted} method will update the list correctly,
|
|
||||||
* @param <T> - type of the sorted list.
|
|
||||||
*/
|
*/
|
||||||
private class SortedArrayList<T> implements Iterable<T> {
|
private class PrioritizedListener implements Comparable<PrioritizedListener> {
|
||||||
|
private PacketListener listener;
|
||||||
|
private ListenerPriority priority;
|
||||||
|
|
||||||
private Comparator<T> comparator;
|
public PrioritizedListener(PacketListener listener, ListenerPriority priority) {
|
||||||
private List<T> list = new ArrayList<T>();
|
this.listener = listener;
|
||||||
|
this.priority = priority;
|
||||||
public SortedArrayList(Comparator<T> comparator) {
|
|
||||||
this.comparator = comparator;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
@Override
|
||||||
public Iterator<T> iterator() {
|
public int compareTo(PrioritizedListener other) {
|
||||||
return list.iterator();
|
// 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.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@ -50,11 +49,12 @@ import com.comphenix.protocol.reflect.FieldAccessException;
|
|||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Sets;
|
|
||||||
|
|
||||||
public final class PacketFilterManager implements ProtocolManager {
|
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
|
// Player injection
|
||||||
private Map<DataInputStream, Player> connectionLookup = new ConcurrentHashMap<DataInputStream, Player>();
|
private Map<DataInputStream, Player> connectionLookup = new ConcurrentHashMap<DataInputStream, Player>();
|
||||||
@ -112,17 +112,29 @@ public final class PacketFilterManager implements ProtocolManager {
|
|||||||
if (listener == null)
|
if (listener == null)
|
||||||
throw new IllegalArgumentException("listener cannot be NULL.");
|
throw new IllegalArgumentException("listener cannot be NULL.");
|
||||||
|
|
||||||
ConnectionSide side = listener.getConnectionSide();
|
// A listener can only be added once
|
||||||
packetListeners.add(listener);
|
if (packetListeners.contains(listener))
|
||||||
|
return;
|
||||||
|
|
||||||
// Add listeners
|
ListeningWhitelist sending = listener.getSendingWhitelist();
|
||||||
if (side.isForServer())
|
ListeningWhitelist receiving = listener.getReceivingWhitelist();
|
||||||
sendingListeners.addListener(listener);
|
boolean hasSending = sending != null && sending.isEnabled();
|
||||||
if (side.isForClient())
|
boolean hasReceiving = receiving != null && receiving.isEnabled();
|
||||||
recievedListeners.addListener(listener);
|
|
||||||
|
|
||||||
// Inform our injected hooks
|
if (hasSending || hasReceiving) {
|
||||||
enablePacketFilters(side, listener.getPacketsID());
|
// 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
|
@Override
|
||||||
@ -130,18 +142,21 @@ public final class PacketFilterManager implements ProtocolManager {
|
|||||||
if (listener == null)
|
if (listener == null)
|
||||||
throw new IllegalArgumentException("listener cannot be NULL");
|
throw new IllegalArgumentException("listener cannot be NULL");
|
||||||
|
|
||||||
ConnectionSide side = listener.getConnectionSide();
|
|
||||||
List<Integer> sendingRemoved = null;
|
List<Integer> sendingRemoved = null;
|
||||||
List<Integer> receivingRemoved = null;
|
List<Integer> receivingRemoved = null;
|
||||||
|
|
||||||
|
ListeningWhitelist sending = listener.getSendingWhitelist();
|
||||||
|
ListeningWhitelist receiving = listener.getReceivingWhitelist();
|
||||||
|
|
||||||
// Remove from the overal list of listeners
|
// Remove from the overal list of listeners
|
||||||
packetListeners.remove(listener);
|
if (!packetListeners.remove(listener))
|
||||||
|
return;
|
||||||
|
|
||||||
// Add listeners
|
// Add listeners
|
||||||
if (side.isForServer())
|
if (sending != null && sending.isEnabled())
|
||||||
sendingRemoved = sendingListeners.removeListener(listener);
|
sendingRemoved = sendingListeners.removeListener(listener, sending);
|
||||||
if (side.isForClient())
|
if (receiving != null && receiving.isEnabled())
|
||||||
receivingRemoved = recievedListeners.removeListener(listener);
|
receivingRemoved = recievedListeners.removeListener(listener, receiving);
|
||||||
|
|
||||||
// Remove hooks, if needed
|
// Remove hooks, if needed
|
||||||
if (sendingRemoved != null && sendingRemoved.size() > 0)
|
if (sendingRemoved != null && sendingRemoved.size() > 0)
|
||||||
@ -154,9 +169,7 @@ public final class PacketFilterManager implements ProtocolManager {
|
|||||||
public void removePacketListeners(Plugin plugin) {
|
public void removePacketListeners(Plugin plugin) {
|
||||||
|
|
||||||
// Iterate through every packet listener
|
// Iterate through every packet listener
|
||||||
for (Object element : packetListeners.toArray()) {
|
for (PacketListener listener : packetListeners) {
|
||||||
PacketListener listener = (PacketListener) element;
|
|
||||||
|
|
||||||
// Remove the listener
|
// Remove the listener
|
||||||
if (Objects.equal(listener.getPlugin(), plugin)) {
|
if (Objects.equal(listener.getPlugin(), plugin)) {
|
||||||
removePacketListener(listener);
|
removePacketListener(listener);
|
||||||
@ -277,11 +290,13 @@ public final class PacketFilterManager implements ProtocolManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Integer> getPacketFilters() {
|
public Set<Integer> getSendingFilters() {
|
||||||
if (packetInjector != null)
|
return ImmutableSet.copyOf(sendingFilters);
|
||||||
return Sets.union(sendingFilters, packetInjector.getPacketHandlers());
|
}
|
||||||
else
|
|
||||||
return sendingFilters;
|
@Override
|
||||||
|
public Set<Integer> getReceivingFilters() {
|
||||||
|
return ImmutableSet.copyOf(packetInjector.getPacketHandlers());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
In neuem Issue referenzieren
Einen Benutzer sperren