Archiviert
13
0

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:
Kristian S. Stangeland 2013-12-17 16:45:01 +01:00
Ursprung 5805150d8c
Commit 6bd8bd2ca2
7 geänderte Dateien mit 512 neuen und 239 gelöschten Zeilen

Datei anzeigen

@ -951,7 +951,6 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
} }
private void onPlayerLogin(PlayerLoginEvent event) { private void onPlayerLogin(PlayerLoginEvent event) {
System.out.println("Address: " + event.getAddress());
playerInjection.updatePlayer(event.getPlayer()); playerInjection.updatePlayer(event.getPlayer());
} }

Datei anzeigen

@ -22,7 +22,6 @@ import net.minecraft.util.io.netty.util.concurrent.GenericFutureListener;
import net.minecraft.util.io.netty.util.internal.TypeParameterMatcher; import net.minecraft.util.io.netty.util.internal.TypeParameterMatcher;
import net.sf.cglib.proxy.Factory; import net.sf.cglib.proxy.Factory;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType; 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.PacketEvent;
import com.comphenix.protocol.events.PacketOutputHandler; import com.comphenix.protocol.events.PacketOutputHandler;
import com.comphenix.protocol.injector.server.SocketInjector; 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.FuzzyReflection;
import com.comphenix.protocol.reflect.VolatileField; import com.comphenix.protocol.reflect.VolatileField;
import com.comphenix.protocol.reflect.accessors.Accessors; 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.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.MapMaker; import com.google.common.collect.MapMaker;
import com.google.common.collect.Maps;
/** /**
* Represents a channel injector. * Represents a channel injector.
* @author Kristian * @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_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."); 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 // The login packet
private static Class<?> PACKET_LOGIN_CLIENT = null; private static Class<?> PACKET_LOGIN_CLIENT = null;
private static FieldAccessor LOGIN_GAME_PROFILE = null; private static FieldAccessor LOGIN_GAME_PROFILE = null;
@ -71,6 +65,9 @@ class ChannelInjector extends ByteToMessageDecoder {
// For retrieving the protocol // For retrieving the protocol
private static FieldAccessor PROTOCOL_ACCESSOR; private static FieldAccessor PROTOCOL_ACCESSOR;
// The factory that created this injector
private InjectionFactory factory;
// The player, or temporary player // The player, or temporary player
private Player player; private Player player;
private Player updated; private Player updated;
@ -112,12 +109,15 @@ class ChannelInjector extends ByteToMessageDecoder {
* @param player - the current player, or temporary player. * @param player - the current player, or temporary player.
* @param networkManager - its network manager. * @param networkManager - its network manager.
* @param channel - its channel. * @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.player = Preconditions.checkNotNull(player, "player cannot be NULL");
this.networkManager = Preconditions.checkNotNull(networkManager, "networkMananger cannot be NULL"); this.networkManager = Preconditions.checkNotNull(networkManager, "networkMananger cannot be NULL");
this.originalChannel = Preconditions.checkNotNull(channel, "channel cannot be NULL"); this.originalChannel = Preconditions.checkNotNull(channel, "channel cannot be NULL");
this.channelListener = Preconditions.checkNotNull(channelListener, "channelListener 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 // Get the channel field
this.channelField = new VolatileField( this.channelField = new VolatileField(
@ -126,103 +126,12 @@ class ChannelInjector extends ByteToMessageDecoder {
networkManager, true); networkManager, true);
} }
/** @Override
* 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.
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public boolean inject() { public boolean inject() {
synchronized (networkManager) { synchronized (networkManager) {
if (closed)
return false;
if (originalChannel instanceof Factory) if (originalChannel instanceof Factory)
return false; return false;
if (!originalChannel.isActive()) if (!originalChannel.isActive())
@ -230,9 +139,6 @@ class ChannelInjector extends ByteToMessageDecoder {
// Don't inject the same channel twice // Don't inject the same channel twice
if (findChannelHandler(originalChannel, ChannelInjector.class) != null) { if (findChannelHandler(originalChannel, ChannelInjector.class) != null) {
// Invalidate cache
if (player != null)
PLAYER_LOOKUP.remove(player);
return false; return false;
} }
@ -304,29 +210,6 @@ class ChannelInjector extends ByteToMessageDecoder {
ENCODER_TYPE_MATCHER.set(encoder, TypeParameterMatcher.get(MinecraftReflection.getPacketClass())); 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. * Encode a packet to a byte buffer, taking over for the standard Minecraft encoder.
* @param ctx - the current context. * @param ctx - the current context.
@ -441,7 +324,7 @@ class ChannelInjector extends ByteToMessageDecoder {
GameProfile profile = (GameProfile) LOGIN_GAME_PROFILE.get(packet); GameProfile profile = (GameProfile) LOGIN_GAME_PROFILE.get(packet);
// Save the channel injector // Save the channel injector
NAME_LOOKUP.put(profile.getName(), this); factory.cacheInjector(profile.getName(), this);
} }
} }
@ -486,12 +369,7 @@ class ChannelInjector extends ByteToMessageDecoder {
} }
} }
/** @Override
* 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 void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) { public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) {
saveMarker(packet, marker); saveMarker(packet, marker);
processedPackets.remove(packet); processedPackets.remove(packet);
@ -522,12 +400,7 @@ class ChannelInjector extends ByteToMessageDecoder {
} }
} }
/** @Override
* 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 void recieveClientPacket(Object packet, NetworkMarker marker, boolean filtered) { public void recieveClientPacket(Object packet, NetworkMarker marker, boolean filtered) {
saveMarker(packet, marker); saveMarker(packet, marker);
processedPackets.remove(packet); processedPackets.remove(packet);
@ -545,10 +418,7 @@ class ChannelInjector extends ByteToMessageDecoder {
} }
} }
/** @Override
* Retrieve the current protocol state.
* @return The current protocol.
*/
public Protocol getCurrentProtocol() { public Protocol getCurrentProtocol() {
if (PROTOCOL_ACCESSOR == null) { if (PROTOCOL_ACCESSOR == null) {
PROTOCOL_ACCESSOR = Accessors.getFieldAccessor( PROTOCOL_ACCESSOR = Accessors.getFieldAccessor(
@ -568,96 +438,57 @@ class ChannelInjector extends ByteToMessageDecoder {
return playerConnection; return playerConnection;
} }
/** @Override
* Undo the ignore status of a packet.
* @param packet - the packet.
* @return TRUE if the ignore status was undone, FALSE otherwise.
*/
public boolean unignorePacket(Object packet) { public boolean unignorePacket(Object packet) {
return ignoredPackets.remove(packet); return ignoredPackets.remove(packet);
} }
/** @Override
* Ignore the given packet.
* @param packet - the packet to ignore.
* @return TRUE if it was ignored, FALSE if it already is ignored.
*/
public boolean ignorePacket(Object packet) { public boolean ignorePacket(Object packet) {
return ignoredPackets.add(packet); return ignoredPackets.add(packet);
} }
/** @Override
* Retrieve the network marker associated with a given packet.
* @param packet - the packet.
* @return The network marker.
*/
public NetworkMarker getMarker(Object packet) { public NetworkMarker getMarker(Object packet) {
return packetMarker.get(packet); return packetMarker.get(packet);
} }
/** @Override
* Associate a given network marker with a specific packet.
* @param packet - the NMS packet.
* @param marker - the associated marker.
*/
public void saveMarker(Object packet, NetworkMarker marker) { public void saveMarker(Object packet, NetworkMarker marker) {
if (marker != null) { if (marker != null) {
packetMarker.put(packet, marker); packetMarker.put(packet, marker);
} }
} }
/** @Override
* Associate a given network marker with a packet event.
* @param marker - the marker.
* @param event - the packet event
*/
public void saveEvent(NetworkMarker marker, PacketEvent event) { public void saveEvent(NetworkMarker marker, PacketEvent event) {
if (marker != null) { if (marker != null) {
markerEvent.put(marker, event); markerEvent.put(marker, event);
} }
} }
/** @Override
* 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.
*/
public Player getPlayer() { public Player getPlayer() {
return player; return player;
} }
/** /**
* Determine if the channel has already been injected. * Set the player instance.
* @return TRUE if it has, FALSE otherwise. * @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() { public boolean isInjected() {
return injected; return injected;
} }
@ -666,10 +497,46 @@ class ChannelInjector extends ByteToMessageDecoder {
* Determine if this channel has been closed and cleaned up. * Determine if this channel has been closed and cleaned up.
* @return TRUE if it has, FALSE otherwise. * @return TRUE if it has, FALSE otherwise.
*/ */
@Override
public boolean isClosed() { public boolean isClosed() {
return closed; 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. * Represents a socket injector that foreards to the current channel injector.
* @author Kristian * @author Kristian

Datei anzeigen

@ -3,6 +3,7 @@ package com.comphenix.protocol.injector.netty;
import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.events.NetworkMarker; import com.comphenix.protocol.events.NetworkMarker;
/** /**
* Represents a listener for received or sent packets. * Represents a listener for received or sent packets.
* @author Kristian * @author Kristian
@ -17,7 +18,7 @@ interface ChannelListener {
* @param marker - the associated network marker, if any. * @param marker - the associated network marker, if any.
* @return The new packet, if it should be changed, or NULL to cancel. * @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. * Invoked when a packet is being received from a client.
@ -28,7 +29,7 @@ interface ChannelListener {
* @param marker - the associated network marker, if any. * @param marker - the associated network marker, if any.
* @return The new packet, if it should be changed, or NULL to cancel. * @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. * Determine if there is a packet listener for the given packet.

Datei anzeigen

@ -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;
}
}

Datei anzeigen

@ -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();
}
}
}

Datei anzeigen

@ -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);
}

Datei anzeigen

@ -7,7 +7,6 @@ import java.lang.reflect.Method;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import net.minecraft.util.io.netty.channel.Channel; 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.ErrorReporter;
import com.comphenix.protocol.error.Report; import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType; import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.ConnectionSide; import com.comphenix.protocol.events.*;
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.injector.ListenerInvoker; import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.packet.PacketInjector; import com.comphenix.protocol.injector.packet.PacketInjector;
import com.comphenix.protocol.injector.packet.PacketRegistry; 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.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
public class NettyProtocolInjector implements ChannelListener { public class NettyProtocolInjector implements ChannelListener {
public static final ReportType REPORT_CANNOT_INJECT_INCOMING_CHANNEL = new ReportType("Unable to to inject incoming channel %s."); 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 TemporaryPlayerFactory playerFactory = new TemporaryPlayerFactory();
private List<VolatileField> bootstrapFields = Lists.newArrayList(); private List<VolatileField> bootstrapFields = Lists.newArrayList();
// The channel injector factory
private InjectionFactory injectionFactory = new InjectionFactory();
// List of network managers // List of network managers
private volatile List<Object> networkManagers; private volatile List<Object> networkManagers;
@ -92,15 +91,9 @@ public class NettyProtocolInjector implements ChannelListener {
@Override @Override
protected void initChannel(Channel channel) throws Exception { protected void initChannel(Channel channel) throws Exception {
try { 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 // This can take a while, so we need to stop the main thread from interfering
synchronized (networkManagers) { synchronized (networkManagers) {
ChannelInjector.fromChannel(channel, NettyProtocolInjector.this, playerFactory).inject(); injectionFactory.fromChannel(channel, NettyProtocolInjector.this, playerFactory).inject();
} }
} catch (Exception e) { } catch (Exception e) {
reporter.reportDetailed(this, Report.newBuilder(REPORT_CANNOT_INJECT_INCOMING_CHANNEL). reporter.reportDetailed(this, Report.newBuilder(REPORT_CANNOT_INJECT_INCOMING_CHANNEL).
@ -167,7 +160,7 @@ public class NettyProtocolInjector implements ChannelListener {
* @param player * @param player
*/ */
public void injectPlayer(Player player) { public void injectPlayer(Player player) {
ChannelInjector.fromPlayer(player, this).inject(); injectionFactory.fromPlayer(player, this).inject();
} }
private List<VolatileField> getBootstrapFields(Object serverConnection) { private List<VolatileField> getBootstrapFields(Object serverConnection) {
@ -203,16 +196,13 @@ public class NettyProtocolInjector implements ChannelListener {
} }
field.revertValue(); field.revertValue();
} }
// Uninject all the players // Uninject all the players
for (Player player : Bukkit.getServer().getOnlinePlayers()) { injectionFactory.close();
ChannelInjector.fromPlayer(player, this).close();
}
} }
} }
@Override @Override
public Object onPacketSending(ChannelInjector injector, Object packet, NetworkMarker marker) { public Object onPacketSending(Injector injector, Object packet, NetworkMarker marker) {
Class<?> clazz = packet.getClass(); Class<?> clazz = packet.getClass();
if (sendingFilters.contains(clazz)) { if (sendingFilters.contains(clazz)) {
@ -235,7 +225,7 @@ public class NettyProtocolInjector implements ChannelListener {
} }
@Override @Override
public Object onPacketReceiving(ChannelInjector injector, Object packet, NetworkMarker marker) { public Object onPacketReceiving(Injector injector, Object packet, NetworkMarker marker) {
Class<?> clazz = packet.getClass(); Class<?> clazz = packet.getClass();
if (reveivedFilters.contains(clazz)) { if (reveivedFilters.contains(clazz)) {
@ -295,12 +285,12 @@ public class NettyProtocolInjector implements ChannelListener {
@Override @Override
public void updatePlayer(Player player) { public void updatePlayer(Player player) {
ChannelInjector.fromPlayer(player, listener).inject(); injectionFactory.fromPlayer(player, listener).inject();
} }
@Override @Override
public void injectPlayer(Player player, ConflictStrategy strategy) { public void injectPlayer(Player player, ConflictStrategy strategy) {
ChannelInjector.fromPlayer(player, listener).inject(); injectionFactory.fromPlayer(player, listener).inject();
} }
@Override @Override
@ -324,19 +314,19 @@ public class NettyProtocolInjector implements ChannelListener {
@Override @Override
public boolean uninjectPlayer(Player player) { public boolean uninjectPlayer(Player player) {
ChannelInjector.fromPlayer(player, listener).close(); injectionFactory.fromPlayer(player, listener).close();
return true; return true;
} }
@Override @Override
public void sendServerPacket(Player receiver, PacketContainer packet, NetworkMarker marker, boolean filters) throws InvocationTargetException { 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); sendServerPacket(packet.getHandle(), marker, filters);
} }
@Override @Override
public void recieveClientPacket(Player player, Object mcPacket) throws IllegalAccessException, InvocationTargetException { public void recieveClientPacket(Player player, Object mcPacket) throws IllegalAccessException, InvocationTargetException {
ChannelInjector.fromPlayer(player, listener). injectionFactory.fromPlayer(player, listener).
recieveClientPacket(mcPacket, null, true); recieveClientPacket(mcPacket, null, true);
} }
@ -348,7 +338,7 @@ public class NettyProtocolInjector implements ChannelListener {
@Override @Override
public void handleDisconnect(Player player) { 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 @Override
public PacketEvent packetRecieved(PacketContainer packet, Player client, byte[] buffered) { public PacketEvent packetRecieved(PacketContainer packet, Player client, byte[] buffered) {
NetworkMarker marker = buffered != null ? new NettyNetworkMarker(ConnectionSide.CLIENT_SIDE, buffered) : null; 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); saveMarker(packet.getHandle(), marker);
return packetReceived(packet, client, marker); return packetReceived(packet, client, marker);
} }