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:
@@ -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)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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));
|
||||||
|
|||||||
Reference in New Issue
Block a user