package net.ME1312.SubServers.Bungee.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.Bungee.BungeeCommon; import net.md_5.bungee.UserConnection; import net.md_5.bungee.api.AbstractReconnectHandler; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ReconnectHandler; import net.md_5.bungee.api.config.ListenerInfo; import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.PendingConnection; import net.md_5.bungee.api.connection.ProxiedPlayer; import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; /** * Smart Fallback Handler Class */ public class SmartFallback implements ReconnectHandler { private static List inspectors = new CopyOnWriteArrayList(); private static ReconnectHandler reconnect; public static boolean dns_forward = false; public SmartFallback(ObjectMap settings) { dns_forward = settings.getBoolean("DNS-Forward", false); if (reconnect == null && settings.getBoolean("Reconnect", false)) reconnect = Try.all.get(() -> Util.reflect(ProxyServer.getInstance().getPluginManager().getPlugin("reconnect_yaml").getClass().getClassLoader().loadClass("net.md_5.bungee.module.reconnect.yaml.YamlReconnectHandler").getConstructor())); } @Override public ServerInfo getServer(ProxiedPlayer player) { return getServer(player, player instanceof UserConnection); } protected ServerInfo getServer(ProxiedPlayer player, boolean queue) { ServerInfo override; if ((override = getForcedHost(player.getPendingConnection())) != null || (override = getDNS(player.getPendingConnection())) != null) { if (queue) ((UserConnection) player).setServerJoinQueue(new LinkedList<>()); return override; } else { Map fallbacks = getFallbackServers(player.getPendingConnection().getListener(), player); if ((override = getReconnectServer(player)) != null || !fallbacks.isEmpty()) { if (queue) ((UserConnection) player).setServerJoinQueue(new LinkedList<>(fallbacks.keySet())); return (override != null)? override : new LinkedList<>(fallbacks.values()).getFirst(); } else { return null; } } } /** * Grabs the Forced Host Server for this connection * * @see AbstractReconnectHandler#getForcedHost(PendingConnection) Essentially the same method, but more ambigous * @param connection Connection to check * @return Forced Host Server (or null if there is none) */ public static ServerInfo getForcedHost(PendingConnection connection) { if (connection.getVirtualHost() == null) { return null; } else { String forced = connection.getListener().getForcedHosts().get(connection.getVirtualHost().getHostString()); //if (forced == null && con.getListener().isForceDefault()) { // This is the part of the method that made it ambiguous // forced = con.getListener().getDefaultServer(); // Aside from that, everything else was fine //} // :( return ProxyServer.getInstance().getServerInfo(forced); } } /** * Grabs the Server that a connection's DNS matches * * @param connection Connection to check * @return DNS Forward Server */ public static ServerInfo getDNS(PendingConnection connection) { if (connection.getVirtualHost() == null || !dns_forward) { return null; } else { Map.Entry server = null; String dns = connection.getVirtualHost().getHostString().toLowerCase(); for (Map.Entry s : ((BungeeCommon) ProxyServer.getInstance()).getServersCopy().entrySet()) { if (dns.startsWith(s.getKey().toLowerCase() + '.')) if (server == null || server.getKey().length() < s.getKey().length()) server = s; } return (server == null)?null:server.getValue(); } } /** * Grabs the Server that a player was last connected to * * @param player Player * @return Reconnect Server */ public static ServerInfo getReconnectServer(ProxiedPlayer 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 * * @param listener Listener to grab fallback servers from * @return Fallback Server Map (with legacy bungee case-sensitive keys) */ public static Map getFallbackServers(ListenerInfo listener) { return getFallbackServers(listener, null); } /** * Generates a smart sorted map of fallback servers using a generated confidence score * * @param listener Listener to grab fallback servers from * @param player Player that is requesting fallback servers * @return Fallback Server Map (with legacy bungee case-sensitive keys) */ public static Map getFallbackServers(ListenerInfo listener, ProxiedPlayer player) { TreeMap> score = new TreeMap>(Collections.reverseOrder()); for (String name : listener.getServerPriority()) { ServerInfo server = ProxyServer.getInstance().getServerInfo(name); 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.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()) { ServerInfo next = servers.get(random.nextInt(servers.size())); map.put(next.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)); } @Override public void setServer(ProxiedPlayer player) { if (reconnect != null) reconnect.setServer(player); } @Override public void save() { if (reconnect != null) reconnect.save(); } @Override public void close() { if (reconnect != null) reconnect.close(); } }