BungeeMain / src / main / java / com / lifeknight / relaymcbungeemain / queue / Queue.java
Queue.java
Raw
package com.lifeknight.relaymcbungeemain.queue;

import com.google.gson.JsonArray;
import com.lifeknight.relaymcbungeemain.Main;
import com.lifeknight.relaymcbungeemain.player.Party;
import com.lifeknight.relaymcbungeemain.player.SmartPlayer;
import com.lifeknight.relaymcbungeemain.utilities.MessageUtils;
import com.lifeknight.relaymcbungeemain.utilities.Utilities;
import com.lifeknight.relayutils.RelayUtils;
import com.lifeknight.relayutils.basic.Miscellaneous;
import com.lifeknight.relayutils.basic.Text;
import com.lifeknight.relayutils.data.QueueData;
import com.lifeknight.relayutils.game.GameDetails;
import com.lifeknight.relayutils.game.GameType;
import com.lifeknight.relayutils.game.MainGame;
import com.lifeknight.relayutils.network.Command;
import com.lifeknight.relayutils.player.Group;
import com.lifeknight.relayutils.utilities.ComponentBuilder;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;

import java.util.*;

public class Queue {

    public final static Map<MainGame, Queue> QUEUES = new HashMap<>();

    static {
        for (MainGame gameTypeMainName : MainGame.values()) {
            QUEUES.put(gameTypeMainName, new Queue());
        }
    }

    private final PlayerGroupList remainingPlayersToJoin;

    public Queue() {
        this.remainingPlayersToJoin = new PlayerGroupList();
    }

    public static void onPlayerLeave(SmartPlayer smartPlayer) {
        for (Queue queue : QUEUES.values()) {
            GameType wasInGameType;
            if ((wasInGameType = queue.remainingPlayersToJoin.removePlayerInadvertently(smartPlayer)) != null) {
                smartPlayer.queueInfoMessageForJoin("Left %s queue.", wasInGameType.getPrefix());
                smartPlayer.emptyQueue();
                check();
                break;
            }
        }
    }

    public static synchronized int getQueueSize(GameType gameType) {
        Queue queue = QUEUES.get(gameType.getMainGame());
        return queue.getSize(gameType);
    }

    public static synchronized int getQueuePosition(SmartPlayer smartPlayer) {
        for (Queue value : QUEUES.values()) {
            if (value.remainingPlayersToJoin.hasPlayer(smartPlayer)) {
                return value.remainingPlayersToJoin.getPosition(smartPlayer);
            }
        }

        return -1;
    }

    public static synchronized int getQueueSize(SmartPlayer smartPlayer) {
        for (Queue value : QUEUES.values()) {
            if (value.remainingPlayersToJoin.hasPlayer(smartPlayer)) {
                return value.remainingPlayersToJoin.getQueueSize(smartPlayer);
            }
        }

        return -1;
    }

    public static void onGameDetailsUpdate() {
        Main.schedule(Queue::check, 2);
    }

    public static void check() {
        Main.synchronous(() -> {
            for (Queue queue : QUEUES.values()) {
                queue.onQueueChange();
                queue.checkForNextGame(null, null, new ArrayList<>());
            }
        });
    }

    public static void removePlayerInadvertently(SmartPlayer smartPlayer, Queue queueing) {
        for (Queue value : QUEUES.values()) {
            if (value != queueing) value.cancelPlayerInadvertently(smartPlayer);
        }
    }

    public static void removePartyInadvertently(SmartPlayer smartPlayer, Party party, Queue queuing) {
        for (Queue value : QUEUES.values()) {
            if (value != queuing) value.cancelPartyInadvertently(smartPlayer, party);
        }
    }

    public static void removePlayersInadvertently(List<SmartPlayer> smartPlayers, Queue queue) {
        for (Queue value : QUEUES.values()) {
            if (queue != value) {
                value.cancelPlayersInadvertently(smartPlayers);
            }
        }
    }

    public static void addPartyToQueue(GameType gameType, QueueData queueData, SmartPlayer smartPlayer, Party party) {
        Main.synchronous(() -> QUEUES.get(gameType.getMainGame()).addParty(smartPlayer, party, gameType, queueData));
    }

