mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2025-01-12 11:21:14 +01:00
Add support for the Netty Spigot build.
This required extensive reworking of the inner packet injection system in ProtocolLib. I've also fixed a minior bug in the fuzzy member contract class.
This commit is contained in:
parent
e8112dba0e
commit
901ab1fdda
@ -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"
|
||||
|
@ -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) {
|
||||
|
@ -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() {
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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.
|
||||
* <p>
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ class TemporaryPlayerFactory {
|
||||
* </ul>
|
||||
* <p>
|
||||
* 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.
|
||||
|
@ -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<Integer> getPacketHandlers() {
|
||||
return reveivedFilters.toSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketEvent packetRecieved(PacketContainer packet, Player client) {
|
||||
return injector.packetReceived(packet, client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanupAll() {
|
||||
reveivedFilters.clear();
|
||||
}
|
||||
}
|
@ -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<Integer> 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<PacketListener> listeners) {
|
||||
// Yes, really
|
||||
}
|
||||
}
|
@ -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<Object> ignoredPackets = Collections.newSetFromMap(new MapMaker().weakKeys().<Object, Boolean>makeMap());
|
||||
|
||||
/**
|
||||
* The amount of ticks to wait before removing all traces of a player.
|
||||
*/
|
||||
private static final int CLEANUP_DELAY = 100;
|
||||
|
||||
/**
|
||||
* Retrieve the spigot packet listener class.
|
||||
* @return The listener class.
|
||||
*/
|
||||
private static Class<?> getSpigotListenerClass() {
|
||||
if (!classChecked) {
|
||||
try {
|
||||
spigotListenerClass = SpigotPacketInjector.class.getClassLoader().loadClass("org.spigotmc.netty.PacketListener");
|
||||
} catch (ClassNotFoundException e) {
|
||||
return null;
|
||||
} finally {
|
||||
// We've given it a try now
|
||||
classChecked = true;
|
||||
}
|
||||
}
|
||||
return spigotListenerClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the register packet listener method.
|
||||
* @return The method used to register a packet listener.
|
||||
*/
|
||||
private static Method getRegisterMethod() {
|
||||
Class<?> clazz = getSpigotListenerClass();
|
||||
|
||||
if (clazz != null) {
|
||||
try {
|
||||
return clazz.getMethod("register", clazz, Plugin.class);
|
||||
} catch (SecurityException e) {
|
||||
// If this happens, then ... we're doomed
|
||||
throw new RuntimeException("Reflection is not allowed.", e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new IllegalStateException("Cannot find register() method in " + clazz, e);
|
||||
}
|
||||
}
|
||||
|
||||
// Also bad
|
||||
throw new IllegalStateException("Spigot could not be found!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if there is a Spigot packet listener.
|
||||
* @return Spigot packet listener.
|
||||
*/
|
||||
public static boolean canUseSpigotListener() {
|
||||
return getSpigotListenerClass() != null;
|
||||
}
|
||||
|
||||
// The listener we will register on Spigot.
|
||||
// Unfortunately, due to the use of PlayerConnection, INetworkManager and Packet, we're
|
||||
// unable to reference it directly. But with CGLib, it shouldn't cost us much.
|
||||
private Object dynamicListener;
|
||||
|
||||
// Reference to ProtocolLib
|
||||
private Plugin plugin;
|
||||
|
||||
// Different sending filters
|
||||
private IntegerSet queuedFilters;
|
||||
private IntegerSet reveivedFilters;
|
||||
|
||||
// NetworkManager to injector and player
|
||||
private ConcurrentMap<Object, NetworkObjectInjector> networkManagerInjector = new ConcurrentHashMap<Object, NetworkObjectInjector>();
|
||||
|
||||
// Player to injector
|
||||
private ConcurrentMap<Player, NetworkObjectInjector> playerInjector = new ConcurrentHashMap<Player, NetworkObjectInjector>();
|
||||
|
||||
// Responsible for informing the PL packet listeners
|
||||
private ListenerInvoker invoker;
|
||||
private ErrorReporter reporter;
|
||||
private Server server;
|
||||
private ClassLoader classLoader;
|
||||
|
||||
/**
|
||||
* Create a new spigot injector.
|
||||
*/
|
||||
public SpigotPacketInjector(ClassLoader classLoader, ErrorReporter reporter, ListenerInvoker invoker, Server server) {
|
||||
this.classLoader = classLoader;
|
||||
this.reporter = reporter;
|
||||
this.invoker = invoker;
|
||||
this.server = server;
|
||||
this.queuedFilters = new IntegerSet(Packets.MAXIMUM_PACKET_ID + 1);
|
||||
this.reveivedFilters = new IntegerSet(Packets.MAXIMUM_PACKET_ID + 1);
|
||||
}
|
||||
|
||||
public boolean register(Plugin plugin) {
|
||||
if (hasRegistered())
|
||||
return false;
|
||||
|
||||
// Save the plugin too
|
||||
this.plugin = plugin;
|
||||
|
||||
final Callback[] callbacks = new Callback[3];
|
||||
final boolean[] found = new boolean[3];
|
||||
|
||||
// Packets received from the clients
|
||||
callbacks[0] = new MethodInterceptor() {
|
||||
@Override
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
return SpigotPacketInjector.this.packetReceived(args[0], args[1], args[2]);
|
||||
}
|
||||
};
|
||||
// Packet sent/queued
|
||||
callbacks[1] = new MethodInterceptor() {
|
||||
@Override
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
return SpigotPacketInjector.this.packetQueued(args[0], args[1], args[2]);
|
||||
}
|
||||
};
|
||||
|
||||
// Don't care for everything else
|
||||
callbacks[2] = NoOp.INSTANCE;
|
||||
|
||||
Enhancer enhancer = new Enhancer();
|
||||
enhancer.setClassLoader(classLoader);
|
||||
enhancer.setSuperclass(getSpigotListenerClass());
|
||||
enhancer.setCallbacks(callbacks);
|
||||
enhancer.setCallbackFilter(new CallbackFilter() {
|
||||
@Override
|
||||
public int accept(Method method) {
|
||||
// We'll be pretty stringent
|
||||
if (matchMethod("packetReceived", method)) {
|
||||
found[0] = true;
|
||||
return 0;
|
||||
} else if (matchMethod("packetQueued", method)) {
|
||||
found[1] = true;
|
||||
return 1;
|
||||
} else {
|
||||
found[2] = true;
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
});
|
||||
dynamicListener = enhancer.create();
|
||||
|
||||
// Verify methods
|
||||
if (!found[0])
|
||||
throw new IllegalStateException("Unable to find a valid packet receiver in Spigot.");
|
||||
if (!found[1])
|
||||
throw new IllegalStateException("Unable to find a valid packet queue in Spigot.");
|
||||
|
||||
// Lets register it too
|
||||
try {
|
||||
getRegisterMethod().invoke(null, dynamicListener, plugin);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot register Spigot packet listener.", e);
|
||||
}
|
||||
|
||||
// If we succeed
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given method is a valid packet receiver or queued method.
|
||||
* @param methodName - the expected name of the method.
|
||||
* @param method - the method we're testing.
|
||||
* @return TRUE if this is a correct method, FALSE otherwise.
|
||||
*/
|
||||
private boolean matchMethod(String methodName, Method method) {
|
||||
return FuzzyMethodContract.newBuilder().
|
||||
nameExact(methodName).
|
||||
parameterCount(3).
|
||||
parameterSuperOf(MinecraftReflection.getNetHandlerClass(), 1).
|
||||
parameterSuperOf(MinecraftReflection.getPacketClass(), 2).
|
||||
returnTypeExact(MinecraftReflection.getPacketClass()).
|
||||
build().
|
||||
isMatch(MethodInfo.fromMethod(method), null);
|
||||
}
|
||||
|
||||
public boolean hasRegistered() {
|
||||
return dynamicListener != null;
|
||||
}
|
||||
|
||||
public PlayerInjectionHandler getPlayerHandler() {
|
||||
return new DummyPlayerHandler(this, queuedFilters);
|
||||
}
|
||||
|
||||
public PacketInjector getPacketInjector() {
|
||||
return new DummyPacketInjector(this, reveivedFilters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the currently registered injector for the given player.
|
||||
* @param player - injected player.
|
||||
* @return The injector.
|
||||
*/
|
||||
NetworkObjectInjector getInjector(Player player) {
|
||||
return playerInjector.get(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve or create a registered injector for the given network manager and connection.
|
||||
* @param networkManager - a INetworkManager object.
|
||||
* @param connection - a Connection (PlayerConnection, PendingConnection) object.
|
||||
* @return The created NetworkObjectInjector with a temporary player.
|
||||
*/
|
||||
NetworkObjectInjector getInjector(Object networkManager, Object connection) {
|
||||
NetworkObjectInjector dummyInjector = networkManagerInjector.get(networkManager);
|
||||
|
||||
if (dummyInjector == null) {
|
||||
// Inject the network manager
|
||||
try {
|
||||
NetworkObjectInjector created = new NetworkObjectInjector(classLoader, reporter, null, invoker, null);
|
||||
|
||||
created.initializeLogin(connection);
|
||||
created.setPlayer(created.createTemporaryPlayer(server));
|
||||
dummyInjector = saveInjector(networkManager, created);
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Cannot create dummy injector.", e);
|
||||
}
|
||||
}
|
||||
|
||||
return dummyInjector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a given player injector for later.
|
||||
* @param networkManager - the associated network manager.
|
||||
* @param created - the created network object creator.
|
||||
* @return Any other network injector that came before us.
|
||||
*/
|
||||
private NetworkObjectInjector saveInjector(Object networkManager, NetworkObjectInjector created) {
|
||||
// Concurrency - use the same injector!
|
||||
NetworkObjectInjector result = networkManagerInjector.putIfAbsent(networkManager, created);
|
||||
|
||||
if (result == null) {
|
||||
result = created;
|
||||
}
|
||||
|
||||
// Save the player as well
|
||||
playerInjector.put(created.getPlayer(), created);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object packetReceived(Object networkManager, Object connection, Object packet) {
|
||||
Integer id = invoker.getPacketID(packet);
|
||||
|
||||
if (id != null && reveivedFilters.contains(id)) {
|
||||
// Check for ignored packets
|
||||
if (ignoredPackets.remove(packet)) {
|
||||
return packet;
|
||||
}
|
||||
|
||||
Player sender = getInjector(networkManager, connection).getUpdatedPlayer();
|
||||
PacketContainer container = new PacketContainer(id, packet);
|
||||
PacketEvent event = packetReceived(container, sender);
|
||||
|
||||
if (!event.isCancelled())
|
||||
return event.getPacket().getHandle();
|
||||
else
|
||||
return null; // Cancel
|
||||
}
|
||||
// Don't change anything
|
||||
return packet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object packetQueued(Object networkManager, Object connection, Object packet) {
|
||||
Integer id = invoker.getPacketID(packet);
|
||||
|
||||
if (id != null & queuedFilters.contains(id)) {
|
||||
// Check for ignored packets
|
||||
if (ignoredPackets.remove(packet)) {
|
||||
return packet;
|
||||
}
|
||||
|
||||
Player reciever = getInjector(networkManager, connection).getUpdatedPlayer();
|
||||
PacketContainer container = new PacketContainer(id, packet);
|
||||
PacketEvent event = packetQueued(container, reciever);
|
||||
|
||||
if (!event.isCancelled())
|
||||
return event.getPacket().getHandle();
|
||||
else
|
||||
return null; // Cancel
|
||||
}
|
||||
// Don't change anything
|
||||
return packet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to inform the event listeners of a queued packet.
|
||||
* @param packet - the packet that is to be sent.
|
||||
* @param reciever - the reciever of this packet.
|
||||
* @return The packet event that was used.
|
||||
*/
|
||||
PacketEvent packetQueued(PacketContainer packet, Player reciever) {
|
||||
PacketEvent event = PacketEvent.fromServer(this, packet, reciever);
|
||||
|
||||
invoker.invokePacketSending(event);
|
||||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to inform the event listeners of a received packet.
|
||||
* @param packet - the packet that has been receieved.
|
||||
* @param sender - the client packet.
|
||||
* @return The packet event that was used.
|
||||
*/
|
||||
PacketEvent packetReceived(PacketContainer packet, Player sender) {
|
||||
PacketEvent event = PacketEvent.fromClient(this, packet, sender);
|
||||
|
||||
invoker.invokePacketRecieving(event);
|
||||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a player has logged in properly.
|
||||
* @param player - the player that has logged in.
|
||||
*/
|
||||
void injectPlayer(Player player) {
|
||||
try {
|
||||
NetworkObjectInjector dummy = new NetworkObjectInjector(classLoader, reporter, player, invoker, null);
|
||||
dummy.initializePlayer(player);
|
||||
|
||||
// Save this player for the network manager
|
||||
NetworkObjectInjector realInjector = networkManagerInjector.get(dummy.getNetworkManager());
|
||||
|
||||
if (realInjector != null) {
|
||||
// Update all future references
|
||||
realInjector.setUpdatedPlayer(player);
|
||||
playerInjector.put(player, realInjector);
|
||||
} else {
|
||||
// Ah - in that case, save this injector
|
||||
saveInjector(dummy.getNetworkManager(), dummy);
|
||||
}
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Cannot inject " + player);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninject the given player.
|
||||
* @param player - the player to uninject.
|
||||
*/
|
||||
void uninjectPlayer(Player player) {
|
||||
final NetworkObjectInjector injector = getInjector(player);
|
||||
|
||||
if (player != null) {
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Clean up
|
||||
playerInjector.remove(injector.getPlayer());
|
||||
playerInjector.remove(injector.getUpdatedPlayer());
|
||||
networkManagerInjector.remove(injector);
|
||||
}
|
||||
}, CLEANUP_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a plugin wants to sent a packet.
|
||||
* @param reciever - the packet receiver.
|
||||
* @param packet - the packet to transmit.
|
||||
* @param filters - whether or not to invoke the packet listeners.
|
||||
* @throws InvocationTargetException If anything went wrong.
|
||||
*/
|
||||
void sendServerPacket(Player reciever, PacketContainer packet, boolean filters) throws InvocationTargetException {
|
||||
NetworkObjectInjector networkObject = getInjector(reciever);
|
||||
|
||||
// If TRUE, process this packet like any other
|
||||
if (filters)
|
||||
ignoredPackets.remove(packet.getHandle());
|
||||
else
|
||||
ignoredPackets.add(packet.getHandle());
|
||||
|
||||
if (networkObject != null)
|
||||
networkObject.sendServerPacket(packet.getHandle(), filters);
|
||||
else
|
||||
throw new PlayerLoggedOutException("Player " + reciever + " has logged out");
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a plugin wants to simulate receiving a packet.
|
||||
* @param player - the supposed sender.
|
||||
* @param mcPacket - the packet to receieve.
|
||||
* @throws IllegalAccessException Reflection is not permitted.
|
||||
* @throws InvocationTargetException Minecraft threw an exception.
|
||||
*/
|
||||
void processPacket(Player player, Object mcPacket) throws IllegalAccessException, InvocationTargetException {
|
||||
NetworkObjectInjector networkObject = getInjector(player);
|
||||
|
||||
// We will always ignore this packet
|
||||
ignoredPackets.add(mcPacket);
|
||||
|
||||
if (networkObject != null)
|
||||
networkObject.processPacket(mcPacket);
|
||||
else
|
||||
throw new PlayerLoggedOutException("Player " + player + " has logged out");
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package com.comphenix.protocol.injector.spigot;
|
||||
|
||||
/**
|
||||
* Represents a proxy for a Spigot packet listener.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
interface SpigotPacketListener {
|
||||
/**
|
||||
* Called when a packet has been received and is about to be handled by the
|
||||
* current Connection.
|
||||
* <p>
|
||||
* The returned packet will be the packet passed on for handling, or in the case of
|
||||
* null being returned, not handled at all.
|
||||
*
|
||||
* @param networkManager the NetworkManager receiving the packet
|
||||
* @param connection the connection which will handle the packet
|
||||
* @param packet the received packet
|
||||
* @return the packet to be handled, or null to cancel
|
||||
*/
|
||||
public Object packetReceived(Object networkManager, Object connection, Object packet);
|
||||
|
||||
/**
|
||||
* Called when a packet is queued to be sent.The returned packet will be
|
||||
* the packet sent. In the case of null being returned, the packet will not
|
||||
* be sent.
|
||||
*
|
||||
* @param networkManager the NetworkManager which will send the packet
|
||||
* @param connection the connection which queued the packet
|
||||
* @param packet the queue packet
|
||||
* @return the packet to be sent, or null if the packet will not be sent.
|
||||
*/
|
||||
public Object packetQueued(Object networkManager, Object connection, Object packet);
|
||||
}
|
@ -153,10 +153,7 @@ public abstract class AbstractFuzzyMember<T extends Member> extends AbstractFuzz
|
||||
* Use this to prepare any special values.
|
||||
*/
|
||||
protected void prepareBuild() {
|
||||
// Permit any modifier if we havent's specified anything
|
||||
if (modifiersRequired == 0) {
|
||||
modifiersRequired = Integer.MAX_VALUE;
|
||||
}
|
||||
// No need to prepare anything
|
||||
}
|
||||
|
||||
// Clone a given contract
|
||||
|
Loading…
Reference in New Issue
Block a user