package us.ajg0702.queue; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ServerPing; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.ProxiedPlayer; import us.ajg0702.utils.GenUtils; import us.ajg0702.utils.bungee.BungeeMessages; import us.ajg0702.utils.bungee.BungeeUtils; public class Manager { static Manager INSTANCE = null; public static Manager getInstance(Main pl) { if(INSTANCE == null) { INSTANCE = new Manager(pl); } return INSTANCE; } public static Manager getInstance() { return INSTANCE; } BungeeMessages msgs; Main pl; private Manager(Main pl) { this.pl = pl; msgs = BungeeMessages.getInstance(); reloadIntervals(); if(!pl.config.getBoolean("wait-to-load-servers")) { pl.getProxy().getScheduler().schedule(pl, new Runnable() { public void run() { reloadServers(); } }, 0, TimeUnit.MILLISECONDS); } else { pl.getProxy().getScheduler().schedule(pl, new Runnable() { public void run() { reloadServers(); } }, pl.config.getInt("wait-to-load-servers-delay"), TimeUnit.MILLISECONDS); } } /* * Returns all servers */ public List getServers() { return servers; } /** * Returns the name of all servers and groups * @return The names of all servers and groups */ public List getServerNames() { List names = new ArrayList<>(); for(QueueServer s : servers) { names.add(s.getName()); } return names; } int sendId = -1; int updateId = -1; int messagerId = -1; int actionbarId = -1; int srvRefId = -1; int queueEventId = -1; /** * Clears all intervals and re-makes them */ public void reloadIntervals() { if(sendId != -1) { try { pl.getProxy().getScheduler().cancel(sendId); } catch(IllegalArgumentException e) {} sendId = -1; } if(updateId != -1) { try { pl.getProxy().getScheduler().cancel(updateId); } catch(IllegalArgumentException e) {} updateId = -1; } if(messagerId != -1) { try { pl.getProxy().getScheduler().cancel(messagerId); } catch(IllegalArgumentException e) {} messagerId = -1; } if(actionbarId != -1) { try { pl.getProxy().getScheduler().cancel(actionbarId); } catch(IllegalArgumentException e) {} actionbarId = -1; } if(srvRefId != -1) { try { pl.getProxy().getScheduler().cancel(srvRefId); } catch(IllegalArgumentException e) {} srvRefId = -1; } if(queueEventId != -1) { try { pl.getProxy().getScheduler().cancel(queueEventId); } catch(IllegalArgumentException e) {} queueEventId = -1; } queueEventId = pl.getProxy().getScheduler().schedule(pl, new Runnable() { public void run() { sendQueueEvents(); } }, 2, 2, TimeUnit.SECONDS).getId(); sendId = pl.getProxy().getScheduler().schedule(pl, new Runnable() { public void run() { sendPlayers(); } }, 2, Math.round(pl.timeBetweenPlayers*1000), TimeUnit.MILLISECONDS).getId(); updateId = pl.getProxy().getScheduler().schedule(pl, new Runnable() { public void run() { updateServers(); } }, 0, Math.max(Math.round(pl.timeBetweenPlayers), 2), TimeUnit.SECONDS).getId(); //pl.getLogger().info("Time: "+pl.timeBetweenPlayers); messagerId = pl.getProxy().getScheduler().schedule(pl, new Runnable() { public void run() { sendMessages(); } }, 0, pl.getConfig().getInt("message-time"), TimeUnit.SECONDS).getId(); actionbarId = pl.getProxy().getScheduler().schedule(pl, new Runnable() { public void run() { sendActionBars(); } }, 0, 2, TimeUnit.SECONDS).getId(); if(pl.config.getInt("reload-servers-interval") > 0) { srvRefId = pl.getProxy().getScheduler().schedule(pl, new Runnable() { public void run() { updateServers(); } }, pl.config.getInt("reload-servers-interval"), pl.config.getInt("reload-servers-interval"), TimeUnit.SECONDS).getId(); } } /** * Get the name of the server the player is queued for. * If multiple servers are queued for, it will use the multi-server-queue-pick option in the config * @param p The player * @return The name of the server, the placeholder none message if not queued */ public String getQueuedName(ProxiedPlayer p) { List queued = findPlayerInQueue(p); if(queued.size() <= 0) { return msgs.get("placeholders.queued.none"); } QueueServer selected = queued.get(0); if(pl.config.getString("multi-server-queue-pick").equalsIgnoreCase("last")) { selected = queued.get(queued.size()-1); } return selected.getName(); } /** * Get a single server the player is queued for. Depends on the multi-server-queue-pick option in the config * @param p The player * @return The server that was chosen that the player is queued for. */ public QueueServer getSingleServer(ProxiedPlayer p) { List queued = findPlayerInQueue(p); if(queued.size() <= 0) { return null; } QueueServer selected = queued.get(0); if(pl.config.getString("multi-server-queue-pick").equalsIgnoreCase("last")) { selected = queued.get(queued.size()-1); } return selected; } List servers = new ArrayList<>(); /** * Checks servers that are in bungeecord and adds any it doesnt * know about. * * Also creates/edits server groups */ public void reloadServers() { if(pl.config == null) { pl.getLogger().warning("[MAN] Config is null"); } Map svs = ProxyServer.getInstance().getServers(); for(String name : svs.keySet()) { if(findServer(name) != null) continue; ServerInfo info = svs.get(name); //pl.getLogger().info("Adding server "+name); servers.add(new QueueServer(name, info)); } List groupsraw = pl.config.getStringList("server-groups"); for(String groupraw : groupsraw) { if(groupraw.isEmpty()) { pl.getLogger().warning("Empty group string! If you dont want server groups, set server-groups like this: server-groups: []"); continue; } String groupname = groupraw.split(":")[0]; String[] serversraw = groupraw.split(":")[1].split(","); if(getServer(groupname) != null) { pl.getLogger().warning("The name of a group ('"+groupname+"') cannot be the same as the name of a server!"); continue; } List servers = new ArrayList<>(); for(String serverraw : serversraw) { ServerInfo si = svs.get(serverraw); if(si == null) { pl.getLogger().warning("Could not find server named '"+serverraw+"' in servergroup '"+groupname+"'!"); continue; } servers.add(si); } if(servers.size() == 0) { pl.getLogger().warning("Server group '"+groupname+"' has no servers! Ignoring it."); continue; } this.servers.add(new QueueServer(groupname, servers)); } } /** * Sends actionbar updates to all players in all queues with their * position in the queue and time remaining */ public void sendActionBars() { if(!pl.getConfig().getBoolean("send-actionbar")) return; for(ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) { QueueServer s = this.getSingleServer(p); if(s == null) continue; List plys = s.getQueue(); int pos = plys.indexOf(p)+1; if(pos == 0) { plys.remove(p); continue; } int len = plys.size(); if(!s.isJoinable(p)) { String status = "unknown"; if(!s.canAccess(p)) { status = msgs.get("status.offline.restricted"); } if(s.isFull()) { status = msgs.get("status.offline.full"); } if(s.isPaused()) { status = msgs.get("status.offline.paused"); } if(!s.isOnline()) { status = msgs.get("status.offline.restarting"); } if(s.getOfflineTime() > pl.config.getInt("offline-time")) { status = msgs.get("status.offline.offline"); } p.sendMessage(ChatMessageType.ACTION_BAR, msgs.getBC("spigot.actionbar.offline", "POS:"+pos, "LEN:"+len, "SERVER:"+pl.aliases.getAlias(s.getName()), "STATUS:"+status)); } else { int time = (int) Math.round(pos*pl.timeBetweenPlayers); int min = (int) Math.floor((time) / (60)); int sec = (int) Math.floor((time % (60))); String timeStr; if(min <= 0) { timeStr = msgs.get("format.time.secs") .replaceAll("\\{m\\}", "0") .replaceAll("\\{s\\}", sec+""); } else { timeStr = msgs.get("format.time.mins") .replaceAll("\\{m\\}", min+"") .replaceAll("\\{s\\}", sec+""); } p.sendMessage(ChatMessageType.ACTION_BAR, msgs.getBC("spigot.actionbar.online", "POS:"+pos, "LEN:"+len, "SERVER:"+pl.aliases.getAlias(s.getName()), "TIME:"+timeStr)); } } } public void sendQueueEvents() { for(QueueServer s : servers) { for(ProxiedPlayer p : s.getQueue()) { BungeeUtils.sendCustomData(p, "inqueueevent", "true"); } } } /** * Sends the message to the player updating them on their position in the queue * along with their time remaining */ public void sendMessages() { for(QueueServer s : servers) { List plys = s.getQueue(); Iterator it = plys.iterator(); while(it.hasNext()) { ProxiedPlayer ply = it.next(); int pos = plys.indexOf(ply)+1; if(pos == 0) { it.remove(); continue; } sendMessage(ply, s); } } } /** * Sends a status message to a player * @param ply The player to send the message to * @param s The QueueServer the message should be about */ public void sendMessage(ProxiedPlayer ply, QueueServer s) { List plys = s.getQueue(); int pos = plys.indexOf(ply)+1; if(pos == 0) return; int len = plys.size(); int ot = s.getOfflineTime(); if(!s.isJoinable(ply)) { String status = msgs.get("status.offline.restarting"); if(ot > pl.config.getInt("offline-time")) { status = msgs.get("status.offline.offline"); } if(s.isFull() && s.isOnline()) { status = msgs.get("status.offline.full"); } if(!s.canAccess(ply)) { status = msgs.get("status.offline.restricted"); } if(s.isPaused()) { status = msgs.get("status.offline.paused"); } if(status.isEmpty()) return; ply.sendMessage(Main.formatMessage( msgs.get("status.offline.base") .replaceAll("\\{STATUS\\}", status) .replaceAll("\\{POS\\}", pos+"") .replaceAll("\\{LEN\\}", len+"") .replaceAll("\\{SERVER\\}", pl.aliases.getAlias(s.getName())) )); } else { if(msgs.get("spigot.actionbar.offline").isEmpty()) return; int time = (int) Math.round(pos*pl.timeBetweenPlayers); int min = (int) Math.floor((time) / (60)); int sec = (int) Math.floor((time % (60))); String timeStr; if(min <= 0) { timeStr = msgs.get("format.time.secs") .replaceAll("\\{m\\}", "0") .replaceAll("\\{s\\}", sec+""); } else { timeStr = msgs.get("format.time.mins") .replaceAll("\\{m\\}", min+"") .replaceAll("\\{s\\}", sec+""); } ply.sendMessage(Main.formatMessage( msgs.get("status.online.base") .replaceAll("\\{POS\\}", pos+"") .replaceAll("\\{LEN\\}", len+"") .replaceAll("\\{TIME\\}", timeStr) .replaceAll("\\{SERVER\\}", pl.aliases.getAlias(s.getName())) )); } } /** * Find a server by name * @param name Name of the server * @return The server if it exists (otherwise null) */ public QueueServer findServer(String name) { for(QueueServer server : servers) { if(server == null) continue; if(server.getName().equalsIgnoreCase(name)) { return server; } } return null; } /** * Updates info about servers. */ public void updateServers() { Iterator it = servers.iterator(); while(it.hasNext()) { it.next().update(); } } /** * Gets the ideal server in a server group. * @param s The group to check * @param p The player to check * @return the ideal server */ public ServerInfo getIdealServer(QueueServer s, ProxiedPlayer p) { HashMap serverInfos = s.getLastPings(); ServerInfo selected = null; int selectednum = 0; if(serverInfos.keySet().size() == 1) { selected = serverInfos.keySet().iterator().next(); } else { for(ServerInfo si : serverInfos.keySet()) { ServerPing sp = serverInfos.get(si); if(sp == null) continue; if(sp.getPlayers() == null) continue; int online = sp.getPlayers().getOnline(); if(selected == null) { selected = si; selectednum = online; continue; } if(selectednum > online && findServer(si.getName()).isJoinable(p)) { selected = si; selectednum = online; continue; } } } if(selected == null && serverInfos.size() > 0) { selected = serverInfos.keySet().iterator().next(); } if(selected == null) { pl.getLogger().warning("Unable to find ideal server, using random server from group."); int r = GenUtils.randomInt(0, s.getInfos().size()-1); selected = s.getInfos().get(r); } return selected; } /** * Attempts to send the first player in all queues */ public void sendPlayers() { sendPlayers(null); } HashMap sendingNowAntiSpam = new HashMap<>(); HashMap sendingAttempts = new HashMap<>(); /** * Attempts to send the first player in this queue * @param server The server to send the first player in the queue. null for all servers. */ public void sendPlayers(String server) { for(QueueServer s : servers) { String name = s.getName(); if(server != null && !server.equals(name)) continue; if(!s.isOnline()) continue; if(s.getQueue().size() <= 0) continue; if(pl.config.getBoolean("send-all-when-back-online") && s.justWentOnline() && s.isOnline()) { for(ProxiedPlayer p : s.getQueue()) { if(s.isFull() && !p.hasPermission("ajqueue.joinfull")) continue; ServerInfo selected = getIdealServer(s, p); if(selected == null) { pl.getLogger().severe("Could not find ideal server for server/group '"+s.getName()+"'!"); continue; } p.sendMessage(msgs.getBC("status.sending-now", "SERVER:"+pl.aliases.getAlias(name))); p.connect(selected); } return; } ProxiedPlayer nextplayer = s.getQueue().get(0); if(nextplayer == null) { if(s.getQueue().size() > 0) { s.getQueue().remove(0); } continue; } if(!s.canAccess(nextplayer)) continue; /*if(nextplayer == null) { pl.getLogger().info("nextplayer is null"); } if(nextplayer.getServer() == null) { pl.getLogger().info("getServer is null"); } if(nextplayer.getServer().getInfo() == null) { pl.getLogger().info("getInfo is null"); } if(nextplayer.getServer().getInfo().getName() == null) { pl.getLogger().info("getName is null"); } if(s == null) { pl.getLogger().info("s is null"); } if(s.getName() == null) { pl.getLogger().info("s.getName() is null"); }*/ if(s.getQueue().size() <= 0) continue; while(!nextplayer.isConnected()) { s.getQueue().remove(nextplayer); if(s.getQueue().size() <= 0) break; nextplayer = s.getQueue().get(0); } while(nextplayer.getServer().getInfo().getName().equals(s.getName())) { s.getQueue().remove(nextplayer); if(s.getQueue().size() <= 0) break; nextplayer = s.getQueue().get(0); } if(s.getQueue().size() <= 0) continue; if(s.isFull() && !nextplayer.hasPermission("ajqueue.joinfull")) continue; if(pl.config.getBoolean("enable-bypasspaused-permission")) { if(s.isPaused() && !nextplayer.hasPermission("ajqueue.bypasspaused")) continue; } else if(s.isPaused()) { continue; } int tries = sendingAttempts.get(nextplayer) == null ? 0 : sendingAttempts.get(nextplayer); int maxTries = pl.config.getInt("max-tries"); if(tries >= maxTries && maxTries > 0) { s.getQueue().remove(nextplayer); sendingAttempts.remove(nextplayer); nextplayer.sendMessage(msgs.getBC("max-tries-reached", "SERVER:"+pl.aliases.getAlias(s.getName()))); continue; } tries++; sendingAttempts.put(nextplayer, tries); if(!sendingNowAntiSpam.containsKey(nextplayer)) { sendingNowAntiSpam.put(nextplayer, (long) 0); } if(System.currentTimeMillis() - sendingNowAntiSpam.get(nextplayer) >= 5000) { nextplayer.sendMessage(msgs.getBC("status.sending-now", "SERVER:"+pl.aliases.getAlias(s.getName()))); sendingNowAntiSpam.put(nextplayer, System.currentTimeMillis()); } ServerInfo selected = getIdealServer(s, nextplayer); if(selected == null) { pl.getLogger().severe("Could not find ideal server for server/group '"+s.getName()+"'!"); continue; } nextplayer.connect(selected); } } /** * Add a player to the queue for a server * @param p The player * @param s The name of the server */ public void addToQueue(ProxiedPlayer p, String s) { if(p == null || s == null) return; QueueServer server = findServer(s); if(server == null) { p.sendMessage(msgs.getBC("errors.server-not-exist")); return; } if(!p.isConnected()) return; if(pl.config.getBoolean("joinfrom-server-permission") && !p.hasPermission("ajqueue.joinfrom."+p.getServer().getInfo().getName())) { p.sendMessage(msgs.getBC("errors.deny-joining-from-server")); return; } if(server.isPaused() && pl.config.getBoolean("prevent-joining-paused")) { p.sendMessage(msgs.getBC("errors.cant-join-paused", "SERVER:"+pl.aliases.getAlias(server.getName()))); return; } if(p.getServer().getInfo().getName().equals(s)) { p.sendMessage(msgs.getBC("errors.already-connected", "SERVER:"+pl.aliases.getAlias(server.getName()))); return; } List beforeQueues = findPlayerInQueue(p); if(beforeQueues.size() > 0) { if(beforeQueues.contains(server)) { p.sendMessage(msgs.getBC("errors.already-queued")); return; } if(!pl.config.getBoolean("allow-multiple-queues")) { p.sendMessage(msgs.getBC("status.left-last-queue", "SERVER:"+pl.aliases.getAlias(server.getName()))); for(QueueServer ser : beforeQueues) { ser.getQueue().remove(p); } } } List list = server.getQueue(); if(list.indexOf(p) != -1) { int pos = list.indexOf(p)+1; int len = list.size(); p.sendMessage(Main.formatMessage( msgs.get("errors.already-queued") .replaceAll("\\{POS\\}", pos+"") .replaceAll("\\{LEN\\}", len+"") )); return; } if(pl.isp) { Logic.priorityLogic(server.getQueue(), s, p); } else { if((p.hasPermission("ajqueue.priority") || p.hasPermission("ajqueue.serverpriority."+s)) && list.size() > 0) { //p.sendMessage(Main.formatMessage("in priority")); int i = 0; for(ProxiedPlayer ply : list) { if(!(ply.hasPermission("ajqueue.priority") || ply.hasPermission("ajqueue.serverpriority."+s))) { //p.sendMessage(Main.formatMessage("Adding beind: "+i)); list.add(i, p); break; } i++; } //p.sendMessage(Main.formatMessage("after loop")); if(list.size() == 0) { list.add(p); } else if(!list.contains(p)) { list.add(p); } } else { //p.sendMessage(Main.formatMessage("normal add")); list.add(p); } } int pos = list.indexOf(p)+1; int len = list.size(); boolean sendInstant = pl.config.getStringList("send-instantly").indexOf(server.getName()) != -1 || server.isJoinable(p); boolean sendInstantp = list.size() <= 1 && server.canAccess(p); boolean timeGood = pl.config.getBoolean("check-last-player-sent-time") ? System.currentTimeMillis() - server.getLastSentTime() > Math.floor(pl.getConfig().getDouble("wait-time")*1000) : true; if((sendInstant && (sendInstantp && timeGood))) { sendPlayers(s); //p.sendMessage(Main.formatMessage("sending instant")); BaseComponent[] m = msgs.getBC("status.now-in-empty-queue", "POS:"+pos, "LEN:"+len, "SERVER:"+pl.aliases.getAlias(s)); if(TextComponent.toPlainText(m).length() > 0) { p.sendMessage(m); } } else { //p.sendMessage(Main.formatMessage("now in queue, not send instant")); p.sendMessage(Main.formatMessage( msgs.get("status.now-in-queue") .replaceAll("\\{POS\\}", pos+"") .replaceAll("\\{LEN\\}", len+"") .replaceAll("\\{SERVER\\}", pl.aliases.getAlias(s)) )); } //p.sendMessage(Main.formatMessage(sendInstant+" && ("+sendInstantp+" && " + timeGood+")")); BungeeUtils.sendCustomData(p, "position", pos+""); BungeeUtils.sendCustomData(p, "positionof", len+""); BungeeUtils.sendCustomData(p, "queuename", pl.aliases.getAlias(s)); BungeeUtils.sendCustomData(p, "inqueue", "true"); BungeeUtils.sendCustomData(p, "inqueueevent", "true"); } /** * Finds which servers the player is queued for * @param p The player to search for * @return The servers the player is queued for. */ public List findPlayerInQueue(ProxiedPlayer p) { List srs = new ArrayList<>(); for(QueueServer s : servers) { if(s.getQueue().contains(p)) { srs.add(s); } } return srs; } public QueueServer getServer(String name) { return findServer(name); } }