    public static void addPlayerToQueue(GameType gameType, QueueData queueData, SmartPlayer smartPlayer) {
        Main.synchronous(() -> QUEUES.get(gameType.getMainGame()).addPlayer(smartPlayer, gameType, queueData));
    }

    public static void removePartyFromQueue(GameType gameType, SmartPlayer smartPlayer, Party party) {
        Main.synchronous(() -> QUEUES.get(gameType.getMainGame()).cancelParty(smartPlayer, party, gameType));
    }

    public static void removePlayerFromQueue(GameType gameType, SmartPlayer smartPlayer) {
        Main.synchronous(() -> QUEUES.get(gameType.getMainGame()).cancelPlayer(smartPlayer, gameType));
    }

    public static synchronized boolean isInFastQueue(SmartPlayer smartPlayer) {
        for (Queue value : QUEUES.values()) {
            if (value.remainingPlayersToJoin.hasPlayer(smartPlayer)) {
                return value.remainingPlayersToJoin.isFastQueue(smartPlayer);
            }
        }

        return false;
    }

    public static void rematch(SmartPlayer smartPlayer, SmartPlayer opponent, GameType rematchGameType, QueueData rematchQueueData) {
        QUEUES.get(rematchGameType.getMainGame()).addPlayers(Miscellaneous.getList(smartPlayer, opponent), rematchGameType, rematchQueueData);
    }

    public static List<QueueData> getQueuedGames() {
        List<QueueData> data = new ArrayList<>();

        for (Queue value : QUEUES.values()) {
            PlayerGroupList playerGroupList = value.remainingPlayersToJoin;
            List<PlayerGroup> groups = Miscellaneous.with(playerGroupList.fastPlayerGroups, playerGroupList.normalPlayerGroups);


            for (PlayerGroup group : groups) {
                group.queueData.attachData("gameType", group.gameType);
                group.queueData.attachData("queued", Text.toCSV(Miscellaneous.processList(group.players, SmartPlayer::getUUID)));
                data.add(group.queueData);
            }
        }

        return data;
    }

    public void addPlayers(List<SmartPlayer> smartPlayers, GameType gameType, QueueData queueData) {
        removePlayersInadvertently(smartPlayers, this);
        int max = queueData.isParty() ? 64 : (gameType.isTeamGame() ? gameType.getTeamSize() : gameType.getTotalPlayerCount());
        if (smartPlayers.size() > max && !(queueData.hasData("owner") || queueData.hasData("with"))) {
            smartPlayers.get(0).sendErrorMessage("Your party size is too large! You can play with a maximum of %d players.", max);
        } else if (this.remainingPlayersToJoin.addPlayers(smartPlayers, gameType, queueData)) {
            if (!this.checkForNextGame(gameType, queueData, smartPlayers)) {
                boolean hasChampion = !Miscellaneous.filter(smartPlayers, SmartPlayer::hasChampion).isEmpty();
                if (hasChampion) {
                    smartPlayers.forEach(smartPlayer -> smartPlayer.sendSuccessMessage("Joined %s queue. Use %s/cancel%s to leave.", gameType.getPrefix(), ChatColor.YELLOW, ChatColor.GREEN));
                } else {
                    smartPlayers.forEach(smartPlayer -> smartPlayer.sendSuccessMessage("Joined %s queue. Use %s/cancel%s to leave. To skip the queue, purchase %sPremium %srank using %s/premium!", gameType.getPrefix(), ChatColor.YELLOW, ChatColor.GREEN, Group.CHAMPION.getColor(), ChatColor.GREEN, ChatColor.AQUA));
                }
                smartPlayers.forEach(smartPlayer -> {
                    smartPlayer.setQueue(gameType);
                    smartPlayer.playSuccessSound();
                });

                if (queueData.hasData("/duel")) {
                    smartPlayers.forEach(smartPlayer -> smartPlayer.sendInfoMessage("No servers are currently available, so you have been placed into the queue."));
                }
            }
            this.onQueueChange();
        }
    }

    public int getSize(GameType gameType) {
        return this.remainingPlayersToJoin.getSize(gameType);
    }

