package net.ME1312.SubServers.Velocity; import net.ME1312.Galaxi.Library.Config.YAMLConfig; import net.ME1312.Galaxi.Library.Container.Pair; import net.ME1312.Galaxi.Library.Map.ObjectMap; import net.ME1312.Galaxi.Library.Try; import net.ME1312.Galaxi.Library.Util; import net.ME1312.Galaxi.Library.Version.Version; import net.ME1312.SubData.Client.DataClient; import net.ME1312.SubData.Client.Encryption.AES; import net.ME1312.SubData.Client.Encryption.DHE; import net.ME1312.SubData.Client.Encryption.RSA; import net.ME1312.SubData.Client.Library.DisconnectReason; import net.ME1312.SubData.Client.SubDataClient; import net.ME1312.SubServers.Client.Common.Network.Packet.PacketDisconnectPlayer; import net.ME1312.SubServers.Velocity.Event.SubAddServerEvent; import net.ME1312.SubServers.Velocity.Event.SubRemoveServerEvent; import net.ME1312.SubServers.Velocity.Event.SubStartEvent; import net.ME1312.SubServers.Velocity.Event.SubStoppedEvent; import net.ME1312.SubServers.Velocity.Library.Compatibility.ChatColor; import net.ME1312.SubServers.Velocity.Library.Compatibility.Logger; import net.ME1312.SubServers.Velocity.Library.ConfigUpdater; import net.ME1312.SubServers.Velocity.Library.Fallback.FallbackState; import net.ME1312.SubServers.Velocity.Library.Fallback.SmartFallback; import net.ME1312.SubServers.Velocity.Library.Metrics; import net.ME1312.SubServers.Velocity.Network.Packet.PacketExSyncPlayer; import net.ME1312.SubServers.Velocity.Network.SubProtocol; import net.ME1312.SubServers.Velocity.Server.CachedPlayer; import net.ME1312.SubServers.Velocity.Server.ServerData; import net.ME1312.SubServers.Velocity.Server.SubServerData; import com.dosse.upnp.UPnP; import com.google.gson.Gson; import com.google.inject.Inject; import com.velocitypowered.api.event.PostOrder; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.connection.DisconnectEvent; import com.velocitypowered.api.event.connection.LoginEvent; import com.velocitypowered.api.event.player.KickedFromServerEvent; import com.velocitypowered.api.event.player.ServerPostConnectEvent; import com.velocitypowered.api.event.player.ServerPreConnectEvent; import com.velocitypowered.api.event.proxy.*; import com.velocitypowered.api.plugin.Plugin; import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.plugin.PluginDescription; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ServerConnection; import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.api.proxy.server.ServerPing; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextReplacementConfig; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.net.InetAddress; import java.net.URL; import java.nio.charset.Charset; import java.util.*; import java.util.concurrent.TimeUnit; @Plugin(id = "subservers-sync", name = "SubServers-Sync", authors = "ME1312", version = "2.19a", url = "https://github.com/ME1312/SubServers-2", description = "Dynamically sync player and server connection info over multiple proxy instances") public class ExProxy { HashMap subdata = new HashMap(); Pair>> lang = null; public final Map servers = new TreeMap(); public final HashMap rPlayerLinkS = new HashMap(); public final HashMap rPlayerLinkP = new HashMap(); public final HashMap rPlayers = new HashMap(); private final HashMap fallback = new HashMap(); public final org.apache.logging.log4j.Logger out; public final File dir = new File(System.getProperty("user.dir")); public YAMLConfig config; public final PluginDescription plugin; private final ProxyServer proxy; private final Metrics.Factory metrics; public final SubAPI api = new SubAPI(this); public SubProtocol subprotocol; public final Version version; public boolean running = false; public long lastReload = -1; private long resetDate = 0; private boolean reconnect = false; private boolean posted = false; @Inject private ExProxy(ProxyServer proxy, PluginContainer plugin, Metrics.Factory metrics) throws Exception { this.proxy = proxy; this.metrics = metrics; this.version = Version.fromString((this.plugin = plugin.getDescription()).getVersion().get()); Util.reflect(Logger.class.getDeclaredField("parent"), null, (this.out = Util.reflect(proxy.getClass().getDeclaredField("logger"), proxy))); Logger.get("SubServers").info("Loading SubServers.Sync v" + version.toString() + " Libraries (for Minecraft " + api.getGameVersion()[api.getGameVersion().length - 1] + ")"); Try.all.run(() -> new CachedPlayer((Player) null)); // runs File dir = new File(this.dir, "SubServers"); dir.mkdir(); ConfigUpdater.updateConfig(new File(dir, "sync.yml")); config = new YAMLConfig(new File(dir, "sync.yml")); SmartFallback.addInspector((player, info) -> { ServerData server = getData(info.getServerInfo()); double confidence = 0; if (server != null) { if (!server.isHidden()) confidence++; if (!server.isRestricted()) confidence++; if (server.getSubData()[0] != null) confidence++; if (player != null) { if (server.canAccess(player)) confidence++; } } if (server instanceof SubServerData) { if (!((SubServerData) server).isRunning()) return null; } return confidence; }); subprotocol = SubProtocol.get(); subprotocol.registerCipher("DHE", DHE.get(128)); subprotocol.registerCipher("DHE-128", DHE.get(128)); subprotocol.registerCipher("DHE-192", DHE.get(192)); subprotocol.registerCipher("DHE-256", DHE.get(256)); } /** * Load Hosts, Servers, SubServers, and SubData Direct */ @Subscribe public void initialize(ProxyInitializeEvent e) { try { running = true; SmartFallback.dns_forward = config.get().getMap("Settings").getMap("Smart-Fallback", new ObjectMap<>()).getBoolean("DNS-Forward", false); resetDate = Calendar.getInstance().getTime().getTime(); ConfigUpdater.updateConfig(new File(dir, "SubServers/sync.yml")); config.reload(); synchronized (rPlayers) { for (Player local : proxy.getAllPlayers()) { CachedPlayer player = new CachedPlayer(local); rPlayerLinkP.put(player.getUniqueId(), player.getProxyName().toLowerCase()); rPlayers.put(player.getUniqueId(), player); ServerInfo server = local.getCurrentServer().map(ServerConnection::getServerInfo).orElse(null); if (servers.containsKey(server)) rPlayerLinkS.put(player.getUniqueId(), servers.get(server)); } } subprotocol.unregisterCipher("AES"); subprotocol.unregisterCipher("AES-128"); subprotocol.unregisterCipher("AES-192"); subprotocol.unregisterCipher("AES-256"); subprotocol.unregisterCipher("RSA"); api.name = config.get().getMap("Settings").getMap("SubData").getString("Name"); if (config.get().getMap("Settings").getMap("SubData").getString("Password", "").length() > 0) { subprotocol.registerCipher("AES", new AES(128, config.get().getMap("Settings").getMap("SubData").getString("Password"))); subprotocol.registerCipher("AES-128", new AES(128, config.get().getMap("Settings").getMap("SubData").getString("Password"))); subprotocol.registerCipher("AES-192", new AES(192, config.get().getMap("Settings").getMap("SubData").getString("Password"))); subprotocol.registerCipher("AES-256", new AES(256, config.get().getMap("Settings").getMap("SubData").getString("Password"))); Logger.get("SubData").info("AES Encryption Available"); } if (new File(dir, "SubServers/subdata.rsa.key").exists()) { try { subprotocol.registerCipher("RSA", new RSA(new File(dir, "SubServers/subdata.rsa.key"))); Logger.get("SubData").info("RSA Encryption Available"); } catch (Exception x) { x.printStackTrace(); } } reconnect = true; Logger.get("SubData").info(""); Logger.get("SubData").info("Connecting to /" + config.get().getMap("Settings").getMap("SubData").getString("Address", "127.0.0.1:4391")); connect(Logger.get("SubData"), null); if (!posted) { posted = true; post(); } } catch (IOException x) { x.printStackTrace(); } } private void connect(java.util.logging.Logger log, Pair disconnect) throws IOException { int reconnect = config.get().getMap("Settings").getMap("SubData").getInt("Reconnect", 60); if (disconnect == null || (this.reconnect && reconnect > 0 && disconnect.key() != DisconnectReason.PROTOCOL_MISMATCH && disconnect.key() != DisconnectReason.ENCRYPTION_MISMATCH)) { long reset = resetDate; Timer timer = new Timer("SubServers.Sync::SubData_Reconnect_Handler"); if (disconnect != null) log.info("Attempting reconnect in " + reconnect + " seconds"); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { try { if (reset == resetDate && (subdata.getOrDefault(0, null) == null || subdata.get(0).isClosed())) { SubDataClient open = subprotocol.open(InetAddress.getByName(config.get().getMap("Settings").getMap("SubData").getString("Address", "127.0.0.1:4391").split(":")[0]), Integer.parseInt(config.get().getMap("Settings").getMap("SubData").getString("Address", "127.0.0.1:4391").split(":")[1])); if (subdata.getOrDefault(0, null) != null) subdata.get(0).reconnect(open); subdata.put(0, open); } timer.cancel(); } catch (IOException e) { Logger.get("SubData").info("Connection was unsuccessful, retrying in " + reconnect + " seconds"); } } }, (disconnect == null)?0:TimeUnit.SECONDS.toMillis(reconnect), TimeUnit.SECONDS.toMillis(reconnect)); } } private void post() { if (!config.get().getMap("Settings").getStringList("Disabled-Overrides", Collections.emptyList()).contains("/server")) proxy.getCommandManager().register("server", new SubCommand.BungeeServer(this)); if (!config.get().getMap("Settings").getStringList("Disabled-Overrides", Collections.emptyList()).contains("/glist")) proxy.getCommandManager().register("glist", new SubCommand.BungeeList(this)); proxy.getChannelRegistrar().register(SubCommand.pmc); proxy.getCommandManager().register("subservers", new SubCommand(this), "subserver", "sub"); if (config.get().getMap("Settings").getMap("Smart-Fallback", new ObjectMap<>()).getBoolean("Enabled", true)) proxy.getEventManager().register(this, new SmartFallback(config.get().getMap("Settings").getMap("Smart-Fallback", new ObjectMap<>()))); Try.none.run(() -> metrics.make(this, 11953).addCustomChart(Util.reflect(Metrics.class.getDeclaredField("PLAYER_VERSIONS"), null))); new Timer("SubServers.Sync::Routine_Update_Check").schedule(new TimerTask() { @SuppressWarnings("unchecked") @Override public void run() { try { ObjectMap tags = new ObjectMap(new Gson().fromJson("{\"tags\":" + Util.readAll(new BufferedReader(new InputStreamReader(new URL("https://api.github.com/repos/ME1312/SubServers-2/git/refs/tags").openStream(), Charset.forName("UTF-8")))) + '}', Map.class)); List versions = new LinkedList(); Version updversion = version; int updcount = 0; for (ObjectMap tag : tags.getMapList("tags")) versions.add(Version.fromString(tag.getString("ref").substring(10))); Collections.sort(versions); for (Version version : versions) { if (version.compareTo(updversion) > 0) { updversion = version; updcount++; } } if (updcount > 0) Logger.get("SubServers").info("SubServers.Sync v" + updversion + " is available. You are " + updcount + " version" + ((updcount == 1)?"":"s") + " behind."); } catch (Exception e) {} } }, 0, TimeUnit.DAYS.toMillis(2)); int rpec_i = config.get().getMap("Settings").getInt("RPEC-Check-Interval", 300); int rpec_s = rpec_i - new Random().nextInt((rpec_i / 3) + 1); new Timer("SubServers.Sync::RemotePlayer_Error_Checking").schedule(new TimerTask() { @Override public void run() { if (api.getName() != null && api.getSubDataNetwork()[0] != null && !api.getSubDataNetwork()[0].isClosed()) { api.getProxy(api.getName(), proxy -> { synchronized (rPlayers) { ArrayList add = new ArrayList(); for (Player player : ExProxy.this.proxy.getAllPlayers()) { if (!rPlayers.containsKey(player.getUniqueId())) { // Add players that don't exist CachedPlayer p = new CachedPlayer(player); rPlayerLinkP.put(player.getUniqueId(), p.getProxyName().toLowerCase()); rPlayers.put(player.getUniqueId(), p); ServerInfo server = player.getCurrentServer().map(ServerConnection::getServerInfo).orElse(null); if (servers.containsKey(server)) rPlayerLinkS.put(player.getUniqueId(), servers.get(server)); add.add(p); } } ArrayList remove = new ArrayList(); for (Pair player : proxy.getPlayers()) { // Remove players that shouldn't exist if (!ExProxy.this.proxy.getPlayer(player.value()).isPresent()) { remove.add(rPlayers.get(player.value())); rPlayerLinkS.remove(player.value()); rPlayerLinkP.remove(player.value()); rPlayers.remove(player.value()); } } for (UUID player : Util.getBackwards(rPlayerLinkP, api.getName().toLowerCase())) { // Remove players that shouldn't exist (internally) if (!ExProxy.this.proxy.getPlayer(player).isPresent()) { rPlayerLinkS.remove(player); rPlayerLinkP.remove(player); rPlayers.remove(player); } } LinkedList packets = new LinkedList(); // Compile change data for external proxies if (add.size() > 0) packets.add(new PacketExSyncPlayer(true, add.toArray(new CachedPlayer[0]))); if (remove.size() > 0) packets.add(new PacketExSyncPlayer(false, remove.toArray(new CachedPlayer[0]))); if (packets.size() > 0) { PacketExSyncPlayer[] packet = packets.toArray(new PacketExSyncPlayer[0]); if (api.getSubDataNetwork()[0] != null) { ((SubDataClient) api.getSubDataNetwork()[0]).sendPacket(packet); } } } }); } } }, TimeUnit.SECONDS.toMillis(rpec_s), TimeUnit.SECONDS.toMillis(rpec_i)); } /** * Port-forward Listeners */ @Subscribe public void registerListener(ListenerBoundEvent e) { if (UPnP.isUPnPAvailable()) { if (config.get().getMap("Settings").getMap("UPnP", new ObjectMap()).getBoolean("Forward-Proxy", true)) { UPnP.openPortTCP(e.getAddress().getPort()); } } else { out.warn("UPnP is currently unavailable. Ports may not be automatically forwarded on this device."); } } /** * Get the Proxy Instance * * @return Proxy Instance */ @SuppressWarnings("deprecation") public static ProxyServer getInstance() { return SubAPI.getInstance().getInternals().proxy; } /** * Get Internal Server Data * * @param server ServerInfo * @return ServerData */ public ServerData getData(ServerInfo server) { return (server == null)? null : servers.getOrDefault(server, null); } /** * Simulate Proxy Reload */ @Subscribe public void reload(ProxyReloadEvent e) { shutdown(null); initialize(null); } /** * Un-port-forward Listeners */ @Subscribe public void unregisterListener(ListenerCloseEvent e) { if (UPnP.isUPnPAvailable()) { if (UPnP.isMappedTCP(e.getAddress().getPort())) UPnP.closePortTCP(e.getAddress().getPort()); } } /** * Reset all changes made by initialize() * * @see ExProxy#initialize(ProxyInitializeEvent) */ @Subscribe public void shutdown(ProxyShutdownEvent e) { try { running = false; Logger.get("SubServers").info("Removing synced data"); servers.clear(); reconnect = false; ArrayList tmp = new ArrayList(); tmp.addAll(subdata.values()); for (SubDataClient client : tmp) if (client != null) { client.close(); Try.all.run(client::waitFor); } subdata.clear(); subdata.put(0, null); rPlayerLinkS.clear(); rPlayerLinkP.clear(); rPlayers.clear(); } catch (Exception x) { x.printStackTrace(); } } @Subscribe(order = PostOrder.LAST) public void ping(ProxyPingEvent e) { int offline = 0; RegisteredServer[] overrides; if ((overrides = SmartFallback.getForcedHosts(e.getConnection())) != null || (overrides = SmartFallback.getDNS(e.getConnection())) != null) { for (RegisteredServer server : overrides) if (server instanceof SubServerData && !((SubServerData) server).isRunning()) offline++; if (offline >= overrides.length) { e.setPing(new ServerPing(e.getPing().getVersion(), e.getPing().getPlayers().orElse(null), ChatColor.convertColor(api.getLang("SubServers", "Bungee.Ping.Offline")), e.getPing().getFavicon().orElse(null), e.getPing().getModinfo().orElse(null))); } } else { for (String name : proxy.getConfiguration().getAttemptConnectionOrder()) { ServerData server = proxy.getServer(name).map(RegisteredServer::getServerInfo).map(this::getData).orElse(null); if (server instanceof SubServerData && !((SubServerData) server).isRunning()) offline++; } if (offline >= proxy.getConfiguration().getAttemptConnectionOrder().size()) { e.setPing(new ServerPing(e.getPing().getVersion(), e.getPing().getPlayers().orElse(null), ChatColor.convertColor(api.getLang("SubServers", "Bungee.Ping.Offline")), e.getPing().getFavicon().orElse(null), e.getPing().getModinfo().orElse(null))); } } } @Subscribe(order = PostOrder.FIRST) public void login(LoginEvent e) { out.info("UUID of player " + e.getPlayer().getGameProfile().getName() + " is " + e.getPlayer().getUniqueId()); if (rPlayers.containsKey(e.getPlayer().getUniqueId())) { Logger.get("SubServers").warning(e.getPlayer().getGameProfile().getName() + " connected, but already had a database entry"); CachedPlayer player = rPlayers.get(e.getPlayer().getUniqueId()); if (player.getProxyName() != null && player.getProxyName().equalsIgnoreCase(api.getName())) { proxy.getPlayer(player.getUniqueId()).ifPresent(p -> p.disconnect(Component.translatable("velocity.error.already-connected-proxy", NamedTextColor.RED))); } else { ((SubDataClient) api.getSubDataNetwork()[0]).sendPacket(new PacketDisconnectPlayer(new UUID[]{ player.getUniqueId() }, LegacyComponentSerializer.legacySection().serialize(Component.translatable("velocity.error.already-connected-proxy", NamedTextColor.RED)))); } } } @Subscribe(order = PostOrder.LAST) public void validate(ServerPreConnectEvent e) { if (e.getPlayer().isActive()) { ServerData server = getData(e.getOriginalServer().getServerInfo()); if (server == null || !server.canAccess(e.getPlayer())) { if (!e.getPlayer().getCurrentServer().isPresent() || fallback.containsKey(e.getPlayer().getUniqueId())) { if (!fallback.containsKey(e.getPlayer().getUniqueId()) || fallback.get(e.getPlayer().getUniqueId()).names.contains(e.getOriginalServer().getServerInfo().getName())) { Component text = ChatColor.convertColor(api.getLang("SubServers", "Bungee.Restricted")); KickedFromServerEvent kick = new KickedFromServerEvent(e.getPlayer(), e.getOriginalServer(), text, true, KickedFromServerEvent.DisconnectPlayer.create(text)); fallback(kick); if (e.getPlayer().getCurrentServer().isPresent()) e.setResult(ServerPreConnectEvent.ServerResult.denied()); if (kick.getResult() instanceof KickedFromServerEvent.DisconnectPlayer) e.getPlayer().disconnect(text); else if (kick.getResult() instanceof KickedFromServerEvent.RedirectPlayer) e.getPlayer().createConnectionRequest(((KickedFromServerEvent.RedirectPlayer) kick.getResult()).getServer()).fireAndForget(); } } else { e.getPlayer().sendMessage(ChatColor.convertColor(api.getLang("SubServers", "Bungee.Restricted"))); e.setResult(ServerPreConnectEvent.ServerResult.denied()); } } else if (e.getPlayer().getCurrentServer().isPresent() && !fallback.containsKey(e.getPlayer().getUniqueId()) && server instanceof SubServerData && !((SubServerData) server).isRunning()) { e.getPlayer().sendMessage(ChatColor.convertColor(api.getLang("SubServers", "Bungee.Server.Offline"))); e.setResult(ServerPreConnectEvent.ServerResult.denied()); } if (fallback.containsKey(e.getPlayer().getUniqueId())) { FallbackState state = fallback.get(e.getPlayer().getUniqueId()); if (state.names.contains(e.getOriginalServer().getServerInfo().getName())) { state.remove(e.getOriginalServer().getServerInfo().getName()); } else if (e.getPlayer().getCurrentServer().isPresent()) { fallback.remove(e.getPlayer().getUniqueId()); } } } else { e.setResult(ServerPreConnectEvent.ServerResult.denied()); } } @SuppressWarnings("UnstableApiUsage") @Subscribe(order = PostOrder.LAST) public void connected(ServerPostConnectEvent e) { ServerData server = getData(e.getPlayer().getCurrentServer().get().getServerInfo()); if (server != null) { if (e.getPlayer().isActive()) { synchronized (rPlayers) { ObjectMap raw = CachedPlayer.translate(e.getPlayer()); raw.set("server", server.getName()); CachedPlayer player = new CachedPlayer(raw); rPlayerLinkP.put(player.getUniqueId(), player.getProxyName().toLowerCase()); rPlayers.put(player.getUniqueId(), player); rPlayerLinkS.put(player.getUniqueId(), server); if (api.getSubDataNetwork()[0] != null) { ((SubDataClient) api.getSubDataNetwork()[0]).sendPacket(new PacketExSyncPlayer(true, player)); } } } if (fallback.containsKey(e.getPlayer().getUniqueId())) { fallback.get(e.getPlayer().getUniqueId()).done(() -> { if (fallback.containsKey(e.getPlayer().getUniqueId()) && e.getPlayer().getCurrentServer().get().getServerInfo().getName().equals(server.get().getName())) { fallback.remove(e.getPlayer().getUniqueId()); } }, proxy.getConfiguration().getConnectTimeout() + 500); } } } @Subscribe(order = PostOrder.LAST) public void fallback(KickedFromServerEvent e) { if (e.getPlayer().isActive() && config.get().getMap("Settings").getMap("Smart-Fallback", new ObjectMap<>()).getBoolean("Fallback", true)) { FallbackState state; boolean init = !fallback.containsKey(e.getPlayer().getUniqueId()); if (init) { Map map = SmartFallback.getFallbackServers(e.getPlayer()); map.remove(e.getServer().getServerInfo().getName()); state = new FallbackState(e.getPlayer().getUniqueId(), map, e.getServerKickReason().orElse(Component.text(""))); if (e.getPlayer().getCurrentServer().isPresent()) state.remove(e.getPlayer().getCurrentServer().get().getServerInfo().getName()); } else { state = fallback.get(e.getPlayer().getUniqueId()); LinkedList tmp = new LinkedList<>(state.servers); for (RegisteredServer server : tmp) if (server.getServerInfo().equals(e.getServer().getServerInfo())) state.remove(server); } RegisteredServer rs = e.getServer(); ServerData server = getData(rs.getServerInfo()); e.getPlayer().sendMessage(ChatColor.convertColor(api.getLang("SubServers", "Bungee.Feature.Smart-Fallback") .replace("$str$", (server != null)? server.getDisplayName() : rs.getServerInfo().getName())) .replaceText(TextReplacementConfig.builder().match("\\$msg\\$").replacement(e.getServerKickReason().orElse(Component.text(""))).build()) ); if (state.servers.isEmpty()) { if (e.getPlayer().getCurrentServer().isPresent()) { fallback.remove(e.getPlayer().getUniqueId()); rs = e.getPlayer().getCurrentServer().get().getServer(); server = getData(rs.getServerInfo()); e.setResult(KickedFromServerEvent.Notify.create(ChatColor.convertColor(api.getLang("SubServers", "Bungee.Feature.Smart-Fallback.Result").replace("$str$", (server != null)? server.getDisplayName() : rs.getServerInfo().getName())))); } else { e.setResult(KickedFromServerEvent.DisconnectPlayer.create(state.reason)); } } else { if (init) fallback.put(e.getPlayer().getUniqueId(), state); rs = state.servers.getFirst(); server = getData(rs.getServerInfo()); e.setResult(KickedFromServerEvent.RedirectPlayer.create(rs, ChatColor.convertColor(api.getLang("SubServers", "Bungee.Feature.Smart-Fallback.Result").replace("$str$", (server != null)? server.getDisplayName() : rs.getServerInfo().getName())))); } } } @Subscribe(order = PostOrder.LAST) public void disconnected(DisconnectEvent e) { UUID id = e.getPlayer().getUniqueId(); fallback.remove(id); SubCommand.permitted.remove(id); synchronized (rPlayers) { if (rPlayers.containsKey(id) && (!rPlayerLinkP.containsKey(id) || rPlayerLinkP.get(id).equalsIgnoreCase(api.getName()))) { CachedPlayer player = rPlayers.get(id); rPlayerLinkS.remove(id); rPlayerLinkP.remove(id); rPlayers.remove(id); if (api.getSubDataNetwork()[0] != null) { ((SubDataClient) api.getSubDataNetwork()[0]).sendPacket(new PacketExSyncPlayer(false, player)); } } } } @SuppressWarnings("unchecked") private Map getSubDataAsMap(net.ME1312.SubServers.Client.Common.Network.API.Server server) { HashMap map = new HashMap(); ObjectMap subdata = new ObjectMap((Map) server.getRaw().getObject("subdata")); for (Integer channel : subdata.getKeys()) map.put(channel, subdata.getUUID(channel)); return map; } @Subscribe(order = PostOrder.FIRST) public void add(SubAddServerEvent e) { api.getServer(e.getServer(), server -> { if (server != null) { ServerData data; if (server instanceof net.ME1312.SubServers.Client.Common.Network.API.SubServer) { data = new SubServerData(server.getSignature(), server.getName(), server.getDisplayName(), server.getAddress(), getSubDataAsMap(server), server.getMotd(), server.isHidden(), server.isRestricted(), server.getWhitelist(), ((net.ME1312.SubServers.Client.Common.Network.API.SubServer) server).isRunning()); Logger.get("SubServers").info("Added SubServer: " + e.getServer()); } else { data = new ServerData(server.getSignature(), server.getName(), server.getDisplayName(), server.getAddress(), getSubDataAsMap(server), server.getMotd(), server.isHidden(), server.isRestricted(), server.getWhitelist()); Logger.get("SubServers").info("Added Server: " + e.getServer()); } ServerData old = servers.put(data.get(), data); if (old != null && proxy.getServer(data.getName()).isPresent()) proxy.unregisterServer(old.get()); proxy.registerServer(data.get()); } else System.out.println("PacketDownloadServerInfo(" + e.getServer() + ") returned with an invalid response"); }); } public Boolean merge(net.ME1312.SubServers.Client.Common.Network.API.Server server) { ServerData current = proxy.getServer(server.getName()).map(RegisteredServer::getServerInfo).map(this::getData).orElse(null); if (server instanceof net.ME1312.SubServers.Client.Common.Network.API.SubServer || !(current instanceof SubServerData)) { if (current == null || !current.getSignature().equals(server.getSignature())) { ServerData data; if (server instanceof net.ME1312.SubServers.Client.Common.Network.API.SubServer) { data = new SubServerData(server.getSignature(), server.getName(), server.getDisplayName(), server.getAddress(), getSubDataAsMap(server), server.getMotd(), server.isHidden(), server.isRestricted(), server.getWhitelist(), ((net.ME1312.SubServers.Client.Common.Network.API.SubServer) server).isRunning()); } else { data = new ServerData(server.getSignature(), server.getName(), server.getDisplayName(), server.getAddress(), getSubDataAsMap(server), server.getMotd(), server.isHidden(), server.isRestricted(), server.getWhitelist()); } ServerData old = servers.put(data.get(), data); if (old != null && proxy.getServer(data.getName()).isPresent()) proxy.unregisterServer(old.get()); proxy.registerServer(data.get()); Logger.get("SubServers").info("Added "+((server instanceof net.ME1312.SubServers.Client.Common.Network.API.SubServer)?"Sub":"")+"Server: " + server.getName()); return true; } else { if (server instanceof net.ME1312.SubServers.Client.Common.Network.API.SubServer) { if (((net.ME1312.SubServers.Client.Common.Network.API.SubServer) server).isRunning() != ((SubServerData) current).isRunning()) ((SubServerData) current).setRunning(((net.ME1312.SubServers.Client.Common.Network.API.SubServer) server).isRunning()); } if (!server.getMotd().equals(current.getMotd())) current.setMotd(server.getMotd()); if (server.isHidden() != current.isHidden()) current.setHidden(server.isHidden()); if (server.isRestricted() != current.isRestricted()) current.setRestricted(server.isRestricted()); if (!server.getDisplayName().equals(current.getDisplayName())) current.setDisplayName(server.getDisplayName()); Logger.get("SubServers").info("Re-added "+((server instanceof net.ME1312.SubServers.Client.Common.Network.API.SubServer)?"Sub":"")+"Server: " + server.getName()); return false; } } return null; } @Subscribe(order = PostOrder.FIRST) public void start(SubStartEvent e) { ServerData server = proxy.getServer(e.getServer()).map(RegisteredServer::getServerInfo).map(this::getData).orElse(null); if (server instanceof SubServerData) ((SubServerData) server).setRunning(true); } @Subscribe(order = PostOrder.FIRST) public void stop(SubStoppedEvent e) { ServerData server = proxy.getServer(e.getServer()).map(RegisteredServer::getServerInfo).map(this::getData).orElse(null); if (server instanceof SubServerData) ((SubServerData) server).setRunning(false); } @Subscribe(order = PostOrder.FIRST) public void remove(SubRemoveServerEvent e) { ServerData server = proxy.getServer(e.getServer()).map(RegisteredServer::getServerInfo).map(this::getData).orElse(null); if (server != null) { servers.remove(server.get()); proxy.unregisterServer(server.get()); Logger.get("SubServers").info("Removed Server: " + e.getServer()); } } }