From b79e7df039c80b69a68d106b3567c353db791b87 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 29 Jul 2018 17:09:27 -0400 Subject: [PATCH] Initial 1.12/1.12.1 support. Bug fixes! --- .../backend/BackendPlaySessionHandler.java | 4 +- .../client/ClientPlaySessionHandler.java | 22 +++-- .../client/StatusSessionHandler.java | 12 +-- .../proxy/protocol/ProtocolConstants.java | 10 ++- .../proxy/protocol/StateRegistry.java | 85 ++++++++++++------- .../protocol/netty/MinecraftDecoder.java | 2 + .../protocol/netty/MinecraftEncoder.java | 2 + .../proxy/protocol/packets/KeepAlive.java | 45 ++++++++++ .../packets/{Ping.java => StatusPing.java} | 19 +---- 9 files changed, 133 insertions(+), 68 deletions(-) create mode 100644 src/main/java/com/velocitypowered/proxy/protocol/packets/KeepAlive.java rename src/main/java/com/velocitypowered/proxy/protocol/packets/{Ping.java => StatusPing.java} (61%) diff --git a/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java b/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java index 02825718..1ee9006e 100644 --- a/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java +++ b/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java @@ -4,7 +4,7 @@ import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.packets.Disconnect; import com.velocitypowered.proxy.protocol.packets.JoinGame; -import com.velocitypowered.proxy.protocol.packets.Ping; +import com.velocitypowered.proxy.protocol.packets.KeepAlive; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.packets.Respawn; import io.netty.buffer.ByteBuf; @@ -18,7 +18,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { @Override public void handle(MinecraftPacket packet) { - if (packet instanceof Ping) { + if (packet instanceof KeepAlive) { // Forward onto the server connection.getChannel().write(packet); } else if (packet instanceof Disconnect) { diff --git a/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java b/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java index 60180018..cf23f7bf 100644 --- a/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java +++ b/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java @@ -6,11 +6,13 @@ import com.velocitypowered.proxy.data.ServerInfo; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.packets.Chat; import com.velocitypowered.proxy.protocol.packets.JoinGame; -import com.velocitypowered.proxy.protocol.packets.Ping; +import com.velocitypowered.proxy.protocol.packets.KeepAlive; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.packets.Respawn; import io.netty.buffer.ByteBuf; import io.netty.channel.EventLoop; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import java.net.InetSocketAddress; import java.util.concurrent.ScheduledFuture; @@ -18,6 +20,8 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; public class ClientPlaySessionHandler implements MinecraftSessionHandler { + private static final Logger logger = LogManager.getLogger(ClientPlaySessionHandler.class); + private final ConnectedPlayer player; private ScheduledFuture pingTask; private long lastPing = -1; @@ -35,19 +39,19 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { } private void ping() { - long randomId = ThreadLocalRandom.current().nextLong(); + long randomId = ThreadLocalRandom.current().nextInt(); lastPing = randomId; - Ping ping = new Ping(); - ping.setRandomId(randomId); - player.getConnection().write(ping); + KeepAlive keepAlive = new KeepAlive(); + keepAlive.setRandomId(randomId); + player.getConnection().write(keepAlive); } @Override public void handle(MinecraftPacket packet) { - if (packet instanceof Ping) { - Ping ping = (Ping) packet; - if (ping.getRandomId() != lastPing) { - throw new IllegalStateException("Client sent invalid ping; expected " + lastPing + ", got " + ping.getRandomId()); + if (packet instanceof KeepAlive) { + KeepAlive keepAlive = (KeepAlive) packet; + if (keepAlive.getRandomId() != lastPing) { + throw new IllegalStateException("Client sent invalid keepAlive; expected " + lastPing + ", got " + keepAlive.getRandomId()); } // Do not forward the packet to the player's server, because we handle pings for all servers already. diff --git a/src/main/java/com/velocitypowered/proxy/connection/client/StatusSessionHandler.java b/src/main/java/com/velocitypowered/proxy/connection/client/StatusSessionHandler.java index 1cc6a9f0..bced97e7 100644 --- a/src/main/java/com/velocitypowered/proxy/connection/client/StatusSessionHandler.java +++ b/src/main/java/com/velocitypowered/proxy/connection/client/StatusSessionHandler.java @@ -1,20 +1,16 @@ package com.velocitypowered.proxy.connection.client; import com.google.common.base.Preconditions; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.config.VelocityConfiguration; import com.velocitypowered.proxy.protocol.MinecraftPacket; -import com.velocitypowered.proxy.protocol.packets.Ping; +import com.velocitypowered.proxy.protocol.packets.KeepAlive; +import com.velocitypowered.proxy.protocol.packets.StatusPing; import com.velocitypowered.proxy.protocol.packets.StatusRequest; import com.velocitypowered.proxy.protocol.packets.StatusResponse; import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.data.ServerPing; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; -import net.kyori.text.Component; -import net.kyori.text.TextComponent; -import net.kyori.text.serializer.GsonComponentSerializer; public class StatusSessionHandler implements MinecraftSessionHandler { private final MinecraftConnection connection; @@ -25,10 +21,10 @@ public class StatusSessionHandler implements MinecraftSessionHandler { @Override public void handle(MinecraftPacket packet) { - Preconditions.checkArgument(packet instanceof Ping || packet instanceof StatusRequest, + Preconditions.checkArgument(packet instanceof StatusPing|| packet instanceof StatusRequest, "Unrecognized packet type " + packet.getClass().getName()); - if (packet instanceof Ping) { + if (packet instanceof StatusPing) { // Just send back the client's packet, no processing to do here. connection.closeWith(packet); return; diff --git a/src/main/java/com/velocitypowered/proxy/protocol/ProtocolConstants.java b/src/main/java/com/velocitypowered/proxy/protocol/ProtocolConstants.java index 768c17fb..9c99def6 100644 --- a/src/main/java/com/velocitypowered/proxy/protocol/ProtocolConstants.java +++ b/src/main/java/com/velocitypowered/proxy/protocol/ProtocolConstants.java @@ -3,10 +3,14 @@ package com.velocitypowered.proxy.protocol; import java.util.Arrays; public enum ProtocolConstants { ; - public static final int MINECRAFT_1_12 = 340; + public static final int MINECRAFT_1_12 = 335; + public static final int MINECRAFT_1_12_1 = 338; + public static final int MINECRAFT_1_12_2 = 340; - private static final int[] SUPPORTED_VERSIONS = new int[] { - MINECRAFT_1_12 + public static final int[] SUPPORTED_VERSIONS = new int[] { + MINECRAFT_1_12, + MINECRAFT_1_12_1, + MINECRAFT_1_12_2 }; public static boolean isSupported(int version) { diff --git a/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java b/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java index d4e52fe9..540db697 100644 --- a/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java +++ b/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java @@ -4,67 +4,70 @@ import com.velocitypowered.proxy.protocol.packets.*; import io.netty.util.collection.IntObjectHashMap; import io.netty.util.collection.IntObjectMap; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.function.Supplier; import static com.velocitypowered.proxy.protocol.ProtocolConstants.MINECRAFT_1_12; +import static com.velocitypowered.proxy.protocol.ProtocolConstants.MINECRAFT_1_12_1; +import static com.velocitypowered.proxy.protocol.ProtocolConstants.MINECRAFT_1_12_2; public enum StateRegistry { HANDSHAKE { { SERVERBOUND.register(Handshake.class, Handshake::new, - generic(0x00)); + lowestVersion(0x00)); } }, STATUS { { SERVERBOUND.register(StatusRequest.class, StatusRequest::new, - generic(0x00)); - SERVERBOUND.register(Ping.class, Ping::new, - generic(0x01)); + lowestVersion(0x00)); + SERVERBOUND.register(StatusPing.class, StatusPing::new, + lowestVersion(0x01)); CLIENTBOUND.register(StatusResponse.class, StatusResponse::new, - generic(0x00)); - CLIENTBOUND.register(Ping.class, Ping::new, - generic(0x01)); + lowestVersion(0x00)); + CLIENTBOUND.register(StatusPing.class, StatusPing::new, + lowestVersion(0x01)); } }, PLAY { { SERVERBOUND.register(Chat.class, Chat::new, - map(0x02, MINECRAFT_1_12)); - SERVERBOUND.register(Ping.class, Ping::new, - map(0x0b, MINECRAFT_1_12)); + map(0x03, MINECRAFT_1_12), + map(0x02, MINECRAFT_1_12_2)); + SERVERBOUND.register(KeepAlive.class, KeepAlive::new, + map(0x0C, MINECRAFT_1_12), + map(0x0B, MINECRAFT_1_12_1)); CLIENTBOUND.register(Chat.class, Chat::new, map(0x0F, MINECRAFT_1_12)); CLIENTBOUND.register(Disconnect.class, Disconnect::new, map(0x1A, MINECRAFT_1_12)); - CLIENTBOUND.register(Ping.class, Ping::new, + CLIENTBOUND.register(KeepAlive.class, KeepAlive::new, map(0x1F, MINECRAFT_1_12)); CLIENTBOUND.register(JoinGame.class, JoinGame::new, map(0x23, MINECRAFT_1_12)); CLIENTBOUND.register(Respawn.class, Respawn::new, - map(0x35, MINECRAFT_1_12)); + map(0x34, MINECRAFT_1_12), + map(0x35, MINECRAFT_1_12_2)); } }, LOGIN { { SERVERBOUND.register(ServerLogin.class, ServerLogin::new, - generic(0x00)); + lowestVersion(0x00)); SERVERBOUND.register(EncryptionResponse.class, EncryptionResponse::new, - generic(0x01)); + lowestVersion(0x01)); CLIENTBOUND.register(Disconnect.class, Disconnect::new, - generic(0x00)); + lowestVersion(0x00)); CLIENTBOUND.register(EncryptionRequest.class, EncryptionRequest::new, - generic(0x01)); + lowestVersion(0x01)); CLIENTBOUND.register(ServerLoginSuccess.class, ServerLoginSuccess::new, - generic(0x02)); + lowestVersion(0x02)); CLIENTBOUND.register(SetCompression.class, SetCompression::new, - generic(0x03)); + lowestVersion(0x03)); } }; @@ -72,20 +75,25 @@ public enum StateRegistry { public final PacketRegistry SERVERBOUND = new PacketRegistry(ProtocolConstants.Direction.SERVERBOUND); public static class PacketRegistry { + private static final IntObjectMap LINKED_PROTOCOL_VERSIONS = new IntObjectHashMap<>(); + + static { + LINKED_PROTOCOL_VERSIONS.put(MINECRAFT_1_12, new int[] { MINECRAFT_1_12_1 }); + LINKED_PROTOCOL_VERSIONS.put(MINECRAFT_1_12_1, new int[] { MINECRAFT_1_12_2 }); + } + private final ProtocolConstants.Direction direction; private final IntObjectMap versions = new IntObjectHashMap<>(); public PacketRegistry(ProtocolConstants.Direction direction) { this.direction = direction; + for (int version : ProtocolConstants.SUPPORTED_VERSIONS) { + versions.put(version, new ProtocolVersion(version)); + } } public ProtocolVersion getVersion(final int version) { - ProtocolVersion result = null; - for (final IntObjectMap.PrimitiveEntry entry : this.versions.entries()) { - if (entry.key() <= version) { - result = entry.value(); - } - } + ProtocolVersion result = versions.get(version); if (result == null) { throw new IllegalArgumentException("Could not find data for protocol version " + version); } @@ -105,6 +113,17 @@ public enum StateRegistry { } version.packetIdToSupplier.put(mapping.id, packetSupplier); version.packetClassToId.put(clazz, mapping.id); + + int[] linked = LINKED_PROTOCOL_VERSIONS.get(mapping.protocolVersion); + if (linked != null) { + links: for (int i : linked) { + // Make sure that later mappings override this one. + for (PacketMapping m : mappings) { + if (i == m.protocolVersion) continue links; + } + register(clazz, packetSupplier, map(mapping.id, i)); + } + } } } @@ -135,6 +154,14 @@ public enum StateRegistry { } return id; } + + @Override + public String toString() { + return "ProtocolVersion{" + + "id=" + id + + ", packetClassToId=" + packetClassToId + + '}'; + } } } @@ -174,7 +201,7 @@ public enum StateRegistry { return new PacketMapping(id, version); } - private static PacketMapping generic(int id) { - return new PacketMapping(id, 0); + private static PacketMapping lowestVersion(int id) { + return new PacketMapping(id, MINECRAFT_1_12); } } diff --git a/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java b/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java index df493a77..625e68d5 100644 --- a/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java +++ b/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java @@ -17,6 +17,7 @@ public class MinecraftDecoder extends MessageToMessageDecoder { public MinecraftDecoder(ProtocolConstants.Direction direction) { this.state = StateRegistry.HANDSHAKE; this.direction = Preconditions.checkNotNull(direction, "direction"); + this.setProtocolVersion(ProtocolConstants.MINECRAFT_1_12); } @Override @@ -57,6 +58,7 @@ public class MinecraftDecoder extends MessageToMessageDecoder { public void setState(StateRegistry state) { this.state = state; + this.setProtocolVersion(protocolVersion.id); } public ProtocolConstants.Direction getDirection() { diff --git a/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftEncoder.java b/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftEncoder.java index 7cc6e346..83350461 100644 --- a/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftEncoder.java +++ b/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftEncoder.java @@ -17,6 +17,7 @@ public class MinecraftEncoder extends MessageToByteEncoder { public MinecraftEncoder(ProtocolConstants.Direction direction) { this.state = StateRegistry.HANDSHAKE; this.direction = Preconditions.checkNotNull(direction, "direction"); + this.setProtocolVersion(ProtocolConstants.MINECRAFT_1_12); } @Override @@ -40,6 +41,7 @@ public class MinecraftEncoder extends MessageToByteEncoder { public void setState(StateRegistry state) { this.state = state; + this.setProtocolVersion(protocolVersion.id); } public ProtocolConstants.Direction getDirection() { diff --git a/src/main/java/com/velocitypowered/proxy/protocol/packets/KeepAlive.java b/src/main/java/com/velocitypowered/proxy/protocol/packets/KeepAlive.java new file mode 100644 index 00000000..9197c269 --- /dev/null +++ b/src/main/java/com/velocitypowered/proxy/protocol/packets/KeepAlive.java @@ -0,0 +1,45 @@ +package com.velocitypowered.proxy.protocol.packets; + +import com.velocitypowered.proxy.protocol.ProtocolConstants; +import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolUtils; +import io.netty.buffer.ByteBuf; + +import static com.velocitypowered.proxy.protocol.ProtocolConstants.MINECRAFT_1_12_2; + +public class KeepAlive implements MinecraftPacket { + private long randomId; + + public long getRandomId() { + return randomId; + } + + public void setRandomId(long randomId) { + this.randomId = randomId; + } + + @Override + public String toString() { + return "KeepAlive{" + + "randomId=" + randomId + + '}'; + } + + @Override + public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { + if (protocolVersion >= MINECRAFT_1_12_2) { + randomId = buf.readLong(); + } else { + randomId = ProtocolUtils.readVarInt(buf); + } + } + + @Override + public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { + if (protocolVersion >= MINECRAFT_1_12_2) { + buf.writeLong(randomId); + } else { + ProtocolUtils.writeVarInt(buf, (int) randomId); + } + } +} diff --git a/src/main/java/com/velocitypowered/proxy/protocol/packets/Ping.java b/src/main/java/com/velocitypowered/proxy/protocol/packets/StatusPing.java similarity index 61% rename from src/main/java/com/velocitypowered/proxy/protocol/packets/Ping.java rename to src/main/java/com/velocitypowered/proxy/protocol/packets/StatusPing.java index c1a3ad3e..5d55e002 100644 --- a/src/main/java/com/velocitypowered/proxy/protocol/packets/Ping.java +++ b/src/main/java/com/velocitypowered/proxy/protocol/packets/StatusPing.java @@ -1,27 +1,12 @@ package com.velocitypowered.proxy.protocol.packets; -import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolConstants; import io.netty.buffer.ByteBuf; -public class Ping implements MinecraftPacket { +public class StatusPing implements MinecraftPacket { private long randomId; - public long getRandomId() { - return randomId; - } - - public void setRandomId(long randomId) { - this.randomId = randomId; - } - - @Override - public String toString() { - return "Ping{" + - "randomId=" + randomId + - '}'; - } - @Override public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { randomId = buf.readLong();