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 2dd1f2a4..26217b83 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 @@ public class FastLoginVelocity implements PlatformPlugin { 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 98e2191a..66ed5e2d 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.VelocityLoginSession; 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; 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 @@ import java.util.UUID; 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 class ConnectListener { 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 class ConnectListener { 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; + } }