From 3eb607772da0a903347459373f165077d50bfc99 Mon Sep 17 00:00:00 2001 From: Tim203 Date: Mon, 18 Jan 2021 01:34:19 +0100 Subject: [PATCH] Stopped using disconnect and (Bungee's) handshake event Instead we're listening for the actual channel to close. --- .../floodgate/api/inject/InjectorAddon.java | 10 + .../floodgate/addon/data/BungeeDataAddon.java | 100 ++++++++++ .../addon/data/BungeeProxyDataHandler.java | 128 +++++++++++++ .../addon/data/BungeeServerDataHandler.java | 112 +++++++++++ .../floodgate/handler/BungeeDataHandler.java | 174 ------------------ .../inject/bungee/BungeeInjector.java | 4 + .../floodgate/listener/BungeeListener.java | 86 +++++---- .../floodgate/module/BungeeAddonModule.java | 7 + .../floodgate/util/BungeeUserAudience.java | 4 - .../addon/data/HandshakeDataImpl.java | 24 ++- .../floodgate/api/SimpleFloodgateApi.java | 5 +- .../floodgate/command/TestCommand.java | 58 ++++++ .../inject/CommonPlatformInjector.java | 18 ++ .../floodgate/module/CommandModule.java | 7 + .../floodgate/module/CommonModule.java | 7 + .../geysermc/floodgate/util/Constants.java | 2 + .../floodgate/addon/data/SpigotDataAddon.java | 16 ++ .../addon/data/SpigotDataHandler.java | 12 +- .../inject/spigot/SpigotInjector.java | 14 +- .../floodgate/listener/SpigotListener.java | 18 +- .../floodgate/util/SpigotUserAudience.java | 4 - .../addon/data/VelocityDataAddon.java | 14 ++ .../addon/data/VelocityProxyDataHandler.java | 13 +- .../addon/data/VelocityServerDataHandler.java | 10 + .../inject/velocity/VelocityInjector.java | 4 + .../floodgate/listener/VelocityListener.java | 21 +-- .../module/VelocityPlatformModule.java | 8 - 27 files changed, 591 insertions(+), 289 deletions(-) create mode 100644 bungee/src/main/java/org/geysermc/floodgate/addon/data/BungeeDataAddon.java create mode 100644 bungee/src/main/java/org/geysermc/floodgate/addon/data/BungeeProxyDataHandler.java create mode 100644 bungee/src/main/java/org/geysermc/floodgate/addon/data/BungeeServerDataHandler.java delete mode 100644 bungee/src/main/java/org/geysermc/floodgate/handler/BungeeDataHandler.java create mode 100644 common/src/main/java/org/geysermc/floodgate/command/TestCommand.java diff --git a/api/src/main/java/org/geysermc/floodgate/api/inject/InjectorAddon.java b/api/src/main/java/org/geysermc/floodgate/api/inject/InjectorAddon.java index 8385e339..6a25ec88 100644 --- a/api/src/main/java/org/geysermc/floodgate/api/inject/InjectorAddon.java +++ b/api/src/main/java/org/geysermc/floodgate/api/inject/InjectorAddon.java @@ -47,6 +47,16 @@ public interface InjectorAddon { */ void onLoginDone(Channel channel); + /** + * Called when the channel has been closed. Note that this method will be called for every + * closed connection (if it is injected), so it'll also run this method for closed connections + * between a server and the proxy (when Floodgate is running on a proxy). + * + * @param channel the channel that the injecor injected + */ + default void onChannelClosed(Channel channel) { + } + /** * Called when Floodgate is removing the injection from the server. The addon should remove his * traces otherwise it is likely that an error will popup after the server is injected again. diff --git a/bungee/src/main/java/org/geysermc/floodgate/addon/data/BungeeDataAddon.java b/bungee/src/main/java/org/geysermc/floodgate/addon/data/BungeeDataAddon.java new file mode 100644 index 00000000..7bbc75f6 --- /dev/null +++ b/bungee/src/main/java/org/geysermc/floodgate/addon/data/BungeeDataAddon.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2019-2021 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.data; + +import com.google.inject.Inject; +import com.google.inject.name.Named; +import io.netty.channel.Channel; +import io.netty.util.AttributeKey; +import org.geysermc.floodgate.api.ProxyFloodgateApi; +import org.geysermc.floodgate.api.inject.InjectorAddon; +import org.geysermc.floodgate.api.logger.FloodgateLogger; +import org.geysermc.floodgate.api.player.FloodgatePlayer; +import org.geysermc.floodgate.config.ProxyFloodgateConfig; +import org.geysermc.floodgate.player.FloodgateHandshakeHandler; +import org.geysermc.floodgate.util.Utils; + +public class BungeeDataAddon implements InjectorAddon { + @Inject private FloodgateHandshakeHandler handshakeHandler; + @Inject private ProxyFloodgateConfig config; + @Inject private ProxyFloodgateApi api; + @Inject private FloodgateLogger logger; + + @Inject + @Named("packetHandler") + private String packetHandler; + + @Inject + @Named("packetEncoder") + private String packetEncoder; + + @Inject + @Named("kickMessageAttribute") + private AttributeKey kickMessageAttribute; + + @Inject + @Named("playerAttribute") + private AttributeKey playerAttribute; + + @Override + public void onInject(Channel channel, boolean toServer) { + if (toServer) { + channel.pipeline().addAfter( + packetEncoder, "floodgate_data_handler", + new BungeeServerDataHandler(config, api, playerAttribute) + ); + return; + } + channel.pipeline().addBefore( + packetHandler, "floodgate_data_handler", + new BungeeProxyDataHandler(config, handshakeHandler, kickMessageAttribute) + ); + } + + @Override + public void onLoginDone(Channel channel) { + onRemoveInject(channel); + } + + @Override + public void onChannelClosed(Channel channel) { + FloodgatePlayer player = channel.attr(playerAttribute).get(); + if (player != null && api.removePlayer(player)) { + api.removeEncryptedData(player.getCorrectUniqueId()); + logger.translatedInfo("floodgate.ingame.disconnect_name", player.getCorrectUsername()); + } + } + + @Override + public void onRemoveInject(Channel channel) { + Utils.removeHandler(channel.pipeline(), "floodgate_data_handler"); + } + + @Override + public boolean shouldInject() { + return true; + } +} diff --git a/bungee/src/main/java/org/geysermc/floodgate/addon/data/BungeeProxyDataHandler.java b/bungee/src/main/java/org/geysermc/floodgate/addon/data/BungeeProxyDataHandler.java new file mode 100644 index 00000000..9d12ca92 --- /dev/null +++ b/bungee/src/main/java/org/geysermc/floodgate/addon/data/BungeeProxyDataHandler.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2019-2021 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.data; + +import static com.google.common.base.Preconditions.checkNotNull; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.util.AttributeKey; +import io.netty.util.ReferenceCountUtil; +import java.lang.reflect.Field; +import java.net.InetSocketAddress; +import lombok.RequiredArgsConstructor; +import net.md_5.bungee.connection.InitialHandler; +import net.md_5.bungee.netty.ChannelWrapper; +import net.md_5.bungee.netty.HandlerBoss; +import net.md_5.bungee.protocol.DefinedPacket; +import net.md_5.bungee.protocol.PacketWrapper; +import net.md_5.bungee.protocol.packet.Handshake; +import org.geysermc.floodgate.api.handshake.HandshakeData; +import org.geysermc.floodgate.api.player.PropertyKey; +import org.geysermc.floodgate.config.ProxyFloodgateConfig; +import org.geysermc.floodgate.player.FloodgateHandshakeHandler; +import org.geysermc.floodgate.player.FloodgateHandshakeHandler.HandshakeResult; +import org.geysermc.floodgate.player.FloodgateHandshakeHandler.ResultType; +import org.geysermc.floodgate.util.ReflectionUtils; + +@SuppressWarnings("ConstantConditions") +@RequiredArgsConstructor +public class BungeeProxyDataHandler extends ChannelInboundHandlerAdapter { + private static final Field HANDLER; + private static final Field CHANNEL_WRAPPER; + + static { + HANDLER = ReflectionUtils.getField(HandlerBoss.class, "handler"); + checkNotNull(HANDLER, "handler field cannot be null"); + + CHANNEL_WRAPPER = + ReflectionUtils.getFieldOfType(InitialHandler.class, ChannelWrapper.class); + checkNotNull(CHANNEL_WRAPPER, "ChannelWrapper field cannot be null"); + } + + private final ProxyFloodgateConfig config; + private final FloodgateHandshakeHandler handler; + private final AttributeKey kickMessageAttribute; + private boolean done; + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + ReferenceCountUtil.retain(msg); + if (done || !(msg instanceof PacketWrapper)) { + ctx.fireChannelRead(msg); + return; + } + + DefinedPacket packet = ((PacketWrapper) msg).packet; + + // we're only interested in the Handshake packet + if (packet instanceof Handshake) { + handleHandshake(ctx, (Handshake) packet); + done = true; + } + + ctx.fireChannelRead(msg); + } + + private void handleHandshake(ChannelHandlerContext ctx, Handshake packet) { + String data = packet.getHost(); + + HandshakeResult result = handler.handle(ctx.channel(), data); + HandshakeData handshakeData = result.getHandshakeData(); + + // we'll change the IP address from the proxy to the IP of the Bedrock client very early on + // so that almost every plugin will use the IP of the Bedrock client + if (result.getFloodgatePlayer() != null) { + + HandlerBoss handlerBoss = ctx.pipeline().get(HandlerBoss.class); + // InitialHandler extends PacketHandler and implements PendingConnection + InitialHandler connection = ReflectionUtils.getCastedValue(handlerBoss, HANDLER); + + ChannelWrapper channelWrapper = + ReflectionUtils.getCastedValue(connection, CHANNEL_WRAPPER); + + InetSocketAddress address = + result.getFloodgatePlayer().getProperty(PropertyKey.SOCKET_ADDRESS); + + channelWrapper.setRemoteAddress(address); + } + + if (handshakeData.getDisconnectReason() != null) { + ctx.channel().attr(kickMessageAttribute).set(handshakeData.getDisconnectReason()); + return; + } + + if (result.getResultType() == ResultType.EXCEPTION) { + ctx.channel().attr(kickMessageAttribute).set( + config.getDisconnect().getInvalidKey()); + } + + if (result.getResultType() == ResultType.INVALID_DATA_LENGTH) { + ctx.channel().attr(kickMessageAttribute) + .set(config.getDisconnect().getInvalidArgumentsLength()); + } + } +} diff --git a/bungee/src/main/java/org/geysermc/floodgate/addon/data/BungeeServerDataHandler.java b/bungee/src/main/java/org/geysermc/floodgate/addon/data/BungeeServerDataHandler.java new file mode 100644 index 00000000..c50f7ffb --- /dev/null +++ b/bungee/src/main/java/org/geysermc/floodgate/addon/data/BungeeServerDataHandler.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2019-2021 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.data; + +import static com.google.common.base.Preconditions.checkNotNull; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageEncoder; +import io.netty.util.AttributeKey; +import io.netty.util.ReferenceCountUtil; +import java.lang.reflect.Field; +import java.util.List; +import lombok.RequiredArgsConstructor; +import net.md_5.bungee.ServerConnector; +import net.md_5.bungee.UserConnection; +import net.md_5.bungee.netty.ChannelWrapper; +import net.md_5.bungee.netty.HandlerBoss; +import net.md_5.bungee.protocol.packet.Handshake; +import org.geysermc.floodgate.api.ProxyFloodgateApi; +import org.geysermc.floodgate.api.player.FloodgatePlayer; +import org.geysermc.floodgate.config.ProxyFloodgateConfig; +import org.geysermc.floodgate.util.ReflectionUtils; + +@SuppressWarnings("ConstantConditions") +@RequiredArgsConstructor +public class BungeeServerDataHandler extends MessageToMessageEncoder { + private static final Field HANDLER; + private static final Field USER_CONNECTION; + private static final Field CHANNEL_WRAPPER; + + static { + HANDLER = ReflectionUtils.getField(HandlerBoss.class, "handler"); + checkNotNull(HANDLER, "handler field cannot be null"); + + USER_CONNECTION = ReflectionUtils.getField(ServerConnector.class, "user"); + checkNotNull(USER_CONNECTION, "user field cannot be null"); + + CHANNEL_WRAPPER = + ReflectionUtils.getFieldOfType(UserConnection.class, ChannelWrapper.class); + checkNotNull(CHANNEL_WRAPPER, "ChannelWrapper field cannot be null"); + } + + private final ProxyFloodgateConfig config; + private final ProxyFloodgateApi api; + private final AttributeKey playerAttribute; + private boolean done; + + @Override + protected void encode(ChannelHandlerContext ctx, Object packet, List out) { + ReferenceCountUtil.retain(packet); + if (done) { + out.add(packet); + return; + } + + // passes the information through to the connecting server if enabled + if (!(packet instanceof Handshake) || !config.isSendFloodgateData()) { + done = true; + out.add(packet); + return; + } + + // get the Proxy <-> Player channel from the Proxy <-> Server channel + HandlerBoss handlerBoss = ctx.pipeline().get(HandlerBoss.class); + ServerConnector connector = ReflectionUtils.getCastedValue(handlerBoss, HANDLER); + UserConnection connection = ReflectionUtils.getCastedValue(connector, USER_CONNECTION); + ChannelWrapper wrapper = ReflectionUtils.getCastedValue(connection, CHANNEL_WRAPPER); + + FloodgatePlayer player = wrapper.getHandle().attr(playerAttribute).get(); + + if (player != null) { + String encryptedData = api.getEncryptedData(player.getCorrectUniqueId()); + + Handshake handshake = (Handshake) packet; + String address = handshake.getHost(); + + // our data goes before all the other data + int addressFinished = address.indexOf('\0'); + String originalAddress = address.substring(0, addressFinished); + String remaining = address.substring(addressFinished); + + handshake.setHost(originalAddress + '\0' + encryptedData + remaining); + // Bungeecord will add his data after our data + } + + done = true; + out.add(packet); + } +} diff --git a/bungee/src/main/java/org/geysermc/floodgate/handler/BungeeDataHandler.java b/bungee/src/main/java/org/geysermc/floodgate/handler/BungeeDataHandler.java deleted file mode 100644 index df0e4369..00000000 --- a/bungee/src/main/java/org/geysermc/floodgate/handler/BungeeDataHandler.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (c) 2019-2021 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.handler; - -import static com.google.common.base.Preconditions.checkNotNull; -import static org.geysermc.floodgate.player.FloodgateHandshakeHandler.ResultType; - -import com.google.inject.Inject; -import io.netty.channel.Channel; -import java.lang.reflect.Field; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import net.md_5.bungee.api.chat.TextComponent; -import net.md_5.bungee.api.connection.PendingConnection; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.event.PreLoginEvent; -import net.md_5.bungee.api.plugin.Plugin; -import net.md_5.bungee.protocol.packet.Handshake; -import org.geysermc.floodgate.api.ProxyFloodgateApi; -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.api.player.PropertyKey; -import org.geysermc.floodgate.config.ProxyFloodgateConfig; -import org.geysermc.floodgate.player.FloodgateHandshakeHandler; -import org.geysermc.floodgate.player.FloodgateHandshakeHandler.HandshakeResult; -import org.geysermc.floodgate.util.BedrockData; -import org.geysermc.floodgate.util.ReflectionUtils; - -public final class BungeeDataHandler { - private static final Field EXTRA_HANDSHAKE_DATA; - private static final Field PLAYER_NAME; - private static final Field PLAYER_CHANNEL_WRAPPER; - private static final Field PLAYER_CHANNEL; - private static final Field PLAYER_REMOTE_ADDRESS; - private static final Field CACHED_HANDSHAKE_PACKET; - - static { - Class initialHandler = ReflectionUtils.getPrefixedClass("connection.InitialHandler"); - EXTRA_HANDSHAKE_DATA = ReflectionUtils.getField(initialHandler, "extraDataInHandshake"); - checkNotNull(EXTRA_HANDSHAKE_DATA, "extraDataInHandshake field cannot be null"); - - PLAYER_NAME = ReflectionUtils.getField(initialHandler, "name"); - checkNotNull(PLAYER_NAME, "Initial name field cannot be null"); - - Class channelWrapper = ReflectionUtils.getPrefixedClass("netty.ChannelWrapper"); - PLAYER_CHANNEL_WRAPPER = ReflectionUtils.getFieldOfType(initialHandler, channelWrapper); - checkNotNull(PLAYER_CHANNEL_WRAPPER, "ChannelWrapper field cannot be null"); - - PLAYER_CHANNEL = ReflectionUtils.getFieldOfType(channelWrapper, Channel.class); - checkNotNull(PLAYER_CHANNEL, "Channel field cannot be null"); - - PLAYER_REMOTE_ADDRESS = ReflectionUtils.getFieldOfType(channelWrapper, SocketAddress.class); - checkNotNull(PLAYER_REMOTE_ADDRESS, "Remote address field cannot be null"); - - Class handshakePacket = ReflectionUtils.getPrefixedClass("protocol.packet.Handshake"); - CACHED_HANDSHAKE_PACKET = ReflectionUtils.getFieldOfType(initialHandler, handshakePacket); - checkNotNull(CACHED_HANDSHAKE_PACKET, "Cached handshake packet field cannot be null"); - } - - @Inject private Plugin plugin; - @Inject private ProxyFloodgateConfig config; - @Inject private ProxyFloodgateApi api; - @Inject private FloodgateHandshakeHandler handler; - @Inject private FloodgateLogger logger; - - public void handlePreLogin(PreLoginEvent event) { - event.registerIntent(plugin); - plugin.getProxy().getScheduler().runAsync(plugin, () -> { - PendingConnection connection = event.getConnection(); - - String extraData = ReflectionUtils.getCastedValue(connection, EXTRA_HANDSHAKE_DATA); - - Object channelWrapper = ReflectionUtils.getValue(connection, PLAYER_CHANNEL_WRAPPER); - Channel channel = ReflectionUtils.getCastedValue(channelWrapper, PLAYER_CHANNEL); - - HandshakeResult result = handler.handle(channel, extraData); - HandshakeData handshakeData = result.getHandshakeData(); - - if (handshakeData.getDisconnectReason() != null) { - //noinspection ConstantConditions - channel.close(); // todo disconnect with message - return; - } - - if (result.getResultType() == ResultType.EXCEPTION) { - event.setCancelReason(config.getDisconnect().getInvalidKey()); - event.completeIntent(plugin); - return; - } - - if (result.getResultType() == ResultType.INVALID_DATA_LENGTH) { - event.setCancelReason(TextComponent.fromLegacyText(String.format( - config.getDisconnect().getInvalidArgumentsLength(), - BedrockData.EXPECTED_LENGTH, result.getBedrockData().getDataLength() - ))); - event.completeIntent(plugin); - return; - } - - // only continue when SUCCESS - if (result.getResultType() != ResultType.SUCCESS) { - event.completeIntent(plugin); - return; - } - - FloodgatePlayer player = result.getFloodgatePlayer(); - - connection.setOnlineMode(false); - connection.setUniqueId(player.getCorrectUniqueId()); - - ReflectionUtils.setValue(connection, PLAYER_NAME, player.getCorrectUsername()); - - SocketAddress remoteAddress = - ReflectionUtils.getCastedValue(channelWrapper, PLAYER_REMOTE_ADDRESS); - - if (!(remoteAddress instanceof InetSocketAddress)) { - logger.info("Player {} doesn't use an InetSocketAddress, it uses {}. " + - "Ignoring the player, I guess.", - player.getUsername(), remoteAddress.getClass().getSimpleName() - ); - event.setCancelled(true); - event.setCancelReason( - new TextComponent("remoteAddress is not an InetSocketAddress!")); - event.completeIntent(plugin); - return; - } - - InetSocketAddress correctAddress = player.getProperty(PropertyKey.SOCKET_ADDRESS); - ReflectionUtils.setValue(channelWrapper, PLAYER_REMOTE_ADDRESS, correctAddress); - - event.completeIntent(plugin); - }); - } - - public void handleServerConnect(ProxiedPlayer player) { - // Passes the information through to the connecting server if enabled - if (config.isSendFloodgateData() && api.isFloodgatePlayer(player.getUniqueId())) { - Handshake handshake = ReflectionUtils.getCastedValue( - player.getPendingConnection(), CACHED_HANDSHAKE_PACKET - ); - - // Ensures that only the hostname remains, - // this way it can't mess up the Floodgate data format - String initialHostname = handshake.getHost().split("\0")[0]; - handshake.setHost(initialHostname + '\0' + api.getEncryptedData(player.getUniqueId())); - - // Bungeecord will add his data after our data - } - } -} diff --git a/bungee/src/main/java/org/geysermc/floodgate/inject/bungee/BungeeInjector.java b/bungee/src/main/java/org/geysermc/floodgate/inject/bungee/BungeeInjector.java index 45609fac..cfc0a8fb 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/inject/bungee/BungeeInjector.java +++ b/bungee/src/main/java/org/geysermc/floodgate/inject/bungee/BungeeInjector.java @@ -96,5 +96,9 @@ public final class BungeeInjector extends CommonPlatformInjector { public void injectClient(Channel channel, boolean clientToProxy) { injectAddonsCall(channel, !clientToProxy); addInjectedClient(channel); + channel.closeFuture().addListener(listener -> { + channelClosedCall(channel); + removeInjectedClient(channel); + }); } } diff --git a/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java b/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java index 2defc012..61b7910a 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java +++ b/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java @@ -25,32 +25,50 @@ package org.geysermc.floodgate.listener; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.inject.Inject; -import com.google.inject.Injector; +import com.google.inject.name.Named; +import io.netty.channel.Channel; +import io.netty.util.AttributeKey; +import java.lang.reflect.Field; import java.util.UUID; -import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.connection.PendingConnection; import net.md_5.bungee.api.event.LoginEvent; import net.md_5.bungee.api.event.PlayerDisconnectEvent; import net.md_5.bungee.api.event.PreLoginEvent; -import net.md_5.bungee.api.event.ServerConnectEvent; import net.md_5.bungee.api.event.ServerConnectedEvent; import net.md_5.bungee.api.plugin.Listener; +import net.md_5.bungee.connection.InitialHandler; import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.event.EventPriority; +import net.md_5.bungee.netty.ChannelWrapper; import org.geysermc.floodgate.api.ProxyFloodgateApi; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.api.player.PropertyKey; import org.geysermc.floodgate.config.ProxyFloodgateConfig; -import org.geysermc.floodgate.handler.BungeeDataHandler; import org.geysermc.floodgate.player.FloodgatePlayerImpl; import org.geysermc.floodgate.pluginmessage.BungeePluginMessageHandler; import org.geysermc.floodgate.skin.SkinHandler; import org.geysermc.floodgate.util.BungeeCommandUtil; import org.geysermc.floodgate.util.LanguageManager; +import org.geysermc.floodgate.util.ReflectionUtils; +@SuppressWarnings("ConstantConditions") public final class BungeeListener implements Listener { - private BungeeDataHandler dataHandler; + private static final Field CHANNEL_WRAPPER; + private static final Field PLAYER_NAME; + + static { + CHANNEL_WRAPPER = + ReflectionUtils.getFieldOfType(InitialHandler.class, ChannelWrapper.class); + checkNotNull(CHANNEL_WRAPPER, "ChannelWrapper field cannot be null"); + + PLAYER_NAME = ReflectionUtils.getField(InitialHandler.class, "name"); + checkNotNull(PLAYER_NAME, "Initial name field cannot be null"); + } + @Inject private ProxyFloodgateApi api; @Inject private LanguageManager languageManager; @Inject private FloodgateLogger logger; @@ -60,14 +78,12 @@ public final class BungeeListener implements Listener { @Inject private SkinHandler skinHandler; @Inject - public void init(Injector injector) { - dataHandler = injector.getInstance(BungeeDataHandler.class); - } + @Named("playerAttribute") + private AttributeKey playerAttribute; - @EventHandler(priority = EventPriority.LOW) - public void onServerConnect(ServerConnectEvent event) { - dataHandler.handleServerConnect(event.getPlayer()); - } + @Inject + @Named("kickMessageAttribute") + private AttributeKey kickMessageAttribute; @EventHandler public void onServerConnected(ServerConnectedEvent event) { @@ -89,15 +105,31 @@ public final class BungeeListener implements Listener { } } - @EventHandler(priority = EventPriority.LOW) + @EventHandler(priority = EventPriority.LOWEST) public void onPreLogin(PreLoginEvent event) { - dataHandler.handlePreLogin(event); - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void onPreLoginMonitor(PreLoginEvent event) { + // well, no reason to check if the player will be kicked anyway if (event.isCancelled()) { - api.removePlayer(event.getConnection().getUniqueId(), true); + return; + } + + PendingConnection connection = event.getConnection(); + + ChannelWrapper wrapper = ReflectionUtils.getCastedValue(connection, CHANNEL_WRAPPER); + Channel channel = wrapper.getHandle(); + + // check if the player has to be kicked + String kickReason = channel.attr(kickMessageAttribute).get(); + if (kickReason != null) { + event.setCancelled(true); + event.setCancelReason(kickReason); + return; + } + + FloodgatePlayer player = channel.attr(playerAttribute).get(); + if (player != null) { + connection.setOnlineMode(false); + connection.setUniqueId(player.getCorrectUniqueId()); + ReflectionUtils.setValue(connection, PLAYER_NAME, player.getCorrectUsername()); } } @@ -117,22 +149,8 @@ public final class BungeeListener implements Listener { } } - @EventHandler(priority = EventPriority.HIGHEST) - public void onLoginMonitor(LoginEvent event) { - if (event.isCancelled()) { - api.removePlayer(event.getConnection().getUniqueId()); - } - } - @EventHandler(priority = EventPriority.HIGHEST) public void onPlayerDisconnect(PlayerDisconnectEvent event) { - ProxiedPlayer player = event.getPlayer(); - - BungeeCommandUtil.AUDIENCE_CACHE.remove(player.getUniqueId()); //todo - - if (api.removePlayer(player.getUniqueId()) != null) { - api.removeEncryptedData(player.getUniqueId()); - logger.translatedInfo("floodgate.ingame.disconnect_name", player.getName()); - } + BungeeCommandUtil.AUDIENCE_CACHE.remove(event.getPlayer().getUniqueId()); //todo } } diff --git a/bungee/src/main/java/org/geysermc/floodgate/module/BungeeAddonModule.java b/bungee/src/main/java/org/geysermc/floodgate/module/BungeeAddonModule.java index ea45a110..95701696 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/module/BungeeAddonModule.java +++ b/bungee/src/main/java/org/geysermc/floodgate/module/BungeeAddonModule.java @@ -30,6 +30,7 @@ import com.google.inject.Singleton; import com.google.inject.multibindings.ProvidesIntoSet; import org.geysermc.floodgate.addon.AddonManagerAddon; import org.geysermc.floodgate.addon.DebugAddon; +import org.geysermc.floodgate.addon.data.BungeeDataAddon; import org.geysermc.floodgate.api.inject.InjectorAddon; import org.geysermc.floodgate.register.AddonRegister; @@ -45,6 +46,12 @@ public final class BungeeAddonModule extends AbstractModule { return new AddonManagerAddon(); } + @Singleton + @ProvidesIntoSet + public InjectorAddon dataAddon() { + return new BungeeDataAddon(); + } + @Singleton @ProvidesIntoSet public InjectorAddon debugAddon() { diff --git a/bungee/src/main/java/org/geysermc/floodgate/util/BungeeUserAudience.java b/bungee/src/main/java/org/geysermc/floodgate/util/BungeeUserAudience.java index 273b9f40..40b453a7 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/util/BungeeUserAudience.java +++ b/bungee/src/main/java/org/geysermc/floodgate/util/BungeeUserAudience.java @@ -75,10 +75,6 @@ public class BungeeUserAudience implements UserAudience, ForwardingAudience.Sing @Override public void sendMessage(@NonNull Identity source, @NonNull Component message, @NonNull MessageType type) { - if (type != MessageType.CHAT) { - throw new IllegalStateException("Cannot send any other messages then chat messages"); - } - this.source.sendMessage(GsonComponentSerializer.gson().serialize(message)); } diff --git a/common/src/main/java/org/geysermc/floodgate/addon/data/HandshakeDataImpl.java b/common/src/main/java/org/geysermc/floodgate/addon/data/HandshakeDataImpl.java index 0e5bbf21..dada69a5 100644 --- a/common/src/main/java/org/geysermc/floodgate/addon/data/HandshakeDataImpl.java +++ b/common/src/main/java/org/geysermc/floodgate/addon/data/HandshakeDataImpl.java @@ -65,15 +65,23 @@ public class HandshakeDataImpl implements HandshakeData { this.rawSkin = rawSkin; this.hostname = hostname; - String prefix = config.getUsernamePrefix(); - int usernameLength = Math.min(bedrockData.getUsername().length(), 16 - prefix.length()); - String javaUsername = prefix + bedrockData.getUsername().substring(0, usernameLength); - if (config.isReplaceSpaces()) { - javaUsername = javaUsername.replaceAll(" ", "_"); - } - this.javaUsername = javaUsername; + String javaUsername = null; + UUID javaUniqueId = null; - this.javaUniqueId = Utils.getJavaUuid(bedrockData.getXuid()); + if (bedrockData != null) { + + String prefix = config.getUsernamePrefix(); + int usernameLength = Math.min(bedrockData.getUsername().length(), 16 - prefix.length()); + javaUsername = prefix + bedrockData.getUsername().substring(0, usernameLength); + if (config.isReplaceSpaces()) { + javaUsername = javaUsername.replaceAll(" ", "_"); + } + + javaUniqueId = Utils.getJavaUuid(bedrockData.getXuid()); + } + + this.javaUsername = javaUsername; + this.javaUniqueId = javaUniqueId; } @Override diff --git a/common/src/main/java/org/geysermc/floodgate/api/SimpleFloodgateApi.java b/common/src/main/java/org/geysermc/floodgate/api/SimpleFloodgateApi.java index f72042cb..255bcac1 100644 --- a/common/src/main/java/org/geysermc/floodgate/api/SimpleFloodgateApi.java +++ b/common/src/main/java/org/geysermc/floodgate/api/SimpleFloodgateApi.java @@ -39,7 +39,7 @@ import org.geysermc.floodgate.util.Utils; @RequiredArgsConstructor public class SimpleFloodgateApi implements FloodgateApi { - private final Map players = new HashMap<>(); + public final Map players = new HashMap<>(); private final PluginMessageHandler pluginMessageHandler; @Override @@ -146,7 +146,6 @@ public class SimpleFloodgateApi implements FloodgateApi { * instance directly. */ public boolean removePlayer(FloodgatePlayer player) { - boolean removed = players.remove(player.getJavaUniqueId(), player); - return removed && player.getLinkedPlayer() != null; + return players.remove(player.getJavaUniqueId(), player); } } diff --git a/common/src/main/java/org/geysermc/floodgate/command/TestCommand.java b/common/src/main/java/org/geysermc/floodgate/command/TestCommand.java new file mode 100644 index 00000000..8a804522 --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/command/TestCommand.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019-2021 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.command; + +import cloud.commandframework.Command; +import cloud.commandframework.CommandManager; +import cloud.commandframework.context.CommandContext; +import net.kyori.adventure.text.Component; +import org.geysermc.floodgate.api.FloodgateApi; +import org.geysermc.floodgate.api.SimpleFloodgateApi; +import org.geysermc.floodgate.config.FloodgateConfig; +import org.geysermc.floodgate.platform.command.FloodgateCommand; +import org.geysermc.floodgate.player.UserAudience; +import org.geysermc.floodgate.util.Constants; + +public class TestCommand implements FloodgateCommand { + @Override + public Command buildCommand(CommandManager commandManager) { + return commandManager.commandBuilder("floodgate-test") + .senderType(UserAudience.class) + .handler(this::execute) + .build(); + } + + @Override + public void execute(CommandContext context) { + int players = ((SimpleFloodgateApi) FloodgateApi.getInstance()).players.size(); + context.getSender().sendMessage(Component.text(players)); + } + + @Override + public boolean shouldRegister(FloodgateConfig config) { + return Constants.DEBUG_MODE; + } +} diff --git a/common/src/main/java/org/geysermc/floodgate/inject/CommonPlatformInjector.java b/common/src/main/java/org/geysermc/floodgate/inject/CommonPlatformInjector.java index 01122d77..00a4b63c 100644 --- a/common/src/main/java/org/geysermc/floodgate/inject/CommonPlatformInjector.java +++ b/common/src/main/java/org/geysermc/floodgate/inject/CommonPlatformInjector.java @@ -30,11 +30,15 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import lombok.AccessLevel; +import lombok.Getter; import org.geysermc.floodgate.api.inject.InjectorAddon; import org.geysermc.floodgate.api.inject.PlatformInjector; public abstract class CommonPlatformInjector implements PlatformInjector { + @Getter(AccessLevel.PROTECTED) private final Set injectedClients = new HashSet<>(); + private final Map, InjectorAddon> addons = new HashMap<>(); protected boolean addInjectedClient(Channel channel) { @@ -86,6 +90,20 @@ public abstract class CommonPlatformInjector implements PlatformInjector { } } + /** + * Method to loop throguh all the addons and call {@link InjectorAddon#onChannelClosed(Channel)} + * if {@link InjectorAddon#shouldInject()} + * + * @param channel the channel that was injected + */ + public void channelClosedCall(Channel channel) { + for (InjectorAddon addon : addons.values()) { + if (addon.shouldInject()) { + addon.onChannelClosed(channel); + } + } + } + /** * Method to loop through all the addons and call {@link InjectorAddon#onRemoveInject(Channel)} * if {@link InjectorAddon#shouldInject()}. diff --git a/common/src/main/java/org/geysermc/floodgate/module/CommandModule.java b/common/src/main/java/org/geysermc/floodgate/module/CommandModule.java index 76871bb0..24f801ab 100644 --- a/common/src/main/java/org/geysermc/floodgate/module/CommandModule.java +++ b/common/src/main/java/org/geysermc/floodgate/module/CommandModule.java @@ -29,6 +29,7 @@ import com.google.inject.AbstractModule; import com.google.inject.Singleton; import com.google.inject.multibindings.ProvidesIntoSet; import org.geysermc.floodgate.command.LinkAccountCommand; +import org.geysermc.floodgate.command.TestCommand; import org.geysermc.floodgate.command.UnlinkAccountCommand; import org.geysermc.floodgate.platform.command.FloodgateCommand; import org.geysermc.floodgate.register.CommandRegister; @@ -50,4 +51,10 @@ public class CommandModule extends AbstractModule { public FloodgateCommand unlinkAccountCommand() { return new UnlinkAccountCommand(); } + + @Singleton + @ProvidesIntoSet + public FloodgateCommand testCommand() { + return new TestCommand(); + } } diff --git a/common/src/main/java/org/geysermc/floodgate/module/CommonModule.java b/common/src/main/java/org/geysermc/floodgate/module/CommonModule.java index b0550078..d2952350 100644 --- a/common/src/main/java/org/geysermc/floodgate/module/CommonModule.java +++ b/common/src/main/java/org/geysermc/floodgate/module/CommonModule.java @@ -149,6 +149,13 @@ public class CommonModule extends AbstractModule { ); } + @Provides + @Singleton + @Named("kickMessageAttribute") + public AttributeKey kickMessageAttribute() { + return AttributeKey.valueOf("floodgate-kick-message"); + } + @Provides @Singleton @Named("playerAttribute") diff --git a/common/src/main/java/org/geysermc/floodgate/util/Constants.java b/common/src/main/java/org/geysermc/floodgate/util/Constants.java index cfe46429..b6909532 100644 --- a/common/src/main/java/org/geysermc/floodgate/util/Constants.java +++ b/common/src/main/java/org/geysermc/floodgate/util/Constants.java @@ -28,4 +28,6 @@ package org.geysermc.floodgate.util; public final class Constants { public static final String DATABASE_NAME_FORMAT = "^floodgate-[a-zA-Z0-9_]{0,16}-database.jar$"; public static final int LOGIN_SUCCESS_PACKET_ID = 2; + + public static final boolean DEBUG_MODE = true; } diff --git a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataAddon.java b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataAddon.java index 7b117f02..c1359138 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataAddon.java +++ b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataAddon.java @@ -28,8 +28,11 @@ package org.geysermc.floodgate.addon.data; import com.google.inject.Inject; import com.google.inject.name.Named; import io.netty.channel.Channel; +import io.netty.util.AttributeKey; +import org.geysermc.floodgate.api.SimpleFloodgateApi; import org.geysermc.floodgate.api.inject.InjectorAddon; 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.Utils; @@ -37,12 +40,17 @@ import org.geysermc.floodgate.util.Utils; public final class SpigotDataAddon implements InjectorAddon { @Inject private FloodgateHandshakeHandler handshakeHandler; @Inject private FloodgateConfig config; + @Inject private SimpleFloodgateApi api; @Inject private FloodgateLogger logger; @Inject @Named("packetHandler") private String packetHandlerName; + @Inject + @Named("playerAttribute") + private AttributeKey playerAttribute; + @Override public void onInject(Channel channel, boolean toServer) { channel.pipeline().addBefore( @@ -56,6 +64,14 @@ public final class SpigotDataAddon implements InjectorAddon { onRemoveInject(channel); } + @Override + public void onChannelClosed(Channel channel) { + FloodgatePlayer player = channel.attr(playerAttribute).get(); + if (player != null && api.removePlayer(player)) { + logger.translatedInfo("floodgate.ingame.disconnect_name", player.getCorrectUsername()); + } + } + @Override public void onRemoveInject(Channel channel) { Utils.removeHandler(channel.pipeline(), "floodgate_data_handler"); diff --git a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java index 3250de4f..e26c6e8c 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java +++ b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java @@ -34,7 +34,8 @@ import static org.geysermc.floodgate.util.ReflectionUtils.makeAccessible; import static org.geysermc.floodgate.util.ReflectionUtils.setValue; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.util.ReferenceCountUtil; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -54,7 +55,7 @@ import org.geysermc.floodgate.util.ReflectionUtils; import org.geysermc.floodgate.util.SpigotUtils; @RequiredArgsConstructor -public final class SpigotDataHandler extends SimpleChannelInboundHandler { +public final class SpigotDataHandler extends ChannelInboundHandlerAdapter { private static final Field SOCKET_ADDRESS; private static final Class HANDSHAKE_PACKET; private static final Field HANDSHAKE_HOST; @@ -160,7 +161,8 @@ public final class SpigotDataHandler extends SimpleChannelInboundHandler private boolean done; @Override - protected void channelRead0(ChannelHandlerContext ctx, Object packet) throws Exception { + public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception { + ReferenceCountUtil.retain(packet); // we're done but we're not yet removed from the connection if (done) { ctx.fireChannelRead(packet); @@ -244,7 +246,9 @@ public final class SpigotDataHandler extends SimpleChannelInboundHandler } finally { // don't let the packet through if the packet is the login packet // because we want to skip the login cycle - if (!isLogin) { + if (isLogin) { + ReferenceCountUtil.release(packet, 2); + } else { ctx.fireChannelRead(packet); } diff --git a/spigot/src/main/java/org/geysermc/floodgate/inject/spigot/SpigotInjector.java b/spigot/src/main/java/org/geysermc/floodgate/inject/spigot/SpigotInjector.java index 74c70a6b..cb57ca3d 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/inject/spigot/SpigotInjector.java +++ b/spigot/src/main/java/org/geysermc/floodgate/inject/spigot/SpigotInjector.java @@ -35,9 +35,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.util.HashSet; import java.util.List; -import java.util.Set; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.geysermc.floodgate.inject.CommonPlatformInjector; @@ -45,8 +43,6 @@ import org.geysermc.floodgate.util.ReflectionUtils; @RequiredArgsConstructor public final class SpigotInjector extends CommonPlatformInjector { - private final Set injectedClients = new HashSet<>(); - private Object serverConnection; private String injectedFieldName; @@ -115,7 +111,11 @@ public final class SpigotInjector extends CommonPlatformInjector { @Override protected void initChannel(Channel channel) { injectAddonsCall(channel, false); - injectedClients.add(channel); + addInjectedClient(channel); + channel.closeFuture().addListener(listener -> { + channelClosedCall(channel); + removeInjectedClient(channel); + }); } }); } @@ -129,10 +129,10 @@ public final class SpigotInjector extends CommonPlatformInjector { } // remove injection from clients - for (Channel channel : injectedClients) { + for (Channel channel : getInjectedClients()) { removeAddonsCall(channel); } - injectedClients.clear(); + getInjectedClients().clear(); // and change the list back to the original Object serverConnection = getServerConnection(); diff --git a/spigot/src/main/java/org/geysermc/floodgate/listener/SpigotListener.java b/spigot/src/main/java/org/geysermc/floodgate/listener/SpigotListener.java index 9b1ae35f..4cade539 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/listener/SpigotListener.java +++ b/spigot/src/main/java/org/geysermc/floodgate/listener/SpigotListener.java @@ -27,11 +27,9 @@ package org.geysermc.floodgate.listener; import com.google.inject.Inject; import java.util.UUID; -import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import org.bukkit.event.player.AsyncPlayerPreLoginEvent; import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.geysermc.floodgate.api.SimpleFloodgateApi; @@ -48,18 +46,10 @@ public final class SpigotListener implements Listener { @Inject private LanguageManager languageManager; @Inject private FloodgateLogger logger; - @EventHandler(priority = EventPriority.MONITOR) - public void onAsyncPreLogin(AsyncPlayerPreLoginEvent event) { - if (event.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) { - api.removePlayer(event.getUniqueId(), true); - } - } - @EventHandler(priority = EventPriority.MONITOR) public void onPlayerLogin(PlayerLoginEvent event) { UUID uniqueId = event.getPlayer().getUniqueId(); if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) { - api.removePlayer(uniqueId); return; } @@ -79,12 +69,6 @@ public final class SpigotListener implements Listener { @EventHandler(priority = EventPriority.MONITOR) public void onPlayerQuit(PlayerQuitEvent event) { - Player player = event.getPlayer(); - - SpigotCommandUtil.AUDIENCE_CACHE.remove(player.getUniqueId()); //todo - - if (api.removePlayer(player.getUniqueId()) != null) { - logger.translatedInfo("floodgate.ingame.disconnect_name", player.getName()); - } + SpigotCommandUtil.AUDIENCE_CACHE.remove(event.getPlayer().getUniqueId()); //todo } } diff --git a/spigot/src/main/java/org/geysermc/floodgate/util/SpigotUserAudience.java b/spigot/src/main/java/org/geysermc/floodgate/util/SpigotUserAudience.java index 67503fad..22bd79ec 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/util/SpigotUserAudience.java +++ b/spigot/src/main/java/org/geysermc/floodgate/util/SpigotUserAudience.java @@ -75,10 +75,6 @@ public class SpigotUserAudience implements UserAudience, ForwardingAudience.Sing @Override public void sendMessage(@NonNull Identity source, @NonNull Component message, @NonNull MessageType type) { - if (type != MessageType.CHAT) { - throw new IllegalStateException("Cannot send any other messages then chat messages"); - } - this.source.sendMessage(GsonComponentSerializer.gson().serialize(message)); } diff --git a/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityDataAddon.java b/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityDataAddon.java index bee136ef..0c60f88f 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityDataAddon.java +++ b/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityDataAddon.java @@ -32,6 +32,7 @@ import io.netty.util.AttributeKey; import org.geysermc.floodgate.api.ProxyFloodgateApi; import org.geysermc.floodgate.api.inject.InjectorAddon; import org.geysermc.floodgate.api.logger.FloodgateLogger; +import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.config.ProxyFloodgateConfig; import org.geysermc.floodgate.player.FloodgateHandshakeHandler; import org.geysermc.floodgate.util.Utils; @@ -54,6 +55,10 @@ public final class VelocityDataAddon implements InjectorAddon { @Named("kickMessageAttribute") private AttributeKey kickMessageAttribute; + @Inject + @Named("playerAttribute") + private AttributeKey playerAttribute; + @Override public void onInject(Channel channel, boolean toServer) { if (toServer) { @@ -75,6 +80,15 @@ public final class VelocityDataAddon implements InjectorAddon { onRemoveInject(channel); } + @Override + public void onChannelClosed(Channel channel) { + FloodgatePlayer player = channel.attr(playerAttribute).get(); + if (player != null && api.removePlayer(player)) { + api.removeEncryptedData(player.getCorrectUniqueId()); + logger.translatedInfo("floodgate.ingame.disconnect_name", player.getUsername()); + } + } + @Override public void onRemoveInject(Channel channel) { Utils.removeHandler(channel.pipeline(), "floodgate_data_handler"); diff --git a/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityProxyDataHandler.java b/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityProxyDataHandler.java index 9185427c..fe051e00 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityProxyDataHandler.java +++ b/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityProxyDataHandler.java @@ -32,7 +32,7 @@ import static org.geysermc.floodgate.util.ReflectionUtils.getPrefixedClass; import static org.geysermc.floodgate.util.ReflectionUtils.setValue; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.util.AttributeKey; import io.netty.util.ReferenceCountUtil; import java.lang.reflect.Field; @@ -46,7 +46,7 @@ import org.geysermc.floodgate.player.FloodgateHandshakeHandler; import org.geysermc.floodgate.player.FloodgateHandshakeHandler.HandshakeResult; @RequiredArgsConstructor -public final class VelocityProxyDataHandler extends SimpleChannelInboundHandler { +public final class VelocityProxyDataHandler extends ChannelInboundHandlerAdapter { private static final Field HANDSHAKE; private static final Class HANDSHAKE_PACKET; private static final Field HANDSHAKE_SERVER_ADDRESS; @@ -76,12 +76,12 @@ public final class VelocityProxyDataHandler extends SimpleChannelInboundHandler< private boolean done; @Override - protected void channelRead0(ChannelHandlerContext ctx, Object msg) { + public void channelRead(ChannelHandlerContext ctx, Object msg) { ReferenceCountUtil.retain(msg); - // we're only interested in the Handshake packet + // we're only interested in the Handshake packet. + // it should be the first packet but you never know if (done || !HANDSHAKE_PACKET.isInstance(msg)) { ctx.fireChannelRead(msg); - done = true; return; } @@ -105,7 +105,8 @@ public final class VelocityProxyDataHandler extends SimpleChannelInboundHandler< case SUCCESS: break; case EXCEPTION: - ctx.channel().attr(kickMessageAttribute).set(config.getDisconnect().getInvalidKey()); + ctx.channel().attr(kickMessageAttribute) + .set(config.getDisconnect().getInvalidKey()); return; case INVALID_DATA_LENGTH: ctx.channel().attr(kickMessageAttribute) diff --git a/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityServerDataHandler.java b/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityServerDataHandler.java index 513cc15f..98b0a721 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityServerDataHandler.java +++ b/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityServerDataHandler.java @@ -47,6 +47,7 @@ import org.geysermc.floodgate.api.ProxyFloodgateApi; import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.config.ProxyFloodgateConfig; +@SuppressWarnings("ConstantConditions") @RequiredArgsConstructor public final class VelocityServerDataHandler extends MessageToMessageEncoder { private static final Class HANDSHAKE_PACKET; @@ -74,6 +75,7 @@ public final class VelocityServerDataHandler extends MessageToMessageEncoder playerAttribute; private boolean done; @Override @@ -100,6 +102,14 @@ public final class VelocityServerDataHandler extends MessageToMessageEncoder Player channel from the Proxy <-> Server channel + //MinecraftConnection minecraftConnection = ctx.pipeline().get("handler"); + //((VelocityServerConnection) minecraftConnection.association).proxyPlayer.connection.channel + + //FloodgatePlayer player = playerChannel.attr(playerAttribute).get(); + // player is not a Floodgate player if (player == null) { out.add(packet); diff --git a/velocity/src/main/java/org/geysermc/floodgate/inject/velocity/VelocityInjector.java b/velocity/src/main/java/org/geysermc/floodgate/inject/velocity/VelocityInjector.java index aaa0611b..cbdfe127 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/inject/velocity/VelocityInjector.java +++ b/velocity/src/main/java/org/geysermc/floodgate/inject/velocity/VelocityInjector.java @@ -100,6 +100,10 @@ public final class VelocityInjector extends CommonPlatformInjector { injector.injectAddonsCall(channel, proxyToServer); injector.addInjectedClient(channel); + channel.closeFuture().addListener(listener -> { + injector.channelClosedCall(channel); + injector.removeInjectedClient(channel); + }); } } } diff --git a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java index 9299a2cc..b3e234f7 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java +++ b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java @@ -42,7 +42,6 @@ import com.velocitypowered.api.event.connection.PreLoginEvent; import com.velocitypowered.api.event.player.GameProfileRequestEvent; import com.velocitypowered.api.event.player.ServerPostConnectEvent; import com.velocitypowered.api.proxy.InboundConnection; -import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.util.GameProfile; import io.netty.channel.Channel; import io.netty.util.AttributeKey; @@ -62,7 +61,6 @@ import org.geysermc.floodgate.util.VelocityCommandUtil; public final class VelocityListener { private static final Field INITIAL_MINECRAFT_CONNECTION; - private static final Field MINECRAFT_CONNECTION; private static final Field CHANNEL; static { @@ -70,8 +68,6 @@ public final class VelocityListener { Class minecraftConnection = getPrefixedClass("connection.MinecraftConnection"); INITIAL_MINECRAFT_CONNECTION = getFieldOfType(initialConnection, minecraftConnection); - Class connectedPlayer = getPrefixedClass("connection.client.ConnectedPlayer"); - MINECRAFT_CONNECTION = getFieldOfType(connectedPlayer, minecraftConnection); CHANNEL = getFieldOfType(minecraftConnection, Channel.class); } @@ -167,21 +163,6 @@ public final class VelocityListener { @Subscribe(order = PostOrder.LAST) public void onDisconnect(DisconnectEvent event) { - Player player = event.getPlayer(); - - VelocityCommandUtil.AUDIENCE_CACHE.remove(player.getUniqueId()); //todo - - try { - Object minecraftConnection = getValue(player, MINECRAFT_CONNECTION); - Channel channel = getCastedValue(minecraftConnection, CHANNEL); - FloodgatePlayer fPlayer = channel.attr(playerAttribute).get(); - - if (fPlayer != null && api.removePlayer(fPlayer)) { - api.removeEncryptedData(event.getPlayer().getUniqueId()); - logger.translatedInfo("floodgate.ingame.disconnect_name", player.getUsername()); - } - } catch (Exception exception) { - logger.error("Failed to remove the player", exception); - } + VelocityCommandUtil.AUDIENCE_CACHE.remove(event.getPlayer().getUniqueId()); //todo } } diff --git a/velocity/src/main/java/org/geysermc/floodgate/module/VelocityPlatformModule.java b/velocity/src/main/java/org/geysermc/floodgate/module/VelocityPlatformModule.java index 6595779a..aedd04ef 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/module/VelocityPlatformModule.java +++ b/velocity/src/main/java/org/geysermc/floodgate/module/VelocityPlatformModule.java @@ -38,7 +38,6 @@ import com.google.inject.name.Named; import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.event.EventManager; import com.velocitypowered.api.proxy.ProxyServer; -import io.netty.util.AttributeKey; import lombok.RequiredArgsConstructor; import org.geysermc.floodgate.VelocityPlugin; import org.geysermc.floodgate.api.logger.FloodgateLogger; @@ -148,11 +147,4 @@ public final class VelocityPlatformModule extends AbstractModule { public String implementationName() { return "Velocity"; } - - @Provides - @Singleton - @Named("kickMessageAttribute") - public AttributeKey kickMessageAttribute() { - return AttributeKey.valueOf("floodgate-kick-message"); - } }