diff --git a/src/de/steamwar/bungeecore/ArenaMode.java b/src/de/steamwar/bungeecore/ArenaMode.java index 6c27feb..3c895c9 100644 --- a/src/de/steamwar/bungeecore/ArenaMode.java +++ b/src/de/steamwar/bungeecore/ArenaMode.java @@ -26,10 +26,20 @@ public class ArenaMode { return byInternal.get(name); } - public static List getAllChatNames() { + public static List getAllChatNames(boolean historic) { List chatNames = new LinkedList<>(); for(ArenaMode mode : byInternal.values()){ - chatNames.addAll(mode.chatNames); + if(historic == mode.historic) + chatNames.addAll(mode.chatNames); + } + return chatNames; + } + + public static List getAllRankedChatNames(){ + List chatNames = new LinkedList<>(); + for(ArenaMode mode : byInternal.values()){ + if(mode.isRanked()) + chatNames.addAll(mode.chatNames); } return chatNames; } @@ -48,6 +58,8 @@ public class ArenaMode { private final String serverJar; private final List maps; private final boolean historic; + private final boolean ranked; + private final String schemType; private ArenaMode(String internalName, Configuration config){ this.internalName = internalName; @@ -55,7 +67,9 @@ public class ArenaMode { this.serverJar = config.getString("serverJar"); this.chatNames = config.getStringList("chatNames"); this.maps = config.getStringList("maps"); - this.historic = config.getBoolean("historic"); + this.historic = config.getBoolean("historic", false); + this.ranked = config.getBoolean("ranked", false); + this.schemType = config.getString("schemType", null); allModes.add(this); byInternal.put(internalName, this); @@ -106,4 +120,12 @@ public class ArenaMode { public boolean isHistoric(){ return historic; } + + public boolean isRanked() { + return ranked; + } + + public String getSchemType() { + return schemType; + } } diff --git a/src/de/steamwar/bungeecore/BungeeCore.java b/src/de/steamwar/bungeecore/BungeeCore.java index 85d43d7..d334823 100644 --- a/src/de/steamwar/bungeecore/BungeeCore.java +++ b/src/de/steamwar/bungeecore/BungeeCore.java @@ -95,6 +95,8 @@ public class BungeeCore extends Plugin { new RegelnCommand(); new HistoricCommand(); new CheckCommand(); + new RankedCommand(); + new RankCommand(); new EventStarter(); new Broadcaster(); diff --git a/src/de/steamwar/bungeecore/SubserverSystem.java b/src/de/steamwar/bungeecore/SubserverSystem.java index 6787ae2..b491417 100644 --- a/src/de/steamwar/bungeecore/SubserverSystem.java +++ b/src/de/steamwar/bungeecore/SubserverSystem.java @@ -62,10 +62,13 @@ public class SubserverSystem { * For event, test and normal arenas: The UUID of the designated leader for the red team * or null (no designated leader). * + * @param ranked + * If the game is a ranked game. + * * @return * The new started subserver. */ - public static Subserver startArena(ArenaMode modus, String map, int eventFightID, int checkSchemID, String serverName, String mapName, UUID player1, UUID player2){ + public static Subserver startArena(ArenaMode modus, String map, int eventFightID, int checkSchemID, String serverName, String mapName, UUID player1, UUID player2, boolean ranked){ //Generate missing parameters int port = freePort(2500); @@ -98,6 +101,8 @@ public class SubserverSystem { cmd.add("-DredLeader=" + player2.toString()); if(checkSchemID != 0) cmd.add("-DcheckSchemID=" + checkSchemID); + if(ranked) + cmd.add("-Dranked=true"); cmd.add("-Xmx2G"); cmd.addAll(JVM_ARGS); cmd.add("-jar"); @@ -136,6 +141,10 @@ public class SubserverSystem { }); } + public static Subserver startArena(ArenaMode modus, String map, int eventFightID, int checkSchemID, String serverName, String mapName, UUID player1, UUID player2){ + return startArena(modus, map, eventFightID, checkSchemID, serverName, mapName, player1, player2, false); + } + public static Subserver startArena(ArenaMode modus, String map){ return startArena(modus, map, 0, 0, null, null, null, null); } diff --git a/src/de/steamwar/bungeecore/commands/ChallengeCommand.java b/src/de/steamwar/bungeecore/commands/ChallengeCommand.java index 8cfa08d..68ec022 100644 --- a/src/de/steamwar/bungeecore/commands/ChallengeCommand.java +++ b/src/de/steamwar/bungeecore/commands/ChallengeCommand.java @@ -72,7 +72,7 @@ public class ChallengeCommand extends BasicCommand { @Override public Iterable onTabComplete(CommandSender commandSender, String[] args) { if(args.length == 2) - return ArenaMode.getAllChatNames(); + return ArenaMode.getAllChatNames(false); return new ArrayList<>(); } } diff --git a/src/de/steamwar/bungeecore/commands/FightCommand.java b/src/de/steamwar/bungeecore/commands/FightCommand.java index 3a5a979..5355b3c 100644 --- a/src/de/steamwar/bungeecore/commands/FightCommand.java +++ b/src/de/steamwar/bungeecore/commands/FightCommand.java @@ -26,7 +26,7 @@ public class FightCommand extends BasicCommand { super("fight", "", "f"); } - private static ArenaMode getMode(CommandSender sender, String arg){ + static ArenaMode getMode(CommandSender sender, String arg){ ArenaMode mode = ArenaMode.getByChat(arg); if(mode != null) return mode; @@ -152,7 +152,7 @@ public class FightCommand extends BasicCommand { @Override public Iterable onTabComplete(CommandSender commandSender, String[] args) { if(args.length == 1){ - return ArenaMode.getAllChatNames(); + return ArenaMode.getAllChatNames(false); }else if(args.length == 2){ ArenaMode mode = ArenaMode.getByChat(args[1]); if(mode == null) diff --git a/src/de/steamwar/bungeecore/commands/HistoricCommand.java b/src/de/steamwar/bungeecore/commands/HistoricCommand.java index b83cbe4..034de52 100644 --- a/src/de/steamwar/bungeecore/commands/HistoricCommand.java +++ b/src/de/steamwar/bungeecore/commands/HistoricCommand.java @@ -33,7 +33,7 @@ public class HistoricCommand extends BasicCommand { @Override public Iterable onTabComplete(CommandSender commandSender, String[] args) { if(args.length == 1){ - return ArenaMode.getAllChatNames(); + return ArenaMode.getAllChatNames(true); }else if(args.length == 2){ ArenaMode mode = ArenaMode.getByChat(args[1]); if(mode == null) diff --git a/src/de/steamwar/bungeecore/commands/RankCommand.java b/src/de/steamwar/bungeecore/commands/RankCommand.java new file mode 100644 index 0000000..4b44c62 --- /dev/null +++ b/src/de/steamwar/bungeecore/commands/RankCommand.java @@ -0,0 +1,48 @@ +package de.steamwar.bungeecore.commands; + +import de.steamwar.bungeecore.ArenaMode; +import de.steamwar.bungeecore.BungeeCore; +import de.steamwar.bungeecore.sql.Elo; +import de.steamwar.bungeecore.sql.SteamwarUser; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.connection.ProxiedPlayer; + +import java.util.ArrayList; +import java.util.List; + +public class RankCommand extends BasicCommand { + public RankCommand() { + super("rank", null); + } + + @Override + public void execute(CommandSender sender, String[] args) { + if(!(sender instanceof ProxiedPlayer)) + return; + + ProxiedPlayer player = (ProxiedPlayer) sender; + BungeeCore.send(player, BungeeCore.CHAT_PREFIX + "§7§lPlatzierungen"); + SteamwarUser user = SteamwarUser.get(player.getUniqueId()); + for(ArenaMode mode : ArenaMode.getAllModes()){ + if(!mode.isRanked()) + continue; + int elo = Elo.getElo(user.getId(), mode.getSchemType()); + int placement = Elo.getPlacement(elo, mode.getSchemType()); + + if(placement == -1) + BungeeCore.send(player, "§7" + mode.getChatName() + "§8: §eunplatziert"); + else + BungeeCore.send(player, "§7" + mode.getChatName() + "§8: §e" + ++placement + "§8. §7mit §e" + elo + " §7Elo§8."); + } + } + + @Override + public Iterable onTabComplete(CommandSender commandSender, String[] args) { + if(args.length > 1) + return new ArrayList<>(); + + List result = ArenaMode.getAllRankedChatNames(); + result.removeIf(name -> !name.startsWith(args[0])); + return result; + } +} diff --git a/src/de/steamwar/bungeecore/commands/RankedCommand.java b/src/de/steamwar/bungeecore/commands/RankedCommand.java new file mode 100644 index 0000000..d64aeaf --- /dev/null +++ b/src/de/steamwar/bungeecore/commands/RankedCommand.java @@ -0,0 +1,196 @@ +package de.steamwar.bungeecore.commands; + +import de.steamwar.bungeecore.*; +import de.steamwar.bungeecore.sql.Elo; +import de.steamwar.bungeecore.sql.SteamwarUser; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.ComponentBuilder; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.connection.ProxiedPlayer; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class RankedCommand extends BasicCommand { + + private static Map queues = new HashMap<>(); + + public RankedCommand() { + super("ranked", null); + for(ArenaMode mode : ArenaMode.getAllModes()) + if(mode.isRanked()) + queues.put(mode, new WaitingQueue(mode)); + + ProxyServer.getInstance().getScheduler().schedule(BungeeCore.get(), this::checkForGames, 1, 1, TimeUnit.SECONDS); + } + + @Override + public void execute(CommandSender sender, String[] args) { + if(args.length < 1){ + getModes(sender, "/ranked"); + return; + }else if(!(sender instanceof ProxiedPlayer)) + return; + + ArenaMode mode = FightCommand.getMode(sender, args[0]); + if(mode == null) + return; + else if(!mode.isRanked()){ + BungeeCore.send(sender, BungeeCore.CHAT_PREFIX + "§cDieser Spielmodus ist nicht für Ranglistenspiele freigeschalten"); + return; + } + + ProxiedPlayer player = (ProxiedPlayer) sender; + queues.get(mode).togglePlayer(player); + } + + @Override + public Iterable onTabComplete(CommandSender commandSender, String[] args) { + if(args.length > 1) + return new ArrayList<>(); + + List result = ArenaMode.getAllRankedChatNames(); + result.removeIf(name -> !name.startsWith(args[0])); + return result; + } + + static void getModes(CommandSender sender, String precommand){ + TextComponent start = new TextComponent(); + TextComponent current = start; + for(ArenaMode mode : ArenaMode.getAllModes()){ + if(!mode.hasChatName() || !mode.isRanked()) + continue; + String command = precommand + mode.getChatName(); + current.setBold(true); + current.setColor(ChatColor.GRAY); + current.setText(mode.getChatName() + " "); + current.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("§e" + command).create())); + current.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, command)); + if(current != start) + start.addExtra(current); + current = new TextComponent(); + } + + sender.sendMessage(start); + } + + private void checkForGames(){ + for(WaitingQueue queue : queues.values()) + queue.checkForGames(); + } + + private static class WaitingQueue{ + + Set players = new HashSet<>(); + private final ArenaMode mode; + + private WaitingQueue(ArenaMode mode) { + this.mode = mode; + } + + private void togglePlayer(ProxiedPlayer player){ + for(WaitingPlayer wp : players){ + if(wp.player == player){ + BungeeCore.send(player, BungeeCore.CHAT_PREFIX + "§cRanglistenspiel-Warteschlange verlassen"); + players.remove(wp); + return; + } + } + + BungeeCore.send(player, BungeeCore.CHAT_PREFIX + "§aRanglistenspiel-Warteschlange betreten"); + BungeeCore.send(player, BungeeCore.CHAT_PREFIX + "§7Wiederhole den Befehl zum Verlassen der Warteschlange"); + players.add(new WaitingPlayer(player, mode.getSchemType())); + } + + private void removeFromAll(ProxiedPlayer player){ + for(WaitingQueue queue : queues.values()){ + Iterator it = queue.players.iterator(); + while(it.hasNext()){ + WaitingPlayer wp = it.next(); + if(wp.player == player){ + it.remove(); + break; + } + } + } + } + + private void checkForGames(){ + players.removeIf(wp -> !wp.doesStillWait()); + Map inRange = new HashMap<>(); + + //Find games + for(WaitingPlayer wp1 : players){ + if(inRange.containsValue(wp1)) + continue; + + for(WaitingPlayer wp2 : players){ + if(wp1 == wp2 || inRange.containsKey(wp2) || inRange.containsValue(wp2)) + continue; + + if(wp1.inRange(wp2)) + inRange.putIfAbsent(wp1, wp2); + } + wp1.increment(); + } + + for(Map.Entry entry : inRange.entrySet()){ + WaitingPlayer wp1 = entry.getKey(); + WaitingPlayer wp2 = entry.getValue(); + BungeeCore.send(wp1.player, BungeeCore.CHAT_PREFIX + "§eGegner gefunden! Ranglistenspiel startet."); + BungeeCore.send(wp2.player, BungeeCore.CHAT_PREFIX + "§eGegner gefunden! Ranglistenspiel startet."); + removeFromAll(wp1.player); + removeFromAll(wp2.player); + + String serverName = wp1.player.getName() + " vs " + wp2.player.getName(); + Subserver arena = SubserverSystem.startArena(mode, mode.getRandomMap(), 0, 0, serverName, serverName.replace(' ', '_'), wp1.player.getUniqueId(), wp2.player.getUniqueId(), true); + arena.sendPlayer(wp1.player); + arena.sendPlayer(wp2.player); + + BungeeCore.broadcast(BungeeCore.CHAT_PREFIX + "§7" + mode.getDisplayName() + "§8-§7Ranglistenspiel§8: §e" + serverName, + "§aZuschauen", + new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/join " + wp1.player.getName())); + } + } + } + + private static class WaitingPlayer{ + private int currentRange; + private final int elo; + private final ProxiedPlayer player; + + private WaitingPlayer(ProxiedPlayer player, String gameMode){ + this.player = player; + SteamwarUser user = SteamwarUser.get(player.getUniqueId()); + elo = Elo.getElo(user.getId(), gameMode); + currentRange = 0; + } + + private boolean inRange(WaitingPlayer player){ + return Math.abs(elo - player.elo) <= this.currentRange; + } + + private void increment(){ + currentRange++; + } + + private boolean doesStillWait(){ + if(!player.isConnected()) + return false; + + Subserver subserver = Subserver.getSubserver(player); + if(subserver == null) + return true; + if(subserver.getType() == Servertype.ARENA){ + BungeeCore.send(player, BungeeCore.CHAT_PREFIX + "§cRanglistenspiel-Warteschlange verlassen"); + return false; + } + + return true; + } + } +} diff --git a/src/de/steamwar/bungeecore/sql/Elo.java b/src/de/steamwar/bungeecore/sql/Elo.java new file mode 100644 index 0000000..450b07a --- /dev/null +++ b/src/de/steamwar/bungeecore/sql/Elo.java @@ -0,0 +1,32 @@ +package de.steamwar.bungeecore.sql; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class Elo { + private Elo(){} + + public static int getElo(int userID, String gameMode){ + ResultSet rs = SQL.select("SELECT Elo FROM Elo WHERE UserID = ? AND GameMode = ?", userID, gameMode); + int elo = 1000; + try { + if(rs.next()) + elo = rs.getInt("Elo"); + } catch (SQLException e) { + throw new SecurityException("Could not get Elo", e); + } + return elo; + } + + public static int getPlacement(int elo, String gameMode){ + ResultSet rs = SQL.select("SELECT COUNT(*) AS Place FROM Elo WHERE GameMode = ? AND Elo > ?", gameMode, elo); + try{ + if(!rs.next()) + return -1; + + return rs.getInt("Place"); + }catch(SQLException e){ + throw new SecurityException("Could not get place", e); + } + } +}