3
0
Mirror von https://github.com/PaperMC/Velocity.git synchronisiert 2024-12-25 15:50:19 +01:00

Connection logging support.

Dieser Commit ist enthalten in:
Andrew Steinborn 2018-07-27 14:44:00 -04:00
Ursprung f34e9b19c9
Commit 5ef27cfa5f
10 geänderte Dateien mit 149 neuen und 32 gelöschten Zeilen

Datei anzeigen

@ -1,19 +1,19 @@
package com.velocitypowered.proxy.connection; package com.velocitypowered.proxy.connection;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.velocitypowered.proxy.Velocity;
import com.velocitypowered.proxy.protocol.PacketWrapper; import com.velocitypowered.proxy.protocol.PacketWrapper;
import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.protocol.compression.JavaVelocityCompressor; import com.velocitypowered.proxy.protocol.compression.JavaVelocityCompressor;
import com.velocitypowered.proxy.protocol.encryption.JavaVelocityCipher; import com.velocitypowered.proxy.protocol.encryption.JavaVelocityCipher;
import com.velocitypowered.proxy.protocol.encryption.VelocityCipher; import com.velocitypowered.proxy.protocol.encryption.VelocityCipher;
import com.velocitypowered.proxy.protocol.netty.*; import com.velocitypowered.proxy.protocol.netty.*;
import com.velocitypowered.proxy.protocol.packets.SetCompression;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
@ -34,11 +34,14 @@ import static com.velocitypowered.network.Connections.MINECRAFT_ENCODER;
* protocol mechanics. * protocol mechanics.
*/ */
public class MinecraftConnection extends ChannelInboundHandlerAdapter { public class MinecraftConnection extends ChannelInboundHandlerAdapter {
private static final Logger logger = LogManager.getLogger(MinecraftConnection.class);
private final Channel channel; private final Channel channel;
private boolean closed; private boolean closed;
private StateRegistry state; private StateRegistry state;
private MinecraftSessionHandler sessionHandler; private MinecraftSessionHandler sessionHandler;
private int protocolVersion; private int protocolVersion;
private MinecraftConnectionAssociation association;
public MinecraftConnection(Channel channel) { public MinecraftConnection(Channel channel) {
this.channel = channel; this.channel = channel;
@ -51,6 +54,10 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
if (sessionHandler != null) { if (sessionHandler != null) {
sessionHandler.connected(); sessionHandler.connected();
} }
if (association != null) {
logger.info("{} has connected", association);
}
} }
@Override @Override
@ -58,6 +65,10 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
if (sessionHandler != null) { if (sessionHandler != null) {
sessionHandler.disconnected(); sessionHandler.disconnected();
} }
if (association != null) {
logger.info("{} has disconnected", association);
}
} }
@Override @Override
@ -87,6 +98,12 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
sessionHandler.exception(cause); sessionHandler.exception(cause);
} }
if (association != null) {
logger.error("{}: exception encountered", association, cause);
} else {
logger.error("{} encountered an exception", cause);
}
closed = true; closed = true;
ctx.close(); ctx.close();
} }
@ -187,4 +204,12 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
channel.pipeline().addBefore(FRAME_DECODER, CIPHER_DECODER, new MinecraftCipherDecoder(decryptionCipher)); channel.pipeline().addBefore(FRAME_DECODER, CIPHER_DECODER, new MinecraftCipherDecoder(decryptionCipher));
channel.pipeline().addBefore(FRAME_ENCODER, CIPHER_ENCODER, new MinecraftCipherEncoder(encryptionCipher)); channel.pipeline().addBefore(FRAME_ENCODER, CIPHER_ENCODER, new MinecraftCipherEncoder(encryptionCipher));
} }
public MinecraftConnectionAssociation getAssociation() {
return association;
}
public void setAssociation(MinecraftConnectionAssociation association) {
this.association = association;
}
} }

Datei anzeigen

@ -0,0 +1,4 @@
package com.velocitypowered.proxy.connection;
public interface MinecraftConnectionAssociation {
}

Datei anzeigen

