3636import com .github .games647 .fastlogin .velocity .task .AsyncPremiumCheck ;
3737import com .github .games647 .fastlogin .velocity .task .FloodgateAuthTask ;
3838import com .github .games647 .fastlogin .velocity .task .ForceLoginTask ;
39+ import com .google .common .cache .Cache ;
40+ import com .google .common .collect .ListMultimap ;
3941import com .velocitypowered .api .event .EventTask ;
4042import com .velocitypowered .api .event .Subscribe ;
4143import com .velocitypowered .api .event .connection .DisconnectEvent ;
4244import com .velocitypowered .api .event .connection .PreLoginEvent ;
4345import com .velocitypowered .api .event .connection .PreLoginEvent .PreLoginComponentResult ;
4446import com .velocitypowered .api .event .player .GameProfileRequestEvent ;
4547import com .velocitypowered .api .event .player .ServerConnectedEvent ;
48+ import com .velocitypowered .api .plugin .PluginContainer ;
4649import com .velocitypowered .api .proxy .InboundConnection ;
4750import com .velocitypowered .api .proxy .Player ;
4851import com .velocitypowered .api .proxy .server .RegisteredServer ;
4952import com .velocitypowered .api .util .GameProfile ;
5053import com .velocitypowered .api .util .GameProfile .Property ;
54+
5155import net .kyori .adventure .text .TextComponent ;
5256import net .kyori .adventure .text .serializer .legacy .LegacyComponentSerializer ;
5357import org .geysermc .floodgate .api .player .FloodgatePlayer ;
5458
59+ import java .lang .reflect .Field ;
5560import java .net .InetSocketAddress ;
5661import java .time .Duration ;
5762import java .util .ArrayList ;
6166
6267public class ConnectListener {
6368
69+ private static final String FLOODGATE_PLUGIN_NAME = "org.geysermc.floodgate.VelocityPlugin" ;
70+
6471 private final FastLoginVelocity plugin ;
6572 private final AntiBotService antiBotService ;
6673
@@ -80,6 +87,16 @@ public EventTask onPreLogin(PreLoginEvent preLoginEvent) {
8087 InetSocketAddress address = connection .getRemoteAddress ();
8188 plugin .getLog ().info ("Incoming login request for {} from {}" , username , address );
8289
90+
91+ // FloodgateVelocity only sets the correct username in GetProfileRequestEvent, but we need it here too.
92+ if (plugin .getFloodgateService () != null ) {
93+ String floodgateUsername = getFloodgateUsername (preLoginEvent , connection );
94+ if (floodgateUsername != null ) {
95+ plugin .getLog ().info ("Found player's Floodgate: {}" , floodgateUsername );
96+ username = floodgateUsername ;
97+ }
98+ }
99+
83100 Action action = antiBotService .onIncomingConnection (address , username );
84101 switch (action ) {
85102 case Ignore :
@@ -177,4 +194,68 @@ public void onDisconnect(DisconnectEvent disconnectEvent) {
177194 Player player = disconnectEvent .getPlayer ();
178195 plugin .getCore ().getPendingConfirms ().remove (player .getUniqueId ());
179196 }
197+
198+ /**
199+ * Get the Floodgate username from the Floodgate plugin's playerCache using lots of reflections
200+ * @param preLoginEvent
201+ * @param connection
202+ * @return the Floodgate username or null if not found
203+ */
204+ private String getFloodgateUsername (PreLoginEvent preLoginEvent , InboundConnection connection ) {
205+ try {
206+ // Get Velocity's event manager
207+ Object eventManager = plugin .getServer ().getEventManager ();
208+ Field handlerField = eventManager .getClass ().getDeclaredField ("handlersByType" );
209+ handlerField .setAccessible (true );
210+ @ SuppressWarnings ("rawtypes" )
211+ ListMultimap handlersByType = (ListMultimap ) handlerField .get (eventManager );
212+ handlerField .setAccessible (false );
213+
214+ // Get all registered PreLoginEvent handlers
215+ @ SuppressWarnings ({ "rawtypes" , "unchecked" })
216+ List preLoginEventHandlres = handlersByType .get (preLoginEvent .getClass ());
217+ Field pluginField = preLoginEventHandlres .get (0 ).getClass ().getDeclaredField ("plugin" );
218+ pluginField .setAccessible (true );
219+ Object floodgateEventHandlerRegistration = null ;
220+
221+ // Find the Floodgate plugin's PreLoginEvent handler
222+ for (Object handler : preLoginEventHandlres ) {
223+ PluginContainer eventHandlerPlugin = (PluginContainer ) pluginField .get (handler );
224+ String eventHandlerPluginName = eventHandlerPlugin .getInstance ().get ().getClass ().getName ();
225+ if (eventHandlerPluginName .equals (FLOODGATE_PLUGIN_NAME )) {
226+ floodgateEventHandlerRegistration = handler ;
227+ break ;
228+ }
229+ }
230+ pluginField .setAccessible (false );
231+ if (floodgateEventHandlerRegistration == null ) {
232+ return null ;
233+ }
234+
235+ // Extract the EventHandler instance from Velocity's internal registration handler storage
236+ Field eventHandlerField = floodgateEventHandlerRegistration .getClass ().getDeclaredField ("instance" );
237+ eventHandlerField .setAccessible (true );
238+ Object floodgateEventHandler = eventHandlerField .get (floodgateEventHandlerRegistration );
239+ eventHandlerField .setAccessible (false );
240+
241+ // Get the Floodgate playerCache field
242+ Field playerCacheField = floodgateEventHandler .getClass ().getDeclaredField ("playerCache" );
243+ playerCacheField .setAccessible (true );
244+ @ SuppressWarnings ("unchecked" )
245+ Cache <InboundConnection , FloodgatePlayer > playerCache =
246+ (Cache <InboundConnection , FloodgatePlayer >) playerCacheField .get (floodgateEventHandler );
247+ playerCacheField .setAccessible (false );
248+
249+ // Find the FloodgatePlayer instance in playerCache
250+ FloodgatePlayer floodgatePlayer = playerCache .getIfPresent (connection );
251+ if (floodgatePlayer == null ) {
252+ return null ;
253+ }
254+ return floodgatePlayer .getCorrectUsername ();
255+
256+ } catch (Exception e ) {
257+ e .printStackTrace ();
258+ }
259+ return null ;
260+ }
180261}
0 commit comments