    public static int[] getQueueInformation(SmartPlayer smartPlayer) {
        int queueSize = -1;
        int queuePosition = -1;
        int specificSize = -1;
        int specificPosition = -1;
        int typeSize = -1;
        boolean fastQueue = false;
        boolean serverAvailable = false;
        if (smartPlayer.isInQueue()) {
            Queue queue = QUEUES.get(smartPlayer.getCurrentQueue().getMainGame());
            if (queue.remainingPlayersToJoin.hasPlayer(smartPlayer)) {
                queuePosition = queue.remainingPlayersToJoin.getPosition(smartPlayer);
                queueSize = queue.remainingPlayersToJoin.getQueueSize(smartPlayer);
                specificPosition = queue.remainingPlayersToJoin.getSpecificPosition(smartPlayer);
                specificSize = queue.remainingPlayersToJoin.getSpecificSize(smartPlayer);
                typeSize = queue.remainingPlayersToJoin.getTypeSize(smartPlayer);
                fastQueue = queue.remainingPlayersToJoin.isFastQueue(smartPlayer);
                serverAvailable = queue.remainingPlayersToJoin.availableServer(smartPlayer);
            }
        }

        return new int[]{queueSize, queuePosition, specificSize, specificPosition, typeSize, fastQueue ? 1 : 0, serverAvailable ? 1 : 0};
    }

    public synchronized boolean checkForNextGame(GameType gameType, QueueData queueData, List<SmartPlayer> smartPlayers) {
        /*if (gameType != null && !gameType.isWaitForFullQueue()) {
            if (!smartPlayers.isEmpty()) {
                GameServer gameServer = Utilities.getBestServer(gameType, smartPlayers);
                if (gameServer == null) {
                    if (RelayUtils.log) {
                        Main.info("Was going to queue players, no server found.");
                    }
                    return false;
                }

                GameDetails gameDetails = gameServer.getAvailable(gameType, smartPlayers);
                if ()

                this.queuePlayers(gameServer, smartPlayers, gameType, queueData);
                return true;
            }
        }*/

        List<PlayerGroup> nextPlayers = this.remainingPlayersToJoin.nextPlayers();
        if (nextPlayers != null) {
            List<SmartPlayer> asSmartPlayers = new ArrayList<>();
            GameType theGameType = Miscellaneous.getFirstEntry(nextPlayers).gameType;
            QueueData theQueueData = new QueueData();

                for (PlayerGroup playerGroup : nextPlayers) {
                    asSmartPlayers.addAll(playerGroup.players);
                    if (playerGroup.queueData.isParty()) {
                        theQueueData.setParty();
                    }
                }

                for (PlayerGroup nextPlayer : nextPlayers) {
                    if (nextPlayer.queueData.hasData("other") && !nextPlayer.queueData.getBoolean("other")) {
                        theQueueData = nextPlayer.queueData.attachData("owner", nextPlayer.leader.getUUID());
                        break;
                    } else if (nextPlayer.queueData.hasData("owner")) {
                        theQueueData = nextPlayer.queueData;
                        break;
                    }
                }

                for (PlayerGroup nextPlayer : nextPlayers) {
                    if (nextPlayer.queueData.hasData("lobby")) {
                        theQueueData.attachData("lobby", nextPlayer.queueData.getData("lobby"));
                    }
                }

                if (nextPlayers.size() == 1 && !theQueueData.isParty()) {
                    theQueueData.attachBoolean("/duel", true);
                }


            GameServer gameServer = Utilities.getBestServer(theGameType, asSmartPlayers);
            if (gameServer == null) {
                if (RelayUtils.log) {
                    Main.info("Was going to queue players, no server found.");
                }
                return false;
            }

            GameDetails gameDetails = gameServer.getAvailable(gameType, asSmartPlayers);
            if (gameDetails != null && (Utilities.suitableQueueSize(theGameType, Miscellaneous.processList(nextPlayers, playerGroup -> playerGroup.queueData), gameDetails.getPlayerCount() + asSmartPlayers.size()) || gameDetails.getPlayerCount() > 0)) {
                this.syncQueue(gameServer, asSmartPlayers, theGameType, theQueueData);
                return new HashSet<>(asSmartPlayers).containsAll(smartPlayers);
            }
        }

        return false;
    }

