diff --git a/core/src/main/java/org/geysermc/floodgate/addon/DebugAddon.java b/core/src/main/java/org/geysermc/floodgate/addon/DebugAddon.java index 7249e167..dcf9a7a6 100644 --- a/core/src/main/java/org/geysermc/floodgate/addon/DebugAddon.java +++ b/core/src/main/java/org/geysermc/floodgate/addon/DebugAddon.java @@ -29,9 +29,9 @@ import com.google.inject.Inject; import com.google.inject.name.Named; import io.netty.channel.Channel; import io.netty.channel.ChannelPipeline; +import java.util.concurrent.atomic.AtomicInteger; import org.geysermc.floodgate.addon.debug.ChannelInDebugHandler; 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.logger.FloodgateLogger; import org.geysermc.floodgate.config.FloodgateConfig; @@ -57,16 +57,14 @@ public final class DebugAddon implements InjectorAddon { public void onInject(Channel channel, boolean toServer) { logger.info("Successfully called onInject. To server? " + toServer); - StateChangeDetector changeDetector = new StateChangeDetector( - channel, packetEncoder, packetDecoder, logger - ); + AtomicInteger 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) ); } diff --git a/core/src/main/java/org/geysermc/floodgate/addon/debug/ChannelInDebugHandler.java b/core/src/main/java/org/geysermc/floodgate/addon/debug/ChannelInDebugHandler.java index d1b1619c..bf1588e3 100644 --- a/core/src/main/java/org/geysermc/floodgate/addon/debug/ChannelInDebugHandler.java +++ b/core/src/main/java/org/geysermc/floodgate/addon/debug/ChannelInDebugHandler.java @@ -30,25 +30,24 @@ 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.api.logger.FloodgateLogger; +import org.geysermc.floodgate.util.Utils; @Sharable public final class ChannelInDebugHandler extends SimpleChannelInboundHandler { 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,14 +55,12 @@ public final class ChannelInDebugHandler extends SimpleChannelInboundHandler { 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,16 +55,13 @@ public final class ChannelOutDebugHandler extends MessageToByteEncoder try { int index = msg.readerIndex(); - if (changeDetector.shouldPrintPacket(msg, toServer)) { + if (packetCount.getAndIncrement() < Utils.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 diff --git a/core/src/main/java/org/geysermc/floodgate/addon/debug/StateChangeDetector.java b/core/src/main/java/org/geysermc/floodgate/addon/debug/StateChangeDetector.java deleted file mode 100644 index 2c60a1c8..00000000 --- a/core/src/main/java/org/geysermc/floodgate/addon/debug/StateChangeDetector.java +++ /dev/null @@ -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; - } -} diff --git a/core/src/main/java/org/geysermc/floodgate/util/Utils.java b/core/src/main/java/org/geysermc/floodgate/util/Utils.java index 19ef78de..cf4b7f1a 100644 --- a/core/src/main/java/org/geysermc/floodgate/util/Utils.java +++ b/core/src/main/java/org/geysermc/floodgate/util/Utils.java @@ -25,7 +25,6 @@ package org.geysermc.floodgate.util; -import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelPipeline; import java.io.BufferedReader; @@ -47,6 +46,7 @@ import java.util.stream.Collectors; public class Utils { private static final Pattern NON_UNIQUE_PREFIX = Pattern.compile("^\\w{0,16}$"); 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.
Most addons can be removed once the player associated to @@ -103,21 +103,6 @@ public class Utils { 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) { StringWriter writer = new StringWriter(); throwable.printStackTrace(new PrintWriter(writer));