13
0
geforkt von Mirrors/Velocity

Merge pull request #255 from VelocityPowered/bungeequack-integrate

Integrate BungeeQuack functionality into Velocity
Dieser Commit ist enthalten in:
Andrew Steinborn 2019-11-26 16:33:37 -05:00 committet von GitHub
Commit 94035366c2
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 4AEE18F83AFDEB23
7 geänderte Dateien mit 598 neuen und 2 gelöschten Zeilen

Datei anzeigen

@ -1,5 +1,8 @@
package com.velocitypowered.proxy.connection.backend; 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.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder;
@ -31,6 +34,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
private final VelocityServerConnection serverConn; private final VelocityServerConnection serverConn;
private final ClientPlaySessionHandler playerSessionHandler; private final ClientPlaySessionHandler playerSessionHandler;
private final MinecraftConnection playerConnection; private final MinecraftConnection playerConnection;
private final BungeeCordMessageResponder bungeecordMessageResponder;
private boolean exceptionTriggered = false; private boolean exceptionTriggered = false;
BackendPlaySessionHandler(VelocityServer server, VelocityServerConnection serverConn) { BackendPlaySessionHandler(VelocityServer server, VelocityServerConnection serverConn) {
@ -44,11 +48,18 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
"Initializing BackendPlaySessionHandler with no backing client play session handler!"); "Initializing BackendPlaySessionHandler with no backing client play session handler!");
} }
this.playerSessionHandler = (ClientPlaySessionHandler) psh; this.playerSessionHandler = (ClientPlaySessionHandler) psh;
this.bungeecordMessageResponder = new BungeeCordMessageResponder(server,
serverConn.getPlayer());
} }
@Override @Override
public void activated() { public void activated() {
serverConn.getServer().addPlayer(serverConn.getPlayer()); serverConn.getServer().addPlayer(serverConn.getPlayer());
MinecraftConnection serverMc = serverConn.ensureConnected();
serverMc.write(PluginMessageUtil.constructChannelsPacket(serverMc.getProtocolVersion(),
ImmutableList.of(getBungeeCordChannel(serverMc.getProtocolVersion()))
));
} }
@Override @Override
@ -86,6 +97,10 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
@Override @Override
public boolean handle(PluginMessage packet) { public boolean handle(PluginMessage packet) {
if (bungeecordMessageResponder.process(packet)) {
return true;
}
if (!serverConn.getPlayer().canForwardPluginMessage(serverConn.ensureConnected() if (!serverConn.getPlayer().canForwardPluginMessage(serverConn.ensureConnected()
.getProtocolVersion(), packet)) { .getProtocolVersion(), packet)) {
return true; return true;

Datei anzeigen

@ -0,0 +1,329 @@
package com.velocitypowered.proxy.connection.backend;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.Player;
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.Unpooled;
import java.util.StringJoiner;
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).flatMap(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());
sendResponseOnConnection(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()) {
sendResponseOnConnection(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()) {
sendResponseOnConnection(buf);
} else {
buf.release();
}
}
private void processGetServers() {
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());
sendResponseOnConnection(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() {
ByteBuf buf = Unpooled.buffer();
ByteBufDataOutput out = new ByteBufDataOutput(buf);
out.writeUTF("GetServer");
out.writeUTF(player.ensureAndGetCurrentServer().getServerInfo().getName());
sendResponseOnConnection(buf);
}
private void processUuid() {
ByteBuf buf = Unpooled.buffer();
ByteBufDataOutput out = new ByteBufDataOutput(buf);
out.writeUTF("UUID");
out.writeUTF(UuidUtils.toUndashed(player.getUniqueId()));
sendResponseOnConnection(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()));
sendResponseOnConnection(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());
sendResponseOnConnection(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 -> sendServerResponse(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 sendResponseOnConnection(ByteBuf buf) {
sendServerResponse(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 sendServerResponse(ConnectedPlayer player, ByteBuf buf) {
MinecraftConnection serverConnection = player.ensureAndGetCurrentServer().ensureConnected();
String chan = getBungeeCordChannel(serverConnection.getProtocolVersion());
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();
}
}
}
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();
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;
}
}

Datei anzeigen

@ -34,6 +34,7 @@ import com.velocitypowered.proxy.protocol.packet.Handshake;
import com.velocitypowered.proxy.protocol.packet.PluginMessage; import com.velocitypowered.proxy.protocol.packet.PluginMessage;
import com.velocitypowered.proxy.protocol.packet.ServerLogin; import com.velocitypowered.proxy.protocol.packet.ServerLogin;
import com.velocitypowered.proxy.server.VelocityRegisteredServer; import com.velocitypowered.proxy.server.VelocityRegisteredServer;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
@ -210,12 +211,22 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
@Override @Override
public boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data) { public boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data) {
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(identifier, "identifier");
Preconditions.checkNotNull(data, "data"); Preconditions.checkNotNull(data, "data");
MinecraftConnection mc = ensureConnected(); MinecraftConnection mc = ensureConnected();
PluginMessage message = new PluginMessage(identifier.getId(), Unpooled.wrappedBuffer(data)); PluginMessage message = new PluginMessage(identifier.getId(), data);
mc.write(message); mc.write(message);
return true; return true;
} }

Datei anzeigen

@ -135,6 +135,18 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
return Optional.ofNullable(connectedServer); 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) {
throw new IllegalStateException("Not connected to server!");
}
return con;
}
@Override @Override
public GameProfile getGameProfile() { public GameProfile getGameProfile() {
return profile; return profile;

Datei anzeigen

@ -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);
}
}
}

Datei anzeigen

@ -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);
}
}
}

Datei anzeigen

@ -18,12 +18,16 @@ import com.velocitypowered.api.proxy.server.ServerInfo;
import com.velocitypowered.api.proxy.server.ServerPing; import com.velocitypowered.api.proxy.server.ServerPing;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder; import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
import com.velocitypowered.proxy.protocol.netty.MinecraftEncoder; import com.velocitypowered.proxy.protocol.netty.MinecraftEncoder;
import com.velocitypowered.proxy.protocol.netty.MinecraftVarintFrameDecoder; import com.velocitypowered.proxy.protocol.netty.MinecraftVarintFrameDecoder;
import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthEncoder; 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.Channel;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
@ -119,8 +123,18 @@ public class VelocityRegisteredServer implements RegisteredServer {
@Override @Override
public boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data) { public boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data) {
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) { for (ConnectedPlayer player : players) {
ServerConnection connection = player.getConnectedServer(); VelocityServerConnection connection = player.getConnectedServer();
if (connection != null && connection.getServerInfo().equals(serverInfo)) { if (connection != null && connection.getServerInfo().equals(serverInfo)) {
return connection.sendPluginMessage(identifier, data); return connection.sendPluginMessage(identifier, data);
} }