    public void syncQueue(GameServer gameServer, List<SmartPlayer> smartPlayers, GameType gameType, QueueData queueData) {
        Main.synchronous(() -> this.queuePlayers(gameServer, smartPlayers, gameType, queueData));
    }

    public synchronized void queuePlayers(GameServer gameServer, List<SmartPlayer> smartPlayers, GameType gameType, QueueData queueData) {
        if (gameServer == null) return;

        List<String> uuids = new ArrayList<>();
        for (SmartPlayer smartPlayer : smartPlayers) {
            uuids.add(smartPlayer.getUUID().toString());
            if (smartPlayer.getCurrentQueue() == null) {
                Main.warn("Player is being queued despite not having current queue! %s", smartPlayer.getName());
            }
            if (!queueData.hasData("/duel")) {
                smartPlayer.emptyQueue();
            }
            this.remainingPlayersToJoin.removePlayer(smartPlayer, true);
        }

        GameDetails gameDetails = gameServer.getAvailable(gameType, smartPlayers);
        if (gameDetails == null) {
            Main.error("Game details for queing are null: %s | %s | %s", gameServer.getServer().getName(), gameType, queueData);
            return;
        }
        UUID queueUUID = gameDetails.getUUID();
        gameDetails.increasePlayerCount(smartPlayers.size());

        if (gameDetails.getPlayerCount() + smartPlayers.size() >= gameType.getTotalPlayerCount() || (gameType.isPartyType() && queueData.isParty())) {
            gameServer.markQueue(queueUUID);
        }

        if (RelayUtils.log) {
            Main.info("Queueing players to %s (%s | %s): %s | %s", gameServer.getServer().getName(), gameType.getFullPrettyName(), queueData, Text.toStringList(smartPlayers, SmartPlayer::getName), queueUUID);
        }

        List<Party> parties = new ArrayList<>();
        for (SmartPlayer smartPlayer : smartPlayers) {
            if (smartPlayer.isInParty()) {
                Miscellaneous.addIfAbsent(parties, smartPlayer.getCurrentParty());
            }
        }

        JsonArray partiesJson = new JsonArray();
        for (Party party1 : parties) {
            partiesJson.add(party1.getPartyData().getAsJson());
        }

        MessageUtils.createAndSend(gameServer.getRecipient(), Command.QUEUE, queueUUID, gameType.getCodeName(), queueData, partiesJson, Text.toCSV(uuids));
    }

    public void addPlayer(SmartPlayer smartPlayer, GameType gameType, QueueData queueData) {
        removePlayerInadvertently(smartPlayer, this);

        if (this.remainingPlayersToJoin.addPlayer(smartPlayer, gameType, queueData)) {
            if (!this.checkForNextGame(gameType, queueData, Miscellaneous.getList(smartPlayer))) {
                if (smartPlayer.isInGroupOrHigher(Group.CHAMPION)) {
                    smartPlayer.sendSuccessMessage("Joined %s queue. Use %s/cancel%s to leave.", gameType.getPrefix(), ChatColor.YELLOW, ChatColor.GREEN);
                } else {
                    smartPlayer.sendSuccessMessage("Joined %s queue. Use %s/cancel%s to leave. To skip the queue, purchase %sPremium %srank using %s/premium!", gameType.getPrefix(), ChatColor.YELLOW, ChatColor.GREEN, Group.CHAMPION.getColor(), ChatColor.GREEN, ChatColor.AQUA);
                }
                smartPlayer.setQueue(gameType);
                smartPlayer.playSuccessSound();
            }
            this.onQueueChange();
        } else {
            smartPlayer.sendErrorMessage("You are already in the %s queue!", gameType.getMainGameName());
        }
    }

    public void cancelPlayer(SmartPlayer smartPlayer, GameType game) {
        if (this.remainingPlayersToJoin.removePlayer(smartPlayer)) {
            this.onQueueChange();
            smartPlayer.sendInfoMessage("Left %s queue.", game.getPrefix());
            smartPlayer.emptyQueue();
        } else {
            smartPlayer.sendErrorMessage("You are not in the %s queue!", game.getPrefix());
        }
    }

