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 b6b656e0..b04d728c 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 1559cf55..162136c6 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 423ece5d..a3408748 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 9d60144a..00000000 --- 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 3c046136..00000000 --- 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 4b147a4a..00000000 --- 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 2203489d..5e7a04e0 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 bfce4011..647cc974 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 00000000..216a92d3 --- /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 00000000..5496a604 --- /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 fe1ae22b..d1b01981 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 f0613f54..34392510 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 8fe3e0b8..504b563d 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 4521f6c3..ca4e735d 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 443c7a5b..eaa34b6a 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 c9156723..a15591db 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 ff41f7b9..31df7963 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 e1770537..ea12d6e1 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 57d4efea..690ed71c 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 1e850e8d..f9fd5ddf 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 219a6306..4d5cd5da 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 00000000..086e3e6e --- /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 3bedbd5e..c685083a 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 792302c6..fab4a903 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 64ecdd88..eebeca08 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 17e90daf..e0da10ee 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 2160175e..b85b9cd9 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 071cddf0..0b3ffb9c 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 8a25fcb9..92b86524 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 9c3dfe66..ba3c87d0 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 978624d8..49fbd223 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 da23d62b..29aa0974 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 3e83ff41..9106f937 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 40dbd649..00000000 --- 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 608513e4..b04bbe81 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 da87014b..fb4991c1 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 00dc2074..9e31b3f7 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 00000000..e5974453 --- /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; + } +}