From 616213924b436185097baf0ea879e3f3beb10e9c Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Sat, 15 Sep 2012 13:07:44 +0200 Subject: [PATCH] Refactored the SortedCopyOnWriteArray out into a separate package. Made it extend the java.util.Collection interface. --- .../comphenix/protocol/ProtocolLibrary.java | 10 +- .../concurrency/SortedCopyOnWriteArray.java | 223 ++++++++++++++++++ .../injector/ConcurrentListenerMultimap.java | 1 + .../protocol/injector/PlayerInjector.java | 27 ++- .../injector/SortedCopyOnWriteArray.java | 112 --------- .../injector/SortedCopyOnWriteArrayTest.java | 1 + 6 files changed, 256 insertions(+), 118 deletions(-) create mode 100644 ProtocolLib/src/com/comphenix/protocol/concurrency/SortedCopyOnWriteArray.java delete mode 100644 ProtocolLib/src/com/comphenix/protocol/injector/SortedCopyOnWriteArray.java diff --git a/ProtocolLib/src/com/comphenix/protocol/ProtocolLibrary.java b/ProtocolLib/src/com/comphenix/protocol/ProtocolLibrary.java index 3a4b7ae3..9e9d53cd 100644 --- a/ProtocolLib/src/com/comphenix/protocol/ProtocolLibrary.java +++ b/ProtocolLib/src/com/comphenix/protocol/ProtocolLibrary.java @@ -51,15 +51,15 @@ public class ProtocolLibrary extends JavaPlugin { Server server = getServer(); PluginManager manager = server.getPluginManager(); + // Notify server managers of incompatible plugins + checkForIncompatibility(manager); + // Player login and logout events protocolManager.registerEvents(manager, this); // Inject our hook into already existing players protocolManager.initializePlayers(server.getOnlinePlayers()); - // Notify server managers of incompatible plugins - checkForIncompatibility(manager); - // Try to enable statistics try { statistisc = new Statistics(this); @@ -77,7 +77,9 @@ public class ProtocolLibrary extends JavaPlugin { for (String plugin : incompatiblePlugins) { if (manager.getPlugin(plugin) != null) { // Check for versions, ect. - logger.severe(ChatColor.RED + "Detected incompatible plugin: " + plugin); + logger.severe("Detected incompatible plugin: " + plugin); + logger.info("Using woraround."); + } } } diff --git a/ProtocolLib/src/com/comphenix/protocol/concurrency/SortedCopyOnWriteArray.java b/ProtocolLib/src/com/comphenix/protocol/concurrency/SortedCopyOnWriteArray.java new file mode 100644 index 00000000..87d69a44 --- /dev/null +++ b/ProtocolLib/src/com/comphenix/protocol/concurrency/SortedCopyOnWriteArray.java @@ -0,0 +1,223 @@ +package com.comphenix.protocol.concurrency; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import com.google.common.base.Objects; +import com.google.common.collect.Iterables; + +/** + * An implicitly sorted array list that preserves insertion order and maintains duplicates. + * @param - type of the elements in the list. + */ +public class SortedCopyOnWriteArray> implements Iterable, Collection { + // Prevent reordering + private volatile List list; + + /** + * Construct an empty sorted array. + */ + public SortedCopyOnWriteArray() { + list = new ArrayList(); + } + + /** + * Create a sorted array from the given list. The elements will be automatically sorted. + * @param wrapped - the collection whose elements are to be placed into the list. + */ + public SortedCopyOnWriteArray(Collection wrapped) { + this.list = new ArrayList(wrapped); + } + + /** + * Create a sorted array from the given list. + * @param wrapped - the collection whose elements are to be placed into the list. + * @param sort - TRUE to automatically sort the collection, FALSE if it is already sorted. + */ + public SortedCopyOnWriteArray(Collection wrapped, boolean sort) { + this.list = new ArrayList(wrapped); + + if (sort) { + Collections.sort(list); + } + } + + /** + * Inserts the given element in the proper location. + * @param value - element to insert. + */ + @Override + public synchronized boolean add(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 copy = new ArrayList(); + + for (T element : list) { + // If the value is now greater than the current element, it should be placed right before it + if (value != null && value.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; + return true; + } + + @Override + public synchronized boolean addAll(Collection values) { + + if (values == null) + throw new IllegalArgumentException("values cannot be NULL"); + if (values.size() == 0) + return false; + + List copy = new ArrayList(); + + // Insert the new content and sort it + copy.addAll(list); + copy.addAll(values); + Collections.sort(copy); + + list = copy; + return true; + } + + /** + * Removes from the list by making a new list with every element except the one given. + *

+ * Objects will be compared using the given objects equals() method. + * @param value - value to remove. + */ + @Override + public synchronized boolean remove(Object value) { + List copy = new ArrayList(); + + // Note that there's not much to be gained from using BinarySearch, as we + // have to copy (and thus read) the entire list regardless. + + // Copy every element except the one given to us. + for (T element : list) { + if (value != null && !Objects.equal(value, element)) { + copy.add(element); + value = null; + } + } + + list = copy; + return value == null; + } + + @Override + public boolean removeAll(Collection values) { + // Special cases + if (values == null) + throw new IllegalArgumentException("values cannot be NULL"); + if (values.size() == 0) + return false; + + List copy = new ArrayList(); + + copy.addAll(list); + copy.removeAll(values); + + list = copy; + return true; + } + + @Override + public boolean retainAll(Collection values) { + // Special cases + if (values == null) + throw new IllegalArgumentException("values cannot be NULL"); + if (values.size() == 0) + return false; + + List copy = new ArrayList(); + + copy.addAll(list); + copy.removeAll(values); + + list = copy; + return true; + } + + /** + * Removes from the list by making a copy of every element except the one with the given index. + * @param index - index of the element to remove. + */ + public synchronized void remove(int index) { + + List copy = new ArrayList(list); + + copy.remove(index); + list = copy; + } + + /** + * Retrieves an element by index. + * @param index - index of element to retrieve. + * @return The element at the given location. + */ + 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. + * Warning: No not attempt to remove elements using the iterator. + */ + public Iterator iterator() { + return Iterables.unmodifiableIterable(list).iterator(); + } + + @Override + public void clear() { + list = new ArrayList(); + } + + @Override + public boolean contains(Object value) { + return list.contains(value); + } + + @Override + public boolean containsAll(Collection values) { + return list.containsAll(values); + } + + @Override + public boolean isEmpty() { + return list.isEmpty(); + } + + @Override + public Object[] toArray() { + return list.toArray(); + } + + @SuppressWarnings("hiding") + @Override + public T[] toArray(T[] a) { + return list.toArray(a); + } +} diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/ConcurrentListenerMultimap.java b/ProtocolLib/src/com/comphenix/protocol/injector/ConcurrentListenerMultimap.java index ec4542e6..385e4d8c 100644 --- a/ProtocolLib/src/com/comphenix/protocol/injector/ConcurrentListenerMultimap.java +++ b/ProtocolLib/src/com/comphenix/protocol/injector/ConcurrentListenerMultimap.java @@ -7,6 +7,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.logging.Level; import java.util.logging.Logger; +import com.comphenix.protocol.concurrency.SortedCopyOnWriteArray; import com.comphenix.protocol.events.ListenerPriority; import com.comphenix.protocol.events.ListeningWhitelist; import com.comphenix.protocol.events.PacketAdapter; diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/PlayerInjector.java b/ProtocolLib/src/com/comphenix/protocol/injector/PlayerInjector.java index 3d5e362c..003c6da2 100644 --- a/ProtocolLib/src/com/comphenix/protocol/injector/PlayerInjector.java +++ b/ProtocolLib/src/com/comphenix/protocol/injector/PlayerInjector.java @@ -50,6 +50,22 @@ class PlayerInjector { public interface FakePacket { // Nothing } + + /** + * Sets the inject hook type. Different types allow for maximum compatibility. + * @author Kristian + */ + public enum InjectHooks { + /** + * Override the packet queue lists in NetworkHandler. + */ + NETWORK_HANDLER_FIELDS, + + /** + * Override the NetworkHandler itself, and it's sendPacket-method. + */ + OVERRIDE_NETWORK_HANDLER + } // Cache previously retrieved fields private static Field serverHandlerField; @@ -68,6 +84,7 @@ class PlayerInjector { private boolean hasInitialized; // Reference to the player's network manager + private VolatileField networkManagerRef; private Object networkManager; // Current net handler @@ -98,6 +115,9 @@ class PlayerInjector { CraftPlayer craft = (CraftPlayer) player; EntityPlayer notchEntity = craft.getHandle(); + Object serverHandler = null; + Object networkManager = null; + if (!hasInitialized) { // Do this first, in case we encounter an exception hasInitialized = true; @@ -105,12 +125,13 @@ class PlayerInjector { // Retrieve the server handler if (serverHandlerField == null) serverHandlerField = FuzzyReflection.fromObject(notchEntity).getFieldByType(".*NetServerHandler"); - Object serverHandler = FieldUtils.readField(serverHandlerField, notchEntity); + serverHandler = FieldUtils.readField(serverHandlerField, notchEntity); // Next, get the network manager if (networkManagerField == null) networkManagerField = FuzzyReflection.fromObject(serverHandler).getFieldByType(".*NetworkManager"); - networkManager = FieldUtils.readField(networkManagerField, serverHandler); + networkManagerRef = new VolatileField(networkManagerField, serverHandler); + networkManager = networkManagerRef.getValue(); // Create the network manager modifier from the actual object type if (networkManager != null && networkModifier == null) @@ -195,6 +216,8 @@ class PlayerInjector { */ public void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException { + + if (networkManager != null) { try { if (!filtered) { diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/SortedCopyOnWriteArray.java b/ProtocolLib/src/com/comphenix/protocol/injector/SortedCopyOnWriteArray.java deleted file mode 100644 index cc89e927..00000000 --- a/ProtocolLib/src/com/comphenix/protocol/injector/SortedCopyOnWriteArray.java +++ /dev/null @@ -1,112 +0,0 @@ -package com.comphenix.protocol.injector; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import com.google.common.base.Objects; -import com.google.common.collect.Iterables; - -/** - * An implicitly sorted array list that preserves insertion order and maintains duplicates. - * @param - type of the elements in the list. - */ -class SortedCopyOnWriteArray implements Iterable { - // Prevent reordering - private volatile List list; - - public SortedCopyOnWriteArray() { - this(new ArrayList()); - } - - public SortedCopyOnWriteArray(List wrapped) { - this.list = wrapped; - } - - /** - * Inserts the given element in the proper location. - * @param value - element to insert. - */ - @SuppressWarnings("unchecked") - public synchronized void add(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 copy = new ArrayList(); - Comparable compare = (Comparable) 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; - } - - /** - * Removes from the list by making a new list with every element except the one given. - *

- * Objects will be compared using the given objects equals() method. - * @param value - value to remove. - */ - public synchronized void remove(T value) { - List copy = new ArrayList(); - - // Copy every element except the one given to us - for (T element : list) { - if (value != null && !Objects.equal(value, element)) { - copy.add(element); - value = null; - } - } - - list = copy; - } - - /** - * Removes from the list by making a copy of every element except the one with the given index. - * @param index - index of the element to remove. - */ - public synchronized void remove(int index) { - - List copy = new ArrayList(list); - - copy.remove(index); - list = copy; - } - - /** - * Retrieves an element by index. - * @param index - index of element to retrieve. - * @return The element at the given location. - */ - 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. - * Warning: No not attempt to remove elements using the iterator. - */ - public Iterator iterator() { - return Iterables.unmodifiableIterable(list).iterator(); - } -} diff --git a/ProtocolLib/test/com/comphenix/protocol/injector/SortedCopyOnWriteArrayTest.java b/ProtocolLib/test/com/comphenix/protocol/injector/SortedCopyOnWriteArrayTest.java index 1191cc48..8bde90db 100644 --- a/ProtocolLib/test/com/comphenix/protocol/injector/SortedCopyOnWriteArrayTest.java +++ b/ProtocolLib/test/com/comphenix/protocol/injector/SortedCopyOnWriteArrayTest.java @@ -8,6 +8,7 @@ import java.util.List; import org.junit.Test; +import com.comphenix.protocol.concurrency.SortedCopyOnWriteArray; import com.comphenix.protocol.events.ListenerPriority; import com.google.common.primitives.Ints;