diff --git a/gradle.properties b/gradle.properties index 531ef28e..8e7bf696 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ minecraft_version=1.17.1 yarn_mappings=1.17.1+build.14 loader_version=0.11.6 # Mod Properties -mod_version=2.0-SNAPSHOT +mod_version=2.1.0-SNAPSHOT maven_group=org.geysermc.floodgate archives_base_name=floodgate-fabric # Dependencies diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 62d4c053..e708b1c0 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0f80bbf5..e750102e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index fbd7c515..4f906e0c 100755 --- a/gradlew +++ b/gradlew @@ -130,7 +130,7 @@ fi if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath diff --git a/gradlew.bat b/gradlew.bat index a9f778a7..ac1b06f9 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -54,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -64,21 +64,6 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line @@ -86,7 +71,7 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/src/main/java/org/geysermc/floodgate/addon/data/FabricDataAddon.java b/src/main/java/org/geysermc/floodgate/addon/data/FabricDataAddon.java index a59c3891..6cab89d5 100644 --- a/src/main/java/org/geysermc/floodgate/addon/data/FabricDataAddon.java +++ b/src/main/java/org/geysermc/floodgate/addon/data/FabricDataAddon.java @@ -22,18 +22,19 @@ public final class FabricDataAddon implements InjectorAddon { @Named("packetHandler") private String packetHandlerName; + @Inject + @Named("kickMessageAttribute") + private AttributeKey kickMessageAttribute; + @Inject @Named("playerAttribute") private AttributeKey playerAttribute; @Override public void onInject(Channel channel, boolean toServer) { - PacketBlocker blocker = new PacketBlocker(); - channel.pipeline().addBefore("decoder", "floodgate_packet_blocker", blocker); - channel.pipeline().addBefore( packetHandlerName, "floodgate_data_handler", - new FabricDataHandler(config, handshakeHandler, blocker, logger) + new FabricDataHandler(handshakeHandler, config, kickMessageAttribute, logger) ); } diff --git a/src/main/java/org/geysermc/floodgate/addon/data/FabricDataHandler.java b/src/main/java/org/geysermc/floodgate/addon/data/FabricDataHandler.java index 3b064a25..b0b769d9 100644 --- a/src/main/java/org/geysermc/floodgate/addon/data/FabricDataHandler.java +++ b/src/main/java/org/geysermc/floodgate/addon/data/FabricDataHandler.java @@ -1,133 +1,91 @@ package org.geysermc.floodgate.addon.data; -import com.google.common.collect.Queues; +import io.netty.channel.Channel; +import io.netty.util.AttributeKey; +import net.minecraft.text.Text; +import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.mixin.ClientConnectionMixin; +import org.geysermc.floodgate.mixin.ClientIntentionPacketMixin; import org.geysermc.floodgate.mixin_interface.ServerLoginNetworkHandlerSetter; import com.mojang.authlib.GameProfile; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.util.ReferenceCountUtil; -import lombok.RequiredArgsConstructor; import net.minecraft.network.ClientConnection; import net.minecraft.network.packet.c2s.handshake.HandshakeC2SPacket; import net.minecraft.network.packet.c2s.login.LoginHelloC2SPacket; import net.minecraft.server.network.ServerLoginNetworkHandler; -import org.geysermc.floodgate.api.handshake.HandshakeData; -import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.config.FloodgateConfig; import org.geysermc.floodgate.player.FloodgateHandshakeHandler; -import org.geysermc.floodgate.util.BedrockData; -import org.geysermc.floodgate.util.Constants; +import org.geysermc.floodgate.player.FloodgateHandshakeHandler.HandshakeResult; import java.net.InetSocketAddress; -import java.util.Queue; -@RequiredArgsConstructor -public final class FabricDataHandler extends ChannelInboundHandlerAdapter { - private final FloodgateConfig config; - private final FloodgateHandshakeHandler handshakeHandler; - private final PacketBlocker blocker; +public final class FabricDataHandler extends CommonDataHandler { private final FloodgateLogger logger; - - private final Queue packetQueue = Queues.newConcurrentLinkedQueue(); - private ClientConnection networkManager; private FloodgatePlayer player; - /** - * As a variable so we can change it without reflection - */ - HandshakeC2SPacket handshakePacket; - /** - * A boolean to compensate for the above - */ - private boolean packetsBlocked; - - @Override - public void channelRead(ChannelHandlerContext ctx, Object packet) { - // prevent other packets from being handled while we handle the handshake packet - if (packetsBlocked) { - packetQueue.add(packet); - return; - } - - if (packet instanceof HandshakeC2SPacket handshakePacket) { - blocker.enable(); - packetsBlocked = true; - this.handshakePacket = handshakePacket; - - networkManager = (ClientConnection) ctx.channel().pipeline().get("packet_handler"); - - handshakeHandler.handle(ctx.channel(), handshakePacket.getAddress()).thenApply(result -> { - HandshakeData handshakeData = result.getHandshakeData(); - - this.handshakePacket = new HandshakeC2SPacket(handshakeData.getHostname(), - handshakePacket.getPort(), handshakePacket.getIntendedState()); - - InetSocketAddress newIp = result.getNewIp(ctx.channel()); - if (newIp != null) { - ((ClientConnectionMixin) networkManager).setAddress(newIp); - } - - if (handshakeData.getDisconnectReason() != null) { - ctx.close(); //todo disconnect with message - return true; - } - - //todo use kickMessageAttribute and let this be common logic - - switch (result.getResultType()) { - case SUCCESS: - break; - case EXCEPTION: - logger.info(Constants.INTERNAL_ERROR_MESSAGE); - ctx.close(); - return true; - case DECRYPT_ERROR: - logger.info(config.getDisconnect().getInvalidKey()); - ctx.close(); - return true; - case INVALID_DATA_LENGTH: - int dataLength = result.getBedrockData().getDataLength(); - logger.info( - config.getDisconnect().getInvalidArgumentsLength(), - BedrockData.EXPECTED_LENGTH, dataLength - ); - ctx.close(); - return true; - default: // only continue when SUCCESS - return true; - } - - player = result.getFloodgatePlayer(); - return player == null; - }).thenAccept(shouldRemove -> { - ctx.fireChannelRead(this.handshakePacket); - Object queuedPacket; - while ((queuedPacket = packetQueue.poll()) != null) { - if (checkLogin(ctx, packet)) { - break; - } - ctx.fireChannelRead(queuedPacket); - } - - if (shouldRemove) { - ctx.pipeline().remove(FabricDataHandler.this); - } - blocker.disable(); - packetsBlocked = false; - }); - return; - } - - if (!checkLogin(ctx, packet)) { - ctx.fireChannelRead(packet); - } + public FabricDataHandler( + FloodgateHandshakeHandler handshakeHandler, + FloodgateConfig config, + AttributeKey kickMessageAttribute, FloodgateLogger logger) { + super(handshakeHandler, config, kickMessageAttribute, new PacketBlocker()); + this.logger = logger; } - private boolean checkLogin(ChannelHandlerContext ctx, Object packet) { + @Override + protected void setNewIp(Channel channel, InetSocketAddress newIp) { + ((ClientConnectionMixin) this.networkManager).setAddress(newIp); + } + + @Override + protected Object setHostname(Object handshakePacket, String hostname) { + // While it would be ideal to simply create a new handshake packet, the packet constructor + // does not allow us to set the protocol version + ((ClientIntentionPacketMixin) handshakePacket).setAddress(hostname); + return handshakePacket; + } + + @Override + protected boolean shouldRemoveHandler(HandshakeResult result) { + player = result.getFloodgatePlayer(); + + if (getKickMessage() != null) { + // we also have to keep this handler if we want to kick then with a disconnect message + return false; + } else if (player == null) { + // player is not a Floodgate player + return true; + } + + if (result.getResultType() == FloodgateHandshakeHandler.ResultType.SUCCESS) { + logger.info("Floodgate player who is logged in as {} {} joined", + player.getCorrectUsername(), player.getCorrectUniqueId()); + } + + // Handler will be removed after the login hello packet is handled + return false; + } + + @Override + protected boolean channelRead(Object packet) { + if (packet instanceof HandshakeC2SPacket handshakePacket) { + ctx.pipeline().addAfter("splitter", "floodgate_packet_blocker", blocker); + networkManager = (ClientConnection) ctx.channel().pipeline().get("packet_handler"); + handle(packet, handshakePacket.getAddress()); + return false; + } + return !checkAndHandleLogin(packet); + } + + private boolean checkAndHandleLogin(Object packet) { if (packet instanceof LoginHelloC2SPacket) { + String kickMessage = getKickMessage(); + if (kickMessage != null) { + networkManager.disconnect(Text.of(kickMessage)); + return true; + } + // we have to fake the offline player (login) cycle if (!(networkManager.getPacketListener() instanceof ServerLoginNetworkHandler)) { // player is not in the login state, abort diff --git a/src/main/java/org/geysermc/floodgate/mixin/ClientIntentionPacketMixin.java b/src/main/java/org/geysermc/floodgate/mixin/ClientIntentionPacketMixin.java new file mode 100644 index 00000000..227729ce --- /dev/null +++ b/src/main/java/org/geysermc/floodgate/mixin/ClientIntentionPacketMixin.java @@ -0,0 +1,14 @@ +package org.geysermc.floodgate.mixin; + +import net.minecraft.network.packet.c2s.handshake.HandshakeC2SPacket; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(HandshakeC2SPacket.class) +public interface ClientIntentionPacketMixin { + + @Accessor("address") + @Mutable + void setAddress(String address); +} diff --git a/src/main/java/org/geysermc/floodgate/mixin_interface/ServerLoginNetworkHandlerSetter.java b/src/main/java/org/geysermc/floodgate/mixin_interface/ServerLoginNetworkHandlerSetter.java index f9e8c2be..fb94a607 100644 --- a/src/main/java/org/geysermc/floodgate/mixin_interface/ServerLoginNetworkHandlerSetter.java +++ b/src/main/java/org/geysermc/floodgate/mixin_interface/ServerLoginNetworkHandlerSetter.java @@ -1,9 +1,6 @@ package org.geysermc.floodgate.mixin_interface; import com.mojang.authlib.GameProfile; -import net.minecraft.server.network.ServerLoginNetworkHandler; - -import java.util.UUID; public interface ServerLoginNetworkHandlerSetter { void setGameProfile(GameProfile profile); diff --git a/src/main/resources/floodgate.mixins.json b/src/main/resources/floodgate.mixins.json index 6aa2ae26..a19fd613 100644 --- a/src/main/resources/floodgate.mixins.json +++ b/src/main/resources/floodgate.mixins.json @@ -6,7 +6,8 @@ "mixins": [ "ClientConnectionMixin", "ServerLoginNetworkHandlerMixin", - "ServerNetworkIoMixin" + "ServerNetworkIoMixin", + "ClientIntentionPacketMixin" ], "injectors": { "defaultRequire": 1