    public void cancelPlayerInadvertently(SmartPlayer smartPlayer) {
        GameType wasInGameType;
        if ((wasInGameType = this.remainingPlayersToJoin.removePlayerInadvertently(smartPlayer)) != null) {
            this.onQueueChange();
            smartPlayer.sendInfoMessage("Left %s queue.", wasInGameType.getPrefix());
            smartPlayer.emptyQueue();
        }
    }

    public void cancelPlayerInadvertently(SmartPlayer smartPlayer, GameType gameType) {
        GameType wasInGameType;
        if ((wasInGameType = this.remainingPlayersToJoin.removePlayerInadvertently(smartPlayer)) != null) {
            if (wasInGameType != gameType) {
                this.onQueueChange();
                smartPlayer.sendInfoMessage("Left %s queue.", wasInGameType.getPrefix());
                smartPlayer.emptyQueue();
            }
        }
    }

    public void duelPlayers(GameType gameType, SmartPlayer caller, SmartPlayer otherPlayer) {
/*
        GameServer gameServer = Utilities.getBestServer(gameType, Miscellaneous.getList(caller, otherPlayer));
        if (gameServer != null) {
            this.syncQueue(gameServer, Miscellaneous.getList(caller, otherPlayer), gameType, new QueueData().attachBoolean("/duel", true).attachData("owner", caller.getUUID()));
            this.onQueueChange();
        } else {
            caller.sendErrorMessage("There are no servers for you to play on.");
            otherPlayer.sendErrorMessage("There are no servers for you to play on.");
        }
*/
        if (gameType == GameType.DUELS_CUSTOM) {
            addPlayers(Miscellaneous.getList(caller, otherPlayer), gameType, new QueueData().attachData("/duel", true).attachData("owner", caller.getUUID()));
        } else {
            addPlayers(Miscellaneous.getList(caller, otherPlayer), gameType, new QueueData().attachData("/duel", true));
        }
    }

    public void addParty(SmartPlayer smartPlayer, Party party, GameType gameType, QueueData queueData) {
        removePartyInadvertently(smartPlayer, party, this);
        int max = queueData.isParty() ? 64 : (gameType.isTeamGame() ? gameType.getTeamSize() : gameType.getTotalPlayerCount());
        if (party.getSize() > max && !(queueData.hasData("owner") || queueData.hasData("with"))) {
            smartPlayer.sendErrorMessage("Your party size is too large! You can play with a maximum of %d players.", max);
        } else if (this.remainingPlayersToJoin.addParty(party, gameType, queueData)) {
            if (!this.checkForNextGame(gameType, queueData, party.getPlayers())) {
                if (smartPlayer.isInGroupOrHigher(Group.CHAMPION)) {
                    smartPlayer.sendSuccessMessage("Joined %s queue. Use %s/cancel%s to leave.", gameType.getPrefix(), ChatColor.YELLOW, ChatColor.GREEN);
                } else {
                    smartPlayer.sendSuccessMessage("Joined %s queue. Use %s/cancel%s to leave. To skip the queue, purchase %sPremium %srank using %s/premium!", gameType.getPrefix(), ChatColor.YELLOW, ChatColor.GREEN, Group.CHAMPION.getColor(), ChatColor.GREEN, ChatColor.AQUA);
                }
                smartPlayer.setQueue(gameType);
                smartPlayer.playSuccessSound();
                for (SmartPlayer otherPlayer : party.getPlayers()) {
                    if (smartPlayer != otherPlayer) {
                        otherPlayer.sendPartySuccessMessage("%s%s joined the %s queue.", smartPlayer.getFormattedName(), ChatColor.GREEN, gameType.getPrefix());
                        otherPlayer.setQueue(gameType);
                        otherPlayer.playSuccessSound();
                    }
                }
            }
            this.onQueueChange();
        } else {
            smartPlayer.sendErrorMessage("At least one of your party members is already in the %s queue!", gameType.getPrefix());
        }
    }

