From af3abbd6f00c3dd8b0824d60d53efb1f85d6e489 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 8 Oct 2021 15:03:10 -0400 Subject: [PATCH] Update to latest Floodgate changes --- .../floodgate/addon/data/FabricDataAddon.java | 5 +- .../addon/data/FabricDataHandler.java | 128 ++++++++++++------ .../mixin/ClientConnectionMixin.java | 13 ++ .../mixin/ServerLoginNetworkHandlerMixin.java | 12 +- .../floodgate/util/FabricCommandUtil.java | 13 +- src/main/resources/floodgate.mixins.json | 1 + 6 files changed, 110 insertions(+), 62 deletions(-) create mode 100644 src/main/java/org/geysermc/floodgate/mixin/ClientConnectionMixin.java 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 385403eb..a59c3891 100644 --- a/src/main/java/org/geysermc/floodgate/addon/data/FabricDataAddon.java +++ b/src/main/java/org/geysermc/floodgate/addon/data/FabricDataAddon.java @@ -28,9 +28,12 @@ public final class FabricDataAddon implements InjectorAddon { @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, logger) + new FabricDataHandler(config, handshakeHandler, blocker, 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 3fd76d80..3b064a25 100644 --- a/src/main/java/org/geysermc/floodgate/addon/data/FabricDataHandler.java +++ b/src/main/java/org/geysermc/floodgate/addon/data/FabricDataHandler.java @@ -1,5 +1,7 @@ package org.geysermc.floodgate.addon.data; +import com.google.common.collect.Queues; +import org.geysermc.floodgate.mixin.ClientConnectionMixin; import org.geysermc.floodgate.mixin_interface.ServerLoginNetworkHandlerSetter; import com.mojang.authlib.GameProfile; import io.netty.channel.ChannelHandlerContext; @@ -18,39 +20,59 @@ import org.geysermc.floodgate.player.FloodgateHandshakeHandler; import org.geysermc.floodgate.util.BedrockData; import org.geysermc.floodgate.util.Constants; +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; private final FloodgateLogger logger; + + private final Queue packetQueue = Queues.newConcurrentLinkedQueue(); + private ClientConnection networkManager; private FloodgatePlayer player; - private boolean done; + + /** + * 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) throws Exception { - ReferenceCountUtil.retain(packet); - if (done) { - super.channelRead(ctx, packet); + 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; } - boolean isHandshake = packet instanceof HandshakeC2SPacket; - boolean isLogin = packet instanceof LoginHelloC2SPacket; - try { - if (isHandshake) { - networkManager = (ClientConnection) ctx.channel().pipeline().get("packet_handler"); + if (packet instanceof HandshakeC2SPacket handshakePacket) { + blocker.enable(); + packetsBlocked = true; + this.handshakePacket = handshakePacket; - HandshakeC2SPacket handshakePacket = (HandshakeC2SPacket) packet; - FloodgateHandshakeHandler.HandshakeResult result = handshakeHandler.handle(ctx.channel(), handshakePacket.getAddress()); + networkManager = (ClientConnection) ctx.channel().pipeline().get("packet_handler"); + + handshakeHandler.handle(ctx.channel(), handshakePacket.getAddress()).thenApply(result -> { HandshakeData handshakeData = result.getHandshakeData(); - packet = new HandshakeC2SPacket(handshakeData.getHostname(), handshakePacket.getPort(), handshakePacket.getIntendedState()); - ReferenceCountUtil.retain(packet); + 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; + return true; } //todo use kickMessageAttribute and let this be common logic @@ -59,9 +81,13 @@ public final class FabricDataHandler extends ChannelInboundHandlerAdapter { 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; + return true; case INVALID_DATA_LENGTH: int dataLength = result.getBedrockData().getDataLength(); logger.info( @@ -69,44 +95,58 @@ public final class FabricDataHandler extends ChannelInboundHandlerAdapter { BedrockData.EXPECTED_LENGTH, dataLength ); ctx.close(); - return; - case TIMESTAMP_DENIED: - logger.info(Constants.TIMESTAMP_DENIED_MESSAGE); - ctx.close(); - return; + return true; default: // only continue when SUCCESS - return; + return true; } player = result.getFloodgatePlayer(); - } else if (isLogin) { - // we have to fake the offline player (login) cycle - if (!(networkManager.getPacketListener() instanceof ServerLoginNetworkHandler)) { - // player is not in the login state, abort - return; + return player == null; + }).thenAccept(shouldRemove -> { + ctx.fireChannelRead(this.handshakePacket); + Object queuedPacket; + while ((queuedPacket = packetQueue.poll()) != null) { + if (checkLogin(ctx, packet)) { + break; + } + ctx.fireChannelRead(queuedPacket); } - GameProfile gameProfile = new GameProfile(player.getCorrectUniqueId(), player.getCorrectUsername()); + if (shouldRemove) { + ctx.pipeline().remove(FabricDataHandler.this); + } + blocker.disable(); + packetsBlocked = false; + }); + return; + } - ((ServerLoginNetworkHandlerSetter) networkManager.getPacketListener()).setGameProfile(gameProfile); - ((ServerLoginNetworkHandlerSetter) networkManager.getPacketListener()).setLoginState(); - } - } finally { - // don't let the packet through if the packet is the login packet - // because we want to skip the login cycle - if (isLogin) { - ReferenceCountUtil.release(packet, 2); - } else { - ctx.fireChannelRead(packet); - } - - if (isLogin || player == null) { - // we're done, we'll just wait for the loginSuccessCall - done = true; - } + if (!checkLogin(ctx, packet)) { + ctx.fireChannelRead(packet); } } + private boolean checkLogin(ChannelHandlerContext ctx, Object packet) { + if (packet instanceof LoginHelloC2SPacket) { + // we have to fake the offline player (login) cycle + if (!(networkManager.getPacketListener() instanceof ServerLoginNetworkHandler)) { + // player is not in the login state, abort + ctx.pipeline().remove(this); + return true; + } + + GameProfile gameProfile = new GameProfile(player.getCorrectUniqueId(), player.getCorrectUsername()); + + ((ServerLoginNetworkHandlerSetter) networkManager.getPacketListener()).setGameProfile(gameProfile); + ((ServerLoginNetworkHandlerSetter) networkManager.getPacketListener()).setLoginState(); + + ctx.pipeline().remove(this); + return true; + } + return false; + } + + @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { super.exceptionCaught(ctx, cause); diff --git a/src/main/java/org/geysermc/floodgate/mixin/ClientConnectionMixin.java b/src/main/java/org/geysermc/floodgate/mixin/ClientConnectionMixin.java new file mode 100644 index 00000000..7392653f --- /dev/null +++ b/src/main/java/org/geysermc/floodgate/mixin/ClientConnectionMixin.java @@ -0,0 +1,13 @@ +package org.geysermc.floodgate.mixin; + +import net.minecraft.network.ClientConnection; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.net.SocketAddress; + +@Mixin(ClientConnection.class) +public interface ClientConnectionMixin { + @Accessor("address") + void setAddress(SocketAddress address); +} diff --git a/src/main/java/org/geysermc/floodgate/mixin/ServerLoginNetworkHandlerMixin.java b/src/main/java/org/geysermc/floodgate/mixin/ServerLoginNetworkHandlerMixin.java index 71049150..b99d0e2a 100644 --- a/src/main/java/org/geysermc/floodgate/mixin/ServerLoginNetworkHandlerMixin.java +++ b/src/main/java/org/geysermc/floodgate/mixin/ServerLoginNetworkHandlerMixin.java @@ -5,19 +5,15 @@ import com.mojang.authlib.GameProfile; import net.minecraft.server.network.ServerLoginNetworkHandler; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.gen.Accessor; @Mixin(ServerLoginNetworkHandler.class) public abstract class ServerLoginNetworkHandlerMixin implements ServerLoginNetworkHandlerSetter { @Shadow - private GameProfile profile; + ServerLoginNetworkHandler.State state; - @Shadow - private ServerLoginNetworkHandler.State state; - - @Override - public void setGameProfile(GameProfile profile) { - this.profile = profile; - } + @Accessor("profile") + public abstract void setGameProfile(GameProfile profile); @Override public void setLoginState() { diff --git a/src/main/java/org/geysermc/floodgate/util/FabricCommandUtil.java b/src/main/java/org/geysermc/floodgate/util/FabricCommandUtil.java index ef001e9b..9040e4a8 100644 --- a/src/main/java/org/geysermc/floodgate/util/FabricCommandUtil.java +++ b/src/main/java/org/geysermc/floodgate/util/FabricCommandUtil.java @@ -7,6 +7,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import me.lucko.fabric.api.permissions.v0.Permissions; import net.kyori.adventure.platform.fabric.FabricServerAudiences; +import net.kyori.adventure.platform.fabric.PlayerLocales; import net.kyori.adventure.platform.fabric.impl.accessor.ConnectionAccess; import net.kyori.adventure.platform.fabric.impl.server.FriendlyByteBufBridge; import net.minecraft.entity.Entity; @@ -71,14 +72,8 @@ public final class FabricCommandUtil implements CommandUtil { } private FabricUserAudience getAudience0(ServerPlayerEntity player) { - // Marked as internal??? Should probably find a better way to get this. - Locale locale; - Channel channel = ((ConnectionAccess) player.networkHandler.getConnection()).getChannel(); - if (channel != null) { - locale = channel.attr(FriendlyByteBufBridge.CHANNEL_LOCALE).get(); - } else { - locale = null; - } + // Apparently can be null even if Javadocs say otherwise + Locale locale = PlayerLocales.locale(player); return new FabricUserAudience.NamedFabricUserAudience( player.getName().asString(), player.getUuid(), locale != null ? @@ -191,7 +186,7 @@ public final class FabricCommandUtil implements CommandUtil { return true; } - protected ServerPlayerEntity getPlayer(Object instance) { + private ServerPlayerEntity getPlayer(Object instance) { try { ServerCommandSource source = (ServerCommandSource) instance; return source.getPlayer(); diff --git a/src/main/resources/floodgate.mixins.json b/src/main/resources/floodgate.mixins.json index 4fdb9935..6aa2ae26 100644 --- a/src/main/resources/floodgate.mixins.json +++ b/src/main/resources/floodgate.mixins.json @@ -4,6 +4,7 @@ "package": "org.geysermc.floodgate.mixin", "compatibilityLevel": "JAVA_16", "mixins": [ + "ClientConnectionMixin", "ServerLoginNetworkHandlerMixin", "ServerNetworkIoMixin" ],