package net.ME1312.SubServers.Velocity.Library.Fallback; import net.ME1312.Galaxi.Library.Map.ObjectMap; import net.ME1312.Galaxi.Library.Try; import net.ME1312.Galaxi.Library.Util; import net.ME1312.SubServers.Velocity.ExProxy; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.player.PlayerChooseInitialServerEvent; import com.velocitypowered.api.proxy.InboundConnection; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.server.RegisteredServer; import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; /** * Smart Fallback Handler Class */ public class SmartFallback { private static List inspectors = new CopyOnWriteArrayList(); public static boolean dns_forward = false; public SmartFallback(ObjectMap settings) { dns_forward = settings.getBoolean("DNS-Forward", false); } @Subscribe public void getServer(PlayerChooseInitialServerEvent e) { RegisteredServer[] overrides; if ((overrides = getForcedHosts(e.getPlayer())) != null || (overrides = getDNS(e.getPlayer())) != null) { e.setInitialServer(overrides[0]); } else { Map fallbacks = getFallbackServers(e.getPlayer()); if (/*(override = getReconnectServer(player)) != null || */!fallbacks.isEmpty()) { e.setInitialServer((overrides != null)? overrides[0] : new LinkedList<>(fallbacks.values()).getFirst()); } else { e.setInitialServer(null); } } } /** * Grabs the Forced Host Server for this connection * * @param connection Connection to check * @return Forced Host Servers (or null if there are none) */ public static RegisteredServer[] getForcedHosts(InboundConnection connection) { if (!connection.getVirtualHost().isPresent()) { return null; } else { List names = ExProxy.getInstance().getConfiguration().getForcedHosts().get(connection.getVirtualHost().get().getHostString()); if (names == null) { return null; } else { LinkedList forced = new LinkedList<>(); for (String name : names) { Optional server = ExProxy.getInstance().getServer(name); server.ifPresent(forced::add); } return (forced.size() == 0)? null : forced.toArray(new RegisteredServer[0]); } } } /** * Grabs the Server that a connection's DNS matches * * @param connection Connection to check * @return DNS Forward Server */ public static RegisteredServer[] getDNS(InboundConnection connection) { if (!connection.getVirtualHost().isPresent() || !dns_forward) { return null; } else { RegisteredServer server = null; String dns = connection.getVirtualHost().get().getHostString().toLowerCase(); for (RegisteredServer s : ExProxy.getInstance().getAllServers()) { if (dns.startsWith(s.getServerInfo().getName().toLowerCase() + '.')) if (server == null || server.getServerInfo().getName().length() < s.getServerInfo().getName().length()) server = s; } return (server == null)? null : new RegisteredServer[]{ server }; } } /** * Grabs the Server that a player was last connected to * * @param player Player * @return Reconnect Server *//* public static ServerInfo getReconnectServer(Player player) { if (reconnect == null) { return null; } else try { return Util.reflect(reconnect.getClass().getDeclaredMethod("getStoredServer", ProxiedPlayer.class), reconnect, player); } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { e.printStackTrace(); return null; } } */ /** * Generates a smart sorted map of fallback servers using a generated confidence score * * @return Fallback Server Map (with legacy bungee case-sensitive keys) */ public static Map getFallbackServers() { return getFallbackServers(null); } /** * Generates a smart sorted map of fallback servers using a generated confidence score * * @param player Player that is requesting fallback servers * @return Fallback Server Map (with legacy bungee case-sensitive keys) */ public static Map getFallbackServers(Player player) { TreeMap> score = new TreeMap>(Collections.reverseOrder()); for (String name : ExProxy.getInstance().getConfiguration().getAttemptConnectionOrder()) { RegisteredServer server = ExProxy.getInstance().getServer(name).orElse(null); if (server != null) { boolean valid = true; double confidence = 0; List inspectors = new ArrayList(); inspectors.addAll(SmartFallback.inspectors); for (FallbackInspector inspector : inspectors) try { Double response = inspector.inspect(player, server); if (response == null) { valid = false; } else { confidence += response; } } catch (Throwable e) { new InvocationTargetException(e, "Exception while running inspecting fallback server: " + server.getServerInfo().getName()).printStackTrace(); } if (valid) { List servers = (score.containsKey(confidence))?score.get(confidence):new LinkedList(); servers.add(server); score.put(confidence, servers); } } } Random random = new Random(); LinkedHashMap map = new LinkedHashMap(); for (List servers : score.values()) { while (!servers.isEmpty()) { RegisteredServer next = servers.get(random.nextInt(servers.size())); map.put(next.getServerInfo().getName(), next); servers.remove(next); } } return map; } /** * Add a Fallback Server Inspector * * @param inspector Inspector */ public static void addInspector(FallbackInspector inspector) { Util.nullpo(inspector); inspectors.add(inspector); } /** * Remove a Fallback Server Inspector * * @param inspector Inspector */ public static void removeInspector(FallbackInspector inspector) { Util.nullpo(inspector); Try.all.run(() -> inspectors.remove(inspector)); } }