diff --git a/src/de/steamwar/bungeecore/ArenaMode.java b/src/de/steamwar/bungeecore/ArenaMode.java index c007a86f..4d5a8689 100644 --- a/src/de/steamwar/bungeecore/ArenaMode.java +++ b/src/de/steamwar/bungeecore/ArenaMode.java @@ -19,17 +19,18 @@ package de.steamwar.bungeecore; +import de.steamwar.bungeecore.sql.SchematicType; import net.md_5.bungee.config.Configuration; import java.util.*; public class ArenaMode { - private static Map byChat = new HashMap<>(); - private static Map byInternal = new HashMap<>(); - private static Map byCheckSchemType = new HashMap<>(); - private static List allModes = new LinkedList<>(); - private static Random random = new Random(); + private static final Map byChat = new HashMap<>(); + private static final Map byInternal = new HashMap<>(); + private static final Map bySchemType = new HashMap<>(); + private static final List allModes = new LinkedList<>(); + private static final Random random = new Random(); static void init(Configuration config){ for(String internalName : config.getKeys()){ @@ -63,32 +64,39 @@ public class ArenaMode { return chatNames; } - public static ArenaMode getByCheckSchemType(String checkSchemType){ - return byCheckSchemType.get(checkSchemType); + public static ArenaMode getBySchemType(SchematicType schemType){ + return bySchemType.get(schemType); } public static List getAllModes(){ return allModes; } - private final String internalName; private final String displayName; + private final String folder; private final List chatNames; private final String serverJar; + private final String config; 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; - this.displayName = config.getString("displayName"); + this.folder = config.getString("folder"); this.serverJar = config.getString("serverJar"); - this.chatNames = config.getStringList("chatNames"); + this.config = config.getString("config"); this.maps = config.getStringList("maps"); + this.displayName = config.getString("displayName", internalName); + if(config.contains("chatNames")) + this.chatNames = config.getStringList("chatNames"); + else + this.chatNames = Collections.emptyList(); this.historic = config.getBoolean("historic", false); + this.schemType = config.getString("schemType", "").toLowerCase(); + this.ranked = config.getBoolean("ranked", false); - this.schemType = config.getString("schemType", null); allModes.add(this); byInternal.put(internalName, this); @@ -96,12 +104,8 @@ public class ArenaMode { byChat.put(name.toLowerCase(), this); } - if(config.contains("checkSchemType")) - byCheckSchemType.put(config.getString("checkSchemType").toLowerCase(), this); - } - - public String getInternalName() { - return internalName; + if(!this.schemType.equals("")) + bySchemType.put(SchematicType.fromDB(this.schemType), this); } public String getDisplayName() { @@ -112,6 +116,10 @@ public class ArenaMode { return serverJar; } + public String getConfig(){ + return config; + } + public String hasMap(String map){ for(String m : maps){ if(m.equalsIgnoreCase(map)) @@ -120,6 +128,10 @@ public class ArenaMode { return null; } + public String getFolder() { + return folder; + } + public String getRandomMap(){ return maps.get(random.nextInt(maps.size())); } @@ -132,8 +144,8 @@ public class ArenaMode { return chatNames.get(0); } - public boolean hasChatName(){ - return !chatNames.isEmpty(); + public boolean withoutChatName(){ + return chatNames.isEmpty(); } public boolean isHistoric(){ diff --git a/src/de/steamwar/bungeecore/BungeeCore.java b/src/de/steamwar/bungeecore/BungeeCore.java index 23b5e2eb..f0a8bb79 100644 --- a/src/de/steamwar/bungeecore/BungeeCore.java +++ b/src/de/steamwar/bungeecore/BungeeCore.java @@ -84,6 +84,7 @@ public class BungeeCore extends Plugin { new CheckListener(); new ModLoaderBlocker(); new WorldDownloader(); + new BrandListener(); commands.put("/b", null); commands.put("/gs", null); @@ -107,6 +108,7 @@ public class BungeeCore extends Plugin { new DenyCommand("watchcat", "wc"); new TeamCommand(); new ServerTeamchatCommand(); + new DevCommand(); new EventCommand(); new EventreloadCommand(); new EventRescheduleCommand(); @@ -120,6 +122,8 @@ public class BungeeCore extends Plugin { new UnIgnoreCommand(); new PollresultCommand(); new ResourcereloadCommand(); + new ListCommand(); + new StatCommand(); if(!EVENT_MODE){ new WebregisterCommand(); @@ -263,6 +267,9 @@ public class BungeeCore extends Plugin { serverPermissions.get(serverName), cmds.toArray(new String[0]) ); + if(server.getBoolean("modchecked", false)) { + ModLoaderBlocker.addServer(serverName); + } } } diff --git a/src/de/steamwar/bungeecore/LoadEvaluation.java b/src/de/steamwar/bungeecore/LoadEvaluation.java new file mode 100644 index 00000000..59fda6c2 --- /dev/null +++ b/src/de/steamwar/bungeecore/LoadEvaluation.java @@ -0,0 +1,96 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2021 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; + +import java.io.*; + +public class LoadEvaluation { + private LoadEvaluation(){} + + private static final File meminfo = new File("/proc/meminfo"); + + public static double getRamPercentage() { + try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(meminfo)))) { + String memTotal = bufferedReader.readLine().replaceAll(" +", " "); + bufferedReader.readLine(); + String memAvailable = bufferedReader.readLine().replaceAll(" +", " "); + + long memTotalLong = getNumber(memTotal); + long memAvailableLong = getNumber(memAvailable); + return (memTotalLong - memAvailableLong) / (double) memTotalLong; + } catch (IOException e) { + return 1D; + } + } + + public static double getRemoteRamPercentage(String remote) { + try { + // Attention: + // memInfo.sh needs to contain: cat /proc/meminfo + Process process = new ProcessBuilder("ssh", remote, "\"./memInfo.sh\"").start(); + process.waitFor(); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String memTotal = bufferedReader.readLine().replaceAll(" +", " "); + bufferedReader.readLine(); + String memAvailable = bufferedReader.readLine().replaceAll(" +", " "); + + long memTotalLong = getNumber(memTotal); + long memAvailableLong = getNumber(memAvailable); + return (memTotalLong - memAvailableLong) / (double) memTotalLong; + } catch (IOException e) { + return 1D; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return 1D; + } + } + + public static double getCPULoad() { + try { + Process process = new ProcessBuilder("bash", "-c", "cat <(grep 'cpu ' /proc/stat) <(sleep 1 && grep 'cpu ' /proc/stat) | awk -v RS=\"\" '{printf \"%.2f\\n\", ($13-$2+$15-$4)*100/($13-$2+$15-$4+$16-$5)}'").start(); + process.waitFor(); + return Double.parseDouble(new BufferedReader(new InputStreamReader(process.getInputStream())).readLine()) / 100.0; + } catch (IOException e) { + return 1D; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return 1D; + } + } + + public static double getRemoteCPULoad(String remote) { + try { + // Attention: + // cpuLoad.sh needs to contain: cat <(grep 'cpu ' /proc/stat) <(sleep 1 && grep 'cpu ' /proc/stat) | awk -v RS="" '{printf "%.2f\n", ($13-$2+$15-$4)*100/($13-$2+$15-$4+$16-$5)}' + Process process = new ProcessBuilder("ssh", remote, "\"./cpuLoad.sh\"").start(); + process.waitFor(); + return Double.parseDouble(new BufferedReader(new InputStreamReader(process.getInputStream())).readLine()) / 100.0; + } catch (IOException e) { + return 1D; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return 1D; + } + } + + private static long getNumber(String s) { + return Long.parseLong(s.split(" ")[1]); + } +} diff --git a/src/de/steamwar/bungeecore/Message.java b/src/de/steamwar/bungeecore/Message.java index 63d851f6..a26f0004 100644 --- a/src/de/steamwar/bungeecore/Message.java +++ b/src/de/steamwar/bungeecore/Message.java @@ -144,6 +144,13 @@ public class Message { } } + public static void team(String message, String onHover, ClickEvent onClick, Object... params){ + for(ProxiedPlayer player : ProxyServer.getInstance().getPlayers()){ + if(player.getGroups().contains(ConnectionListener.TEAM_GROUP)) + sendPrefixless(message, player, Message.parse(onHover, player, params), onClick, params); + } + } + private final String message; private final Object[] params; diff --git a/src/de/steamwar/bungeecore/SubserverSystem.java b/src/de/steamwar/bungeecore/SubserverSystem.java index 50d55526..d5e85b03 100644 --- a/src/de/steamwar/bungeecore/SubserverSystem.java +++ b/src/de/steamwar/bungeecore/SubserverSystem.java @@ -28,8 +28,10 @@ import net.md_5.bungee.api.connection.ProxiedPlayer; import java.io.File; import java.io.IOException; +import java.net.InetSocketAddress; import java.time.format.DateTimeFormatter; import java.util.*; +import java.util.logging.Level; public class SubserverSystem { private SubserverSystem(){} @@ -41,13 +43,13 @@ public class SubserverSystem { private static final String SERVER_PATH = BACKBONE + "server/"; private static final String EVENT_PATH = BACKBONE + "event/"; - private static final int firstArenaPort; + private static final int FIRST_ARENA_PORT; static { if(BungeeCore.EVENT_MODE) - firstArenaPort = 6000; + FIRST_ARENA_PORT = 6000; else - firstArenaPort = 2500; + FIRST_ARENA_PORT = 2500; } /** @@ -87,15 +89,15 @@ public class SubserverSystem { * @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, boolean ranked){ + public static synchronized Subserver startArena(ArenaMode modus, String map, int eventFightID, int checkSchemID, int prepareSchemID, String serverName, String mapName, UUID player1, UUID player2, boolean ranked){ //Generate missing parameters - int port = freePort(firstArenaPort); + int port = freePort(FIRST_ARENA_PORT); if(serverName == null){ if(ranked) - serverName = "Ranked" + (port - firstArenaPort); + serverName = "Ranked" + (port - FIRST_ARENA_PORT); else - serverName = modus.getDisplayName() + (port - firstArenaPort); + serverName = modus.getDisplayName() + (port - FIRST_ARENA_PORT); } if(mapName == null) mapName = serverName; @@ -108,52 +110,55 @@ public class SubserverSystem { //Copy world try { - new ProcessBuilder("cp", "-r", SERVER_PATH + modus.getInternalName() + "/" + map, worldDir + mapName).start().waitFor(); - } catch (IOException | InterruptedException e) { + new ProcessBuilder("cp", "-r", SERVER_PATH + modus.getFolder() + "/arenas/" + map, worldDir + mapName).start().waitFor(); + } catch (IOException e) { throw new SecurityException("Could not copy folder", e); + } catch (InterruptedException e) { + ProxyServer.getInstance().getLogger().log(Level.SEVERE, "Interrupted while copying folder", e); + Thread.currentThread().interrupt(); } + File directory = new File(SERVER_PATH, modus.getFolder()); List cmd = serverStartCommand( modus.serverJar(), + directory, worldDir, mapName, port, "2G", "logPath=" + mapName, + "config="+modus.getConfig(), "fightID=" + eventFightID, "ranked=" + ranked, "checkSchemID=" + checkSchemID, - player1 != null && eventFightID != -1 ? "blueLeader=" + player1.toString() : null, - player2 != null ? "redLeader=" + player2.toString() : null); + "prepareSchemID=" + prepareSchemID, + player1 != null && eventFightID != -1 ? "blueLeader=" + player1 : null, + player2 != null ? "redLeader=" + player2 : null); //Start server ProcessBuilder process = new ProcessBuilder(cmd); - process.directory(new File(SERVER_PATH, modus.getInternalName())); + process.directory(directory); String finalMapName = mapName; if(eventFightID == -1) - return new Bauserver(serverName, player1, port, process, () -> { - try { - new ProcessBuilder("rm", "-r", ARENA_PATH + finalMapName).start().waitFor(); - } catch (IOException | InterruptedException e) { - throw new SecurityException("Could not clean up folder", e); - } - }); + return new Bauserver(serverName, player1, port, process, () -> deleteFolder(ARENA_PATH + finalMapName)); else return new Subserver(Servertype.ARENA, serverName, port, process, () -> { if(eventFightID > 0) return; - - try { - new ProcessBuilder("rm", "-r", ARENA_PATH + finalMapName).start().waitFor(); - } catch (IOException | InterruptedException e) { - throw new SecurityException("Could not clean up folder", e); - } + deleteFolder(ARENA_PATH + finalMapName); }); } - 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 void deleteFolder(String worldName){ + try { + new ProcessBuilder("rm", "-r", worldName).start().waitFor(); + } catch (IOException e) { + throw new SecurityException("Could not clean up folder", e); + } catch (InterruptedException e) { + ProxyServer.getInstance().getLogger().log(Level.SEVERE, "Interrupted while deleting folder", e); + Thread.currentThread().interrupt(); + } } public static Subserver startEventArena(EventFight eventFight, String serverName){ @@ -162,60 +167,58 @@ public class SubserverSystem { eventFight.getMap(), eventFight.getFightID(), 0, + 0, serverName, serverName.replace(' ', '_') + eventFight.getStartTime().toLocalDateTime().format(DateTimeFormatter.ISO_TIME), null, - null); + null, + false); } - public static void startTestServer(ProxiedPlayer p, ArenaMode m, String map, int checkSchemId){ - startArena(m, map, -1, checkSchemId, p.getName() + "s Bau", p.getName(), p.getUniqueId(), null).sendPlayer(p); + public static void startTestServer(ProxiedPlayer p, ArenaMode m, String map, int checkSchemId, int prepareSchemId){ + startArena(m, map, -1, checkSchemId, prepareSchemId, p.getName() + "s Bau", p.getName(), p.getUniqueId(), null, false).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; + + SteamwarUser user = SteamwarUser.get(owner); + copyBauweltIfRequired(p, prototype, worldFolder + worldName); + int port = freePort(4000); + + File directory = new File(SERVER_PATH, serverName); + List cmd = serverStartCommand( + serverJar, + directory, + worldDir, + worldName, + port, + xmx, + "logPath=" + worldName); + + //Start server + ProcessBuilder process = new ProcessBuilder(cmd); + process.directory(directory); + + new Bauserver(user.getUserName() + "s Bau", owner, port, process, () -> {}).sendPlayer(p); } public static void sendToBauServer(ProxiedPlayer p, UUID owner){ - if(bauRunning(p, owner)) - return; + sendToBau(p, owner, BungeeCore.BAUWELT_PROTOTYP, BungeeCore.WORLD_FOLDER, "spigot-1.12.2.jar", "/home/minecraft/userworlds", owner.toString(), "256M", "UserBau"); + } + private static void sendToBau15(ProxiedPlayer p, UUID owner, String serverJar){ SteamwarUser user = SteamwarUser.get(owner); - copyBauweltIfRequired(p, BungeeCore.BAUWELT_PROTOTYP, BungeeCore.WORLD_FOLDER + owner.toString()); - int port = freePort(4000); - - List cmd = serverStartCommand( - "spigot-1.12.2.jar", - "/home/minecraft/userworlds", - owner.toString(), - port, - "256M", - "logPath=" + owner.toString()); - - //Start server - ProcessBuilder process = new ProcessBuilder(cmd); - process.directory(new File(SERVER_PATH, "UserBau")); - - new Bauserver(user.getUserName() + "s Bau", owner, port, process, () -> {}).sendPlayer(p); + sendToBau(p, owner, BungeeCore.BAUWELT15, BungeeCore.USERWORLDS15, serverJar, "/home/minecraft/userworlds15", String.valueOf(user.getId()), "512M", "Bau15"); } public static void sendToBau15(ProxiedPlayer p, UUID owner){ - if(bauRunning(p, owner)) - return; + sendToBau15(p, owner, "spigot-1.15.2.jar"); + } - SteamwarUser user = SteamwarUser.get(owner); - copyBauweltIfRequired(p, BungeeCore.BAUWELT15, BungeeCore.USERWORLDS15 + user.getId()); - int port = freePort(4000); - - List cmd = serverStartCommand( - "spigot-1.15.2.jar", - "/home/minecraft/userworlds15", - String.valueOf(user.getId()), - port, - "512M", - "logPath=" + user.getId()); - - //Start server - ProcessBuilder process = new ProcessBuilder(cmd); - process.directory(new File(SERVER_PATH, "Bau15")); - - new Bauserver(user.getUserName() + "s Bau", owner, port, process, () -> {}).sendPlayer(p); + public static void sendToBau15paper(ProxiedPlayer p, UUID owner){ + sendToBau15(p, owner, "paper-1.15.2.jar"); } public static void sendDeniedMessage(ProxiedPlayer p, UUID owner){ @@ -232,9 +235,30 @@ public class SubserverSystem { new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/bau addmember " + p.getName())); } - private static List serverStartCommand(String serverJar, String worldDir, String levelName, int port, String xmx, String... dParams){ + private static List serverStartCommand(String serverJar, File directory, String worldDir, String levelName, int port, String xmx, String... dParams){ List cmd = new ArrayList<>(); boolean jdk11 = serverJar.contains("1.15.2"); + + boolean fallback = false; + if (!steamwarStartAvailable()) { + cmd.add("ssh"); + cmd.add("-L"); + cmd.add(port + ":localhost:" + port); + if (remoteStartAvailable("lx")) { + cmd.add("lx"); + } else if (remoteStartAvailable("az")) { + cmd.add("az"); + } else { + fallback = true; + } + cmd.add("cd"); + cmd.add(directory.getPath()); + cmd.add(";"); + } + if (fallback) { + cmd.clear(); + } + if(jdk11) cmd.add("/usr/lib/jvm/java-11-openjdk-amd64/bin/java"); else @@ -260,6 +284,14 @@ public class SubserverSystem { return cmd; } + private static boolean steamwarStartAvailable(){ + return LoadEvaluation.getCPULoad() < 0.7 && LoadEvaluation.getRamPercentage() < 0.8; + } + + private static boolean remoteStartAvailable(String remote) { + return LoadEvaluation.getRemoteCPULoad(remote) < 0.7 && LoadEvaluation.getRemoteRamPercentage(remote) < 0.8; + } + private static boolean bauRunning(ProxiedPlayer p, UUID owner){ for(Subserver subserver : Subserver.getServerList()){ if(subserver.getType() == Servertype.BAUSERVER && ((Bauserver)subserver).getOwner().equals(owner)){ @@ -274,11 +306,7 @@ public class SubserverSystem { File w = new File(targetPath); if (!w.exists() || !w.isDirectory()){ try { - Process pr; - ProcessBuilder pb = new ProcessBuilder( - "cp", "-r", sourcePath, targetPath); - pr = pb.start(); - pr.waitFor(); + new ProcessBuilder("cp", "-r", sourcePath, targetPath).start().waitFor(); } catch (IOException e) { Message.send("SERVER_WORLD_ERROR", p); throw new SecurityException("Could not create Bauwelt", e); @@ -298,7 +326,7 @@ public class SubserverSystem { port++; isFree = true; for(Subserver server : Subserver.getServerList()){ - if(server.getServer().getAddress().getPort() == port){ + if(((InetSocketAddress)server.getServer().getSocketAddress()).getPort() == port){ isFree = false; break; } diff --git a/src/de/steamwar/bungeecore/commands/BanCommand.java b/src/de/steamwar/bungeecore/commands/BanCommand.java index 293b3f1c..6b3bcea7 100644 --- a/src/de/steamwar/bungeecore/commands/BanCommand.java +++ b/src/de/steamwar/bungeecore/commands/BanCommand.java @@ -42,7 +42,7 @@ public class BanCommand extends BasicCommand { return; } - SteamwarUser target = user(sender, args[0]); + SteamwarUser target = unsafeUser(sender, args[0]); if(target == null) return; diff --git a/src/de/steamwar/bungeecore/commands/BasicCommand.java b/src/de/steamwar/bungeecore/commands/BasicCommand.java index 9cdf19a9..d4b9c0ad 100644 --- a/src/de/steamwar/bungeecore/commands/BasicCommand.java +++ b/src/de/steamwar/bungeecore/commands/BasicCommand.java @@ -53,10 +53,17 @@ abstract class BasicCommand extends Command implements TabExecutor { return new ArrayList<>(); } - protected SteamwarUser user(CommandSender sender, String arg){ + protected SteamwarUser existingUser(CommandSender sender, String arg){ SteamwarUser target = SteamwarUser.get(arg); if(target == null) Message.send("UNKNOWN_PLAYER", sender); return target; } + + protected SteamwarUser unsafeUser(CommandSender sender, String arg){ + SteamwarUser target = SteamwarUser.getOrCreateOfflinePlayer(arg); + if(target == null) + Message.send("UNKNOWN_PLAYER", sender); + return target; + } } diff --git a/src/de/steamwar/bungeecore/commands/BauCommand.java b/src/de/steamwar/bungeecore/commands/BauCommand.java index a0d120fb..43944d72 100644 --- a/src/de/steamwar/bungeecore/commands/BauCommand.java +++ b/src/de/steamwar/bungeecore/commands/BauCommand.java @@ -28,8 +28,6 @@ import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.event.ChatEvent; -import java.io.File; - public class BauCommand { private BauCommand(){} @@ -70,6 +68,9 @@ public class BauCommand { case "1.15": SubserverSystem.sendToBau15(p, p.getUniqueId()); break; + case "paper": + SubserverSystem.sendToBau15paper(p, p.getUniqueId()); + break; case "addmember": addmember(p, command); break; @@ -77,11 +78,10 @@ public class BauCommand { case "teleport": teleport(p, command); break; - case "togglebuild": - if(ownBau && command.length > 2) + case "info": + if (bau != null) { e.setCancelled(false); - else - togglebuild(p, command); + } break; case "togglewe": if(ownBau && command.length > 2) @@ -129,7 +129,7 @@ public class BauCommand { return; } - new BauweltMember(p.getUniqueId(), target.getUuid(), true, false, false); + new BauweltMember(p.getUniqueId(), target.getUuid(), true, false); Message.send("BAU_ADDMEMBER_ADDED", p); ProxiedPlayer z = ProxyServer.getInstance().getPlayer(target.getUuid()); @@ -183,15 +183,6 @@ public class BauCommand { } } - private static void togglebuild(ProxiedPlayer p, String[] command){ - BauweltMember target = toggle(p, command, "togglebuild"); - if(target == null) - return; - - target.setBuild(!target.isBuild()); - isAllowedTo(target.isBuild(), p, target, "bauen"); - } - private static void togglewe(ProxiedPlayer p, String[] command){ BauweltMember target = toggle(p, command, "togglewe"); if(target == null) @@ -226,12 +217,13 @@ public class BauCommand { } target.remove(); - ProxiedPlayer z = ProxyServer.getInstance().getPlayer(SteamwarUser.get(target.getMemberID()).getUuid()); - if(z != null){ - Message.send("BAU_DELMEMBER_DELETED_TARGET", z, p.getName()); - Subserver server = Subserver.getSubserver(z); - if(server != null && server.getType() == Servertype.BAUSERVER && ((Bauserver)server).getOwner().equals(p.getUniqueId())) - z.connect(ProxyServer.getInstance().getServerInfo(BungeeCore.LOBBY_SERVER)); + ProxiedPlayer toRemove = ProxyServer.getInstance().getPlayer(SteamwarUser.get(target.getMemberID()).getUuid()); + if(toRemove != null){ + Message.send("BAU_DELMEMBER_DELETED_TARGET", toRemove, p.getName()); + Subserver currentServer = Subserver.getSubserver(toRemove.getServer().getInfo()); + if (currentServer != null && currentServer.getType() == Servertype.BAUSERVER && ((Bauserver) currentServer).getOwner().equals(p.getUniqueId())) { + toRemove.connect(ProxyServer.getInstance().getServerInfo(BungeeCore.LOBBY_SERVER)); + } } Message.send("BAU_DELMEMBER_DELETED", p); } @@ -261,29 +253,32 @@ public class BauCommand { break; } } - File directory = new File(world); - del(directory); + SubserverSystem.deleteFolder(world); }); } + public static void stopBauserver(ProxiedPlayer p){ + for (Subserver subserver : Subserver.getServerList()) { + if (subserver.getType() == Servertype.BAUSERVER && ((Bauserver) subserver).getOwner().equals(p.getUniqueId())) { + if(subserver.getServer().getPlayers().isEmpty()){ + Message.send("BAU_START_ALREADY", p); + return; + } + 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; + } + } + } + private static void testarena(ProxiedPlayer p, String[] command){ FightCommand.createArena(p, "/bau testarena ", command, 2, false, (player, mode, map) -> ProxyServer.getInstance().getScheduler().runAsync(BungeeCore.get(), () -> { - for (Subserver subserver : Subserver.getServerList()) { - if (subserver.getType() == Servertype.BAUSERVER && ((Bauserver) subserver).getOwner().equals(p.getUniqueId())) { - if(subserver.getServer().getPlayers().isEmpty()){ - Message.send("BAU_START_ALREADY", p); - return; - } - 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; - } - } - SubserverSystem.startTestServer(p, mode, map, 0); + stopBauserver(p); + SubserverSystem.startTestServer(p, mode, map, 0, 0); })); } @@ -324,19 +319,4 @@ public class BauCommand { Message.send("BAU_MEMBER_TOGGLE_OFF", p, what); } } - - private static void del(File dir){ - if (dir.isDirectory()){ - String[] entries = dir.list(); - assert entries != null; - for (String entry : entries) { - File aktFile = new File(dir.getPath(), entry); - del(aktFile); - } - dir.delete(); - } - else{ - dir.delete(); - } - } } diff --git a/src/de/steamwar/bungeecore/commands/ChallengeCommand.java b/src/de/steamwar/bungeecore/commands/ChallengeCommand.java index 2c3d6574..3d4150d3 100644 --- a/src/de/steamwar/bungeecore/commands/ChallengeCommand.java +++ b/src/de/steamwar/bungeecore/commands/ChallengeCommand.java @@ -75,7 +75,7 @@ public class ChallengeCommand extends BasicCommand { challenges.remove(target); challenges.remove(player); - Subserver arena = SubserverSystem.startArena(mode, map, 0, 0, null, null, player.getUniqueId(), target.getUniqueId()); + Subserver arena = SubserverSystem.startArena(mode, map, 0, 0, 0, null, null, player.getUniqueId(), target.getUniqueId(), false); arena.sendPlayer(player); arena.sendPlayer(target); diff --git a/src/de/steamwar/bungeecore/commands/CheckCommand.java b/src/de/steamwar/bungeecore/commands/CheckCommand.java index bd268ec5..fe8b40e2 100644 --- a/src/de/steamwar/bungeecore/commands/CheckCommand.java +++ b/src/de/steamwar/bungeecore/commands/CheckCommand.java @@ -68,9 +68,14 @@ public class CheckCommand extends BasicCommand { ProxyServer.getInstance().getScheduler().schedule(BungeeCore.get(), () -> { List schematics = getSchemsToCheck(); if(schematics.size() != currentCheckers.size()) - Message.team("CHECK_REMINDER", schematics.size() - currentCheckers.size()); + Message.team("CHECK_REMINDER", "CHECK_REMINDER_HOVER", new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/check list"), schematics.size() - currentCheckers.size()); }, 10, 10, TimeUnit.MINUTES); } + public static void sendReminder(ProxiedPlayer player) { + List schematics = getSchemsToCheck(); + if(schematics.size() != currentCheckers.size()) + Message.send("CHECK_REMINDER", player, "CHECK_REMINDER_HOVER", new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/check list"), schematics.size() - currentCheckers.size()); + } @Override public void execute(CommandSender sender, String[] args) { @@ -223,15 +228,10 @@ public class CheckCommand extends BasicCommand { this.checkList = checkQuestions.get(schematic.getSchemtype()).listIterator(); ProxyServer.getInstance().getScheduler().runAsync(BungeeCore.get(), () -> { - for (Subserver subserver : Subserver.getServerList()) { - if (subserver.getType() == Servertype.BAUSERVER && ((Bauserver) subserver).getOwner().equals(checker.getUniqueId())) { - subserver.stop(); - break; - } - } + BauCommand.stopBauserver(checker); - //ArenaMode mode = ArenaMode.getByCheckSchemType(schematic.getSchemtype().toDB()); - //SubserverSystem.startTestServer(checker, mode, FightCommand.getMap(checker, mode, "Random"), schematic.getId()); + ArenaMode mode = ArenaMode.getByCheckSchemType(schematic.getSchemType().toDB()); + SubserverSystem.startTestServer(checker, mode, FightCommand.getMap(checker, mode, "Random"), schematic.getSchemID()); currentCheckers.put(checker.getUniqueId(), this); currentSchems.put(schematic.getId(), this); for(CheckedSchematic previous : CheckedSchematic.previousChecks(schematic.getId())) diff --git a/src/de/steamwar/bungeecore/commands/DevCommand.java b/src/de/steamwar/bungeecore/commands/DevCommand.java new file mode 100644 index 00000000..7e5ed2d4 --- /dev/null +++ b/src/de/steamwar/bungeecore/commands/DevCommand.java @@ -0,0 +1,97 @@ +/* + * 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.Message; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.config.ServerInfo; +import net.md_5.bungee.api.connection.ProxiedPlayer; + +import java.io.File; +import java.net.InetSocketAddress; +import java.util.Arrays; +import java.util.Collections; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +public class DevCommand extends BasicCommand { + + private final File DevServerDir = new File("/configs/DevServer"); + + public DevCommand() { + super("dev", ""); + } + + @Override + public void execute(CommandSender sender, String[] args) { + if (!(sender instanceof ProxiedPlayer)) { + return; + } + String[] devServer = DevServerDir.list(); + if (devServer == null || devServer.length == 0) { + Message.send("DEV_NO_SERVER", sender); + } else if (devServer.length == 1) { + String[] server = devServer[0].split("\\."); + sendToServer((ProxiedPlayer) sender, server[0], Integer.parseInt(server[1])); + } else { + if (args.length == 0) { + send(devServer, (ProxiedPlayer) sender, s -> s.equalsIgnoreCase(((ProxiedPlayer) sender).getDisplayName()), () -> Message.send("DEV_UNKNOWN_SERVER", sender)); + } else { + send(devServer, (ProxiedPlayer) sender, args[0]::equalsIgnoreCase, () -> Message.send("DEV_NO_SERVER", sender)); + } + } + } + + private void send(String[] devServer, ProxiedPlayer sender, Predicate test, Runnable error) { + for (String s : devServer) { + String[] server = s.split("\\."); + if (test.test(server[0])) { + sendToServer(sender, server[0], Integer.parseInt(server[1])); + break; + } + } + error.run(); + } + + private void sendToServer(ProxiedPlayer proxiedPlayer, String name, int port) { + InetSocketAddress address = new InetSocketAddress("127.0.0.1", port); + ServerInfo serverInfo = ProxyServer.getInstance().constructServerInfo("Dev-" + name, address, "SteamWar.de - Subserver", false); + proxiedPlayer.connect(serverInfo); + } + + @Override + public Iterable onTabComplete(CommandSender sender, String[] args) { + if (!(sender instanceof ProxiedPlayer)) { + return Collections.emptyList(); + } + String[] devSever = DevServerDir.list(); + if (devSever == null) { + return Collections.emptyList(); + } + if (args.length > 1) { + return Collections.emptyList(); + } + return Arrays.stream(devSever).map(s -> s.split("\\.")).map(s -> s[0]).filter(s -> { + if (args.length == 0) return true; + return s.startsWith(args[0]); + }).collect(Collectors.toList()); + } +} diff --git a/src/de/steamwar/bungeecore/commands/FightCommand.java b/src/de/steamwar/bungeecore/commands/FightCommand.java index 44e9cf10..984bc06b 100644 --- a/src/de/steamwar/bungeecore/commands/FightCommand.java +++ b/src/de/steamwar/bungeecore/commands/FightCommand.java @@ -69,7 +69,7 @@ public class FightCommand extends BasicCommand { TextComponent start = new TextComponent(); TextComponent current = start; for(ArenaMode mode : ArenaMode.getAllModes()){ - if(!mode.hasChatName() || mode.isHistoric() != historic) + if(mode.withoutChatName() || mode.isHistoric() != historic) continue; String command = precommand + mode.getChatName(); current.setBold(true); @@ -164,7 +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, null, null, player.getUniqueId(), null); + Subserver arena = SubserverSystem.startArena(mode, map, 0, 0, 0, null, null, player.getUniqueId(), null, false); arena.sendPlayer(player); Message.broadcast("FIGHT_BROADCAST", "FIGHT_BROADCAST_HOVER" , new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/join " + player.getName()), mode.getDisplayName(), player.getName()); diff --git a/src/de/steamwar/bungeecore/commands/HelpCommand.java b/src/de/steamwar/bungeecore/commands/HelpCommand.java index 7394036d..d2febdc4 100644 --- a/src/de/steamwar/bungeecore/commands/HelpCommand.java +++ b/src/de/steamwar/bungeecore/commands/HelpCommand.java @@ -99,7 +99,6 @@ public class HelpCommand extends BasicCommand { Message.send("HELP_BAU_TP", true, p, ChatMessageType.SYSTEM, Message.parse("HELP_BAU_TP_HOVER", p), new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/bau tp ")); Message.send("HELP_BAU_ADDMEMBER", true, p, ChatMessageType.SYSTEM, Message.parse("HELP_BAU_ADDMEMBER_HOVER", p), new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/bau addmember ")); Message.send("HELP_BAU_DELMEMBER", true, p, ChatMessageType.SYSTEM, Message.parse("HELP_BAU_DELMEMBER_HOVER", p), new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/bau delmember ")); - Message.send("HELP_BAU_TOGGLEBUILD", true, p, ChatMessageType.SYSTEM, Message.parse("HELP_BAU_TOGGLEBUILD_HOVER", p), new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/bau togglebuild ")); Message.send("HELP_BAU_TOGGLEWE", true, p, ChatMessageType.SYSTEM, Message.parse("HELP_BAU_TOGGLEWE_HOVER", p), new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/bau togglewe ")); Message.send("HELP_BAU_TOGGLEWORLD", true, p, ChatMessageType.SYSTEM, Message.parse("HELP_BAU_TOGGLEWORLD_HOVER", p), new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/bau toggleworld ")); Message.send("HELP_BAU_DELETE", true, p, ChatMessageType.SYSTEM, Message.parse("HELP_BAU_DELETE_HOVER", p), new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/bau delete ")); diff --git a/src/de/steamwar/bungeecore/commands/HistoricCommand.java b/src/de/steamwar/bungeecore/commands/HistoricCommand.java index c7d64deb..ebf93123 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 void execute(CommandSender sender, String[] args) { FightCommand.createArena(sender, "/historic ", args, 0, true, (player, mode, map) -> { - Subserver arena = SubserverSystem.startArena(mode, map, 0, 0, null, null, player.getUniqueId(), null); + Subserver arena = SubserverSystem.startArena(mode, map, 0, 0, 0, null, null, player.getUniqueId(), null, false); arena.sendPlayer(player); Message.broadcast("HISTORIC_BROADCAST", "HISTORIC_BROADCAST_HOVER" , new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/join " + player.getName()), mode.getDisplayName(), player.getName()); diff --git a/src/de/steamwar/bungeecore/commands/ListCommand.java b/src/de/steamwar/bungeecore/commands/ListCommand.java new file mode 100644 index 00000000..b5d2f2d8 --- /dev/null +++ b/src/de/steamwar/bungeecore/commands/ListCommand.java @@ -0,0 +1,75 @@ +/* + * 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.Message; +import de.steamwar.bungeecore.Servertype; +import de.steamwar.bungeecore.Subserver; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.config.ServerInfo; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.connection.Server; + +import java.util.ArrayList; +import java.util.List; +import java.util.TreeMap; +import java.util.stream.Collectors; + +public class ListCommand extends BasicCommand { + + public ListCommand() { + super("list", ""); + } + + private final TreeMap> playerMap = new TreeMap<>(); + + private synchronized void updateCustomTablist(){ + //Calculate server-player-map + playerMap.clear(); + for (ProxiedPlayer player : ProxyServer.getInstance().getPlayers()) { + Server pserver = player.getServer(); + if (pserver == null) //Happens temporarily + continue; + + ServerInfo server = pserver.getInfo(); + String serverName = server.getName(); + + Subserver subserver = Subserver.getSubserver(server); + if (subserver != null && subserver.getType() == Servertype.BAUSERVER) { + playerMap.computeIfAbsent("Bau", s -> new ArrayList<>()).add(player); + } else { + playerMap.computeIfAbsent(serverName, s -> new ArrayList<>()).add(player); + } + } + playerMap.forEach((server, players) -> players.sort((proxiedPlayer, t1) -> proxiedPlayer.getName().compareToIgnoreCase(t1.getName()))); + } + + @Override + public void execute(CommandSender commandSender, String[] strings) { + updateCustomTablist(); + for (String server : playerMap.navigableKeySet()) { + if (server.equals("Bau")) { + server = Message.parse("TABLIST_BAU", commandSender); + } + Message.send("LIST_COMMAND", commandSender, server, playerMap.get(server).stream().map(CommandSender::getName).collect(Collectors.joining(", "))); + } + } +} diff --git a/src/de/steamwar/bungeecore/commands/MuteCommand.java b/src/de/steamwar/bungeecore/commands/MuteCommand.java index ea778885..9ecfde18 100644 --- a/src/de/steamwar/bungeecore/commands/MuteCommand.java +++ b/src/de/steamwar/bungeecore/commands/MuteCommand.java @@ -38,7 +38,7 @@ public class MuteCommand extends BasicCommand { return; } - SteamwarUser target = user(sender, args[0]); + SteamwarUser target = unsafeUser(sender, args[0]); if(target == null) return; diff --git a/src/de/steamwar/bungeecore/commands/RankedCommand.java b/src/de/steamwar/bungeecore/commands/RankedCommand.java index c70fff93..1a978b55 100644 --- a/src/de/steamwar/bungeecore/commands/RankedCommand.java +++ b/src/de/steamwar/bungeecore/commands/RankedCommand.java @@ -81,7 +81,7 @@ public class RankedCommand extends BasicCommand { TextComponent start = new TextComponent(); TextComponent current = start; for(ArenaMode mode : ArenaMode.getAllModes()){ - if(!mode.hasChatName() || !mode.isRanked()) + if(mode.withoutChatName() || !mode.isRanked()) continue; String command = precommand + mode.getChatName(); current.setBold(true); @@ -165,7 +165,7 @@ public class RankedCommand extends BasicCommand { removeFromAll(wp1.player); removeFromAll(wp2.player); - Subserver arena = SubserverSystem.startArena(mode, mode.getRandomMap(), 0, 0, null, null, wp1.player.getUniqueId(), wp2.player.getUniqueId(), true); + Subserver arena = SubserverSystem.startArena(mode, mode.getRandomMap(), 0, 0, 0, null, null, wp1.player.getUniqueId(), wp2.player.getUniqueId(), true); arena.sendPlayer(wp1.player); arena.sendPlayer(wp2.player); diff --git a/src/de/steamwar/bungeecore/commands/ServerTeamchatCommand.java b/src/de/steamwar/bungeecore/commands/ServerTeamchatCommand.java index 90a6139d..0aca552f 100644 --- a/src/de/steamwar/bungeecore/commands/ServerTeamchatCommand.java +++ b/src/de/steamwar/bungeecore/commands/ServerTeamchatCommand.java @@ -20,6 +20,7 @@ package de.steamwar.bungeecore.commands; import de.steamwar.bungeecore.Message; +import de.steamwar.bungeecore.listeners.ChatListener; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.ProxyServer; @@ -56,7 +57,7 @@ public class ServerTeamchatCommand extends BasicCommand { for (ProxiedPlayer target : ProxyServer.getInstance().getPlayers()){ if ((target.hasPermission("bungeecore.teamchat")) && target.getChatMode() == ProxiedPlayer.ChatMode.SHOWN){ - Message.sendPrefixless("STC_FORMAT", target, sender.getName(), message); + Message.sendPrefixless("STC_FORMAT", target, sender.getName(), ChatListener.parseAtMessage(message, "§r", target)); } } } diff --git a/src/de/steamwar/bungeecore/commands/StatCommand.java b/src/de/steamwar/bungeecore/commands/StatCommand.java new file mode 100644 index 00000000..1488dbfa --- /dev/null +++ b/src/de/steamwar/bungeecore/commands/StatCommand.java @@ -0,0 +1,69 @@ +/* + 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.LoadEvaluation; +import de.steamwar.bungeecore.Message; +import net.md_5.bungee.api.CommandSender; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; + +public class StatCommand extends BasicCommand { + + public StatCommand() { + super("stat", "bungeecore.softreload", "stats"); + } + + @Override + public void execute(CommandSender sender, String[] args) { + try { + Process process = new ProcessBuilder("ps", "x").start(); + process.waitFor(); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream())); + Map serverCounts = new HashMap<>(); + bufferedReader.lines().forEach(s -> { + if (!s.contains("--port")) { + return; + } + String server = "SW"; + if (s.contains("ssh -L")) { + server = s.substring(s.indexOf("ssh -L") + 6).split(" ")[2]; + } + serverCounts.put(server, serverCounts.computeIfAbsent(server, s1 -> 0) + 1); + }); + serverCounts.forEach((s, integer) -> { + if (s.equals("SW")) { + Message.send("STAT_SERVER", sender, s, LoadEvaluation.getRamPercentage(), LoadEvaluation.getCPULoad(), integer); + } else { + Message.send("STAT_SERVER", sender, s.toUpperCase(), LoadEvaluation.getRemoteRamPercentage(s), LoadEvaluation.getRemoteCPULoad(s), integer); + } + }); + if (serverCounts.isEmpty()) { + Message.send("NO_STATS", sender); + } + } catch (Exception e) { + throw new SecurityException(e.getMessage(), e); + } + } + +} diff --git a/src/de/steamwar/bungeecore/commands/TeamCommand.java b/src/de/steamwar/bungeecore/commands/TeamCommand.java index a1ca9954..af3a84ba 100644 --- a/src/de/steamwar/bungeecore/commands/TeamCommand.java +++ b/src/de/steamwar/bungeecore/commands/TeamCommand.java @@ -38,6 +38,7 @@ import net.md_5.bungee.api.connection.ProxiedPlayer; import java.time.Instant; import java.time.format.DateTimeFormatter; import java.util.*; +import java.util.stream.Collectors; import static de.steamwar.bungeecore.Storage.teamInvitations; @@ -90,13 +91,14 @@ public class TeamCommand extends BasicCommand { Message.send("TEAM_HELP_LEAVE", sender); Team team = Team.get(user.getTeam()); - if(team.getTeamLeader() == user.getId()){ + if(user.isLeader()){ Message.send("TEAM_HELP_INVITE", sender); Message.send("TEAM_HELP_REMOVE", sender); Message.send("TEAM_HELP_KUERZEL", sender); Message.send("TEAM_HELP_NAME", sender); Message.send("TEAM_HELP_COLOR", sender); Message.send("TEAM_HELP_LEADER", sender); + Message.send("TEAM_HELP_STEP_BACK", sender); } } } @@ -122,6 +124,9 @@ public class TeamCommand extends BasicCommand { case "join": join(player, user, args); break; + case "stepback": + stepBack(player,user,team); + break; case "leave": leave(player, user, team); break; @@ -137,8 +142,8 @@ public class TeamCommand extends BasicCommand { case "changename": changename(player, user, team, args); break; - case "changeleader": - changeleader(player, user, team, args); + case "promote": + promote(player, user, team, args); break; case "changecolor": changeColor(player, user, team); @@ -172,8 +177,9 @@ public class TeamCommand extends BasicCommand { if(checkTeamName(player, team, args[2])) return; - Team.create(args[1], args[2], user.getId()); + Team.create(args[1], args[2], user); user.setTeam(Team.get(args[1]).getTeamId()); + user.setLeader(true); Message.send("TEAM_CREATE_CREATED", player, args[2]); } @@ -225,19 +231,33 @@ public class TeamCommand extends BasicCommand { Message.send("TEAM_JOIN_JOINED", player, Team.get(t).getTeamName()); } + private void stepBack(ProxiedPlayer player, SteamwarUser user, Team team) { + if(notLeader(player, user, team)) + return; + + if(team.size() > 1 && team.getMembers().stream().map(SteamwarUser::get).filter(member -> user != member).noneMatch(SteamwarUser::isLeader)){ + Message.send("TEAM_OTHER_LEADER_REQUIRED", player); + return; + } + + user.setLeader(false); + Message.send("TEAM_STEP_BACK", player); + } + private void leave(ProxiedPlayer player, SteamwarUser user, Team team){ if(notInTeam(player, user)) return; - if(team.getTeamLeader() == user.getId() && team.size() > 1){ - Message.send("TEAM_LEAVE_OTHER_LEADER", player); + int teamSize = team.size(); + if(teamSize > 1 && user.isLeader() && team.getMembers().stream().map(SteamwarUser::get).filter(member -> user != member).noneMatch(SteamwarUser::isLeader)){ + Message.send("TEAM_OTHER_LEADER_REQUIRED", player); return; } user.setTeam(0); - if(team.getTeamLeader() == user.getId()){ - team.disband(); + if(teamSize == 1){ + team.disband(user); } Message.send("TEAM_LEAVE_LEFT", player); @@ -296,7 +316,7 @@ public class TeamCommand extends BasicCommand { return; } - if (team.getTeamLeader() == target.getId()) { + if (target.isLeader()) { Message.send("TEAM_REMOVE_NOT_LEADER", player); return; } @@ -363,7 +383,7 @@ public class TeamCommand extends BasicCommand { Message.send("TEAM_NAME_CHANGED", player); } - private void changeleader(ProxiedPlayer player, SteamwarUser user, Team team, String[] args){ + private void promote(ProxiedPlayer player, SteamwarUser user, Team team, String[] args){ if(notLeader(player, user, team)) return; @@ -386,8 +406,8 @@ public class TeamCommand extends BasicCommand { return; } - team.setTeamLeader(target.getId()); - Message.send("TEAM_LEADER_CHANGED", player, args[1]); + target.setLeader(true); + Message.send("TEAM_LEADER_PROMOTED", player, args[1]); } private String playerName(SteamwarUser user){ @@ -408,17 +428,15 @@ public class TeamCommand extends BasicCommand { } Message.sendPrefixless("TEAM_INFO_TEAM", player, team.getTeamName(), team.getTeamColor(), team.getTeamKuerzel()); - Message.sendPrefixless("TEAM_INFO_LEADER", player, playerName(SteamwarUser.get(team.getTeamLeader()))); - if(team.getMembers().size() > 1) { - StringBuilder sb = new StringBuilder(); - List members = team.getMembers(); - for (int i : members) { - if (i == team.getTeamLeader()) - continue; - sb.append(playerName(SteamwarUser.get(i))).append(" "); - } - Message.sendPrefixless("TEAM_INFO_MEMBER", player, sb.toString()); + List users = team.getMembers().stream().map(SteamwarUser::get).collect(Collectors.toList()); + + Message.sendPrefixless("TEAM_INFO_LEADER", player, getMemberList(users, true)); + + String members = getMemberList(users, false); + + if(members.length() > 0) { + Message.sendPrefixless("TEAM_INFO_MEMBER", player, members); } Set events = TeamTeilnahme.getEvents(team.getTeamId()); @@ -430,6 +448,16 @@ public class TeamCommand extends BasicCommand { } } + private String getMemberList(List users, boolean leaders) { + StringBuilder sb = new StringBuilder(); + for(SteamwarUser user : users) { + if(user.isLeader() == leaders) { + sb.append(playerName(user)).append(" "); + } + } + return sb.toString(); + } + private void list(ProxiedPlayer player, String[] args){ final int TEAMS_PER_PAGE = 10; @@ -458,7 +486,7 @@ public class TeamCommand extends BasicCommand { Team tm = all.get(i); Message.sendPrefixless("TEAM_LIST_TEAM", player, Message.parse("TEAM_LIST_TEAM_HOVER", player), - new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/team info " + tm.getTeamKuerzel()), tm.getTeamColor(), tm.getTeamKuerzel(), tm.getTeamName(), SteamwarUser.get(tm.getTeamLeader()).getUserName()); + new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/team info " + tm.getTeamKuerzel()), tm.getTeamColor(), tm.getTeamKuerzel(), tm.getTeamName()); } TextComponent beforePage = new TextComponent("««"); @@ -593,7 +621,7 @@ public class TeamCommand extends BasicCommand { private boolean notLeader(ProxiedPlayer player, SteamwarUser user, Team team){ if(notInTeam(player, user)) return true; - if(team.getTeamLeader() != user.getId()){ + if(!user.isLeader()){ Message.send("TEAM_NOT_LEADER", player); return true; } @@ -615,6 +643,7 @@ public class TeamCommand extends BasicCommand { tab.add("create"); tab.add("join"); tab.add("invite"); + tab.add("stepback"); tab.add("leave"); tab.add("info"); tab.add("remove"); diff --git a/src/de/steamwar/bungeecore/commands/TeamchatCommand.java b/src/de/steamwar/bungeecore/commands/TeamchatCommand.java index f5d17f5e..d85ac6ee 100644 --- a/src/de/steamwar/bungeecore/commands/TeamchatCommand.java +++ b/src/de/steamwar/bungeecore/commands/TeamchatCommand.java @@ -20,6 +20,7 @@ package de.steamwar.bungeecore.commands; import de.steamwar.bungeecore.Message; +import de.steamwar.bungeecore.listeners.ChatListener; import de.steamwar.bungeecore.sql.SteamwarUser; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.CommandSender; @@ -59,7 +60,7 @@ public class TeamchatCommand extends BasicCommand { SteamwarUser targetuser = SteamwarUser.get(target.getUniqueId()); if (targetuser.getTeam() == user.getTeam() && target.getChatMode() == ProxiedPlayer.ChatMode.SHOWN){ - Message.sendPrefixless("TC_FORMAT", target, player.getName(), msg); + Message.sendPrefixless("TC_FORMAT", target, player.getName(), ChatListener.parseAtMessage(msg, "§f", target)); } } } diff --git a/src/de/steamwar/bungeecore/commands/UnbanCommand.java b/src/de/steamwar/bungeecore/commands/UnbanCommand.java index 58c0a6fb..176029ab 100644 --- a/src/de/steamwar/bungeecore/commands/UnbanCommand.java +++ b/src/de/steamwar/bungeecore/commands/UnbanCommand.java @@ -39,7 +39,7 @@ public class UnbanCommand extends BasicCommand { return; } - SteamwarUser target = user(sender, args[0]); + SteamwarUser target = existingUser(sender, args[0]); if(target == null) return; diff --git a/src/de/steamwar/bungeecore/comms/PacketIdManager.java b/src/de/steamwar/bungeecore/comms/PacketIdManager.java index ae86d708..8376d591 100644 --- a/src/de/steamwar/bungeecore/comms/PacketIdManager.java +++ b/src/de/steamwar/bungeecore/comms/PacketIdManager.java @@ -24,6 +24,7 @@ public class PacketIdManager { //0x0(X) Standalone Packets public static final byte PING_PACKET = 0x01; public static final byte TABLIST_NAME = 0x02; + public static final byte PREPARE_SCHEM = 0x03; //0x1(X) Bungee Inventory public static final byte INVENTORY_PACKET = 0x10; diff --git a/src/de/steamwar/bungeecore/comms/SpigotReceiver.java b/src/de/steamwar/bungeecore/comms/SpigotReceiver.java index b18b3a04..f87646f1 100644 --- a/src/de/steamwar/bungeecore/comms/SpigotReceiver.java +++ b/src/de/steamwar/bungeecore/comms/SpigotReceiver.java @@ -22,6 +22,7 @@ package de.steamwar.bungeecore.comms; import com.google.common.io.ByteArrayDataInput; import com.google.common.io.ByteStreams; import de.steamwar.bungeecore.comms.handlers.InventoryCallbackHandler; +import de.steamwar.bungeecore.comms.handlers.PrepareSchemHandler; import de.steamwar.bungeecore.comms.handlers.TablistNameHandler; import de.steamwar.bungeecore.listeners.BasicListener; import net.md_5.bungee.api.event.PluginMessageEvent; @@ -55,5 +56,6 @@ public class SpigotReceiver extends BasicListener { static { registerHandler(PacketIdManager.INVENTORY_CALLBACK_PACKET, new InventoryCallbackHandler()); registerHandler(PacketIdManager.TABLIST_NAME, new TablistNameHandler()); + registerHandler(PacketIdManager.PREPARE_SCHEM, new PrepareSchemHandler()); } } diff --git a/src/de/steamwar/bungeecore/comms/handlers/PrepareSchemHandler.java b/src/de/steamwar/bungeecore/comms/handlers/PrepareSchemHandler.java new file mode 100644 index 00000000..46d1a032 --- /dev/null +++ b/src/de/steamwar/bungeecore/comms/handlers/PrepareSchemHandler.java @@ -0,0 +1,42 @@ +/* + 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.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.comms.SpigotHandler; +import de.steamwar.bungeecore.sql.SchematicType; +import de.steamwar.bungeecore.sql.SteamwarUser; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.connection.ProxiedPlayer; + +public class PrepareSchemHandler implements SpigotHandler { + @Override + public void handle(ByteArrayDataInput byteArrayDataInput) { + ProxiedPlayer player = ProxyServer.getInstance().getPlayer(SteamwarUser.get(byteArrayDataInput.readInt()).getUuid()); + int schematicID = byteArrayDataInput.readInt(); + ArenaMode mode = ArenaMode.getBySchemType(SchematicType.fromDB(byteArrayDataInput.readUTF())); + + BauCommand.stopBauserver(player); + SubserverSystem.startTestServer(player, mode, mode.getRandomMap(), 0, schematicID); + } +} diff --git a/src/de/steamwar/bungeecore/listeners/BrandListener.java b/src/de/steamwar/bungeecore/listeners/BrandListener.java new file mode 100644 index 00000000..cbc16a41 --- /dev/null +++ b/src/de/steamwar/bungeecore/listeners/BrandListener.java @@ -0,0 +1,63 @@ +/* + 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.listeners; + +import de.steamwar.bungeecore.BungeeCore; +import de.steamwar.bungeecore.Message; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.PluginMessageEvent; +import net.md_5.bungee.event.EventHandler; +import net.md_5.bungee.protocol.DefinedPacket; +import net.md_5.bungee.protocol.ProtocolConstants; + +import java.util.concurrent.TimeUnit; + +public class BrandListener extends BasicListener { + + @EventHandler + public void onServerSwitch(PluginMessageEvent event) { + if(!event.getTag().equals("minecraft:brand") || !event.getTag().equals("MC|Brand")) { + return; + } + + if(event.getReceiver().getAddress().getHostName().contains("localhost")) { + return; + } + + BungeeCore.get().getProxy().getScheduler().schedule(BungeeCore.get(), () -> { + ProxiedPlayer player = (ProxiedPlayer) event.getReceiver(); + + String channel = player.getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_13 ? "minecraft:brand" : "MC|Brand"; + StringBuilder serverBrand = new StringBuilder(); + for (byte b:event.getData()) { + serverBrand.append((char) b); + } + + String brandString = Message.parse("STEAMWAR_BRAND", player, BungeeCore.get().getProxy().getName(), player.getServer().getInfo().getName(), serverBrand.substring(1)); + + ByteBuf brand = ByteBufAllocator.DEFAULT.heapBuffer(); + DefinedPacket.writeString(brandString, brand); + player.sendData(channel, DefinedPacket.toArray(brand)); + brand.release(); + }, 50, TimeUnit.MILLISECONDS); + } +} diff --git a/src/de/steamwar/bungeecore/listeners/ChatListener.java b/src/de/steamwar/bungeecore/listeners/ChatListener.java index 2a5e9227..a73ea6d5 100644 --- a/src/de/steamwar/bungeecore/listeners/ChatListener.java +++ b/src/de/steamwar/bungeecore/listeners/ChatListener.java @@ -19,10 +19,7 @@ package de.steamwar.bungeecore.listeners; -import de.steamwar.bungeecore.BungeeCore; -import de.steamwar.bungeecore.Message; -import de.steamwar.bungeecore.Servertype; -import de.steamwar.bungeecore.Subserver; +import de.steamwar.bungeecore.*; import de.steamwar.bungeecore.commands.BauCommand; import de.steamwar.bungeecore.commands.TpCommand; import de.steamwar.bungeecore.comms.packets.PingPacket; @@ -51,6 +48,8 @@ public class ChatListener extends BasicListener { sanitize7(e); if(e.getMessage().startsWith("/")) onCommand(e); + else if(e.getMessage().startsWith("+")) + onPlusMessage(e); else onChat(e); } @@ -64,6 +63,10 @@ public class ChatListener extends BasicListener { e.setMessage("/" + e.getMessage().substring(1)); }else if(begin.startsWith("77") && begin.substring(2).matches("[A-Za-z]+")){ e.setMessage("//" + e.getMessage().substring(2)); + }else if(begin.startsWith("7/") && begin.substring(2).matches("[A-Za-z]+")){ + e.setMessage("//" + e.getMessage().substring(2)); + }else if(begin.startsWith("/7") && begin.substring(2).matches("[A-Za-z]+")){ + e.setMessage("//" + e.getMessage().substring(2)); } } @@ -116,6 +119,20 @@ public class ChatListener extends BasicListener { scheduler.schedule(BungeeCore.get(), () -> Message.sendPrefixless("MSG_FORMAT", sender, "YoyoNow", sender.getDisplayName(), Message.parse("CHAT_YOYONOW_4", sender)), 12, TimeUnit.SECONDS); } + private void onPlusMessage(ChatEvent e) { + ProxiedPlayer p = (ProxiedPlayer) e.getSender(); + Subserver subserver = Subserver.getSubserver(p); + if(subserver instanceof Bauserver) { + String[] smolArgs = e.getMessage().substring(1).split(" "); + String[] args = new String[smolArgs.length + 1]; + args[0] = ""; + System.arraycopy(smolArgs, 0, args, 1, smolArgs.length); + localChat(e, args); + } else { + onChat(e); + } + } + private void onChat(ChatEvent e){ if(e.getSender() instanceof ProxiedPlayer){ ProxiedPlayer sender = (ProxiedPlayer) e.getSender(); @@ -157,7 +174,7 @@ public class ChatListener extends BasicListener { String name = sender.getDisplayName(); String chatcolor = user.getUserGroup().getChatColorCode(); - if(user.getUserGroup() != UserGroup.Member) + if(user.getUserGroup() != UserGroup.Member || user.getTeam() == 12 || user.getTeam() == 285 || user.getTeam() == 54) message = ChatColor.translateAlternateColorCodes('&', message); String msg = name + "§7»" + chatcolor + " " + message; @@ -166,22 +183,11 @@ public class ChatListener extends BasicListener { msg = "§" + team.getTeamColor() + team.getTeamKuerzel() + " §r" + msg; } - String[] msgSplits = msg.split(" "); - StringBuilder builder = new StringBuilder(); - for(ProxiedPlayer target : ProxyServer.getInstance().getPlayers()){ Subserver targetServer = Subserver.getSubserver(target); if(!(targetServer == null || targetServer.getType() != Servertype.ARENA || targetServer.getServer() != target.getServer().getInfo())) continue; - for (String curr : msgSplits) { - if(curr.equalsIgnoreCase("@" + target.getName())) { - builder.append("§e@").append(target.getName()).append(chatcolor).append(" "); - new PingPacket(SteamwarUser.get(target).getId()).send(target); - }else { - builder.append(curr).append(" "); - } - } - BungeeCore.send(target, ChatMessageType.CHAT, builder.toString()); - builder = new StringBuilder(); + + BungeeCore.send(target, ChatMessageType.CHAT, parseAtMessage(msg, chatcolor, target)); } BungeeCore.log(sender.getServer().getInfo(), msg); } @@ -211,6 +217,28 @@ public class ChatListener extends BasicListener { e.setMessage(e.getMessage().substring(command[0].length() + 1)); } + public static String parseAtMessage(String message, String returnColor, ProxiedPlayer player) { + if(!message.contains("@")) { + return message; + } + + StringBuilder builder = new StringBuilder(); + for (String curr : message.split(" ")) { + if(curr.toLowerCase().startsWith("@" + player.getName().toLowerCase())) { + new PingPacket(SteamwarUser.get(player).getId()).send(player); + builder.append("§e@") + .append(player.getName()) + .append(returnColor) + .append(curr.substring(player.getName().length() + 1)) + .append(" "); + }else { + builder.append(curr) + .append(" "); + } + } + return builder.toString(); + } + @EventHandler public void onTabCompleteEvent(TabCompleteEvent e){ List suggestions = e.getSuggestions(); @@ -226,6 +254,17 @@ public class ChatListener extends BasicListener { suggestions.add(name); } } + + if(last.startsWith("@")) { + String plrName = last.replace("@", ""); + for(ProxiedPlayer player : ProxyServer.getInstance().getPlayers()){ + String name = player.getName(); + if ((plrName.isEmpty() || name.startsWith(plrName)) && !plrName.equalsIgnoreCase(name)) { + suggestions.add("@" + name); + } + } + } + if(e.getSender() instanceof ProxiedPlayer && cursor.length == 1 && cursor[0].startsWith("/")){ ProxiedPlayer player = (ProxiedPlayer) e.getSender(); for(String cmd : BungeeCore.commands.keySet()){ diff --git a/src/de/steamwar/bungeecore/listeners/ConnectionListener.java b/src/de/steamwar/bungeecore/listeners/ConnectionListener.java index 7f0f88f9..ef04b147 100644 --- a/src/de/steamwar/bungeecore/listeners/ConnectionListener.java +++ b/src/de/steamwar/bungeecore/listeners/ConnectionListener.java @@ -24,15 +24,13 @@ import de.steamwar.bungeecore.Message; import de.steamwar.bungeecore.Servertype; import de.steamwar.bungeecore.Subserver; import de.steamwar.bungeecore.commands.ChallengeCommand; +import de.steamwar.bungeecore.commands.CheckCommand; import de.steamwar.bungeecore.commands.MsgCommand; import de.steamwar.bungeecore.sql.SteamwarUser; import de.steamwar.bungeecore.sql.UserGroup; import net.md_5.bungee.api.AbstractReconnectHandler; 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.config.ServerInfo; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.event.PlayerDisconnectEvent; @@ -66,8 +64,10 @@ public class ConnectionListener extends BasicListener { if(user.getUserGroup().isAdminGroup()) player.addGroups(ADMIN_GROUP); - if(user.getUserGroup().isTeamGroup()) + if(user.getUserGroup().isTeamGroup()) { player.addGroups(TEAM_GROUP); + CheckCommand.sendReminder(player); + } if(user.getUserGroup().isCheckSchematics()) player.setPermission(CHECK_PERMISSION, true); @@ -139,6 +139,6 @@ public class ConnectionListener extends BasicListener { ProxiedPlayer player = e.getPlayer(); Collection players = server.getPlayers(); if(players.isEmpty() || (players.size() == 1 && players.contains(player))) - ProxyServer.getInstance().getScheduler().runAsync(BungeeCore.get(), subserver::stop); + ProxyServer.getInstance().getScheduler().runAsync(BungeeCore.get(), subserver::waitForTermination); } } diff --git a/src/de/steamwar/bungeecore/listeners/TablistManager.java b/src/de/steamwar/bungeecore/listeners/TablistManager.java index c0b12fdf..d69a1e81 100644 --- a/src/de/steamwar/bungeecore/listeners/TablistManager.java +++ b/src/de/steamwar/bungeecore/listeners/TablistManager.java @@ -27,6 +27,7 @@ import de.steamwar.bungeecore.Message; import de.steamwar.bungeecore.Servertype; import de.steamwar.bungeecore.Subserver; import de.steamwar.bungeecore.sql.SteamwarUser; +import de.steamwar.bungeecore.sql.Team; import de.steamwar.bungeecore.sql.UserGroup; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.config.ServerInfo; @@ -41,6 +42,7 @@ import java.io.File; import java.io.IOException; import java.util.*; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; public class TablistManager extends BasicListener { @@ -67,38 +69,29 @@ public class TablistManager extends BasicListener { } } - private void calculateSize(){ + private void calculateSize() { size = -1; - for(Map.Entry> server : playerMap.entrySet()) - size += 2 + server.getValue().size(); - - size = (size+19)/20; - - if(size > 5) - size = 5; + size += playerMap.size() * 2 + ProxyServer.getInstance().getPlayers().size(); + size = (size + 19) / 20; + if(size > 5) size = 5; } private synchronized void updateCustomTablist(){ //Calculate server-player-map playerMap.clear(); - for(ProxiedPlayer player : ProxyServer.getInstance().getPlayers()){ + for (ProxiedPlayer player : ProxyServer.getInstance().getPlayers()) { Server pserver = player.getServer(); - if(pserver == null) //Happens temporarily + if (pserver == null) //Happens temporarily continue; ServerInfo server = pserver.getInfo(); String serverName = server.getName(); Subserver subserver = Subserver.getSubserver(server); - List players; - if(subserver != null && subserver.getType() == Servertype.BAUSERVER){ - players = playerMap.getOrDefault("Bau", new ArrayList<>()); - players.add(player); - playerMap.putIfAbsent("Bau", players); - }else{ - players = playerMap.getOrDefault(serverName, new ArrayList<>()); - players.add(player); - playerMap.putIfAbsent(serverName, players); + if (subserver != null && subserver.getType() == Servertype.BAUSERVER) { + playerMap.computeIfAbsent("Bau", s -> new ArrayList<>()).add(player); + } else { + playerMap.computeIfAbsent(serverName, s -> new ArrayList<>()).add(player); } } playerMap.forEach((server, players) -> players.sort((proxiedPlayer, t1) -> proxiedPlayer.getName().compareToIgnoreCase(t1.getName()))); @@ -131,15 +124,13 @@ public class TablistManager extends BasicListener { } private String calcHeader(ProxiedPlayer player){ - int phase = (seconds % 16) / 4; + int phase = (seconds % 16) / 3; switch(phase){ case 0: - return Message.parse("TABLIST_PHASE_0", player, ProxyServer.getInstance().getPlayers().size()); - case 1: return Message.parse("TABLIST_PHASE_1", player); - case 2: + case 1: return Message.parse("TABLIST_PHASE_2", player); - case 3: + case 2: default: return Message.parse("TABLIST_PHASE_DEFAULT", player); } @@ -157,13 +148,18 @@ public class TablistManager extends BasicListener { } private void refresh(){ + ServerInfo currentServer = player.getServer().getInfo(); + setHeader(calcHeader(player)); + // TABLIST_FOOTER=§e{0} {1}§8ms §eSpieler§8: §7{2} + setFooter("§e" + currentServer.getName() + " " + getPing() + "§8ms §eSpieler§8: §7" + ProxyServer.getInstance().getPlayers().size()); + setSize(size, 20); + + if (size >= 5) { + refreshSlim(currentServer); + return; + } try { - ServerInfo currentServer = player.getServer().getInfo(); - setHeader(calcHeader(player)); - setFooter("§e" + currentServer.getName() + " " + getPing() + "§8ms"); - setSize(size, 20); - int i = 0; for (String server : playerMap.navigableKeySet()) { if (i > 0){ @@ -174,24 +170,85 @@ public class TablistManager extends BasicListener { server = Message.parse("TABLIST_BAU", player); setSlot(i%20, i/20, gray, "§7§l" + server, 1000); i++; - for (ProxiedPlayer p : playerMap.get(server)){ - boolean sameServer = currentServer == p.getServer().getInfo(); - setSlot(i % 20, i / 20, BungeeTabListPlusAPI.getIconFromPlayer(p), getTablistName(p, sameServer), (sameServer ? 1 : 500)); - i++; - } + i = update(currentServer, playerMap.get(server), i); } - while (i < size*20){ - setSlot(i%20, i/20, darkGray, "", 1000); - i++; - } + finish(i); }catch(IndexOutOfBoundsException | NullPointerException e){ //Ignore IndexOutOfBoundsException //Ignore NPE, happens sometimes (only 1s long) when somebody is joining, server switching or disconnecting } } - private String getTablistName(ProxiedPlayer p, boolean sameServer) { + private void refreshSlim(ServerInfo currentServer) { + try { + int i = 0; + boolean spacer = true; + for (String server : playerMap.navigableKeySet()) { + if (i > 0 && spacer) { + setSlot(i%20, i/20, darkGray, "", 1000); + i++; + } + spacer = true; + Team team = getTeam(player); + List players = playerMap.get(server) + .stream() + .filter(p -> p.getServer().getInfo() == currentServer || SteamwarUser.get(p).getUserGroup() != UserGroup.Member || (team != null && team == getTeam(p))) + .collect(Collectors.toList()); + + Subserver subserver = Subserver.getSubserver(player); + if (server.equals("Bau")) { + if (subserver != null && subserver.getType() == Servertype.BAUSERVER) { + players = playerMap.get(server); + } + server = Message.parse("TABLIST_BAU", player); + } + if (subserver != null && subserver.getType() == Servertype.ARENA && playerMap.get(server).size() == 1) { + players = playerMap.get(server); + } + + int increment = playerMap.get(server).size() - players.size(); + if (players.isEmpty()) { + server += " §7(" + increment + ")"; + spacer = false; + } else if (increment != 0) { + server += " §7(+" + increment + ")"; + } + + setSlot(i%20, i/20, gray, "§7§l" + server, 1000); + i++; + i = update(currentServer, players, i); + } + + finish(i); + }catch(IndexOutOfBoundsException | NullPointerException e){ + //Ignore IndexOutOfBoundsException + //Ignore NPE, happens sometimes (only 1s long) when somebody is joining, server switching or disconnecting + } + } + + private int update(ServerInfo currentServer, List players, int i) { + for (ProxiedPlayer p : players){ + boolean sameServer = currentServer == p.getServer().getInfo(); + setSlot(i % 20, i / 20, BungeeTabListPlusAPI.getIconFromPlayer(p), getTablistName(p), (sameServer ? 1 : 500)); + i++; + } + return i; + } + + private void finish(int i) { + while (i < size*20){ + setSlot(i%20, i/20, darkGray, "", 1000); + i++; + } + } + + private Team getTeam(ProxiedPlayer p) { + Team team = Team.get(SteamwarUser.get(p).getTeam()); + return team.getTeamId() <= 0 ? null : team; + } + + private String getTablistName(ProxiedPlayer p) { Subserver server = Subserver.getSubserver(p); if(server != null){ String tablistName = server.getTablistNames().get(p); @@ -202,13 +259,14 @@ public class TablistManager extends BasicListener { StringBuilder st = new StringBuilder(); UserGroup group = SteamwarUser.get(p).getUserGroup(); - //else st.append("§7"); - if(group == UserGroup.Member && sameServer) - st.append("§f"); - else + if (group == UserGroup.Member) { + Team team = getTeam(player); + if (team != null && team == getTeam(p)) st.append("§f"); + else st.append("§7"); + } else { st.append(group.getColorCode()); + } - // if (!sameServer) st.append("§o"); return st.append(p.getName()).toString(); } } diff --git a/src/de/steamwar/bungeecore/listeners/mods/ModLoaderBlocker.java b/src/de/steamwar/bungeecore/listeners/mods/ModLoaderBlocker.java index 11e8d874..281830cf 100644 --- a/src/de/steamwar/bungeecore/listeners/mods/ModLoaderBlocker.java +++ b/src/de/steamwar/bungeecore/listeners/mods/ModLoaderBlocker.java @@ -29,9 +29,13 @@ import net.md_5.bungee.api.event.ServerSwitchEvent; import net.md_5.bungee.event.EventHandler; import java.nio.charset.StandardCharsets; +import java.util.HashSet; +import java.util.Set; public class ModLoaderBlocker extends BasicListener { + private static final Set BLOCKED_SERVER = new HashSet<>(); + @EventHandler public void onPluginMessageEvent(PluginMessageEvent e){ Connection sender = e.getSender(); @@ -79,8 +83,9 @@ public class ModLoaderBlocker extends BasicListener { @EventHandler public void onServerSwitch(ServerSwitchEvent event) { - if(Subserver.getSubserver(event.getPlayer()) != null - && Subserver.getSubserver(event.getPlayer()).getType() == Servertype.ARENA + if(((Subserver.getSubserver(event.getPlayer()) != null + && Subserver.getSubserver(event.getPlayer()).getType() == Servertype.ARENA) + || BLOCKED_SERVER.contains(event.getPlayer().getServer().getInfo().getName())) && isFabric(event.getPlayer())) { event.getPlayer().connect(BungeeCore.get().getProxy().getServerInfo(BungeeCore.LOBBY_SERVER)); Message.send("MODLOADER_DENIED", event.getPlayer()); @@ -90,4 +95,8 @@ public class ModLoaderBlocker extends BasicListener { public static boolean isFabric(ProxiedPlayer player) { return Storage.fabricPlayers.contains(player); } + + public static void addServer(String server) { + BLOCKED_SERVER.add(server); + } } diff --git a/src/de/steamwar/bungeecore/sql/BauweltMember.java b/src/de/steamwar/bungeecore/sql/BauweltMember.java index e35f0e5c..f05cab23 100644 --- a/src/de/steamwar/bungeecore/sql/BauweltMember.java +++ b/src/de/steamwar/bungeecore/sql/BauweltMember.java @@ -30,27 +30,25 @@ import java.util.UUID; public class BauweltMember{ private final int bauweltID; private final int memberID; - private boolean build; private boolean worldEdit; private boolean world; - private BauweltMember(int ownerID, int memberID, boolean build, boolean worldEdit, boolean world, boolean updateDB){ + private BauweltMember(int ownerID, int memberID, boolean worldEdit, boolean world, boolean updateDB){ bauweltID = ownerID; this.memberID = memberID; - this.build = build; this.worldEdit = worldEdit; this.world = world; if(updateDB) updateDB(); } - public BauweltMember(int ownerID, int memberID, boolean build, boolean worldEdit, boolean world){ - this(ownerID, memberID, build, worldEdit, world, true); + public BauweltMember(int ownerID, int memberID, boolean worldEdit, boolean world){ + this(ownerID, memberID, worldEdit, world, true); } - public BauweltMember(UUID ownerID, UUID memberID, boolean build, boolean worldEdit, boolean world){ - this(SteamwarUser.get(ownerID).getId(), SteamwarUser.get(memberID).getId(), build, worldEdit, world); + public BauweltMember(UUID ownerID, UUID memberID, boolean worldEdit, boolean world){ + this(SteamwarUser.get(ownerID).getId(), SteamwarUser.get(memberID).getId(), worldEdit, world); } public void remove(){ @@ -58,8 +56,8 @@ public class BauweltMember{ } private void updateDB(){ - SQL.update("INSERT INTO BauweltMember (BauweltID, MemberID, Build, WorldEdit, World) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE Build = VALUES(Build), WorldEdit = VALUES(WorldEdit), World = VALUES(World)", - bauweltID, memberID, build, worldEdit, world); + SQL.update("INSERT INTO BauweltMember (BauweltID, MemberID, WorldEdit, World) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE WorldEdit = VALUES(WorldEdit), World = VALUES(World)", + bauweltID, memberID, worldEdit, world); } public static BauweltMember getBauMember(UUID ownerID, UUID memberID){ @@ -72,10 +70,9 @@ public class BauweltMember{ if(member == null || !member.next()){ return null; } - boolean build = member.getBoolean("Build"); boolean worldEdit = member.getBoolean("WorldEdit"); boolean world = member.getBoolean("World"); - return new BauweltMember(ownerID, memberID, build, worldEdit, world, false); + return new BauweltMember(ownerID, memberID, worldEdit, world, false); } catch (SQLException e) { BungeeCore.log("Could not load BauweltMember", e); } @@ -92,10 +89,9 @@ public class BauweltMember{ List members = new ArrayList<>(); while(memberlist.next()){ int memberID = memberlist.getInt("MemberID"); - boolean build = memberlist.getBoolean("Build"); boolean worldEdit = memberlist.getBoolean("WorldEdit"); boolean world = memberlist.getBoolean("World"); - members.add(new BauweltMember(bauweltID, memberID, build, worldEdit, world, false)); + members.add(new BauweltMember(bauweltID, memberID, worldEdit, world, false)); } return members; }catch(SQLException e){ @@ -112,15 +108,6 @@ public class BauweltMember{ return memberID; } - public boolean isBuild() { - return build; - } - - public void setBuild(boolean build) { - this.build = build; - updateDB(); - } - public boolean isWorldEdit() { return worldEdit; } diff --git a/src/de/steamwar/bungeecore/sql/SteamwarUser.java b/src/de/steamwar/bungeecore/sql/SteamwarUser.java index 1a1aefd7..d221c8b7 100644 --- a/src/de/steamwar/bungeecore/sql/SteamwarUser.java +++ b/src/de/steamwar/bungeecore/sql/SteamwarUser.java @@ -19,10 +19,15 @@ package de.steamwar.bungeecore.sql; +import com.google.gson.JsonParser; import de.steamwar.bungeecore.BungeeCore; import de.steamwar.bungeecore.Message; import de.steamwar.bungeecore.commands.WebregisterCommand; import de.steamwar.bungeecore.listeners.ConnectionListener; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Scanner; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.connection.PendingConnection; @@ -38,18 +43,22 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; + public class SteamwarUser { private final int id; private final UUID uuid; private String userName; private UserGroup userGroup; private int team; + private boolean leader; private Map punishments; private static final Map usersByName = new HashMap<>(); private static final Map usersByUUID = new HashMap<>(); private static final Map usersById = new HashMap<>(); private static final InetAddress LIXFEL_DE; + private static final String API_URL = "https://api.mojang.com/users/profiles/minecraft/"; + private static final JsonParser jsonParser = new JsonParser(); static { try { @@ -66,6 +75,7 @@ public class SteamwarUser { userName = rs.getString("UserName"); userGroup = UserGroup.getUsergroup(rs.getString("UserGroup")); team = rs.getInt("Team"); + leader = rs.getBoolean("Leader"); usersById.put(id, this); usersByName.put(userName.toLowerCase(), this); usersByUUID.put(uuid, this); @@ -83,8 +93,7 @@ public class SteamwarUser { user.userName = userName; } }else{ - SQL.update("INSERT INTO UserData (UUID, UserName, UserGroup) VALUES (?, ?, 'Member')", connection.getUniqueId().toString(), connection.getName()); - user = dbInit(SQL.select("SELECT * FROM UserData WHERE UUID = ?", connection.getUniqueId().toString())); + user = SteamwarUser.createUserInDatabase(connection.getUniqueId(), connection.getName()); if(user == null) throw new SecurityException("user == null"); ConnectionListener.newPlayer(user.uuid); @@ -95,6 +104,25 @@ public class SteamwarUser { return user; } + public static SteamwarUser getOrCreateOfflinePlayer(String name){ + SteamwarUser user = SteamwarUser.get(name); + if (user != null) { + return user; + } + + UUID uuid = SteamwarUser.loadUUID(name); + if (uuid == null) { + return null; + } + + return SteamwarUser.createUserInDatabase(uuid, name); + } + + private static SteamwarUser createUserInDatabase(UUID uuid, String name) { + SQL.update("INSERT INTO UserData (UUID, UserName, UserGroup) VALUES (?, ?, 'Member')", uuid.toString(), name); + return dbInit(SQL.select("SELECT * FROM UserData WHERE UUID = ?", uuid.toString())); + } + public static SteamwarUser get(String userName){ userName = userName.toLowerCase(); if(usersByName.containsKey(userName)) @@ -124,9 +152,26 @@ public class SteamwarUser { usersByUUID.clear(); } + public static UUID loadUUID(String playerName) { + try { + final URL url = new URL(API_URL + playerName); + return getUniqueIdFromString(jsonParser.parse(new Scanner(url.openConnection().getInputStream()).nextLine()).getAsJsonObject().get("id").getAsString()); + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + private static UUID getUniqueIdFromString(String uuid) { + return UUID.fromString(uuid.replaceFirst("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5")); + } + public void setTeam(int team){ this.team = team; SQL.update("Update UserData SET Team = ? WHERE id = ?", team, id); + setLeader(false); } public int getId() { @@ -261,4 +306,13 @@ public class SteamwarUser { throw new SecurityException("Could not load First Join"); } } + + public boolean isLeader() { + return leader; + } + + public void setLeader(boolean leader) { + this.leader = leader; + SQL.update("Update UserData SET Leader = ? WHERE id = ?", leader, id); + } } diff --git a/src/de/steamwar/bungeecore/sql/Team.java b/src/de/steamwar/bungeecore/sql/Team.java index 12855478..b5c083bd 100644 --- a/src/de/steamwar/bungeecore/sql/Team.java +++ b/src/de/steamwar/bungeecore/sql/Team.java @@ -33,17 +33,15 @@ public class Team { private final int teamId; private String teamKuerzel; private String teamName; - private int teamLeader; private String teamColor; private static final List teamCache = new LinkedList<>(); - private static final Team pub = new Team(0, "PUB", "Öffentlich", 0, "8"); + private static final Team pub = new Team(0, "PUB", "Öffentlich", "8"); - private Team(int id, String kuerzel, String name, int leader, String color){ + private Team(int id, String kuerzel, String name, String color){ teamId = id; teamKuerzel = kuerzel; teamName = name; - teamLeader = leader; teamColor = color; if (id != 0) { teamCache.add(this); @@ -51,16 +49,16 @@ public class Team { } private Team(ResultSet rs) throws SQLException { - this(rs.getInt("TeamID"), rs.getString("TeamKuerzel"), rs.getString("TeamName"), rs.getInt("TeamLeader"), rs.getString("TeamColor")); + this(rs.getInt("TeamID"), rs.getString("TeamKuerzel"), rs.getString("TeamName"), rs.getString("TeamColor")); } - public static void create(String kuerzel, String name, int leader){ - SQL.update("INSERT INTO Team (TeamKuerzel, TeamName, TeamLeader) VALUES (?, ?, ?)", kuerzel, name, leader); + public static void create(String kuerzel, String name, SteamwarUser user){ + SQL.update("INSERT INTO Team (TeamKuerzel, TeamName) VALUES (?, ?)", kuerzel, name); } public static Team get(int id){ if(id == -1) - return new Team(-1, "?", "?", 0, "8"); + return new Team(-1, "?", "?", "8"); if(id == 0) return pub; for(Team team : teamCache) @@ -110,7 +108,7 @@ public class Team { } private void updateDB(){ - SQL.update("INSERT INTO Team (TeamID, TeamKuerzel, TeamName, TeamLeader, TeamColor) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE TeamName = VALUES(TeamName), TeamKuerzel = VALUES(TeamKuerzel), TeamLeader = VALUES(TeamLeader), TeamColor = VALUES(TeamColor)", teamId, teamKuerzel, teamName, teamLeader, teamColor); + SQL.update("INSERT INTO Team (TeamID, TeamKuerzel, TeamName, TeamColor) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE TeamName = VALUES(TeamName), TeamKuerzel = VALUES(TeamKuerzel), TeamColor = VALUES(TeamColor)", teamId, teamKuerzel, teamName, teamColor); } public int getTeamId() { @@ -135,15 +133,6 @@ public class Team { updateDB(); } - public int getTeamLeader() { - return teamLeader; - } - - public void setTeamLeader(int teamLeader) { - this.teamLeader = teamLeader; - updateDB(); - } - public String getTeamColor() { return teamColor; } @@ -164,8 +153,9 @@ public class Team { } } - public void disband(){ - SQL.update("UPDATE Team SET TeamDeleted = 1, TeamLeader = NULL WHERE TeamID = ?", teamId); + public void disband(SteamwarUser user){ + user.setLeader(false); + SQL.update("UPDATE Team SET TeamDeleted = 1 WHERE TeamID = ?", teamId); teamCache.remove(this); } diff --git a/src/de/steamwar/messages/BungeeCore.properties b/src/de/steamwar/messages/BungeeCore.properties index 71ff3cad..669ea673 100644 --- a/src/de/steamwar/messages/BungeeCore.properties +++ b/src/de/steamwar/messages/BungeeCore.properties @@ -7,6 +7,11 @@ UNKNOWN_PLAYER=§cDiesen Spieler gibt es nicht. UNKNOWN_TEAM=§cDieses Team gibt es nicht. INVALID_TIME=§cUngültige Zeitangabe. +STEAMWAR_BRAND=§eSteam§8War.de §7({0}) §r<- §e{1} §7({2})§r + +DEV_NO_SERVER=§cDer Server ist derzeit nicht erreichbar. +DEV_UNKNOWN_SERVER=§cBitte gib einen DevServer an. + #ModLoader blocker MODLOADER_INSTALLED=§7Du hast §e{0} §7installiert. Daher kannst du keinen Arenen beitreten. MODLOADER_DENIED=§cMit Fabric, Forge und LiteLoader kannst du keinen Arenen beitreten. @@ -79,8 +84,6 @@ HELP_BAU_ADDMEMBER=§8/§ebau addmember §8- §7Fügt einen Freund hinzu HELP_BAU_ADDMEMBER_HOVER=§eFüge einen Freund hinzu HELP_BAU_DELMEMBER=§8/§ebau delmember §8- §7Entfernt einen Spieler HELP_BAU_DELMEMBER_HOVER=§eEntfernt einen Spieler -HELP_BAU_TOGGLEBUILD=§8/§ebau togglebuild §8- §7Berechtigung zum Bauen, /tp, /gm -HELP_BAU_TOGGLEBUILD_HOVER=§eStellt Spielerberechtigungen ein HELP_BAU_TOGGLEWE=§8/§ebau togglewe §8- §7Berechtigung für WorldEdit, /testblock HELP_BAU_TOGGLEWE_HOVER=§eStellt Spielerberechtigungen ein HELP_BAU_TOGGLEWORLD=§8/§ebau toggleworld §8- §7/reset, /trace, /fire, /tnt @@ -104,6 +107,8 @@ MOD_YELLOW_PLUR=§7Deaktiviere die Mods\n§e{0}\n§7um weiter auf §eSteam§8War #Various commands ALERT=§f{0} +STAT_SERVER=§7Server §f{0} - §7Ram §f{1} §7CPU §f{2} §7Server Count §f{3} +NO_STATS=§7Kein Bau oder Fight Server gestartet #Ban&Mute-Command BAN_TEAM_BANNED={0} §c{1} wurde von {2} {3} gebannt. §f§lGrund: §f{4} @@ -201,6 +206,7 @@ FIGHT_BROADCAST_HOVER=§aGegen §7{1} §ekämpfen #CheckCommand CHECK_REMINDER=§7Es sind §e{0} §7Schematics zu prüfen§8! +CHECK_REMINDER_HOVER=§eZu prüfende Schematics CHECK_NOT_CHECKING=§cDu prüfst derzeit nicht. CHECK_HELP_LIST=§8/§7check list §8- §7Zeigt die Liste der ungeprüften Schematics CHECK_HELP_NEXT=§8/§7check next §8- §7Nächste Prüffrage§8/§7freigeben @@ -224,7 +230,7 @@ CHECK_RANK_HOVER=§aMit diesem Rang freigeben CHECK_ACCEPTED=§aDein §e{0} {1} §ewurde freigegeben§8! CHECK_ACCEPTED_TEAM=§7Die Schematic §e{0} §7von §e{1} §7ist nun freigegeben! CHECK_DECLINED=§cDein §e{0} {1} §cwurde abgelehnt§8: §c{2} -CHECK_DECLINED_TEAM=§7Die Schematic §e{0} §7von §e{1} §awurde aufgrund von §e{2} §7abgelehnt! +CHECK_DECLINED_TEAM=§7Die Schematic §e{0} §7von §e{1} §7wurde aufgrund von §e{2} §7abgelehnt! #HistoricCommand HISTORIC_BROADCAST=§7Historischer §e{0}§8-§7Kampf von §e{1}§8! @@ -325,8 +331,9 @@ TEAM_HELP_INVITE=§8/§7team invite §8- §7Lade jemanden in dein Team ein. TEAM_HELP_REMOVE=§8/§7team remove §8- §7Entferne jemanden aus deinem Team. TEAM_HELP_KUERZEL=§8/§7team changekuerzel §8- §7Ändere dein Teamkürzel. TEAM_HELP_NAME=§8/§7team changename §8- §7Ändere deinen Teamnamen. -TEAM_HELP_COLOR=§8/§7eteam changecolor §8- §7Ändere deine Teamfarbe. -TEAM_HELP_LEADER=§8/§7eteam changeleader §8- §7Ernenne jemanden zum Teamleader. +TEAM_HELP_COLOR=§8/§7team changecolor §8- §7Ändere deine Teamfarbe. +TEAM_HELP_LEADER=§8/§7team promote §8- §7Ernenne jemanden zum Teamleader. +TEAM_HELP_STEP_BACK=§8/§7team stepback §8- §7Tritt als Leader zurück. #Team Create TEAM_CREATE_USAGE=§8/§7team create §8[§eTeamkürzel§8] §8[§eTeamname§8] @@ -340,9 +347,12 @@ TEAM_JOIN_NOT_BY_TEAM=§cVon diesem Team wurdest du nicht eingeladen. TEAM_JOIN_JOINED=§7Du bist dem Team §e{0} §7beigetreten! #Team Leave -TEAM_LEAVE_OTHER_LEADER=§cBitte ernenne zunächst ein anderes Teammitglied zum Leader! +TEAM_OTHER_LEADER_REQUIRED=§cBitte ernenne zunächst ein anderes Teammitglied zum Leader! TEAM_LEAVE_LEFT=§7Du hast dein Team verlassen! +#Team Step Back +TEAM_STEP_BACK=§7Du hast deinen Posten als Teamleader abgegeben§8! + #Team Invite TEAM_INVITE_USAGE=§8/§7team invite §8[§eSpieler§8] TEAM_INVITE_NO_PLAYER=§cDiesen Spieler gibt es nicht. @@ -354,7 +364,7 @@ TEAM_INVITE_INVITED_TARGET=§7Du wurdest in das Team §{0}{1} §7eingeladen! #Team Remove TEAM_REMOVE_USAGE=§8/§7team remove §8[§eSpieler§8] TEAM_REMOVE_NOT_PLAYER=§cDiesen Spieler gibt es nicht. -TEAM_REMOVE_NOT_LEADER=§cLeader entfernen sich nicht selbst. +TEAM_REMOVE_NOT_LEADER=§cLeader können nicht rausgeworfen werden. TEAM_REMOVE_INVITE=§7Die Einladung wurde zurückgezogen. TEAM_REMOVE_NO_INVITE=§cDieser Spieler hat keine Einladung erhalten. TEAM_REMOVE_NOT_IN_TEAM=§cDieser Spieler ist nicht in deinem Team. @@ -374,10 +384,10 @@ TEAM_NAME_LENGHT=§cEin Teamname muss aus 4 bis 15 Buchstaben bestehen. TEAM_NAME_TAKEN=§cEs gibt bereits ein Team mit diesem Namen. #Team Leader -TEAM_LEADER_USAGE=§8/§7team changeleader §8[§eMember§8] +TEAM_LEADER_USAGE=§8/§7team promote §8[§eMember§8] TEAM_LEADER_NOT_USER=§cDen Spieler {0} gibt es nicht. TEAM_LEADER_NOT_MEMBER=§cDer Spieler ist nicht in deinem Team. -TEAM_LEADER_CHANGED=§7Du hast den Spieler §e{0} §7zum Leader gemacht! +TEAM_LEADER_PROMOTED=§7Du hast den Spieler §e{0} §7zum Leader gemacht! #Team Info TEAM_INFO_USAGE=§8/§7team info §8[§eTeamname§8] @@ -390,7 +400,7 @@ TEAM_INFO_EVENTS=§7Events§8: §e{0} TEAM_LIST_NOT_PAGE=§cKeine Seitenzahl angegeben TEAM_LIST_UNKNOWN_PAGE=§cUngültige Seitenzahl angegeben TEAM_LIST_HEADER=§7§lTeamliste §7{0}§8/§7{1} -TEAM_LIST_TEAM=§{0}{1} §e{2} §8[§7{3}§8] +TEAM_LIST_TEAM=§{0}{1} §e{2} TEAM_LIST_TEAM_HOVER=§7Teaminfo TEAM_LIST_PAGE=Seite TEAM_LIST_NEXT=§eNächste Seite @@ -475,6 +485,7 @@ TABLIST_PHASE_1=§8Teamspeak: §eSteam§8War.de TABLIST_PHASE_2=§8Discord: §8https://§eSteam§8War.de/discord TABLIST_PHASE_DEFAULT=§8Website: https://§eSteam§8War.de TABLIST_BAU=Bau +LIST_COMMAND=§e{0}§8: §7{1} #EventStarter EVENT_FIGHT_BROADCAST=§7Hier §eklicken §7für den Kampf §{0}{1} §8vs §{2}{3}