diff --git a/.gitignore b/.gitignore index eba3be62..958dc459 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ lib .idea target +dependency-reduced-pom.xml \ No newline at end of file diff --git a/src/de/steamwar/bungeecore/BungeeCore.java b/src/de/steamwar/bungeecore/BungeeCore.java index 6409ad8c..b3be7cb2 100644 --- a/src/de/steamwar/bungeecore/BungeeCore.java +++ b/src/de/steamwar/bungeecore/BungeeCore.java @@ -24,10 +24,7 @@ import de.steamwar.bungeecore.bot.config.SteamwarDiscordBotConfig; import de.steamwar.bungeecore.commands.*; import de.steamwar.bungeecore.comms.SpigotReceiver; import de.steamwar.bungeecore.listeners.*; -import de.steamwar.bungeecore.listeners.mods.Forge; -import de.steamwar.bungeecore.listeners.mods.LabyMod; -import de.steamwar.bungeecore.listeners.mods.ModLoaderBlocker; -import de.steamwar.bungeecore.listeners.mods.WorldDownloader; +import de.steamwar.bungeecore.listeners.mods.*; import de.steamwar.bungeecore.sql.Punishment; import de.steamwar.bungeecore.sql.Statement; import de.steamwar.bungeecore.sql.SteamwarUser; @@ -80,7 +77,9 @@ public class BungeeCore extends Plugin { errorLogger = new ErrorLogger(); new ConnectionListener(); new Forge(); + new Forge12(); new LabyMod(); + new Badlion(); new ChatListener(); new BanListener(); new CheckListener(); @@ -127,6 +126,8 @@ public class BungeeCore extends Plugin { new VerifyCommand(); new ReplayCommand(); new GDPRQuery(); + new PlaytimeCommand(); + new ArenaCommand(); // Punishment Commands: new PunishmentCommand("ban", Punishment.PunishmentType.Ban); diff --git a/src/de/steamwar/bungeecore/ErrorLogger.java b/src/de/steamwar/bungeecore/ErrorLogger.java index 80b417f1..17799bea 100644 --- a/src/de/steamwar/bungeecore/ErrorLogger.java +++ b/src/de/steamwar/bungeecore/ErrorLogger.java @@ -91,6 +91,7 @@ public class ErrorLogger extends Handler { contains.add("DownstreamBridge"); contains.add(" took "); contains.add("No client connected for pending server!"); + contains.add("Error authenticating "); ignoreContains = Collections.unmodifiableList(contains); } } diff --git a/src/de/steamwar/bungeecore/Message.java b/src/de/steamwar/bungeecore/Message.java index b9a6f585..a54694ed 100644 --- a/src/de/steamwar/bungeecore/Message.java +++ b/src/de/steamwar/bungeecore/Message.java @@ -44,24 +44,28 @@ public class Message { }; public static TextComponent parseToComponent(String message, boolean prefixed, CommandSender sender, Object... params){ - return new TextComponent(TextComponent.fromLegacyText(parse(message, prefixed, sender, params))); + return new TextComponent(TextComponent.fromLegacyText(parse(message, prefixed, locale(sender), params))); } public static String parsePrefixed(String message, CommandSender sender, Object... params){ - return parse(message, true, sender, params); + return parse(message, true, locale(sender), params); } public static String parse(String message, CommandSender sender, Object... params){ - return parse(message, false, sender, params); + return parse(message, false, locale(sender), params); } - private static String parse(String message, boolean prefixed, CommandSender sender, Object... params){ - Locale locale = null; - if(sender instanceof ProxiedPlayer) - locale = ((ProxiedPlayer)sender).getLocale(); + public static String parse(String message, Locale locale, Object... params){ + return parse(message, false, locale, params); + } + + private static Locale locale(CommandSender sender) { + return sender instanceof ProxiedPlayer ? ((ProxiedPlayer)sender).getLocale() : Locale.getDefault(); + } + + 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); String pattern = ""; if(prefixed) @@ -72,7 +76,7 @@ public class Message { for (int i = 0; i < params.length; i++) { if(params[i] instanceof Message) { Message msg = (Message) params[i]; - params[i] = parse(msg.getMessage(), sender, msg.getParams()); + params[i] = parse(msg.getMessage(), false, locale, msg.getParams()); } else if(params[i] instanceof Date) { params[i] = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale).format((Date) params[i]); } @@ -120,7 +124,7 @@ public class Message { public static void broadcast(String message, String onHover, ClickEvent onClick, Object... params){ for(ProxiedPlayer player : ProxyServer.getInstance().getPlayers()) - send(message, player, parse(onHover, false, player, params), onClick, params); + send(message, player, parse(onHover, player, params), onClick, params); } public static void broadcast(String message, Object... params){ diff --git a/src/de/steamwar/bungeecore/Node.java b/src/de/steamwar/bungeecore/Node.java index d53d2e8e..f07293aa 100644 --- a/src/de/steamwar/bungeecore/Node.java +++ b/src/de/steamwar/bungeecore/Node.java @@ -22,10 +22,7 @@ package de.steamwar.bungeecore; import net.md_5.bungee.api.ProxyServer; import java.io.*; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; +import java.util.*; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.logging.Level; @@ -34,6 +31,14 @@ public abstract class Node { private static final List OPENJ9_ARGS = Arrays.asList("-Xgc:excessiveGCratio=80", "-Xsyslog:none", "-Xtrace:none", "-Xdisableexplicitgc", "-XX:+AlwaysPreTouch", "-XX:+CompactStrings", "-XX:-HeapDumpOnOutOfMemory", "-XX:+ExitOnOutOfMemoryError", "-Dlog4j.configurationFile=log4j2.xml"); private static final double MIN_FREE_MEM = 4.0 * 1024 * 1024; // 4 GiB + private static final Set JAVA_8 = new HashSet<>(); + static { + JAVA_8.add("paper-1.8.8.jar"); + JAVA_8.add("paper-1.10.2.jar"); + JAVA_8.add("spigot-1.8.8.jar"); + JAVA_8.add("spigot-1.9.4.jar"); + JAVA_8.add("spigot-1.10.2.jar"); + } private static final List nodes = new ArrayList<>(); public static Node local = null; @@ -74,7 +79,10 @@ public abstract class Node { public abstract double getLoad(); protected void constructServerstart(File directory, List cmd, String serverJar, String worldDir, String levelName, int port, String xmx, String... dParams) { - cmd.add("java"); + if (JAVA_8.contains(serverJar)) + cmd.add("/usr/lib/jvm/java-8-openj9-amd64/bin/java"); + else + cmd.add("java"); for(String param : dParams){ cmd.add("-D" + param); @@ -82,6 +90,10 @@ public abstract class Node { cmd.add("-Xmx" + xmx); cmd.add("-Xshareclasses:nonfatal,name=" + directory.getName()); cmd.addAll(OPENJ9_ARGS); + if (!JAVA_8.contains(serverJar)) { + cmd.add("--add-opens"); + cmd.add("java.base/jdk.internal.misc=ALL-UNNAMED"); + } cmd.add("-jar"); cmd.add("/binarys/" + serverJar); cmd.add("--log-strip-color"); diff --git a/src/de/steamwar/bungeecore/commands/ArenaCommand.java b/src/de/steamwar/bungeecore/commands/ArenaCommand.java new file mode 100644 index 00000000..9abd0f5b --- /dev/null +++ b/src/de/steamwar/bungeecore/commands/ArenaCommand.java @@ -0,0 +1,56 @@ +/* + 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; + +public class ArenaCommand extends BasicCommand { + + public ArenaCommand() { + super("arena", null); + } + + @Override + public void execute(CommandSender sender, String[] args) { + if(!(sender instanceof ProxiedPlayer)) + return; + ProxiedPlayer player = (ProxiedPlayer) sender; + + ServerInfo server = ProxyServer.getInstance().getServerInfo(String.join(" ", args)); + if(server == null) { + Message.send("ARENA_NOT_FOUND", player); + return; + } + + Subserver subserver = Subserver.getSubserver(server); + if(subserver == null || subserver.getType() != Servertype.ARENA) { + Message.send("ARENA_NOT_FOUND", player); + return; + } + + subserver.sendPlayer(player); + } +} diff --git a/src/de/steamwar/bungeecore/commands/BauCommand.java b/src/de/steamwar/bungeecore/commands/BauCommand.java index 37955184..cc9b8ef6 100644 --- a/src/de/steamwar/bungeecore/commands/BauCommand.java +++ b/src/de/steamwar/bungeecore/commands/BauCommand.java @@ -119,7 +119,7 @@ public class BauCommand extends BasicCommand { return; } - new BauweltMember(p.getUniqueId(), target.getUuid(), true, false); + new BauweltMember(p.getUniqueId(), target.getUuid(), false, false); Message.send("BAU_ADDMEMBER_ADDED", p); ProxiedPlayer z = ProxyServer.getInstance().getPlayer(target.getUuid()); @@ -259,16 +259,22 @@ public class BauCommand extends BasicCommand { }); } - public static boolean stopBauserver(ProxiedPlayer p){ + 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 false; + 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())) { + 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 @@ -278,7 +284,8 @@ public class BauCommand extends BasicCommand { break; } } - return true; + + return !startingBau(p); } private static void testarena(ProxiedPlayer p, String[] args){ diff --git a/src/de/steamwar/bungeecore/commands/ChallengeCommand.java b/src/de/steamwar/bungeecore/commands/ChallengeCommand.java index ae34de97..1a2db5cf 100644 --- a/src/de/steamwar/bungeecore/commands/ChallengeCommand.java +++ b/src/de/steamwar/bungeecore/commands/ChallengeCommand.java @@ -82,7 +82,7 @@ public class ChallengeCommand extends BasicCommand { arena.sendPlayer(target); Message.broadcast("CHALLENGE_BROADCAST", "CHALLENGE_BROADCAST_HOVER", - new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/join " + player.getName()), mode.getDisplayName(), player.getName(), target.getName()); + new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/arena " + arena.getServer().getName()), mode.getDisplayName(), player.getName(), target.getName()); }else{ if(!challenges.containsKey(player)){ challenges.put(player, new LinkedList<>()); diff --git a/src/de/steamwar/bungeecore/commands/CheckCommand.java b/src/de/steamwar/bungeecore/commands/CheckCommand.java index ba662c03..a74dc788 100644 --- a/src/de/steamwar/bungeecore/commands/CheckCommand.java +++ b/src/de/steamwar/bungeecore/commands/CheckCommand.java @@ -301,7 +301,6 @@ public class CheckCommand extends BasicCommand { } private void decline(String reason){ - schematic.setType(SchematicType.Normal.toDB()); CheckedSchematic.create(schematic, SteamwarUser.get(checker.getUniqueId()).getId(), startTime, Timestamp.from(Instant.now()), reason); SteamwarUser user = SteamwarUser.get(schematic.getOwner()); ProxiedPlayer player = ProxyServer.getInstance().getPlayer(user.getUuid()); @@ -311,6 +310,7 @@ public class CheckCommand extends BasicCommand { DiscordSchemAlert.sendDecline(schematic, user, reason); } Message.team("CHECK_DECLINED_TEAM", schematic.getName(), user.getUserName(), reason); + schematic.setType(SchematicType.Normal.toDB()); stop(); } diff --git a/src/de/steamwar/bungeecore/commands/FightCommand.java b/src/de/steamwar/bungeecore/commands/FightCommand.java index 65eea1c6..0f995a91 100644 --- a/src/de/steamwar/bungeecore/commands/FightCommand.java +++ b/src/de/steamwar/bungeecore/commands/FightCommand.java @@ -167,7 +167,7 @@ public class FightCommand extends BasicCommand { Subserver arena = SubserverSystem.startArena(mode, map, 0, 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()); + , 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 6d1f936e..05379381 100644 --- a/src/de/steamwar/bungeecore/commands/HistoricCommand.java +++ b/src/de/steamwar/bungeecore/commands/HistoricCommand.java @@ -39,7 +39,7 @@ public class HistoricCommand extends BasicCommand { Subserver arena = SubserverSystem.startArena(mode, map, 0, 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()); + , new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/arena " + arena.getServer().getName()), mode.getDisplayName(), player.getName()); }); } diff --git a/src/de/steamwar/bungeecore/commands/PlaytimeCommand.java b/src/de/steamwar/bungeecore/commands/PlaytimeCommand.java new file mode 100644 index 00000000..23e16a09 --- /dev/null +++ b/src/de/steamwar/bungeecore/commands/PlaytimeCommand.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.commands; + +import de.steamwar.bungeecore.Message; +import de.steamwar.bungeecore.sql.SteamwarUser; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import org.apache.commons.lang3.LocaleUtils; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.Locale; + +public class PlaytimeCommand extends BasicCommand{ + + public PlaytimeCommand() { + super("playtime", null); + } + + @Override + public void execute(CommandSender sender, String[] strings) { + if(!(sender instanceof ProxiedPlayer)) + return; + + NumberFormat format = NumberFormat.getNumberInstance(((ProxiedPlayer)sender).getLocale()); + format.setMaximumFractionDigits(2); + String formattedText = format.format((SteamwarUser.get((ProxiedPlayer) sender).getOnlinetime() / (double) 3600)); + + Message.send("HOURS_PLAYED", sender, formattedText); + } +} diff --git a/src/de/steamwar/bungeecore/commands/RankedCommand.java b/src/de/steamwar/bungeecore/commands/RankedCommand.java index a9d87771..55c9b094 100644 --- a/src/de/steamwar/bungeecore/commands/RankedCommand.java +++ b/src/de/steamwar/bungeecore/commands/RankedCommand.java @@ -173,7 +173,7 @@ public class RankedCommand extends BasicCommand { arena.sendPlayer(wp2.player); Message.broadcast("RANKED_BROADCAST", "RANKED_BROADCAST_HOVER", - new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/join " + wp1.player.getName()), mode.getDisplayName(), wp1.player.getName(), wp2.player.getName()); + new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/arena " + arena.getServer().getName()), mode.getDisplayName(), wp1.player.getName(), wp2.player.getName()); } } } diff --git a/src/de/steamwar/bungeecore/commands/TeamCommand.java b/src/de/steamwar/bungeecore/commands/TeamCommand.java index 58a3c355..0cd0277d 100644 --- a/src/de/steamwar/bungeecore/commands/TeamCommand.java +++ b/src/de/steamwar/bungeecore/commands/TeamCommand.java @@ -647,7 +647,7 @@ public class TeamCommand extends BasicCommand { tab.add("event"); tab.add("changekuerzel"); tab.add("changename"); - tab.add("changeleader"); + tab.add("promote"); tab.add("changecolor"); }else if(args.length == 2){ if(args[1].equalsIgnoreCase("event")){ diff --git a/src/de/steamwar/bungeecore/commands/VerifyCommand.java b/src/de/steamwar/bungeecore/commands/VerifyCommand.java index 927c2765..cbc5430e 100644 --- a/src/de/steamwar/bungeecore/commands/VerifyCommand.java +++ b/src/de/steamwar/bungeecore/commands/VerifyCommand.java @@ -40,7 +40,15 @@ public class VerifyCommand extends BasicCommand { Message.send("VERIFY_USAGE", sender); return; } - byte[] bytes = Base64.getDecoder().decode(strings[0]); + + byte[] bytes; + try { + bytes = Base64.getDecoder().decode(strings[0]); + } catch (IllegalArgumentException e) { + Message.send("VERIFY_INVALID", sender); + return; + } + if(bytes.length != 16) { Message.send("VERIFY_INVALID", sender); return; diff --git a/src/de/steamwar/bungeecore/comms/PacketIdManager.java b/src/de/steamwar/bungeecore/comms/PacketIdManager.java index 01d474a5..8db10ad7 100644 --- a/src/de/steamwar/bungeecore/comms/PacketIdManager.java +++ b/src/de/steamwar/bungeecore/comms/PacketIdManager.java @@ -26,6 +26,7 @@ public class PacketIdManager { public static final byte TABLIST_NAME = 0x02; public static final byte PREPARE_SCHEM = 0x03; public static final byte BAUMEMBER_UPDATE = 0x04; + public static final byte EXECUTE_COMMAND = 0x05; //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 ab6d246f..8eadfd74 100644 --- a/src/de/steamwar/bungeecore/comms/SpigotReceiver.java +++ b/src/de/steamwar/bungeecore/comms/SpigotReceiver.java @@ -58,5 +58,6 @@ public class SpigotReceiver extends BasicListener { registerHandler(PacketIdManager.PREPARE_SCHEM, new PrepareSchemHandler()); registerHandler(PacketIdManager.I_AM_A_LOBBY, new ImALobbyHandler()); registerHandler(PacketIdManager.FIGHT_INFO, new FightInfoHandler()); + registerHandler(PacketIdManager.EXECUTE_COMMAND, new ExecuteCommandHandler()); } } diff --git a/src/de/steamwar/bungeecore/comms/handlers/ExecuteCommandHandler.java b/src/de/steamwar/bungeecore/comms/handlers/ExecuteCommandHandler.java new file mode 100644 index 00000000..0051e55f --- /dev/null +++ b/src/de/steamwar/bungeecore/comms/handlers/ExecuteCommandHandler.java @@ -0,0 +1,36 @@ +/* + 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.comms.handlers; + +import com.google.common.io.ByteArrayDataInput; +import de.steamwar.bungeecore.comms.SpigotHandler; +import de.steamwar.bungeecore.sql.SteamwarUser; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.config.ServerInfo; + +public class ExecuteCommandHandler implements SpigotHandler { + @Override + public void handle(ByteArrayDataInput in, ServerInfo info) { + SteamwarUser target = SteamwarUser.get(in.readInt()); + String command = in.readUTF(); + + ProxyServer.getInstance().getPluginManager().dispatchCommand(ProxyServer.getInstance().getPlayer(target.getUuid()), command); + } +} diff --git a/src/de/steamwar/bungeecore/listeners/mods/Badlion.java b/src/de/steamwar/bungeecore/listeners/mods/Badlion.java index b0036161..cfc9ab70 100644 --- a/src/de/steamwar/bungeecore/listeners/mods/Badlion.java +++ b/src/de/steamwar/bungeecore/listeners/mods/Badlion.java @@ -11,6 +11,7 @@ public class Badlion extends BasicListener { public void onPostLogin(PostLoginEvent event) { /* { + "modsDisallowed": { "Clear Glass":{"disabled":true}, "ClearWater":{"disabled":true}, "FOV Changer":{"disabled":true}, @@ -20,20 +21,12 @@ public class Badlion extends BasicListener { "Replay":{"disabled":true}, "Schematica":{"disabled":true}, "ToggleSneak":{"disabled":true}, - "ToggleSprint":{"disabled":true} + "ToggleSprint":{"disabled":true}, + "TNT Time":{"disabled":true} + } } */ - event.getPlayer().unsafe().sendPacket(new PluginMessage("badlion:mods", ("{" + - "\"Clear Glass\":{\"disabled\":true}," + - "\"ClearWater\":{\"disabled\":true}," + - "\"FOV Changer\":{\"disabled\":true}," + - "\"Hitboxes\":{\"disabled\":true}," + - "\"MiniMap\":{\"disabled\":true}," + - "\"MLG Cobweb\":{\"disabled\":true}," + - "\"Replay\":{\"disabled\":true}," + - "\"Schematica\":{\"disabled\":true}," + - "\"ToggleSneak\":{\"disabled\":true}," + - "\"ToggleSprint\":{\"disabled\":true}" + - "}").getBytes(), false)); + + event.getPlayer().unsafe().sendPacket(new PluginMessage("badlion:mods", ("{\"Clear Glass\":{\"disabled\":true},\"ClearWater\":{\"disabled\":true},\"FOV Changer\":{\"disabled\":true},\"Hitboxes\":{\"disabled\":true},\"MiniMap\":{\"disabled\":true},\"MLG Cobweb\":{\"disabled\":true},\"Replay\":{\"disabled\":true},\"Schematica\":{\"disabled\":true},\"ToggleSneak\":{\"disabled\":true},\"ToggleSprint\":{\"disabled\":true},\"TNT Time\":{\"disabled\":true}}").getBytes(), false)); } } diff --git a/src/de/steamwar/bungeecore/listeners/mods/FMLPing.java b/src/de/steamwar/bungeecore/listeners/mods/FMLPing.java new file mode 100644 index 00000000..a8b59947 --- /dev/null +++ b/src/de/steamwar/bungeecore/listeners/mods/FMLPing.java @@ -0,0 +1,86 @@ +/* + 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.mods; + +import net.md_5.bungee.api.ServerPing; + +import java.util.*; + +public class FMLPing extends ServerPing { + + private final ForgeData forgeData; + + public FMLPing(ServerPing existing, int version) { + super(existing.getVersion(), existing.getPlayers(), existing.getDescriptionComponent(), existing.getFaviconObject()); + forgeData = new ForgeData(version); + } + + private static class ForgeData { + private final List channels = new ArrayList<>(); + private final List mods = new ArrayList<>(); + private final int fmlNetworkVersion = 2; + + public ForgeData(int versionNumber) { + channels.add(new ForgeChannel("minecraft:unregister")); + channels.add(new ForgeChannel("minecraft:register")); + channels.add(new ForgeChannel("fml:handshake")); + mods.add(new ForgeMod("minecraft", ProtocolVersion.getVersion(versionNumber))); + mods.add(new ForgeMod("forge", "ANY")); + } + + public final static class ProtocolVersion { + + private static final HashMap versions; + + static { + versions = new HashMap(); + versions.put(757, "1.18"); + versions.put(756, "1.17.1"); + versions.put(754, "1.16.5"); + versions.put(578, "1.15.2"); + versions.put(498, "1.14.1"); + versions.put(393, "1.13"); + } + + public static String getVersion(int version) { + return versions.get(version); + } + } + + private static class ForgeChannel { + private final String res; + private final String version = "FML2"; + private final boolean required = true; + + private ForgeChannel(String res) { + this.res = res; + } + } + + private static class ForgeMod { + private final String modId; + private final String modmarker; + + private ForgeMod(String modId, String modmarker) { + this.modId = modId; + this.modmarker = modmarker; + } + } + } +} diff --git a/src/de/steamwar/bungeecore/listeners/mods/Forge.java b/src/de/steamwar/bungeecore/listeners/mods/Forge.java index 905eecb8..c29a03cb 100644 --- a/src/de/steamwar/bungeecore/listeners/mods/Forge.java +++ b/src/de/steamwar/bungeecore/listeners/mods/Forge.java @@ -22,102 +22,125 @@ package de.steamwar.bungeecore.listeners.mods; import de.steamwar.bungeecore.BungeeCore; import de.steamwar.bungeecore.listeners.BasicListener; import de.steamwar.bungeecore.sql.Mod; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.UnpooledByteBufAllocator; -import net.md_5.bungee.api.ProxyServer; -import net.md_5.bungee.api.connection.Connection; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.event.PluginMessageEvent; -import net.md_5.bungee.api.event.PostLoginEvent; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.connection.PendingConnection; +import net.md_5.bungee.api.event.LoginEvent; +import net.md_5.bungee.api.event.ProxyPingEvent; +import net.md_5.bungee.connection.InitialHandler; import net.md_5.bungee.event.EventHandler; +import net.md_5.bungee.netty.ChannelWrapper; +import net.md_5.bungee.netty.HandlerBoss; +import net.md_5.bungee.netty.PacketHandler; +import net.md_5.bungee.protocol.packet.LoginPayloadRequest; +import net.md_5.bungee.protocol.packet.LoginPayloadResponse; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.TimeUnit; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.logging.Level; public class Forge extends BasicListener { - private static final String FMLHS = "FML|HS"; - private static final String FMLHS13 = "fml:handshake"; - private static final byte[] REGISTER; - private static final byte[] REGISTER13; - private static final byte[] HELLO = new byte[]{0, 2, 0, 0, 0, 0}; - private static final Set unlocked = new HashSet<>(); + private static final String WRAPPER = "fml:loginwrapper"; + private static final Field initialHandlerCh; static{ - ByteBuf buf = UnpooledByteBufAllocator.DEFAULT.directBuffer(7); - buf.writeByte(6); - buf.writeCharSequence(FMLHS, StandardCharsets.UTF_8); - REGISTER = new byte[buf.readableBytes()]; - buf.readBytes(REGISTER); - - buf.clear(); - buf.writeByte(13); - buf.writeCharSequence(FMLHS13, StandardCharsets.UTF_8); - REGISTER13 = new byte[buf.readableBytes()]; - buf.readBytes(REGISTER13); + try { + initialHandlerCh = InitialHandler.class.getDeclaredField("ch"); + } catch (NoSuchFieldException e) { + throw new SecurityException("Could not initialize Reflection", e); + } + initialHandlerCh.setAccessible(true); } @EventHandler - public void onPostLogin(PostLoginEvent event) { - ProxiedPlayer player = event.getPlayer(); + public void onServerPing(ProxyPingEvent event) { + event.setResponse(new FMLPing(event.getResponse(), event.getConnection().getVersion())); + } - synchronized (unlocked) { - if(unlocked.contains(player.getUniqueId())){ - unlocked.remove(player.getUniqueId()); + @EventHandler + public void onServerConnected(LoginEvent event){ + if(event.getConnection().getVersion() < 340) return; //1.13+ + + //fml:handshake without mods, channels and registries + //for more information see https://wiki.vg/Minecraft_Forge_Handshake#FML2_protocol_.281.13_-_Current.29 + event.getConnection().unsafe().sendPacket(new LoginPayloadRequest(1, WRAPPER, new byte[]{13,102,109,108,58,104,97,110,100,115,104,97,107,101,4,1,0,0,0})); + + InitialHandler handler = (InitialHandler) event.getConnection(); + + ChannelWrapper wrapper; + try{ + wrapper = (ChannelWrapper) initialHandlerCh.get(handler); + } catch (IllegalAccessException e) { + BungeeCore.get().getLogger().log(Level.SEVERE, "Could not get Channel", e); + return; + } + + event.registerIntent(BungeeCore.get()); + wrapper.getHandle().pipeline().get(HandlerBoss.class).setHandler(new CustomPacketHandler(event)); + } + + private static class CustomPacketHandler extends PacketHandler { + private final LoginEvent event; + + public CustomPacketHandler(LoginEvent event) { + this.event = event; + } + + @Override + public String toString() { + return "SteamWar Forge Handler"; + } + + @Override + public void handle(LoginPayloadResponse response){ + byte[] data = response.getData(); + if(data == null) { + event.completeIntent(BungeeCore.get()); return; } - } - if(player.getPendingConnection().getVersion() > 340) { - player.sendData("minecraft:register", REGISTER13); //1.13+ - player.sendData(FMLHS13, Forge.HELLO); - }else{ - player.sendData("REGISTER", REGISTER); //1.12- - player.sendData(FMLHS, Forge.HELLO); - } - } + //for more information see https://wiki.vg/Minecraft_Forge_Handshake#FML2_protocol_.281.13_-_Current.29 + Utils.VarInt channelLength = Utils.readVarInt(data, 0); + int pos = channelLength.length; + if(!new String(data, pos, channelLength.value).equals("fml:handshake")) { + event.getConnection().disconnect(TextComponent.fromLegacyText("Invalid forge registry response (0x00)")); + return; + } + pos += channelLength.value; - @EventHandler - public void onPluginMessageEvent(PluginMessageEvent e){ - if(!e.getTag().equals(FMLHS) && !e.getTag().equals(FMLHS13)) - return; - - e.setCancelled(true); - byte[] data = e.getData(); - - Connection sender = e.getSender(); - if(!(sender instanceof ProxiedPlayer)) - return; - ProxiedPlayer p = (ProxiedPlayer) sender; - - if (data[0] == 2) { - Utils.VarInt numMods = Utils.readVarInt(data, 1); - List mods = new LinkedList<>(); - - int bytePos = 1 + numMods.length; - for (int i = 0; i < numMods.value; i++) { - byte[] name = Arrays.copyOfRange(data, bytePos + 1, bytePos + data[bytePos] + 1); - bytePos += 1 + data[bytePos]; - //Version information is unused - bytePos += 1 + data[bytePos]; - - mods.add(Mod.get(new String(name), Mod.Platform.FORGE)); + Utils.VarInt length = Utils.readVarInt(data, pos); + pos += length.length; + if(channelLength.length + channelLength.value + length.length + length.value != data.length) { + event.getConnection().disconnect(TextComponent.fromLegacyText("Invalid forge registry response (0x01)")); + return; } - if (Utils.handleMods(p, mods)) { - synchronized (unlocked) { - unlocked.add(p.getUniqueId()); - } - ProxyServer.getInstance().getScheduler().schedule(BungeeCore.get(), - () -> p.disconnect(BungeeCore.stringToText("§7Deine installierten Mods wurden überprüft\n§aDu kannst nun §eSteam§8War §abetreten")), - 2, TimeUnit.SECONDS); - ProxyServer.getInstance().getScheduler().schedule(BungeeCore.get(), () -> { - synchronized (unlocked) { - unlocked.remove(p.getUniqueId()); - } - }, 30, TimeUnit.SECONDS); + Utils.VarInt packetId = Utils.readVarInt(data, pos); + pos += packetId.length; + if(packetId.value != 2) { + event.getConnection().disconnect(TextComponent.fromLegacyText("Invalid forge registry response (0x02)")); + return; } + + Utils.VarInt modCount = Utils.readVarInt(data, pos); + pos += modCount.length; + + List mods = new ArrayList<>(); + for(int i = 0; i < modCount.value; i++) { + Utils.VarInt nameLength = Utils.readVarInt(data, pos); + pos += nameLength.length; + + mods.add(Mod.get(new String(data, pos, nameLength.value), Mod.Platform.FORGE)); + pos += nameLength.value; + } + + PendingConnection connection = event.getConnection(); + if(!Utils.handleMods(connection.getUniqueId(), Locale.getDefault(), event::setCancelReason, mods)) { + event.setCancelled(true); + } + event.completeIntent(BungeeCore.get()); } } } diff --git a/src/de/steamwar/bungeecore/listeners/mods/Forge12.java b/src/de/steamwar/bungeecore/listeners/mods/Forge12.java new file mode 100644 index 00000000..eb463f40 --- /dev/null +++ b/src/de/steamwar/bungeecore/listeners/mods/Forge12.java @@ -0,0 +1,113 @@ +/* + 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.listeners.mods; + +import de.steamwar.bungeecore.BungeeCore; +import de.steamwar.bungeecore.listeners.BasicListener; +import de.steamwar.bungeecore.sql.Mod; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.UnpooledByteBufAllocator; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.connection.Connection; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.PluginMessageEvent; +import net.md_5.bungee.api.event.PostLoginEvent; +import net.md_5.bungee.event.EventHandler; + +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class Forge12 extends BasicListener { + private static final String FMLHS = "FML|HS"; + private static final byte[] REGISTER; + private static final byte[] HELLO = new byte[]{0, 2, 0, 0, 0, 0}; + + private static final Set unlocked = new HashSet<>(); + + static { + ByteBuf buf = UnpooledByteBufAllocator.DEFAULT.directBuffer(7); + buf.writeByte(6); + buf.writeCharSequence(FMLHS, StandardCharsets.UTF_8); + REGISTER = new byte[buf.readableBytes()]; + buf.readBytes(REGISTER); + } + + + @EventHandler + public void onPostLogin(PostLoginEvent event) { + ProxiedPlayer player = event.getPlayer(); + + synchronized (unlocked) { + if(unlocked.contains(player.getUniqueId())){ + unlocked.remove(player.getUniqueId()); + return; + } + } + + if(player.getPendingConnection().getVersion() <= 340) { + player.sendData("REGISTER", REGISTER); //1.12- + player.sendData(FMLHS, HELLO); + } + } + + @EventHandler + public void onPluginMessageEvent(PluginMessageEvent e){ + if(!e.getTag().equals(FMLHS)) + return; + + e.setCancelled(true); + byte[] data = e.getData(); + + Connection sender = e.getSender(); + if(!(sender instanceof ProxiedPlayer)) + return; + ProxiedPlayer p = (ProxiedPlayer) sender; + + if (data[0] == 2) { + Utils.VarInt numMods = Utils.readVarInt(data, 1); + List mods = new LinkedList<>(); + + int bytePos = 1 + numMods.length; + for (int i = 0; i < numMods.value; i++) { + byte[] name = Arrays.copyOfRange(data, bytePos + 1, bytePos + data[bytePos] + 1); + bytePos += 1 + data[bytePos]; + //Version information is unused + bytePos += 1 + data[bytePos]; + + mods.add(Mod.get(new String(name), Mod.Platform.FORGE)); + } + + if (Utils.handleMods(p, mods)) { + synchronized (unlocked) { + unlocked.add(p.getUniqueId()); + } + ProxyServer.getInstance().getScheduler().schedule(BungeeCore.get(), + () -> p.disconnect(BungeeCore.stringToText("§7Deine installierten Mods wurden überprüft\n§aDu kannst nun §eSteam§8War §abetreten")), + 2, TimeUnit.SECONDS); + ProxyServer.getInstance().getScheduler().schedule(BungeeCore.get(), () -> { + synchronized (unlocked) { + unlocked.remove(p.getUniqueId()); + } + }, 30, TimeUnit.SECONDS); + } + } + } +} diff --git a/src/de/steamwar/bungeecore/listeners/mods/ModLoaderBlocker.java b/src/de/steamwar/bungeecore/listeners/mods/ModLoaderBlocker.java index 281830cf..a4d54e6d 100644 --- a/src/de/steamwar/bungeecore/listeners/mods/ModLoaderBlocker.java +++ b/src/de/steamwar/bungeecore/listeners/mods/ModLoaderBlocker.java @@ -56,26 +56,6 @@ public class ModLoaderBlocker extends BasicListener { } } - @EventHandler - public void onPluginMessage(PluginMessageEvent e){ - if(!e.getTag().equals("minecraft:register")) - return; - - Connection sender = e.getSender(); - if(!(sender instanceof ProxiedPlayer)) - return; - ProxiedPlayer p = (ProxiedPlayer) sender; - - if(p.getPendingConnection().getVersion() <= 340) - return; - - String registered = new String(e.getData(), StandardCharsets.UTF_8); - if(registered.contains("fml:loginwrapper") || registered.contains("fml:handshake") || registered.contains("fml:play")){ - Storage.fabricPlayers.add(p); - Message.send("MODLOADER_INSTALLED", p, "Forge"); - } - } - @EventHandler public void onDisconnect(PlayerDisconnectEvent e){ Storage.fabricPlayers.remove(e.getPlayer()); diff --git a/src/de/steamwar/bungeecore/listeners/mods/Utils.java b/src/de/steamwar/bungeecore/listeners/mods/Utils.java index 9ed160af..7e3e4cc5 100644 --- a/src/de/steamwar/bungeecore/listeners/mods/Utils.java +++ b/src/de/steamwar/bungeecore/listeners/mods/Utils.java @@ -25,6 +25,8 @@ import de.steamwar.bungeecore.sql.Mod; import de.steamwar.bungeecore.sql.Mod.ModType; import de.steamwar.bungeecore.sql.Punishment; import de.steamwar.bungeecore.sql.SteamwarUser; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.connection.ProxiedPlayer; import java.sql.Timestamp; @@ -32,7 +34,11 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Iterator; import java.util.List; +import java.util.Locale; +import java.util.UUID; +import java.util.function.Consumer; import java.util.logging.Level; +import java.util.stream.Collectors; class Utils { private Utils(){} @@ -55,9 +61,13 @@ class Utils { return new VarInt(numRead, result); } - static boolean handleMods(ProxiedPlayer player, List mods){ - SteamwarUser user = SteamwarUser.get(player.getUniqueId()); - boolean privileged = player.hasPermission("bungeecore.youtubermods"); + static boolean handleMods(ProxiedPlayer player, List mods) { + return handleMods(player.getUniqueId(), player.getLocale(), player::disconnect, mods); + } + + static boolean handleMods(UUID uuid, Locale locale, Consumer disconnect, List mods){ + SteamwarUser user = SteamwarUser.get(uuid); + boolean privileged = user.getUserGroup().privilegedMods(); ModType max = ModType.YELLOW; Iterator it = mods.iterator(); @@ -72,25 +82,22 @@ class Utils { if(mods.isEmpty()) return true; - if(mods.size() == 1){ - if(max == ModType.YELLOW) - player.disconnect(BungeeCore.stringToText(Message.parse("MOD_YELLOW_SING", player, mods.get(0).getModName()))); - else{ - user.punish(Punishment.PunishmentType.Ban, Timestamp.from(Instant.now().plus(7, ChronoUnit.DAYS)), Message.parse("MOD_RED_SING", player, mods.get(0).getModName()), 0, false); - BungeeCore.log(Level.SEVERE, user.getUserName() + " " + user.getId() + " wurde automatisch wegen des Mods " + mods.get(0).getModName() + " gebannt."); - } - }else{ - StringBuilder sb = new StringBuilder(); - mods.forEach(mod -> sb.append(mod.getModName()).append('\n')); + ModType finalMax = max; + String modList = mods.stream().filter(mod -> finalMax == ModType.YELLOW || mod.getModType() == ModType.RED).map(Mod::getModName).collect(Collectors.joining("\n")); + String message; - if(max == ModType.YELLOW) - player.disconnect(BungeeCore.stringToText(Message.parse("MOD_YELLOW_PLUR", player, sb.toString()))); - else{ - user.punish(Punishment.PunishmentType.Ban, Timestamp.from(Instant.now().plus(7, ChronoUnit.DAYS)), Message.parse("MOD_RED_PLUR", player, sb.toString()), 0, false); - BungeeCore.log(Level.SEVERE, user.getUserName() + " " + user.getId() + " wurde automatisch wegen der Mods " + sb.toString() + " gebannt."); - } + if(mods.size() == 1) { + message = Message.parse(max == ModType.RED ? "MOD_RED_SING" : "MOD_YELLOW_SING", locale, modList); + } else { + message = Message.parse(max == ModType.RED ? "MOD_RED_PLUR" : "MOD_YELLOW_PLUR", locale, modList); } + if(max == ModType.RED) { + user.punish(Punishment.PunishmentType.Ban, Timestamp.from(Instant.now().plus(7, ChronoUnit.DAYS)), message, 0, false); + BungeeCore.log(Level.SEVERE, user.getUserName() + " " + user.getId() + " wurde automatisch wegen der Mods " + modList + " gebannt."); + } + + disconnect.accept(TextComponent.fromLegacyText(message)); return false; } diff --git a/src/de/steamwar/bungeecore/sql/SteamwarUser.java b/src/de/steamwar/bungeecore/sql/SteamwarUser.java index 86d5d023..58c53a68 100644 --- a/src/de/steamwar/bungeecore/sql/SteamwarUser.java +++ b/src/de/steamwar/bungeecore/sql/SteamwarUser.java @@ -224,10 +224,12 @@ public class SteamwarUser { final URL url = new URL(API_URL + playerName); String uuid = jsonParser.parse(new Scanner(url.openConnection().getInputStream()).nextLine()).getAsJsonObject().get("id").getAsString(); return UUID.fromString(uuid.replaceFirst("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5")); - } catch (NoSuchElementException | IOException e) { + } catch (NoSuchElementException e) { + // ignore, player does not exist + } catch (IOException e) { BungeeCore.get().getLogger().log(Level.SEVERE, "Could not get offline player UUID " + playerName, e); - return null; } + return null; } public void setTeam(int team) { diff --git a/src/de/steamwar/bungeecore/sql/Team.java b/src/de/steamwar/bungeecore/sql/Team.java index f721fa5d..be3bbec1 100644 --- a/src/de/steamwar/bungeecore/sql/Team.java +++ b/src/de/steamwar/bungeecore/sql/Team.java @@ -146,6 +146,7 @@ public class Team { user.setLeader(false); delete.update(teamId); teamCache.remove(this); + TeamTeilnahme.deleteFuture(teamId); } public List getMembers(){ diff --git a/src/de/steamwar/bungeecore/sql/TeamTeilnahme.java b/src/de/steamwar/bungeecore/sql/TeamTeilnahme.java index 68b1192e..1688cefb 100644 --- a/src/de/steamwar/bungeecore/sql/TeamTeilnahme.java +++ b/src/de/steamwar/bungeecore/sql/TeamTeilnahme.java @@ -28,6 +28,7 @@ public class TeamTeilnahme { private static final Statement insert = new Statement("INSERT INTO TeamTeilnahme (TeamID, EventID) VALUES (?, ?)"); private static final Statement delete = new Statement("DELETE FROM TeamTeilnahme WHERE TeamID = ? AND EventID = ?"); + private static final Statement deleteFuture = new Statement("DELETE t FROM TeamTeilnahme t INNER JOIN Event e ON t.EventID = e.EventID WHERE t.TeamID = ? AND e.Start > NOW()"); private static final Statement byEventTeam = new Statement("SELECT * FROM TeamTeilnahme WHERE TeamID = ? AND EventID = ?"); private static final Statement byEvent = new Statement("SELECT * FROM TeamTeilnahme WHERE EventID = ?"); private static final Statement byTeam = new Statement("SELECT * FROM TeamTeilnahme WHERE TeamID = ?"); @@ -44,6 +45,10 @@ public class TeamTeilnahme { return byEventTeam.select(ResultSet::next, teamID, eventID); } + public static void deleteFuture(int teamID) { + deleteFuture.update(teamID); + } + public static Set getTeams(int eventID){ return byEvent.select(rs -> { Set teams = new HashSet<>(); diff --git a/src/de/steamwar/bungeecore/sql/UserGroup.java b/src/de/steamwar/bungeecore/sql/UserGroup.java index 73273dcf..e6c30f9a 100644 --- a/src/de/steamwar/bungeecore/sql/UserGroup.java +++ b/src/de/steamwar/bungeecore/sql/UserGroup.java @@ -24,26 +24,28 @@ import java.util.stream.Collectors; public enum UserGroup { - Admin("§4", "§e", true, true, true), - Developer("§3", "§f", true, true, true), - Moderator("§c", "§f", true, true, true), - Supporter("§9", "§f", false, true, true), - Builder("§2", "§f", false, true, false), - YouTuber("§5", "§f", false, false, false), - Member("§7", "§7", false, false, false); + 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); private final String colorCode; private final String chatColorCode; 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) { + UserGroup(String colorCode, String chatColorCode, boolean adminGroup, boolean teamGroup, boolean checkSchematics, boolean privilegedMods) { this.colorCode = colorCode; this.chatColorCode = chatColorCode; this.adminGroup = adminGroup; this.teamGroup = teamGroup; this.checkSchematics = checkSchematics; + this.privilegedMods = privilegedMods; } public String getColorCode() { @@ -62,6 +64,10 @@ public enum UserGroup { return checkSchematics; } + public boolean privilegedMods() { + return privilegedMods; + } + public String getChatColorCode() { return chatColorCode; } diff --git a/src/de/steamwar/messages/BungeeCore.properties b/src/de/steamwar/messages/BungeeCore.properties index e8c16864..0a948903 100644 --- a/src/de/steamwar/messages/BungeeCore.properties +++ b/src/de/steamwar/messages/BungeeCore.properties @@ -537,7 +537,7 @@ SERVER_WORLD_ERROR=§cDas Erstellen der Welt ist fehlgeschlagen. #WhoisCommand -WHOIS_USAGE=§c/whois [Spieler/ID] {-a} +WHOIS_USAGE=§c/whois [Spieler/ID] <-a> WHOIS_USERNAME=§7Username§8: §e{0} WHOIS_UUID=§7UUID§8: §e{0} WHOIS_UUID_HOVER=§eUUID Kopieren @@ -568,4 +568,10 @@ GDPR_STATUS_WORLD=§7Packe Bauwelten... GDPR_STATUS_INVENTORIES=§7Suche und packe Inventare... GDPR_STATUS_DATABASE=§7Packe Datenbankinhalte... GDPR_STATUS_LOGS=§7Suche und packe logs... -GDPR_STATUS_FINISHED=§7Packen abgeschlossen \ No newline at end of file +GDPR_STATUS_FINISHED=§7Packen abgeschlossen + +#Playtime Command +HOURS_PLAYED=§7Deine Spielzeit beträgt§8: §e{0}h + +#Arena command +ARENA_NOT_FOUND=§cDie angegebene Arena konnte nicht gefunden werden