1
0
mirror of https://github.com/GeyserMC/Floodgate.git synced 2025-12-19 14:59:20 +00:00

Made the debug addon a bit more dumb

This commit is contained in:
Tim203
2024-02-18 15:36:44 +01:00
parent 2886a71480
commit 7f38765010
5 changed files with 19 additions and 236 deletions

View File

@@ -29,9 +29,9 @@ import com.google.inject.Inject;
import com.google.inject.name.Named; import com.google.inject.name.Named;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import java.util.concurrent.atomic.AtomicInteger;
import org.geysermc.floodgate.addon.debug.ChannelInDebugHandler; import org.geysermc.floodgate.addon.debug.ChannelInDebugHandler;
import org.geysermc.floodgate.addon.debug.ChannelOutDebugHandler; import org.geysermc.floodgate.addon.debug.ChannelOutDebugHandler;
import org.geysermc.floodgate.addon.debug.StateChangeDetector;
import org.geysermc.floodgate.api.inject.InjectorAddon; import org.geysermc.floodgate.api.inject.InjectorAddon;
import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.config.FloodgateConfig; import org.geysermc.floodgate.config.FloodgateConfig;
@@ -57,16 +57,14 @@ public final class DebugAddon implements InjectorAddon {
public void onInject(Channel channel, boolean toServer) { public void onInject(Channel channel, boolean toServer) {
logger.info("Successfully called onInject. To server? " + toServer); logger.info("Successfully called onInject. To server? " + toServer);
StateChangeDetector changeDetector = new StateChangeDetector( AtomicInteger packetCount = new AtomicInteger();
channel, packetEncoder, packetDecoder, logger
);
channel.pipeline().addBefore( channel.pipeline().addBefore(
packetEncoder, "floodgate_debug_out", packetEncoder, "floodgate_debug_out",
new ChannelOutDebugHandler(implementationName, toServer, changeDetector, logger) new ChannelOutDebugHandler(implementationName, toServer, packetCount, logger)
).addBefore( ).addBefore(
packetDecoder, "floodgate_debug_in", packetDecoder, "floodgate_debug_in",
new ChannelInDebugHandler(implementationName, toServer, changeDetector, logger) new ChannelInDebugHandler(implementationName, toServer, packetCount, logger)
); );
} }

View File

@@ -30,25 +30,24 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
import java.util.concurrent.atomic.AtomicInteger;
import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.util.Utils;
@Sharable @Sharable
public final class ChannelInDebugHandler extends SimpleChannelInboundHandler<ByteBuf> { public final class ChannelInDebugHandler extends SimpleChannelInboundHandler<ByteBuf> {
private final String direction; private final String direction;
private final FloodgateLogger logger; private final FloodgateLogger logger;
private final AtomicInteger packetCount;
private final boolean toServer;
private final StateChangeDetector changeDetector;
public ChannelInDebugHandler( public ChannelInDebugHandler(
String implementationType, String implementationType,
boolean toServer, boolean toServer,
StateChangeDetector changeDetector, AtomicInteger packetCount,
FloodgateLogger logger) { FloodgateLogger logger) {
this.direction = (toServer ? "Server -> " : "Player -> ") + implementationType; this.direction = (toServer ? "Server -> " : "Player -> ") + implementationType;
this.logger = logger; this.logger = logger;
this.toServer = toServer; this.packetCount = packetCount;
this.changeDetector = changeDetector;
} }
@Override @Override
@@ -56,14 +55,12 @@ public final class ChannelInDebugHandler extends SimpleChannelInboundHandler<Byt
try { try {
int index = msg.readerIndex(); int index = msg.readerIndex();
if (changeDetector.shouldPrintPacket(msg, !toServer)) { if (packetCount.getAndIncrement() < Utils.MAX_DEBUG_PACKET_COUNT) {
logger.info("{} {}:\n{}", logger.info("{} {}:\n{}",
direction, direction,
changeDetector.getCurrentState(), packetCount.get(),
ByteBufUtil.prettyHexDump(msg) ByteBufUtil.prettyHexDump(msg)
); );
changeDetector.checkPacket(msg, !toServer);
} }
// reset index // reset index

View File

@@ -30,25 +30,24 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder; import io.netty.handler.codec.MessageToByteEncoder;
import java.util.concurrent.atomic.AtomicInteger;
import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.util.Utils;
@Sharable @Sharable
public final class ChannelOutDebugHandler extends MessageToByteEncoder<ByteBuf> { public final class ChannelOutDebugHandler extends MessageToByteEncoder<ByteBuf> {
private final String direction; private final String direction;
private final FloodgateLogger logger; private final FloodgateLogger logger;
private final AtomicInteger packetCount;
private final boolean toServer;
private final StateChangeDetector changeDetector;
public ChannelOutDebugHandler( public ChannelOutDebugHandler(
String implementationType, String implementationType,
boolean toServer, boolean toServer,
StateChangeDetector changeDetector, AtomicInteger packetCount,
FloodgateLogger logger) { FloodgateLogger logger) {
this.direction = implementationType + (toServer ? " -> Server" : " -> Player"); this.direction = implementationType + (toServer ? " -> Server" : " -> Player");
this.logger = logger; this.logger = logger;
this.toServer = toServer; this.packetCount = packetCount;
this.changeDetector = changeDetector;
} }
@Override @Override
@@ -56,16 +55,13 @@ public final class ChannelOutDebugHandler extends MessageToByteEncoder<ByteBuf>
try { try {
int index = msg.readerIndex(); int index = msg.readerIndex();
if (changeDetector.shouldPrintPacket(msg, toServer)) { if (packetCount.getAndIncrement() < Utils.MAX_DEBUG_PACKET_COUNT) {
logger.info( logger.info(
"{} {}:\n{}", "{} {}:\n{}",
direction, direction,
changeDetector.getCurrentState(), packetCount.get(),
ByteBufUtil.prettyHexDump(msg) ByteBufUtil.prettyHexDump(msg)
); );
// proxy acts as a client when it connects to a server
changeDetector.checkPacket(msg, toServer);
} }
// reset index // reset index

View File

@@ -1,193 +0,0 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Floodgate
*/
package org.geysermc.floodgate.addon.debug;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelPipeline;
import java.nio.charset.StandardCharsets;
import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.util.Constants;
import org.geysermc.floodgate.util.Utils;
public class StateChangeDetector {
private static volatile int pluginMessageToClientId = -1;
private static volatile int pluginMessageToServerId = -1;
private final Channel channel;
private final FloodgateLogger logger;
private final String packetEncoderName;
private final String packetDecoderName;
private volatile boolean enableCompressionNext;
private volatile State currentState = State.HANDSHAKE;
public StateChangeDetector(
Channel channel,
String packetEncoderName,
String packetDecoderName,
FloodgateLogger logger) {
this.channel = channel;
this.packetEncoderName = packetEncoderName;
this.packetDecoderName = packetDecoderName;
this.logger = logger;
}
/**
* Checks (and acts) if the current packet is one of the packets that we need to switch states.
*
* @param packet the packet to check
* @param fromClient if the packet is clientbound or serverbound
*/
public void checkPacket(ByteBuf packet, boolean fromClient) {
int index = packet.readerIndex();
if (enableCompressionNext) {
// data length
Utils.readVarInt(packet);
fixCompressionPipes();
enableCompressionNext = false;
}
int packetId = Utils.readVarInt(packet);
if (fromClient) {
if (currentState == State.HANDSHAKE && packetId == Constants.HANDSHAKE_PACKET_ID) {
// have to read the content to determine the next state
// protocol version
Utils.readVarInt(packet);
// server address
int hostLength = Utils.readVarInt(packet);
// read server address + port (short = 2 bytes)
packet.readerIndex(packet.readerIndex() + hostLength + 2);
// next state
currentState = State.getById(Utils.readVarInt(packet));
}
} else {
if (currentState == State.LOGIN) {
if (packetId == Constants.LOGIN_SUCCESS_PACKET_ID) {
currentState = State.PLAY;
}
if (packetId == Constants.SET_COMPRESSION_PACKET_ID) {
enableCompressionNext = true;
}
}
}
// reset index
packet.readerIndex(index);
}
private void fixCompressionPipes() {
// The previous packet was the compression packet, meaning that starting with this
// packet the data can already be compressed. The compression handler has been added
// directly before the packet encoder and decoder, so we have to reclaim that place.
// If we don't, we'll see the compressed data.
ChannelPipeline pipeline = channel.pipeline();
ChannelHandler outDebug = pipeline.remove(ChannelOutDebugHandler.class);
ChannelHandler inDebug = pipeline.remove(ChannelInDebugHandler.class);
pipeline.addBefore(packetEncoderName, "floodgate_debug_out", outDebug);
pipeline.addBefore(packetDecoderName, "floodgate_debug_in", inDebug);
}
public boolean shouldPrintPacket(ByteBuf packet, boolean clientbound) {
return Constants.PRINT_ALL_PACKETS ||
currentState == State.HANDSHAKE || currentState == State.LOGIN ||
currentState != State.STATUS && shouldPrintPlayPacket(packet, clientbound);
}
public boolean shouldPrintPlayPacket(ByteBuf packet, boolean clientbound) {
int index = packet.readerIndex();
int packetId = Utils.readVarInt(packet);
// we're only interested in the plugin message packets
// use cached packet ids
if (clientbound && pluginMessageToClientId != -1) {
return pluginMessageToClientId == packetId;
}
if (!clientbound && pluginMessageToServerId != -1) {
return pluginMessageToServerId == packetId;
}
boolean shouldPrint = false;
if (packet.isReadable()) {
// format plugin message packet: channel - remaining data
try {
int channelLength = Utils.readVarInt(packet);
if (channelLength >= 1 && channelLength <= 128 &&
packet.isReadable(channelLength)) {
byte[] channelBytes = new byte[channelLength];
packet.readBytes(channelBytes);
String channelName = new String(channelBytes, StandardCharsets.UTF_8);
if (channelName.contains(":")) {
// some other packets will still match,
// but since plugin message packets are send early on
// we can almost know for certain that this is a plugin message packet
printIdentified(clientbound, packetId);
if (clientbound) {
pluginMessageToClientId = packetId;
} else {
pluginMessageToServerId = packetId;
}
shouldPrint = true;
}
}
} catch (RuntimeException ignored) {
// not the plugin message packet
}
}
// reset index
packet.readerIndex(index);
return shouldPrint;
}
private void printIdentified(boolean clientbound, int packetId) {
logger.info(
"Identified plugin message packet ({}) as {} ({})",
clientbound ? "clientbound" : "serverbound",
packetId,
Integer.toHexString(packetId)
);
}
public State getCurrentState() {
return currentState;
}
}

View File

@@ -25,7 +25,6 @@
package org.geysermc.floodgate.util; package org.geysermc.floodgate.util;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import java.io.BufferedReader; import java.io.BufferedReader;
@@ -47,6 +46,7 @@ import java.util.stream.Collectors;
public class Utils { public class Utils {
private static final Pattern NON_UNIQUE_PREFIX = Pattern.compile("^\\w{0,16}$"); private static final Pattern NON_UNIQUE_PREFIX = Pattern.compile("^\\w{0,16}$");
private static final Pattern DATABASE_NAME = Pattern.compile(Constants.DATABASE_NAME_FORMAT); private static final Pattern DATABASE_NAME = Pattern.compile(Constants.DATABASE_NAME_FORMAT);
public static final int MAX_DEBUG_PACKET_COUNT = 25;
/** /**
* This method is used in Addons.<br> Most addons can be removed once the player associated to * This method is used in Addons.<br> Most addons can be removed once the player associated to
@@ -103,21 +103,6 @@ public class Utils {
return DATABASE_NAME.matcher(databaseName).matches(); return DATABASE_NAME.matcher(databaseName).matches();
} }
public static int readVarInt(ByteBuf buffer) {
int out = 0;
int count = 0;
byte current;
do {
current = buffer.readByte();
out |= (current & 0x7F) << (count++ * 7);
if (count > 5) {
throw new RuntimeException("VarInt is bigger then allowed");
}
} while ((current & 0x80) != 0);
return out;
}
public static String getStackTrace(Throwable throwable) { public static String getStackTrace(Throwable throwable) {
StringWriter writer = new StringWriter(); StringWriter writer = new StringWriter();
throwable.printStackTrace(new PrintWriter(writer)); throwable.printStackTrace(new PrintWriter(writer));