diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java index 0a0be04b..c4add014 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java @@ -42,6 +42,7 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.server.PluginDisableEvent; import org.bukkit.plugin.Plugin; @@ -921,10 +922,13 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok try { manager.registerEvents(new Listener() { @EventHandler(priority = EventPriority.LOWEST) + public void onPlayerLogin(PlayerLoginEvent event) { + PacketFilterManager.this.onPlayerLogin(event); + } + @EventHandler(priority = EventPriority.LOWEST) public void onPrePlayerJoin(PlayerJoinEvent event) { PacketFilterManager.this.onPrePlayerJoin(event); } - @EventHandler(priority = EventPriority.MONITOR) public void onPlayerJoin(PlayerJoinEvent event) { PacketFilterManager.this.onPlayerJoin(event); @@ -946,6 +950,11 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok } } + private void onPlayerLogin(PlayerLoginEvent event) { + System.out.println("Address: " + event.getAddress()); + playerInjection.updatePlayer(event.getPlayer()); + } + private void onPrePlayerJoin(PlayerJoinEvent event) { playerInjection.updatePlayer(event.getPlayer()); } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelInjector.java index 189679b4..fb0ead13 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelInjector.java @@ -10,6 +10,7 @@ import java.util.NoSuchElementException; import java.util.Set; import java.util.concurrent.ConcurrentMap; +import net.minecraft.util.com.mojang.authlib.GameProfile; import net.minecraft.util.io.netty.buffer.ByteBuf; import net.minecraft.util.io.netty.channel.Channel; import net.minecraft.util.io.netty.channel.ChannelHandler; @@ -24,6 +25,7 @@ import net.sf.cglib.proxy.Factory; import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import com.comphenix.protocol.PacketType; import com.comphenix.protocol.PacketType.Protocol; import com.comphenix.protocol.ProtocolLibrary; import com.comphenix.protocol.error.Report; @@ -44,6 +46,7 @@ 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. @@ -53,7 +56,12 @@ class ChannelInjector extends ByteToMessageDecoder { 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 cachedInjector = new MapMaker().weakKeys().makeMap(); + private static final ConcurrentMap PLAYER_LOOKUP = Maps.newConcurrentMap(); + private static final ConcurrentMap NAME_LOOKUP = new MapMaker().weakValues().makeMap(); + + // The login packet + private static Class PACKET_LOGIN_CLIENT = null; + private static FieldAccessor LOGIN_GAME_PROFILE = null; // Saved accessors private static MethodAccessor DECODE_BUFFER; @@ -124,7 +132,7 @@ class ChannelInjector extends ByteToMessageDecoder { * @return A new injector, or an existing injector associated with this player. */ public static ChannelInjector fromPlayer(Player player, ChannelListener listener) { - ChannelInjector injector = cachedInjector.get(player); + ChannelInjector injector = PLAYER_LOOKUP.get(player); // Find a temporary injector as well if (injector == null) @@ -133,6 +141,11 @@ class ChannelInjector extends ByteToMessageDecoder { 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 @@ -145,7 +158,8 @@ class ChannelInjector extends ByteToMessageDecoder { injector = new ChannelInjector(player, networkManager, channel, listener); } // Cache injector and return - cachedInjector.put(player, injector); + NAME_LOOKUP.put(player.getName(), injector); + PLAYER_LOOKUP.put(player, injector); return injector; } @@ -163,6 +177,24 @@ class ChannelInjector extends ByteToMessageDecoder { 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.player = player; + return injector; + } + throw new IllegalArgumentException("Cannot inject temporary Bukkit player."); + } + /** * Construct a new channel injector for the given channel. * @param channel - the channel. @@ -199,7 +231,7 @@ class ChannelInjector extends ByteToMessageDecoder { if (findChannelHandler(originalChannel, ChannelInjector.class) != null) { // Invalidate cache if (player != null) - cachedInjector.remove(player); + PLAYER_LOOKUP.remove(player); return false; } @@ -287,6 +319,9 @@ class ChannelInjector extends ByteToMessageDecoder { } catch (NoSuchElementException e) { // Ignore it - the player has logged out } + // Clear cache + NAME_LOOKUP.remove(player.getName()); + PLAYER_LOOKUP.remove(player); } } } @@ -367,6 +402,9 @@ class ChannelInjector extends ByteToMessageDecoder { Class packetClass = input.getClass(); NetworkMarker marker = null; + // Special case! + handleLogin(packetClass, input); + if (channelListener.includeBuffer(packetClass)) { byteBuffer.resetReaderIndex(); marker = new NettyNetworkMarker(ConnectionSide.CLIENT_SIDE, getBytes(byteBuffer)); @@ -385,6 +423,27 @@ class ChannelInjector extends ByteToMessageDecoder { } } + /** + * Invoked when we may need to handle the login packet. + * @param packetClass - the packet class. + * @param packet - the packet. + */ + protected void handleLogin(Class packetClass, Object packet) { + // Initialize packet class + if (PACKET_LOGIN_CLIENT == null) { + PACKET_LOGIN_CLIENT = PacketType.Login.Client.START.getPacketClass(); + LOGIN_GAME_PROFILE = Accessors.getFieldAccessor(PACKET_LOGIN_CLIENT, GameProfile.class, true); + } + + // See if we are dealing with the login packet + if (PACKET_LOGIN_CLIENT.equals(packetClass)) { + GameProfile profile = (GameProfile) LOGIN_GAME_PROFILE.get(packet); + + // Save the channel injector + NAME_LOOKUP.put(profile.getName(), this); + } + } + @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { super.channelActive(ctx); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftFields.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftFields.java index 14e074c7..428fbcd3 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftFields.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftFields.java @@ -22,7 +22,7 @@ public class MinecraftFields { /** * Retrieve the network mananger associated with a particular player. * @param player - the player. - * @return The network manager. + * @return The network manager, or NULL if no network manager has been asssociated yet. */ public static Object getNetworkManager(Player player) { Object nmsPlayer = BukkitUnwrapper.getInstance().unwrapItem(player); @@ -33,7 +33,11 @@ public class MinecraftFields { NETWORK_ACCESSOR = Accessors.getFieldAccessor(connectionClass, networkClass, true); } // Retrieve the network manager - return NETWORK_ACCESSOR.get(getPlayerConnection(nmsPlayer)); + final Object playerConnection = getPlayerConnection(nmsPlayer); + + if (playerConnection != null) + return NETWORK_ACCESSOR.get(playerConnection); + return null; } /**