From 26521cf2ff74f0f605398e8c1c536a166977be9e Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Sun, 31 Aug 2014 09:24:38 +1000 Subject: [PATCH] Add support for Minecraft 1.8.x This commit allows BungeeCord to support Minecraft clients both of versions 1.7.x and of 1.8.x. There should be no breakages to any other support, however following their deprecation and uselessness within 1.8, the Tab list APIs have been removed. Please report any issues to GitHub and be sure to mention client, server and BungeeCord versions. When used with an appropriate server jar (such as multi protocol Spigot), this will allow clients of many versions to concurrently be connected to the same set of servers. --- .../java/net/md_5/bungee/api/ProxyServer.java | 9 - .../md_5/bungee/api/config/ListenerInfo.java | 8 +- .../bungee/api/connection/ProxiedPlayer.java | 20 --- .../md_5/bungee/api/tab/CustomTabList.java | 63 ------- .../md_5/bungee/api/tab/TabListAdapter.java | 43 ----- .../md_5/bungee/api/tab/TabListHandler.java | 53 ------ .../protocol/AbstractPacketHandler.java | 5 + .../md_5/bungee/protocol/DefinedPacket.java | 30 +++- .../bungee/protocol/PacketCompressor.java | 43 +++++ .../bungee/protocol/PacketDecompressor.java | 40 +++++ .../net/md_5/bungee/protocol/Protocol.java | 9 +- .../bungee/protocol/ProtocolConstants.java | 2 +- .../net/md_5/bungee/protocol/packet/Chat.java | 4 +- .../protocol/packet/EncryptionRequest.java | 27 ++- .../protocol/packet/EncryptionResponse.java | 27 ++- .../bungee/protocol/packet/KeepAlive.java | 4 +- .../md_5/bungee/protocol/packet/Login.java | 14 +- .../protocol/packet/PlayerListItem.java | 157 +++++++++++++++-- .../bungee/protocol/packet/PluginMessage.java | 22 ++- .../protocol/packet/ScoreboardObjective.java | 4 +- .../protocol/packet/ScoreboardScore.java | 20 ++- .../protocol/packet/SetCompression.java | 38 +++++ .../protocol/packet/TabCompleteRequest.java | 22 ++- .../net/md_5/bungee/protocol/packet/Team.java | 8 +- .../main/java/net/md_5/bungee/BungeeCord.java | 28 ++-- .../java/net/md_5/bungee/ServerConnector.java | 12 +- .../java/net/md_5/bungee/UserConnection.java | 51 ++++-- .../java/net/md_5/bungee/conf/YamlConfig.java | 9 +- .../bungee/connection/DownstreamBridge.java | 16 +- .../bungee/connection/UpstreamBridge.java | 11 +- .../net/md_5/bungee/entitymap/EntityMap.java | 4 +- ...ap_14_11_a.java => EntityMap_14_21_b.java} | 85 +++++----- .../net/md_5/bungee/netty/ChannelWrapper.java | 26 +++ .../main/java/net/md_5/bungee/tab/Custom.java | 158 ------------------ .../main/java/net/md_5/bungee/tab/Global.java | 135 +++++++++++++-- .../java/net/md_5/bungee/tab/GlobalPing.java | 21 ++- .../net/md_5/bungee/tab/ServerUnique.java | 103 ++++++++++-- .../java/net/md_5/bungee/tab/TabList.java | 59 +++++++ 38 files changed, 857 insertions(+), 533 deletions(-) delete mode 100644 api/src/main/java/net/md_5/bungee/api/tab/CustomTabList.java delete mode 100644 api/src/main/java/net/md_5/bungee/api/tab/TabListAdapter.java delete mode 100644 api/src/main/java/net/md_5/bungee/api/tab/TabListHandler.java create mode 100644 protocol/src/main/java/net/md_5/bungee/protocol/PacketCompressor.java create mode 100644 protocol/src/main/java/net/md_5/bungee/protocol/PacketDecompressor.java create mode 100644 protocol/src/main/java/net/md_5/bungee/protocol/packet/SetCompression.java rename proxy/src/main/java/net/md_5/bungee/entitymap/{EntityMap_14_11_a.java => EntityMap_14_21_b.java} (73%) delete mode 100644 proxy/src/main/java/net/md_5/bungee/tab/Custom.java create mode 100644 proxy/src/main/java/net/md_5/bungee/tab/TabList.java diff --git a/api/src/main/java/net/md_5/bungee/api/ProxyServer.java b/api/src/main/java/net/md_5/bungee/api/ProxyServer.java index b6b656e0f..b04d728c3 100644 --- a/api/src/main/java/net/md_5/bungee/api/ProxyServer.java +++ b/api/src/main/java/net/md_5/bungee/api/ProxyServer.java @@ -15,7 +15,6 @@ import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.scheduler.TaskScheduler; -import net.md_5.bungee.api.tab.CustomTabList; public abstract class ProxyServer { @@ -260,14 +259,6 @@ public abstract class ProxyServer */ public abstract void broadcast(BaseComponent message); - /** - * Gets a new instance of this proxies custom tab list. - * - * @param player the player to generate this list in the context of - * @return a new {@link CustomTabList} instance - */ - public abstract CustomTabList customTabList(ProxiedPlayer player); - /** * Gets the commands which are disabled and will not be run on this proxy. * diff --git a/api/src/main/java/net/md_5/bungee/api/config/ListenerInfo.java b/api/src/main/java/net/md_5/bungee/api/config/ListenerInfo.java index 1559cf555..162136c64 100644 --- a/api/src/main/java/net/md_5/bungee/api/config/ListenerInfo.java +++ b/api/src/main/java/net/md_5/bungee/api/config/ListenerInfo.java @@ -3,7 +3,6 @@ package net.md_5.bungee.api.config; import java.net.InetSocketAddress; import java.util.Map; import lombok.Data; -import net.md_5.bungee.api.tab.TabListHandler; /** * Class representing the configuration of a server listener. Used for allowing @@ -49,12 +48,9 @@ public class ListenerInfo */ private final Map forcedHosts; /** - * Class used to build tab lists for this player. - * - * @deprecated Future Minecraft versions render this API useless + * The type of tab list to use */ - @Deprecated - private final Class tabList; + private final String tabListType; /** * Whether to set the local address when connecting to servers. */ diff --git a/api/src/main/java/net/md_5/bungee/api/connection/ProxiedPlayer.java b/api/src/main/java/net/md_5/bungee/api/connection/ProxiedPlayer.java index 423ece5de..a3408748a 100644 --- a/api/src/main/java/net/md_5/bungee/api/connection/ProxiedPlayer.java +++ b/api/src/main/java/net/md_5/bungee/api/connection/ProxiedPlayer.java @@ -4,7 +4,6 @@ import java.util.Locale; import net.md_5.bungee.api.Callback; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.config.ServerInfo; -import net.md_5.bungee.api.tab.TabListHandler; import java.util.UUID; /** @@ -86,25 +85,6 @@ public interface ProxiedPlayer extends Connection, CommandSender */ void chat(String message); - /** - * Sets the new tab list for the user. At this stage it is not advisable to - * change after the user has logged in! - * - * @param list the new list - * @deprecated Future Minecraft versions render this API useless - */ - @Deprecated - void setTabList(TabListHandler list); - - /** - * Get the current tab list. - * - * @return the tab list in use by this user - * @deprecated Future Minecraft versions render this API useless - */ - @Deprecated - TabListHandler getTabList(); - /** * Get the server which this player will be sent to next time the log in. * diff --git a/api/src/main/java/net/md_5/bungee/api/tab/CustomTabList.java b/api/src/main/java/net/md_5/bungee/api/tab/CustomTabList.java deleted file mode 100644 index 9d60144ab..000000000 --- a/api/src/main/java/net/md_5/bungee/api/tab/CustomTabList.java +++ /dev/null @@ -1,63 +0,0 @@ -package net.md_5.bungee.api.tab; - -/** - * Represents a custom tab list, which may have slots manipulated. - * - * @deprecated Future Minecraft versions render this API useless - */ -@Deprecated -public interface CustomTabList extends TabListHandler -{ - - /** - * Blank out this tab list and update immediately. - */ - void clear(); - - /** - * Gets the columns in this list. - * - * @return the width of this list - */ - int getColumns(); - - /** - * Gets the rows in this list. - * - * @return the height of this list - */ - int getRows(); - - /** - * Get the total size of this list. - * - * @return {@link #getRows()} * {@link #getColumns()} - */ - int getSize(); - - /** - * Set the text in the specified slot and update immediately. - * - * @param row the row to set - * @param column the column to set - * @param text the text to set - * @return the padded text - */ - String setSlot(int row, int column, String text); - - /** - * Set the text in the specified slot. - * - * @param row the row to set - * @param column the column to set - * @param text the text to set - * @param update whether or not to invoke {@link #update()} upon completion - * @return the padded text - */ - String setSlot(int row, int column, String text, boolean update); - - /** - * Flush all queued changes to the user. - */ - void update(); -} diff --git a/api/src/main/java/net/md_5/bungee/api/tab/TabListAdapter.java b/api/src/main/java/net/md_5/bungee/api/tab/TabListAdapter.java deleted file mode 100644 index 3c046136f..000000000 --- a/api/src/main/java/net/md_5/bungee/api/tab/TabListAdapter.java +++ /dev/null @@ -1,43 +0,0 @@ -package net.md_5.bungee.api.tab; - -import lombok.Getter; -import lombok.NoArgsConstructor; -import net.md_5.bungee.api.connection.ProxiedPlayer; - -/** - * @deprecated Future Minecraft versions render this API useless - */ -@Deprecated -@NoArgsConstructor -public abstract class TabListAdapter implements TabListHandler -{ - - @Getter - private ProxiedPlayer player; - - @Override - public void init(ProxiedPlayer player) - { - this.player = player; - } - - @Override - public void onConnect() - { - } - - @Override - public void onDisconnect() - { - } - - @Override - public void onServerChange() - { - } - - @Override - public void onPingChange(int ping) - { - } -} diff --git a/api/src/main/java/net/md_5/bungee/api/tab/TabListHandler.java b/api/src/main/java/net/md_5/bungee/api/tab/TabListHandler.java deleted file mode 100644 index 4b147a4ad..000000000 --- a/api/src/main/java/net/md_5/bungee/api/tab/TabListHandler.java +++ /dev/null @@ -1,53 +0,0 @@ -package net.md_5.bungee.api.tab; - -import net.md_5.bungee.api.connection.ProxiedPlayer; - -/** - * @deprecated Future Minecraft versions render this API useless - */ -@Deprecated -public interface TabListHandler -{ - - /** - * Called so that this class may set member fields to keep track of its - * internal state. You should not do any packet sending or manipulation of - * the passed player, other than storing it. - * - * @param player the player to be associated with this list - */ - void init(ProxiedPlayer player); - - /** - * Called when this player first connects to the proxy. - */ - void onConnect(); - - /** - * Called when a player first connects to the proxy. - */ - void onServerChange(); - - /** - * Called when a players ping changes. The new ping will have not updated in - * the player instance until this method returns. - * - * @param ping the player's new ping. - */ - void onPingChange(int ping); - - /** - * Called when a player disconnects. - */ - void onDisconnect(); - - /** - * Called when a list update packet is sent from server to client. - * - * @param name the player which this packet is relevant to - * @param online whether the subject player is online - * @param ping ping of the subject player - * @return whether to send the packet to the client - */ - boolean onListUpdate(String name, boolean online, int ping); -} diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/AbstractPacketHandler.java b/protocol/src/main/java/net/md_5/bungee/protocol/AbstractPacketHandler.java index 2203489d9..5e7a04e03 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/AbstractPacketHandler.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/AbstractPacketHandler.java @@ -7,6 +7,7 @@ import net.md_5.bungee.protocol.packet.Login; import net.md_5.bungee.protocol.packet.Chat; import net.md_5.bungee.protocol.packet.EncryptionRequest; import net.md_5.bungee.protocol.packet.PlayerListItem; +import net.md_5.bungee.protocol.packet.SetCompression; import net.md_5.bungee.protocol.packet.TabCompleteRequest; import net.md_5.bungee.protocol.packet.ScoreboardObjective; import net.md_5.bungee.protocol.packet.ScoreboardScore; @@ -128,4 +129,8 @@ public abstract class AbstractPacketHandler public void handle(LegacyHandshake legacyHandshake) throws Exception { } + + public void handle(SetCompression setCompression) throws Exception + { + } } diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/DefinedPacket.java b/protocol/src/main/java/net/md_5/bungee/protocol/DefinedPacket.java index bfce4011c..647cc9743 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/DefinedPacket.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/DefinedPacket.java @@ -5,6 +5,8 @@ import com.google.common.base.Preconditions; import io.netty.buffer.ByteBuf; import lombok.RequiredArgsConstructor; +import java.util.UUID; + @RequiredArgsConstructor public abstract class DefinedPacket { @@ -29,7 +31,7 @@ public abstract class DefinedPacket return new String( b, Charsets.UTF_8 ); } - public static void writeArray(byte[] b, ByteBuf buf) + public static void writeArrayLegacy(byte[] b, ByteBuf buf) { Preconditions.checkArgument( b.length <= Short.MAX_VALUE, "Cannot send array longer than Short.MAX_VALUE (got %s bytes)", b.length ); @@ -37,7 +39,7 @@ public abstract class DefinedPacket buf.writeBytes( b ); } - public static byte[] readArray(ByteBuf buf) + public static byte[] readArrayLegacy(ByteBuf buf) { short len = buf.readShort(); Preconditions.checkArgument( len <= Short.MAX_VALUE, "Cannot receive array longer than Short.MAX_VALUE (got %s bytes)", len ); @@ -47,6 +49,19 @@ public abstract class DefinedPacket return ret; } + public static void writeArray(byte[] b, ByteBuf buf) + { + writeVarInt( b.length, buf ); + buf.writeBytes( b ); + } + + public static byte[] readArray(ByteBuf buf) + { + byte[] ret = new byte[ readVarInt( buf ) ]; + buf.readBytes( ret ); + return ret; + } + public static void writeStringArray(String[] s, ByteBuf buf) { writeVarInt( s.length, buf ); @@ -114,6 +129,17 @@ public abstract class DefinedPacket } } + public static void writeUUID(UUID value, ByteBuf output) + { + output.writeLong( value.getMostSignificantBits() ); + output.writeLong( value.getLeastSignificantBits() ); + } + + public static UUID readUUID(ByteBuf input) + { + return new UUID( input.readLong(), input.readLong() ); + } + public void read(ByteBuf buf) { throw new UnsupportedOperationException( "Packet must implement read method" ); diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/PacketCompressor.java b/protocol/src/main/java/net/md_5/bungee/protocol/PacketCompressor.java new file mode 100644 index 000000000..216a92d36 --- /dev/null +++ b/protocol/src/main/java/net/md_5/bungee/protocol/PacketCompressor.java @@ -0,0 +1,43 @@ +package net.md_5.bungee.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; +import lombok.Setter; + +import java.util.zip.Deflater; + +public class PacketCompressor extends MessageToByteEncoder +{ + + private final byte[] buffer = new byte[ 8192 ]; + private final Deflater deflater = new Deflater(); + @Setter + private int threshold = 256; + + @Override + protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception + { + int origSize = msg.readableBytes(); + if ( origSize < threshold ) + { + DefinedPacket.writeVarInt( 0, out ); + out.writeBytes( msg ); + } else + { + byte[] data = new byte[ origSize ]; + msg.readBytes( data ); + + DefinedPacket.writeVarInt( data.length, out ); + + deflater.setInput( data ); + deflater.finish(); + while ( !deflater.finished() ) + { + int count = deflater.deflate( buffer ); + out.writeBytes( buffer, 0, count ); + } + deflater.reset(); + } + } +} diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/PacketDecompressor.java b/protocol/src/main/java/net/md_5/bungee/protocol/PacketDecompressor.java new file mode 100644 index 000000000..5496a604d --- /dev/null +++ b/protocol/src/main/java/net/md_5/bungee/protocol/PacketDecompressor.java @@ -0,0 +1,40 @@ +package net.md_5.bungee.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; + +import java.util.List; +import java.util.zip.Inflater; + +public class PacketDecompressor extends ByteToMessageDecoder +{ + + private final Inflater inflater = new Inflater(); + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception + { + if ( in.readableBytes() == 0 ) + { + return; + } + + int size = DefinedPacket.readVarInt( in ); + if ( size == 0 ) + { + out.add( in.readBytes( in.readableBytes() ) ); + } else + { + byte[] compressedData = new byte[ in.readableBytes() ]; + in.readBytes( compressedData ); + inflater.setInput( compressedData ); + + byte[] data = new byte[ size ]; + inflater.inflate( data ); + out.add( Unpooled.wrappedBuffer( data ) ); + inflater.reset(); + } + } +} diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java b/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java index fe1ae22bc..d1b019811 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java @@ -25,6 +25,7 @@ import net.md_5.bungee.protocol.packet.Respawn; import net.md_5.bungee.protocol.packet.ScoreboardDisplay; import net.md_5.bungee.protocol.packet.ScoreboardObjective; import net.md_5.bungee.protocol.packet.ScoreboardScore; +import net.md_5.bungee.protocol.packet.SetCompression; import net.md_5.bungee.protocol.packet.StatusRequest; import net.md_5.bungee.protocol.packet.StatusResponse; import net.md_5.bungee.protocol.packet.TabCompleteRequest; @@ -59,6 +60,7 @@ public enum Protocol TO_CLIENT.registerPacket( 0x3E, Team.class ); TO_CLIENT.registerPacket( 0x3F, PluginMessage.class ); TO_CLIENT.registerPacket( 0x40, Kick.class ); + TO_CLIENT.registerPacket( 0x46, SetCompression.class ); TO_SERVER.registerPacket( 0x00, KeepAlive.class ); TO_SERVER.registerPacket( 0x01, Chat.class ); @@ -87,6 +89,7 @@ public enum Protocol TO_CLIENT.registerPacket( 0x00, Kick.class ); TO_CLIENT.registerPacket( 0x01, EncryptionRequest.class ); TO_CLIENT.registerPacket( 0x02, LoginSuccess.class ); + TO_CLIENT.registerPacket( 0x03, SetCompression.class ); TO_SERVER.registerPacket( 0x00, LoginRequest.class ); TO_SERVER.registerPacket( 0x01, EncryptionResponse.class ); @@ -94,7 +97,11 @@ public enum Protocol }; /*========================================================================*/ public static final int MAX_PACKET_ID = 0xFF; - public static List supportedVersions = Arrays.asList( ProtocolConstants.MINECRAFT_1_7_2, ProtocolConstants.MINECRAFT_1_7_6, ProtocolConstants.MINECRAFT_14_11_a ); + public static List supportedVersions = Arrays.asList( + ProtocolConstants.MINECRAFT_1_7_2, + ProtocolConstants.MINECRAFT_1_7_6, + ProtocolConstants.MINECRAFT_SNAPSHOT + ); /*========================================================================*/ public final DirectionData TO_SERVER = new DirectionData( ProtocolConstants.Direction.TO_SERVER ); public final DirectionData TO_CLIENT = new DirectionData( ProtocolConstants.Direction.TO_CLIENT ); diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/ProtocolConstants.java b/protocol/src/main/java/net/md_5/bungee/protocol/ProtocolConstants.java index f0613f540..343925107 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/ProtocolConstants.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/ProtocolConstants.java @@ -5,7 +5,7 @@ public class ProtocolConstants public static final int MINECRAFT_1_7_2 = 4; public static final int MINECRAFT_1_7_6 = 5; - public static final int MINECRAFT_14_11_a = 14; + public static final int MINECRAFT_SNAPSHOT = 46; public enum Direction { diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Chat.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Chat.java index 8fe3e0b83..504b563da 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Chat.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Chat.java @@ -28,7 +28,7 @@ public class Chat extends DefinedPacket public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { message = readString( buf ); - if ( direction == ProtocolConstants.Direction.TO_CLIENT && protocolVersion >= ProtocolConstants.MINECRAFT_14_11_a ) + if ( direction == ProtocolConstants.Direction.TO_CLIENT && protocolVersion >= ProtocolConstants.MINECRAFT_SNAPSHOT ) { position = buf.readByte(); } @@ -38,7 +38,7 @@ public class Chat extends DefinedPacket public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { writeString( message, buf ); - if ( direction == ProtocolConstants.Direction.TO_CLIENT && protocolVersion >= ProtocolConstants.MINECRAFT_14_11_a ) + if ( direction == ProtocolConstants.Direction.TO_CLIENT && protocolVersion >= ProtocolConstants.MINECRAFT_SNAPSHOT ) { buf.writeByte( position ); } diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionRequest.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionRequest.java index 4521f6c3b..ca4e735dc 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionRequest.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionRequest.java @@ -7,6 +7,7 @@ import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import net.md_5.bungee.protocol.AbstractPacketHandler; import net.md_5.bungee.protocol.DefinedPacket; +import net.md_5.bungee.protocol.ProtocolConstants; @Data @NoArgsConstructor @@ -20,19 +21,33 @@ public class EncryptionRequest extends DefinedPacket private byte[] verifyToken; @Override - public void read(ByteBuf buf) + public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { serverId = readString( buf ); - publicKey = readArray( buf ); - verifyToken = readArray( buf ); + if ( protocolVersion < ProtocolConstants.MINECRAFT_SNAPSHOT ) + { + publicKey = readArrayLegacy( buf ); + verifyToken = readArrayLegacy( buf ); + } else + { + publicKey = readArray( buf ); + verifyToken = readArray( buf ); + } } @Override - public void write(ByteBuf buf) + public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { writeString( serverId, buf ); - writeArray( publicKey, buf ); - writeArray( verifyToken, buf ); + if ( protocolVersion < ProtocolConstants.MINECRAFT_SNAPSHOT ) + { + writeArrayLegacy( publicKey, buf ); + writeArrayLegacy( verifyToken, buf ); + } else + { + writeArray( publicKey, buf ); + writeArray( verifyToken, buf ); + } } @Override diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionResponse.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionResponse.java index 443c7a5b8..eaa34b6a0 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionResponse.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionResponse.java @@ -7,6 +7,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import net.md_5.bungee.protocol.AbstractPacketHandler; +import net.md_5.bungee.protocol.ProtocolConstants; @Data @NoArgsConstructor @@ -19,17 +20,31 @@ public class EncryptionResponse extends DefinedPacket private byte[] verifyToken; @Override - public void read(ByteBuf buf) + public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - sharedSecret = readArray( buf ); - verifyToken = readArray( buf ); + if ( protocolVersion < ProtocolConstants.MINECRAFT_SNAPSHOT ) + { + sharedSecret = readArrayLegacy( buf ); + verifyToken = readArrayLegacy( buf ); + } else + { + sharedSecret = readArray( buf ); + verifyToken = readArray( buf ); + } } @Override - public void write(ByteBuf buf) + public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - writeArray( sharedSecret, buf ); - writeArray( verifyToken, buf ); + if ( protocolVersion < ProtocolConstants.MINECRAFT_SNAPSHOT ) + { + writeArrayLegacy( sharedSecret, buf ); + writeArrayLegacy( verifyToken, buf ); + } else + { + writeArray( sharedSecret, buf ); + writeArray( verifyToken, buf ); + } } @Override diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/KeepAlive.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/KeepAlive.java index c9156723a..a15591db8 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/KeepAlive.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/KeepAlive.java @@ -21,7 +21,7 @@ public class KeepAlive extends DefinedPacket @Override public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - if ( direction == ProtocolConstants.Direction.TO_SERVER && protocolVersion >= ProtocolConstants.MINECRAFT_14_11_a ) + if ( protocolVersion >= ProtocolConstants.MINECRAFT_SNAPSHOT ) { randomId = readVarInt( buf ); } else @@ -33,7 +33,7 @@ public class KeepAlive extends DefinedPacket @Override public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - if ( direction == ProtocolConstants.Direction.TO_SERVER && protocolVersion >= ProtocolConstants.MINECRAFT_14_11_a ) + if ( protocolVersion >= ProtocolConstants.MINECRAFT_SNAPSHOT ) { writeVarInt( randomId, buf ); } else diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Login.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Login.java index ff41f7b99..31df79633 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Login.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Login.java @@ -7,6 +7,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import net.md_5.bungee.protocol.AbstractPacketHandler; +import net.md_5.bungee.protocol.ProtocolConstants; @Data @NoArgsConstructor @@ -21,9 +22,10 @@ public class Login extends DefinedPacket private short difficulty; private short maxPlayers; private String levelType; + private boolean reducedDebugInfo; @Override - public void read(ByteBuf buf) + public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { entityId = buf.readInt(); gameMode = buf.readUnsignedByte(); @@ -31,10 +33,14 @@ public class Login extends DefinedPacket difficulty = buf.readUnsignedByte(); maxPlayers = buf.readUnsignedByte(); levelType = readString( buf ); + if ( protocolVersion >= 29 ) + { + reducedDebugInfo = buf.readBoolean(); + } } @Override - public void write(ByteBuf buf) + public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { buf.writeInt( entityId ); buf.writeByte( gameMode ); @@ -42,6 +48,10 @@ public class Login extends DefinedPacket buf.writeByte( difficulty ); buf.writeByte( maxPlayers ); writeString( levelType, buf ); + if ( protocolVersion >= 29 ) + { + buf.writeBoolean( reducedDebugInfo ); + } } @Override diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/PlayerListItem.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/PlayerListItem.java index e1770537e..ea12d6e14 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/PlayerListItem.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/PlayerListItem.java @@ -2,49 +2,144 @@ package net.md_5.bungee.protocol.packet; import net.md_5.bungee.protocol.DefinedPacket; import io.netty.buffer.ByteBuf; -import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import net.md_5.bungee.protocol.AbstractPacketHandler; import net.md_5.bungee.protocol.ProtocolConstants; +import java.util.UUID; + @Data @NoArgsConstructor -@AllArgsConstructor @EqualsAndHashCode(callSuper = false) public class PlayerListItem extends DefinedPacket { - private String username; - private boolean online; - private int ping; + private Action action; + private Item[] items; @Override public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - username = readString( buf ); - online = buf.readBoolean(); - if ( protocolVersion >= ProtocolConstants.MINECRAFT_14_11_a ) + if ( protocolVersion < ProtocolConstants.MINECRAFT_SNAPSHOT ) { - ping = readVarInt( buf ); + items = new Item[ 1 ]; + Item item = items[ 0 ] = new Item(); + item.displayName = item.username = readString( buf ); + action = !buf.readBoolean() ? Action.REMOVE_PLAYER : Action.ADD_PLAYER; + item.ping = buf.readShort(); } else { - ping = buf.readShort(); + action = Action.values()[ DefinedPacket.readVarInt( buf )]; + items = new Item[ DefinedPacket.readVarInt( buf ) ]; + for ( int i = 0; i < items.length; i++ ) + { + Item item = items[ i ] = new Item(); + item.setUuid( DefinedPacket.readUUID( buf ) ); + switch ( action ) + { + case ADD_PLAYER: + item.username = DefinedPacket.readString( buf ); + item.properties = new String[ DefinedPacket.readVarInt( buf ) ][]; + for ( int j = 0; j < item.properties.length; j++ ) + { + String name = DefinedPacket.readString( buf ); + String value = DefinedPacket.readString( buf ); + if ( buf.readBoolean() ) + { + item.properties[ j] = new String[] + { + name, value, DefinedPacket.readString( buf ) + }; + } else + { + item.properties[ j ] = new String[] + { + name, value + }; + } + } + item.gamemode = DefinedPacket.readVarInt( buf ); + item.ping = DefinedPacket.readVarInt( buf ); + if ( buf.readBoolean() ) + { + item.displayName = DefinedPacket.readString( buf ); + } + break; + case UPDATE_GAMEMODE: + item.gamemode = DefinedPacket.readVarInt( buf ); + break; + case UPDATE_LATENCY: + item.ping = DefinedPacket.readVarInt( buf ); + break; + case UPDATE_DISPLAY_NAME: + if ( buf.readBoolean() ) + { + item.displayName = DefinedPacket.readString( buf ); + } + } + } } } @Override public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - writeString( username, buf ); - buf.writeBoolean( online ); - if ( protocolVersion >= ProtocolConstants.MINECRAFT_14_11_a ) + if ( protocolVersion < ProtocolConstants.MINECRAFT_SNAPSHOT ) { - writeVarInt( ping, buf ); + Item item = items[0]; // Only one at a time + writeString( item.displayName, buf ); + buf.writeBoolean( action != Action.REMOVE_PLAYER ); + buf.writeShort( item.ping ); } else { - buf.writeShort( ping ); + DefinedPacket.writeVarInt( action.ordinal(), buf ); + DefinedPacket.writeVarInt( items.length, buf ); + for ( Item item : items ) + { + DefinedPacket.writeUUID( item.uuid, buf ); + switch ( action ) + { + case ADD_PLAYER: + DefinedPacket.writeString( item.username, buf ); + DefinedPacket.writeVarInt( item.properties.length, buf ); + for ( String[] prop : item.properties ) + { + DefinedPacket.writeString( prop[ 0], buf ); + DefinedPacket.writeString( prop[ 1], buf ); + if ( prop.length >= 3 ) + { + buf.writeBoolean( true ); + DefinedPacket.writeString( prop[ 2], buf ); + } else + { + buf.writeBoolean( false ); + } + } + DefinedPacket.writeVarInt( item.gamemode, buf ); + DefinedPacket.writeVarInt( item.ping, buf ); + buf.writeBoolean( item.displayName != null ); + if ( item.displayName != null ) + { + DefinedPacket.writeString( item.displayName, buf ); + } + break; + case UPDATE_GAMEMODE: + DefinedPacket.writeVarInt( item.gamemode, buf ); + break; + case UPDATE_LATENCY: + DefinedPacket.writeVarInt( item.ping, buf ); + break; + case UPDATE_DISPLAY_NAME: + buf.writeBoolean( item.displayName != null ); + if ( item.displayName != null ) + { + DefinedPacket.writeString( item.displayName, buf ); + } + break; + } + } } } @@ -53,4 +148,36 @@ public class PlayerListItem extends DefinedPacket { handler.handle( this ); } + + public static enum Action + { + + ADD_PLAYER, + UPDATE_GAMEMODE, + UPDATE_LATENCY, + UPDATE_DISPLAY_NAME, + REMOVE_PLAYER; + } + + @Data + public static class Item + { + + // ALL + private UUID uuid; + + // ADD_PLAYER + private String username; + private String[][] properties; + + // ADD_PLAYER & UPDATE_GAMEMODE + private int gamemode; + + // ADD_PLAYER & UPDATE_LATENCY + private int ping; + + // ADD_PLAYER & UPDATE_DISPLAY_NAME + private String displayName; + + } } diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/PluginMessage.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/PluginMessage.java index 57d4efeae..690ed71ce 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/PluginMessage.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/PluginMessage.java @@ -12,6 +12,7 @@ import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import net.md_5.bungee.protocol.MinecraftInput; import net.md_5.bungee.protocol.AbstractPacketHandler; +import net.md_5.bungee.protocol.ProtocolConstants; @Data @NoArgsConstructor @@ -24,17 +25,30 @@ public class PluginMessage extends DefinedPacket private byte[] data; @Override - public void read(ByteBuf buf) + public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { tag = readString( buf ); - data = readArray( buf ); + if ( protocolVersion < ProtocolConstants.MINECRAFT_SNAPSHOT ) + { + data = readArrayLegacy( buf ); + } else + { + data = new byte[ buf.readableBytes() ]; + buf.readBytes( data ); + } } @Override - public void write(ByteBuf buf) + public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { writeString( tag, buf ); - writeArray( data, buf ); + if ( protocolVersion < ProtocolConstants.MINECRAFT_SNAPSHOT ) + { + writeArrayLegacy( data, buf ); + } else + { + buf.writeBytes( data ); + } } @Override diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardObjective.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardObjective.java index 1e850e8da..f9fd5ddf5 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardObjective.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardObjective.java @@ -33,7 +33,7 @@ public class ScoreboardObjective extends DefinedPacket value = readString( buf ); } action = buf.readByte(); - if ( protocolVersion >= ProtocolConstants.MINECRAFT_14_11_a && ( action == 0 || action == 2 ) ) + if ( protocolVersion >= ProtocolConstants.MINECRAFT_SNAPSHOT && ( action == 0 || action == 2 ) ) { value = readString( buf ); type = readString( buf ); @@ -49,7 +49,7 @@ public class ScoreboardObjective extends DefinedPacket writeString( value, buf ); } buf.writeByte( action ); - if ( protocolVersion >= ProtocolConstants.MINECRAFT_14_11_a && ( action == 0 || action == 2 ) ) + if ( protocolVersion >= ProtocolConstants.MINECRAFT_SNAPSHOT && ( action == 0 || action == 2 ) ) { writeString( value, buf ); writeString( type, buf ); diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardScore.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardScore.java index 219a63066..4d5cd5da3 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardScore.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardScore.java @@ -29,14 +29,18 @@ public class ScoreboardScore extends DefinedPacket { itemName = readString( buf ); action = buf.readByte(); - if ( action != 1 ) + if ( protocolVersion >= ProtocolConstants.MINECRAFT_SNAPSHOT ) { scoreName = readString( buf ); - if ( protocolVersion >= ProtocolConstants.MINECRAFT_14_11_a ) + if ( action != 1 ) { value = readVarInt( buf ); - } else + } + } else + { + if ( action != 1 ) { + scoreName = readString( buf ); value = buf.readInt(); } } @@ -47,14 +51,18 @@ public class ScoreboardScore extends DefinedPacket { writeString( itemName, buf ); buf.writeByte( action ); - if ( action != 1 ) + if ( protocolVersion >= ProtocolConstants.MINECRAFT_SNAPSHOT ) { writeString( scoreName, buf ); - if ( protocolVersion >= ProtocolConstants.MINECRAFT_14_11_a ) + if ( action != 1 ) { writeVarInt( value, buf ); - } else + } + } else + { + if ( action != 1 ) { + writeString( scoreName, buf ); buf.writeInt( value ); } } diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/SetCompression.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/SetCompression.java new file mode 100644 index 000000000..086e3e6ed --- /dev/null +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/SetCompression.java @@ -0,0 +1,38 @@ +package net.md_5.bungee.protocol.packet; + +import io.netty.buffer.ByteBuf; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import net.md_5.bungee.protocol.AbstractPacketHandler; +import net.md_5.bungee.protocol.DefinedPacket; +import net.md_5.bungee.protocol.ProtocolConstants; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class SetCompression extends DefinedPacket +{ + + private int threshold; + + @Override + public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + threshold = DefinedPacket.readVarInt( buf ); + } + + @Override + public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + DefinedPacket.writeVarInt( threshold, buf ); + } + + @Override + public void handle(AbstractPacketHandler handler) throws Exception + { + handler.handle( this ); + } +} diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/TabCompleteRequest.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/TabCompleteRequest.java index 3bedbd5eb..c685083a6 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/TabCompleteRequest.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/TabCompleteRequest.java @@ -7,6 +7,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import net.md_5.bungee.protocol.AbstractPacketHandler; +import net.md_5.bungee.protocol.ProtocolConstants; @Data @NoArgsConstructor @@ -16,17 +17,34 @@ public class TabCompleteRequest extends DefinedPacket { private String cursor; + private boolean hasPositon; + private long position; @Override - public void read(ByteBuf buf) + public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { cursor = readString( buf ); + if ( protocolVersion >= ProtocolConstants.MINECRAFT_SNAPSHOT ) + { + if ( hasPositon = buf.readBoolean() ) + { + position = buf.readLong(); + } + } } @Override - public void write(ByteBuf buf) + public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { writeString( cursor, buf ); + if ( protocolVersion >= ProtocolConstants.MINECRAFT_SNAPSHOT ) + { + buf.writeBoolean( hasPositon ); + if ( hasPositon ) + { + buf.writeLong( position ); + } + } } @Override diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Team.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Team.java index 792302c69..fab4a9036 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Team.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Team.java @@ -50,7 +50,7 @@ public class Team extends DefinedPacket prefix = readString( buf ); suffix = readString( buf ); friendlyFire = buf.readByte(); - if ( protocolVersion >= ProtocolConstants.MINECRAFT_14_11_a ) + if ( protocolVersion >= ProtocolConstants.MINECRAFT_SNAPSHOT ) { nameTagVisibility = readString( buf ); color = buf.readByte(); @@ -58,7 +58,7 @@ public class Team extends DefinedPacket } if ( mode == 0 || mode == 3 || mode == 4 ) { - int len = ( protocolVersion >= ProtocolConstants.MINECRAFT_14_11_a ) ? readVarInt( buf ) : buf.readShort(); + int len = ( protocolVersion >= ProtocolConstants.MINECRAFT_SNAPSHOT ) ? readVarInt( buf ) : buf.readShort(); players = new String[ len ]; for ( int i = 0; i < len; i++ ) { @@ -78,7 +78,7 @@ public class Team extends DefinedPacket writeString( prefix, buf ); writeString( suffix, buf ); buf.writeByte( friendlyFire ); - if ( protocolVersion >= ProtocolConstants.MINECRAFT_14_11_a ) + if ( protocolVersion >= ProtocolConstants.MINECRAFT_SNAPSHOT ) { writeString( nameTagVisibility, buf ); buf.writeByte( color ); @@ -86,7 +86,7 @@ public class Team extends DefinedPacket } if ( mode == 0 || mode == 3 || mode == 4 ) { - if ( protocolVersion >= ProtocolConstants.MINECRAFT_14_11_a ) + if ( protocolVersion >= ProtocolConstants.MINECRAFT_SNAPSHOT ) { writeVarInt( players.length, buf ); } else diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java index 64ecdd88f..eebeca08c 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -36,6 +36,7 @@ import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Locale; import java.util.Map; @@ -65,7 +66,6 @@ import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.plugin.PluginManager; -import net.md_5.bungee.api.tab.CustomTabList; import net.md_5.bungee.command.*; import net.md_5.bungee.conf.YamlConfig; import net.md_5.bungee.log.LoggingOutputStream; @@ -76,7 +76,6 @@ import net.md_5.bungee.protocol.ProtocolConstants; import net.md_5.bungee.protocol.packet.Chat; import net.md_5.bungee.protocol.packet.PluginMessage; import net.md_5.bungee.query.RemoteQuery; -import net.md_5.bungee.tab.Custom; import net.md_5.bungee.util.CaseInsensitiveMap; import org.fusesource.jansi.AnsiConsole; @@ -113,6 +112,8 @@ public class BungeeCord extends ProxyServer * Fully qualified connections. */ private final Map connections = new CaseInsensitiveMap<>(); + // Used to help with packet rewriting + private final Map connectionsByOfflineUUID = new HashMap<>(); private final ReadWriteLock connectionLock = new ReentrantReadWriteLock(); /** * Plugin manager. @@ -144,7 +145,7 @@ public class BungeeCord extends ProxyServer private ConnectionThrottle connectionThrottle; private final ModuleManager moduleManager = new ModuleManager(); - + { // TODO: Proper fallback when we interface the manager getPluginManager().registerCommand( null, new CommandReload() ); @@ -468,6 +469,18 @@ public class BungeeCord extends ProxyServer } } + public UserConnection getPlayerByOfflineUUID(UUID name) + { + connectionLock.readLock().lock(); + try + { + return connectionsByOfflineUUID.get( name ); + } finally + { + connectionLock.readLock().unlock(); + } + } + @Override public ProxiedPlayer getPlayer(UUID uuid) { @@ -577,6 +590,7 @@ public class BungeeCord extends ProxyServer try { connections.put( con.getName(), con ); + connectionsByOfflineUUID.put( con.getPendingConnection().getOfflineId(), con ); } finally { connectionLock.writeLock().unlock(); @@ -589,19 +603,13 @@ public class BungeeCord extends ProxyServer try { connections.remove( con.getName() ); + connectionsByOfflineUUID.remove( con.getPendingConnection().getOfflineId() ); } finally { connectionLock.writeLock().unlock(); } } - @Override - public CustomTabList customTabList(ProxiedPlayer player) - { - return new Custom( player ); - } - - @Override public Collection getDisabledCommands() { return config.getDisabledCommands(); diff --git a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java index 17e90daff..e0da10eeb 100644 --- a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java +++ b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java @@ -31,6 +31,7 @@ import net.md_5.bungee.protocol.packet.ScoreboardObjective; import net.md_5.bungee.protocol.packet.PluginMessage; import net.md_5.bungee.protocol.packet.Kick; import net.md_5.bungee.protocol.packet.LoginSuccess; +import net.md_5.bungee.protocol.packet.SetCompression; @RequiredArgsConstructor public class ServerConnector extends PacketHandler @@ -102,6 +103,13 @@ public class ServerConnector extends PacketHandler throw CancelSendSignal.INSTANCE; } + @Override + public void handle(SetCompression setCompression) throws Exception + { + user.setCompressionThreshold( setCompression.getThreshold() ); + ch.setCompressionThreshold( setCompression.getThreshold() ); + } + @Override public void handle(Login login) throws Exception { @@ -139,7 +147,7 @@ public class ServerConnector extends PacketHandler // Set tab list size, this sucks balls, TODO: what shall we do about packet mutability Login modLogin = new Login( login.getEntityId(), login.getGameMode(), (byte) login.getDimension(), login.getDifficulty(), - (byte) user.getPendingConnection().getListener().getTabListSize(), login.getLevelType() ); + (byte) user.getPendingConnection().getListener().getTabListSize(), login.getLevelType(), login.isReducedDebugInfo() ); user.unsafe().sendPacket( modLogin ); @@ -148,7 +156,7 @@ public class ServerConnector extends PacketHandler user.unsafe().sendPacket( new PluginMessage( "MC|Brand", out.toArray() ) ); } else { - user.getTabList().onServerChange(); + user.getTabListHandler().onServerChange(); Scoreboard serverScoreboard = user.getServerSentScoreboard(); for ( Objective objective : serverScoreboard.getObjectives() ) diff --git a/proxy/src/main/java/net/md_5/bungee/UserConnection.java b/proxy/src/main/java/net/md_5/bungee/UserConnection.java index 2160175e3..b85b9cd96 100644 --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java @@ -30,7 +30,6 @@ import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.event.PermissionCheckEvent; import net.md_5.bungee.api.event.ServerConnectEvent; import net.md_5.bungee.api.score.Scoreboard; -import net.md_5.bungee.api.tab.TabListHandler; import net.md_5.bungee.chat.ComponentSerializer; import net.md_5.bungee.connection.InitialHandler; import net.md_5.bungee.entitymap.EntityMap; @@ -46,6 +45,11 @@ import net.md_5.bungee.protocol.packet.Chat; import net.md_5.bungee.protocol.packet.ClientSettings; import net.md_5.bungee.protocol.packet.PluginMessage; import net.md_5.bungee.protocol.packet.Kick; +import net.md_5.bungee.protocol.packet.SetCompression; +import net.md_5.bungee.tab.Global; +import net.md_5.bungee.tab.GlobalPing; +import net.md_5.bungee.tab.ServerUnique; +import net.md_5.bungee.tab.TabList; import net.md_5.bungee.util.CaseInsensitiveSet; @RequiredArgsConstructor @@ -73,8 +77,6 @@ public final class UserConnection implements ProxiedPlayer private final Collection pendingConnects = new HashSet<>(); /*========================================================================*/ @Getter - private TabListHandler tabList; - @Getter @Setter private int sentPingId; @Getter @@ -86,6 +88,13 @@ public final class UserConnection implements ProxiedPlayer @Getter @Setter private ServerInfo reconnectServer; + @Getter + private TabList tabListHandler; + @Getter + @Setter + private int gamemode; + @Getter + private int compressionThreshold = -1; /*========================================================================*/ private final Collection groups = new CaseInsensitiveSet(); private final Collection permissions = new CaseInsensitiveSet(); @@ -121,14 +130,19 @@ public final class UserConnection implements ProxiedPlayer this.entityRewrite = EntityMap.getEntityMap( getPendingConnection().getVersion() ); this.displayName = name; - try + + switch ( getPendingConnection().getListener().getTabListType() ) { - this.tabList = getPendingConnection().getListener().getTabList().getDeclaredConstructor().newInstance(); - } catch ( ReflectiveOperationException ex ) - { - throw new RuntimeException( ex ); + case "GLOBAL": + tabListHandler = new Global( this ); + break; + case "SERVER": + tabListHandler = new ServerUnique( this ); + break; + default: + tabListHandler = new GlobalPing( this ); + break; } - this.tabList.init( this ); Collection g = bungee.getConfigurationAdapter().getGroups( name ); for ( String s : g ) @@ -137,13 +151,6 @@ public final class UserConnection implements ProxiedPlayer } } - @Override - public void setTabList(TabListHandler tabList) - { - tabList.init( this ); - this.tabList = tabList; - } - public void sendPacket(PacketWrapper packet) { ch.write( packet ); @@ -160,9 +167,7 @@ public final class UserConnection implements ProxiedPlayer { Preconditions.checkNotNull( name, "displayName" ); Preconditions.checkArgument( name.length() <= 16, "Display name cannot be longer than 16 characters" ); - getTabList().onDisconnect(); displayName = name; - getTabList().onConnect(); } @Override @@ -461,4 +466,14 @@ public final class UserConnection implements ProxiedPlayer { return ( locale == null && settings != null ) ? locale = Locale.forLanguageTag( settings.getLocale().replaceAll( "_", "-" ) ) : locale; } + + public void setCompressionThreshold(int compressionThreshold) + { + if ( this.compressionThreshold == -1 ) + { + this.compressionThreshold = compressionThreshold; + unsafe.sendPacket( new SetCompression( compressionThreshold ) ); + ch.setCompressionThreshold( compressionThreshold ); + } + } } diff --git a/proxy/src/main/java/net/md_5/bungee/conf/YamlConfig.java b/proxy/src/main/java/net/md_5/bungee/conf/YamlConfig.java index 071cddf03..0b3ffb9cf 100644 --- a/proxy/src/main/java/net/md_5/bungee/conf/YamlConfig.java +++ b/proxy/src/main/java/net/md_5/bungee/conf/YamlConfig.java @@ -22,10 +22,6 @@ import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.config.ConfigurationAdapter; import net.md_5.bungee.api.config.ListenerInfo; import net.md_5.bungee.api.config.ServerInfo; -import net.md_5.bungee.api.tab.TabListHandler; -import net.md_5.bungee.tab.Global; -import net.md_5.bungee.tab.GlobalPing; -import net.md_5.bungee.tab.ServerUnique; import net.md_5.bungee.util.CaseInsensitiveMap; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; @@ -40,8 +36,7 @@ public class YamlConfig implements ConfigurationAdapter private enum DefaultTabList { - GLOBAL( Global.class ), GLOBAL_PING( GlobalPing.class ), SERVER( ServerUnique.class ); - private final Class clazz; + GLOBAL(), GLOBAL_PING(), SERVER(); } private final Yaml yaml; private Map config; @@ -224,7 +219,7 @@ public class YamlConfig implements ConfigurationAdapter boolean query = get( "query_enabled", false, val ); int queryPort = get( "query_port", 25577, val ); - ListenerInfo info = new ListenerInfo( address, motd, maxPlayers, tabListSize, defaultServer, fallbackServer, forceDefault, forced, value.clazz, setLocalAddress, pingPassthrough, queryPort, query ); + ListenerInfo info = new ListenerInfo( address, motd, maxPlayers, tabListSize, defaultServer, fallbackServer, forceDefault, forced, value.toString(), setLocalAddress, pingPassthrough, queryPort, query ); ret.add( info ); } diff --git a/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java b/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java index 8a25fcb91..92b865247 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java @@ -31,6 +31,8 @@ import net.md_5.bungee.protocol.packet.ScoreboardScore; import net.md_5.bungee.protocol.packet.ScoreboardDisplay; import net.md_5.bungee.protocol.packet.PluginMessage; import net.md_5.bungee.protocol.packet.Kick; +import net.md_5.bungee.protocol.packet.SetCompression; +import net.md_5.bungee.tab.TabList; @RequiredArgsConstructor public class DownstreamBridge extends PacketHandler @@ -100,11 +102,8 @@ public class DownstreamBridge extends PacketHandler @Override public void handle(PlayerListItem playerList) throws Exception { - - if ( !con.getTabList().onListUpdate( playerList.getUsername(), playerList.isOnline(), playerList.getPing() ) ) - { - throw CancelSendSignal.INSTANCE; - } + con.getTabListHandler().onUpdate( TabList.rewrite( playerList ) ); + throw CancelSendSignal.INSTANCE; // Always throw because of profile rewriting } @Override @@ -448,6 +447,13 @@ public class DownstreamBridge extends PacketHandler throw CancelSendSignal.INSTANCE; } + @Override + public void handle(SetCompression setCompression) throws Exception + { + con.setCompressionThreshold( setCompression.getThreshold() ); + server.getCh().setCompressionThreshold( setCompression.getThreshold() ); + } + @Override public String toString() { diff --git a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java index 9c3dfe66a..ba3c87d03 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java @@ -34,7 +34,7 @@ public class UpstreamBridge extends PacketHandler this.con = con; BungeeCord.getInstance().addConnection( con ); - con.getTabList().onConnect(); + con.getTabListHandler().onConnect(); con.unsafe().sendPacket( BungeeCord.getInstance().registerChannels() ); } @@ -50,7 +50,7 @@ public class UpstreamBridge extends PacketHandler // We lost connection to the client PlayerDisconnectEvent event = new PlayerDisconnectEvent( con ); bungee.getPluginManager().callEvent( event ); - con.getTabList().onDisconnect(); + con.getTabListHandler().onDisconnect(); BungeeCord.getInstance().removeConnection( con ); if ( con.getServer() != null ) @@ -62,10 +62,7 @@ public class UpstreamBridge extends PacketHandler @Override public void handle(PacketWrapper packet) throws Exception { - if ( con.getPendingConnection().getVersion() <= ProtocolConstants.MINECRAFT_1_7_6 ) - { - con.getEntityRewrite().rewriteServerbound( packet.buf, con.getClientEntityId(), con.getServerEntityId() ); - } + con.getEntityRewrite().rewriteServerbound( packet.buf, con.getClientEntityId(), con.getServerEntityId() ); if ( con.getServer() != null ) { con.getServer().getCh().write( packet ); @@ -78,7 +75,7 @@ public class UpstreamBridge extends PacketHandler if ( alive.getRandomId() == con.getSentPingId() ) { int newPing = (int) ( System.currentTimeMillis() - con.getSentPingTime() ); - con.getTabList().onPingChange( newPing ); + con.getTabListHandler().onPingChange( newPing ); con.setPing( newPing ); } } diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java index 978624d87..49fbd223d 100644 --- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java +++ b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java @@ -30,8 +30,8 @@ public abstract class EntityMap return new EntityMap_1_7_2(); case ProtocolConstants.MINECRAFT_1_7_6: return new EntityMap_1_7_6(); - case ProtocolConstants.MINECRAFT_14_11_a: - return new EntityMap_14_11_a(); + case ProtocolConstants.MINECRAFT_SNAPSHOT: + return new EntityMap_14_21_b(); } throw new RuntimeException( "Version " + version + " has no entity map" ); } diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_14_11_a.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_14_21_b.java similarity index 73% rename from proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_14_11_a.java rename to proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_14_21_b.java index da23d62bd..29aa09745 100644 --- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_14_11_a.java +++ b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_14_21_b.java @@ -4,14 +4,15 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.netty.buffer.ByteBuf; import net.md_5.bungee.BungeeCord; import net.md_5.bungee.UserConnection; -import net.md_5.bungee.connection.LoginResult; +import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.protocol.DefinedPacket; import net.md_5.bungee.protocol.ProtocolConstants; +import java.util.UUID; -class EntityMap_14_11_a extends EntityMap +class EntityMap_14_21_b extends EntityMap { - EntityMap_14_11_a() + EntityMap_14_21_b() { addRewrite( 0x04, ProtocolConstants.Direction.TO_CLIENT, true ); // Entity Equipment addRewrite( 0x0A, ProtocolConstants.Direction.TO_CLIENT, true ); // Use bed @@ -38,6 +39,7 @@ class EntityMap_14_11_a extends EntityMap addRewrite( 0x25, ProtocolConstants.Direction.TO_CLIENT, true ); // Block Break Animation addRewrite( 0x2C, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Global Entity addRewrite( 0x43, ProtocolConstants.Direction.TO_CLIENT, true ); // Camera + addRewrite( 0x49, ProtocolConstants.Direction.TO_CLIENT, true ); // Update Entity NBT addRewrite( 0x02, ProtocolConstants.Direction.TO_SERVER, true ); // Use Entity addRewrite( 0x0B, ProtocolConstants.Direction.TO_SERVER, true ); // Entity Action @@ -84,23 +86,24 @@ class EntityMap_14_11_a extends EntityMap } } else if ( packetId == 0x0E /* Spawn Object */ ) { - DefinedPacket.readVarInt( packet ); - int idLength = packet.readerIndex() - readerIndex - packetIdLength; - int type = packet.getByte( readerIndex + packetIdLength + idLength ); + DefinedPacket.readVarInt( packet ); + int type = packet.readUnsignedByte(); if ( type == 60 || type == 90 ) { - int readId = packet.getInt( packetIdLength + idLength + 15 ); + packet.skipBytes( 14 ); + int position = packet.readerIndex(); + int readId = packet.readInt(); int changedId = -1; if ( readId == oldId ) { - packet.setInt( packetIdLength + idLength + 15, newId ); + packet.setInt( position, newId ); changedId = newId; } else if ( readId == newId ) { - packet.setInt( packetIdLength + idLength + 15, oldId ); - changedId = newId; + packet.setInt( position, oldId ); + changedId = oldId; } if ( changedId != -1 ) { @@ -118,36 +121,17 @@ class EntityMap_14_11_a extends EntityMap } } else if ( packetId == 0x0C /* Spawn Player */ ) { - DefinedPacket.readVarInt( packet ); + DefinedPacket.readVarInt( packet ); // Entity ID int idLength = packet.readerIndex() - readerIndex - packetIdLength; - String uuid = DefinedPacket.readString( packet ); - String username = DefinedPacket.readString( packet ); - int props = DefinedPacket.readVarInt( packet ); - if ( props == 0 ) + UUID uuid = DefinedPacket.readUUID( packet ); + ProxiedPlayer player; + if ( ( player = BungeeCord.getInstance().getPlayerByOfflineUUID( uuid ) ) != null ) { - UserConnection player = (UserConnection) BungeeCord.getInstance().getPlayer( username ); - if ( player != null ) - { - LoginResult profile = player.getPendingConnection().getLoginProfile(); - if ( profile != null && profile.getProperties() != null - && profile.getProperties().length >= 1 ) - { - ByteBuf rest = packet.slice().copy(); - packet.readerIndex( readerIndex ); - packet.writerIndex( readerIndex + packetIdLength + idLength ); - DefinedPacket.writeString( player.getUniqueId().toString(), packet ); - DefinedPacket.writeString( username, packet ); - DefinedPacket.writeVarInt( profile.getProperties().length, packet ); - for ( LoginResult.Property property : profile.getProperties() ) - { - DefinedPacket.writeString( property.getName(), packet ); - DefinedPacket.writeString( property.getValue(), packet ); - DefinedPacket.writeString( property.getSignature(), packet ); - } - packet.writeBytes( rest ); - rest.release(); - } - } + int previous = packet.writerIndex(); + packet.readerIndex( readerIndex ); + packet.writerIndex( readerIndex + packetIdLength + idLength ); + DefinedPacket.writeUUID( player.getUniqueId(), packet ); + packet.writerIndex( previous ); } } else if ( packetId == 0x42 /* Combat Event */ ) { @@ -167,4 +151,29 @@ class EntityMap_14_11_a extends EntityMap } packet.readerIndex( readerIndex ); } + + @Override + public void rewriteServerbound(ByteBuf packet, int oldId, int newId) + { + super.rewriteServerbound( packet, oldId, newId ); + //Special cases + int readerIndex = packet.readerIndex(); + int packetId = DefinedPacket.readVarInt( packet ); + int packetIdLength = packet.readerIndex() - readerIndex; + + if ( packetId == 0x18 /* Spectate */ ) + { + UUID uuid = DefinedPacket.readUUID( packet ); + ProxiedPlayer player; + if ( ( player = BungeeCord.getInstance().getPlayer( uuid ) ) != null ) + { + int previous = packet.writerIndex(); + packet.readerIndex( readerIndex ); + packet.writerIndex( readerIndex + packetIdLength ); + DefinedPacket.writeUUID( ( (UserConnection) player ).getPendingConnection().getOfflineId(), packet ); + packet.writerIndex( previous ); + } + } + packet.readerIndex( readerIndex ); + } } diff --git a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java index 3e83ff41e..9106f9374 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java @@ -1,5 +1,7 @@ package net.md_5.bungee.netty; +import net.md_5.bungee.protocol.PacketCompressor; +import net.md_5.bungee.protocol.PacketDecompressor; import net.md_5.bungee.protocol.PacketWrapper; import com.google.common.base.Preconditions; import io.netty.channel.Channel; @@ -71,4 +73,28 @@ public class ChannelWrapper { return ch; } + + public void setCompressionThreshold(int compressionThreshold) + { + if ( ch.pipeline().get( PacketCompressor.class ) == null && compressionThreshold != -1 ) + { + addBefore( PipelineUtils.PACKET_ENCODER, "compress", new PacketCompressor() ); + } + if ( compressionThreshold != -1 ) + { + ch.pipeline().get( PacketCompressor.class ).setThreshold( compressionThreshold ); + } else + { + ch.pipeline().remove( "compress" ); + } + + if ( ch.pipeline().get( PacketDecompressor.class ) == null && compressionThreshold != -1 ) + { + addBefore( PipelineUtils.PACKET_DECODER, "decompress", new PacketDecompressor() ); + } + if ( compressionThreshold == -1 ) + { + ch.pipeline().remove( "decompress" ); + } + } } diff --git a/proxy/src/main/java/net/md_5/bungee/tab/Custom.java b/proxy/src/main/java/net/md_5/bungee/tab/Custom.java deleted file mode 100644 index 40dbd649a..000000000 --- a/proxy/src/main/java/net/md_5/bungee/tab/Custom.java +++ /dev/null @@ -1,158 +0,0 @@ -package net.md_5.bungee.tab; - -import com.google.common.base.Preconditions; -import java.util.Collection; -import java.util.HashSet; -import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.tab.CustomTabList; -import net.md_5.bungee.api.tab.TabListAdapter; -import net.md_5.bungee.protocol.packet.PlayerListItem; - -public class Custom extends TabListAdapter implements CustomTabList -{ - - private static final int ROWS = 20; - private static final int COLUMNS = 3; - private static final char[] FILLER = new char[] - { - '0', '1', '2', '2', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' - }; - private static final int MAX_LEN = 16; - /*========================================================================*/ - private final Collection sentStuff = new HashSet<>(); - /*========================================================================*/ - private final String[][] sent = new String[ ROWS ][ COLUMNS ]; - private final String[][] slots = new String[ ROWS ][ COLUMNS ]; - private int rowLim; - private int colLim; - - public Custom(ProxiedPlayer player) - { - this.init( player ); - } - - @Override - public synchronized String setSlot(int row, int column, String text) - { - return setSlot( row, column, text, true ); - } - - @Override - public synchronized String setSlot(int row, int column, String text, boolean update) - { - Preconditions.checkArgument( row > 0 && row <= ROWS, "row out of range" ); - Preconditions.checkArgument( column > 0 && column <= COLUMNS, "column out of range" ); - - if ( text != null ) - { - Preconditions.checkArgument( text.length() <= MAX_LEN - 2, "text must be <= %s chars", MAX_LEN - 2 ); - Preconditions.checkArgument( !ChatColor.stripColor( text ).isEmpty(), "Text cannot consist entirely of colour codes" ); - text = attempt( text ); - sentStuff.add( text ); - - if ( rowLim < row || colLim < column ) - { - rowLim = row; - colLim = column; - } - } - - slots[--row][--column] = text; - if ( update ) - { - update(); - } - return text; - } - - private String attempt(String s) - { - for ( char c : FILLER ) - { - String attempt = s + Character.toString( ChatColor.COLOR_CHAR ) + c; - if ( !sentStuff.contains( attempt ) ) - { - return attempt; - } - } - if ( s.length() <= MAX_LEN - 4 ) - { - return attempt( s + Character.toString( ChatColor.COLOR_CHAR ) + FILLER[0] ); - } - throw new IllegalArgumentException( "List already contains all variants of string" ); - } - - @Override - public synchronized void update() - { - clear(); - for ( int i = 0; i < rowLim; i++ ) - { - for ( int j = 0; j < colLim; j++ ) - { - String text = ( slots[i][j] != null ) ? slots[i][j] : new StringBuilder().append( base( i ) ).append( base( j ) ).toString(); - sent[i][j] = text; - getPlayer().unsafe().sendPacket( new PlayerListItem( text, true, (short) 0 ) ); - } - } - } - - @Override - public synchronized void clear() - { - for ( int i = 0; i < rowLim; i++ ) - { - for ( int j = 0; j < colLim; j++ ) - { - if ( sent[i][j] != null ) - { - String text = sent[i][j]; - sent[i][j] = null; - getPlayer().unsafe().sendPacket( new PlayerListItem( text, false, (short) 9999 ) ); - } - } - } - } - - @Override - public synchronized int getRows() - { - return ROWS; - } - - @Override - public synchronized int getColumns() - { - return COLUMNS; - } - - @Override - public synchronized int getSize() - { - return ROWS * COLUMNS; - } - - @Override - public boolean onListUpdate(String name, boolean online, int ping) - { - return false; - } - - private static char[] base(int n) - { - String hex = Integer.toHexString( n + 1 ); - char[] alloc = new char[ hex.length() * 2 ]; - for ( int i = 0; i < alloc.length; i++ ) - { - if ( i % 2 == 0 ) - { - alloc[i] = ChatColor.COLOR_CHAR; - } else - { - alloc[i] = hex.charAt( i / 2 ); - } - } - return alloc; - } -} diff --git a/proxy/src/main/java/net/md_5/bungee/tab/Global.java b/proxy/src/main/java/net/md_5/bungee/tab/Global.java index 608513e4e..b04bbe81a 100644 --- a/proxy/src/main/java/net/md_5/bungee/tab/Global.java +++ b/proxy/src/main/java/net/md_5/bungee/tab/Global.java @@ -1,24 +1,30 @@ package net.md_5.bungee.tab; import net.md_5.bungee.BungeeCord; -import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.UserConnection; +import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.tab.TabListAdapter; +import net.md_5.bungee.chat.ComponentSerializer; +import net.md_5.bungee.connection.LoginResult; +import net.md_5.bungee.protocol.ProtocolConstants; import net.md_5.bungee.protocol.packet.PlayerListItem; -public class Global extends TabListAdapter +import java.util.Collection; + +public class Global extends TabList { private boolean sentPing; - @Override - public void onConnect() + public Global(ProxiedPlayer player) { - for ( ProxiedPlayer p : ProxyServer.getInstance().getPlayers() ) - { - getPlayer().unsafe().sendPacket( new PlayerListItem( p.getDisplayName(), true, (short) p.getPing() ) ); - } - BungeeCord.getInstance().broadcast( new PlayerListItem( getPlayer().getDisplayName(), true, (short) getPlayer().getPing() ) ); + super( player ); + } + + @Override + public void onUpdate(PlayerListItem playerListItem) + { + } @Override @@ -27,19 +33,112 @@ public class Global extends TabListAdapter if ( !sentPing ) { sentPing = true; - BungeeCord.getInstance().broadcast( new PlayerListItem( getPlayer().getDisplayName(), true, (short) getPlayer().getPing() ) ); + PlayerListItem packet = new PlayerListItem(); + packet.setAction( PlayerListItem.Action.UPDATE_LATENCY ); + PlayerListItem.Item item = new PlayerListItem.Item(); + item.setUuid( player.getUniqueId() ); + item.setUsername( player.getName() ); + item.setDisplayName( ComponentSerializer.toString( TextComponent.fromLegacyText( player.getDisplayName() ) ) ); + item.setPing( player.getPing() ); + packet.setItems( new PlayerListItem.Item[] + { + item + } ); + BungeeCord.getInstance().broadcast( packet ); } } + @Override + public void onServerChange() + { + + } + + @Override + public void onConnect() + { + PlayerListItem playerListItem = new PlayerListItem(); + playerListItem.setAction( PlayerListItem.Action.ADD_PLAYER ); + Collection players = BungeeCord.getInstance().getPlayers(); + PlayerListItem.Item[] items = new PlayerListItem.Item[ players.size() ]; + playerListItem.setItems( items ); + int i = 0; + for ( ProxiedPlayer p : players ) + { + PlayerListItem.Item item = items[i++] = new PlayerListItem.Item(); + item.setUuid( p.getUniqueId() ); + item.setUsername( p.getName() ); + item.setDisplayName( ComponentSerializer.toString( TextComponent.fromLegacyText( p.getDisplayName() ) ) ); + LoginResult loginResult = ( (UserConnection) p ).getPendingConnection().getLoginProfile(); + String[][] props = new String[ loginResult.getProperties().length ][]; + for ( int j = 0; j < props.length; j++ ) + { + props[ j] = new String[] + { + loginResult.getProperties()[j].getName(), + loginResult.getProperties()[j].getValue(), + loginResult.getProperties()[j].getSignature() + }; + } + item.setProperties( props ); + item.setGamemode( ( (UserConnection) p ).getGamemode() ); + item.setPing( p.getPing() ); + } + if ( player.getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_SNAPSHOT ) + { + player.unsafe().sendPacket( playerListItem ); + } else + { + // Split up the packet + for ( PlayerListItem.Item item : playerListItem.getItems() ) + { + PlayerListItem packet = new PlayerListItem(); + packet.setAction( playerListItem.getAction() ); + PlayerListItem.Item[] it = new PlayerListItem.Item[ 1 ]; + it[0] = item; + packet.setItems( it ); + player.unsafe().sendPacket( packet ); + } + } + PlayerListItem packet = new PlayerListItem(); + packet.setAction( PlayerListItem.Action.ADD_PLAYER ); + PlayerListItem.Item item = new PlayerListItem.Item(); + item.setUuid( player.getUniqueId() ); + item.setUsername( player.getName() ); + item.setDisplayName( ComponentSerializer.toString( TextComponent.fromLegacyText( player.getDisplayName() ) ) ); + LoginResult loginResult = ( (UserConnection) player ).getPendingConnection().getLoginProfile(); + String[][] props = new String[ loginResult.getProperties().length ][]; + for ( int j = 0; j < props.length; j++ ) + { + props[ j] = new String[] + { + loginResult.getProperties()[j].getName(), + loginResult.getProperties()[j].getValue(), + loginResult.getProperties()[j].getSignature() + }; + } + item.setProperties( props ); + item.setGamemode( ( (UserConnection) player ).getGamemode() ); + item.setPing( player.getPing() ); + packet.setItems( new PlayerListItem.Item[] + { + item + } ); + BungeeCord.getInstance().broadcast( packet ); + } + @Override public void onDisconnect() { - BungeeCord.getInstance().broadcast( new PlayerListItem( getPlayer().getDisplayName(), false, (short) 9999 ) ); - } - - @Override - public boolean onListUpdate(String name, boolean online, int ping) - { - return false; + PlayerListItem packet = new PlayerListItem(); + packet.setAction( PlayerListItem.Action.REMOVE_PLAYER ); + PlayerListItem.Item item = new PlayerListItem.Item(); + item.setUuid( player.getUniqueId() ); + item.setUsername( player.getName() ); + packet.setItems( new PlayerListItem.Item[] + { + item + } ); + BungeeCord.getInstance().broadcast( packet ); } } diff --git a/proxy/src/main/java/net/md_5/bungee/tab/GlobalPing.java b/proxy/src/main/java/net/md_5/bungee/tab/GlobalPing.java index da87014be..fb4991c1d 100644 --- a/proxy/src/main/java/net/md_5/bungee/tab/GlobalPing.java +++ b/proxy/src/main/java/net/md_5/bungee/tab/GlobalPing.java @@ -1,6 +1,9 @@ package net.md_5.bungee.tab; import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.chat.ComponentSerializer; import net.md_5.bungee.protocol.packet.PlayerListItem; public class GlobalPing extends Global @@ -10,13 +13,29 @@ public class GlobalPing extends Global /*========================================================================*/ private int lastPing; + public GlobalPing(ProxiedPlayer player) + { + super( player ); + } + @Override public void onPingChange(int ping) { if ( ping - PING_THRESHOLD > lastPing && ping + PING_THRESHOLD < lastPing ) { lastPing = ping; - BungeeCord.getInstance().broadcast( new PlayerListItem( getPlayer().getDisplayName(), true, (short) ping ) ); + PlayerListItem packet = new PlayerListItem(); + packet.setAction( PlayerListItem.Action.UPDATE_LATENCY ); + PlayerListItem.Item item = new PlayerListItem.Item(); + item.setUuid( player.getUniqueId() ); + item.setUsername( player.getName() ); + item.setDisplayName( ComponentSerializer.toString( TextComponent.fromLegacyText( player.getDisplayName() ) ) ); + item.setPing( player.getPing() ); + packet.setItems( new PlayerListItem.Item[] + { + item + } ); + BungeeCord.getInstance().broadcast( packet ); } } } diff --git a/proxy/src/main/java/net/md_5/bungee/tab/ServerUnique.java b/proxy/src/main/java/net/md_5/bungee/tab/ServerUnique.java index 00dc20742..9e31b3f7a 100644 --- a/proxy/src/main/java/net/md_5/bungee/tab/ServerUnique.java +++ b/proxy/src/main/java/net/md_5/bungee/tab/ServerUnique.java @@ -2,38 +2,105 @@ package net.md_5.bungee.tab; import java.util.Collection; import java.util.HashSet; -import net.md_5.bungee.api.tab.TabListAdapter; +import java.util.UUID; + +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.protocol.ProtocolConstants; import net.md_5.bungee.protocol.packet.PlayerListItem; -public class ServerUnique extends TabListAdapter +public class ServerUnique extends TabList { - private final Collection usernames = new HashSet<>(); + private final Collection uuids = new HashSet<>(); + private final Collection usernames = new HashSet<>(); // Support for <=1.7.9 + + public ServerUnique(ProxiedPlayer player) + { + super( player ); + } + + @Override + public void onUpdate(PlayerListItem playerListItem) + { + for ( PlayerListItem.Item item : playerListItem.getItems() ) + { + if ( playerListItem.getAction() == PlayerListItem.Action.ADD_PLAYER ) + { + if ( item.getUuid() != null ) + { + uuids.add( item.getUuid() ); + } else + { + usernames.add( item.getUsername() ); + } + } else if ( playerListItem.getAction() == PlayerListItem.Action.REMOVE_PLAYER ) + { + if ( item.getUuid() != null ) + { + uuids.remove( item.getUuid() ); + } else + { + usernames.remove( item.getUsername() ); + } + } + } + player.unsafe().sendPacket( playerListItem ); + } + + @Override + public void onPingChange(int ping) + { + + } @Override public void onServerChange() { - synchronized ( usernames ) + PlayerListItem packet = new PlayerListItem(); + packet.setAction( PlayerListItem.Action.REMOVE_PLAYER ); + PlayerListItem.Item[] items = new PlayerListItem.Item[ uuids.size() + usernames.size() ]; + int i = 0; + for ( UUID uuid : uuids ) { - for ( String username : usernames ) - { - getPlayer().unsafe().sendPacket( new PlayerListItem( username, false, (short) 9999 ) ); - } - usernames.clear(); + PlayerListItem.Item item = items[i++] = new PlayerListItem.Item(); + item.setUuid( uuid ); } + for ( String username : usernames ) + { + PlayerListItem.Item item = items[i++] = new PlayerListItem.Item(); + item.setUsername( username ); + item.setDisplayName( username ); + } + packet.setItems( items ); + if ( player.getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_SNAPSHOT ) + { + player.unsafe().sendPacket( packet ); + } else + { + // Split up the packet + for ( PlayerListItem.Item item : packet.getItems() ) + { + PlayerListItem p2 = new PlayerListItem(); + p2.setAction( packet.getAction() ); + PlayerListItem.Item[] it = new PlayerListItem.Item[ 1 ]; + it[0] = item; + p2.setItems( it ); + player.unsafe().sendPacket( p2 ); + } + } + uuids.clear(); + usernames.clear(); } @Override - public boolean onListUpdate(String name, boolean online, int ping) + public void onConnect() + { + + } + + @Override + public void onDisconnect() { - if ( online ) - { - usernames.add( name ); - } else - { - usernames.remove( name ); - } - return true; } } diff --git a/proxy/src/main/java/net/md_5/bungee/tab/TabList.java b/proxy/src/main/java/net/md_5/bungee/tab/TabList.java new file mode 100644 index 000000000..e59744536 --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/tab/TabList.java @@ -0,0 +1,59 @@ +package net.md_5.bungee.tab; + +import lombok.RequiredArgsConstructor; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.UserConnection; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.connection.LoginResult; +import net.md_5.bungee.protocol.packet.PlayerListItem; + +@RequiredArgsConstructor +public abstract class TabList +{ + + protected final ProxiedPlayer player; + + public abstract void onUpdate(PlayerListItem playerListItem); + + public abstract void onPingChange(int ping); + + public abstract void onServerChange(); + + public abstract void onConnect(); + + public abstract void onDisconnect(); + + public static PlayerListItem rewrite(PlayerListItem playerListItem) + { + for ( PlayerListItem.Item item : playerListItem.getItems() ) + { + if ( item.getUuid() == null ) // Old style ping + { + continue; + } + UserConnection player = BungeeCord.getInstance().getPlayerByOfflineUUID( item.getUuid() ); + if ( player != null ) + { + item.setUuid( player.getUniqueId() ); + LoginResult loginResult = player.getPendingConnection().getLoginProfile(); + String[][] props = new String[ loginResult.getProperties().length ][]; + for ( int i = 0; i < props.length; i++ ) + { + props[ i] = new String[] + { + loginResult.getProperties()[i].getName(), + loginResult.getProperties()[i].getValue(), + loginResult.getProperties()[i].getSignature() + }; + } + item.setProperties( props ); + if ( playerListItem.getAction() == PlayerListItem.Action.ADD_PLAYER || playerListItem.getAction() == PlayerListItem.Action.UPDATE_GAMEMODE ) + { + player.setGamemode( item.getGamemode() ); + } + player.setPing( player.getPing() ); + } + } + return playerListItem; + } +}