From 6b5cd703f04815eab226f369c342adb4fb692631 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 30 Jul 2018 20:35:05 -0400 Subject: [PATCH] Initial remapping support, fix protocol crap. --- .../backend/BackendPlaySessionHandler.java | 10 ++++-- .../client/ClientPlaySessionHandler.java | 12 ++++++- .../proxy/protocol/StateRegistry.java | 4 +-- .../protocol/packets/ScoreboardSetScore.java | 4 +-- .../protocol/remap/DummyEntityIdRemapper.java | 32 +++++++++++++++++++ .../protocol/remap/EntityIdRemapper.java | 29 +++++++++++++++++ .../proxy/protocol/PacketRegistryTest.java | 23 ++++++++++--- 7 files changed, 101 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/velocitypowered/proxy/protocol/remap/DummyEntityIdRemapper.java create mode 100644 src/main/java/com/velocitypowered/proxy/protocol/remap/EntityIdRemapper.java 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 46ef748aa..efe2655ea 100644 --- a/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java +++ b/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java @@ -2,6 +2,7 @@ package com.velocitypowered.proxy.connection.backend; import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.packets.*; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; @@ -46,7 +47,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { PluginMessage pm = (PluginMessage) packet; try { PluginMessage newPacket = pm; - if (!canForwardMessage(newPacket)) { + if (!canForwardPluginMessage(newPacket)) { return; } @@ -72,7 +73,10 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { @Override public void handleUnknown(ByteBuf buf) { - connection.getProxyPlayer().getConnection().write(buf.retain()); + ClientPlaySessionHandler playerHandler = + (ClientPlaySessionHandler) connection.getProxyPlayer().getConnection().getSessionHandler(); + ByteBuf remapped = playerHandler.getIdRemapper().remap(buf, ProtocolConstants.Direction.CLIENTBOUND); + connection.getProxyPlayer().getConnection().write(remapped); } @Override @@ -80,7 +84,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { connection.getProxyPlayer().handleConnectionException(connection.getServerInfo(), throwable); } - private boolean canForwardMessage(PluginMessage message) { + private boolean canForwardPluginMessage(PluginMessage message) { // TODO: Update for 1.13 ClientPlaySessionHandler playerHandler = (ClientPlaySessionHandler) connection.getProxyPlayer().getConnection().getSessionHandler(); 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 66bfd4075..125ac5dd0 100644 --- a/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java +++ b/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java @@ -8,8 +8,10 @@ import com.velocitypowered.proxy.data.scoreboard.Score; import com.velocitypowered.proxy.data.scoreboard.Scoreboard; import com.velocitypowered.proxy.data.scoreboard.Team; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.packets.*; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; +import com.velocitypowered.proxy.protocol.remap.EntityIdRemapper; import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; import com.velocitypowered.proxy.util.ThrowableUtils; import io.netty.buffer.ByteBuf; @@ -39,6 +41,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { private PluginMessage brandMessage; private int currentDimension; private Scoreboard serverScoreboard = new Scoreboard(); + private EntityIdRemapper idRemapper; public ClientPlaySessionHandler(ConnectedPlayer player) { this.player = player; @@ -95,7 +98,8 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { @Override public void handleUnknown(ByteBuf buf) { - player.getConnectedServer().getChannel().write(buf.retain()); + ByteBuf remapped = idRemapper.remap(buf, ProtocolConstants.Direction.SERVERBOUND); + player.getConnectedServer().getChannel().write(remapped); } @Override @@ -123,6 +127,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { spawned = true; currentDimension = joinGame.getDimension(); player.getConnection().delayedWrite(joinGame); + idRemapper = EntityIdRemapper.getMapper(joinGame.getEntityId(), player.getConnection().getProtocolVersion()); } else { // In order to handle switching to another server we will need send three packets: // - The join game packet from the backend server @@ -130,6 +135,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { // - Another respawn with the correct dimension // We can't simply ignore the packet with the different dimension. If you try to be smart about it it doesn't // work. + idRemapper.setServerEntityId(joinGame.getEntityId()); player.getConnection().delayedWrite(joinGame); int tempDim = joinGame.getDimension() == 0 ? -1 : 0; player.getConnection().delayedWrite(new Respawn(tempDim, joinGame.getDifficulty(), joinGame.getGamemode(), joinGame.getLevelType())); @@ -304,4 +310,8 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { public Set getClientPluginMsgChannels() { return clientPluginMsgChannels; } + + public EntityIdRemapper getIdRemapper() { + return idRemapper; + } } diff --git a/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java b/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java index f318af64e..899057807 100644 --- a/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java +++ b/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java @@ -107,8 +107,7 @@ public enum StateRegistry { private static final IntObjectMap LINKED_PROTOCOL_VERSIONS = new IntObjectHashMap<>(); static { - LINKED_PROTOCOL_VERSIONS.put(MINECRAFT_1_10, new int[] { MINECRAFT_1_11 }); - LINKED_PROTOCOL_VERSIONS.put(MINECRAFT_1_11, new int[] { MINECRAFT_1_11_1, MINECRAFT_1_12 }); + LINKED_PROTOCOL_VERSIONS.put(MINECRAFT_1_10, new int[] { MINECRAFT_1_11, MINECRAFT_1_11_1, MINECRAFT_1_12 }); 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 }); } @@ -142,6 +141,7 @@ public enum StateRegistry { if (version == null) { throw new IllegalArgumentException("Unknown protocol version " + mapping.protocolVersion); } + version.packetIdToSupplier.put(mapping.id, packetSupplier); version.packetClassToId.put(clazz, mapping.id); diff --git a/src/main/java/com/velocitypowered/proxy/protocol/packets/ScoreboardSetScore.java b/src/main/java/com/velocitypowered/proxy/protocol/packets/ScoreboardSetScore.java index 8c553245d..9c561e126 100644 --- a/src/main/java/com/velocitypowered/proxy/protocol/packets/ScoreboardSetScore.java +++ b/src/main/java/com/velocitypowered/proxy/protocol/packets/ScoreboardSetScore.java @@ -58,7 +58,7 @@ public class ScoreboardSetScore implements MinecraftPacket { this.entity = ProtocolUtils.readString(buf, 40); this.action = buf.readByte(); this.objective = ProtocolUtils.readString(buf, 16); - if (this.action == 0) { + if (this.action != 1) { value = ProtocolUtils.readVarInt(buf); } } @@ -68,7 +68,7 @@ public class ScoreboardSetScore implements MinecraftPacket { ProtocolUtils.writeString(buf, entity); buf.writeByte(action); ProtocolUtils.writeString(buf, objective); - if (this.action == 0) { + if (this.action != 1) { ProtocolUtils.writeVarInt(buf, value); } } diff --git a/src/main/java/com/velocitypowered/proxy/protocol/remap/DummyEntityIdRemapper.java b/src/main/java/com/velocitypowered/proxy/protocol/remap/DummyEntityIdRemapper.java new file mode 100644 index 000000000..bd60ff333 --- /dev/null +++ b/src/main/java/com/velocitypowered/proxy/protocol/remap/DummyEntityIdRemapper.java @@ -0,0 +1,32 @@ +package com.velocitypowered.proxy.protocol.remap; + +import com.velocitypowered.proxy.protocol.ProtocolConstants; +import io.netty.buffer.ByteBuf; + +public class DummyEntityIdRemapper implements EntityIdRemapper { + public static final DummyEntityIdRemapper INSTANCE = new DummyEntityIdRemapper(); + + private DummyEntityIdRemapper() { + + } + + @Override + public ByteBuf remap(ByteBuf original, ProtocolConstants.Direction direction) { + return original.retain(); + } + + @Override + public int getClientEntityId() { + return 0; + } + + @Override + public int getServerEntityId() { + return 0; + } + + @Override + public void setServerEntityId(int id) { + + } +} diff --git a/src/main/java/com/velocitypowered/proxy/protocol/remap/EntityIdRemapper.java b/src/main/java/com/velocitypowered/proxy/protocol/remap/EntityIdRemapper.java new file mode 100644 index 000000000..8ba085948 --- /dev/null +++ b/src/main/java/com/velocitypowered/proxy/protocol/remap/EntityIdRemapper.java @@ -0,0 +1,29 @@ +package com.velocitypowered.proxy.protocol.remap; + +import com.velocitypowered.proxy.protocol.ProtocolConstants; +import io.netty.buffer.ByteBuf; + +/** + * Represents a protocol-specific entity ID remapper for certain Minecraft packets. This is mostly required to support + * old versions of Minecraft. For Minecraft 1.9 clients and above, Velocity can use a more efficient method based on + * sending JoinGame packets multiple times. + */ +public interface EntityIdRemapper { + /** + * Remaps the entity IDs in this packet so that they apply to the player. + * @param original the packet to remap + * @param direction the direction of the packet + * @return a remapped packet, which may either be a retained version of the original buffer or an entirely new buffer + */ + ByteBuf remap(ByteBuf original, ProtocolConstants.Direction direction); + + int getClientEntityId(); + + int getServerEntityId(); + + void setServerEntityId(int id); + + static EntityIdRemapper getMapper(int eid, int protocolVersion) { + return DummyEntityIdRemapper.INSTANCE; + } +} diff --git a/src/test/java/com/velocitypowered/proxy/protocol/PacketRegistryTest.java b/src/test/java/com/velocitypowered/proxy/protocol/PacketRegistryTest.java index d1f33dfb8..e6abe22d3 100644 --- a/src/test/java/com/velocitypowered/proxy/protocol/PacketRegistryTest.java +++ b/src/test/java/com/velocitypowered/proxy/protocol/PacketRegistryTest.java @@ -4,32 +4,35 @@ import com.velocitypowered.proxy.protocol.packets.Handshake; import com.velocitypowered.proxy.protocol.packets.KeepAlive; import org.junit.jupiter.api.Test; +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; import static org.junit.jupiter.api.Assertions.*; class PacketRegistryTest { private StateRegistry.PacketRegistry setupRegistry() { StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry(ProtocolConstants.Direction.CLIENTBOUND); - registry.register(Handshake.class, Handshake::new, new StateRegistry.PacketMapping(0x00, ProtocolConstants.MINECRAFT_1_12)); + registry.register(Handshake.class, Handshake::new, new StateRegistry.PacketMapping(0x00, MINECRAFT_1_12)); return registry; } @Test void packetRegistryWorks() { StateRegistry.PacketRegistry registry = setupRegistry(); - MinecraftPacket packet = registry.getVersion(ProtocolConstants.MINECRAFT_1_12).createPacket(0); + MinecraftPacket packet = registry.getVersion(MINECRAFT_1_12).createPacket(0); assertNotNull(packet, "Packet was not found in registry"); assertEquals(Handshake.class, packet.getClass(), "Registry returned wrong class"); - assertEquals(0, registry.getVersion(ProtocolConstants.MINECRAFT_1_12).getPacketId(packet), "Registry did not return the correct packet ID"); + assertEquals(0, registry.getVersion(MINECRAFT_1_12).getPacketId(packet), "Registry did not return the correct packet ID"); } @Test void packetRegistryLinkingWorks() { StateRegistry.PacketRegistry registry = setupRegistry(); - MinecraftPacket packet = registry.getVersion(ProtocolConstants.MINECRAFT_1_12_1).createPacket(0); + MinecraftPacket packet = registry.getVersion(MINECRAFT_1_12_1).createPacket(0); assertNotNull(packet, "Packet was not found in registry"); assertEquals(Handshake.class, packet.getClass(), "Registry returned wrong class"); - assertEquals(0, registry.getVersion(ProtocolConstants.MINECRAFT_1_12_1).getPacketId(packet), "Registry did not return the correct packet ID"); + assertEquals(0, registry.getVersion(MINECRAFT_1_12_1).getPacketId(packet), "Registry did not return the correct packet ID"); } @Test @@ -38,4 +41,14 @@ class PacketRegistryTest { assertThrows(IllegalArgumentException.class, () -> registry.register(Handshake.class, Handshake::new)); assertThrows(IllegalArgumentException.class, () -> registry.getVersion(0).getPacketId(new Handshake())); } + + @Test + void registrySuppliesCorrectPacketsByProtocol() { + StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry(ProtocolConstants.Direction.CLIENTBOUND); + registry.register(Handshake.class, Handshake::new, new StateRegistry.PacketMapping(0x00, MINECRAFT_1_12), + new StateRegistry.PacketMapping(0x01, MINECRAFT_1_12_1)); + assertEquals(Handshake.class, registry.getVersion(MINECRAFT_1_12).createPacket(0x00).getClass()); + assertEquals(Handshake.class, registry.getVersion(MINECRAFT_1_12_1).createPacket(0x01).getClass()); + assertEquals(Handshake.class, registry.getVersion(MINECRAFT_1_12_2).createPacket(0x01).getClass()); + } } \ No newline at end of file