Handle plugin reloading properly, uninjecting all player channels.
This should prevent any crashes if the plugin is reloaded during a player login.
Dieser Commit ist enthalten in:
Ursprung
5805150d8c
Commit
6bd8bd2ca2
@ -951,7 +951,6 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
||||
}
|
||||
|
||||
private void onPlayerLogin(PlayerLoginEvent event) {
|
||||
System.out.println("Address: " + event.getAddress());
|
||||
playerInjection.updatePlayer(event.getPlayer());
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,6 @@ import net.minecraft.util.io.netty.util.concurrent.GenericFutureListener;
|
||||
import net.minecraft.util.io.netty.util.internal.TypeParameterMatcher;
|
||||
import net.sf.cglib.proxy.Factory;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
@ -35,7 +34,6 @@ import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketOutputHandler;
|
||||
import com.comphenix.protocol.injector.server.SocketInjector;
|
||||
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.VolatileField;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
@ -46,19 +44,15 @@ import com.comphenix.protocol.utility.MinecraftMethods;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.MapMaker;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* Represents a channel injector.
|
||||
* @author Kristian
|
||||
*/
|
||||
class ChannelInjector extends ByteToMessageDecoder {
|
||||
class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
public static final ReportType REPORT_CANNOT_INTERCEPT_SERVER_PACKET = new ReportType("Unable to intercept a written server packet.");
|
||||
public static final ReportType REPORT_CANNOT_INTERCEPT_CLIENT_PACKET = new ReportType("Unable to intercept a read client packet.");
|
||||
|
||||
private static final ConcurrentMap<Player, ChannelInjector> PLAYER_LOOKUP = Maps.newConcurrentMap();
|
||||
private static final ConcurrentMap<String, ChannelInjector> NAME_LOOKUP = new MapMaker().weakValues().makeMap();
|
||||
|
||||
// The login packet
|
||||
private static Class<?> PACKET_LOGIN_CLIENT = null;
|
||||
private static FieldAccessor LOGIN_GAME_PROFILE = null;
|
||||
@ -71,6 +65,9 @@ class ChannelInjector extends ByteToMessageDecoder {
|
||||
// For retrieving the protocol
|
||||
private static FieldAccessor PROTOCOL_ACCESSOR;
|
||||
|
||||
// The factory that created this injector
|
||||
private InjectionFactory factory;
|
||||
|
||||
// The player, or temporary player
|
||||
private Player player;
|
||||
private Player updated;
|
||||
@ -112,117 +109,29 @@ class ChannelInjector extends ByteToMessageDecoder {
|
||||
* @param player - the current player, or temporary player.
|
||||
* @param networkManager - its network manager.
|
||||
* @param channel - its channel.
|
||||
* @param channelListener - a listener.
|
||||
* @param factory - the factory that created this injector
|
||||
*/
|
||||
private ChannelInjector(Player player, Object networkManager, Channel channel, ChannelListener channelListener) {
|
||||
public ChannelInjector(Player player, Object networkManager, Channel channel, ChannelListener channelListener, InjectionFactory factory) {
|
||||
this.player = Preconditions.checkNotNull(player, "player cannot be NULL");
|
||||
this.networkManager = Preconditions.checkNotNull(networkManager, "networkMananger cannot be NULL");
|
||||
this.originalChannel = Preconditions.checkNotNull(channel, "channel cannot be NULL");
|
||||
this.channelListener = Preconditions.checkNotNull(channelListener, "channelListener cannot be NULL");
|
||||
|
||||
this.factory = Preconditions.checkNotNull(factory, "factory cannot be NULL");
|
||||
|
||||
// Get the channel field
|
||||
this.channelField = new VolatileField(
|
||||
FuzzyReflection.fromObject(networkManager, true).
|
||||
getFieldByType("channel", Channel.class),
|
||||
networkManager, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct or retrieve a channel injector from an existing Bukkit player.
|
||||
* @param player - the existing Bukkit player.
|
||||
* @param channelListener - the listener.
|
||||
* @return A new injector, or an existing injector associated with this player.
|
||||
*/
|
||||
public static ChannelInjector fromPlayer(Player player, ChannelListener listener) {
|
||||
ChannelInjector injector = PLAYER_LOOKUP.get(player);
|
||||
|
||||
// Find a temporary injector as well
|
||||
if (injector == null)
|
||||
injector = getTemporaryInjector(player);
|
||||
if (injector != null)
|
||||
return injector;
|
||||
|
||||
Object networkManager = MinecraftFields.getNetworkManager(player);
|
||||
|
||||
// Must be a temporary Bukkit player
|
||||
if (networkManager == null)
|
||||
return fromName(player.getName(), player);
|
||||
|
||||
Channel channel = FuzzyReflection.getFieldValue(networkManager, Channel.class, true);
|
||||
|
||||
// See if a channel has already been created
|
||||
injector = (ChannelInjector) findChannelHandler(channel, ChannelInjector.class);
|
||||
|
||||
if (injector != null) {
|
||||
// Update the player instance
|
||||
injector.player = player;
|
||||
} else {
|
||||
injector = new ChannelInjector(player, networkManager, channel, listener);
|
||||
}
|
||||
// Cache injector and return
|
||||
NAME_LOOKUP.put(player.getName(), injector);
|
||||
PLAYER_LOOKUP.put(player, injector);
|
||||
return injector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the associated channel injector.
|
||||
* @param player - the temporary player, or normal Bukkit player.
|
||||
* @return The associated injector, or NULL if this is a Bukkit player.
|
||||
*/
|
||||
private static ChannelInjector getTemporaryInjector(Player player) {
|
||||
SocketInjector injector = TemporaryPlayerFactory.getInjectorFromPlayer(player);
|
||||
|
||||
if (injector != null) {
|
||||
return ((ChannelSocketInjector) injector).getChannelInjector();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a cached injector from a name.
|
||||
* @param address - the name.
|
||||
* @return The cached injector.
|
||||
* @throws IllegalArgumentException If we cannot find the corresponding injector.
|
||||
*/
|
||||
private static ChannelInjector fromName(String name, Player player) {
|
||||
ChannelInjector injector = NAME_LOOKUP.get(name);
|
||||
|
||||
// We can only retrieve cached injectors
|
||||
if (injector != null) {
|
||||
// Update instance
|
||||
injector.updated = player;
|
||||
return injector;
|
||||
}
|
||||
throw new IllegalArgumentException("Cannot inject temporary Bukkit player.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new channel injector for the given channel.
|
||||
* @param channel - the channel.
|
||||
* @param playerFactory - a temporary player creator.
|
||||
* @param channelListener - the listener.
|
||||
* @param loader - the current (plugin) class loader.
|
||||
* @return The channel injector.
|
||||
*/
|
||||
public static ChannelInjector fromChannel(Channel channel, ChannelListener listener, TemporaryPlayerFactory playerFactory) {
|
||||
Object networkManager = findNetworkManager(channel);
|
||||
Player temporaryPlayer = playerFactory.createTemporaryPlayer(Bukkit.getServer());
|
||||
ChannelInjector injector = new ChannelInjector(temporaryPlayer, networkManager, channel, listener);
|
||||
|
||||
// Initialize temporary player
|
||||
TemporaryPlayerFactory.setInjectorInPlayer(temporaryPlayer, new ChannelSocketInjector(injector));
|
||||
return injector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject the current channel.
|
||||
* <p>
|
||||
* Note that only active channels can be injected.
|
||||
* @return TRUE if we injected the channel, false if we could not inject or it was already injected.
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean inject() {
|
||||
synchronized (networkManager) {
|
||||
if (closed)
|
||||
return false;
|
||||
if (originalChannel instanceof Factory)
|
||||
return false;
|
||||
if (!originalChannel.isActive())
|
||||
@ -230,9 +139,6 @@ class ChannelInjector extends ByteToMessageDecoder {
|
||||
|
||||
// Don't inject the same channel twice
|
||||
if (findChannelHandler(originalChannel, ChannelInjector.class) != null) {
|
||||
// Invalidate cache
|
||||
if (player != null)
|
||||
PLAYER_LOOKUP.remove(player);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -303,30 +209,7 @@ class ChannelInjector extends ByteToMessageDecoder {
|
||||
}
|
||||
ENCODER_TYPE_MATCHER.set(encoder, TypeParameterMatcher.get(MinecraftReflection.getPacketClass()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the current injector.
|
||||
*/
|
||||
public void close() {
|
||||
if (!closed) {
|
||||
closed = true;
|
||||
|
||||
if (injected) {
|
||||
channelField.revertValue();
|
||||
|
||||
try {
|
||||
originalChannel.pipeline().remove(this);
|
||||
originalChannel.pipeline().remove(protocolEncoder);
|
||||
} catch (NoSuchElementException e) {
|
||||
// Ignore it - the player has logged out
|
||||
}
|
||||
// Clear cache
|
||||
NAME_LOOKUP.remove(player.getName());
|
||||
PLAYER_LOOKUP.remove(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Encode a packet to a byte buffer, taking over for the standard Minecraft encoder.
|
||||
* @param ctx - the current context.
|
||||
@ -441,7 +324,7 @@ class ChannelInjector extends ByteToMessageDecoder {
|
||||
GameProfile profile = (GameProfile) LOGIN_GAME_PROFILE.get(packet);
|
||||
|
||||
// Save the channel injector
|
||||
NAME_LOOKUP.put(profile.getName(), this);
|
||||
factory.cacheInjector(profile.getName(), this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -485,13 +368,8 @@ class ChannelInjector extends ByteToMessageDecoder {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a packet to a player's client.
|
||||
* @param packet - the packet to send.
|
||||
* @param marker - the network marker.
|
||||
* @param filtered - whether or not the packet is filtered.
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) {
|
||||
saveMarker(packet, marker);
|
||||
processedPackets.remove(packet);
|
||||
@ -522,12 +400,7 @@ class ChannelInjector extends ByteToMessageDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recieve a packet on the server.
|
||||
* @param packet - the (NMS) packet to send.
|
||||
* @param marker - the network marker.
|
||||
* @param filtered - whether or not the packet is filtered.
|
||||
*/
|
||||
@Override
|
||||
public void recieveClientPacket(Object packet, NetworkMarker marker, boolean filtered) {
|
||||
saveMarker(packet, marker);
|
||||
processedPackets.remove(packet);
|
||||
@ -545,10 +418,7 @@ class ChannelInjector extends ByteToMessageDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current protocol state.
|
||||
* @return The current protocol.
|
||||
*/
|
||||
@Override
|
||||
public Protocol getCurrentProtocol() {
|
||||
if (PROTOCOL_ACCESSOR == null) {
|
||||
PROTOCOL_ACCESSOR = Accessors.getFieldAccessor(
|
||||
@ -568,96 +438,57 @@ class ChannelInjector extends ByteToMessageDecoder {
|
||||
return playerConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo the ignore status of a packet.
|
||||
* @param packet - the packet.
|
||||
* @return TRUE if the ignore status was undone, FALSE otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean unignorePacket(Object packet) {
|
||||
return ignoredPackets.remove(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore the given packet.
|
||||
* @param packet - the packet to ignore.
|
||||
* @return TRUE if it was ignored, FALSE if it already is ignored.
|
||||
*/
|
||||
@Override
|
||||
public boolean ignorePacket(Object packet) {
|
||||
return ignoredPackets.add(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the network marker associated with a given packet.
|
||||
* @param packet - the packet.
|
||||
* @return The network marker.
|
||||
*/
|
||||
@Override
|
||||
public NetworkMarker getMarker(Object packet) {
|
||||
return packetMarker.get(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a given network marker with a specific packet.
|
||||
* @param packet - the NMS packet.
|
||||
* @param marker - the associated marker.
|
||||
*/
|
||||
@Override
|
||||
public void saveMarker(Object packet, NetworkMarker marker) {
|
||||
if (marker != null) {
|
||||
packetMarker.put(packet, marker);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a given network marker with a packet event.
|
||||
* @param marker - the marker.
|
||||
* @param event - the packet event
|
||||
*/
|
||||
@Override
|
||||
public void saveEvent(NetworkMarker marker, PacketEvent event) {
|
||||
if (marker != null) {
|
||||
markerEvent.put(marker, event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the network manager in a channel's pipeline.
|
||||
* @param channel - the channel.
|
||||
* @return The network manager.
|
||||
*/
|
||||
private static Object findNetworkManager(Channel channel) {
|
||||
// Find the network manager
|
||||
Object networkManager = findChannelHandler(channel, MinecraftReflection.getNetworkManagerClass());
|
||||
|
||||
if (networkManager != null)
|
||||
return networkManager;
|
||||
throw new IllegalArgumentException("Unable to find NetworkManager in " + channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the first channel handler that is assignable to a given type.
|
||||
* @param channel - the channel.
|
||||
* @param clazz - the type.
|
||||
* @return The first handler, or NULL.
|
||||
*/
|
||||
private static ChannelHandler findChannelHandler(Channel channel, Class<?> clazz) {
|
||||
for (Entry<String, ChannelHandler> entry : channel.pipeline()) {
|
||||
if (clazz.isAssignableFrom(entry.getValue().getClass())) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current player or temporary player associated with the injector.
|
||||
* @return The current player.
|
||||
*/
|
||||
|
||||
@Override
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the channel has already been injected.
|
||||
* @return TRUE if it has, FALSE otherwise.
|
||||
* Set the player instance.
|
||||
* @param player - current instance.
|
||||
*/
|
||||
public void setPlayer(Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the updated player instance.
|
||||
* @param updated - updated instance.
|
||||
*/
|
||||
public void setUpdatedPlayer(Player updated) {
|
||||
this.updated = updated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInjected() {
|
||||
return injected;
|
||||
}
|
||||
@ -666,10 +497,46 @@ class ChannelInjector extends ByteToMessageDecoder {
|
||||
* Determine if this channel has been closed and cleaned up.
|
||||
* @return TRUE if it has, FALSE otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (!closed) {
|
||||
closed = true;
|
||||
|
||||
if (injected) {
|
||||
channelField.revertValue();
|
||||
|
||||
try {
|
||||
originalChannel.pipeline().remove(this);
|
||||
originalChannel.pipeline().remove(protocolEncoder);
|
||||
} catch (NoSuchElementException e) {
|
||||
// Ignore it - the player has logged out
|
||||
}
|
||||
// Clear cache
|
||||
factory.invalidate(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the first channel handler that is assignable to a given type.
|
||||
* @param channel - the channel.
|
||||
* @param clazz - the type.
|
||||
* @return The first handler, or NULL.
|
||||
*/
|
||||
public static ChannelHandler findChannelHandler(Channel channel, Class<?> clazz) {
|
||||
for (Entry<String, ChannelHandler> entry : channel.pipeline()) {
|
||||
if (clazz.isAssignableFrom(entry.getValue().getClass())) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a socket injector that foreards to the current channel injector.
|
||||
* @author Kristian
|
||||
|
@ -3,6 +3,7 @@ package com.comphenix.protocol.injector.netty;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a listener for received or sent packets.
|
||||
* @author Kristian
|
||||
@ -17,7 +18,7 @@ interface ChannelListener {
|
||||
* @param marker - the associated network marker, if any.
|
||||
* @return The new packet, if it should be changed, or NULL to cancel.
|
||||
*/
|
||||
public Object onPacketSending(ChannelInjector injector, Object packet, NetworkMarker marker);
|
||||
public Object onPacketSending(Injector injector, Object packet, NetworkMarker marker);
|
||||
|
||||
/**
|
||||
* Invoked when a packet is being received from a client.
|
||||
@ -28,7 +29,7 @@ interface ChannelListener {
|
||||
* @param marker - the associated network marker, if any.
|
||||
* @return The new packet, if it should be changed, or NULL to cancel.
|
||||
*/
|
||||
public Object onPacketReceiving(ChannelInjector injector, Object packet, NetworkMarker marker);
|
||||
public Object onPacketReceiving(Injector injector, Object packet, NetworkMarker marker);
|
||||
|
||||
/**
|
||||
* Determine if there is a packet listener for the given packet.
|
||||
|
@ -0,0 +1,99 @@
|
||||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a closed injector.
|
||||
* @author Kristian
|
||||
*/
|
||||
class ClosedInjector implements Injector {
|
||||
private Player player;
|
||||
|
||||
/**
|
||||
* Construct a new injector that is always closed.
|
||||
* @param player - the associated player.
|
||||
*/
|
||||
public ClosedInjector(Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inject() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recieveClientPacket(Object packet, NetworkMarker marker, boolean filtered) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public Protocol getCurrentProtocol() {
|
||||
return Protocol.HANDSHAKING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unignorePacket(Object packet) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean ignorePacket(Object packet) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NetworkMarker getMarker(Object packet) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveMarker(Object packet, NetworkMarker marker) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveEvent(NetworkMarker marker, PacketEvent event) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUpdatedPlayer(Player player) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayer(Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInjected() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,202 @@
|
||||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import net.minecraft.util.io.netty.channel.Channel;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.injector.netty.ChannelInjector.ChannelSocketInjector;
|
||||
import com.comphenix.protocol.injector.server.SocketInjector;
|
||||
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftFields;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.collect.MapMaker;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* Represents an injector factory.
|
||||
* <p>
|
||||
* Note that the factory will return {@link ClosedInjector} when the factory is closed.
|
||||
* @author Kristian
|
||||
*/
|
||||
class InjectionFactory {
|
||||
private final ConcurrentMap<Player, Injector> playerLookup = Maps.newConcurrentMap();
|
||||
private final ConcurrentMap<String, Injector> nameLookup = new MapMaker().weakValues().makeMap();
|
||||
|
||||
// Whether or not the factory is closed
|
||||
private boolean closed;
|
||||
|
||||
/**
|
||||
* Construct or retrieve a channel injector from an existing Bukkit player.
|
||||
* @param player - the existing Bukkit player.
|
||||
* @param channelListener - the listener.
|
||||
* @return A new injector, an existing injector associated with this player, or a closed injector.
|
||||
*/
|
||||
@Nonnull
|
||||
public Injector fromPlayer(Player player, ChannelListener listener) {
|
||||
if (closed)
|
||||
return new ClosedInjector(player);
|
||||
Injector injector = playerLookup.get(player);
|
||||
|
||||
// Find a temporary injector as well
|
||||
if (injector == null)
|
||||
injector = getTemporaryInjector(player);
|
||||
if (injector != null)
|
||||
return injector;
|
||||
|
||||
Object networkManager = MinecraftFields.getNetworkManager(player);
|
||||
|
||||
// Must be a temporary Bukkit player
|
||||
if (networkManager == null) {
|
||||
return fromName(player.getName(), player);
|
||||
}
|
||||
Channel channel = FuzzyReflection.getFieldValue(networkManager, Channel.class, true);
|
||||
|
||||
// See if a channel has already been created
|
||||
injector = (ChannelInjector) ChannelInjector.findChannelHandler(channel, ChannelInjector.class);
|
||||
|
||||
if (injector != null) {
|
||||
// Update the player instance
|
||||
playerLookup.remove(injector.getPlayer());
|
||||
injector.setPlayer(player);
|
||||
} else {
|
||||
injector = new ChannelInjector(player, networkManager, channel, listener, this);
|
||||
}
|
||||
|
||||
// Cache injector and return
|
||||
cacheInjector(player, injector);
|
||||
return injector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a cached injector from a name.
|
||||
* <p>
|
||||
* The injector may be NULL if the plugin has been reloaded during a player login.
|
||||
* @param address - the name.
|
||||
* @return The cached injector, or a closed injector if it could not be found.
|
||||
*/
|
||||
public Injector fromName(String name, Player player) {
|
||||
if (closed)
|
||||
return new ClosedInjector(player);
|
||||
Injector injector = nameLookup.get(name);
|
||||
|
||||
// We can only retrieve cached injectors
|
||||
if (injector != null) {
|
||||
// Update instance
|
||||
injector.setUpdatedPlayer(player);
|
||||
return injector;
|
||||
}
|
||||
return new ClosedInjector(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new channel injector for the given channel.
|
||||
* @param channel - the channel.
|
||||
* @param playerFactory - a temporary player creator.
|
||||
* @param channelListener - the listener.
|
||||
* @param loader - the current (plugin) class loader.
|
||||
* @return The channel injector, or a closed injector.
|
||||
*/
|
||||
@Nonnull
|
||||
public Injector fromChannel(Channel channel, ChannelListener listener, TemporaryPlayerFactory playerFactory) {
|
||||
if (closed)
|
||||
return new ClosedInjector(null);
|
||||
|
||||
Object networkManager = findNetworkManager(channel);
|
||||
Player temporaryPlayer = playerFactory.createTemporaryPlayer(Bukkit.getServer());
|
||||
ChannelInjector injector = new ChannelInjector(temporaryPlayer, networkManager, channel, listener, this);
|
||||
|
||||
// Initialize temporary player
|
||||
TemporaryPlayerFactory.setInjectorInPlayer(temporaryPlayer, new ChannelSocketInjector(injector));
|
||||
return injector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate a cached injector.
|
||||
* @param player - the associated player.
|
||||
* @return The cached injector, or NULL if nothing was cached.
|
||||
*/
|
||||
public Injector invalidate(Player player) {
|
||||
Injector injector = playerLookup.remove(player);
|
||||
|
||||
nameLookup.remove(player.getName());
|
||||
return injector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache an injector by player.
|
||||
* @param player - the player.
|
||||
* @param injector - the injector to cache.
|
||||
* @return The previously cached injector.
|
||||
*/
|
||||
public Injector cacheInjector(Player player, Injector injector) {
|
||||
nameLookup.put(player.getName(), injector);
|
||||
return playerLookup.put(player, injector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache an injector by name alone.
|
||||
* @param name - the name to lookup.
|
||||
* @param injector - the injector.
|
||||
* @return The cached injector.
|
||||
*/
|
||||
public Injector cacheInjector(String name, Injector injector) {
|
||||
return nameLookup.put(name, injector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the associated channel injector.
|
||||
* @param player - the temporary player, or normal Bukkit player.
|
||||
* @return The associated injector, or NULL if this is a Bukkit player.
|
||||
*/
|
||||
private ChannelInjector getTemporaryInjector(Player player) {
|
||||
SocketInjector injector = TemporaryPlayerFactory.getInjectorFromPlayer(player);
|
||||
|
||||
if (injector != null) {
|
||||
return ((ChannelSocketInjector) injector).getChannelInjector();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the network manager in a channel's pipeline.
|
||||
* @param channel - the channel.
|
||||
* @return The network manager.
|
||||
*/
|
||||
private Object findNetworkManager(Channel channel) {
|
||||
// Find the network manager
|
||||
Object networkManager = ChannelInjector.findChannelHandler(channel, MinecraftReflection.getNetworkManagerClass());
|
||||
|
||||
if (networkManager != null)
|
||||
return networkManager;
|
||||
throw new IllegalArgumentException("Unable to find NetworkManager in " + channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the factory is closed.
|
||||
* <p>
|
||||
* If it is, all new injectors will be closed by default.
|
||||
* @return TRUE if it is closed, FALSE otherwise.
|
||||
*/
|
||||
public boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all injectors created by this factory, and cease the creation of new injections.
|
||||
*/
|
||||
public synchronized void close() {
|
||||
if (!closed) {
|
||||
closed = true;
|
||||
|
||||
// Close everything
|
||||
for (Injector injector : playerLookup.values())
|
||||
injector.close();
|
||||
for (Injector injector : nameLookup.values())
|
||||
injector.close();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
|
||||
/**
|
||||
* Represents an injected client connection.
|
||||
* @author Kristian
|
||||
*/
|
||||
interface Injector {
|
||||
/**
|
||||
* Inject the current channel.
|
||||
* <p>
|
||||
* Note that only active channels can be injected.
|
||||
* @return TRUE if we injected the channel, false if we could not inject or it was already injected.
|
||||
*/
|
||||
public abstract boolean inject();
|
||||
|
||||
/**
|
||||
* Close the current injector.
|
||||
*/
|
||||
public abstract void close();
|
||||
|
||||
/**
|
||||
* Send a packet to a player's client.
|
||||
* @param packet - the packet to send.
|
||||
* @param marker - the network marker.
|
||||
* @param filtered - whether or not the packet is filtered.
|
||||
*/
|
||||
public abstract void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered);
|
||||
|
||||
/**
|
||||
* Recieve a packet on the server.
|
||||
* @param packet - the (NMS) packet to send.
|
||||
* @param marker - the network marker.
|
||||
* @param filtered - whether or not the packet is filtered.
|
||||
*/
|
||||
public abstract void recieveClientPacket(Object packet, NetworkMarker marker, boolean filtered);
|
||||
|
||||
/**
|
||||
* Retrieve the current protocol state.
|
||||
* @return The current protocol.
|
||||
*/
|
||||
public abstract Protocol getCurrentProtocol();
|
||||
|
||||
/**
|
||||
* Undo the ignore status of a packet.
|
||||
* @param packet - the packet.
|
||||
* @return TRUE if the ignore status was undone, FALSE otherwise.
|
||||
*/
|
||||
public abstract boolean unignorePacket(Object packet);
|
||||
|
||||
/**
|
||||
* Ignore the given packet.
|
||||
* @param packet - the packet to ignore.
|
||||
* @return TRUE if it was ignored, FALSE if it already is ignored.
|
||||
*/
|
||||
public abstract boolean ignorePacket(Object packet);
|
||||
|
||||
/**
|
||||
* Retrieve the network marker associated with a given packet.
|
||||
* @param packet - the packet.
|
||||
* @return The network marker.
|
||||
*/
|
||||
public abstract NetworkMarker getMarker(Object packet);
|
||||
|
||||
/**
|
||||
* Associate a given network marker with a specific packet.
|
||||
* @param packet - the NMS packet.
|
||||
* @param marker - the associated marker.
|
||||
*/
|
||||
public abstract void saveMarker(Object packet, NetworkMarker marker);
|
||||
|
||||
/**
|
||||
* Associate a given network marker with a packet event.
|
||||
* @param marker - the marker.
|
||||
* @param event - the packet event
|
||||
*/
|
||||
public abstract void saveEvent(NetworkMarker marker, PacketEvent event);
|
||||
|
||||
/**
|
||||
* Retrieve the current player or temporary player associated with the injector.
|
||||
* @return The current player.
|
||||
*/
|
||||
public abstract Player getPlayer();
|
||||
|
||||
/**
|
||||
* Set the current player instance.
|
||||
* @param player - the current player.
|
||||
*/
|
||||
public abstract void setPlayer(Player player);
|
||||
|
||||
/**
|
||||
* Determine if the channel has already been injected.
|
||||
* @return TRUE if it has, FALSE otherwise.
|
||||
*/
|
||||
public abstract boolean isInjected();
|
||||
|
||||
/**
|
||||
* Determine if this channel has been closed and cleaned up.
|
||||
* @return TRUE if it has, FALSE otherwise.
|
||||
*/
|
||||
public abstract boolean isClosed();
|
||||
|
||||
/**
|
||||
* Set the updated player instance.
|
||||
* <p>
|
||||
* This will not replace the current instance, but it will allow PacketEvent to provide additional player information.
|
||||
* @param player - the more up-to-date player.
|
||||
*/
|
||||
public abstract void setUpdatedPlayer(Player player);
|
||||
}
|
@ -7,7 +7,6 @@ import java.lang.reflect.Method;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import net.minecraft.util.io.netty.channel.Channel;
|
||||
@ -23,11 +22,7 @@ import com.comphenix.protocol.concurrency.PacketTypeSet;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.events.ConnectionSide;
|
||||
import com.comphenix.protocol.events.ListenerOptions;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.*;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.packet.PacketInjector;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
@ -40,6 +35,7 @@ import com.comphenix.protocol.reflect.VolatileField;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
|
||||
public class NettyProtocolInjector implements ChannelListener {
|
||||
public static final ReportType REPORT_CANNOT_INJECT_INCOMING_CHANNEL = new ReportType("Unable to to inject incoming channel %s.");
|
||||
|
||||
@ -50,6 +46,9 @@ public class NettyProtocolInjector implements ChannelListener {
|
||||
private TemporaryPlayerFactory playerFactory = new TemporaryPlayerFactory();
|
||||
private List<VolatileField> bootstrapFields = Lists.newArrayList();
|
||||
|
||||
// The channel injector factory
|
||||
private InjectionFactory injectionFactory = new InjectionFactory();
|
||||
|
||||
// List of network managers
|
||||
private volatile List<Object> networkManagers;
|
||||
|
||||
@ -92,15 +91,9 @@ public class NettyProtocolInjector implements ChannelListener {
|
||||
@Override
|
||||
protected void initChannel(Channel channel) throws Exception {
|
||||
try {
|
||||
// Check and see if the injector has closed
|
||||
synchronized (this) {
|
||||
if (closed)
|
||||
return;
|
||||
}
|
||||
|
||||
// This can take a while, so we need to stop the main thread from interfering
|
||||
synchronized (networkManagers) {
|
||||
ChannelInjector.fromChannel(channel, NettyProtocolInjector.this, playerFactory).inject();
|
||||
injectionFactory.fromChannel(channel, NettyProtocolInjector.this, playerFactory).inject();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
reporter.reportDetailed(this, Report.newBuilder(REPORT_CANNOT_INJECT_INCOMING_CHANNEL).
|
||||
@ -167,7 +160,7 @@ public class NettyProtocolInjector implements ChannelListener {
|
||||
* @param player
|
||||
*/
|
||||
public void injectPlayer(Player player) {
|
||||
ChannelInjector.fromPlayer(player, this).inject();
|
||||
injectionFactory.fromPlayer(player, this).inject();
|
||||
}
|
||||
|
||||
private List<VolatileField> getBootstrapFields(Object serverConnection) {
|
||||
@ -203,16 +196,13 @@ public class NettyProtocolInjector implements ChannelListener {
|
||||
}
|
||||
field.revertValue();
|
||||
}
|
||||
|
||||
// Uninject all the players
|
||||
for (Player player : Bukkit.getServer().getOnlinePlayers()) {
|
||||
ChannelInjector.fromPlayer(player, this).close();
|
||||
}
|
||||
injectionFactory.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onPacketSending(ChannelInjector injector, Object packet, NetworkMarker marker) {
|
||||
public Object onPacketSending(Injector injector, Object packet, NetworkMarker marker) {
|
||||
Class<?> clazz = packet.getClass();
|
||||
|
||||
if (sendingFilters.contains(clazz)) {
|
||||
@ -235,7 +225,7 @@ public class NettyProtocolInjector implements ChannelListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onPacketReceiving(ChannelInjector injector, Object packet, NetworkMarker marker) {
|
||||
public Object onPacketReceiving(Injector injector, Object packet, NetworkMarker marker) {
|
||||
Class<?> clazz = packet.getClass();
|
||||
|
||||
if (reveivedFilters.contains(clazz)) {
|
||||
@ -295,12 +285,12 @@ public class NettyProtocolInjector implements ChannelListener {
|
||||
|
||||
@Override
|
||||
public void updatePlayer(Player player) {
|
||||
ChannelInjector.fromPlayer(player, listener).inject();
|
||||
injectionFactory.fromPlayer(player, listener).inject();
|
||||
|
||||
}
|
||||
@Override
|
||||
public void injectPlayer(Player player, ConflictStrategy strategy) {
|
||||
ChannelInjector.fromPlayer(player, listener).inject();
|
||||
injectionFactory.fromPlayer(player, listener).inject();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -324,19 +314,19 @@ public class NettyProtocolInjector implements ChannelListener {
|
||||
|
||||
@Override
|
||||
public boolean uninjectPlayer(Player player) {
|
||||
ChannelInjector.fromPlayer(player, listener).close();
|
||||
injectionFactory.fromPlayer(player, listener).close();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Player receiver, PacketContainer packet, NetworkMarker marker, boolean filters) throws InvocationTargetException {
|
||||
ChannelInjector.fromPlayer(receiver, listener).
|
||||
injectionFactory.fromPlayer(receiver, listener).
|
||||
sendServerPacket(packet.getHandle(), marker, filters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recieveClientPacket(Player player, Object mcPacket) throws IllegalAccessException, InvocationTargetException {
|
||||
ChannelInjector.fromPlayer(player, listener).
|
||||
injectionFactory.fromPlayer(player, listener).
|
||||
recieveClientPacket(mcPacket, null, true);
|
||||
}
|
||||
|
||||
@ -348,7 +338,7 @@ public class NettyProtocolInjector implements ChannelListener {
|
||||
|
||||
@Override
|
||||
public void handleDisconnect(Player player) {
|
||||
ChannelInjector.fromPlayer(player, listener).close();
|
||||
injectionFactory.fromPlayer(player, listener).close();
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -363,7 +353,7 @@ public class NettyProtocolInjector implements ChannelListener {
|
||||
@Override
|
||||
public PacketEvent packetRecieved(PacketContainer packet, Player client, byte[] buffered) {
|
||||
NetworkMarker marker = buffered != null ? new NettyNetworkMarker(ConnectionSide.CLIENT_SIDE, buffered) : null;
|
||||
ChannelInjector.fromPlayer(client, NettyProtocolInjector.this).
|
||||
injectionFactory.fromPlayer(client, NettyProtocolInjector.this).
|
||||
saveMarker(packet.getHandle(), marker);
|
||||
return packetReceived(packet, client, marker);
|
||||
}
|
||||
|
In neuem Issue referenzieren
Einen Benutzer sperren