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:
@@ -30,10 +30,10 @@ import io.netty.channel.ChannelPipeline;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.geysermc.floodgate.api.inject.InjectorAddon;
|
||||
import org.geysermc.floodgate.core.addon.debug.ChannelInDebugHandler;
|
||||
import org.geysermc.floodgate.core.addon.debug.ChannelOutDebugHandler;
|
||||
import org.geysermc.floodgate.core.addon.debug.StateChangeDetector;
|
||||
import org.geysermc.floodgate.core.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.core.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.core.util.Utils;
|
||||
@@ -59,16 +59,14 @@ public final class DebugAddon implements InjectorAddon {
|
||||
public void onInject(Channel channel, boolean toServer) {
|
||||
logger.info("Successfully called onInject. To server? {} ({})", toServer, channel.id());
|
||||
|
||||
StateChangeDetector changeDetector = new StateChangeDetector(
|
||||
channel, packetEncoder, packetDecoder, logger
|
||||
);
|
||||
var packetCount = new AtomicInteger();
|
||||
|
||||
channel.pipeline().addBefore(
|
||||
packetEncoder, "floodgate_debug_out",
|
||||
new ChannelOutDebugHandler(implementationName, toServer, changeDetector, logger)
|
||||
new ChannelOutDebugHandler(implementationName, toServer, packetCount, logger)
|
||||
).addBefore(
|
||||
packetDecoder, "floodgate_debug_in",
|
||||
new ChannelInDebugHandler(implementationName, toServer, changeDetector, logger)
|
||||
new ChannelInDebugHandler(implementationName, toServer, packetCount, logger)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,25 +30,25 @@ import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.geysermc.floodgate.core.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.core.util.Constants;
|
||||
|
||||
@Sharable
|
||||
public final class ChannelInDebugHandler extends SimpleChannelInboundHandler<ByteBuf> {
|
||||
private final String direction;
|
||||
private final FloodgateLogger logger;
|
||||
|
||||
private final boolean toServer;
|
||||
private final StateChangeDetector changeDetector;
|
||||
private final AtomicInteger packetCount;
|
||||
|
||||
public ChannelInDebugHandler(
|
||||
String implementationType,
|
||||
boolean toServer,
|
||||
StateChangeDetector changeDetector,
|
||||
AtomicInteger packetCount,
|
||||
FloodgateLogger logger) {
|
||||
this.direction = (toServer ? "Server -> " : "Player -> ") + implementationType;
|
||||
this.logger = logger;
|
||||
this.toServer = toServer;
|
||||
this.changeDetector = changeDetector;
|
||||
this.packetCount = packetCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -56,22 +56,20 @@ public final class ChannelInDebugHandler extends SimpleChannelInboundHandler<Byt
|
||||
try {
|
||||
int index = msg.readerIndex();
|
||||
|
||||
if (changeDetector.shouldPrintPacket(msg, !toServer)) {
|
||||
if (packetCount.getAndIncrement() < Constants.MAX_DEBUG_PACKET_COUNT) {
|
||||
logger.info("{} {}:\n{}",
|
||||
direction,
|
||||
changeDetector.getCurrentState(),
|
||||
packetCount.get(),
|
||||
ByteBufUtil.prettyHexDump(msg)
|
||||
);
|
||||
|
||||
changeDetector.checkPacket(msg, !toServer);
|
||||
}
|
||||
|
||||
// reset index
|
||||
msg.readerIndex(index);
|
||||
|
||||
ctx.fireChannelRead(msg.retain());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} catch (Exception exception) {
|
||||
logger.error("Error in ChannelInDebugHandler", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,25 +30,25 @@ import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.geysermc.floodgate.core.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.core.util.Constants;
|
||||
|
||||
@Sharable
|
||||
public final class ChannelOutDebugHandler extends MessageToByteEncoder<ByteBuf> {
|
||||
private final String direction;
|
||||
private final FloodgateLogger logger;
|
||||
|
||||
private final boolean toServer;
|
||||
private final StateChangeDetector changeDetector;
|
||||
private final AtomicInteger packetCount;
|
||||
|
||||
public ChannelOutDebugHandler(
|
||||
String implementationType,
|
||||
boolean toServer,
|
||||
StateChangeDetector changeDetector,
|
||||
AtomicInteger packetCount,
|
||||
FloodgateLogger logger) {
|
||||
this.direction = implementationType + (toServer ? " -> Server" : " -> Player");
|
||||
this.logger = logger;
|
||||
this.toServer = toServer;
|
||||
this.changeDetector = changeDetector;
|
||||
this.packetCount = packetCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -56,24 +56,21 @@ public final class ChannelOutDebugHandler extends MessageToByteEncoder<ByteBuf>
|
||||
try {
|
||||
int index = msg.readerIndex();
|
||||
|
||||
if (changeDetector.shouldPrintPacket(msg, toServer)) {
|
||||
if (packetCount.getAndIncrement() < Constants.MAX_DEBUG_PACKET_COUNT) {
|
||||
logger.info(
|
||||
"{} {}:\n{}",
|
||||
direction,
|
||||
changeDetector.getCurrentState(),
|
||||
packetCount.get(),
|
||||
ByteBufUtil.prettyHexDump(msg)
|
||||
);
|
||||
|
||||
// proxy acts as a client when it connects to a server
|
||||
changeDetector.checkPacket(msg, toServer);
|
||||
}
|
||||
|
||||
// reset index
|
||||
msg.readerIndex(index);
|
||||
|
||||
out.writeBytes(msg);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} catch (Exception exception) {
|
||||
logger.error("Error in ChannelOutDebugHandler", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2023 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.core.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.core.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.core.util.Constants;
|
||||
import org.geysermc.floodgate.core.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;
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,8 @@ public final class Constants {
|
||||
public static final int CONFIG_VERSION = 3;
|
||||
|
||||
public static final int PROTOCOL_HEX_COLOR = 713; // added in 20w17a (1.16 snapshot)
|
||||
|
||||
public static final int MAX_DEBUG_PACKET_COUNT = 20;
|
||||
public static final boolean DEBUG_MODE = false;
|
||||
public static final boolean PRINT_ALL_PACKETS = false;
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
package org.geysermc.floodgate.core.util;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import java.io.IOException;
|
||||
@@ -119,21 +118,6 @@ public class Utils {
|
||||
return (char) ('A' + codeChar);
|
||||
}
|
||||
|
||||
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) {
|
||||
StringWriter writer = new StringWriter();
|
||||
throwable.printStackTrace(new PrintWriter(writer));
|
||||
|
||||
Reference in New Issue
Block a user