diff --git a/src/de/steamwar/bungeecore/BungeeCore.java b/src/de/steamwar/bungeecore/BungeeCore.java index 8e9e8fde..6ba5f08e 100644 --- a/src/de/steamwar/bungeecore/BungeeCore.java +++ b/src/de/steamwar/bungeecore/BungeeCore.java @@ -25,10 +25,7 @@ import de.steamwar.bungeecore.commands.*; import de.steamwar.bungeecore.comms.SpigotReceiver; import de.steamwar.bungeecore.listeners.*; import de.steamwar.bungeecore.listeners.mods.*; -import de.steamwar.bungeecore.sql.Punishment; -import de.steamwar.bungeecore.sql.Statement; -import de.steamwar.bungeecore.sql.SteamwarUser; -import de.steamwar.bungeecore.sql.Team; +import de.steamwar.bungeecore.sql.*; import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.ProxyServer; @@ -125,10 +122,10 @@ public class BungeeCore extends Plugin { new ListCommand(); new StatCommand(); new VerifyCommand(); - new ReplayCommand(); new GDPRQuery(); new PlaytimeCommand(); new ArenaCommand(); + new RankCommand(); // Punishment Commands: new PunishmentCommand("ban", Punishment.PunishmentType.Ban); @@ -145,6 +142,8 @@ public class BungeeCore extends Plugin { new ChallengeCommand(); new HistoricCommand(); new CheckCommand(); + new ReplayCommand(); + new TutorialCommand(); new Broadcaster(); }else{ @@ -158,6 +157,7 @@ public class BungeeCore extends Plugin { getProxy().getScheduler().schedule(this, () -> { SteamwarUser.clearCache(); + UserElo.clearCache(); Team.clearCache(); }, 1, 1, TimeUnit.HOURS); diff --git a/src/de/steamwar/bungeecore/EventStarter.java b/src/de/steamwar/bungeecore/EventStarter.java index b1738285..98d75e39 100644 --- a/src/de/steamwar/bungeecore/EventStarter.java +++ b/src/de/steamwar/bungeecore/EventStarter.java @@ -64,12 +64,13 @@ public class EventStarter implements Runnable { Team blue = Team.get(next.getTeamBlue()); Team red = Team.get(next.getTeamRed()); - String serverName = blue.getTeamKuerzel() + " vs " + red.getTeamKuerzel(); - Subserver subserver = SubserverSystem.startEventArena(next, serverName); + ServerStarter starter = new ServerStarter().event(next); ProxiedPlayer leiter = ProxyServer.getInstance().getPlayer(SteamwarUser.get(next.getKampfleiter()).getUuid()); if(leiter != null) - subserver.sendPlayer(leiter); + starter.send(leiter); + + Subserver subserver = starter.start(); eventServer.put(blue.getTeamId(), subserver); eventServer.put(red.getTeamId(), subserver); diff --git a/src/de/steamwar/bungeecore/Message.java b/src/de/steamwar/bungeecore/Message.java index a54694ed..acaa1891 100644 --- a/src/de/steamwar/bungeecore/Message.java +++ b/src/de/steamwar/bungeecore/Message.java @@ -28,20 +28,15 @@ 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.io.IOException; +import java.io.InputStream; import java.text.DateFormat; import java.text.MessageFormat; -import java.util.Date; -import java.util.Locale; -import java.util.ResourceBundle; +import java.util.*; public class Message { - private static final ResourceBundle.Control CONTROL = new ResourceBundle.Control() { - @Override - public long getTimeToLive(String arg0, Locale arg1) { - return 60000; //Cache only 1 minute - } - }; + private static final Map bundles = new HashMap<>(); public static TextComponent parseToComponent(String message, boolean prefixed, CommandSender sender, Object... params){ return new TextComponent(TextComponent.fromLegacyText(parse(message, prefixed, locale(sender), params))); @@ -63,10 +58,29 @@ public class Message { return sender instanceof ProxiedPlayer ? ((ProxiedPlayer)sender).getLocale() : Locale.getDefault(); } + private static final String BASE_PATH = "/" + "de.steamwar.messages.BungeeCore".replace('.', '/'); + + private static ResourceBundle getResourceBundle(String locale, ResourceBundle parent) { + return bundles.computeIfAbsent(locale, locale1 -> { + InputStream inputStream = Message.class.getResourceAsStream(BASE_PATH + ("".equals(locale) ? "" : "_" + locale) + ".properties"); + if(inputStream == null) + return parent; + try { + return new SteamwarResourceBundle(inputStream, parent); + } catch (IOException e) { + return parent; + } + }); + } + + private static ResourceBundle getResourceBundle(Locale locale) { + return getResourceBundle(locale.toString(), getResourceBundle(locale.getLanguage(), getResourceBundle( "", (ResourceBundle) null))); + } + private static String parse(String message, boolean prefixed, Locale locale, Object... params){ if(locale == null) locale = Locale.getDefault(); - ResourceBundle resourceBundle = ResourceBundle.getBundle("de.steamwar.messages.BungeeCore", locale, CONTROL); + ResourceBundle resourceBundle = getResourceBundle(locale); String pattern = ""; if(prefixed) pattern = resourceBundle.getObject("PREFIX") + " "; @@ -174,4 +188,11 @@ public class Message { public Object[] getParams() { return params; } + + private static class SteamwarResourceBundle extends PropertyResourceBundle { + public SteamwarResourceBundle(InputStream stream, ResourceBundle parent) throws IOException { + super(stream); + setParent(parent); + } + } } \ No newline at end of file diff --git a/src/de/steamwar/bungeecore/ServerStarter.java b/src/de/steamwar/bungeecore/ServerStarter.java new file mode 100644 index 00000000..622b7a42 --- /dev/null +++ b/src/de/steamwar/bungeecore/ServerStarter.java @@ -0,0 +1,272 @@ +package de.steamwar.bungeecore; + +import de.steamwar.bungeecore.sql.EventFight; +import de.steamwar.bungeecore.sql.SteamwarUser; +import de.steamwar.bungeecore.sql.Team; +import de.steamwar.bungeecore.sql.Tutorial; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.connection.ProxiedPlayer; + +import java.io.File; +import java.net.InetSocketAddress; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.function.BooleanSupplier; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class ServerStarter { + + private static final boolean MAIN_SERVER = ProxyServer.getInstance().getConfig().getListeners().stream().anyMatch(info -> ((InetSocketAddress) info.getSocketAddress()).getPort() == 25565); + private static final Portrange BAU_PORTS = MAIN_SERVER ? new Portrange(10100, 20000) : new Portrange(2100, 2200); + private static final Portrange ARENA_PORTS = MAIN_SERVER ? new Portrange(3000, 3100) : (BungeeCore.EVENT_MODE ? new Portrange(4000, 5000) : BAU_PORTS); + + private static final String BACKBONE = "/home/minecraft/"; + private static final String SERVER_PATH = BACKBONE + "server/"; + private static final String EVENT_PATH = BACKBONE + "event/"; + public static final String TEMP_WORLD_PATH = BACKBONE + "arenaserver/"; + public static final String TUTORIAL_PATH = BACKBONE + "tutorials/"; + + private File directory = null; + private String worldDir = null; + + private Node node = null; + private String serverJar = "spigot-1.15.2.jar"; + private String xmx = "768M"; + private Portrange portrange = BAU_PORTS; + private Function serverNameProvider = port -> node.getName() + port; + private BooleanSupplier startCondition = () -> true; + private ServerConstructor constructor = (serverName, port, builder, shutdownCallback) -> new Subserver(Servertype.ARENA, serverName, port, builder, shutdownCallback); + private Runnable worldSetup = () -> {}; + private String worldName = null; + private Runnable worldCleanup = () -> {}; + + private final Set playersToSend = new HashSet<>(); + private final Map arguments = new HashMap<>(); + + public ServerStarter arena(ArenaMode mode, String map) { + portrange = ARENA_PORTS; + serverNameProvider = port -> mode.getChatName() + (port - portrange.start); + serverJar = mode.serverJar(); + directory = new File(SERVER_PATH, mode.getFolder()); + arguments.put("config", mode.getConfig()); + tempWorld(SERVER_PATH + mode.getFolder() + "/arenas/" + map); + return this; + } + + public ServerStarter event(EventFight eventFight) { + arena(eventFight.getSpielmodus(), eventFight.getMap()); + node = Node.local; + worldDir = EVENT_PATH; + worldCleanup = () -> {}; + arguments.put("fightID", String.valueOf(eventFight.getFightID())); + + String serverName = Team.get(eventFight.getTeamBlue()).getTeamKuerzel() + " vs " + Team.get(eventFight.getTeamRed()).getTeamKuerzel(); + serverNameProvider = port -> serverName; + worldName = serverToWorldName(serverName + eventFight.getStartTime().toLocalDateTime().format(DateTimeFormatter.ISO_TIME)); + return this; + } + + public ServerStarter test(ArenaMode mode, String map, ProxiedPlayer owner) { + arena(mode, map); + buildWithTemp(owner); + portrange = BAU_PORTS; + arguments.put("fightID", String.valueOf(-1)); + return send(owner); + } + + public ServerStarter blueLeader(ProxiedPlayer player) { + arguments.put("blueLeader", player.getUniqueId().toString()); + return send(player); + } + + public ServerStarter redLeader(ProxiedPlayer player) { + arguments.put("redLeader", player.getUniqueId().toString()); + return send(player); + } + + public ServerStarter check(int schemID) { + arguments.put("checkSchemID", String.valueOf(schemID)); + return this; + } + + public ServerStarter prepare(int schemID) { + arguments.put("prepareSchemID", String.valueOf(schemID)); + return this; + } + + public ServerStarter replay(int replayID) { + arguments.put("replay", String.valueOf(replayID)); + return this; + } + + public ServerStarter build15(UUID owner) { + directory = new File(SERVER_PATH, "Bau15"); + worldDir = BungeeCore.USERWORLDS15; + worldName = String.valueOf(SteamwarUser.get(owner).getId()); + buildWithWorld(owner, BungeeCore.BAUWELT15); + return this; + } + + public ServerStarter build12(UUID owner) { + directory = new File(SERVER_PATH, "UserBau"); + serverJar = "spigot-1.12.2.jar"; + xmx = "256M"; + worldDir = BungeeCore.WORLD_FOLDER; + worldName = owner.toString(); + buildWithWorld(owner, BungeeCore.BAUWELT_PROTOTYP); + return this; + } + + public ServerStarter tutorial(ProxiedPlayer owner, Tutorial tutorial) { + directory = new File(SERVER_PATH, "Tutorial"); + buildWithTemp(owner); + tempWorld(TUTORIAL_PATH + tutorial.id()); + arguments.put("tutorial", String.valueOf(tutorial.id())); + return send(owner); + } + + private void tempWorld(String template) { + worldDir = TEMP_WORLD_PATH; + worldSetup = () -> copyWorld(node, template, worldDir + worldName); + worldCleanup = () -> SubserverSystem.deleteFolder(node, worldDir + worldName); + } + + private void buildWithWorld(UUID owner, String prototype) { + build(owner); + + worldSetup = () -> { + File world = new File(worldDir, worldName); + if (!world.exists()) + copyWorld(node, prototype, world.getPath()); + }; + + // Send players to existing server + startCondition = () -> { + for(Subserver subserver : Subserver.getServerList()) { + if(subserver.getType() == Servertype.BAUSERVER && ((Bauserver)subserver).getOwner().equals(owner)) { + for(ProxiedPlayer p : playersToSend) + SubserverSystem.sendPlayer(subserver, p); + return false; + } + } + return true; + }; + } + + private void buildWithTemp(ProxiedPlayer owner) { + build(owner.getUniqueId()); + + // Stop existing build server + startCondition = () -> { + if(startingBau(owner)) + return false; + + for (Subserver subserver : Subserver.getServerList()) { + if (subserver.getType() == Servertype.BAUSERVER && ((Bauserver) subserver).getOwner().equals(owner.getUniqueId()) && subserver.hasStarted()) { + subserver.stop(); + break; + } + } + + return !startingBau(owner); + }; + } + + private void build(UUID owner) { + constructor = (serverName, port, builder, shutdownCallback) -> new Bauserver(serverName, owner, port, builder, shutdownCallback); + serverNameProvider = port -> bauServerName(SteamwarUser.get(owner)); + } + + public ServerStarter send(ProxiedPlayer player) { + playersToSend.add(player); + return this; + } + + public Subserver start() { + if(!startCondition.getAsBoolean()) + return null; + + int port = portrange.freePort(); + String serverName = serverNameProvider.apply(port); + + if(node == null) + node = Node.getNode(); + if(worldName == null) + worldName = serverToWorldName(serverName); + + worldSetup.run(); + arguments.put("logPath", worldName); + + Subserver subserver = constructor.construct(serverName, port, node.startServer( + serverJar, directory, worldDir, worldName, port, xmx, arguments.entrySet().stream().map(entry -> entry.getKey() + "=" + entry.getValue()).toArray(String[]::new) + ), worldCleanup); + + for(ProxiedPlayer p : playersToSend) + SubserverSystem.sendPlayer(subserver, p); + + return subserver; + } + + private static boolean startingBau(ProxiedPlayer p) { + for (Subserver subserver : Subserver.getServerList()) { + if (subserver.getType() == Servertype.BAUSERVER && ((Bauserver) subserver).getOwner().equals(p.getUniqueId()) && !subserver.hasStarted()) { + Message.send("BAU_START_ALREADY", p); + return true; + } + } + return false; + } + + public static String bauServerName(SteamwarUser user) { + return user.getUserName() + "s Bau"; + } + + public static String serverToWorldName(String serverName) { + return serverName.replace(' ', '_').replace("[", "").replace("]", ""); + } + + public static void copyWorld(Node node, String template, String target) { + node.execute("cp", "-r", template, target); + } + + private interface ServerConstructor { + Subserver construct(String serverName, int port, ProcessBuilder builder, Runnable shutdownCallback); + } + + private static class Portrange { + + private final int start; + private final int end; + private int current; + + private Portrange(int start, int end) { + this.start = start; + this.end = end; + current = start; + } + + private void increment() { + current++; + + if(current == end) + current = start; + } + + private synchronized int freePort() { + Set usedPorts; + + synchronized (Subserver.getServerList()) { + usedPorts = Subserver.getServerList().stream().map(server -> ((InetSocketAddress) server.getServer().getSocketAddress()).getPort()).collect(Collectors.toSet()); + } + + while(usedPorts.contains(current)) { + increment(); + } + + int result = current; + increment(); + return result; + } + } +} diff --git a/src/de/steamwar/bungeecore/SubserverSystem.java b/src/de/steamwar/bungeecore/SubserverSystem.java index 102b64fd..2908acb1 100644 --- a/src/de/steamwar/bungeecore/SubserverSystem.java +++ b/src/de/steamwar/bungeecore/SubserverSystem.java @@ -19,156 +19,23 @@ package de.steamwar.bungeecore; -import de.steamwar.bungeecore.sql.EventFight; +import de.steamwar.bungeecore.comms.handlers.FightInfoHandler; +import de.steamwar.bungeecore.comms.packets.StartingServerPacket; import de.steamwar.bungeecore.sql.IgnoreSystem; import de.steamwar.bungeecore.sql.SteamwarUser; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.connection.ProxiedPlayer; -import java.io.File; -import java.net.InetSocketAddress; -import java.time.format.DateTimeFormatter; -import java.util.Set; import java.util.UUID; -import java.util.stream.Collectors; public class SubserverSystem { private SubserverSystem(){} - private static final String BACKBONE = "/home/minecraft/"; - private static final String ARENA_PATH = BACKBONE + "arenaserver/"; - private static final String SERVER_PATH = BACKBONE + "server/"; - private static final String EVENT_PATH = BACKBONE + "event/"; - - private static final boolean MAIN_SERVER = ProxyServer.getInstance().getConfig().getListeners().stream().anyMatch(info -> ((InetSocketAddress) info.getSocketAddress()).getPort() == 25565); - private static final Portrange bauPorts = MAIN_SERVER ? new Portrange(10100, 20000) : new Portrange(2100, 2200); - private static final Portrange arenaPorts = MAIN_SERVER ? new Portrange(3000, 3100) : (BungeeCore.EVENT_MODE ? new Portrange(4000, 5000) : bauPorts); - - /** - * This function starts every arena (even test- and eventarenas). - * - * @param modus - * This has to be a valid arena mode - * - * @param map - * This String must be the internal map name of the selected map - * - * @param eventFightID - * For a normal arena: 0 - * For a test arena: -1 - * For an event arena: EventFightID of the fight - * - * @param replayID - * @param serverName - * The name of the server (for event and test arenas) - * or null (autogenerated arena name modus.getDisplayName() + number) - * - * @param mapName - * The name of the map copy the server runs on (for event and test arenas) - * or null (autogenerated map name equals server name) - * - * @param player1 - * For event and normal arenas: The UUID of the designated leader for the blue team - * or null (no designated leader). - * For test arenas: The owner's UUID of the test arena (disables normal function). - * - * @param player2 - * For event, test and normal arenas: The UUID of the designated leader for the red team - * or null (no designated leader). - * - * @return - * The new started subserver. - */ - public static synchronized Subserver startArena(ArenaMode modus, String map, int eventFightID, int checkSchemID, int prepareSchemID, int replayID, String serverName, String mapName, UUID player1, UUID player2){ - //Generate missing parameters - Node node = eventFightID > 0 ? Node.local : Node.getNode(); - int port = arenaPorts.freePort(); - - if(serverName == null){ - serverName = modus.getDisplayName() + (port - arenaPorts.start); - } - if(mapName == null) - mapName = serverName; - - mapName = mapName.replace(' ', '_').replace("[", "").replace("]", ""); - - String worldDir; - if(eventFightID > 0) - worldDir = EVENT_PATH; - else - worldDir = ARENA_PATH; - - //Copy world - node.execute("cp", "-r", SERVER_PATH + modus.getFolder() + "/arenas/" + map, worldDir + mapName); - - File directory = new File(SERVER_PATH, modus.getFolder()); - ProcessBuilder builder = node.startServer( - modus.serverJar(), directory, worldDir, mapName, port, "768M", - "logPath=" + mapName, "config=" + modus.getConfig(), - "fightID=" + eventFightID, - "checkSchemID=" + checkSchemID, "prepareSchemID=" + prepareSchemID, - "replay=" + replayID, - player1 != null && eventFightID != -1 ? "blueLeader=" + player1 : null, - player2 != null ? "redLeader=" + player2 : null - ); - - String finalMapName = mapName; - if(eventFightID == -1) - return new Bauserver(serverName, player1, port, builder, () -> deleteFolder(node, ARENA_PATH + finalMapName)); - else - return new Subserver(Servertype.ARENA, serverName, port, builder, () -> { - if(eventFightID > 0) - return; - deleteFolder(node, ARENA_PATH + finalMapName); - }); - } - - public static void deleteFolder(Node node, String worldName){ + public static void deleteFolder(Node node, String worldName) { node.execute("rm", "-r", worldName); } - public static Subserver startEventArena(EventFight eventFight, String serverName){ - return startArena( - eventFight.getSpielmodus(), - eventFight.getMap(), - eventFight.getFightID(), - 0, - 0, - 0, serverName, - serverName + eventFight.getStartTime().toLocalDateTime().format(DateTimeFormatter.ISO_TIME), - null, - null); - } - - public static void startTestServer(ProxiedPlayer p, ArenaMode m, String map, int checkSchemId, int prepareSchemId){ - startArena(m, map, -1, checkSchemId, prepareSchemId, 0, p.getName() + "s Bau", p.getName(), p.getUniqueId(), null).sendPlayer(p); - } - - private static synchronized void sendToBau(ProxiedPlayer p, UUID owner, String prototype, String worldFolder, String serverJar, String worldDir, String worldName, String xmx, String serverName){ - if(bauRunning(p, owner)) - return; - - copyBauweltIfRequired(prototype, worldFolder + worldName); - SteamwarUser user = SteamwarUser.get(owner); - File directory = new File(SERVER_PATH, serverName); - Node node = Node.getNode(); - int port = bauPorts.freePort(); - - new Bauserver(user.getUserName() + "s Bau", owner, port, node.startServer( - serverJar, directory, worldDir, worldName, port, xmx, "logPath=" + worldName - ), () -> {}).sendPlayer(p); - } - - public static void sendToBauServer(ProxiedPlayer p, UUID owner){ - sendToBau(p, owner, BungeeCore.BAUWELT_PROTOTYP, BungeeCore.WORLD_FOLDER, "spigot-1.12.2.jar", "/home/minecraft/userworlds", owner.toString(), "256M", "UserBau"); - } - - public static void sendToBau15(ProxiedPlayer p, UUID owner){ - SteamwarUser user = SteamwarUser.get(owner); - sendToBau(p, owner, BungeeCore.BAUWELT15, BungeeCore.USERWORLDS15, "spigot-1.15.2.jar", "/home/minecraft/userworlds15", String.valueOf(user.getId()), "768M", "Bau15"); - } - public static void sendDeniedMessage(ProxiedPlayer p, UUID owner){ ProxiedPlayer o = ProxyServer.getInstance().getPlayer(owner); if(o == null) @@ -183,56 +50,9 @@ public class SubserverSystem { new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/bau addmember " + p.getName())); } - private static boolean bauRunning(ProxiedPlayer p, UUID owner){ - for(Subserver subserver : Subserver.getServerList()){ - if(subserver.getType() == Servertype.BAUSERVER && ((Bauserver)subserver).getOwner().equals(owner)){ - subserver.sendPlayer(p); - return true; - } - } - return false; - } - - private static void copyBauweltIfRequired(String sourcePath, String targetPath){ - File w = new File(targetPath); - if (!w.exists() || !w.isDirectory()) - Node.local.execute("cp", "-r", sourcePath, targetPath); - } - - - private static class Portrange { - - private final int start; - private final int end; - private int current; - - private Portrange(int start, int end) { - this.start = start; - this.end = end; - current = start; - } - - private void increment() { - current++; - - if(current == end) - current = start; - } - - private synchronized int freePort() { - Set usedPorts; - - synchronized (Subserver.getServerList()) { - usedPorts = Subserver.getServerList().stream().map(server -> ((InetSocketAddress) server.getServer().getSocketAddress()).getPort()).collect(Collectors.toSet()); - } - - while(usedPorts.contains(current)) { - increment(); - } - - int result = current; - increment(); - return result; - } + public static void sendPlayer(Subserver subserver, ProxiedPlayer player) { + subserver.sendPlayer(player); + if(!subserver.hasStarted() && FightInfoHandler.onLobby(player)) + new StartingServerPacket(SteamwarUser.get(player.getUniqueId())).send(player); } } diff --git a/src/de/steamwar/bungeecore/commands/ArenaCommand.java b/src/de/steamwar/bungeecore/commands/ArenaCommand.java index 9abd0f5b..d88950f7 100644 --- a/src/de/steamwar/bungeecore/commands/ArenaCommand.java +++ b/src/de/steamwar/bungeecore/commands/ArenaCommand.java @@ -22,6 +22,7 @@ package de.steamwar.bungeecore.commands; import de.steamwar.bungeecore.Message; import de.steamwar.bungeecore.Servertype; import de.steamwar.bungeecore.Subserver; +import de.steamwar.bungeecore.SubserverSystem; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.config.ServerInfo; @@ -51,6 +52,6 @@ public class ArenaCommand extends BasicCommand { return; } - subserver.sendPlayer(player); + SubserverSystem.sendPlayer(subserver, player); } } diff --git a/src/de/steamwar/bungeecore/commands/BauCommand.java b/src/de/steamwar/bungeecore/commands/BauCommand.java index cc9b8ef6..5a58a738 100644 --- a/src/de/steamwar/bungeecore/commands/BauCommand.java +++ b/src/de/steamwar/bungeecore/commands/BauCommand.java @@ -45,20 +45,17 @@ public class BauCommand extends BasicCommand { ProxiedPlayer p = (ProxiedPlayer) sender; if(args.length == 0){ - if(bau15(p, args, 0)) - SubserverSystem.sendToBau15(p, p.getUniqueId()); - else - SubserverSystem.sendToBauServer(p, p.getUniqueId()); + (bau15(p, args, 0) ? new ServerStarter().build15(p.getUniqueId()) : new ServerStarter().build12(p.getUniqueId())).send(p).start(); return; } switch(args[0].toLowerCase()){ - case "ws": - case "warship": case "12": case "1.12": - SubserverSystem.sendToBauServer(p, p.getUniqueId()); + new ServerStarter().build12(p.getUniqueId()).send(p).start(); break; + case "ws": + case "warship": case "as": case "airship": case "mwg": @@ -67,7 +64,7 @@ public class BauCommand extends BasicCommand { case "wargear": case "15": case "1.15": - SubserverSystem.sendToBau15(p, p.getUniqueId()); + new ServerStarter().build15(p.getUniqueId()).send(p).start(); break; case "addmember": addmember(p, args); @@ -143,10 +140,7 @@ public class BauCommand extends BasicCommand { return; } - if(bau15(p, args, 2)) - SubserverSystem.sendToBau15(p, worldOwner.getUuid()); - else - SubserverSystem.sendToBauServer(p, worldOwner.getUuid()); + (bau15(p, args, 2) ? new ServerStarter().build15(worldOwner.getUuid()) : new ServerStarter().build12(worldOwner.getUuid())).send(p).start(); } private static boolean bau15(ProxiedPlayer p, String[] args, int pos){ @@ -259,40 +253,8 @@ public class BauCommand extends BasicCommand { }); } - private static boolean startingBau(ProxiedPlayer p) { - for (Subserver subserver : Subserver.getServerList()) { - if (subserver.getType() == Servertype.BAUSERVER && ((Bauserver) subserver).getOwner().equals(p.getUniqueId()) && !subserver.hasStarted()) { - Message.send("BAU_START_ALREADY", p); - return true; - } - } - return false; - } - - public static boolean stopBauserver(ProxiedPlayer p){ - if(startingBau(p)) - return false; - - for (Subserver subserver : Subserver.getServerList()) { - if (subserver.getType() == Servertype.BAUSERVER && ((Bauserver) subserver).getOwner().equals(p.getUniqueId()) && subserver.hasStarted()) { - subserver.stop(); - try { - Thread.sleep(200); // Wait until possible testarena-World has been deleted - } catch (InterruptedException e) { - throw new SecurityException("Subserver stop interrupted", e); - } - break; - } - } - - return !startingBau(p); - } - private static void testarena(ProxiedPlayer p, String[] args){ - FightCommand.createArena(p, "/bau testarena ", args, 1, false, (player, mode, map) -> ProxyServer.getInstance().getScheduler().runAsync(BungeeCore.get(), () -> { - if(stopBauserver(p)) - SubserverSystem.startTestServer(p, mode, map, 0, 0); - })); + FightCommand.createArena(p, "/bau testarena ", args, 1, false, (player, mode, map) -> ProxyServer.getInstance().getScheduler().runAsync(BungeeCore.get(), () -> new ServerStarter().test(mode, map, p).start())); } private static BauweltMember member(ProxiedPlayer p, SteamwarUser member){ diff --git a/src/de/steamwar/bungeecore/commands/ChallengeCommand.java b/src/de/steamwar/bungeecore/commands/ChallengeCommand.java index df477c9a..7f480c1b 100644 --- a/src/de/steamwar/bungeecore/commands/ChallengeCommand.java +++ b/src/de/steamwar/bungeecore/commands/ChallengeCommand.java @@ -76,11 +76,7 @@ public class ChallengeCommand extends BasicCommand { challenges.remove(target); challenges.remove(player); - Subserver arena = SubserverSystem.startArena(mode, map, 0, 0, 0, 0, null, null, player.getUniqueId(), target.getUniqueId()); - - arena.sendPlayer(player); - arena.sendPlayer(target); - + Subserver arena = new ServerStarter().arena(mode, map).blueLeader(player).redLeader(target).start(); Message.broadcast("CHALLENGE_BROADCAST", "CHALLENGE_BROADCAST_HOVER", new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/arena " + arena.getServer().getName()), mode.getDisplayName(), player.getName(), target.getName()); }else{ diff --git a/src/de/steamwar/bungeecore/commands/CheckCommand.java b/src/de/steamwar/bungeecore/commands/CheckCommand.java index a74dc788..b65b1cc4 100644 --- a/src/de/steamwar/bungeecore/commands/CheckCommand.java +++ b/src/de/steamwar/bungeecore/commands/CheckCommand.java @@ -228,13 +228,12 @@ public class CheckCommand extends BasicCommand { this.checkList = checkQuestions.get(schematic.getSchemtype()).listIterator(); ProxyServer.getInstance().getScheduler().runAsync(BungeeCore.get(), () -> { - if(!BauCommand.stopBauserver(checker)){ + ArenaMode mode = ArenaMode.getBySchemType(schematic.getSchemtype().fightType()); + if(new ServerStarter().test(mode, mode.getRandomMap(), checker).check(schematic.getId()).start() == null) { remove(); return; } - ArenaMode mode = ArenaMode.getBySchemType(schematic.getSchemtype().fightType()); - SubserverSystem.startTestServer(checker, mode, FightCommand.getMap(checker, mode, "Random"), schematic.getId(), 0); currentCheckers.put(checker.getUniqueId(), this); currentSchems.put(schematic.getId(), this); for(CheckedSchematic previous : CheckedSchematic.previousChecks(schematic)) diff --git a/src/de/steamwar/bungeecore/commands/EventCommand.java b/src/de/steamwar/bungeecore/commands/EventCommand.java index 680b0a93..79ae6208 100644 --- a/src/de/steamwar/bungeecore/commands/EventCommand.java +++ b/src/de/steamwar/bungeecore/commands/EventCommand.java @@ -19,10 +19,7 @@ package de.steamwar.bungeecore.commands; -import de.steamwar.bungeecore.BungeeCore; -import de.steamwar.bungeecore.EventStarter; -import de.steamwar.bungeecore.Message; -import de.steamwar.bungeecore.Subserver; +import de.steamwar.bungeecore.*; import de.steamwar.bungeecore.sql.Event; import de.steamwar.bungeecore.sql.EventFight; import de.steamwar.bungeecore.sql.Team; @@ -72,7 +69,7 @@ public class EventCommand extends BasicCommand { return; } - eventArena.sendPlayer(player); + SubserverSystem.sendPlayer(eventArena, player); } private void noCurrentEvent(ProxiedPlayer player){ diff --git a/src/de/steamwar/bungeecore/commands/FightCommand.java b/src/de/steamwar/bungeecore/commands/FightCommand.java index 653db48d..736d78dc 100644 --- a/src/de/steamwar/bungeecore/commands/FightCommand.java +++ b/src/de/steamwar/bungeecore/commands/FightCommand.java @@ -164,8 +164,7 @@ public class FightCommand extends BasicCommand { @Override public void execute(CommandSender sender, String[] args) { createArena(sender, "/fight ", args, 0, false, (player, mode, map) -> { - Subserver arena = SubserverSystem.startArena(mode, map, 0, 0, 0, 0, null, null, player.getUniqueId(), null); - arena.sendPlayer(player); + Subserver arena = new ServerStarter().arena(mode, map).blueLeader(player).start(); Message.broadcast("FIGHT_BROADCAST", "FIGHT_BROADCAST_HOVER" , new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/arena " + arena.getServer().getName()), mode.getDisplayName(), player.getName()); }); diff --git a/src/de/steamwar/bungeecore/commands/HistoricCommand.java b/src/de/steamwar/bungeecore/commands/HistoricCommand.java index 7e34836c..f07a6a3e 100644 --- a/src/de/steamwar/bungeecore/commands/HistoricCommand.java +++ b/src/de/steamwar/bungeecore/commands/HistoricCommand.java @@ -19,10 +19,7 @@ package de.steamwar.bungeecore.commands; -import de.steamwar.bungeecore.ArenaMode; -import de.steamwar.bungeecore.Message; -import de.steamwar.bungeecore.Subserver; -import de.steamwar.bungeecore.SubserverSystem; +import de.steamwar.bungeecore.*; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.chat.ClickEvent; @@ -36,8 +33,7 @@ public class HistoricCommand extends BasicCommand { @Override public void execute(CommandSender sender, String[] args) { FightCommand.createArena(sender, "/historic ", args, 0, true, (player, mode, map) -> { - Subserver arena = SubserverSystem.startArena(mode, map, 0, 0, 0, 0, null, null, player.getUniqueId(), null); - arena.sendPlayer(player); + Subserver arena = new ServerStarter().arena(mode, map).blueLeader(player).start(); Message.broadcast("HISTORIC_BROADCAST", "HISTORIC_BROADCAST_HOVER" , new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/arena " + arena.getServer().getName()), mode.getDisplayName(), player.getName()); }); diff --git a/src/de/steamwar/bungeecore/commands/JoinmeCommand.java b/src/de/steamwar/bungeecore/commands/JoinmeCommand.java index 37fb8061..105063c0 100644 --- a/src/de/steamwar/bungeecore/commands/JoinmeCommand.java +++ b/src/de/steamwar/bungeecore/commands/JoinmeCommand.java @@ -59,12 +59,12 @@ public class JoinmeCommand extends BasicCommand { if(subserver != null) { Servertype type = subserver.getType(); if (type == Servertype.ARENA) { - subserver.sendPlayer(player); + SubserverSystem.sendPlayer(subserver, player); } else if (type == Servertype.BAUSERVER) { Bauserver bauserver = (Bauserver) subserver; if (bauserver.getOwner().equals(player.getUniqueId()) || BauweltMember.getBauMember(bauserver.getOwner(), player.getUniqueId()) != null) { - subserver.sendPlayer(player); + SubserverSystem.sendPlayer(subserver, player); } else { SubserverSystem.sendDeniedMessage(player, bauserver.getOwner()); Message.send("JOIN_PLAYER_BLOCK", player); diff --git a/src/de/steamwar/bungeecore/commands/RankCommand.java b/src/de/steamwar/bungeecore/commands/RankCommand.java new file mode 100644 index 00000000..f76932e0 --- /dev/null +++ b/src/de/steamwar/bungeecore/commands/RankCommand.java @@ -0,0 +1,70 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bungeecore.commands; + +import de.steamwar.bungeecore.ArenaMode; +import de.steamwar.bungeecore.Message; +import de.steamwar.bungeecore.sql.SteamwarUser; +import de.steamwar.bungeecore.sql.UserElo; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.connection.ProxiedPlayer; + +import java.util.Optional; + +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; + SteamwarUser user = SteamwarUser.get(player.getUniqueId()); + if (args.length > 0) { + SteamwarUser nUser = SteamwarUser.get(args[0]); + if (nUser == null) { + Message.send("RANK_PLAYER_NOT_FOUND", player); + return; + } + Message.send("RANK_PLAYER_FOUND", player, nUser.getUserName()); + user = nUser; + } + for(ArenaMode mode : ArenaMode.getAllModes()) { + if (!mode.isRanked()) + continue; + + Message.send("RANK_HEADER", player, mode.getChatName()); + + Optional elo = UserElo.getElo(user.getId(), mode.getSchemType()); + + if (elo.isPresent()) { + int placement = UserElo.getPlacement(elo.get(), mode.getSchemType()); + Message.send("RANK_PLACED", player, placement, elo.get()); + } else { + Message.send("RANK_UNPLACED", player); + } + Message.send("RANK_EMBLEM", player, UserElo.getEmblemProgression(player, mode.getChatName(), user.getId())); + } + } +} diff --git a/src/de/steamwar/bungeecore/commands/ReplayCommand.java b/src/de/steamwar/bungeecore/commands/ReplayCommand.java index 806df5a3..9ff3876f 100644 --- a/src/de/steamwar/bungeecore/commands/ReplayCommand.java +++ b/src/de/steamwar/bungeecore/commands/ReplayCommand.java @@ -1,8 +1,25 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2022 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package de.steamwar.bungeecore.commands; -import de.steamwar.bungeecore.ArenaMode; -import de.steamwar.bungeecore.Message; -import de.steamwar.bungeecore.SubserverSystem; +import de.steamwar.bungeecore.*; import de.steamwar.bungeecore.inventory.SWItem; import de.steamwar.bungeecore.inventory.SWListInv; import de.steamwar.bungeecore.inventory.SWStreamInv; @@ -29,13 +46,17 @@ public class ReplayCommand extends BasicCommand { ProxiedPlayer player = (ProxiedPlayer) sender; new SWStreamInv<>(player, Message.parse("REPLAY_TITLE", player), (click, fight) -> { - if(!fight.isReplayAvailable()) { - Message.send("REPLAY_UNAVAILABLE", player); - return; - } + SteamwarUser user = SteamwarUser.get(player.getUniqueId()); ArenaMode mode = fight.getGameMode(); + ServerStarter starter = new ServerStarter().replay(fight.getFightID()).blueLeader(player); - SubserverSystem.startArena(mode, mode.getRandomMap(), 0, 0, 0, fight.getFightID(), null, null, null, null).sendPlayer(player); + if (user.getUserGroup().isAdminGroup() && click.isShiftClick() && fight.replayExists()) { + starter.test(mode, mode.getRandomMap(), player).start(); + } else if(!fight.replayAllowed()) { + Message.send("REPLAY_UNAVAILABLE", player); + } else { + starter.arena(mode, mode.getRandomMap()).start(); + } }, page -> Fight.getPage(page, 45).stream().map(fight -> new SWListInv.SWListEntry<>(getFightItem(player, fight), fight)).collect(Collectors.toList())).open(); } @@ -48,11 +69,11 @@ public class ReplayCommand extends BasicCommand { lore.add(Message.parse("REPLAY_TIME", player, fight.getStartTime())); lore.add(""); lore.add(Message.parse("REPLAY_SERVER", player, fight.getServer())); - if(!fight.isReplayAvailable()) + if(!fight.replayAllowed()) lore.add(Message.parse("REPLAY_UNAVAILABLE", player)); item.setLore(lore); - if(fight.isReplayAvailable()) + if(fight.replayAllowed()) item.setEnchanted(true); return item; diff --git a/src/de/steamwar/bungeecore/commands/TpCommand.java b/src/de/steamwar/bungeecore/commands/TpCommand.java index ae2f9a99..c3a81b25 100644 --- a/src/de/steamwar/bungeecore/commands/TpCommand.java +++ b/src/de/steamwar/bungeecore/commands/TpCommand.java @@ -118,12 +118,12 @@ public class TpCommand extends BasicCommand { if(subserver != null) { Servertype type = subserver.getType(); if (type == Servertype.ARENA) { - subserver.sendPlayer(player); + SubserverSystem.sendPlayer(subserver, player); } else if (type == Servertype.BAUSERVER) { Bauserver bauserver = (Bauserver) subserver; if (bauserver.getOwner().equals(player.getUniqueId()) || BauweltMember.getBauMember(bauserver.getOwner(), player.getUniqueId()) != null) { - subserver.sendPlayer(player); + SubserverSystem.sendPlayer(subserver, player); } else { SubserverSystem.sendDeniedMessage(player, bauserver.getOwner()); Message.send("JOIN_PLAYER_BLOCK", player); diff --git a/src/de/steamwar/bungeecore/commands/TutorialCommand.java b/src/de/steamwar/bungeecore/commands/TutorialCommand.java new file mode 100644 index 00000000..4f557426 --- /dev/null +++ b/src/de/steamwar/bungeecore/commands/TutorialCommand.java @@ -0,0 +1,176 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2022 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bungeecore.commands; + +import de.steamwar.bungeecore.*; +import de.steamwar.bungeecore.inventory.SWInventory; +import de.steamwar.bungeecore.inventory.SWItem; +import de.steamwar.bungeecore.inventory.SWListInv; +import de.steamwar.bungeecore.inventory.SWStreamInv; +import de.steamwar.bungeecore.sql.SteamwarUser; +import de.steamwar.bungeecore.sql.Tutorial; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.connection.ProxiedPlayer; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.stream.Collectors; + +public class TutorialCommand extends BasicCommand { + + public TutorialCommand() { + super("tutorial", null); + } + + @Override + public void execute(CommandSender sender, String[] args) { + if(!(sender instanceof ProxiedPlayer)) + return; + ProxiedPlayer player = (ProxiedPlayer) sender; + + if(args.length == 0) { + openInventory(player, true, false); + return; + } + + switch(args[0].toLowerCase()) { + case "rate": + if(args.length < 2) { + player.chat("/tutorial rate"); + return; + } + + int id; + try { + id = Integer.parseInt(args[1]); + } catch (NumberFormatException e) { + BungeeCore.get().getLogger().log(Level.SEVERE, "rate executed with non number: " + args[1]); + return; + } + + Tutorial tutorial = Tutorial.get(id); + if(tutorial == null) { + BungeeCore.get().getLogger().log(Level.SEVERE, "rate executed with nonexistent id: " + id); + return; + } + + rate(player, tutorial); + break; + case "create": + if(args.length < 3) { + Message.send("TUTORIAL_CREATE_HELP", player); + return; + } + + create(player, Arrays.stream(args).skip(2).collect(Collectors.joining(" ")), args[1].toUpperCase()); + break; + case "own": + openInventory(player, false, true); + break; + case "unreleased": + if (SteamwarUser.get(player.getUniqueId()).getUserGroup().isTeamGroup()) { + openInventory(player, false, false); + break; + } + default: + Message.send("TUTORIAL_CREATE_HELP", player); + Message.send("TUTORIAL_OWN_HELP", player); + } + } + + private void openInventory(ProxiedPlayer player, boolean released, boolean own) { + SteamwarUser user = SteamwarUser.get(player.getUniqueId()); + + new SWStreamInv<>( + player, + Message.parse("TUTORIAL_TITLE", player), + (click, tutorial) -> { + if(!released && click.isShiftClick() && user.getUserGroup().isTeamGroup() && user != tutorial.creator()) { + tutorial.release(); + openInventory(player, released, own); + return; + } else if(own && click.isShiftClick() && click.isRightClick()) { + tutorial.delete(); + openInventory(player, released, own); + return; + } + + new ServerStarter().tutorial(player, tutorial).start(); + }, + page -> (own ? Tutorial.getOwn(user, page, 45) : Tutorial.getPage(page, 45, released)).stream().map(tutorial -> new SWListInv.SWListEntry<>(getTutorialItem(player, tutorial, own), tutorial)).collect(Collectors.toList()) + ).open(); + } + + private SWItem getTutorialItem(ProxiedPlayer player, Tutorial tutorial, boolean personalHighlights) { + SWItem item = new SWItem(tutorial.item(), Message.parse("TUTORIAL_NAME", player, tutorial.name())); + item.setHideAttributes(true); + + List lore = new ArrayList<>(); + lore.add(Message.parse("TUTORIAL_BY", player, tutorial.creator().getUserName())); + lore.add(Message.parse("TUTORIAL_STARS", player, String.format("%.1f", tutorial.stars()))); + + if (personalHighlights) + lore.add(Message.parse("TUTORIAL_DELETE", player)); + + item.setLore(lore); + + if (personalHighlights && tutorial.released()) + item.setEnchanted(true); + + return item; + } + + private void rate(ProxiedPlayer player, Tutorial tutorial) { + SteamwarUser user = SteamwarUser.get(player.getUniqueId()); + int[] rates = new int[]{1, 2, 3, 4, 5}; + + new SWListInv<>(player, Message.parse("TUTORIAL_RATE_TITLE", player), Arrays.stream(rates).mapToObj(rate -> new SWListInv.SWListEntry<>(new SWItem("NETHER_STAR", Message.parse("TUTORIAL_RATE", player, rate)), rate)).collect(Collectors.toList()), (click, rate) -> { + tutorial.rate(user, rate); + SWInventory.close(player); + }).open(); + } + + private void create(ProxiedPlayer player, String name, String item) { + Subserver subserver = Subserver.getSubserver(player); + SteamwarUser user = SteamwarUser.get(player.getUniqueId()); + File tempWorld = new File(ServerStarter.TEMP_WORLD_PATH, ServerStarter.serverToWorldName(ServerStarter.bauServerName(user))); + + if(subserver == null || !subserver.hasStarted() || subserver.getType() != Servertype.BAUSERVER || !tempWorld.exists()) { + Message.send("TUTORIAL_CREATE_MISSING", player); + return; + } + + subserver.execute("save-all"); + ProxyServer.getInstance().getScheduler().schedule(BungeeCore.get(), () -> { + Tutorial tutorial = Tutorial.create(user, name, item); + File tutorialWorld = tutorial.world(); + + if (tutorialWorld.exists()) + SubserverSystem.deleteFolder(Node.local, tutorialWorld.getPath()); + ServerStarter.copyWorld(Node.local, tempWorld.getPath(), tutorialWorld.getPath()); + Message.send("TUTORIAL_CREATED", player); + }, 1, TimeUnit.SECONDS); + } +} diff --git a/src/de/steamwar/bungeecore/comms/PacketIdManager.java b/src/de/steamwar/bungeecore/comms/PacketIdManager.java index 1ee0c27a..8689fc6a 100644 --- a/src/de/steamwar/bungeecore/comms/PacketIdManager.java +++ b/src/de/steamwar/bungeecore/comms/PacketIdManager.java @@ -37,4 +37,5 @@ public class PacketIdManager { public static final byte I_AM_A_LOBBY = 0x20; public static final byte FIGHT_INFO = 0x21; public static final byte FIGHT_ENDS = 0x22; + public static final byte STARTING_SERVER = 0x23; } diff --git a/src/de/steamwar/bungeecore/comms/handlers/FightEndsHandler.java b/src/de/steamwar/bungeecore/comms/handlers/FightEndsHandler.java index 1592fccc..0dda622d 100644 --- a/src/de/steamwar/bungeecore/comms/handlers/FightEndsHandler.java +++ b/src/de/steamwar/bungeecore/comms/handlers/FightEndsHandler.java @@ -20,15 +20,71 @@ package de.steamwar.bungeecore.comms.handlers; import com.google.common.io.ByteArrayDataInput; +import de.steamwar.bungeecore.ArenaMode; import de.steamwar.bungeecore.comms.SpigotHandler; import de.steamwar.bungeecore.comms.packets.FightEndsPacket; +import de.steamwar.bungeecore.sql.SchemElo; +import de.steamwar.bungeecore.sql.SchematicNode; +import de.steamwar.bungeecore.sql.SchematicType; +import de.steamwar.bungeecore.sql.UserElo; import net.md_5.bungee.api.config.ServerInfo; +import java.util.List; + public class FightEndsHandler implements SpigotHandler { + private int K = 20; + @Override public void handle(ByteArrayDataInput in, ServerInfo info) { FightEndsPacket fightEndsPacket = new FightEndsPacket(in); + if (!ArenaMode.getBySchemType(SchematicType.fromDB(fightEndsPacket.getGameMode())).isRanked()) { + return; + } + + int bluePlayerSize = fightEndsPacket.getBluePlayers().size(); + int redPlayerSize = fightEndsPacket.getRedPlayers().size(); + double playerRatio = bluePlayerSize > redPlayerSize ? (double) redPlayerSize / bluePlayerSize : (double) bluePlayerSize / redPlayerSize; + if (playerRatio < 0.6) { + return; + } + + boolean bluePublic = SchematicNode.getSchematicNode(fightEndsPacket.getBlueSchem()).getOwner() == 0; + boolean redPublic = SchematicNode.getSchematicNode(fightEndsPacket.getRedSchem()).getOwner() == 0; + + if (bluePublic ^ redPublic) { + return; + } + + double blueResult; + if (fightEndsPacket.getWin() == 0) { + blueResult = 0.5; + } else if (fightEndsPacket.getWin() == 1) { + blueResult = 1; + } else { + blueResult = 0; + } + + int blueSchemElo = SchemElo.getElo(fightEndsPacket.getBlueSchem()); + int redSchemElo = SchemElo.getElo(fightEndsPacket.getRedSchem()); + + int blueTeamElo = fightEndsPacket.getBluePlayers().stream().mapToInt(player -> UserElo.getEloOrDefault(player, fightEndsPacket.getGameMode())).sum(); + int redTeamElo = fightEndsPacket.getRedPlayers().stream().mapToInt(player -> UserElo.getEloOrDefault(player, fightEndsPacket.getGameMode())).sum(); + + calculateEloOfTeam(fightEndsPacket.getBlueSchem(), blueSchemElo, redSchemElo, blueTeamElo, redTeamElo, blueResult, fightEndsPacket.getBluePlayers(), fightEndsPacket.getGameMode(), bluePublic || redPublic); + calculateEloOfTeam(fightEndsPacket.getRedSchem(), redSchemElo, blueSchemElo, redTeamElo, blueTeamElo, 1 - blueResult, fightEndsPacket.getRedPlayers(), fightEndsPacket.getGameMode(), bluePublic || redPublic); + } + + private void calculateEloOfTeam(int schemId, int eloSchemOwn, int eloSchemEnemy, int eloTeamOwn, int eloTeamEnemy, double result, List players, String gameMode, boolean noPlayerRank) { + double winSchemExpectation = 1 / (1 + Math.pow(10, (eloSchemEnemy - eloSchemOwn) / 600f)); + SchemElo.setElo(schemId, (int) Math.round(eloSchemOwn + K * (result - winSchemExpectation))); + + if (noPlayerRank) return; + double winTeamExpectation = 1 / (1 + Math.pow(10, (eloTeamEnemy - eloTeamOwn) / 600f)); + for (int player : players) { + int playerElo = UserElo.getEloOrDefault(player, gameMode); + UserElo.setElo(player, gameMode, (int) Math.round(playerElo + K * (result - winTeamExpectation))); + } } } diff --git a/src/de/steamwar/bungeecore/comms/handlers/FightInfoHandler.java b/src/de/steamwar/bungeecore/comms/handlers/FightInfoHandler.java index 998e53e3..eb708dfb 100644 --- a/src/de/steamwar/bungeecore/comms/handlers/FightInfoHandler.java +++ b/src/de/steamwar/bungeecore/comms/handlers/FightInfoHandler.java @@ -38,6 +38,10 @@ public class FightInfoHandler implements SpigotHandler { lobbys.add(lobby); } + public static boolean onLobby(ProxiedPlayer player) { + return lobbys.contains(player.getServer().getInfo()); + } + @Override public void handle(ByteArrayDataInput in, ServerInfo info) { FightInfoPacket packet = new FightInfoPacket(in); diff --git a/src/de/steamwar/bungeecore/comms/handlers/PrepareSchemHandler.java b/src/de/steamwar/bungeecore/comms/handlers/PrepareSchemHandler.java index 14171c57..d9681781 100644 --- a/src/de/steamwar/bungeecore/comms/handlers/PrepareSchemHandler.java +++ b/src/de/steamwar/bungeecore/comms/handlers/PrepareSchemHandler.java @@ -21,8 +21,7 @@ package de.steamwar.bungeecore.comms.handlers; import com.google.common.io.ByteArrayDataInput; import de.steamwar.bungeecore.ArenaMode; -import de.steamwar.bungeecore.SubserverSystem; -import de.steamwar.bungeecore.commands.BauCommand; +import de.steamwar.bungeecore.ServerStarter; import de.steamwar.bungeecore.comms.SpigotHandler; import de.steamwar.bungeecore.sql.SchematicType; import de.steamwar.bungeecore.sql.SteamwarUser; @@ -37,7 +36,6 @@ public class PrepareSchemHandler implements SpigotHandler { int schematicID = in.readInt(); ArenaMode mode = ArenaMode.getBySchemType(SchematicType.fromDB(in.readUTF())); - BauCommand.stopBauserver(player); - SubserverSystem.startTestServer(player, mode, mode.getRandomMap(), 0, schematicID); + new ServerStarter().test(mode, mode.getRandomMap(), player).prepare(schematicID).start(); } } diff --git a/src/de/steamwar/bungeecore/comms/packets/StartingServerPacket.java b/src/de/steamwar/bungeecore/comms/packets/StartingServerPacket.java new file mode 100644 index 00000000..1e5edf15 --- /dev/null +++ b/src/de/steamwar/bungeecore/comms/packets/StartingServerPacket.java @@ -0,0 +1,25 @@ +package de.steamwar.bungeecore.comms.packets; + +import com.google.common.io.ByteArrayDataOutput; +import de.steamwar.bungeecore.comms.BungeePacket; +import de.steamwar.bungeecore.comms.PacketIdManager; +import de.steamwar.bungeecore.sql.SteamwarUser; + +public class StartingServerPacket extends BungeePacket { + + private final int user; + + public StartingServerPacket(SteamwarUser user) { + this.user = user.getId(); + } + + @Override + public int getId() { + return PacketIdManager.STARTING_SERVER; + } + + @Override + public void writeVars(ByteArrayDataOutput out) { + out.writeInt(user); + } +} diff --git a/src/de/steamwar/bungeecore/inventory/SWInventory.java b/src/de/steamwar/bungeecore/inventory/SWInventory.java index 31cb89ed..b827e884 100644 --- a/src/de/steamwar/bungeecore/inventory/SWInventory.java +++ b/src/de/steamwar/bungeecore/inventory/SWInventory.java @@ -116,6 +116,10 @@ public class SWInventory { } public void close() { + close(player); + } + + public static void close(ProxiedPlayer player) { new CloseInventoryPacket(SteamwarUser.get(player).getId()).send(player); } diff --git a/src/de/steamwar/bungeecore/listeners/ChatListener.java b/src/de/steamwar/bungeecore/listeners/ChatListener.java index 96c628a4..54b9bb4d 100644 --- a/src/de/steamwar/bungeecore/listeners/ChatListener.java +++ b/src/de/steamwar/bungeecore/listeners/ChatListener.java @@ -23,10 +23,7 @@ import de.steamwar.bungeecore.*; import de.steamwar.bungeecore.bot.SteamwarDiscordBot; import de.steamwar.bungeecore.commands.TpCommand; import de.steamwar.bungeecore.comms.packets.PingPacket; -import de.steamwar.bungeecore.sql.Punishment; -import de.steamwar.bungeecore.sql.SteamwarUser; -import de.steamwar.bungeecore.sql.Team; -import de.steamwar.bungeecore.sql.UserGroup; +import de.steamwar.bungeecore.sql.*; import net.md_5.bungee.api.*; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.connection.ProxiedPlayer; @@ -200,7 +197,7 @@ public class ChatListener extends BasicListener { } private void publicChat(SteamwarUser user, ProxiedPlayer sender, String message){ - String name = sender.getDisplayName(); + String name = UserElo.getEmblem(user) + sender.getDisplayName(); String chatcolor = user.getUserGroup().getChatColorCode(); if(user.getUserGroup() != UserGroup.Member || user.getTeam() == 12 || user.getTeam() == 285 || user.getTeam() == 54) diff --git a/src/de/steamwar/bungeecore/listeners/ConnectionListener.java b/src/de/steamwar/bungeecore/listeners/ConnectionListener.java index 65855b39..95f6a89c 100644 --- a/src/de/steamwar/bungeecore/listeners/ConnectionListener.java +++ b/src/de/steamwar/bungeecore/listeners/ConnectionListener.java @@ -76,10 +76,10 @@ public class ConnectionListener extends BasicListener { if(user.getUserGroup() != UserGroup.Member) { player.setPermission(YOUTUBER_MODS, true); - player.setDisplayName(user.getUserGroup().getColorCode() + user.getUserGroup().name() + " " + player.getName() + "§r"); + player.setDisplayName(user.getUserGroup().getColorCode() + user.getUserGroup().getChatPrefix() + " " + player.getName() + "§r"); player.setPermission("bungeecore.group." + user.getUserGroup().name().toLowerCase(), true); }else { - player.setDisplayName(player.getName()); + player.setDisplayName("§f" + player.getName()); } for(Subserver subserver : Subserver.getServerList()){ diff --git a/src/de/steamwar/bungeecore/sql/Elo.java b/src/de/steamwar/bungeecore/sql/Elo.java deleted file mode 100644 index da1db904..00000000 --- a/src/de/steamwar/bungeecore/sql/Elo.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - This file is a part of the SteamWar software. - - Copyright (C) 2020 SteamWar.de-Serverteam - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -package de.steamwar.bungeecore.sql; - -public class Elo { - - private static final Statement elo = new Statement("SELECT Elo FROM Elo WHERE UserID = ? AND GameMode = ? AND Season = ?"); - private static final Statement place = new Statement("SELECT COUNT(*) AS Place FROM Elo WHERE GameMode = ? AND Elo > ? AND Season = ?"); - - private Elo(){} - - public static int getElo(int userID, String gameMode){ - return getElo(Season.getSeason(), userID, gameMode); - } - - public static int getElo(int season, int userID, String gameMode){ - return elo.select(rs -> { - if(rs.next()) - return rs.getInt("Elo"); - return 1000; - }, userID, gameMode, season); - } - - public static int getPlacement(int elo, String gameMode){ - return getPlacement(Season.getSeason(), elo, gameMode); - } - - public static int getPlacement(int season, int elo, String gameMode){ - return place.select(rs -> { - if(rs.next()) - return rs.getInt("Place"); - return -1; - }, gameMode, elo, season); - } -} diff --git a/src/de/steamwar/bungeecore/sql/Fight.java b/src/de/steamwar/bungeecore/sql/Fight.java index 77ceefde..514946d6 100644 --- a/src/de/steamwar/bungeecore/sql/Fight.java +++ b/src/de/steamwar/bungeecore/sql/Fight.java @@ -5,30 +5,25 @@ import de.steamwar.bungeecore.ArenaMode; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; -import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; public class Fight { - private static final Statement getPage = new Statement("SELECT FightID, GameMode, Server, StartTime, Duration, BlueLeader, RedLeader, BlueSchem, RedSchem, Win, WinCondition, ReplayLock, Replay is not NULL AS ReplayAvailable FROM Fight ORDER BY FightID DESC LIMIT ?, ?"); + private static final Statement getPage = new Statement("SELECT f.FightID, f.GameMode, f.Server, f.StartTime, f.BlueLeader, f.RedLeader, (b.NodeId IS NULL OR b.AllowReplay) AND (r.NodeId IS NULL OR r.AllowReplay) AS ReplayAllowed, f.Win, f.Replay IS NOT NULL AS ReplayExists FROM Fight f LEFT OUTER JOIN SchematicNode b ON f.BlueSchem = b.NodeId LEFT OUTER JOIN SchematicNode r ON f.RedSchem = r.NodeId ORDER BY FightID DESC LIMIT ?, ?"); private final int fightID; private final String gameMode; private final String server; private final Timestamp startTime; - private final int duration; private final int blueLeader; private final int redLeader; - private final int blueSchem; - private final int redSchem; private final int win; - private final String winCondition; - private final Timestamp replayLock; - private final boolean replayAvailable; + private final boolean replayExists; + private final boolean replayAllowed; private final List bluePlayers = new ArrayList<>(); private final List redPlayers = new ArrayList<>(); @@ -38,15 +33,11 @@ public class Fight { gameMode = rs.getString("GameMode"); server = rs.getString("Server"); startTime = rs.getTimestamp("StartTime"); - duration = rs.getInt("Duration"); blueLeader = rs.getInt("BlueLeader"); redLeader = rs.getInt("RedLeader"); - blueSchem = rs.getInt("BlueSchem"); - redSchem = rs.getInt("RedSchem"); + replayAllowed = rs.getBoolean("ReplayAllowed"); win = rs.getInt("Win"); - winCondition = rs.getString("WinCondition"); - replayLock = rs.getTimestamp("ReplayLock"); - replayAvailable = rs.getBoolean("ReplayAvailable"); + replayExists = rs.getBoolean("ReplayExists"); } private void initPlayers(List fightPlayers) { @@ -122,7 +113,11 @@ public class Fight { return redPlayers; } - public boolean isReplayAvailable() { - return replayAvailable && replayLock.before(Timestamp.from(Instant.now())) && getGameMode() != null; + public boolean replayAllowed() { + return replayExists() && replayAllowed; + } + + public boolean replayExists() { + return replayExists && getGameMode() != null; } } diff --git a/src/de/steamwar/bungeecore/sql/SchemElo.java b/src/de/steamwar/bungeecore/sql/SchemElo.java new file mode 100644 index 00000000..55df604e --- /dev/null +++ b/src/de/steamwar/bungeecore/sql/SchemElo.java @@ -0,0 +1,49 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bungeecore.sql; + +import static de.steamwar.bungeecore.sql.UserElo.ELO_DEFAULT; + +public class SchemElo { + private SchemElo() {} + + private static final Statement elo = new Statement("SELECT Elo FROM SchemElo WHERE SchemID = ? AND Season = ?"); + private static final Statement setElo = new Statement("INSERT INTO SchemElo (Season, SchemID, Elo) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE Elo = VALUES(Elo)"); + + public static int getElo(int schemID) { + return getElo(Season.getSeason(), schemID); + } + + public static int getElo(int season, int schemID) { + return elo.select(rs -> { + if (rs.next()) + return rs.getInt("Elo"); + return ELO_DEFAULT; + }, schemID, season); + } + + public static void setElo(int schemID, int elo) { + setElo(Season.getSeason(), schemID, elo); + } + + public static void setElo(int season, int schemID, int elo) { + setElo.update(season, schemID, elo); + } +} diff --git a/src/de/steamwar/bungeecore/sql/Season.java b/src/de/steamwar/bungeecore/sql/Season.java index b1d6bdc4..86dd01c7 100644 --- a/src/de/steamwar/bungeecore/sql/Season.java +++ b/src/de/steamwar/bungeecore/sql/Season.java @@ -30,6 +30,11 @@ public class Season { return (calendar.get(Calendar.YEAR) * 3 + yearIndex); } + public static String getSeasonStart() { + Calendar calendar = Calendar.getInstance(); + return calendar.get(Calendar.YEAR) + "-" + (calendar.get(Calendar.MONTH) / 3 * 3 + 1) + "-1"; + } + public static String convertSeasonToString(int season){ if (season == -1) return ""; int yearSeason = season % 3; diff --git a/src/de/steamwar/bungeecore/sql/Tutorial.java b/src/de/steamwar/bungeecore/sql/Tutorial.java new file mode 100644 index 00000000..75916f2e --- /dev/null +++ b/src/de/steamwar/bungeecore/sql/Tutorial.java @@ -0,0 +1,137 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2022 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bungeecore.sql; + +import de.steamwar.bungeecore.Node; +import de.steamwar.bungeecore.ServerStarter; +import de.steamwar.bungeecore.SubserverSystem; + +import java.io.File; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class Tutorial { + + private static final Statement by_popularity = new Statement("SELECT t.*, AVG(r.Stars) AS Stars FROM Tutorial t LEFT OUTER JOIN TutorialRating r ON t.TutorialID = r.TutorialID WHERE t.Released = ? GROUP BY t.TutorialID ORDER BY SUM(r.Stars) DESC LIMIT ?, ?"); + private static final Statement own = new Statement("SELECT t.*, AVG(r.Stars) AS Stars FROM Tutorial t LEFT OUTER JOIN TutorialRating r ON t.TutorialID = r.TutorialID WHERE t.Creator = ? GROUP BY t.TutorialID ORDER BY t.TutorialID ASC LIMIT ?, ?"); + private static final Statement by_creator_name = new Statement("SELECT t.*, AVG(r.Stars) AS Stars FROM Tutorial t LEFT OUTER JOIN TutorialRating r ON t.TutorialID = r.TutorialID WHERE t.Creator = ? AND t.Name = ? GROUP BY t.TutorialID"); + private static final Statement by_id = new Statement("SELECT t.*, AVG(r.Stars) AS Stars FROM Tutorial t LEFT OUTER JOIN TutorialRating r ON t.TutorialID = r.TutorialID WHERE t.TutorialID = ? GROUP BY t.TutorialID"); + private static final Statement rate = new Statement("INSERT INTO TutorialRating (TutorialID, UserID, Stars) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE Stars = VALUES(Stars)"); + private static final Statement create = new Statement("INSERT INTO Tutorial (Creator, Name, Item) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE Item = VALUES(Item), Released = 0"); + private static final Statement release = new Statement("UPDATE Tutorial SET Released = 1 WHERE TutorialID = ?"); + private static final Statement delete = new Statement("DELETE FROM Tutorial WHERE TutorialID = ?"); + + public static List getPage(int page, int elementsPerPage, boolean released) { + List tutorials = by_popularity.select(rs -> { + List t = new ArrayList<>(); + while(rs.next()) + t.add(new Tutorial(rs)); + return t; + }, released, page * elementsPerPage, elementsPerPage); + + SteamwarUser.batchCache(tutorials.stream().map(tutorial -> tutorial.creator).collect(Collectors.toSet())); + return tutorials; + } + + public static List getOwn(SteamwarUser user, int page, int elementsPerPage) { + return own.select(rs -> { + List t = new ArrayList<>(); + while(rs.next()) + t.add(new Tutorial(rs)); + return t; + }, user.getId(), page * elementsPerPage, elementsPerPage); + } + + public static Tutorial create(SteamwarUser creator, String name, String item) { + create.update(creator.getId(), name, item); + return by_creator_name.select(rs -> { + rs.next(); + return new Tutorial(rs); + }, creator.getId(), name); + } + + public static Tutorial get(int id) { + return by_id.select(rs -> { + if(rs.next()) + return new Tutorial(rs); + return null; + }, id); + } + + private final int id; + private final int creator; + private final String name; + private final String item; + private final double stars; + private final boolean released; + + public Tutorial(ResultSet rs) throws SQLException { + this.id = rs.getInt("TutorialID"); + this.creator = rs.getInt("Creator"); + this.name = rs.getString("Name"); + this.item = rs.getString("Item"); + this.released = rs.getBoolean("Released"); + this.stars = rs.getDouble("Stars"); + } + + public SteamwarUser creator() { + return SteamwarUser.get(creator); + } + + public double stars() { + return stars; + } + + public int id() { + return id; + } + + public String name() { + return name; + } + + public String item() { + return item; + } + + public boolean released() { + return released; + } + + public void release() { + release.update(id); + } + + public void delete() { + delete.update(id); + SubserverSystem.deleteFolder(Node.local, world().getPath()); + } + + public File world() { + return new File(ServerStarter.TUTORIAL_PATH, String.valueOf(id)); + } + + public void rate(SteamwarUser user, int rating) { + rate.update(id, user.getId(), rating); + } +} diff --git a/src/de/steamwar/bungeecore/sql/UserElo.java b/src/de/steamwar/bungeecore/sql/UserElo.java new file mode 100644 index 00000000..ca7e7237 --- /dev/null +++ b/src/de/steamwar/bungeecore/sql/UserElo.java @@ -0,0 +1,175 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bungeecore.sql; + +import de.steamwar.bungeecore.ArenaMode; +import de.steamwar.bungeecore.Message; +import net.md_5.bungee.api.connection.ProxiedPlayer; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public class UserElo { + private UserElo() {} + + public static final int ELO_DEFAULT = 1000; + + private static final Map>> gameModeUserEloCache = new HashMap<>(); + private static final Map maxEloCache = new HashMap<>(); + private static final Map emblemCache = new HashMap<>(); + + private static final Statement elo = new Statement("SELECT Elo FROM UserElo WHERE UserID = ? AND GameMode = ? AND Season = ?"); + private static final Statement setElo = new Statement("INSERT INTO UserElo (Season, GameMode, UserID, Elo) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE Elo = VALUES(Elo)"); + private static final Statement maxElo = new Statement("SELECT MAX(Elo) AS MaxElo FROM UserElo WHERE Season = ? AND GameMode = ?"); + + private static final Statement place = new Statement("SELECT COUNT(*) AS Place FROM UserElo WHERE GameMode = ? AND Elo > ? AND Season = ?"); + private static final Statement fightsOfSeason = new Statement("SELECT COUNT(*) AS Fights FROM FightPlayer INNER JOIN Fight F on FightPlayer.FightID = F.FightID WHERE UserID = ? AND GameMode = ? AND UNIX_TIMESTAMP(StartTime) + Duration >= UNIX_TIMESTAMP(?)"); + + public static int getEloOrDefault(int userID, String gameMode) { + return getElo(userID, gameMode).orElse(ELO_DEFAULT); + } + + public static Optional getElo(int userID, String gameMode) { + return gameModeUserEloCache.computeIfAbsent(gameMode, gm -> new HashMap<>()).computeIfAbsent(userID, uid -> elo.select(rs -> { + if (rs.next()) + return Optional.of(rs.getInt("Elo")); + return Optional.empty(); + }, userID, gameMode, Season.getSeason())); + } + + private static int getFightsOfSeason(int userID, String gameMode) { + return fightsOfSeason.select(rs -> { + if (rs.next()) + return rs.getInt("Fights"); + return 0; + }, userID, gameMode, Season.getSeasonStart()); + } + + private static int getMaxElo(String gameMode) { + return maxEloCache.computeIfAbsent(gameMode, gm -> maxElo.select(rs -> { + if (rs.next()) + return rs.getInt("MaxElo"); + return 0; + }, Season.getSeason(), gameMode)); + } + + public static void setElo(int userId, String gameMode, int elo) { + emblemCache.remove(userId); + + Optional oldElo = Optional.ofNullable(gameModeUserEloCache.computeIfAbsent(gameMode, gm -> new HashMap<>()).put(userId, Optional.of(elo))).orElse(Optional.empty()); + int maxElo = getMaxElo(gameMode); + if (elo > maxElo || (oldElo.isPresent() && oldElo.get() == maxElo)) { + maxEloCache.remove(gameMode); + emblemCache.clear(); + } + + setElo.update(Season.getSeason(), gameMode, userId, elo); + } + + public static int getPlacement(int elo, String gameMode) { + return place.select(rs -> { + if (rs.next()) + return rs.getInt("Place") + 1; + return -1; + }, gameMode, elo, Season.getSeason()); + } + + public static String getEmblem(SteamwarUser user) { + return emblemCache.computeIfAbsent(user.getId(), userId -> { + switch( + ArenaMode.getAllModes().stream().filter( + ArenaMode::isRanked + ).filter( + mode -> UserElo.getFightsOfSeason(user.getId(), mode.getSchemType()) >= 10 + ).map( + mode -> getProgression(user.getId(), mode.getSchemType()) + ).max(Integer::compareTo).orElse(0) + ) { + case 0: + return ""; + case 1: + return "§7✧ "; + case 2: + return "§f✦ "; + case 3: + return "§e✶ "; + case 4: + return "§a✷ "; + case 5: + return "§b✸ "; + case 6: + return "§c✹ "; + case 7: + return "§5❂ "; + default: + throw new SecurityException("Progression out of range"); + } + }); + } + + public static String getEmblemProgression(ProxiedPlayer player, String gameMode, int userId) { + int fightsOfSeason = getFightsOfSeason(userId, gameMode); + if (fightsOfSeason < 10) + return Message.parse("RANK_NEEDED_FIGHTS_LEFT", player, "§8✧ ✦ ✶ ✷ ✸ ✹ ❂", 10 - fightsOfSeason); + + switch (getProgression(userId, gameMode)) { + case 0: + return "§8✧ ✦ ✶ ✷ ✸ ✹ ❂"; + case 1: + return "§7✧ §8✦ ✶ ✷ ✸ ✹ ❂"; + case 2: + return "§8✧ §f✦ §8✶ ✷ ✸ ✹ ❂"; + case 3: + return "§8✧ ✦ §e✶ §8✷ ✸ ✹ ❂"; + case 4: + return "§8✧ ✦ ✶ §a✷ §8✸ ✹ ❂"; + case 5: + return "§8✧ ✦ ✶ ✷ §b✸ §8✹ ❂"; + case 6: + return "§8✧ ✦ ✶ ✷ ✸ §c✹ §8❂"; + case 7: + return "§8✧ ✦ ✶ ✷ ✸ ✹ §5❂"; + default: + throw new SecurityException("Progression is not in range"); + } + } + + private static int getProgression(int userId, String gameMode) { + int elo = UserElo.getElo(userId, gameMode).orElse(0); + if(elo == 0) + return 0; + int maxElo = UserElo.getMaxElo(gameMode); + + if (elo > maxElo * 0.99) return 7; + if (elo > maxElo * 0.97) return 6; + if (elo > maxElo * 0.94) return 5; + if (elo > maxElo * 0.88) return 4; + if (elo > maxElo * 0.76) return 3; + if (elo > maxElo * 0.51) return 2; + return 1; + } + + public static void clearCache() { + gameModeUserEloCache.clear(); + maxEloCache.clear(); + emblemCache.clear(); + } +} diff --git a/src/de/steamwar/bungeecore/sql/UserGroup.java b/src/de/steamwar/bungeecore/sql/UserGroup.java index e6c30f9a..6ca7a9ae 100644 --- a/src/de/steamwar/bungeecore/sql/UserGroup.java +++ b/src/de/steamwar/bungeecore/sql/UserGroup.java @@ -24,24 +24,26 @@ import java.util.stream.Collectors; public enum UserGroup { - Admin("§4", "§e", true, true, true, true), - Developer("§3", "§f", true, true, true, true), - Moderator("§c", "§f", true, true, true, true), - Supporter("§9", "§f", false, true, true, true), - Builder("§2", "§f", false, true, false, true), - YouTuber("§5", "§f", false, false, false, true), - Member("§7", "§7", false, false, false, false); + Admin("§4", "§e", "Admin", true, true, true, true), + Developer("§3", "§f", "Dev", true, true, true, true), + Moderator("§c", "§f", "Mod", true, true, true, true), + Supporter("§9", "§f", "Sup", false, true, true, true), + Builder("§2", "§f", "Arch", false, true, false, true), + YouTuber("§5", "§f", "YT", false, false, false, true), + Member("§7", "§7", "", false, false, false, false); private final String colorCode; private final String chatColorCode; + private final String chatPrefix; private final boolean adminGroup; private final boolean teamGroup; private final boolean checkSchematics; private final boolean privilegedMods; - UserGroup(String colorCode, String chatColorCode, boolean adminGroup, boolean teamGroup, boolean checkSchematics, boolean privilegedMods) { + UserGroup(String colorCode, String chatColorCode, String chatPrefix, boolean adminGroup, boolean teamGroup, boolean checkSchematics, boolean privilegedMods) { this.colorCode = colorCode; this.chatColorCode = chatColorCode; + this.chatPrefix = chatPrefix; this.adminGroup = adminGroup; this.teamGroup = teamGroup; this.checkSchematics = checkSchematics; @@ -75,4 +77,8 @@ public enum UserGroup { public static UserGroup getUsergroup(String name) { return Arrays.stream(UserGroup.values()).filter(userGroup -> userGroup.name().equalsIgnoreCase(name)).collect(Collectors.toList()).get(0); } + + public String getChatPrefix() { + return chatPrefix; + } } \ No newline at end of file diff --git a/src/de/steamwar/messages/BungeeCore.properties b/src/de/steamwar/messages/BungeeCore.properties index c872b856..19226ef8 100644 --- a/src/de/steamwar/messages/BungeeCore.properties +++ b/src/de/steamwar/messages/BungeeCore.properties @@ -333,6 +333,19 @@ REPLAY_LOSER=§e{0} §7+§e{1} REPLAY_TIME=§7{0} REPLAY_SERVER=§7{0} +#TutorialCommand +TUTORIAL_TITLE=Tutorials +TUTORIAL_NAME=§e{0} +TUTORIAL_BY=§8von §7{0} +TUTORIAL_STARS=§e{0} §7Sterne +TUTORIAL_RATE_TITLE=Tutorial bewerten +TUTORIAL_RATE=§e{0} §7Stern(e) +TUTORIAL_DELETE=§cMit Shift+Rechtsklick löschen +TUTORIAL_CREATE_HELP=§8/§7tutorial create §8[§eMaterial§8] §8[§eName§8] +TUTORIAL_CREATE_MISSING=§cEin Tutorial kann nur von einem Tutorialserver aus erstellt werden! +TUTORIAL_CREATED=§7Das Tutorial wurde erstellt§8. +TUTORIAL_OWN_HELP=§8/§7tutorial own §8- §7Liste der eigenen Tutorials + #ServerTeamchatCommand STC_USAGE=§8/§7stc §8[§eNachricht an das Team§8] STC_FORMAT=§8STC §e{0}» §r{1} @@ -561,3 +574,12 @@ HOURS_PLAYED=§7Deine Spielzeit beträgt§8: §e{0}h #Arena command ARENA_NOT_FOUND=§cDie angegebene Arena konnte nicht gefunden werden + +#Rank +RANK_PLAYER_NOT_FOUND=§cSpieler nicht gefunden +RANK_PLAYER_FOUND=§eRang §7von §e{0} +RANK_HEADER=§7§lModus {0} +RANK_UNPLACED=§eunplatziert +RANK_PLACED=§e{0}§8. §7mit §e{1} §7Elo§8. +RANK_EMBLEM=§eEmblem§8: {0} +RANK_NEEDED_FIGHTS_LEFT={0} §8(§7noch §e{1}§7 Kämpfe nötig§8)