3
0
Mirror von https://github.com/PaperMC/Velocity.git synchronisiert 2024-11-17 05:20:14 +01:00

Integrate BungeeQuack functionality into Velocity

Dieser Commit ist enthalten in:
Andrew Steinborn 2019-09-26 01:04:38 -04:00
Ursprung 9b570ff599
Commit 9d5930d96b
7 geänderte Dateien mit 571 neuen und 2 gelöschten Zeilen

Datei anzeigen

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

Datei anzeigen

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

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.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;
}

Datei anzeigen

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

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