From 2d177e3df563fe30b4f8ab4ec42c07a8761fda07 Mon Sep 17 00:00:00 2001 From: Smart123s Date: Sat, 6 Jul 2024 18:10:26 +0200 Subject: [PATCH 1/3] Retrieve and use Floodgate username in Velocity PreLoginEvent --- .../fastlogin/velocity/FastLoginVelocity.java | 4 + .../velocity/listener/ConnectListener.java | 81 +++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/velocity/src/main/java/com/github/games647/fastlogin/velocity/FastLoginVelocity.java b/velocity/src/main/java/com/github/games647/fastlogin/velocity/FastLoginVelocity.java index 2dd1f2a4b..26217b835 100644 --- a/velocity/src/main/java/com/github/games647/fastlogin/velocity/FastLoginVelocity.java +++ b/velocity/src/main/java/com/github/games647/fastlogin/velocity/FastLoginVelocity.java @@ -230,4 +230,8 @@ private void loadOrGenerateProxyId() { public UUID getProxyId() { return proxyId; } + + public ProxyServer getServer() { + return server; + } } diff --git a/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java b/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java index 98e2191a5..66ed5e2d1 100644 --- a/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java +++ b/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java @@ -36,6 +36,8 @@ import com.github.games647.fastlogin.velocity.task.AsyncPremiumCheck; import com.github.games647.fastlogin.velocity.task.FloodgateAuthTask; import com.github.games647.fastlogin.velocity.task.ForceLoginTask; +import com.google.common.cache.Cache; +import com.google.common.collect.ListMultimap; import com.velocitypowered.api.event.EventTask; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.connection.DisconnectEvent; @@ -43,15 +45,18 @@ import com.velocitypowered.api.event.connection.PreLoginEvent.PreLoginComponentResult; import com.velocitypowered.api.event.player.GameProfileRequestEvent; import com.velocitypowered.api.event.player.ServerConnectedEvent; +import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.proxy.InboundConnection; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.api.util.GameProfile.Property; + import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.geysermc.floodgate.api.player.FloodgatePlayer; +import java.lang.reflect.Field; import java.net.InetSocketAddress; import java.time.Duration; import java.util.ArrayList; @@ -61,6 +66,8 @@ public class ConnectListener { + private static final String FLOODGATE_PLUGIN_NAME = "org.geysermc.floodgate.VelocityPlugin"; + private final FastLoginVelocity plugin; private final AntiBotService antiBotService; @@ -80,6 +87,16 @@ public EventTask onPreLogin(PreLoginEvent preLoginEvent) { InetSocketAddress address = connection.getRemoteAddress(); plugin.getLog().info("Incoming login request for {} from {}", username, address); + + // FloodgateVelocity only sets the correct username in GetProfileRequestEvent, but we need it here too. + if (plugin.getFloodgateService() != null) { + String floodgateUsername = getFloodgateUsername(preLoginEvent, connection); + if (floodgateUsername != null) { + plugin.getLog().info("Found player's Floodgate: {}", floodgateUsername); + username = floodgateUsername; + } + } + Action action = antiBotService.onIncomingConnection(address, username); switch (action) { case Ignore: @@ -177,4 +194,68 @@ public void onDisconnect(DisconnectEvent disconnectEvent) { Player player = disconnectEvent.getPlayer(); plugin.getCore().getPendingConfirms().remove(player.getUniqueId()); } + + /** + * Get the Floodgate username from the Floodgate plugin's playerCache using lots of reflections + * @param preLoginEvent + * @param connection + * @return the Floodgate username or null if not found + */ + private String getFloodgateUsername(PreLoginEvent preLoginEvent, InboundConnection connection) { + try { + // Get Velocity's event manager + Object eventManager = plugin.getServer().getEventManager(); + Field handlerField = eventManager.getClass().getDeclaredField("handlersByType"); + handlerField.setAccessible(true); + @SuppressWarnings("rawtypes") + ListMultimap handlersByType = (ListMultimap) handlerField.get(eventManager); + handlerField.setAccessible(false); + + // Get all registered PreLoginEvent handlers + @SuppressWarnings({ "rawtypes", "unchecked" }) + List preLoginEventHandlres = handlersByType.get(preLoginEvent.getClass()); + Field pluginField = preLoginEventHandlres.get(0).getClass().getDeclaredField("plugin"); + pluginField.setAccessible(true); + Object floodgateEventHandlerRegistration = null; + + // Find the Floodgate plugin's PreLoginEvent handler + for (Object handler : preLoginEventHandlres) { + PluginContainer eventHandlerPlugin = (PluginContainer) pluginField.get(handler); + String eventHandlerPluginName = eventHandlerPlugin.getInstance().get().getClass().getName(); + if (eventHandlerPluginName.equals(FLOODGATE_PLUGIN_NAME)) { + floodgateEventHandlerRegistration = handler; + break; + } + } + pluginField.setAccessible(false); + if (floodgateEventHandlerRegistration == null) { + return null; + } + + // Extract the EventHandler instance from Velocity's internal registration handler storage + Field eventHandlerField = floodgateEventHandlerRegistration.getClass().getDeclaredField("instance"); + eventHandlerField.setAccessible(true); + Object floodgateEventHandler = eventHandlerField.get(floodgateEventHandlerRegistration); + eventHandlerField.setAccessible(false); + + // Get the Floodgate playerCache field + Field playerCacheField = floodgateEventHandler.getClass().getDeclaredField("playerCache"); + playerCacheField.setAccessible(true); + @SuppressWarnings("unchecked") + Cache playerCache = + (Cache) playerCacheField.get(floodgateEventHandler); + playerCacheField.setAccessible(false); + + // Find the FloodgatePlayer instance in playerCache + FloodgatePlayer floodgatePlayer = playerCache.getIfPresent(connection); + if (floodgatePlayer == null) { + return null; + } + return floodgatePlayer.getCorrectUsername(); + + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } } From 9a40cf0afb1d59d77892b098ca8fdbc13d33b4b9 Mon Sep 17 00:00:00 2001 From: Smart123s Date: Sat, 6 Jul 2024 18:10:52 +0200 Subject: [PATCH 2/3] Mark Floodgate Join events as fired too --- .../games647/fastlogin/bukkit/listener/ConnectionListener.java | 1 + 1 file changed, 1 insertion(+) diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/ConnectionListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/ConnectionListener.java index 2bacbc5d8..33b5c4253 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/ConnectionListener.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/ConnectionListener.java @@ -90,6 +90,7 @@ private void delayForceLogin(Player player) { if (floodgatePlayer != null) { Runnable floodgateAuthTask = new FloodgateAuthTask(plugin.getCore(), player, floodgatePlayer); Bukkit.getScheduler().runTaskAsynchronously(plugin, floodgateAuthTask); + plugin.getBungeeManager().markJoinEventFired(player); return; } } From 7f488498cf716fce02de443457c2151322068cc8 Mon Sep 17 00:00:00 2001 From: games647 Date: Mon, 8 Jul 2024 10:46:48 +0200 Subject: [PATCH 3/3] Minor code styling * Use logging instead of raw exception printing * Extract method * Shorter var names * More precise generics if possible * Don't restore accessible state could be in conflict with other plugins if we don't restore the exact value --- .../velocity/listener/ConnectListener.java | 89 ++++++++++--------- 1 file changed, 47 insertions(+), 42 deletions(-) diff --git a/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java b/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java index 66ed5e2d1..90513b237 100644 --- a/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java +++ b/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java @@ -38,6 +38,7 @@ import com.github.games647.fastlogin.velocity.task.ForceLoginTask; import com.google.common.cache.Cache; import com.google.common.collect.ListMultimap; +import com.velocitypowered.api.event.EventManager; import com.velocitypowered.api.event.EventTask; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.connection.DisconnectEvent; @@ -51,7 +52,6 @@ import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.api.util.GameProfile.Property; - import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.geysermc.floodgate.api.player.FloodgatePlayer; @@ -87,10 +87,9 @@ public EventTask onPreLogin(PreLoginEvent preLoginEvent) { InetSocketAddress address = connection.getRemoteAddress(); plugin.getLog().info("Incoming login request for {} from {}", username, address); - // FloodgateVelocity only sets the correct username in GetProfileRequestEvent, but we need it here too. if (plugin.getFloodgateService() != null) { - String floodgateUsername = getFloodgateUsername(preLoginEvent, connection); + String floodgateUsername = getFloodgateUsername(connection); if (floodgateUsername != null) { plugin.getLog().info("Found player's Floodgate: {}", floodgateUsername); username = floodgateUsername; @@ -197,65 +196,71 @@ public void onDisconnect(DisconnectEvent disconnectEvent) { /** * Get the Floodgate username from the Floodgate plugin's playerCache using lots of reflections - * @param preLoginEvent + * * @param connection * @return the Floodgate username or null if not found */ - private String getFloodgateUsername(PreLoginEvent preLoginEvent, InboundConnection connection) { + private String getFloodgateUsername(InboundConnection connection) { try { - // Get Velocity's event manager - Object eventManager = plugin.getServer().getEventManager(); - Field handlerField = eventManager.getClass().getDeclaredField("handlersByType"); - handlerField.setAccessible(true); - @SuppressWarnings("rawtypes") - ListMultimap handlersByType = (ListMultimap) handlerField.get(eventManager); - handlerField.setAccessible(false); - - // Get all registered PreLoginEvent handlers - @SuppressWarnings({ "rawtypes", "unchecked" }) - List preLoginEventHandlres = handlersByType.get(preLoginEvent.getClass()); - Field pluginField = preLoginEventHandlres.get(0).getClass().getDeclaredField("plugin"); - pluginField.setAccessible(true); - Object floodgateEventHandlerRegistration = null; - - // Find the Floodgate plugin's PreLoginEvent handler - for (Object handler : preLoginEventHandlres) { - PluginContainer eventHandlerPlugin = (PluginContainer) pluginField.get(handler); - String eventHandlerPluginName = eventHandlerPlugin.getInstance().get().getClass().getName(); - if (eventHandlerPluginName.equals(FLOODGATE_PLUGIN_NAME)) { - floodgateEventHandlerRegistration = handler; - break; - } - } - pluginField.setAccessible(false); - if (floodgateEventHandlerRegistration == null) { + // get floodgate's event handler + Object floodgateEventHandler = getFloodgateHandler(); + if (floodgateEventHandler == null) { return null; } - // Extract the EventHandler instance from Velocity's internal registration handler storage - Field eventHandlerField = floodgateEventHandlerRegistration.getClass().getDeclaredField("instance"); - eventHandlerField.setAccessible(true); - Object floodgateEventHandler = eventHandlerField.get(floodgateEventHandlerRegistration); - eventHandlerField.setAccessible(false); - // Get the Floodgate playerCache field Field playerCacheField = floodgateEventHandler.getClass().getDeclaredField("playerCache"); playerCacheField.setAccessible(true); @SuppressWarnings("unchecked") Cache playerCache = - (Cache) playerCacheField.get(floodgateEventHandler); - playerCacheField.setAccessible(false); + (Cache) playerCacheField.get(floodgateEventHandler); // Find the FloodgatePlayer instance in playerCache FloodgatePlayer floodgatePlayer = playerCache.getIfPresent(connection); if (floodgatePlayer == null) { return null; } - return floodgatePlayer.getCorrectUsername(); - } catch (Exception e) { - e.printStackTrace(); + return floodgatePlayer.getCorrectUsername(); + } catch (Exception ex) { + plugin.getLog().error("Failed to fetch current floodgate username", ex); } + return null; } + + private Object getFloodgateHandler() + throws NoSuchFieldException, IllegalAccessException { + // Get Velocity's event manager + EventManager eventManager = plugin.getServer().getEventManager(); + Field handlerField = eventManager.getClass().getDeclaredField("handlersByType"); + handlerField.setAccessible(true); + @SuppressWarnings("unchecked") + ListMultimap, ?> handlersByType = (ListMultimap, ?>) handlerField.get(eventManager); + + // Get all registered PreLoginEvent handlers + List loginEventRegistrations = handlersByType.get(PreLoginEvent.class); + Field pluginField = loginEventRegistrations.get(0).getClass().getDeclaredField("plugin"); + pluginField.setAccessible(true); + + // Find the Floodgate plugin's PreLoginEvent handler registration (Velocity implementation) + Object floodgateRegistration = null; + for (Object handler : loginEventRegistrations) { + PluginContainer eventHandlerPlugin = (PluginContainer) pluginField.get(handler); + String eventHandlerPluginName = eventHandlerPlugin.getInstance().get().getClass().getName(); + if (eventHandlerPluginName.equals(FLOODGATE_PLUGIN_NAME)) { + floodgateRegistration = handler; + break; + } + } + + if (floodgateRegistration == null) { + return null; + } + + // Extract the EventHandler instance (floodgate impl) from Velocity's internal registration handler storage + Field eventHandlerField = floodgateRegistration.getClass().getDeclaredField("instance"); + eventHandlerField.setAccessible(true); + return eventHandlerField.get(floodgateRegistration); + } }