diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/CleanupStaticMembers.java b/ProtocolLib/src/main/java/com/comphenix/protocol/CleanupStaticMembers.java
index f4940bee..57f31fe7 100644
--- a/ProtocolLib/src/main/java/com/comphenix/protocol/CleanupStaticMembers.java
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/CleanupStaticMembers.java
@@ -78,9 +78,9 @@ class CleanupStaticMembers {
"com.comphenix.protocol.injector.player.PlayerInjector",
"com.comphenix.protocol.injector.player.TemporaryPlayerFactory",
"com.comphenix.protocol.injector.EntityUtilities",
- "com.comphenix.protocol.injector.MinecraftRegistry",
- "com.comphenix.protocol.injector.PacketInjector",
- "com.comphenix.protocol.injector.ReadPacketModifier",
+ "com.comphenix.protocol.injector.packet.PacketRegistry",
+ "com.comphenix.protocol.injector.packet.PacketInjector",
+ "com.comphenix.protocol.injector.packet.ReadPacketModifier",
"com.comphenix.protocol.injector.StructureCache",
"com.comphenix.protocol.reflect.compiler.BoxingHelper",
"com.comphenix.protocol.reflect.compiler.MethodDescriptor"
diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/concurrency/IntegerSet.java b/ProtocolLib/src/main/java/com/comphenix/protocol/concurrency/IntegerSet.java
index 2393fced..e2aaa3f6 100644
--- a/ProtocolLib/src/main/java/com/comphenix/protocol/concurrency/IntegerSet.java
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/concurrency/IntegerSet.java
@@ -44,7 +44,7 @@ public class IntegerSet {
/**
* Determine whether or not the given element exists in the set.
- * @param value - the element to check. Must be in the range [0, count).
+ * @param element - the element to check. Must be in the range [0, count).
* @return TRUE if the given element exists, FALSE otherwise.
*/
public boolean contains(int element) {
diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java
index 6cfc086f..eea43aae 100644
--- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java
@@ -56,6 +56,7 @@ import com.comphenix.protocol.injector.packet.PacketInjectorBuilder;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
import com.comphenix.protocol.injector.player.PlayerInjectorBuilder;
+import com.comphenix.protocol.injector.spigot.SpigotPacketInjector;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.utility.MinecraftReflection;
@@ -142,6 +143,9 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
// Whether or not plugins are using the send/receive methods
private AtomicBoolean packetCreation = new AtomicBoolean();
+ // Spigot listener, if in use
+ private SpigotPacketInjector spigotInjector;
+
/**
* Only create instances of this class if protocol lib is disabled.
* @param unhookTask
@@ -181,22 +185,30 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
};
try {
- // Initialize injection mangers
- this.playerInjection = PlayerInjectorBuilder.newBuilder().
- invoker(this).
- server(server).
- reporter(reporter).
- classLoader(classLoader).
- packetListeners(packetListeners).
- injectionFilter(isInjectionNecessary).
- buildHandler();
-
- this.packetInjector = PacketInjectorBuilder.newBuilder().
- invoker(this).
- reporter(reporter).
- classLoader(classLoader).
- playerInjection(playerInjection).
- buildInjector();
+ // Spigot
+ if (SpigotPacketInjector.canUseSpigotListener()) {
+ spigotInjector = new SpigotPacketInjector(classLoader, reporter, this, server);
+ this.playerInjection = spigotInjector.getPlayerHandler();
+ this.packetInjector = spigotInjector.getPacketInjector();
+
+ } else {
+ // Initialize standard injection mangers
+ this.playerInjection = PlayerInjectorBuilder.newBuilder().
+ invoker(this).
+ server(server).
+ reporter(reporter).
+ classLoader(classLoader).
+ packetListeners(packetListeners).
+ injectionFilter(isInjectionNecessary).
+ buildHandler();
+
+ this.packetInjector = PacketInjectorBuilder.newBuilder().
+ invoker(this).
+ reporter(reporter).
+ classLoader(classLoader).
+ playerInjection(playerInjection).
+ buildInjector();
+ }
this.asyncFilterManager = new AsyncFilterManager(reporter, server.getScheduler(), this);
@@ -618,6 +630,8 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
* @param plugin - the parent plugin.
*/
public void registerEvents(PluginManager manager, final Plugin plugin) {
+ if (spigotInjector != null && !spigotInjector.register(plugin))
+ throw new IllegalArgumentException("Spigot has already been registered.");
try {
manager.registerEvents(new Listener() {
diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkObjectInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkObjectInjector.java
index 8b9f44f8..8fa193a4 100644
--- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkObjectInjector.java
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkObjectInjector.java
@@ -28,6 +28,7 @@ import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
+import org.bukkit.Server;
import org.bukkit.entity.Player;
import com.comphenix.protocol.Packets;
@@ -38,13 +39,14 @@ import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.GamePhase;
import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
+import com.comphenix.protocol.injector.player.TemporaryPlayerFactory.InjectContainer;
/**
- * Injection method that overrides the NetworkHandler itself, and it's sendPacket-method.
+ * Injection method that overrides the NetworkHandler itself, and it's queue-method.
*
* @author Kristian
*/
-class NetworkObjectInjector extends PlayerInjector {
+public class NetworkObjectInjector extends PlayerInjector {
// Determine if we're listening
private IntegerSet sendingFilters;
@@ -54,6 +56,9 @@ class NetworkObjectInjector extends PlayerInjector {
// Shared callback filter - avoid creating a new class every time
private static CallbackFilter callbackFilter;
+ // Temporary player factory
+ private static volatile TemporaryPlayerFactory tempPlayerFactory;
+
public NetworkObjectInjector(ClassLoader classLoader, ErrorReporter reporter, Player player,
ListenerInvoker invoker, IntegerSet sendingFilters) throws IllegalAccessException {
super(reporter, player, invoker);
@@ -66,6 +71,21 @@ class NetworkObjectInjector extends PlayerInjector {
return sendingFilters.contains(packetID);
}
+ /**
+ * Create a temporary player for use during login.
+ * @param server - Bukkit server.
+ * @return The temporary player.
+ */
+ public Player createTemporaryPlayer(Server server) {
+ if (tempPlayerFactory == null)
+ tempPlayerFactory = new TemporaryPlayerFactory();
+
+ // Create and associate this fake player with this network injector
+ Player player = tempPlayerFactory.createTemporaryPlayer(server);
+ ((InjectContainer) player).setInjector(this);
+ return player;
+ }
+
@Override
public void sendServerPacket(Object packet, boolean filtered) throws InvocationTargetException {
Object networkDelegate = filtered ? networkManagerRef.getValue() : networkManagerRef.getOldValue();
diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkServerInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkServerInjector.java
index da328bd2..f81be360 100644
--- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkServerInjector.java
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkServerInjector.java
@@ -52,7 +52,7 @@ import com.google.common.collect.Maps;
*
* @author Kristian
*/
-public class NetworkServerInjector extends PlayerInjector {
+class NetworkServerInjector extends PlayerInjector {
private volatile static CallbackFilter callbackFilter;
diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java
index da1e1834..d3f0109a 100644
--- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java
@@ -58,10 +58,10 @@ abstract class PlayerInjector {
protected static Field proxyServerField;
protected static Field networkManagerField;
- protected static Field inputField;
protected static Field netHandlerField;
protected static Field socketField;
+ private static Field inputField;
private static Field entityPlayerField;
// Whether or not we're using a proxy type
@@ -206,11 +206,6 @@ abstract class PlayerInjector {
if (queueMethod == null)
queueMethod = FuzzyReflection.fromClass(reference.getType()).
getMethodByParameters("queue", MinecraftReflection.getPacketClass());
-
- // And the data input stream that we'll use to identify a player
- if (inputField == null)
- inputField = FuzzyReflection.fromObject(networkManager, true).
- getFieldByType("java\\.io\\.DataInputStream");
}
/**
@@ -250,9 +245,9 @@ abstract class PlayerInjector {
public Socket getSocket() throws IllegalAccessException {
try {
if (socketField == null)
- socketField = FuzzyReflection.fromObject(networkManager).getFieldListByType(Socket.class).get(0);
+ socketField = FuzzyReflection.fromObject(networkManager, true).getFieldListByType(Socket.class).get(0);
if (socket == null)
- socket = (Socket) FieldUtils.readField(socketField, networkManager);
+ socket = (Socket) FieldUtils.readField(socketField, networkManager, true);
return socket;
} catch (IndexOutOfBoundsException e) {
@@ -570,11 +565,13 @@ abstract class PlayerInjector {
* @return The player's input stream.
*/
public DataInputStream getInputStream(boolean cache) {
- if (inputField == null)
- throw new IllegalStateException("Input field is NULL.");
+ // And the data input stream that we'll use to identify a player
if (networkManager == null)
- throw new IllegalStateException("Network manager is NULL.");
-
+ throw new IllegalStateException("Network manager is NULL.");
+ if (inputField == null)
+ inputField = FuzzyReflection.fromObject(networkManager, true).
+ getFieldByType("java\\.io\\.DataInputStream");
+
// Get the associated input stream
try {
if (cache && cachedInput != null)
@@ -604,6 +601,16 @@ abstract class PlayerInjector {
return player;
}
+ /**
+ * Set the hooked player.
+ *
+ * Should only be called during the creation of the injector.
+ * @param player - the new hooked player.
+ */
+ public void setPlayer(Player player) {
+ this.player = player;
+ }
+
/**
* Object that can invoke the packet events.
* @return Packet event invoker.
@@ -622,4 +629,12 @@ abstract class PlayerInjector {
else
return player;
}
+
+ /**
+ * Set the real Bukkit player that we will use.
+ * @param updatedPlayer - the real Bukkit player.
+ */
+ public void setUpdatedPlayer(Player updatedPlayer) {
+ this.updatedPlayer = updatedPlayer;
+ }
}
diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjectorBuilder.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjectorBuilder.java
index 39448bd3..120d5dc9 100644
--- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjectorBuilder.java
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjectorBuilder.java
@@ -138,7 +138,8 @@ public class PlayerInjectorBuilder {
// Fill any default fields
initializeDefaults();
- return new ProxyPlayerInjectionHandler(classLoader, reporter, injectionFilter, invoker,
- packetListeners, server);
+ return new ProxyPlayerInjectionHandler(
+ classLoader, reporter, injectionFilter,
+ invoker, packetListeners, server);
}
}
diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/TemporaryPlayerFactory.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/TemporaryPlayerFactory.java
index 45942257..67336df5 100644
--- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/TemporaryPlayerFactory.java
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/TemporaryPlayerFactory.java
@@ -81,7 +81,7 @@ class TemporaryPlayerFactory {
*
*
* Note that the player a player has not been assigned a name yet, and thus cannot be
- * uniquely identified. Use the
+ * uniquely identified. Use the address instead.
* @param injector - the player injector used.
* @param server - the current server.
* @return A temporary player instance.
diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/spigot/DummyPacketInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/spigot/DummyPacketInjector.java
new file mode 100644
index 00000000..e77a32de
--- /dev/null
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/spigot/DummyPacketInjector.java
@@ -0,0 +1,57 @@
+package com.comphenix.protocol.injector.spigot;
+
+import java.util.Set;
+
+import org.bukkit.entity.Player;
+
+import com.comphenix.protocol.concurrency.IntegerSet;
+import com.comphenix.protocol.events.PacketContainer;
+import com.comphenix.protocol.events.PacketEvent;
+import com.comphenix.protocol.injector.packet.PacketInjector;
+
+public class DummyPacketInjector implements PacketInjector {
+ private SpigotPacketInjector injector;
+ private IntegerSet reveivedFilters;
+
+ public DummyPacketInjector(SpigotPacketInjector injector, IntegerSet reveivedFilters) {
+ this.injector = injector;
+ this.reveivedFilters = reveivedFilters;
+ }
+
+ @Override
+ public void undoCancel(Integer id, Object packet) {
+ // Do nothing yet
+ }
+
+ @Override
+ public boolean addPacketHandler(int packetID) {
+ reveivedFilters.add(packetID);
+ return true;
+ }
+
+ @Override
+ public boolean removePacketHandler(int packetID) {
+ reveivedFilters.remove(packetID);
+ return true;
+ }
+
+ @Override
+ public boolean hasPacketHandler(int packetID) {
+ return reveivedFilters.contains(packetID);
+ }
+
+ @Override
+ public Set getPacketHandlers() {
+ return reveivedFilters.toSet();
+ }
+
+ @Override
+ public PacketEvent packetRecieved(PacketContainer packet, Player client) {
+ return injector.packetReceived(packet, client);
+ }
+
+ @Override
+ public void cleanupAll() {
+ reveivedFilters.clear();
+ }
+}
diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/spigot/DummyPlayerHandler.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/spigot/DummyPlayerHandler.java
new file mode 100644
index 00000000..ee8139f0
--- /dev/null
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/spigot/DummyPlayerHandler.java
@@ -0,0 +1,123 @@
+package com.comphenix.protocol.injector.spigot;
+
+import java.io.DataInputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.net.InetSocketAddress;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.bukkit.entity.Player;
+
+import com.comphenix.protocol.concurrency.IntegerSet;
+import com.comphenix.protocol.events.PacketContainer;
+import com.comphenix.protocol.events.PacketListener;
+import com.comphenix.protocol.injector.GamePhase;
+import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
+import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
+
+class DummyPlayerHandler implements PlayerInjectionHandler {
+ private SpigotPacketInjector injector;
+ private IntegerSet sendingFilters;
+
+ public DummyPlayerHandler(SpigotPacketInjector injector, IntegerSet sendingFilters) {
+ this.injector = injector;
+ this.sendingFilters = sendingFilters;
+ }
+
+ @Override
+ public boolean uninjectPlayer(InetSocketAddress address) {
+ return true;
+ }
+
+ @Override
+ public boolean uninjectPlayer(Player player) {
+ injector.uninjectPlayer(player);
+ return true;
+ }
+
+ @Override
+ public void setPlayerHook(GamePhase phase, PlayerInjectHooks playerHook) {
+ throw new UnsupportedOperationException("This is not needed in Spigot.");
+ }
+
+ @Override
+ public void setPlayerHook(PlayerInjectHooks playerHook) {
+ throw new UnsupportedOperationException("This is not needed in Spigot.");
+ }
+
+ @Override
+ public void scheduleDataInputRefresh(Player player) {
+ // Fine
+ }
+
+ @Override
+ public void addPacketHandler(int packetID) {
+ sendingFilters.add(packetID);
+ }
+
+ @Override
+ public void removePacketHandler(int packetID) {
+ sendingFilters.remove(packetID);
+ }
+
+ @Override
+ public Set getSendingFilters() {
+ return sendingFilters.toSet();
+ }
+
+ @Override
+ public void close() {
+ sendingFilters.clear();
+ }
+
+ @Override
+ public void sendServerPacket(Player reciever, PacketContainer packet, boolean filters) throws InvocationTargetException {
+ injector.sendServerPacket(reciever, packet, filters);
+ }
+
+ @Override
+ public void processPacket(Player player, Object mcPacket) throws IllegalAccessException, InvocationTargetException {
+ injector.processPacket(player, mcPacket);
+ }
+
+ @Override
+ public void injectPlayer(Player player) {
+ injector.injectPlayer(player);
+ }
+
+ @Override
+ public void handleDisconnect(Player player) {
+ // Just ignore
+ }
+
+ @Override
+ public PlayerInjectHooks getPlayerHook(GamePhase phase) {
+ return PlayerInjectHooks.NETWORK_SERVER_OBJECT;
+ }
+
+ @Override
+ public PlayerInjectHooks getPlayerHook() {
+ // Pretend that we do
+ return PlayerInjectHooks.NETWORK_SERVER_OBJECT;
+ }
+
+ @Override
+ public Player getPlayerByConnection(DataInputStream inputStream, long playerTimeout, TimeUnit unit) throws InterruptedException {
+ throw new UnsupportedOperationException("This is not needed in Spigot.");
+ }
+
+ @Override
+ public Player getPlayerByConnection(DataInputStream inputStream) throws InterruptedException {
+ throw new UnsupportedOperationException("This is not needed in Spigot.");
+ }
+
+ @Override
+ public void checkListener(PacketListener listener) {
+ // They're all fine!
+ }
+
+ @Override
+ public void checkListener(Set listeners) {
+ // Yes, really
+ }
+}
diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/spigot/SpigotPacketInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/spigot/SpigotPacketInjector.java
new file mode 100644
index 00000000..1ff5ca62
--- /dev/null
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/spigot/SpigotPacketInjector.java
@@ -0,0 +1,447 @@
+package com.comphenix.protocol.injector.spigot;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Server;
+import org.bukkit.entity.Player;
+import org.bukkit.plugin.Plugin;
+
+import net.sf.cglib.proxy.Callback;
+import net.sf.cglib.proxy.CallbackFilter;
+import net.sf.cglib.proxy.Enhancer;
+import net.sf.cglib.proxy.MethodInterceptor;
+import net.sf.cglib.proxy.MethodProxy;
+import net.sf.cglib.proxy.NoOp;
+
+import com.comphenix.protocol.Packets;
+import com.comphenix.protocol.concurrency.IntegerSet;
+import com.comphenix.protocol.error.ErrorReporter;
+import com.comphenix.protocol.events.PacketContainer;
+import com.comphenix.protocol.events.PacketEvent;
+import com.comphenix.protocol.injector.ListenerInvoker;
+import com.comphenix.protocol.injector.PlayerLoggedOutException;
+import com.comphenix.protocol.injector.packet.PacketInjector;
+import com.comphenix.protocol.injector.player.NetworkObjectInjector;
+import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
+import com.comphenix.protocol.reflect.MethodInfo;
+import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
+import com.comphenix.protocol.utility.MinecraftReflection;
+import com.google.common.collect.MapMaker;
+
+/**
+ * Offload all the work to Spigot, if possible.
+ *
+ * @author Kristian
+ */
+public class SpigotPacketInjector implements SpigotPacketListener {
+ // Lazily retrieve the spigot listener class
+ private static volatile Class> spigotListenerClass;
+ private static volatile boolean classChecked;
+
+ // Packets that are not to be processed by the filters
+ private Set