From 66c9fefc6e8a3ea8eb541d9056422866928e9e5f Mon Sep 17 00:00:00 2001 From: Ty Sayers Date: Tue, 5 May 2015 15:34:32 -0400 Subject: [PATCH] Add leftover Clans commit to introduce GearManager. --- .../src/mineplex/game/clans/Clans.java | 5 +- .../src/mineplex/queuer/Queuer.java | 514 +++++++++++------- .../mineplex/servermonitor/ServerMonitor.java | 74 +-- 3 files changed, 344 insertions(+), 249 deletions(-) diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/Clans.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/Clans.java index 6c61713e8..25e4af9a7 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/Clans.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/Clans.java @@ -1,8 +1,8 @@ package mineplex.game.clans; import org.bukkit.plugin.java.JavaPlugin; -import net.minecraft.server.v1_7_R4.MinecraftServer; +import net.minecraft.server.v1_7_R4.MinecraftServer; import mineplex.core.account.CoreClientManager; import mineplex.core.antihack.AntiHack; import mineplex.core.blockrestore.BlockRestore; @@ -27,6 +27,7 @@ import mineplex.core.teleport.Teleport; import mineplex.core.updater.FileUpdater; import mineplex.core.updater.Updater; import mineplex.game.clans.clans.ClansManager; +import mineplex.game.clans.items.GearManager; import mineplex.game.clans.shop.building.BuildingShop; import mineplex.game.clans.shop.pvp.PvpShop; @@ -91,6 +92,8 @@ public class Clans extends JavaPlugin new BuildingShop(clans, _clientManager, _donationManager); new PvpShop(clans, _clientManager, _donationManager); + GearManager customGear = new GearManager(this); + //Updates getServer().getScheduler().scheduleSyncRepeatingTask(this, new Updater(this), 1, 1); diff --git a/Plugins/Mineplex.Queuer/src/mineplex/queuer/Queuer.java b/Plugins/Mineplex.Queuer/src/mineplex/queuer/Queuer.java index 331d025a2..09fee8b15 100644 --- a/Plugins/Mineplex.Queuer/src/mineplex/queuer/Queuer.java +++ b/Plugins/Mineplex.Queuer/src/mineplex/queuer/Queuer.java @@ -9,208 +9,55 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Random; import java.util.Set; +import java.util.UUID; import mineplex.serverdata.Region; +import mineplex.serverdata.commands.ServerTransfer; +import mineplex.serverdata.commands.TransferCommand; +import mineplex.serverdata.data.DedicatedServer; +import mineplex.serverdata.data.ServerGroup; +import mineplex.serverdata.servers.ConnectionData; +import mineplex.serverdata.servers.ServerManager; +import mineplex.serverdata.servers.ServerRepository; +import mineplex.serverprocesses.GenericRunnable; +import mineplex.serverprocesses.ProcessManager; +import mineplex.serverprocesses.ProcessRunner; public class Queuer { + public static final int MIN_QUEUE_WAIT = 0; // The number of seconds required in queue before creating a new match for a party. + private static QueueRepository _repo; + private static ServerRepository _serverRepository; + private static Set _pendingMatches; // Set of all matches awaiting players + private static int _matchId = 0; + private static int _matchesMade = 0; + private static int _updates = 0; + private static int _partyId = 0; + private static int _serverId = 0; public static void main (String args[]) { Region region = (!new File("eu.dat").exists()) ? Region.US : Region.EU; - + _serverRepository = ServerManager.getServerRepository(region); _repo = new QueueRepository(region); - - HashMap playerVarianceMap = new HashMap(); - HashMap playerPrepMatchMap = new HashMap(); - Set matches = new HashSet(); - - QueuePartySorter partySorter = new QueuePartySorter(); - - int matchId = 1; + _pendingMatches = new HashSet(); while (true) - { - int matchesMade = 0; - matchId %= 1500; + { + _updates++; + updateQueuer(); + ProcessManager.getInstance().updateProcesses(); - List assignedMatchIdChecked = new ArrayList(); - Map queueParties = _repo.getMappedQueueParties(); - - int matchPlayerCount = 2; - - System.out.println("Checking " + queueParties.size() + " queues..."); - for (QueueParty queueParty : queueParties.values()) - { - int partyId = queueParty.getId(); - int variance = playerVarianceMap.containsKey(partyId) ? playerVarianceMap.get(partyId) : 0; - variance += 25; - playerVarianceMap.put(partyId, variance); - - if (queueParty.hasAssignedMatch()) - { - for (Match match : matches) - { - if (Math.abs(match.getAverageElo() - queueParty.getAverageElo()) <= variance) - { - if (playerPrepMatchMap.containsKey(partyId)) - { - if (playerPrepMatchMap.get(partyId) == match) - break; - - playerPrepMatchMap.get(partyId).quitQueueParty(queueParty); - } - - match.joinQueueParty(queueParty); - playerPrepMatchMap.put(partyId, match); - - log("Found prep match for '" + queueParty.getId() + "'"); - break; - } - } - - if (!playerPrepMatchMap.containsKey(partyId)) - { - Match match = new Match(matchId++, queueParty.getAverageElo(), queueParty); - - playerPrepMatchMap.put(partyId, match); - matches.add(match); - } - } - else if (!assignedMatchIdChecked.contains(queueParty.getAssignedMatch())) - { - int assignedMatchId = queueParty.getAssignedMatch(); - log("Checking if match '" + assignedMatchId + "' is ready."); - //List matchStatuses = _repo.getMatchStatuses(queueRecord.AssignedMatch); - Collection joinedParties = _repo.getJoinedQueueParties(assignedMatchId); - boolean matchReady = true; - boolean matchDeny = false; - - for (QueueParty joinedParty : joinedParties) - { - String partyState = joinedParty.getState(); - if (partyState.equalsIgnoreCase("Deny")) - { - matchDeny = true; - matchReady = false; - break; - } - else if (!partyState.equalsIgnoreCase("Ready")) - { - matchReady = false; - } - } - - if (matchReady) - { - _repo.startMatch(assignedMatchId); - _repo.deleteAssignedParties(assignedMatchId); - - System.out.println("Starting match '" + assignedMatchId + "'"); - } - else if (matchDeny) - { - _repo.deleteMatch(assignedMatchId); - } - - assignedMatchIdChecked.add(assignedMatchId); - } - } - - System.out.println("Checking " + matches.size() + " matches..."); - - // Check for and kick off invites for ready matches - for (Iterator matchIterator = matches.iterator(); matchIterator.hasNext();) - { - Match match = matchIterator.next(); - - // Don't give me crap about not using iterator...can't cuz of stupid thing. - Set partiesToRemove = new HashSet(); - - for (QueueParty queueParty : match.getParties()) - { - if (!queueParties.containsKey(queueParty.getId())) - { - log("Removing matchStatus : " + queueParty.getId()); - partiesToRemove.add(queueParty); - - if (match.isWaitingForInvites()) - { - _repo.deleteMatch(match.getId()); - match.setWaitingForInvites(false); - } - } - } - - for (QueueParty party : partiesToRemove) - { - match.quitQueueParty(party); - } - - if (match.isWaitingForInvites()) - { - if ((match.getWaitDuration()) > 15000) - { - for (QueueParty queueParty : match.getParties()) - { - if (!queueParty.getState().equalsIgnoreCase("Ready")) - { - _repo.deleteQueueParty(queueParty.getId()); - } - } - - _repo.deleteMatch(match.getId()); - match.setWaitingForInvites(false); - } - - continue; - } - - if (match.getPlayerCount() >= matchPlayerCount) - { - List partyList = new ArrayList(match.getParties()); - Collections.sort(partyList, partySorter); - - int playerCount = 0; - for (QueueParty party : partyList) - { - if (playerCount + party.getPlayerCount() > matchPlayerCount) - { - match.quitQueueParty(party); - playerPrepMatchMap.remove(party.getId()); - log("Oops hit player cap, can't fit you in this match."); - continue; - } - - playerCount += party.getPlayerCount(); - } - - if (playerCount == matchPlayerCount) - { - log("Sent match invites for '" + match.getId() + "'"); - - for (QueueParty party : match.getParties()) - { - playerPrepMatchMap.remove(party.getId()); - _repo.assignMatch(party, match); - } - - match.setWaitingForInvites(true); - matchesMade++; - } - } - else if (match.getPlayerCount() == 0) - { - matchIterator.remove(); - } - } + log("Total pending matches after update: " + _pendingMatches.size()); + log("Total queued parties after update: " + _repo.getQueueParties().size()); try { - if (matchesMade > 0) - System.out.println("Made " + matchesMade + " matches."); + if (_matchesMade > 0) + System.out.println("Made " + _matchesMade + " matches."); Thread.sleep(1000); } @@ -222,6 +69,305 @@ public class Queuer } + + /** + * Tick & update the Queuer as a whole, making one whole pass through all queued players and pending matches to + * assign matches to parties and start matches. + */ + private static void updateQueuer() + { + // Update the status of each queue party, searching for best matchings and assigning matches to parties. + for (QueueParty queueParty : _repo.getQueueParties()) + { + updateParty(queueParty); + } + + // Update all matches, and remove pending matches if they are finished. + Iterator iterator = _pendingMatches.iterator(); + while (iterator.hasNext()) + { + Match match = iterator.next(); + boolean matchFinished = updateMatch(match); + + // Remove match if it is completed/finished + if (matchFinished) iterator.remove(); + } + } + + /** + * Update the status of a {@link QueueParty} by attempting to locate the best resulting + * {@code Match} available, or creating a new one if required. + * @param queueParty - the queue party to be updated for matchmaking purposes. + */ + private static void updateParty(QueueParty queueParty) + { + int queueDuration = (int) queueParty.getQueueDuration() / 1000; // Queue duration in seconds + Match bestMatch = getBestMatch(queueParty); + + if (queueParty.hasAssignedMatch()) + { + // TODO: If player has been waiting too long in current game and there is a better match, join that! + } + else + { + if (bestMatch != null) // Assign party into best match! + { + bestMatch.joinQueueParty(queueParty); + _repo.assignMatch(queueParty, bestMatch); + } + else if (queueDuration >= MIN_QUEUE_WAIT) // Create a new match for others to join! + { + Match match = new Match(_matchId++, queueParty.getServerGroup(), queueParty); + _pendingMatches.add(match); + _repo.assignMatch(queueParty, match); + } + } + } + + /** + * Update a {@link Match} by verifying it's player statuses, sending out invites + * and managing a Match from creation to deletion. + * @param match - the match to be updated. + * @return true, if the match is no longer required (successful or otherwise) and should be removed, false otherwise. + */ + private static boolean updateMatch(Match match) + { + // Remove queued parties that have left queue/match + // Don't give me crap about not using iterator...can't cuz of stupid thing. + Set partiesToRemove = new HashSet(); + for (QueueParty queueParty : match.getParties()) + { + int partyId = queueParty.getId(); + if (!_repo.queuePartyExists(partyId)) + { + log("Removing matchStatus : " + queueParty.getId()); + partiesToRemove.add(queueParty); + + if (match.isWaitingForInvites()) + { + _repo.deleteMatch(match.getId()); + match.setWaitingForInvites(false); + } + } + } + for (QueueParty party : partiesToRemove) + { + match.quitQueueParty(party); + } + + // If match took too long to find players, or is empty, quit match. + if (match.getPlayerCount() == 0) + { + return true; // Match is empty, remove from pending matches. + } + + // If match sent invites and is waiting for too long (15 seconds), kick players who didn't + // accept and keep looking + // Otherwise if everyone accepted, start game! + if (match.isWaitingForInvites()) + { + boolean matchReady = true; + for (QueueParty party : _repo.getJoinedQueueParties(match.getId())) + { + if (!party.isReady()) + { + matchReady = false; + } + } + + if (!matchReady && match.getWaitDuration() > 500) + { + matchReady = true; + } + + if (match.isReady()) // Invites accepted, MinecraftServer started, and players transferred. + { + return true; + } + else if (matchReady) // Players accepted invites, start match! + { + startMatch(match); + return false; + } + else if (match.getWaitDuration() > 15000) + { + for (QueueParty queueParty : match.getParties()) + { + if (!queueParty.isReady()) + { + _repo.deleteQueueParty(queueParty.getId()); + } + } + + _repo.deleteMatch(match.getId()); + match.setWaitingForInvites(false); + } + + return false; + } + + // Match has filled up, send out invites! + if (match.getOpenSlots() == 0) + { + for (QueueParty party : match.getParties()) + { + _repo.sendInvite(party); + } + + match.setWaitingForInvites(true); + } + + return false; + } + + /** + * @param queueParty - the party for whom a match is being searched. + * @return the best matching {@link Match} for the {@code queueParty}, if an acceptable {@link Match} + * could be found, null otherwise. + */ + private static Match getBestMatch(QueueParty queueParty) + { + Match best = null; + int minEloDelta = 0; + int variance = getSearchVariance(queueParty); + + for (Match match : _pendingMatches) + { + if (match.getOpenSlots() >= queueParty.getPlayerCount()) + { + int eloDelta = getEloDelta(queueParty, match); + if (eloDelta <= variance && (eloDelta < minEloDelta || best == null)) + { + best = match; + minEloDelta = eloDelta; + } + } + } + + return best; + } + + /** + * @param r1 + * @param r2 + * @return the ELO point delta (difference) between two {@link Ranked} objects. + */ + private static int getEloDelta(Ranked r1, Ranked r2) + { + return Math.abs(r1.getElo() - r2.getElo()); + } + + public static boolean startMatch(Match match) + { + ServerGroup group = match.getServerGroup(); + DedicatedServer bestServer = getBestDedicatedServer(group); + startServer(bestServer, group, match); + return true; + } + + /** + * Transfer all players queue'd into a {@link Match} to one server. + * @param match - the match whose queue'd players are to be transferred + * @param serverName - the name of the server to transfer the players to + */ + public static void transferPlayers(Match match, String serverName) + { + // Transfer players to the server + for (QueueParty queueParty : _repo.getJoinedQueueParties(match.getId())) + { + for (String playerName : queueParty.getPlayers()) + { + // Execute a transfer command + ServerTransfer serverTransfer = new ServerTransfer(playerName, serverName); + TransferCommand transferCommand = new TransferCommand(serverTransfer); + transferCommand.publish(); + } + } + + // Server transfers sent out, match has started! + _matchesMade++; + + // Delete queue parties for players who've been matched + for (QueueParty queueParty : match.getParties()) + { + if (!queueParty.isReady()) + { + _repo.deleteQueueParty(queueParty.getId()); + } + } + match.setReady(true); // Ready to be deleted/removed + } + + /** + * @return newly generated unique server id. + */ + public static int generateServerId() + { + return _serverId++; + } + + private static void startServer(final DedicatedServer serverSpace, final ServerGroup serverGroup, final Match match) + { + String cmd = "/home/mineplex/easyRemoteStartServerCustom.sh"; + final String groupPrefix = serverGroup.getPrefix(); + final String serverName = serverSpace.getName(); + final String serverAddress = serverSpace.getPublicAddress(); + final int serverId = generateServerId(); + + ProcessRunner pr = new ProcessRunner(new String[] {"/bin/sh", cmd, serverAddress, serverSpace.getPrivateAddress(), (serverGroup.getPortSection() + serverId) + "", serverGroup.getRequiredRam() + "", serverGroup.getWorldZip(), serverGroup.getPlugin(), serverGroup.getConfigPath(), serverGroup.getName(), serverGroup.getPrefix() + "-" + serverId, serverSpace.isUsRegion() ? "true" : "false", serverGroup.getAddNoCheat() + "" }); + pr.start(new GenericRunnable() + { + public void run(Boolean error) + { + if (!error) + { + // Successfully started server, now transfer players + transferPlayers(match, serverName); + } + else + { + // TODO: Error in starting server for ELO match, try again or disband queued match? + log("[" + serverName + ":" + serverAddress + " Free Resources; CPU " + serverSpace.getAvailableCpu() + " RAM " + serverSpace.getAvailableRam() + "MB] Errored " + serverName + "(" + groupPrefix+ "-" + serverId + ")"); + } + + } + }); + + ProcessManager.getInstance().addProcess(pr); + serverSpace.incrementServerCount(serverGroup); + } + + private static DedicatedServer getBestDedicatedServer(ServerGroup serverGroup) + { + Collection dedicatedServers = _serverRepository.getDedicatedServers(); + DedicatedServer bestServer = null; + + for (DedicatedServer serverData : dedicatedServers) + { + if (serverData.getAvailableRam() > serverGroup.getRequiredRam() + && serverData.getAvailableCpu() > serverGroup.getRequiredCpu()) + { + if (bestServer == null || serverData.getServerCount(serverGroup) < bestServer.getServerCount(serverGroup)) + { + bestServer = serverData; + } + } + } + + return bestServer; + } + + /** + * @param party - the party whose ELO search variance is being fetched. + * @return the variance in ELO search parameters for {@code party}. + * I.E: Queuer searches for potential matches in [party.elo - variance, party.elo + variance] ELO range. + */ + private static int getSearchVariance(QueueParty party) + { + int seconds = (int) party.getQueueDuration() / 1000; // Duration of queue in seconds + + return seconds * 10; // 5 ELO variance for every second in queue + } private static void log(String message) { diff --git a/Plugins/Mineplex.ServerMonitor/src/mineplex/servermonitor/ServerMonitor.java b/Plugins/Mineplex.ServerMonitor/src/mineplex/servermonitor/ServerMonitor.java index 441cfe60b..e7d4c3fdf 100644 --- a/Plugins/Mineplex.ServerMonitor/src/mineplex/servermonitor/ServerMonitor.java +++ b/Plugins/Mineplex.ServerMonitor/src/mineplex/servermonitor/ServerMonitor.java @@ -29,13 +29,15 @@ import mineplex.serverdata.data.ServerGroup; import mineplex.serverdata.servers.DedicatedServerSorter; import mineplex.serverdata.servers.ServerManager; import mineplex.serverdata.servers.ServerRepository; +import mineplex.serverprocesses.GenericRunnable; +import mineplex.serverprocesses.ProcessManager; +import mineplex.serverprocesses.ProcessRunner; public class ServerMonitor { private static ServerRepository _repository = null; private static StatusHistoryRepository _historyRepository = null; private static int _count = 0; - private static HashSet _processes = new HashSet(); private static HashMap _badServers = new HashMap(); private static Collection _serverStatuses = null; private static Collection _serverGroups = null; @@ -54,18 +56,6 @@ public class ServerMonitor public static void main (String args[]) { - /* - MinecraftPingReply data = null; - try - { - data = new MinecraftPing().getPing(new MinecraftPingOptions().setHostname("127.0.0.1").setPort(25565)); - } - catch (IOException e2) - { - e2.printStackTrace(); - } - System.out.println(data.getDescription() + " " + data.getPlayers().getOnline()); - */ _region = !new File("eu.dat").exists() ? Region.US : Region.EU; _debug = new File("debug.dat").exists(); _repository = ServerManager.getServerRepository(_region); // Fetches and connects to server repo @@ -283,55 +273,7 @@ public class ServerMonitor } } - int processWaits = 0; - - while (_processes.size() > 0) - { - for (Iterator iterator = _processes.iterator(); iterator.hasNext();) - { - ProcessRunner pr = iterator.next(); - - try - { - pr.join(100); - } - catch (InterruptedException e) - { - e.printStackTrace(); - } - - if (pr.isDone()) - iterator.remove(); - } - - if (_processes.size() > 0) - { - try - { - log("Sleeping while processes run..."); - Thread.sleep(6000); - } - catch (InterruptedException e) - { - e.printStackTrace(); - } - } - - if (processWaits >= 10) - { - log("Killing stale processes."); - - for (Iterator iterator = _processes.iterator(); iterator.hasNext();) - { - iterator.next().abort(); - iterator.remove(); - } - } - - processWaits++; - } - - processWaits = 0; + ProcessManager.getInstance().updateProcesses(); try { @@ -565,7 +507,9 @@ public class ServerMonitor if (!pr.isDone()) - _processes.add(pr); + { + ProcessManager.getInstance().addProcess(pr); + } } private static boolean isServerOffline(DedicatedServer serverData) @@ -666,7 +610,9 @@ public class ServerMonitor serverSpace.incrementServerCount(serverGroup); if (!pr.isDone()) - _processes.add(pr); + { + ProcessManager.getInstance().addProcess(pr); + } } private static void log(String message)