Dynamically add or remove injected hooks depending on the listeners.
This occurs whenever a listener is added or removed. A listener can now specify whether or not it's listening for packets sent BEFORE a player has logged in (every packet upto Packet1Login and a few more), or AFTER. By default, listeners only receive notifcation of packets sent and received after. ProtocolLib will now only hook NetLoginHandler if there's a login listener, and vice versa. Thus, the new login feature will only tax the server if another plugin is using it. In addition, ProtocolLib will not consume any resources when it's not serving any listeners.
Dieser Commit ist enthalten in:
Ursprung
ecdc9b4b6c
Commit
476a918794
@ -58,6 +58,9 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
private int tickCounter = 0;
|
private int tickCounter = 0;
|
||||||
private static final int ASYNC_PACKET_DELAY = 1;
|
private static final int ASYNC_PACKET_DELAY = 1;
|
||||||
|
|
||||||
|
// Used for debugging
|
||||||
|
private boolean debugListener;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoad() {
|
public void onLoad() {
|
||||||
logger = getLoggerSafely();
|
logger = getLoggerSafely();
|
||||||
@ -81,13 +84,9 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
// Player login and logout events
|
// Player login and logout events
|
||||||
protocolManager.registerEvents(manager, this);
|
protocolManager.registerEvents(manager, this);
|
||||||
|
|
||||||
// Inject our hook into already existing players
|
|
||||||
protocolManager.initializePlayers(server.getOnlinePlayers());
|
|
||||||
|
|
||||||
// Worker that ensures that async packets are eventually sent
|
// Worker that ensures that async packets are eventually sent
|
||||||
createAsyncTask(server);
|
createAsyncTask(server);
|
||||||
|
//toggleDebugListener();
|
||||||
addDebugListener();
|
|
||||||
|
|
||||||
// Try to enable statistics
|
// Try to enable statistics
|
||||||
try {
|
try {
|
||||||
@ -99,19 +98,38 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addDebugListener() {
|
/**
|
||||||
|
* Toggle a listener that prints every sent and received packet.
|
||||||
|
*/
|
||||||
|
void toggleDebugListener() {
|
||||||
|
|
||||||
|
if (debugListener) {
|
||||||
|
protocolManager.removePacketListeners(this);
|
||||||
|
} else {
|
||||||
// DEBUG DEBUG
|
// DEBUG DEBUG
|
||||||
protocolManager.addPacketListener(new MonitorAdapter(this, ConnectionSide.BOTH, logger) {
|
protocolManager.addPacketListener(new MonitorAdapter(this, ConnectionSide.BOTH, logger) {
|
||||||
@Override
|
@Override
|
||||||
public void onPacketReceiving(PacketEvent event) {
|
public void onPacketReceiving(PacketEvent event) {
|
||||||
System.out.println("RECEIVING " + event.getPacketID() + " from " + event.getPlayer().getName());
|
Object handle = event.getPacket().getHandle();
|
||||||
|
|
||||||
|
System.out.println(String.format(
|
||||||
|
"RECEIVING %s@%s from %s.",
|
||||||
|
handle.getClass().getSimpleName(), handle.hashCode(), event.getPlayer().getName()
|
||||||
|
));
|
||||||
};
|
};
|
||||||
@Override
|
@Override
|
||||||
public void onPacketSending(PacketEvent event) {
|
public void onPacketSending(PacketEvent event) {
|
||||||
System.out.println("SENDING " + event.getPacketID() + " to " + event.getPlayer().getName());
|
Object handle = event.getPacket().getHandle();
|
||||||
|
|
||||||
|
System.out.println(String.format(
|
||||||
|
"SENDING %s@%s from %s.",
|
||||||
|
handle.getClass().getSimpleName(), handle.hashCode(), event.getPlayer().getName()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
debugListener = !debugListener;
|
||||||
|
}
|
||||||
|
|
||||||
private void createAsyncTask(Server server) {
|
private void createAsyncTask(Server server) {
|
||||||
try {
|
try {
|
||||||
|
@ -67,7 +67,7 @@ class NullPacketListener implements PacketListener {
|
|||||||
|
|
||||||
private ListeningWhitelist cloneWhitelist(ListenerPriority priority, ListeningWhitelist whitelist) {
|
private ListeningWhitelist cloneWhitelist(ListenerPriority priority, ListeningWhitelist whitelist) {
|
||||||
if (whitelist != null)
|
if (whitelist != null)
|
||||||
return new ListeningWhitelist(priority, whitelist.getWhitelist());
|
return new ListeningWhitelist(priority, whitelist.getWhitelist(), whitelist.getGamePhase());
|
||||||
else
|
else
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ package com.comphenix.protocol.events;
|
|||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.injector.GamePhase;
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
@ -36,6 +37,7 @@ public class ListeningWhitelist {
|
|||||||
|
|
||||||
private ListenerPriority priority;
|
private ListenerPriority priority;
|
||||||
private Set<Integer> whitelist;
|
private Set<Integer> whitelist;
|
||||||
|
private GamePhase gamePhase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a packet whitelist for a given priority with a set of packet IDs.
|
* Creates a packet whitelist for a given priority with a set of packet IDs.
|
||||||
@ -43,8 +45,19 @@ public class ListeningWhitelist {
|
|||||||
* @param whitelist - set of IDs to observe/enable.
|
* @param whitelist - set of IDs to observe/enable.
|
||||||
*/
|
*/
|
||||||
public ListeningWhitelist(ListenerPriority priority, Set<Integer> whitelist) {
|
public ListeningWhitelist(ListenerPriority priority, Set<Integer> whitelist) {
|
||||||
|
this(priority, whitelist, GamePhase.PLAYING);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
* @param gamePhase - which game phase to receieve notifications on.
|
||||||
|
*/
|
||||||
|
public ListeningWhitelist(ListenerPriority priority, Set<Integer> whitelist, GamePhase gamePhase) {
|
||||||
this.priority = priority;
|
this.priority = priority;
|
||||||
this.whitelist = whitelist;
|
this.whitelist = whitelist;
|
||||||
|
this.gamePhase = gamePhase;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -55,6 +68,19 @@ public class ListeningWhitelist {
|
|||||||
public ListeningWhitelist(ListenerPriority priority, Integer... whitelist) {
|
public ListeningWhitelist(ListenerPriority priority, Integer... whitelist) {
|
||||||
this.priority = priority;
|
this.priority = priority;
|
||||||
this.whitelist = Sets.newHashSet(whitelist);
|
this.whitelist = Sets.newHashSet(whitelist);
|
||||||
|
this.gamePhase = GamePhase.PLAYING;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a packet whitelist for a given priority with a set of packet IDs.
|
||||||
|
* @param priority - the listener priority.
|
||||||
|
* @param whitelist - list of packet IDs to observe/enable.
|
||||||
|
* @param gamePhase - which game phase to receieve notifications on.
|
||||||
|
*/
|
||||||
|
public ListeningWhitelist(ListenerPriority priority, Integer[] whitelist, GamePhase gamePhase) {
|
||||||
|
this.priority = priority;
|
||||||
|
this.whitelist = Sets.newHashSet(whitelist);
|
||||||
|
this.gamePhase = gamePhase;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -81,9 +107,17 @@ public class ListeningWhitelist {
|
|||||||
return whitelist;
|
return whitelist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve which game phase this listener is active under.
|
||||||
|
* @return The active game phase.
|
||||||
|
*/
|
||||||
|
public GamePhase getGamePhase() {
|
||||||
|
return gamePhase;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode(){
|
public int hashCode(){
|
||||||
return Objects.hashCode(priority, whitelist);
|
return Objects.hashCode(priority, whitelist, gamePhase);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -9,8 +9,8 @@ import com.comphenix.protocol.Packets;
|
|||||||
import com.comphenix.protocol.events.ConnectionSide;
|
import com.comphenix.protocol.events.ConnectionSide;
|
||||||
import com.comphenix.protocol.events.ListenerPriority;
|
import com.comphenix.protocol.events.ListenerPriority;
|
||||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||||
import com.comphenix.protocol.events.PacketEvent;
|
|
||||||
import com.comphenix.protocol.events.PacketListener;
|
import com.comphenix.protocol.events.PacketListener;
|
||||||
|
import com.comphenix.protocol.injector.GamePhase;
|
||||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,14 +38,14 @@ public abstract class MonitorAdapter implements PacketListener {
|
|||||||
// Recover in case something goes wrong
|
// Recover in case something goes wrong
|
||||||
try {
|
try {
|
||||||
if (side.isForServer())
|
if (side.isForServer())
|
||||||
this.sending = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Server.getSupported());
|
this.sending = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Server.getSupported(), GamePhase.BOTH);
|
||||||
if (side.isForClient())
|
if (side.isForClient())
|
||||||
this.receiving = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Client.getSupported());
|
this.receiving = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Client.getSupported(), GamePhase.BOTH);
|
||||||
} catch (FieldAccessException e) {
|
} catch (FieldAccessException e) {
|
||||||
if (side.isForServer())
|
if (side.isForServer())
|
||||||
this.sending = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Server.getRegistry().values());
|
this.sending = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Server.getRegistry().values(), GamePhase.BOTH);
|
||||||
if (side.isForClient())
|
if (side.isForClient())
|
||||||
this.receiving = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Client.getRegistry().values());
|
this.receiving = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Client.getRegistry().values(), GamePhase.BOTH);
|
||||||
logger.log(Level.WARNING, "Defaulting to 1.3 packets.", e);
|
logger.log(Level.WARNING, "Defaulting to 1.3 packets.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,16 +63,6 @@ public abstract class MonitorAdapter implements PacketListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPacketReceiving(PacketEvent event) {
|
|
||||||
// Empty for now
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPacketSending(PacketEvent event) {
|
|
||||||
// Empty for now
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ListeningWhitelist getSendingWhitelist() {
|
public ListeningWhitelist getSendingWhitelist() {
|
||||||
return sending;
|
return sending;
|
||||||
|
@ -21,9 +21,12 @@ import java.util.Set;
|
|||||||
|
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.injector.GamePhase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a packet listener with useful constructors.
|
* Represents a packet listener with useful constructors.
|
||||||
*
|
* <p>
|
||||||
|
* Remember to override onPacketReceiving() and onPacketSending(), depending on the ConnectionSide.
|
||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
public abstract class PacketAdapter implements PacketListener {
|
public abstract class PacketAdapter implements PacketListener {
|
||||||
@ -51,7 +54,20 @@ 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, Set<Integer> packets) {
|
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority, Set<Integer> packets) {
|
||||||
this(plugin, connectionSide, listenerPriority, packets.toArray(new Integer[0]));
|
this(plugin, connectionSide, listenerPriority, GamePhase.PLAYING, packets.toArray(new Integer[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a packet listener for a single connection side.
|
||||||
|
* <p>
|
||||||
|
* The game phase is used to optmize performance. A listener should only choose BOTH or LOGIN if it's absolutely necessary.
|
||||||
|
* @param plugin - the plugin that spawned this listener.
|
||||||
|
* @param connectionSide - the packet type the listener is looking for.
|
||||||
|
* @param listenerPriority - the event priority.
|
||||||
|
* @param packets - the packet IDs the listener is looking for.
|
||||||
|
*/
|
||||||
|
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority, GamePhase gamePhase, Set<Integer> packets) {
|
||||||
|
this(plugin, connectionSide, listenerPriority, gamePhase, packets.toArray(new Integer[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -62,20 +78,36 @@ 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) {
|
||||||
|
this(plugin, connectionSide, listenerPriority, GamePhase.PLAYING, packets);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a packet listener for a single connection side.
|
||||||
|
* <p>
|
||||||
|
* The game phase is used to optmize performance. A listener should only choose BOTH or LOGIN if it's absolutely necessary.
|
||||||
|
* @param plugin - the plugin that spawned this listener.
|
||||||
|
* @param connectionSide - the packet type the listener is looking for.
|
||||||
|
* @param listenerPriority - the event priority.
|
||||||
|
* @param gamePhase - which game phase this listener is active under.
|
||||||
|
* @param packets - the packet IDs the listener is looking for.
|
||||||
|
*/
|
||||||
|
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority, GamePhase gamePhase, Integer... packets) {
|
||||||
if (plugin == null)
|
if (plugin == null)
|
||||||
throw new IllegalArgumentException("plugin cannot be null");
|
throw new IllegalArgumentException("plugin cannot be null");
|
||||||
if (connectionSide == null)
|
if (connectionSide == null)
|
||||||
throw new IllegalArgumentException("connectionSide cannot be null");
|
throw new IllegalArgumentException("connectionSide cannot be null");
|
||||||
if (listenerPriority == null)
|
if (listenerPriority == null)
|
||||||
throw new IllegalArgumentException("listenerPriority cannot be null");
|
throw new IllegalArgumentException("listenerPriority cannot be null");
|
||||||
|
if (gamePhase == null)
|
||||||
|
throw new IllegalArgumentException("gamePhase cannot be NULL");
|
||||||
if (packets == null)
|
if (packets == null)
|
||||||
throw new IllegalArgumentException("packets cannot be null");
|
throw new IllegalArgumentException("packets cannot be null");
|
||||||
|
|
||||||
// Add whitelists
|
// Add whitelists
|
||||||
if (connectionSide.isForServer())
|
if (connectionSide.isForServer())
|
||||||
sendingWhitelist = new ListeningWhitelist(listenerPriority, packets);
|
sendingWhitelist = new ListeningWhitelist(listenerPriority, packets, gamePhase);
|
||||||
if (connectionSide.isForClient())
|
if (connectionSide.isForClient())
|
||||||
receivingWhitelist = new ListeningWhitelist(listenerPriority, packets);
|
receivingWhitelist = new ListeningWhitelist(listenerPriority, packets, gamePhase);
|
||||||
|
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.connectionSide = connectionSide;
|
this.connectionSide = connectionSide;
|
||||||
@ -83,12 +115,14 @@ public abstract class PacketAdapter implements PacketListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPacketReceiving(PacketEvent event) {
|
public void onPacketReceiving(PacketEvent event) {
|
||||||
// Default is to do nothing
|
// Lets prevent some bugs
|
||||||
|
throw new IllegalStateException("Override onPacketReceiving to get notifcations of received packets!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPacketSending(PacketEvent event) {
|
public void onPacketSending(PacketEvent event) {
|
||||||
// And here too
|
// Lets prevent some bugs
|
||||||
|
throw new IllegalStateException("Override onPacketSending to get notifcations of sent packets!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
package com.comphenix.protocol.injector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current player phase. This is used to limit the number of different injections.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public enum GamePhase {
|
||||||
|
/**
|
||||||
|
* Only listen for packets sent or received before a player has logged in.
|
||||||
|
*/
|
||||||
|
LOGIN,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only listen for packets sent or received after a player has logged in.
|
||||||
|
*/
|
||||||
|
PLAYING,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen for every sent and received packet.
|
||||||
|
*/
|
||||||
|
BOTH;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the current value represents the login phase.
|
||||||
|
* @return TRUE if it does, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean hasLogin() {
|
||||||
|
return this == LOGIN || this == BOTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the current value represents the playing phase.
|
||||||
|
* @return TRUE if it does, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean hasPlaying() {
|
||||||
|
return this == PLAYING || this == BOTH;
|
||||||
|
}
|
||||||
|
}
|
@ -23,9 +23,12 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import net.minecraft.server.Packet;
|
import net.minecraft.server.Packet;
|
||||||
import net.sf.cglib.proxy.Enhancer;
|
import net.sf.cglib.proxy.Enhancer;
|
||||||
import net.sf.cglib.proxy.MethodInterceptor;
|
import net.sf.cglib.proxy.MethodInterceptor;
|
||||||
@ -52,6 +55,7 @@ import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
|||||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
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.base.Predicate;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
|
||||||
public final class PacketFilterManager implements ProtocolManager, ListenerInvoker {
|
public final class PacketFilterManager implements ProtocolManager, ListenerInvoker {
|
||||||
@ -109,6 +113,9 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
// Error logger
|
// Error logger
|
||||||
private Logger logger;
|
private Logger logger;
|
||||||
|
|
||||||
|
// The current server
|
||||||
|
private Server server;
|
||||||
|
|
||||||
// The async packet handler
|
// The async packet handler
|
||||||
private AsyncFilterManager asyncFilterManager;
|
private AsyncFilterManager asyncFilterManager;
|
||||||
|
|
||||||
@ -116,6 +123,9 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
private Set<Integer> serverPackets;
|
private Set<Integer> serverPackets;
|
||||||
private Set<Integer> clientPackets;
|
private Set<Integer> clientPackets;
|
||||||
|
|
||||||
|
// Ensure that we're not performing too may injections
|
||||||
|
private AtomicInteger phaseLoginCount = new AtomicInteger(0);
|
||||||
|
private AtomicInteger phasePlayingCount = new AtomicInteger(0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only create instances of this class if protocol lib is disabled.
|
* Only create instances of this class if protocol lib is disabled.
|
||||||
@ -126,11 +136,26 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
if (classLoader == null)
|
if (classLoader == null)
|
||||||
throw new IllegalArgumentException("classLoader cannot be NULL.");
|
throw new IllegalArgumentException("classLoader cannot be NULL.");
|
||||||
|
|
||||||
|
// Used to determine if injection is needed
|
||||||
|
Predicate<GamePhase> isInjectionNecessary = new Predicate<GamePhase>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(@Nullable GamePhase phase) {
|
||||||
|
boolean result = true;
|
||||||
|
|
||||||
|
if (phase.hasLogin())
|
||||||
|
result &= getPhaseLoginCount() > 0;
|
||||||
|
if (phase.hasPlaying())
|
||||||
|
result &= getPhasePlayingCount() > 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Initialize values
|
// Initialize values
|
||||||
|
this.server = server;
|
||||||
this.classLoader = classLoader;
|
this.classLoader = classLoader;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.playerInjection = new PlayerInjectionHandler(classLoader, logger, this, server);
|
this.playerInjection = new PlayerInjectionHandler(classLoader, logger, isInjectionNecessary, this, server);
|
||||||
this.packetInjector = new PacketInjector(classLoader, this, playerInjection);
|
this.packetInjector = new PacketInjector(classLoader, this, playerInjection);
|
||||||
this.asyncFilterManager = new AsyncFilterManager(logger, server.getScheduler(), this);
|
this.asyncFilterManager = new AsyncFilterManager(logger, server.getScheduler(), this);
|
||||||
|
|
||||||
@ -210,11 +235,51 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
enablePacketFilters(listener, ConnectionSide.CLIENT_SIDE, receiving.getWhitelist());
|
enablePacketFilters(listener, ConnectionSide.CLIENT_SIDE, receiving.getWhitelist());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Increment phases too
|
||||||
|
if (hasSending)
|
||||||
|
incrementPhases(sending.getGamePhase());
|
||||||
|
if (hasReceiving)
|
||||||
|
incrementPhases(receiving.getGamePhase());
|
||||||
|
|
||||||
// Inform our injected hooks
|
// Inform our injected hooks
|
||||||
packetListeners.add(listener);
|
packetListeners.add(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked to handle the different game phases of a added listener.
|
||||||
|
* @param phase - listener's game game phase.
|
||||||
|
*/
|
||||||
|
private void incrementPhases(GamePhase phase) {
|
||||||
|
if (phase.hasLogin())
|
||||||
|
phaseLoginCount.incrementAndGet();
|
||||||
|
|
||||||
|
// We may have to inject into every current player
|
||||||
|
if (phase.hasPlaying()) {
|
||||||
|
if (phasePlayingCount.incrementAndGet() == 1) {
|
||||||
|
// Inject our hook into already existing players
|
||||||
|
initializePlayers(server.getOnlinePlayers());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked to handle the different game phases of a removed listener.
|
||||||
|
* @param phase - listener's game game phase.
|
||||||
|
*/
|
||||||
|
private void decrementPhases(GamePhase phase) {
|
||||||
|
if (phase.hasLogin())
|
||||||
|
phaseLoginCount.decrementAndGet();
|
||||||
|
|
||||||
|
// We may have to inject into every current player
|
||||||
|
if (phase.hasPlaying()) {
|
||||||
|
if (phasePlayingCount.decrementAndGet() == 0) {
|
||||||
|
// Inject our hook into already existing players
|
||||||
|
uninitializePlayers(server.getOnlinePlayers());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the packet IDs in a whitelist is valid.
|
* Determine if the packet IDs in a whitelist is valid.
|
||||||
* @param listener - the listener that will be mentioned in the error.
|
* @param listener - the listener that will be mentioned in the error.
|
||||||
@ -246,11 +311,15 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
if (!packetListeners.remove(listener))
|
if (!packetListeners.remove(listener))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Add listeners
|
// Remove listeners and phases
|
||||||
if (sending != null && sending.isEnabled())
|
if (sending != null && sending.isEnabled()) {
|
||||||
sendingRemoved = sendingListeners.removeListener(listener, sending);
|
sendingRemoved = sendingListeners.removeListener(listener, sending);
|
||||||
if (receiving != null && receiving.isEnabled())
|
decrementPhases(sending.getGamePhase());
|
||||||
|
}
|
||||||
|
if (receiving != null && receiving.isEnabled()) {
|
||||||
receivingRemoved = recievedListeners.removeListener(listener, receiving);
|
receivingRemoved = recievedListeners.removeListener(listener, receiving);
|
||||||
|
decrementPhases(receiving.getGamePhase());
|
||||||
|
}
|
||||||
|
|
||||||
// Remove hooks, if needed
|
// Remove hooks, if needed
|
||||||
if (sendingRemoved != null && sendingRemoved.size() > 0)
|
if (sendingRemoved != null && sendingRemoved.size() > 0)
|
||||||
@ -471,6 +540,15 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
playerInjection.injectPlayer(player);
|
playerInjection.injectPlayer(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uninitialize the packet injection of every player.
|
||||||
|
* @param players - list of players to uninject.
|
||||||
|
*/
|
||||||
|
public void uninitializePlayers(Player[] players) {
|
||||||
|
for (Player player : players)
|
||||||
|
playerInjection.uninjectPlayer(player);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register this protocol manager on Bukkit.
|
* Register this protocol manager on Bukkit.
|
||||||
* @param manager - Bukkit plugin manager that provides player join/leave events.
|
* @param manager - Bukkit plugin manager that provides player join/leave events.
|
||||||
@ -488,6 +566,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
|
|
||||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||||
|
// This call will be ignored if no listeners are registered
|
||||||
playerInjection.injectPlayer(event.getPlayer());
|
playerInjection.injectPlayer(event.getPlayer());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -512,6 +591,22 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the number of listeners that expect packets during playing.
|
||||||
|
* @return Number of listeners.
|
||||||
|
*/
|
||||||
|
private int getPhasePlayingCount() {
|
||||||
|
return phasePlayingCount.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the number of listeners that expect packets during login.
|
||||||
|
* @return Number of listeners
|
||||||
|
*/
|
||||||
|
private int getPhaseLoginCount() {
|
||||||
|
return phaseLoginCount.get();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPacketID(Packet packet) {
|
public int getPacketID(Packet packet) {
|
||||||
if (packet == null)
|
if (packet == null)
|
||||||
|
@ -198,9 +198,13 @@ class PacketInjector {
|
|||||||
|
|
||||||
// Called from the ReadPacketModified monitor
|
// Called from the ReadPacketModified monitor
|
||||||
PacketEvent packetRecieved(PacketContainer packet, DataInputStream input) {
|
PacketEvent packetRecieved(PacketContainer packet, DataInputStream input) {
|
||||||
|
|
||||||
Player client = playerInjection.getPlayerByConnection(input);
|
Player client = playerInjection.getPlayerByConnection(input);
|
||||||
|
|
||||||
|
// Never invoke a event if we don't know where it's from
|
||||||
|
if (client != null)
|
||||||
return packetRecieved(packet, client);
|
return packetRecieved(packet, client);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -102,10 +102,11 @@ class ReadPacketModifier implements MethodInterceptor {
|
|||||||
// Let the people know
|
// Let the people know
|
||||||
PacketContainer container = new PacketContainer(packetID, (Packet) thisObj);
|
PacketContainer container = new PacketContainer(packetID, (Packet) thisObj);
|
||||||
PacketEvent event = packetInjector.packetRecieved(container, input);
|
PacketEvent event = packetInjector.packetRecieved(container, input);
|
||||||
Packet result = event.getPacket().getHandle();
|
|
||||||
|
|
||||||
// Handle override
|
// Handle override
|
||||||
if (event != null) {
|
if (event != null) {
|
||||||
|
Packet result = event.getPacket().getHandle();
|
||||||
|
|
||||||
if (event.isCancelled()) {
|
if (event.isCancelled()) {
|
||||||
override.put(thisObj, null);
|
override.put(thisObj, null);
|
||||||
} else if (!objectEquals(thisObj, result)) {
|
} else if (!objectEquals(thisObj, result)) {
|
||||||
|
@ -4,11 +4,13 @@ import java.util.concurrent.ConcurrentMap;
|
|||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReadWriteLock;
|
import java.util.concurrent.locks.ReadWriteLock;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.bukkit.Server;
|
import org.bukkit.Server;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.GamePhase;
|
import com.comphenix.protocol.injector.GamePhase;
|
||||||
import com.comphenix.protocol.injector.player.TemporaryPlayerFactory.InjectContainer;
|
import com.comphenix.protocol.injector.player.TemporaryPlayerFactory.InjectContainer;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
@ -25,12 +27,16 @@ class NetLoginInjector {
|
|||||||
private PlayerInjectionHandler injectionHandler;
|
private PlayerInjectionHandler injectionHandler;
|
||||||
private Server server;
|
private Server server;
|
||||||
|
|
||||||
|
// The current logger
|
||||||
|
private Logger logger;
|
||||||
|
|
||||||
private ReadWriteLock injectionLock = new ReentrantReadWriteLock();
|
private ReadWriteLock injectionLock = new ReentrantReadWriteLock();
|
||||||
|
|
||||||
// Used to create fake players
|
// Used to create fake players
|
||||||
private TemporaryPlayerFactory tempPlayerFactory = new TemporaryPlayerFactory();
|
private TemporaryPlayerFactory tempPlayerFactory = new TemporaryPlayerFactory();
|
||||||
|
|
||||||
public NetLoginInjector(PlayerInjectionHandler injectionHandler, Server server) {
|
public NetLoginInjector(Logger logger, PlayerInjectionHandler injectionHandler, Server server) {
|
||||||
|
this.logger = logger;
|
||||||
this.injectionHandler = injectionHandler;
|
this.injectionHandler = injectionHandler;
|
||||||
this.server = server;
|
this.server = server;
|
||||||
}
|
}
|
||||||
@ -45,6 +51,10 @@ class NetLoginInjector {
|
|||||||
injectionLock.writeLock().lock();
|
injectionLock.writeLock().lock();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Make sure we actually need to inject during this phase
|
||||||
|
if (!injectionHandler.isInjectionNecessary(GamePhase.LOGIN))
|
||||||
|
return inserting;
|
||||||
|
|
||||||
Player fakePlayer = tempPlayerFactory.createTemporaryPlayer(server);
|
Player fakePlayer = tempPlayerFactory.createTemporaryPlayer(server);
|
||||||
PlayerInjector injector = injectionHandler.injectPlayer(fakePlayer, inserting, GamePhase.LOGIN);
|
PlayerInjector injector = injectionHandler.injectPlayer(fakePlayer, inserting, GamePhase.LOGIN);
|
||||||
injector.updateOnLogin = true;
|
injector.updateOnLogin = true;
|
||||||
@ -56,6 +66,11 @@ class NetLoginInjector {
|
|||||||
// NetServerInjector can never work (currently), so we don't need to replace the NetLoginHandler
|
// NetServerInjector can never work (currently), so we don't need to replace the NetLoginHandler
|
||||||
return inserting;
|
return inserting;
|
||||||
|
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// Minecraft can't handle this, so we'll deal with it here
|
||||||
|
logger.log(Level.WARNING, "Unable to hook NetLoginHandler.", e);
|
||||||
|
return inserting;
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
injectionLock.writeLock().unlock();
|
injectionLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
|
@ -31,9 +31,9 @@ import org.bukkit.entity.Player;
|
|||||||
import com.comphenix.protocol.Packets;
|
import com.comphenix.protocol.Packets;
|
||||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||||
import com.comphenix.protocol.events.PacketListener;
|
import com.comphenix.protocol.events.PacketListener;
|
||||||
|
import com.comphenix.protocol.injector.GamePhase;
|
||||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||||
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.GamePhase;
|
|
||||||
import com.comphenix.protocol.reflect.FieldUtils;
|
import com.comphenix.protocol.reflect.FieldUtils;
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
import com.comphenix.protocol.reflect.StructureModifier;
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
|
@ -36,9 +36,9 @@ import org.bukkit.entity.Player;
|
|||||||
import com.comphenix.protocol.Packets;
|
import com.comphenix.protocol.Packets;
|
||||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||||
import com.comphenix.protocol.events.PacketListener;
|
import com.comphenix.protocol.events.PacketListener;
|
||||||
|
import com.comphenix.protocol.injector.GamePhase;
|
||||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||||
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.GamePhase;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection method that overrides the NetworkHandler itself, and it's sendPacket-method.
|
* Injection method that overrides the NetworkHandler itself, and it's sendPacket-method.
|
||||||
|
@ -34,9 +34,9 @@ import net.sf.cglib.proxy.NoOp;
|
|||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import com.comphenix.protocol.events.PacketListener;
|
import com.comphenix.protocol.events.PacketListener;
|
||||||
|
import com.comphenix.protocol.injector.GamePhase;
|
||||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||||
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.GamePhase;
|
|
||||||
import com.comphenix.protocol.reflect.FieldUtils;
|
import com.comphenix.protocol.reflect.FieldUtils;
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
import com.comphenix.protocol.reflect.ObjectCloner;
|
import com.comphenix.protocol.reflect.ObjectCloner;
|
||||||
@ -264,7 +264,7 @@ public class NetworkServerInjector extends PlayerInjector {
|
|||||||
@Override
|
@Override
|
||||||
public boolean canInject(GamePhase phase) {
|
public boolean canInject(GamePhase phase) {
|
||||||
// Doesn't work when logging in
|
// Doesn't work when logging in
|
||||||
return phase == GamePhase.PLAYING || phase == GamePhase.CLOSING;
|
return phase == GamePhase.PLAYING;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -38,9 +38,11 @@ import org.bukkit.entity.Player;
|
|||||||
import com.comphenix.protocol.events.PacketAdapter;
|
import com.comphenix.protocol.events.PacketAdapter;
|
||||||
import com.comphenix.protocol.events.PacketContainer;
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
import com.comphenix.protocol.events.PacketListener;
|
import com.comphenix.protocol.events.PacketListener;
|
||||||
|
import com.comphenix.protocol.injector.GamePhase;
|
||||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||||
import com.comphenix.protocol.injector.PlayerLoggedOutException;
|
import com.comphenix.protocol.injector.PlayerLoggedOutException;
|
||||||
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
@ -51,16 +53,6 @@ import com.google.common.collect.Maps;
|
|||||||
*/
|
*/
|
||||||
public class PlayerInjectionHandler {
|
public class PlayerInjectionHandler {
|
||||||
|
|
||||||
/**
|
|
||||||
* The current player phase.
|
|
||||||
* @author Kristian
|
|
||||||
*/
|
|
||||||
enum GamePhase {
|
|
||||||
LOGIN,
|
|
||||||
PLAYING,
|
|
||||||
CLOSING,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Server connection injection
|
// Server connection injection
|
||||||
private InjectedServerConnection serverInjection;
|
private InjectedServerConnection serverInjection;
|
||||||
|
|
||||||
@ -93,11 +85,17 @@ public class PlayerInjectionHandler {
|
|||||||
// The class loader we're using
|
// The class loader we're using
|
||||||
private ClassLoader classLoader;
|
private ClassLoader classLoader;
|
||||||
|
|
||||||
public PlayerInjectionHandler(ClassLoader classLoader, Logger logger, ListenerInvoker invoker, Server server) {
|
// Used to filter injection attempts
|
||||||
|
private Predicate<GamePhase> injectionFilter;
|
||||||
|
|
||||||
|
public PlayerInjectionHandler(ClassLoader classLoader, Logger logger, Predicate<GamePhase> injectionFilter,
|
||||||
|
ListenerInvoker invoker, Server server) {
|
||||||
|
|
||||||
this.classLoader = classLoader;
|
this.classLoader = classLoader;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.invoker = invoker;
|
this.invoker = invoker;
|
||||||
this.netLoginInjector = new NetLoginInjector(this, server);
|
this.injectionFilter = injectionFilter;
|
||||||
|
this.netLoginInjector = new NetLoginInjector(logger, this, server);
|
||||||
this.serverInjection = new InjectedServerConnection(logger, server, netLoginInjector);
|
this.serverInjection = new InjectedServerConnection(logger, server, netLoginInjector);
|
||||||
serverInjection.injectList();
|
serverInjection.injectList();
|
||||||
}
|
}
|
||||||
@ -185,15 +183,31 @@ public class PlayerInjectionHandler {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize a player hook, allowing us to read server packets.
|
* Initialize a player hook, allowing us to read server packets.
|
||||||
|
* <p>
|
||||||
|
* This call will be ignored if there's no listener that can receive the given events.
|
||||||
* @param player - player to hook.
|
* @param player - player to hook.
|
||||||
*/
|
*/
|
||||||
public void injectPlayer(Player player) {
|
public void injectPlayer(Player player) {
|
||||||
// Inject using the player instance itself
|
// Inject using the player instance itself
|
||||||
|
if (isInjectionNecessary(GamePhase.PLAYING)) {
|
||||||
injectPlayer(player, player, GamePhase.PLAYING);
|
injectPlayer(player, player, GamePhase.PLAYING);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if it's truly necessary to perform the given player injection.
|
||||||
|
* @param phase - current game phase.
|
||||||
|
* @return TRUE if we should perform the injection, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isInjectionNecessary(GamePhase phase) {
|
||||||
|
return injectionFilter.apply(phase);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize a player hook, allowing us to read server packets.
|
* Initialize a player hook, allowing us to read server packets.
|
||||||
|
* <p>
|
||||||
|
* This method will always perform the instructed injection.
|
||||||
|
*
|
||||||
* @param player - player to hook.
|
* @param player - player to hook.
|
||||||
* @param injectionPoint - the object to use during the injection process.
|
* @param injectionPoint - the object to use during the injection process.
|
||||||
* @param phase - the current game phase.
|
* @param phase - the current game phase.
|
||||||
@ -205,6 +219,8 @@ public class PlayerInjectionHandler {
|
|||||||
PlayerInjectHooks tempHook = playerHook;
|
PlayerInjectHooks tempHook = playerHook;
|
||||||
PlayerInjectHooks permanentHook = tempHook;
|
PlayerInjectHooks permanentHook = tempHook;
|
||||||
|
|
||||||
|
// The given player object may be fake, so be careful!
|
||||||
|
|
||||||
// See if we need to inject something else
|
// See if we need to inject something else
|
||||||
boolean invalidInjector = injector != null ? !injector.canInject(phase) : true;
|
boolean invalidInjector = injector != null ? !injector.canInject(phase) : true;
|
||||||
|
|
||||||
|
@ -38,9 +38,9 @@ import com.comphenix.protocol.Packets;
|
|||||||
import com.comphenix.protocol.events.PacketContainer;
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
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.comphenix.protocol.injector.GamePhase;
|
||||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||||
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.GamePhase;
|
|
||||||
import com.comphenix.protocol.reflect.FieldUtils;
|
import com.comphenix.protocol.reflect.FieldUtils;
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
import com.comphenix.protocol.reflect.StructureModifier;
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
|
In neuem Issue referenzieren
Einen Benutzer sperren