@ -8,9 +8,6 @@ import com.velocitypowered.proxy.protocol.packets.Ping;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.packets.Respawn; import com.velocitypowered.proxy.protocol.packets.Respawn;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.kyori.text.TextComponent;
import net.kyori.text.format.TextColor;
import net.kyori.text.serializer.ComponentSerializers;
public class BackendPlaySessionHandler implements MinecraftSessionHandler { public class BackendPlaySessionHandler implements MinecraftSessionHandler {
private final ServerConnection connection; private final ServerConnection connection;
@ -25,15 +22,8 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
// Forward onto the server // Forward onto the server
connection.getChannel().write(packet); connection.getChannel().write(packet);
} else if (packet instanceof Disconnect) { } else if (packet instanceof Disconnect) {
// The server wants to disconnect us. TODO fallback handling
Disconnect original = (Disconnect) packet; Disconnect original = (Disconnect) packet;
TextComponent reason = TextComponent.builder() connection.getProxyPlayer().handleConnectionException(connection.getServerInfo(), original);
.content("Disconnected from " + connection.getServerInfo().getName() + ":")
.color(TextColor.RED)
.append(TextComponent.of(" ", TextColor.WHITE))
.append(ComponentSerializers.JSON.deserialize(original.getReason()))
.build();
connection.getProxyPlayer().close(reason);
} else if (packet instanceof JoinGame) { } else if (packet instanceof JoinGame) {
ClientPlaySessionHandler playerHandler = ClientPlaySessionHandler playerHandler =
(ClientPlaySessionHandler) connection.getProxyPlayer().getConnection().getSessionHandler(); (ClientPlaySessionHandler) connection.getProxyPlayer().getConnection().getSessionHandler();

Datei anzeigen

@ -1,6 +1,7 @@
package com.velocitypowered.proxy.connection.backend; package com.velocitypowered.proxy.connection.backend;
import com.velocitypowered.proxy.config.IPForwardingMode; import com.velocitypowered.proxy.config.IPForwardingMode;
import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation;
import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.ProtocolConstants;
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;
@ -26,7 +27,7 @@ import static com.velocitypowered.network.Connections.MINECRAFT_ENCODER;
import static com.velocitypowered.network.Connections.READ_TIMEOUT; import static com.velocitypowered.network.Connections.READ_TIMEOUT;
import static com.velocitypowered.network.Connections.SERVER_READ_TIMEOUT_SECONDS; import static com.velocitypowered.network.Connections.SERVER_READ_TIMEOUT_SECONDS;
public class ServerConnection { public class ServerConnection implements MinecraftConnectionAssociation {
private final ServerInfo serverInfo; private final ServerInfo serverInfo;
private final ConnectedPlayer proxyPlayer; private final ConnectedPlayer proxyPlayer;
private final VelocityServer server; private final VelocityServer server;
@ -53,6 +54,7 @@ public class ServerConnection {
MinecraftConnection connection = new MinecraftConnection(ch); MinecraftConnection connection = new MinecraftConnection(ch);
connection.setState(StateRegistry.HANDSHAKE); connection.setState(StateRegistry.HANDSHAKE);
connection.setSessionHandler(new LoginSessionHandler(ServerConnection.this)); connection.setSessionHandler(new LoginSessionHandler(ServerConnection.this));
connection.setAssociation(ServerConnection.this);
ch.pipeline().addLast(HANDLER, connection); ch.pipeline().addLast(HANDLER, connection);
} }
}) })
@ -120,4 +122,9 @@ public class ServerConnection {
channel.close(); channel.close();
channel = null; channel = null;
} }
@Override
public String toString() {
return "[server connection] " + proxyPlayer.getProfile().getName() + " -> " + serverInfo.getName();
}
} }

Datei anzeigen