    public void cancelParty(SmartPlayer smartPlayer, Party party, GameType gameType) {
        if (this.remainingPlayersToJoin.removeParty(party)) {
            smartPlayer.sendInfoMessage("Left %s queue.", gameType.getPrefix());
            smartPlayer.emptyQueue();
            for (SmartPlayer otherPlayer : party.getPlayers()) {
                if (smartPlayer != otherPlayer) {
                    otherPlayer.sendInfoMessage("%s%s left the %s queue.", smartPlayer.getFormattedName(), ChatColor.YELLOW, gameType.getPrefix());
                    otherPlayer.emptyQueue();
                }
            }
            this.onQueueChange();
        } else {
            smartPlayer.sendErrorMessage("None of your party members are in the %s queue!", gameType.getPrefix());
        }
    }

    public void cancelPartyInadvertently(SmartPlayer smartPlayer, Party party) {
        GameType wasInGameType;
        if ((wasInGameType = this.remainingPlayersToJoin.removePartyInadvertently(party)) != null) {
            smartPlayer.sendInfoMessage("Left %s queue.", wasInGameType.getPrefix());
            smartPlayer.emptyQueue();
            for (SmartPlayer otherPlayer : party.getPlayers()) {
                if (smartPlayer != otherPlayer) {
                    otherPlayer.sendInfoMessage("%s%s left the %s queue.", smartPlayer.getFormattedName(), ChatColor.YELLOW, wasInGameType.getPrefix());
                    otherPlayer.emptyQueue();
                }
            }
            this.onQueueChange();
        }
    }

    public void cancelPlayersInadvertently(List<SmartPlayer> smartPlayers) {
        for (SmartPlayer smartPlayer : smartPlayers) {
            cancelPlayerInadvertently(smartPlayer);
        }
    }

    public void cancelPartyInadvertently(SmartPlayer smartPlayer, Party party, GameType gameType) {
        GameType wasInGameType;
        if ((wasInGameType = this.remainingPlayersToJoin.removePartyInadvertently(party)) != null) {
            if (wasInGameType != gameType) {
                smartPlayer.sendInfoMessage("Left %s queue.", wasInGameType.getPrefix());
                smartPlayer.emptyQueue();
                smartPlayer.updateScoreboard();
                for (SmartPlayer otherPlayer : party.getPlayers()) {
                    if (smartPlayer != otherPlayer) {
                        otherPlayer.sendInfoMessage("%s%s left the %s queue.", smartPlayer.getFormattedName(), ChatColor.YELLOW, wasInGameType.getPrefix());
                        otherPlayer.emptyQueue();
                        otherPlayer.updateScoreboard();
                    }
                }
            }
            this.onQueueChange();
        }
    }

    public void removePlayer(SmartPlayer smartPlayer) {
        this.remainingPlayersToJoin.removePlayer(smartPlayer);
    }

    public void printQueue(CommandSender commandSender) {
        ComponentBuilder component = new ComponentBuilder("Queue").append("\n");
        component.append("----------------");
        commandSender.sendMessage(component.getResult());
        component = new ComponentBuilder();
        for (PlayerGroup playerGroup : this.remainingPlayersToJoin.allPlayerGroups) {
            List<String> playerNames = new ArrayList<>();
            for (SmartPlayer player : playerGroup.players) {
                playerNames.add(player.getName());
            }
            component.append("\n").append(playerGroup.getType().getFullPrettyName()).append(" - ").append(playerNames);
            commandSender.sendMessage(component.getResult());
            component = new ComponentBuilder();
        }
        component.append("\n").append("----------------");
        commandSender.sendMessage(component.getResult());
    }

    public synchronized void onQueueChange() {
        this.remainingPlayersToJoin.removeNullPlayerGroups();
        for (PlayerGroup allPlayerGroup : this.remainingPlayersToJoin.allPlayerGroups) {
            for (SmartPlayer player : allPlayerGroup.players) {
                player.updateScoreboard();
            }
        }
        Main.broadcastPublicQueues();
        Main.schedule(Main::broadcastPublicQueues, 2);
    }
}