From 5c314c9068814aca404b68cf8442af7201bdcf43 Mon Sep 17 00:00:00 2001 From: Gabik21 Date: Mon, 1 Jul 2019 08:54:34 +0200 Subject: [PATCH] Add 1.7 Protocol Support --- .../api/network/ProtocolVersion.java | 4 +- .../proxy/connection/ConnectionTypes.java | 4 + .../backend/BackendPlaySessionHandler.java | 7 +- .../client/ClientConnectionPhase.java | 3 + .../client/ClientPlaySessionHandler.java | 12 +- .../connection/client/ConnectedPlayer.java | 7 +- .../client/HandshakeSessionHandler.java | 4 + .../client/LoginSessionHandler.java | 3 +- .../LegacyForgeHandshakeBackendPhase.java | 2 +- .../proxy/protocol/ProtocolUtils.java | 98 ++++++++++++- .../proxy/protocol/StateRegistry.java | 55 ++++--- .../proxy/protocol/packet/Chat.java | 4 +- .../proxy/protocol/packet/ClientSettings.java | 11 ++ .../protocol/packet/EncryptionRequest.java | 20 ++- .../protocol/packet/EncryptionResponse.java | 18 ++- .../proxy/protocol/packet/JoinGame.java | 10 +- .../proxy/protocol/packet/KeepAlive.java | 8 +- .../proxy/protocol/packet/PlayerListItem.java | 134 +++++++++++------- .../proxy/protocol/packet/PluginMessage.java | 16 ++- .../protocol/packet/ServerLoginSuccess.java | 7 +- .../protocol/packet/TabCompleteRequest.java | 17 ++- .../protocol/util/PluginMessageUtil.java | 31 ++-- .../proxy/tablist/VelocityTabList.java | 17 ++- .../tablist/VelocityTabListEntryLegacy.java | 20 +++ .../proxy/tablist/VelocityTabListLegacy.java | 118 +++++++++++++++ 25 files changed, 485 insertions(+), 145 deletions(-) create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListEntryLegacy.java create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java diff --git a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java index 7b096f194..941728e22 100644 --- a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java +++ b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java @@ -13,6 +13,8 @@ import java.util.Set; public enum ProtocolVersion { UNKNOWN(-1, "Unknown"), LEGACY(-2, "Legacy"), + MINECRAFT_1_7_2(4, "1.7.2"), + MINECRAFT_1_7_6(5, "1.7.6"), MINECRAFT_1_8(47, "1.8"), MINECRAFT_1_9(107, "1.9"), MINECRAFT_1_9_1(108, "1.9.1"), @@ -38,7 +40,7 @@ public enum ProtocolVersion { /** * Represents the lowest supported version. */ - public static final ProtocolVersion MINIMUM_VERSION = MINECRAFT_1_8; + public static final ProtocolVersion MINIMUM_VERSION = MINECRAFT_1_7_2; /** * Represents the highest supported version. */ diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/ConnectionTypes.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/ConnectionTypes.java index baf97c90f..150ab816e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/ConnectionTypes.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/ConnectionTypes.java @@ -3,6 +3,7 @@ package com.velocitypowered.proxy.connection; import com.velocitypowered.proxy.connection.backend.BackendConnectionPhases; import com.velocitypowered.proxy.connection.client.ClientConnectionPhases; import com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConnectionType; +import com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeHandshakeClientPhase; import com.velocitypowered.proxy.connection.util.ConnectionTypeImpl; /** @@ -24,6 +25,9 @@ public final class ConnectionTypes { public static final ConnectionType VANILLA = new ConnectionTypeImpl(ClientConnectionPhases.VANILLA, BackendConnectionPhases.VANILLA); + public static final ConnectionType UNDETERMINED_17 = new ConnectionTypeImpl( + LegacyForgeHandshakeClientPhase.NOT_STARTED, BackendConnectionPhases.UNKNOWN); + /** * Indicates that the connection is a 1.8-1.12 Forge * connection. diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java index 5128efe9e..2f5d0f106 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java @@ -5,20 +5,17 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.tree.LiteralCommandNode; import com.velocitypowered.api.event.connection.PluginMessageEvent; -import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler; -import com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConstants; import com.velocitypowered.proxy.connection.util.ConnectionMessages; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.packet.AvailableCommands; import com.velocitypowered.proxy.protocol.packet.AvailableCommands.ProtocolSuggestionProvider; import com.velocitypowered.proxy.protocol.packet.BossBar; import com.velocitypowered.proxy.protocol.packet.Disconnect; -import com.velocitypowered.proxy.protocol.packet.JoinGame; import com.velocitypowered.proxy.protocol.packet.KeepAlive; import com.velocitypowered.proxy.protocol.packet.PlayerListItem; import com.velocitypowered.proxy.protocol.packet.PluginMessage; @@ -103,8 +100,8 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { } if (PluginMessageUtil.isMcBrand(packet)) { - PluginMessage rewritten = PluginMessageUtil.rewriteMinecraftBrand(packet, - server.getVersion()); + PluginMessage rewritten = PluginMessageUtil.rewriteMinecraftBrand(packet, server.getVersion(), + playerConnection.getProtocolVersion()); playerConnection.write(rewritten); return true; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConnectionPhase.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConnectionPhase.java index ba45251a7..06c69bfd4 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConnectionPhase.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConnectionPhase.java @@ -1,8 +1,11 @@ package com.velocitypowered.proxy.connection.client; +import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; +import com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConstants; import com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeHandshakeClientPhase; import com.velocitypowered.proxy.protocol.packet.PluginMessage; +import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; /** * Provides connection phase specific actions. diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java index 9593108a8..3d2e3a9f7 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java @@ -1,6 +1,7 @@ package com.velocitypowered.proxy.connection.client; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8; import static com.velocitypowered.proxy.protocol.util.PluginMessageUtil.constructChannelsPacket; import com.velocitypowered.api.event.connection.PluginMessageEvent; @@ -32,11 +33,9 @@ import io.netty.buffer.ByteBuf; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Queue; -import java.util.Set; import java.util.UUID; import net.kyori.text.TextComponent; import net.kyori.text.format.TextColor; @@ -218,7 +217,8 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { player.getKnownChannels().removeAll(PluginMessageUtil.getChannels(packet)); backendConn.write(packet); } else if (PluginMessageUtil.isMcBrand(packet)) { - backendConn.write(PluginMessageUtil.rewriteMinecraftBrand(packet, server.getVersion())); + backendConn.write(PluginMessageUtil + .rewriteMinecraftBrand(packet, server.getVersion(), player.getProtocolVersion())); } else { if (serverConn.getPhase() == BackendConnectionPhases.IN_TRANSITION) { // We must bypass the currently-connected server when forwarding Forge packets. @@ -378,8 +378,10 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { } // Clear any title from the previous server. - player.getMinecraftConnection() - .delayedWrite(TitlePacket.resetForProtocolVersion(player.getProtocolVersion())); + if (player.getProtocolVersion().compareTo(MINECRAFT_1_8) >= 0) { + player.getMinecraftConnection() + .delayedWrite(TitlePacket.resetForProtocolVersion(player.getProtocolVersion())); + } // Flush everything player.getMinecraftConnection().flush(); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java index 7589300fa..878e2fc40 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java @@ -46,6 +46,7 @@ import com.velocitypowered.proxy.protocol.packet.TitlePacket; import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; import com.velocitypowered.proxy.server.VelocityRegisteredServer; import com.velocitypowered.proxy.tablist.VelocityTabList; +import com.velocitypowered.proxy.tablist.VelocityTabListLegacy; import com.velocitypowered.proxy.util.VelocityMessages; import com.velocitypowered.proxy.util.collect.CappedSet; import io.netty.buffer.ByteBufUtil; @@ -102,7 +103,11 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { ConnectedPlayer(VelocityServer server, GameProfile profile, MinecraftConnection minecraftConnection, @Nullable InetSocketAddress virtualHost) { this.server = server; - this.tabList = new VelocityTabList(minecraftConnection); + if (minecraftConnection.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { + this.tabList = new VelocityTabList(minecraftConnection); + } else { + this.tabList = new VelocityTabListLegacy(minecraftConnection); + } this.profile = profile; this.minecraftConnection = minecraftConnection; this.virtualHost = virtualHost; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java index fdc16fb87..1eb25a2ba 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java @@ -114,6 +114,10 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler { if (handshake.getServerAddress().endsWith(LegacyForgeConstants.HANDSHAKE_HOSTNAME_TOKEN) && handshake.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_13) < 0) { return ConnectionTypes.LEGACY_FORGE; + } else if (handshake.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_7_6) <= 0) { + // 1.7 Forge will not notify us during handshake. UNDETERMINED will listen for incoming + // forge handshake attempts. Also sends a reset handshake packet on every transition. + return ConnectionTypes.UNDETERMINED_17; } else { // For later: See if we can determine Forge 1.13+ here, else this will need to be UNDETERMINED // until later in the cycle (most likely determinable during the LOGIN phase) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java index d0707588c..7f85107d7 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java @@ -2,6 +2,7 @@ package com.velocitypowered.proxy.connection.client; import static com.google.common.net.UrlEscapers.urlFormParameterEscaper; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8; import static com.velocitypowered.proxy.VelocityServer.GSON; import static com.velocitypowered.proxy.connection.VelocityConstants.EMPTY_BYTE_ARRAY; import static com.velocitypowered.proxy.connection.VelocityConstants.VELOCITY_IP_FORWARDING_CHANNEL; @@ -249,7 +250,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler { } int threshold = server.getConfiguration().getCompressionThreshold(); - if (threshold >= 0) { + if (threshold >= 0 && mcConnection.getProtocolVersion().compareTo(MINECRAFT_1_8) >= 0) { mcConnection.write(new SetCompression(threshold)); mcConnection.setCompressionThreshold(threshold); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeBackendPhase.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeBackendPhase.java index ad3610231..060525d5e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeBackendPhase.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeBackendPhase.java @@ -90,7 +90,7 @@ public enum LegacyForgeHandshakeBackendPhase implements BackendConnectionPhase { @Nullable private final Integer packetToAdvanceOn; /** - * Creates an instance of the {@link LegacyForgeHandshakeClientPhase}. + * Creates an instance of the {@link LegacyForgeHandshakeBackendPhase}. * * @param packetToAdvanceOn The ID of the packet discriminator that indicates * that the server has moved onto a new phase, and diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java index 907a8640f..ab6dbc75d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java @@ -3,11 +3,11 @@ package com.velocitypowered.proxy.protocol; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; +import com.google.common.base.Preconditions; import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.util.GameProfile; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; - import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -19,6 +19,7 @@ public enum ProtocolUtils { /** * Reads a Minecraft-style VarInt from the specified {@code buf}. + * * @param buf the buffer to read from * @return the decoded VarInt */ @@ -40,6 +41,7 @@ public enum ProtocolUtils { /** * Writes a Minecraft-style VarInt to the specified {@code buf}. + * * @param buf the buffer to read from * @param value the integer to write */ @@ -60,8 +62,9 @@ public enum ProtocolUtils { } /** - * Reads a VarInt length-prefixed string from the {@code buf}, making sure to not go over - * {@code cap} size. + * Reads a VarInt length-prefixed string from the {@code buf}, making sure to not go over {@code + * cap} size. + * * @param buf the buffer to read from * @param cap the maximum size of the string, in UTF-8 character length * @return the decoded string @@ -85,6 +88,7 @@ public enum ProtocolUtils { /** * Writes the specified {@code str} to the {@code buf} with a VarInt prefix. + * * @param buf the buffer to write to * @param str the string to write */ @@ -101,6 +105,7 @@ public enum ProtocolUtils { /** * Reads a VarInt length-prefixed byte array from the {@code buf}, making sure to not go over * {@code cap} size. + * * @param buf the buffer to read from * @param cap the maximum size of the string, in UTF-8 character length * @return the byte array @@ -124,6 +129,7 @@ public enum ProtocolUtils { /** * Reads an VarInt-prefixed array of VarInt integers from the {@code buf}. + * * @param buf the buffer to read from * @return an array of integers */ @@ -139,6 +145,7 @@ public enum ProtocolUtils { /** * Reads an UUID from the {@code buf}. + * * @param buf the buffer to read from * @return the UUID from the buffer */ @@ -155,6 +162,7 @@ public enum ProtocolUtils { /** * Writes a list of {@link com.velocitypowered.api.util.GameProfile.Property} to the buffer. + * * @param buf the buffer to write to * @param properties the properties to serialize */ @@ -175,6 +183,7 @@ public enum ProtocolUtils { /** * Reads a list of {@link com.velocitypowered.api.util.GameProfile.Property} from the buffer. + * * @param buf the buffer to read from * @return the read properties */ @@ -194,12 +203,93 @@ public enum ProtocolUtils { return properties; } + private static final int FORGE_MAX_ARRAY_LENGTH = Integer.MAX_VALUE & 0x1FFF9A; + + /** + * Reads an byte array for legacy version 1.7 from the specified {@code buf} + * + * @param buf the buffer to read from + * @return the read byte array + */ + public static byte[] readByteArray17(ByteBuf buf) { + // Read in a 2 or 3 byte number that represents the length of the packet. (3 byte "shorts" for + // Forge only) + // No vanilla packet should give a 3 byte packet + int len = readExtendedForgeShort(buf); + + Preconditions.checkArgument(len <= (FORGE_MAX_ARRAY_LENGTH), + "Cannot receive array longer than %s (got %s bytes)", FORGE_MAX_ARRAY_LENGTH, len); + + byte[] ret = new byte[len]; + buf.readBytes(ret); + return ret; + } + + /** + * Writes an byte array for legacy version 1.7 to the specified {@code buf} + * + * @param b array + * @param buf buf + * @param allowExtended forge + */ + public static void writeByteArray17(byte[] b, ByteBuf buf, boolean allowExtended) { + if (allowExtended) { + Preconditions + .checkArgument(b.length <= (FORGE_MAX_ARRAY_LENGTH), + "Cannot send array longer than %s (got %s bytes)", FORGE_MAX_ARRAY_LENGTH, + b.length); + } else { + Preconditions.checkArgument(b.length <= Short.MAX_VALUE, + "Cannot send array longer than Short.MAX_VALUE (got %s bytes)", b.length); + } + // Write a 2 or 3 byte number that represents the length of the packet. (3 byte "shorts" for + // Forge only) + // No vanilla packet should give a 3 byte packet, this method will still retain vanilla + // behaviour. + writeExtendedForgeShort(buf, b.length); + buf.writeBytes(b); + } + + /** + * Reads a Minecraft-style extended short from the specified {@code buf}. + * + * @param buf buf to write + * @return read extended short + */ + public static int readExtendedForgeShort(ByteBuf buf) { + int low = buf.readUnsignedShort(); + int high = 0; + if ((low & 0x8000) != 0) { + low = low & 0x7FFF; + high = buf.readUnsignedByte(); + } + return ((high & 0xFF) << 15) | low; + } + + /** + * Writes a Minecraft-style extended short to the specified {@code buf}. + * + * @param buf buf to write + * @param toWrite the extended short to write + */ + public static void writeExtendedForgeShort(ByteBuf buf, int toWrite) { + int low = toWrite & 0x7FFF; + int high = (toWrite & 0x7F8000) >> 15; + if (high != 0) { + low = low | 0x8000; + } + buf.writeShort(low); + if (high != 0) { + buf.writeByte(high); + } + } + public enum Direction { SERVERBOUND, CLIENTBOUND; public StateRegistry.PacketRegistry.ProtocolRegistry getProtocolRegistry(StateRegistry state, - ProtocolVersion version) { + ProtocolVersion version) { return (this == SERVERBOUND ? state.serverbound : state.clientbound) .getProtocolRegistry(version); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java index fd5c268b2..4444722cb 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java @@ -5,6 +5,7 @@ import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_12; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_12_1; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_14; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_7_2; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9_4; @@ -44,14 +45,12 @@ import io.netty.util.collection.IntObjectHashMap; import io.netty.util.collection.IntObjectMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; - import java.util.Collections; import java.util.EnumMap; import java.util.EnumSet; import java.util.Map; import java.util.Objects; import java.util.function.Supplier; - import org.checkerframework.checker.nullness.qual.Nullable; public enum StateRegistry { @@ -59,20 +58,20 @@ public enum StateRegistry { HANDSHAKE { { serverbound.register(Handshake.class, Handshake::new, - map(0x00, MINECRAFT_1_8, false)); + map(0x00, MINECRAFT_1_7_2, false)); } }, STATUS { { serverbound.register(StatusRequest.class, () -> StatusRequest.INSTANCE, - map(0x00, MINECRAFT_1_8, false)); + map(0x00, MINECRAFT_1_7_2, false)); serverbound.register(StatusPing.class, StatusPing::new, - map(0x01, MINECRAFT_1_8, false)); + map(0x01, MINECRAFT_1_7_2, false)); clientbound.register(StatusResponse.class, StatusResponse::new, - map(0x00, MINECRAFT_1_8, false)); + map(0x00, MINECRAFT_1_7_2, false)); clientbound.register(StatusPing.class, StatusPing::new, - map(0x01, MINECRAFT_1_8, false)); + map(0x01, MINECRAFT_1_7_2, false)); } }, PLAY { @@ -81,33 +80,33 @@ public enum StateRegistry { clientbound.fallback = false; serverbound.register(TabCompleteRequest.class, TabCompleteRequest::new, - map(0x14, MINECRAFT_1_8, false), + map(0x14, MINECRAFT_1_7_2, false), map(0x01, MINECRAFT_1_9, false), map(0x02, MINECRAFT_1_12, false), map(0x01, MINECRAFT_1_12_1, false), map(0x05, MINECRAFT_1_13, false), map(0x06, MINECRAFT_1_14, false)); serverbound.register(Chat.class, Chat::new, - map(0x01, MINECRAFT_1_8, false), + map(0x01, MINECRAFT_1_7_2, false), map(0x02, MINECRAFT_1_9, false), map(0x03, MINECRAFT_1_12, false), map(0x02, MINECRAFT_1_12_1, false), map(0x03, MINECRAFT_1_14, false)); serverbound.register(ClientSettings.class, ClientSettings::new, - map(0x15, MINECRAFT_1_8, false), + map(0x15, MINECRAFT_1_7_2, false), map(0x04, MINECRAFT_1_9, false), map(0x05, MINECRAFT_1_12, false), map(0x04, MINECRAFT_1_12_1, false), map(0x05, MINECRAFT_1_14, false)); serverbound.register(PluginMessage.class, PluginMessage::new, - map(0x17, MINECRAFT_1_8, false), + map(0x17, MINECRAFT_1_7_2, false), map(0x09, MINECRAFT_1_9, false), map(0x0A, MINECRAFT_1_12, false), map(0x09, MINECRAFT_1_12_1, false), map(0x0A, MINECRAFT_1_13, false), map(0x0B, MINECRAFT_1_14, false)); serverbound.register(KeepAlive.class, KeepAlive::new, - map(0x00, MINECRAFT_1_8, false), + map(0x00, MINECRAFT_1_7_2, false), map(0x0B, MINECRAFT_1_9, false), map(0x0C, MINECRAFT_1_12, false), map(0x0B, MINECRAFT_1_12_1, false), @@ -123,37 +122,37 @@ public enum StateRegistry { clientbound.register(BossBar.class, BossBar::new, map(0x0C, MINECRAFT_1_9, false)); clientbound.register(Chat.class, Chat::new, - map(0x02, MINECRAFT_1_8, true), + map(0x02, MINECRAFT_1_7_2, true), map(0x0F, MINECRAFT_1_9, true), map(0x0E, MINECRAFT_1_13, true)); clientbound.register(TabCompleteResponse.class, TabCompleteResponse::new, - map(0x3A, MINECRAFT_1_8, false), + map(0x3A, MINECRAFT_1_7_2, false), map(0x0E, MINECRAFT_1_9, false), map(0x10, MINECRAFT_1_13, false)); clientbound.register(AvailableCommands.class, AvailableCommands::new, map(0x11, MINECRAFT_1_13, false)); clientbound.register(PluginMessage.class, PluginMessage::new, - map(0x3F, MINECRAFT_1_8, false), + map(0x3F, MINECRAFT_1_7_2, false), map(0x18, MINECRAFT_1_9, false), map(0x19, MINECRAFT_1_13, false), map(0x18, MINECRAFT_1_14, false)); clientbound.register(Disconnect.class, Disconnect::new, - map(0x40, MINECRAFT_1_8, false), + map(0x40, MINECRAFT_1_7_2, false), map(0x1A, MINECRAFT_1_9, false), map(0x1B, MINECRAFT_1_13, false), map(0x1A, MINECRAFT_1_14, false)); clientbound.register(KeepAlive.class, KeepAlive::new, - map(0x00, MINECRAFT_1_8, false), + map(0x00, MINECRAFT_1_7_2, false), map(0x1F, MINECRAFT_1_9, false), map(0x21, MINECRAFT_1_13, false), map(0x20, MINECRAFT_1_14, false)); clientbound.register(JoinGame.class, JoinGame::new, - map(0x01, MINECRAFT_1_8, false), + map(0x01, MINECRAFT_1_7_2, false), map(0x23, MINECRAFT_1_9, false), map(0x25, MINECRAFT_1_13, false), map(0x25, MINECRAFT_1_14, false)); clientbound.register(Respawn.class, Respawn::new, - map(0x07, MINECRAFT_1_8, true), + map(0x07, MINECRAFT_1_7_2, true), map(0x33, MINECRAFT_1_9, true), map(0x34, MINECRAFT_1_12, true), map(0x35, MINECRAFT_1_12_1, true), @@ -182,7 +181,7 @@ public enum StateRegistry { map(0x4B, MINECRAFT_1_13, true), map(0x4F, MINECRAFT_1_14, true)); clientbound.register(PlayerListItem.class, PlayerListItem::new, - map(0x38, MINECRAFT_1_8, false), + map(0x38, MINECRAFT_1_7_2, false), map(0x2D, MINECRAFT_1_9, false), map(0x2E, MINECRAFT_1_12_1, false), map(0x30, MINECRAFT_1_13, false), @@ -192,17 +191,17 @@ public enum StateRegistry { LOGIN { { serverbound.register(ServerLogin.class, ServerLogin::new, - map(0x00, MINECRAFT_1_8, false)); + map(0x00, MINECRAFT_1_7_2, false)); serverbound.register(EncryptionResponse.class, EncryptionResponse::new, - map(0x01, MINECRAFT_1_8, false)); + map(0x01, MINECRAFT_1_7_2, false)); serverbound.register(LoginPluginResponse.class, LoginPluginResponse::new, map(0x02, MINECRAFT_1_13, false)); clientbound.register(Disconnect.class, Disconnect::new, - map(0x00, MINECRAFT_1_8, false)); + map(0x00, MINECRAFT_1_7_2, false)); clientbound.register(EncryptionRequest.class, EncryptionRequest::new, - map(0x01, MINECRAFT_1_8, false)); + map(0x01, MINECRAFT_1_7_2, false)); clientbound.register(ServerLoginSuccess.class, ServerLoginSuccess::new, - map(0x02, MINECRAFT_1_8, false)); + map(0x02, MINECRAFT_1_7_2, false)); clientbound.register(SetCompression.class, SetCompression::new, map(0x03, MINECRAFT_1_8, false)); clientbound.register(LoginPluginMessage.class, LoginPluginMessage::new, @@ -246,7 +245,7 @@ public enum StateRegistry { }

void register(Class

clazz, Supplier

packetSupplier, - PacketMapping... mappings) { + PacketMapping... mappings) { if (mappings.length == 0) { throw new IllegalArgumentException("At least one mapping must be provided."); } @@ -382,8 +381,8 @@ public enum StateRegistry { /** * Creates a PacketMapping using the provided arguments. * - * @param id Packet Id - * @param version Protocol version + * @param id Packet Id + * @param version Protocol version * @param encodeOnly When true packet decoding will be disabled * @return PacketMapping with the provided arguments */ diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Chat.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Chat.java index c4761a667..2321b518f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Chat.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Chat.java @@ -56,7 +56,7 @@ public class Chat implements MinecraftPacket { @Override public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { message = ProtocolUtils.readString(buf); - if (direction == ProtocolUtils.Direction.CLIENTBOUND) { + if (direction == ProtocolUtils.Direction.CLIENTBOUND && version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { type = buf.readByte(); } } @@ -67,7 +67,7 @@ public class Chat implements MinecraftPacket { throw new IllegalStateException("Message is not specified"); } ProtocolUtils.writeString(buf, message); - if (direction == ProtocolUtils.Direction.CLIENTBOUND) { + if (direction == ProtocolUtils.Direction.CLIENTBOUND && version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { buf.writeByte(type); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientSettings.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientSettings.java index 80db8e997..361b1178e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientSettings.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientSettings.java @@ -13,6 +13,7 @@ public class ClientSettings implements MinecraftPacket { private byte viewDistance; private int chatVisibility; private boolean chatColors; + private byte difficulty; // 1.7 Protocol private short skinParts; private int mainHand; @@ -98,6 +99,11 @@ public class ClientSettings implements MinecraftPacket { this.viewDistance = buf.readByte(); this.chatVisibility = ProtocolUtils.readVarInt(buf); this.chatColors = buf.readBoolean(); + + if (version.compareTo(ProtocolVersion.MINECRAFT_1_7_6) <= 0) { + this.difficulty = buf.readByte(); + } + this.skinParts = buf.readUnsignedByte(); if (version.compareTo(ProtocolVersion.MINECRAFT_1_9) >= 0) { @@ -114,6 +120,11 @@ public class ClientSettings implements MinecraftPacket { buf.writeByte(viewDistance); ProtocolUtils.writeVarInt(buf, chatVisibility); buf.writeBoolean(chatColors); + + if (version.compareTo(ProtocolVersion.MINECRAFT_1_7_6) <= 0) { + buf.writeByte(difficulty); + } + buf.writeByte(skinParts); if (version.compareTo(ProtocolVersion.MINECRAFT_1_9) >= 0) { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionRequest.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionRequest.java index a238226a2..a60165b6e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionRequest.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionRequest.java @@ -42,15 +42,27 @@ public class EncryptionRequest implements MinecraftPacket { @Override public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { this.serverId = ProtocolUtils.readString(buf, 20); - publicKey = ProtocolUtils.readByteArray(buf, 256); - verifyToken = ProtocolUtils.readByteArray(buf, 16); + + if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { + publicKey = ProtocolUtils.readByteArray(buf, 256); + verifyToken = ProtocolUtils.readByteArray(buf, 16); + } else { + publicKey = ProtocolUtils.readByteArray17(buf); + verifyToken = ProtocolUtils.readByteArray17(buf); + } } @Override public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { ProtocolUtils.writeString(buf, this.serverId); - ProtocolUtils.writeByteArray(buf, publicKey); - ProtocolUtils.writeByteArray(buf, verifyToken); + + if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { + ProtocolUtils.writeByteArray(buf, publicKey); + ProtocolUtils.writeByteArray(buf, verifyToken); + } else { + ProtocolUtils.writeByteArray17(publicKey, buf, false); + ProtocolUtils.writeByteArray17(verifyToken, buf, false); + } } @Override diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionResponse.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionResponse.java index 7d64ca4ca..04316a8f6 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionResponse.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionResponse.java @@ -40,14 +40,24 @@ public class EncryptionResponse implements MinecraftPacket { @Override public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - this.sharedSecret = ProtocolUtils.readByteArray(buf, 256); - this.verifyToken = ProtocolUtils.readByteArray(buf, 128); + if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { + this.sharedSecret = ProtocolUtils.readByteArray(buf, 256); + this.verifyToken = ProtocolUtils.readByteArray(buf, 128); + } else { + this.sharedSecret = ProtocolUtils.readByteArray17(buf); + this.verifyToken = ProtocolUtils.readByteArray17(buf); + } } @Override public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - ProtocolUtils.writeByteArray(buf, sharedSecret); - ProtocolUtils.writeByteArray(buf, verifyToken); + if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { + ProtocolUtils.writeByteArray(buf, sharedSecret); + ProtocolUtils.writeByteArray(buf, verifyToken); + } else { + ProtocolUtils.writeByteArray17(sharedSecret, buf, false); + ProtocolUtils.writeByteArray17(verifyToken, buf, false); + } } @Override diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/JoinGame.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/JoinGame.java index af5eb6774..0b542803f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/JoinGame.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/JoinGame.java @@ -116,7 +116,9 @@ public class JoinGame implements MinecraftPacket { if (version.compareTo(ProtocolVersion.MINECRAFT_1_14) >= 0) { this.viewDistance = ProtocolUtils.readVarInt(buf); } - this.reducedDebugInfo = buf.readBoolean(); + if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { + this.reducedDebugInfo = buf.readBoolean(); + } } @Override @@ -137,9 +139,11 @@ public class JoinGame implements MinecraftPacket { } ProtocolUtils.writeString(buf, levelType); if (version.compareTo(ProtocolVersion.MINECRAFT_1_14) >= 0) { - ProtocolUtils.writeVarInt(buf,viewDistance); + ProtocolUtils.writeVarInt(buf, viewDistance); + } + if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { + buf.writeBoolean(reducedDebugInfo); } - buf.writeBoolean(reducedDebugInfo); } @Override diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/KeepAlive.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/KeepAlive.java index ac6810a65..32febe2b6 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/KeepAlive.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/KeepAlive.java @@ -29,8 +29,10 @@ public class KeepAlive implements MinecraftPacket { public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { if (version.compareTo(ProtocolVersion.MINECRAFT_1_12_2) >= 0) { randomId = buf.readLong(); - } else { + } else if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { randomId = ProtocolUtils.readVarInt(buf); + } else { + randomId = buf.readInt(); } } @@ -38,8 +40,10 @@ public class KeepAlive implements MinecraftPacket { public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { if (version.compareTo(ProtocolVersion.MINECRAFT_1_12_2) >= 0) { buf.writeLong(randomId); - } else { + } else if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { ProtocolUtils.writeVarInt(buf, (int) randomId); + } else { + buf.writeInt((int) randomId); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PlayerListItem.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PlayerListItem.java index 4c31d92a4..1037ac765 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PlayerListItem.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PlayerListItem.java @@ -13,6 +13,7 @@ import java.util.List; import java.util.UUID; import net.kyori.text.Component; import net.kyori.text.serializer.gson.GsonComponentSerializer; +import net.kyori.text.serializer.legacy.LegacyComponentSerializer; import org.checkerframework.checker.nullness.qual.Nullable; public class PlayerListItem implements MinecraftPacket { @@ -43,35 +44,43 @@ public class PlayerListItem implements MinecraftPacket { @Override public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - action = ProtocolUtils.readVarInt(buf); - int length = ProtocolUtils.readVarInt(buf); + if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { + action = ProtocolUtils.readVarInt(buf); + int length = ProtocolUtils.readVarInt(buf); - for (int i = 0; i < length; i++) { - Item item = new Item(ProtocolUtils.readUuid(buf)); - items.add(item); - switch (action) { - case ADD_PLAYER: - item.setName(ProtocolUtils.readString(buf)); - item.setProperties(ProtocolUtils.readProperties(buf)); - item.setGameMode(ProtocolUtils.readVarInt(buf)); - item.setLatency(ProtocolUtils.readVarInt(buf)); - item.setDisplayName(readOptionalComponent(buf)); - break; - case UPDATE_GAMEMODE: - item.setGameMode(ProtocolUtils.readVarInt(buf)); - break; - case UPDATE_LATENCY: - item.setLatency(ProtocolUtils.readVarInt(buf)); - break; - case UPDATE_DISPLAY_NAME: - item.setDisplayName(readOptionalComponent(buf)); - break; - case REMOVE_PLAYER: - //Do nothing, all that is needed is the uuid - break; - default: - throw new UnsupportedOperationException("Unknown action " + action); + for (int i = 0; i < length; i++) { + Item item = new Item(ProtocolUtils.readUuid(buf)); + items.add(item); + switch (action) { + case ADD_PLAYER: + item.setName(ProtocolUtils.readString(buf)); + item.setProperties(ProtocolUtils.readProperties(buf)); + item.setGameMode(ProtocolUtils.readVarInt(buf)); + item.setLatency(ProtocolUtils.readVarInt(buf)); + item.setDisplayName(readOptionalComponent(buf)); + break; + case UPDATE_GAMEMODE: + item.setGameMode(ProtocolUtils.readVarInt(buf)); + break; + case UPDATE_LATENCY: + item.setLatency(ProtocolUtils.readVarInt(buf)); + break; + case UPDATE_DISPLAY_NAME: + item.setDisplayName(readOptionalComponent(buf)); + break; + case REMOVE_PLAYER: + //Do nothing, all that is needed is the uuid + break; + default: + throw new UnsupportedOperationException("Unknown action " + action); + } } + } else { + Item item = new Item(); + item.setName(ProtocolUtils.readString(buf)); + action = buf.readBoolean() ? ADD_PLAYER : REMOVE_PLAYER; + item.setLatency(buf.readShort()); + items.add(item); } } @@ -84,34 +93,47 @@ public class PlayerListItem implements MinecraftPacket { @Override public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - ProtocolUtils.writeVarInt(buf, action); - ProtocolUtils.writeVarInt(buf, items.size()); - for (Item item : items) { - ProtocolUtils.writeUuid(buf, item.getUuid()); - switch (action) { - case ADD_PLAYER: - ProtocolUtils.writeString(buf, item.getName()); - ProtocolUtils.writeProperties(buf, item.getProperties()); - ProtocolUtils.writeVarInt(buf, item.getGameMode()); - ProtocolUtils.writeVarInt(buf, item.getLatency()); + if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { + ProtocolUtils.writeVarInt(buf, action); + ProtocolUtils.writeVarInt(buf, items.size()); + for (Item item : items) { + ProtocolUtils.writeUuid(buf, item.getUuid()); + switch (action) { + case ADD_PLAYER: + ProtocolUtils.writeString(buf, item.getName()); + ProtocolUtils.writeProperties(buf, item.getProperties()); + ProtocolUtils.writeVarInt(buf, item.getGameMode()); + ProtocolUtils.writeVarInt(buf, item.getLatency()); - writeDisplayName(buf, item.getDisplayName()); - break; - case UPDATE_GAMEMODE: - ProtocolUtils.writeVarInt(buf, item.getGameMode()); - break; - case UPDATE_LATENCY: - ProtocolUtils.writeVarInt(buf, item.getLatency()); - break; - case UPDATE_DISPLAY_NAME: - writeDisplayName(buf, item.getDisplayName()); - break; - case REMOVE_PLAYER: - //Do nothing, all that is needed is the uuid - break; - default: - throw new UnsupportedOperationException("Unknown action " + action); + writeDisplayName(buf, item.getDisplayName()); + break; + case UPDATE_GAMEMODE: + ProtocolUtils.writeVarInt(buf, item.getGameMode()); + break; + case UPDATE_LATENCY: + ProtocolUtils.writeVarInt(buf, item.getLatency()); + break; + case UPDATE_DISPLAY_NAME: + writeDisplayName(buf, item.getDisplayName()); + break; + case REMOVE_PLAYER: + //Do nothing, all that is needed is the uuid + break; + default: + throw new UnsupportedOperationException("Unknown action " + action); + } } + } else { + Item item = items.get(0); + if (item.getDisplayName() != null) { + String displayName = LegacyComponentSerializer.legacy().serialize(item.getDisplayName()); + ProtocolUtils.writeString(buf, + displayName.length() > 16 ? displayName.substring(0, 16) : displayName); + } else { + ProtocolUtils.writeString(buf, item.getName()); + } + buf.writeBoolean(action != REMOVE_PLAYER); + buf.writeShort(item.getLatency()); } } @@ -136,6 +158,10 @@ public class PlayerListItem implements MinecraftPacket { private int latency; private @Nullable Component displayName; + public Item() { + uuid = null; + } + public Item(UUID uuid) { this.uuid = uuid; } @@ -149,7 +175,7 @@ public class PlayerListItem implements MinecraftPacket { .setDisplayName(entry.getDisplayName().orElse(null)); } - public UUID getUuid() { + public @Nullable UUID getUuid() { return uuid; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PluginMessage.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PluginMessage.java index 7c5045568..487c1e2bc 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PluginMessage.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PluginMessage.java @@ -7,9 +7,7 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; -import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; import org.checkerframework.checker.nullness.qual.Nullable; public class PluginMessage implements MinecraftPacket { @@ -50,8 +48,12 @@ public class PluginMessage implements MinecraftPacket { if (version.compareTo(ProtocolVersion.MINECRAFT_1_13) >= 0) { this.channel = transformLegacyToModernChannel(this.channel); } - this.data = new byte[buf.readableBytes()]; - buf.readBytes(data); + if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { + this.data = new byte[buf.readableBytes()]; + buf.readBytes(data); + } else { + data = ProtocolUtils.readByteArray17(buf); + } } @Override @@ -64,7 +66,11 @@ public class PluginMessage implements MinecraftPacket { } else { ProtocolUtils.writeString(buf, this.channel); } - buf.writeBytes(data); + if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { + buf.writeBytes(data); + } else { + ProtocolUtils.writeByteArray17(data, buf, true); // True for Forge support + } } @Override diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLoginSuccess.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLoginSuccess.java index 0d068bd7a..76b599466 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLoginSuccess.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLoginSuccess.java @@ -1,6 +1,7 @@ package com.velocitypowered.proxy.protocol.packet; import com.velocitypowered.api.network.ProtocolVersion; +import com.velocitypowered.api.util.UuidUtils; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; @@ -45,7 +46,11 @@ public class ServerLoginSuccess implements MinecraftPacket { @Override public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - uuid = UUID.fromString(ProtocolUtils.readString(buf, 36)); + if (version.compareTo(ProtocolVersion.MINECRAFT_1_7_6) >= 0) { + uuid = UUID.fromString(ProtocolUtils.readString(buf, 36)); + } else { + uuid = UuidUtils.fromUndashed(ProtocolUtils.readString(buf, 32)); + } username = ProtocolUtils.readString(buf, 16); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteRequest.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteRequest.java index 27afacaf9..e382861fe 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteRequest.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteRequest.java @@ -1,6 +1,7 @@ package com.velocitypowered.proxy.protocol.packet; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9; import com.google.common.base.MoreObjects; @@ -83,9 +84,11 @@ public class TabCompleteRequest implements MinecraftPacket { if (version.compareTo(MINECRAFT_1_9) >= 0) { this.assumeCommand = buf.readBoolean(); } - this.hasPosition = buf.readBoolean(); - if (hasPosition) { - this.position = buf.readLong(); + if (version.compareTo(MINECRAFT_1_8) >= 0) { + this.hasPosition = buf.readBoolean(); + if (hasPosition) { + this.position = buf.readLong(); + } } } } @@ -104,9 +107,11 @@ public class TabCompleteRequest implements MinecraftPacket { if (version.compareTo(MINECRAFT_1_9) >= 0) { buf.writeBoolean(assumeCommand); } - buf.writeBoolean(hasPosition); - if (hasPosition) { - buf.writeLong(position); + if (version.compareTo(MINECRAFT_1_8) >= 0) { + buf.writeBoolean(hasPosition); + if (hasPosition) { + buf.writeLong(position); + } } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java index 9fef82a1f..d58ee95a7 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java @@ -126,27 +126,34 @@ public class PluginMessageUtil { * @param version the proxy version * @return the rewritten plugin message */ - public static PluginMessage rewriteMinecraftBrand(PluginMessage message, ProxyVersion version) { + public static PluginMessage rewriteMinecraftBrand(PluginMessage message, ProxyVersion version, + ProtocolVersion protocolVersion) { checkNotNull(message, "message"); checkNotNull(version, "version"); checkArgument(isMcBrand(message), "message is not a brand plugin message"); String toAppend = " (" + version.getName() + ")"; - byte[] rewrittenData; - ByteBuf rewrittenBuf = Unpooled.buffer(); - try { - String currentBrand = ProtocolUtils.readString(Unpooled.wrappedBuffer(message.getData())); - ProtocolUtils.writeString(rewrittenBuf, currentBrand + toAppend); - rewrittenData = new byte[rewrittenBuf.readableBytes()]; - rewrittenBuf.readBytes(rewrittenData); - } finally { - rewrittenBuf.release(); - } - PluginMessage newMsg = new PluginMessage(); newMsg.setChannel(message.getChannel()); + + byte[] rewrittenData; + if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { + ByteBuf rewrittenBuf = Unpooled.buffer(); + try { + String currentBrand = ProtocolUtils.readString(Unpooled.wrappedBuffer(message.getData())); + ProtocolUtils.writeString(rewrittenBuf, currentBrand + toAppend); + rewrittenData = new byte[rewrittenBuf.readableBytes()]; + rewrittenBuf.readBytes(rewrittenData); + } finally { + rewrittenBuf.release(); + } + } else { + String currentBrand = new String(message.getData(), StandardCharsets.UTF_8); + rewrittenData = (currentBrand + toAppend).getBytes(); + } newMsg.setData(rewrittenData); + return newMsg; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java index aacf26012..09d8c01d5 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java @@ -7,6 +7,7 @@ import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.protocol.packet.HeaderAndFooter; import com.velocitypowered.proxy.protocol.packet.PlayerListItem; +import com.velocitypowered.proxy.protocol.packet.PlayerListItem.Item; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -20,8 +21,8 @@ import org.checkerframework.checker.nullness.qual.Nullable; public class VelocityTabList implements TabList { - private final MinecraftConnection connection; - private final Map entries = new ConcurrentHashMap<>(); + protected final MinecraftConnection connection; + protected final Map entries = new ConcurrentHashMap<>(); public VelocityTabList(MinecraftConnection connection) { this.connection = connection; @@ -76,9 +77,9 @@ public class VelocityTabList implements TabList { } /** - * Clears all entries from the tab list. Note that the entries are written with - * {@link MinecraftConnection#delayedWrite(Object)}, so make sure to do an explicit - * {@link MinecraftConnection#flush()}. + * Clears all entries from the tab list. Note that the entries are written with {@link + * MinecraftConnection#delayedWrite(Object)}, so make sure to do an explicit {@link + * MinecraftConnection#flush()}. */ public void clearAll() { List items = new ArrayList<>(); @@ -86,7 +87,9 @@ public class VelocityTabList implements TabList { items.add(PlayerListItem.Item.from(value)); } entries.clear(); - connection.delayedWrite(new PlayerListItem(PlayerListItem.REMOVE_PLAYER, items)); + if (!items.isEmpty()) { + connection.delayedWrite(new PlayerListItem(PlayerListItem.REMOVE_PLAYER, items)); + } } @Override @@ -102,12 +105,14 @@ public class VelocityTabList implements TabList { /** * Processes a tab list entry packet from the backend. + * * @param packet the packet to process */ public void processBackendPacket(PlayerListItem packet) { // Packets are already forwarded on, so no need to do that here for (PlayerListItem.Item item : packet.getItems()) { UUID uuid = item.getUuid(); + if (packet.getAction() != PlayerListItem.ADD_PLAYER && !entries.containsKey(uuid)) { // Sometimes UPDATE_GAMEMODE is sent before ADD_PLAYER so don't want to warn here continue; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListEntryLegacy.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListEntryLegacy.java new file mode 100644 index 000000000..7ef7b5dd7 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListEntryLegacy.java @@ -0,0 +1,20 @@ +package com.velocitypowered.proxy.tablist; + +import com.velocitypowered.api.proxy.player.TabListEntry; +import com.velocitypowered.api.util.GameProfile; +import net.kyori.text.Component; +import org.checkerframework.checker.nullness.qual.Nullable; + +public class VelocityTabListEntryLegacy extends VelocityTabListEntry { + + VelocityTabListEntryLegacy(VelocityTabListLegacy tabList, GameProfile profile, + @Nullable Component displayName, int latency, int gameMode) { + super(tabList, profile, displayName, latency, gameMode); + } + + @Override + public TabListEntry setDisplayName(@Nullable Component displayName) { + getTabList().removeEntry(getProfile().getId()); // We have to remove first if updating + return super.setDisplayName(displayName); + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java new file mode 100644 index 000000000..31b494545 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java @@ -0,0 +1,118 @@ +package com.velocitypowered.proxy.tablist; + +import com.google.common.collect.ImmutableList; +import com.velocitypowered.api.proxy.player.TabListEntry; +import com.velocitypowered.api.util.GameProfile; +import com.velocitypowered.proxy.connection.MinecraftConnection; +import com.velocitypowered.proxy.protocol.packet.PlayerListItem; +import com.velocitypowered.proxy.protocol.packet.PlayerListItem.Item; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import net.kyori.text.Component; +import net.kyori.text.serializer.legacy.LegacyComponentSerializer; +import net.kyori.text.serializer.plain.PlainComponentSerializer; +import org.checkerframework.checker.nullness.qual.Nullable; + +public class VelocityTabListLegacy extends VelocityTabList { + + private final Map nameMapping = new ConcurrentHashMap<>(); + + public VelocityTabListLegacy(MinecraftConnection connection) { + super(connection); + } + + @Override + public void setHeaderAndFooter(Component header, Component footer) { + } + + @Override + public void clearHeaderAndFooter() { + } + + @Override + public void addEntry(TabListEntry entry) { + super.addEntry(entry); + nameMapping.put(entry.getProfile().getName(), entry.getProfile().getId()); + } + + @Override + public Optional removeEntry(UUID uuid) { + Optional entry = super.removeEntry(uuid); + entry.map(TabListEntry::getProfile).map(GameProfile::getName).ifPresent(nameMapping::remove); + return entry; + } + + @Override + public void clearAll() { + for (TabListEntry value : entries.values()) { + connection.delayedWrite(new PlayerListItem(PlayerListItem.REMOVE_PLAYER, + Collections.singletonList(PlayerListItem.Item.from(value)))); + } + entries.clear(); + } + + @Override + public void processBackendPacket(PlayerListItem packet) { + + Item item = packet.getItems().get(0); // Only one item per packet in 1.7 + + Component displayName = LegacyComponentSerializer.legacy().deserialize(item.getName()); + String strippedName = PlainComponentSerializer.INSTANCE.serialize(displayName); + + switch (packet.getAction()) { + case PlayerListItem.ADD_PLAYER: + if (nameMapping.containsKey(strippedName)) { // ADD_PLAYER also used for updating ping + VelocityTabListEntry entry = entries.get(nameMapping.get(strippedName)); + if (entry != null) { + entry.setLatency(item.getLatency()); + } + } else { + UUID uuid = UUID.randomUUID(); // Use a fake uuid to preserve function of custom entries + nameMapping.put(strippedName, uuid); + entries.put(uuid, (VelocityTabListEntry) TabListEntry.builder() + .tabList(this) + .profile(new GameProfile(uuid, strippedName, ImmutableList.of())) + .displayName(displayName) + .latency(item.getLatency()) + .build()); + } + break; + case PlayerListItem.REMOVE_PLAYER: + UUID removedUuid = nameMapping.remove(strippedName); + if (removedUuid != null) { + entries.remove(removedUuid); + } + break; + default: + // For 1.7 there is only add and remove + break; + } + + } + + @Override + void updateEntry(int action, TabListEntry entry) { + if (entries.containsKey(entry.getProfile().getId())) { + switch (action) { + case PlayerListItem.UPDATE_LATENCY: + case PlayerListItem.UPDATE_DISPLAY_NAME: // Add here because we removed beforehand + connection + .write(new PlayerListItem(PlayerListItem.ADD_PLAYER, // ADD_PLAYER also updates ping + Collections.singletonList(PlayerListItem.Item.from(entry)))); + break; + default: + // Can't do anything else + break; + } + } + } + + @Override + public TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency, + int gameMode) { + return new VelocityTabListEntryLegacy(this, profile, displayName, latency, gameMode); + } +}