@ -1,20 +1,27 @@
package com.velocitypowered.proxy.connection.client; package com.velocitypowered.proxy.connection.client;
import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation;
import com.velocitypowered.proxy.data.GameProfile; import com.velocitypowered.proxy.data.GameProfile;
import com.velocitypowered.proxy.protocol.packets.Chat; import com.velocitypowered.proxy.protocol.packets.Chat;
import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.backend.ServerConnection; import com.velocitypowered.proxy.connection.backend.ServerConnection;
import com.velocitypowered.proxy.util.ComponentUtils;
import com.velocitypowered.proxy.util.ThrowableUtils; import com.velocitypowered.proxy.util.ThrowableUtils;
import com.velocitypowered.proxy.data.ServerInfo; import com.velocitypowered.proxy.data.ServerInfo;
import com.velocitypowered.proxy.protocol.packets.Disconnect; import com.velocitypowered.proxy.protocol.packets.Disconnect;
import net.kyori.text.Component;
import net.kyori.text.TextComponent; import net.kyori.text.TextComponent;
import net.kyori.text.format.TextColor; import net.kyori.text.format.TextColor;
import net.kyori.text.serializer.ComponentSerializers; import net.kyori.text.serializer.ComponentSerializers;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.UUID; import java.util.UUID;
public class ConnectedPlayer { public class ConnectedPlayer implements MinecraftConnectionAssociation {
private static final Logger logger = LogManager.getLogger(ConnectedPlayer.class);
private final GameProfile profile; private final GameProfile profile;
private final MinecraftConnection connection; private final MinecraftConnection connection;
private ServerConnection connectedServer; private ServerConnection connectedServer;
@ -50,22 +57,39 @@ public class ConnectedPlayer {
public void handleConnectionException(ServerInfo info, Throwable throwable) { public void handleConnectionException(ServerInfo info, Throwable throwable) {
String error = ThrowableUtils.briefDescription(throwable); String error = ThrowableUtils.briefDescription(throwable);
Disconnect disconnect = Disconnect.create(TextComponent.of(error, TextColor.RED)); String userMessage;
handleConnectionException(info, disconnect); if (connectedServer != null && connectedServer.getServerInfo().equals(info)) {
logger.error("{}: exception occurred in connection to {}", this, info.getName(), throwable);
userMessage = "Exception in server " + info.getName();
} else {
logger.error("{}: unable to connect to server {}", this, info.getName(), throwable);
userMessage = "Exception connecting to server " + info.getName();
}
handleConnectionException(info, TextComponent.builder()
.content(userMessage + ": ")
.color(TextColor.RED)
.append(TextComponent.of(error))
.build());
} }
public void handleConnectionException(ServerInfo info, Disconnect disconnect) { public void handleConnectionException(ServerInfo info, Disconnect disconnect) {
TextComponent component = TextComponent.builder() Component disconnectReason = ComponentSerializers.JSON.deserialize(disconnect.getReason());
.content("Exception connecting to server " + info.getName() + ": ") String reason = ComponentUtils.asPlainText(disconnectReason);
.color(TextColor.RED) if (connectedServer != null && connectedServer.getServerInfo().equals(info)) {
.append(ComponentSerializers.JSON.deserialize(disconnect.getReason())) logger.error("{}: kicked from server {}: {}", this, info.getName(), reason);
.build();
if (connectedServer == null) {
// The player isn't yet connected to a server - we should disconnect them.
connection.closeWith(Disconnect.create(component));
} else { } else {
connection.write(Chat.create(component)); logger.error("{}: disconnected while connecting to {}: {}", this, info.getName(), reason);
}
handleConnectionException(info, disconnectReason);
}
private void handleConnectionException(ServerInfo info, Component disconnectReason) {
if (connectedServer == null || connectedServer.getServerInfo().equals(info)) {
// The player isn't yet connected to a server or they are already connected to the server
// they're disconnected from.
connection.closeWith(Disconnect.create(disconnectReason));
} else {
connection.write(Chat.create(disconnectReason));
} }
} }
@ -76,4 +100,9 @@ public class ConnectedPlayer {
public void close(TextComponent reason) { public void close(TextComponent reason) {
connection.closeWith(Disconnect.create(reason)); connection.closeWith(Disconnect.create(reason));
} }
@Override
public String toString() {
return "[connected player] " + getProfile().getName() + " (" + getRemoteAddress() + ")";
}
} }

Datei anzeigen

@ -105,6 +105,8 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
// Initiate a regular connection and move over to it. // Initiate a regular connection and move over to it.
ConnectedPlayer player = new ConnectedPlayer(profile, inbound); ConnectedPlayer player = new ConnectedPlayer(profile, inbound);
logger.info("{} has connected", player);
inbound.setAssociation(player);
ServerInfo info = new ServerInfo("test", new InetSocketAddress("localhost", 25565)); ServerInfo info = new ServerInfo("test", new InetSocketAddress("localhost", 25565));
ServerConnection connection = new ServerConnection(info, player, VelocityServer.getServer()); ServerConnection connection = new ServerConnection(info, player, VelocityServer.getServer());

Datei anzeigen

@ -5,7 +5,7 @@ import com.velocitypowered.proxy.protocol.ProtocolConstants;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.kyori.text.TextComponent; import net.kyori.text.Component;
import net.kyori.text.serializer.ComponentSerializers; import net.kyori.text.serializer.ComponentSerializers;
public class Chat implements MinecraftPacket { public class Chat implements MinecraftPacket {
@ -60,11 +60,11 @@ public class Chat implements MinecraftPacket {
} }
} }
public static Chat create(TextComponent component) { public static Chat create(Component component) {
return create(component, (byte) 0); return create(component, (byte) 0);
} }
public static Chat create(TextComponent component, byte pos) { public static Chat create(Component component, byte pos) {
Preconditions.checkNotNull(component, "component"); Preconditions.checkNotNull(component, "component");
return new Chat(ComponentSerializers.JSON.serialize(component), pos); return new Chat(ComponentSerializers.JSON.serialize(component), pos);
} }

Datei anzeigen

@ -5,7 +5,7 @@ import com.velocitypowered.proxy.protocol.ProtocolConstants;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.kyori.text.TextComponent; import net.kyori.text.Component;
import net.kyori.text.serializer.ComponentSerializers; import net.kyori.text.serializer.ComponentSerializers;
public class Disconnect implements MinecraftPacket { public class Disconnect implements MinecraftPacket {
@ -43,7 +43,7 @@ public class Disconnect implements MinecraftPacket {
ProtocolUtils.writeString(buf, reason); ProtocolUtils.writeString(buf, reason);
} }
public static Disconnect create(TextComponent component) { public static Disconnect create(Component component) {
Preconditions.checkNotNull(component, "component"); Preconditions.checkNotNull(component, "component");
return new Disconnect(ComponentSerializers.JSON.serialize(component)); return new Disconnect(ComponentSerializers.JSON.serialize(component));
} }

Datei anzeigen

@ -0,0 +1,29 @@
package com.velocitypowered.proxy.util;
import com.google.common.base.Preconditions;
import net.kyori.text.Component;
import net.kyori.text.TextComponent;
import net.kyori.text.TranslatableComponent;
public enum ComponentUtils {
;
public static String asPlainText(Component component) {
Preconditions.checkNotNull(component, "component");
StringBuilder builder = new StringBuilder();
appendPlainText(component, builder);
return builder.toString();
}
private static void appendPlainText(Component component, StringBuilder builder) {
if (component instanceof TextComponent) {
builder.append(((TextComponent) component).content());
}
if (component instanceof TranslatableComponent) {
builder.append(((TranslatableComponent) component).key());
}
for (Component child : component.children()) {
appendPlainText(child, builder);
}
}
}

Datei anzeigen

@ -0,0 +1,31 @@
package com.velocitypowered.proxy.util;
import net.kyori.text.TextComponent;
import net.kyori.text.format.TextColor;
import net.kyori.text.format.TextDecoration;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class ComponentUtilsTest {
private static final String SIMPLE_COMPONENT_TEXT = "hello";
private static final TextComponent SIMPLE_COMPONENT = TextComponent.of(SIMPLE_COMPONENT_TEXT, TextColor.RED);
private static final String COMPLEX_COMPONENT_TEXT = "Hello world! Welcome to Velocity, the Minecraft server proxy built for mass scale.";
private static final TextComponent COMPLEX_COMPONENT = TextComponent.builder("Hello world! ")
.decoration(TextDecoration.BOLD, true)
.append(TextComponent.of("Welcome to "))
.decoration(TextDecoration.BOLD, false)
.color(TextColor.GREEN)
.append(TextComponent.of("Velocity"))
.color(TextColor.DARK_AQUA)
.append(TextComponent.of(", the Minecraft server proxy built for mass scale."))
.resetStyle()
.build();
@Test
void asPlainText() {
assertEquals(SIMPLE_COMPONENT_TEXT, ComponentUtils.asPlainText(SIMPLE_COMPONENT));
assertEquals(COMPLEX_COMPONENT_TEXT, ComponentUtils.asPlainText(COMPLEX_COMPONENT));
}
}