Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,4 +230,8 @@ private void loadOrGenerateProxyId() {
public UUID getProxyId() {
return proxyId;
}

public ProxyServer getServer() {
return server;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,17 @@
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.EventManager;
import com.velocitypowered.api.event.EventTask;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.DisconnectEvent;
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;
Expand All @@ -52,6 +56,7 @@
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;
Expand All @@ -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;

Expand All @@ -80,6 +87,15 @@ 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(connection);
if (floodgateUsername != null) {
plugin.getLog().info("Found player's Floodgate: {}", floodgateUsername);
username = floodgateUsername;
}
}

Action action = antiBotService.onIncomingConnection(address, username);
switch (action) {
case Ignore:
Expand Down Expand Up @@ -177,4 +193,74 @@ 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 connection
* @return the Floodgate username or null if not found
*/
private String getFloodgateUsername(InboundConnection connection) {
try {
// get floodgate's event handler
Object floodgateEventHandler = getFloodgateHandler();
if (floodgateEventHandler == null) {
return null;
}

// Get the Floodgate playerCache field
Field playerCacheField = floodgateEventHandler.getClass().getDeclaredField("playerCache");
playerCacheField.setAccessible(true);
@SuppressWarnings("unchecked")
Cache<InboundConnection, FloodgatePlayer> playerCache =
(Cache<InboundConnection, FloodgatePlayer>) playerCacheField.get(floodgateEventHandler);

// Find the FloodgatePlayer instance in playerCache
FloodgatePlayer floodgatePlayer = playerCache.getIfPresent(connection);
if (floodgatePlayer == null) {
return null;
}

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<Class<?>, ?> handlersByType = (ListMultimap<Class<?>, ?>) 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);
}
}