From 9d5930d96b6baca44ee6c7177009d6731966ae06 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Thu, 26 Sep 2019 01:04:38 -0400 Subject: [PATCH 1/3] Integrate BungeeQuack functionality into Velocity --- .../backend/BackendPlaySessionHandler.java | 8 + .../backend/BungeeCordMessageResponder.java | 325 ++++++++++++++++++ .../backend/VelocityServerConnection.java | 7 +- .../connection/client/ConnectedPlayer.java | 8 + .../proxy/protocol/util/ByteBufDataInput.java | 110 ++++++ .../protocol/util/ByteBufDataOutput.java | 105 ++++++ .../server/VelocityRegisteredServer.java | 10 +- 7 files changed, 571 insertions(+), 2 deletions(-) create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ByteBufDataInput.java create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ByteBufDataOutput.java 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 ba6678b78..dbedce09a 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 @@ -31,6 +31,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { private final VelocityServerConnection serverConn; private final ClientPlaySessionHandler playerSessionHandler; private final MinecraftConnection playerConnection; + private final BungeeCordMessageResponder bungeecordMessageResponder; private boolean exceptionTriggered = false; BackendPlaySessionHandler(VelocityServer server, VelocityServerConnection serverConn) { @@ -44,6 +45,9 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { "Initializing BackendPlaySessionHandler with no backing client play session handler!"); } this.playerSessionHandler = (ClientPlaySessionHandler) psh; + + this.bungeecordMessageResponder = new BungeeCordMessageResponder(server, + serverConn.getPlayer()); } @Override @@ -86,6 +90,10 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { @Override public boolean handle(PluginMessage packet) { + if (bungeecordMessageResponder.process(packet)) { + return true; + } + if (!serverConn.getPlayer().canForwardPluginMessage(serverConn.ensureConnected() .getProtocolVersion(), packet)) { return true; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java new file mode 100644 index 000000000..45c697628 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java @@ -0,0 +1,325 @@ +package com.velocitypowered.proxy.connection.backend; + +import com.velocitypowered.api.network.ProtocolVersion; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.ServerConnection; +import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier; +import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; +import com.velocitypowered.api.proxy.server.RegisteredServer; +import com.velocitypowered.api.util.UuidUtils; +import com.velocitypowered.proxy.VelocityServer; +import com.velocitypowered.proxy.connection.MinecraftConnection; +import com.velocitypowered.proxy.connection.client.ConnectedPlayer; +import com.velocitypowered.proxy.protocol.packet.PluginMessage; +import com.velocitypowered.proxy.protocol.util.ByteBufDataInput; +import com.velocitypowered.proxy.protocol.util.ByteBufDataOutput; +import com.velocitypowered.proxy.server.VelocityRegisteredServer; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; +import java.util.StringJoiner; +import java.util.stream.Collectors; +import net.kyori.text.serializer.legacy.LegacyComponentSerializer; + +class BungeeCordMessageResponder { + + private static final MinecraftChannelIdentifier MODERN_CHANNEL = MinecraftChannelIdentifier + .create("bungeecord", "main"); + private static final LegacyChannelIdentifier LEGACY_CHANNEL = + new LegacyChannelIdentifier("BungeeCord"); + + private final VelocityServer proxy; + private final ConnectedPlayer player; + + BungeeCordMessageResponder(VelocityServer proxy, ConnectedPlayer player) { + this.proxy = proxy; + this.player = player; + } + + private void processConnect(ByteBufDataInput in) { + String serverName = in.readUTF(); + proxy.getServer(serverName).ifPresent(server -> player.createConnectionRequest(server) + .fireAndForget()); + } + + private void processConnectOther(ByteBufDataInput in) { + String playerName = in.readUTF(); + String serverName = in.readUTF(); + + proxy.getPlayer(playerName).ifPresent(player -> { + proxy.getServer(serverName).ifPresent(server -> player.createConnectionRequest(server) + .fireAndForget()); + }); + } + + private void processIp(ByteBufDataInput in) { + ByteBuf buf = Unpooled.buffer(); + ByteBufDataOutput out = new ByteBufDataOutput(buf); + out.writeUTF("IP"); + out.writeUTF(player.getRemoteAddress().getHostString()); + out.writeInt(player.getRemoteAddress().getPort()); + sendResponse(buf); + } + + private void processPlayerCount(ByteBufDataInput in) { + ByteBuf buf = Unpooled.buffer(); + ByteBufDataOutput out = new ByteBufDataOutput(buf); + + String target = in.readUTF(); + if (target.equals("ALL")) { + out.writeUTF("PlayerCount"); + out.writeUTF("ALL"); + out.writeInt(proxy.getPlayerCount()); + } else { + proxy.getServer(target).ifPresent(rs -> { + int playersOnServer = rs.getPlayersConnected().size(); + out.writeUTF("PlayerCount"); + out.writeUTF(rs.getServerInfo().getName()); + out.writeInt(playersOnServer); + }); + } + + if (buf.isReadable()) { + sendResponse(buf); + } else { + buf.release(); + } + } + + private void processPlayerList(ByteBufDataInput in) { + ByteBuf buf = Unpooled.buffer(); + ByteBufDataOutput out = new ByteBufDataOutput(buf); + + String target = in.readUTF(); + if (target.equals("ALL")) { + out.writeUTF("PlayerList"); + out.writeUTF("ALL"); + + StringJoiner joiner = new StringJoiner(", "); + for (Player online : proxy.getAllPlayers()) { + joiner.add(online.getUsername()); + } + out.writeUTF(joiner.toString()); + } else { + proxy.getServer(target).ifPresent(info -> { + out.writeUTF("PlayerList"); + out.writeUTF(info.getServerInfo().getName()); + + StringJoiner joiner = new StringJoiner(", "); + for (Player online : info.getPlayersConnected()) { + joiner.add(online.getUsername()); + } + out.writeUTF(joiner.toString()); + }); + } + + if (buf.isReadable()) { + sendResponse(buf); + } else { + buf.release(); + } + } + + private void processGetServers(ByteBufDataInput in) { + StringJoiner joiner = new StringJoiner(", "); + for (RegisteredServer server : proxy.getAllServers()) { + joiner.add(server.getServerInfo().getName()); + } + + ByteBuf buf = Unpooled.buffer(); + ByteBufDataOutput out = new ByteBufDataOutput(buf); + out.writeUTF("GetServers"); + out.writeUTF(joiner.toString()); + + sendResponse(buf); + } + + private void processMessage(ByteBufDataInput in) { + String target = in.readUTF(); + String message = in.readUTF(); + if (target.equals("ALL")) { + for (Player player : proxy.getAllPlayers()) { + player.sendMessage(LegacyComponentSerializer.INSTANCE.deserialize(message)); + } + } else { + proxy.getPlayer(target).ifPresent(player -> { + player.sendMessage(LegacyComponentSerializer.INSTANCE.deserialize(message)); + }); + } + } + + private void processGetServer(ByteBufDataInput in) { + ByteBuf buf = Unpooled.buffer(); + ByteBufDataOutput out = new ByteBufDataOutput(buf); + + out.writeUTF("GetServer"); + out.writeUTF(player.ensureAndGetCurrentServer().getServerInfo().getName()); + + sendResponse(buf); + } + + private void processUuid(ByteBufDataInput in) { + ByteBuf buf = Unpooled.buffer(); + ByteBufDataOutput out = new ByteBufDataOutput(buf); + + out.writeUTF("UUID"); + out.writeUTF(UuidUtils.toUndashed(player.getUniqueId())); + + sendResponse(buf); + } + + private void processUuidOther(ByteBufDataInput in) { + proxy.getPlayer(in.readUTF()).ifPresent(player -> { + ByteBuf buf = Unpooled.buffer(); + ByteBufDataOutput out = new ByteBufDataOutput(buf); + + out.writeUTF("UUIDOther"); + out.writeUTF(player.getUsername()); + out.writeUTF(UuidUtils.toUndashed(player.getUniqueId())); + + sendResponse(buf); + }); + } + + private void processServerIp(ByteBufDataInput in) { + proxy.getServer(in.readUTF()).ifPresent(info -> { + ByteBuf buf = Unpooled.buffer(); + ByteBufDataOutput out = new ByteBufDataOutput(buf); + + out.writeUTF("ServerIP"); + out.writeUTF(info.getServerInfo().getName()); + out.writeUTF(info.getServerInfo().getAddress().getHostString()); + out.writeShort(info.getServerInfo().getAddress().getPort()); + + sendResponse(buf); + }); + } + + private void processKick(ByteBufDataInput in) { + proxy.getPlayer(in.readUTF()).ifPresent(player -> { + String kickReason = in.readUTF(); + player.disconnect(LegacyComponentSerializer.INSTANCE.deserialize(kickReason)); + }); + } + + private ByteBuf prepareForwardMessage(ByteBufDataInput in) { + String channel = in.readUTF(); + short messageLength = in.readShort(); + + ByteBuf buf = Unpooled.buffer(); + ByteBufDataOutput forwarded = new ByteBufDataOutput(buf); + forwarded.writeUTF(channel); + forwarded.writeShort(messageLength); + buf.writeBytes(in.unwrap().readSlice(messageLength)); + return buf; + } + + private void processForwardToPlayer(ByteBufDataInput in) { + proxy.getPlayer(in.readUTF()) + .flatMap(Player::getCurrentServer) + .ifPresent(server -> sendResponse(player, prepareForwardMessage(in))); + } + + private void processForwardToServer(ByteBufDataInput in) { + ByteBuf toForward = prepareForwardMessage(in); + String target = in.readUTF(); + if (target.equals("ALL")) { + ByteBuf unreleasableForward = Unpooled.unreleasableBuffer(toForward); + try { + for (RegisteredServer rs : proxy.getAllServers()) { + ((VelocityRegisteredServer) rs).sendPluginMessage(LEGACY_CHANNEL, unreleasableForward); + } + } finally { + toForward.release(); + } + } else { + proxy.getServer(target).ifPresent(rs -> ((VelocityRegisteredServer) rs) + .sendPluginMessage(LEGACY_CHANNEL, toForward)); + } + } + + // Note: this method will always release the buffer! + private void sendResponse(ByteBuf buf) { + sendResponse(this.player, buf); + } + + // Note: this method will always release the buffer! + private static void sendResponse(ConnectedPlayer player, ByteBuf buf) { + MinecraftConnection serverConnection = player.ensureAndGetCurrentServer().ensureConnected(); + String chan = serverConnection.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_13) + >= 0 ? MODERN_CHANNEL.getId() : LEGACY_CHANNEL.getId(); + + PluginMessage msg = null; + boolean released = false; + + try { + VelocityServerConnection vsc = player.getConnectedServer(); + if (vsc == null) { + return; + } + + MinecraftConnection serverConn = vsc.ensureConnected(); + msg = new PluginMessage(chan, buf); + serverConn.write(msg); + released = true; + } finally { + if (!released && msg != null) { + msg.release(); + } + } + } + + public boolean process(PluginMessage message) { + if (!MODERN_CHANNEL.getId().equals(message.getChannel()) && !LEGACY_CHANNEL.getId() + .equals(message.getChannel())) { + return false; + } + + ByteBufDataInput in = new ByteBufDataInput(message.content()); + String subChannel = in.readUTF(); + if (subChannel.equals("ForwardToPlayer")) { + this.processForwardToPlayer(in); + } + if (subChannel.equals("Forward")) { + this.processForwardToServer(in); + } + if (subChannel.equals("Connect")) { + this.processConnect(in); + } + if (subChannel.equals("ConnectOther")) { + this.processConnectOther(in); + } + if (subChannel.equals("IP")) { + this.processIp(in); + } + if (subChannel.equals("PlayerCount")) { + this.processPlayerCount(in); + } + if (subChannel.equals("PlayerList")) { + this.processPlayerList(in); + } + if (subChannel.equals("GetServers")) { + this.processGetServers(in); + } + if (subChannel.equals("Message")) { + this.processMessage(in); + } + if (subChannel.equals("GetServer")) { + this.processGetServer(in); + } + if (subChannel.equals("UUID")) { + this.processUuid(in); + } + if (subChannel.equals("UUIDOther")) { + this.processUuidOther(in); + } + if (subChannel.equals("ServerIP")) { + this.processServerIp(in); + } + if (subChannel.equals("KickPlayer")) { + this.processKick(in); + } + + return true; + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java index da9806e16..9f0903469 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java @@ -34,6 +34,7 @@ import com.velocitypowered.proxy.protocol.packet.Handshake; import com.velocitypowered.proxy.protocol.packet.PluginMessage; import com.velocitypowered.proxy.protocol.packet.ServerLogin; import com.velocitypowered.proxy.server.VelocityRegisteredServer; +import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFutureListener; @@ -210,12 +211,16 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, @Override public boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data) { + return sendPluginMessage(identifier, Unpooled.wrappedBuffer(data)); + } + + public boolean sendPluginMessage(ChannelIdentifier identifier, ByteBuf data) { Preconditions.checkNotNull(identifier, "identifier"); Preconditions.checkNotNull(data, "data"); MinecraftConnection mc = ensureConnected(); - PluginMessage message = new PluginMessage(identifier.getId(), Unpooled.wrappedBuffer(data)); + PluginMessage message = new PluginMessage(identifier.getId(), data); mc.write(message); return true; } 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 c012e8fcb..0483ce352 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 @@ -135,6 +135,14 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { return Optional.ofNullable(connectedServer); } + public VelocityServerConnection ensureAndGetCurrentServer() { + VelocityServerConnection con = this.connectedServer; + if (con == null) { + throw new IllegalStateException("Not connected to server!"); + } + return con; + } + @Override public GameProfile getGameProfile() { return profile; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ByteBufDataInput.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ByteBufDataInput.java new file mode 100644 index 000000000..43b6d44aa --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ByteBufDataInput.java @@ -0,0 +1,110 @@ +package com.velocitypowered.proxy.protocol.util; + +import com.google.common.io.ByteArrayDataInput; +import io.netty.buffer.ByteBuf; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.IOException; + +/** + * A wrapper around {@link io.netty.buffer.ByteBuf} that implements the exception-free + * {@link ByteArrayDataInput} interface from Guava. + */ +public class ByteBufDataInput implements ByteArrayDataInput, DataInput { + + private final ByteBuf in; + + /** + * Creates a new ByteBufDataInput instance. The ByteBufDataInput simply "borrows" the ByteBuf + * while it is in use. + * + * @param buf the buffer to read from + */ + public ByteBufDataInput(ByteBuf buf) { + this.in = buf; + } + + public ByteBuf unwrap() { + return in; + } + + @Override + public void readFully(byte[] b) { + in.readBytes(b); + } + + @Override + public void readFully(byte[] b, int off, int len) { + in.readBytes(b, off, len); + } + + @Override + public int skipBytes(int n) { + in.skipBytes(n); + return n; + } + + @Override + public boolean readBoolean() { + return in.readBoolean(); + } + + @Override + public byte readByte() { + return in.readByte(); + } + + @Override + public int readUnsignedByte() { + return in.readUnsignedByte() & 0xFF; + } + + @Override + public short readShort() { + return in.readShort(); + } + + @Override + public int readUnsignedShort() { + return in.readUnsignedShort(); + } + + @Override + public char readChar() { + return in.readChar(); + } + + @Override + public int readInt() { + return in.readInt(); + } + + @Override + public long readLong() { + return in.readLong(); + } + + @Override + public float readFloat() { + return in.readFloat(); + } + + @Override + public double readDouble() { + return in.readDouble(); + } + + @Override + public String readLine() { + throw new UnsupportedOperationException(); + } + + @Override + public String readUTF() { + try { + return DataInputStream.readUTF(this); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ByteBufDataOutput.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ByteBufDataOutput.java new file mode 100644 index 000000000..b894525ab --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ByteBufDataOutput.java @@ -0,0 +1,105 @@ +package com.velocitypowered.proxy.protocol.util; + +import com.google.common.io.ByteArrayDataOutput; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +/** + * A {@link DataOutput} equivalent to {@link ByteBufDataInput}. + */ +public class ByteBufDataOutput extends OutputStream implements DataOutput, ByteArrayDataOutput { + + private final ByteBuf buf; + private final DataOutputStream utf8out; + + public ByteBufDataOutput(ByteBuf buf) { + this.buf = buf; + this.utf8out = new DataOutputStream(this); + } + + @Override + public byte[] toByteArray() { + return ByteBufUtil.getBytes(buf); + } + + @Override + public void write(int b) { + buf.writeByte(b); + } + + @Override + public void write(byte[] b) { + buf.writeBytes(b); + } + + @Override + public void write(byte[] b, int off, int len) { + buf.writeBytes(b, off, len); + } + + @Override + public void writeBoolean(boolean v) { + buf.writeBoolean(v); + } + + @Override + public void writeByte(int v) { + buf.writeByte(v); + } + + @Override + public void writeShort(int v) { + buf.writeShort(v); + } + + @Override + public void writeChar(int v) { + buf.writeChar(v); + } + + @Override + public void writeInt(int v) { + buf.writeInt(v); + } + + @Override + public void writeLong(long v) { + buf.writeLong(v); + } + + @Override + public void writeFloat(float v) { + buf.writeFloat(v); + } + + @Override + public void writeDouble(double v) { + buf.writeDouble(v); + } + + @Override + public void writeBytes(String s) { + buf.writeCharSequence(s, StandardCharsets.US_ASCII); + } + + @Override + public void writeChars(String s) { + for (char c : s.toCharArray()) { + buf.writeChar(c); + } + } + + @Override + public void writeUTF(String s) { + try { + this.utf8out.writeUTF(s); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java b/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java index cf6694cb4..e999d5e7c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java @@ -18,12 +18,16 @@ import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.api.proxy.server.ServerPing; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.MinecraftConnection; +import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder; import com.velocitypowered.proxy.protocol.netty.MinecraftEncoder; import com.velocitypowered.proxy.protocol.netty.MinecraftVarintFrameDecoder; import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthEncoder; +import com.velocitypowered.proxy.protocol.packet.PluginMessage; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; @@ -119,8 +123,12 @@ public class VelocityRegisteredServer implements RegisteredServer { @Override public boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data) { + return sendPluginMessage(identifier, Unpooled.wrappedBuffer(data)); + } + + public boolean sendPluginMessage(ChannelIdentifier identifier, ByteBuf data) { for (ConnectedPlayer player : players) { - ServerConnection connection = player.getConnectedServer(); + VelocityServerConnection connection = player.getConnectedServer(); if (connection != null && connection.getServerInfo().equals(serverInfo)) { return connection.sendPluginMessage(identifier, data); } From 87ad188f9261ea52bb5267b1ae60e6eaa25af92f Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Tue, 26 Nov 2019 16:27:30 -0500 Subject: [PATCH 2/3] Finalize integrated BungeeQuack --- .../backend/BackendPlaySessionHandler.java | 7 ++ .../backend/BungeeCordMessageResponder.java | 112 +++++++++--------- 2 files changed, 65 insertions(+), 54 deletions(-) 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 dbedce09a..747006dce 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 @@ -1,5 +1,8 @@ package com.velocitypowered.proxy.connection.backend; +import static com.velocitypowered.proxy.connection.backend.BungeeCordMessageResponder.getBungeeCordChannel; + +import com.google.common.collect.ImmutableList; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder; @@ -53,6 +56,10 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { @Override public void activated() { serverConn.getServer().addPlayer(serverConn.getPlayer()); + MinecraftConnection serverMc = serverConn.ensureConnected(); + serverMc.write(PluginMessageUtil.constructChannelsPacket(serverMc.getProtocolVersion(), + ImmutableList.of(getBungeeCordChannel(serverMc.getProtocolVersion())) + )); } @Override diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java index 45c697628..018f0ea17 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java @@ -2,7 +2,6 @@ package com.velocitypowered.proxy.connection.backend; import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.proxy.Player; -import com.velocitypowered.api.proxy.ServerConnection; import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier; import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; import com.velocitypowered.api.proxy.server.RegisteredServer; @@ -15,10 +14,8 @@ import com.velocitypowered.proxy.protocol.util.ByteBufDataInput; import com.velocitypowered.proxy.protocol.util.ByteBufDataOutput; import com.velocitypowered.proxy.server.VelocityRegisteredServer; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import java.util.StringJoiner; -import java.util.stream.Collectors; import net.kyori.text.serializer.legacy.LegacyComponentSerializer; class BungeeCordMessageResponder { @@ -46,10 +43,8 @@ class BungeeCordMessageResponder { String playerName = in.readUTF(); String serverName = in.readUTF(); - proxy.getPlayer(playerName).ifPresent(player -> { - proxy.getServer(serverName).ifPresent(server -> player.createConnectionRequest(server) - .fireAndForget()); - }); + proxy.getPlayer(playerName).flatMap(player -> proxy.getServer(serverName)) + .ifPresent(server -> player.createConnectionRequest(server).fireAndForget()); } private void processIp(ByteBufDataInput in) { @@ -120,7 +115,7 @@ class BungeeCordMessageResponder { } } - private void processGetServers(ByteBufDataInput in) { + private void processGetServers() { StringJoiner joiner = new StringJoiner(", "); for (RegisteredServer server : proxy.getAllServers()) { joiner.add(server.getServerInfo().getName()); @@ -148,7 +143,7 @@ class BungeeCordMessageResponder { } } - private void processGetServer(ByteBufDataInput in) { + private void processGetServer() { ByteBuf buf = Unpooled.buffer(); ByteBufDataOutput out = new ByteBufDataOutput(buf); @@ -158,7 +153,7 @@ class BungeeCordMessageResponder { sendResponse(buf); } - private void processUuid(ByteBufDataInput in) { + private void processUuid() { ByteBuf buf = Unpooled.buffer(); ByteBufDataOutput out = new ByteBufDataOutput(buf); @@ -243,11 +238,15 @@ class BungeeCordMessageResponder { sendResponse(this.player, buf); } + static String getBungeeCordChannel(ProtocolVersion version) { + return version.compareTo(ProtocolVersion.MINECRAFT_1_13) >= 0 ? MODERN_CHANNEL.getId() + : LEGACY_CHANNEL.getId(); + } + // Note: this method will always release the buffer! private static void sendResponse(ConnectedPlayer player, ByteBuf buf) { MinecraftConnection serverConnection = player.ensureAndGetCurrentServer().ensureConnected(); - String chan = serverConnection.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_13) - >= 0 ? MODERN_CHANNEL.getId() : LEGACY_CHANNEL.getId(); + String chan = getBungeeCordChannel(serverConnection.getProtocolVersion()); PluginMessage msg = null; boolean released = false; @@ -269,7 +268,7 @@ class BungeeCordMessageResponder { } } - public boolean process(PluginMessage message) { + boolean process(PluginMessage message) { if (!MODERN_CHANNEL.getId().equals(message.getChannel()) && !LEGACY_CHANNEL.getId() .equals(message.getChannel())) { return false; @@ -277,47 +276,52 @@ class BungeeCordMessageResponder { ByteBufDataInput in = new ByteBufDataInput(message.content()); String subChannel = in.readUTF(); - if (subChannel.equals("ForwardToPlayer")) { - this.processForwardToPlayer(in); - } - if (subChannel.equals("Forward")) { - this.processForwardToServer(in); - } - if (subChannel.equals("Connect")) { - this.processConnect(in); - } - if (subChannel.equals("ConnectOther")) { - this.processConnectOther(in); - } - if (subChannel.equals("IP")) { - this.processIp(in); - } - if (subChannel.equals("PlayerCount")) { - this.processPlayerCount(in); - } - if (subChannel.equals("PlayerList")) { - this.processPlayerList(in); - } - if (subChannel.equals("GetServers")) { - this.processGetServers(in); - } - if (subChannel.equals("Message")) { - this.processMessage(in); - } - if (subChannel.equals("GetServer")) { - this.processGetServer(in); - } - if (subChannel.equals("UUID")) { - this.processUuid(in); - } - if (subChannel.equals("UUIDOther")) { - this.processUuidOther(in); - } - if (subChannel.equals("ServerIP")) { - this.processServerIp(in); - } - if (subChannel.equals("KickPlayer")) { - this.processKick(in); + switch (subChannel) { + case "ForwardToPlayer": + this.processForwardToPlayer(in); + break; + case "Forward": + this.processForwardToServer(in); + break; + case "Connect": + this.processConnect(in); + break; + case "ConnectOther": + this.processConnectOther(in); + break; + case "IP": + this.processIp(in); + break; + case "PlayerCount": + this.processPlayerCount(in); + break; + case "PlayerList": + this.processPlayerList(in); + break; + case "GetServers": + this.processGetServers(); + break; + case "Message": + this.processMessage(in); + break; + case "GetServer": + this.processGetServer(); + break; + case "UUID": + this.processUuid(); + break; + case "UUIDOther": + this.processUuidOther(in); + break; + case "ServerIP": + this.processServerIp(in); + break; + case "KickPlayer": + this.processKick(in); + break; + default: + // Do nothing, unknown command + break; } return true; From 0c14eabe6b1ed9ce4b06fc6e8b06cd18c840d35c Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Tue, 26 Nov 2019 16:32:50 -0500 Subject: [PATCH 3/3] Fix Checkstyle issues --- .../backend/BungeeCordMessageResponder.java | 24 +++++++++---------- .../backend/VelocityServerConnection.java | 6 +++++ .../connection/client/ConnectedPlayer.java | 4 ++++ .../server/VelocityRegisteredServer.java | 6 +++++ 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java index 018f0ea17..678c77e8f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java @@ -53,7 +53,7 @@ class BungeeCordMessageResponder { out.writeUTF("IP"); out.writeUTF(player.getRemoteAddress().getHostString()); out.writeInt(player.getRemoteAddress().getPort()); - sendResponse(buf); + sendResponseOnConnection(buf); } private void processPlayerCount(ByteBufDataInput in) { @@ -75,7 +75,7 @@ class BungeeCordMessageResponder { } if (buf.isReadable()) { - sendResponse(buf); + sendResponseOnConnection(buf); } else { buf.release(); } @@ -109,7 +109,7 @@ class BungeeCordMessageResponder { } if (buf.isReadable()) { - sendResponse(buf); + sendResponseOnConnection(buf); } else { buf.release(); } @@ -126,7 +126,7 @@ class BungeeCordMessageResponder { out.writeUTF("GetServers"); out.writeUTF(joiner.toString()); - sendResponse(buf); + sendResponseOnConnection(buf); } private void processMessage(ByteBufDataInput in) { @@ -150,7 +150,7 @@ class BungeeCordMessageResponder { out.writeUTF("GetServer"); out.writeUTF(player.ensureAndGetCurrentServer().getServerInfo().getName()); - sendResponse(buf); + sendResponseOnConnection(buf); } private void processUuid() { @@ -160,7 +160,7 @@ class BungeeCordMessageResponder { out.writeUTF("UUID"); out.writeUTF(UuidUtils.toUndashed(player.getUniqueId())); - sendResponse(buf); + sendResponseOnConnection(buf); } private void processUuidOther(ByteBufDataInput in) { @@ -172,7 +172,7 @@ class BungeeCordMessageResponder { out.writeUTF(player.getUsername()); out.writeUTF(UuidUtils.toUndashed(player.getUniqueId())); - sendResponse(buf); + sendResponseOnConnection(buf); }); } @@ -186,7 +186,7 @@ class BungeeCordMessageResponder { out.writeUTF(info.getServerInfo().getAddress().getHostString()); out.writeShort(info.getServerInfo().getAddress().getPort()); - sendResponse(buf); + sendResponseOnConnection(buf); }); } @@ -212,7 +212,7 @@ class BungeeCordMessageResponder { private void processForwardToPlayer(ByteBufDataInput in) { proxy.getPlayer(in.readUTF()) .flatMap(Player::getCurrentServer) - .ifPresent(server -> sendResponse(player, prepareForwardMessage(in))); + .ifPresent(server -> sendServerResponse(player, prepareForwardMessage(in))); } private void processForwardToServer(ByteBufDataInput in) { @@ -234,8 +234,8 @@ class BungeeCordMessageResponder { } // Note: this method will always release the buffer! - private void sendResponse(ByteBuf buf) { - sendResponse(this.player, buf); + private void sendResponseOnConnection(ByteBuf buf) { + sendServerResponse(this.player, buf); } static String getBungeeCordChannel(ProtocolVersion version) { @@ -244,7 +244,7 @@ class BungeeCordMessageResponder { } // Note: this method will always release the buffer! - private static void sendResponse(ConnectedPlayer player, ByteBuf buf) { + private static void sendServerResponse(ConnectedPlayer player, ByteBuf buf) { MinecraftConnection serverConnection = player.ensureAndGetCurrentServer().ensureConnected(); String chan = getBungeeCordChannel(serverConnection.getProtocolVersion()); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java index 9f0903469..a7cf3ed82 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java @@ -214,6 +214,12 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, return sendPluginMessage(identifier, Unpooled.wrappedBuffer(data)); } + /** + * Sends a plugin message to the server through this connection. + * @param identifier the channel ID to use + * @param data the data + * @return whether or not the message was sent + */ public boolean sendPluginMessage(ChannelIdentifier identifier, ByteBuf data) { Preconditions.checkNotNull(identifier, "identifier"); Preconditions.checkNotNull(data, "data"); 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 97dc93305..d90fc2054 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 @@ -135,6 +135,10 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { return Optional.ofNullable(connectedServer); } + /** + * Makes sure the player is connected to a server and returns the server they are connected to. + * @return the server the player is connected to + */ public VelocityServerConnection ensureAndGetCurrentServer() { VelocityServerConnection con = this.connectedServer; if (con == null) { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java b/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java index e999d5e7c..dc8f1687d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java @@ -126,6 +126,12 @@ public class VelocityRegisteredServer implements RegisteredServer { return sendPluginMessage(identifier, Unpooled.wrappedBuffer(data)); } + /** + * Sends a plugin message to the server through this connection. + * @param identifier the channel ID to use + * @param data the data + * @return whether or not the message was sent + */ public boolean sendPluginMessage(ChannelIdentifier identifier, ByteBuf data) { for (ConnectedPlayer player : players) { VelocityServerConnection connection = player.getConnectedServer();