diff options
| author | sowgro <tpoke.ferrari@gmail.com> | 2024-06-27 02:40:06 -0400 | 
|---|---|---|
| committer | sowgro <tpoke.ferrari@gmail.com> | 2024-06-27 02:40:06 -0400 | 
| commit | 02cc74cc5ac06a69ec59d6277234ce69031402f4 (patch) | |
| tree | e213f687ac5fb5731fa7f2ed7420c54028c919b4 /src | |
| parent | d849ab8d085675715622dcef212d32239eb5d4bb (diff) | |
| download | Tumble-02cc74cc5ac06a69ec59d6277234ce69031402f4.tar.gz Tumble-02cc74cc5ac06a69ec59d6277234ce69031402f4.tar.bz2 Tumble-02cc74cc5ac06a69ec59d6277234ce69031402f4.zip | |
overhaul command and config formats (again)
Diffstat (limited to 'src')
27 files changed, 972 insertions, 488 deletions
| diff --git a/src/main/java/com/MylesAndMore/Tumble/Main.java b/src/main/java/com/MylesAndMore/Tumble/Main.java index 274765e..f098a12 100644 --- a/src/main/java/com/MylesAndMore/Tumble/Main.java +++ b/src/main/java/com/MylesAndMore/Tumble/Main.java @@ -1,8 +1,10 @@  package com.MylesAndMore.Tumble;  import com.MylesAndMore.Tumble.commands.*; -import com.MylesAndMore.Tumble.plugin.ConfigManager; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.config.ConfigManager; +import com.MylesAndMore.Tumble.config.LanguageManager;  import org.bstats.bukkit.Metrics;  import org.bukkit.Bukkit; @@ -16,17 +18,14 @@ public class Main extends JavaPlugin{      @Override      public void onEnable() {          plugin = this; +        new ArenaManager(); +        new ConfigManager(); +        new LanguageManager(); -        Objects.requireNonNull(this.getCommand("tumble-reload")).setExecutor(new Reload()); -        Objects.requireNonNull(this.getCommand("tumble-config")).setExecutor(new Config()); -        Objects.requireNonNull(this.getCommand("tumble-forcestart")).setExecutor(new ForceStart()); -        Objects.requireNonNull(this.getCommand("tumble-join")).setExecutor(new Join()); -        Objects.requireNonNull(this.getCommand("tumble-leave")).setExecutor(new Leave()); -        Objects.requireNonNull(this.getCommand("tumble-forcestop")).setExecutor(new ForceStop()); +        Objects.requireNonNull(this.getCommand("tumble")).setExecutor(new Tumble());          new Metrics(this, 16940);          this.saveDefaultConfig(); // Saves the default config file (packaged in the JAR) if we haven't already -        ConfigManager.readConfig();          Bukkit.getServer().getLogger().info("[Tumble] Tumble successfully enabled!");      } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Config.java b/src/main/java/com/MylesAndMore/Tumble/commands/Config.java deleted file mode 100644 index 5fc2de6..0000000 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Config.java +++ /dev/null @@ -1,122 +0,0 @@ -package com.MylesAndMore.Tumble.commands; - -import com.MylesAndMore.Tumble.game.Arena; -import com.MylesAndMore.Tumble.plugin.ConfigManager; -import org.bukkit.ChatColor; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.bukkit.command.TabCompleter; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -public class Config implements CommandExecutor, TabCompleter { -    @Override -    public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { -        if (!(sender instanceof Player)) { -            sender.sendMessage(ChatColor.RED + "This cannot be run by the console"); -            return false; -        } - -        if (!sender.hasPermission("tumble.config")) { -            sender.sendMessage(ChatColor.RED + "You do not have permission to perform this command!"); -            return false; -        } - -        if (args.length < 2 || args[0] == null || args[1] == null) { -            sender.sendMessage(ChatColor.RED + "Missing arguments"); -            return false; -        } - -        switch (args[0]) { -            case "add" -> { -                String arenaName = args[1]; -                ConfigManager.arenas.put(arenaName, new Arena(arenaName, ((Player)sender).getLocation(), null)); -                sender.sendMessage(ChatColor.GREEN + "Arena added."); -            } -            case "set" -> { -                String world = args[1]; -                if (ConfigManager.arenas.containsKey(world)) { -                    ConfigManager.arenas.get(world).location = ((Player)sender).getLocation(); -                } -                else if (world.equals("waitArea")) { -                    ConfigManager.waitArea = ((Player)sender).getLocation(); -                } -                else if (world.equals("lobbySpawn")) { -                    ConfigManager.lobby = ((Player)sender).getLocation(); -                } -                else if (world.equals("winnerLobbySpawn")) { -                    ConfigManager.winnerLobby = ((Player)sender).getLocation(); -                } -                else { -                    sender.sendMessage(ChatColor.RED + "Invalid parameter"); -                    return false; -                } -                sender.sendMessage(ChatColor.GREEN + "Location set."); -            } -            case "disable" -> { -                String world = args[1]; -                if (world.equals("waitArea")) { -                    ConfigManager.waitArea = null; -                } -                else if (world.equals("winnerLobbySpawn")) { -                    ConfigManager.winnerLobby = null; -                } -                else { -                    sender.sendMessage(ChatColor.RED + "Invalid parameter"); -                    return false; -                } -                sender.sendMessage(ChatColor.GREEN + "World disabled."); -            } -            case "remove" -> { -                String world = args[1]; -                if (ConfigManager.arenas.containsKey(world)) { -                    ConfigManager.arenas.remove(world); -                } -                else { -                    sender.sendMessage(ChatColor.RED + "Invalid parameter"); -                    return false; -                } -                sender.sendMessage(ChatColor.GREEN + "Location set"); -            } -            default -> { -                sender.sendMessage(ChatColor.RED + "Invalid parameter"); -                return false; -            } -        } - -        ConfigManager.WriteConfig(); -        sender.sendMessage(ChatColor.GREEN + "Wrote changes to file."); -        return true; -    } - -    @Override -    public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { -        if (args.length == 1) { -            return new ArrayList<>(Arrays.asList("add", "set", "disable", "remove")); -        } -        if (args.length == 2) { -            switch (args[0]) { -                case "set" -> { -                    ArrayList<String> temp = new ArrayList<>(ConfigManager.arenas.keySet()); -                    temp.addAll(Arrays.asList("waitArea", "lobbySpawn", "winnerLobbySpawn")); -                    return temp; -                } -                case "disable" -> { -                    return Arrays.asList("waitArea", "winnerLobbySpawn"); -                } -                case "delete" -> { -                    return ConfigManager.arenas.keySet().stream().toList(); -                } -                default -> { -                    return new ArrayList<>(); -                } -            } -        } -        return new ArrayList<>(); -    } -} diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Create.java b/src/main/java/com/MylesAndMore/Tumble/commands/Create.java new file mode 100644 index 0000000..3340cc6 --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Create.java @@ -0,0 +1,44 @@ +package com.MylesAndMore.Tumble.commands; + +import com.MylesAndMore.Tumble.game.Arena; +import com.MylesAndMore.Tumble.config.LanguageManager; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.plugin.SubCommand; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; + +public class Create implements SubCommand, CommandExecutor, TabCompleter { +    @Override +    public String getCommandName() { +        return "create"; +    } + +    @Override +    public String getPermission() { +        return "tumble.create"; +    } + +    @Override +    public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { +        if (args.length == 0 || args[0] == null || args[0].isEmpty()) { +            sender.sendMessage(LanguageManager.fromKey("missing-arena-parameter")); +            return false; +        } + +        String arenaName = args[0]; +        ArenaManager.arenas.put(arenaName, new Arena(arenaName)); +        sender.sendMessage(LanguageManager.fromKey("create-success")); +        return true; +    } + +    @Override +    public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { +        return Collections.emptyList(); +    } +} diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java index bdead44..c8042bc 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java @@ -1,8 +1,9 @@  package com.MylesAndMore.Tumble.commands;  import com.MylesAndMore.Tumble.game.Game; -import com.MylesAndMore.Tumble.plugin.ConfigManager; -import org.bukkit.ChatColor; +import com.MylesAndMore.Tumble.config.LanguageManager; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.plugin.SubCommand;  import org.bukkit.command.Command;  import org.bukkit.command.CommandExecutor;  import org.bukkit.command.CommandSender; @@ -13,36 +14,42 @@ import org.jetbrains.annotations.NotNull;  import java.util.ArrayList;  import java.util.List; -public class ForceStart implements CommandExecutor, TabCompleter { +public class ForceStart implements SubCommand, CommandExecutor, TabCompleter { +      @Override -    public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { +    public String getCommandName() { +        return "forceStart"; +    } -        if (!sender.hasPermission("tumble.forcestart")) { -            sender.sendMessage(ChatColor.RED + "You do not have permission to perform this command!"); -            return false; -        } +    @Override +    public String getPermission() { +        return "tumble.forceStart"; +    } + +    @Override +    public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {          Game game;          if (args.length < 1 || args[0] == null) { -            game = ConfigManager.findGamePlayerIsIn((Player)sender); +            game = ArenaManager.findGamePlayerIsIn((Player)sender);              if (game == null) { -                sender.sendMessage(ChatColor.RED + "Missing arena name"); +                sender.sendMessage(LanguageManager.fromKey("missing-arena-parameter"));                  return false;              }          }          else { -            game = ConfigManager.arenas.get(args[0]).game; +            game = ArenaManager.arenas.get(args[0]).game;          }          game.gameStart(); -        sender.sendMessage(ChatColor.GREEN + "Starting game"); +        sender.sendMessage(LanguageManager.fromKey("forcestart-success"));          return true;      }      @Override      public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {          if (args.length == 1) { -            return ConfigManager.arenas.keySet().stream().toList(); +            return ArenaManager.arenas.keySet().stream().toList();          }          return new ArrayList<>();      } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java index ddd5826..96e8334 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java @@ -1,8 +1,9 @@  package com.MylesAndMore.Tumble.commands;  import com.MylesAndMore.Tumble.game.Game; -import com.MylesAndMore.Tumble.plugin.ConfigManager; -import org.bukkit.ChatColor; +import com.MylesAndMore.Tumble.config.LanguageManager; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.plugin.SubCommand;  import org.bukkit.command.Command;  import org.bukkit.command.CommandExecutor;  import org.bukkit.command.CommandSender; @@ -13,41 +14,47 @@ import org.jetbrains.annotations.NotNull;  import java.util.ArrayList;  import java.util.List; -public class ForceStop implements CommandExecutor, TabCompleter { +public class ForceStop implements SubCommand, CommandExecutor, TabCompleter { +      @Override -    public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { +    public String getCommandName() { +        return "forcestop"; +    } -        if (!sender.hasPermission("tumble.forcestop")) { -            sender.sendMessage(ChatColor.RED + "You do not have permission to perform this command!"); -            return false; -        } +    @Override +    public String getPermission() { +        return "tumble.forcestop"; +    } + +    @Override +    public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {          Game game;          if (args.length < 1 || args[0] == null) { -            game = ConfigManager.findGamePlayerIsIn((Player)sender); +            game = ArenaManager.findGamePlayerIsIn((Player)sender);              if (game == null) { -                sender.sendMessage(ChatColor.RED + "Missing arena name"); +                sender.sendMessage(LanguageManager.fromKey("missing-arena-parameter"));                  return false;              }          }          else { -            game = ConfigManager.arenas.get(args[0]).game; +            game = ArenaManager.arenas.get(args[0]).game;          }          if (game == null) { -            sender.sendMessage(ChatColor.RED + "No game is currently running in this arena"); +            sender.sendMessage(LanguageManager.fromKey("no-game-in-arena"));              return false;          }          game.gameEnd(); -        sender.sendMessage(ChatColor.GREEN + "Game stopped."); +        sender.sendMessage(LanguageManager.fromKey("forcestop-success"));          return true;      }      @Override      public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {          if (args.length == 1) { -            return ConfigManager.arenas.keySet().stream().toList(); +            return ArenaManager.arenas.keySet().stream().toList();          }          return new ArrayList<>();      } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Join.java b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java index de44da1..a887c99 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Join.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java @@ -1,10 +1,12 @@  package com.MylesAndMore.Tumble.commands;  import com.MylesAndMore.Tumble.game.Arena; -import com.MylesAndMore.Tumble.plugin.ConfigManager; +import com.MylesAndMore.Tumble.config.LanguageManager; +import com.MylesAndMore.Tumble.config.ArenaManager;  import com.MylesAndMore.Tumble.game.Game;  import com.MylesAndMore.Tumble.plugin.GameState;  import com.MylesAndMore.Tumble.plugin.GameType; +import com.MylesAndMore.Tumble.plugin.SubCommand;  import org.bukkit.ChatColor;  import org.bukkit.command.Command;  import org.bukkit.command.CommandExecutor; @@ -19,35 +21,41 @@ import java.util.List;  import java.util.Objects;  import java.util.stream.Collectors; -public class Join implements CommandExecutor, TabCompleter { +public class Join implements SubCommand, CommandExecutor, TabCompleter { + +    @Override +    public String getCommandName() { +        return "join"; +    } + +    @Override +    public String getPermission() { +        return "tumble.join"; +    } +      @Override      public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {          if (!(sender instanceof Player)) { -            sender.sendMessage(ChatColor.RED + "This cannot be run by the console"); -            return false; -        } - -        if (!sender.hasPermission("tumble.join")) { -            sender.sendMessage(ChatColor.RED + "You do not have permission to perform this command!"); +            sender.sendMessage(LanguageManager.fromKey("not-for-console"));              return false;          } -        if (ConfigManager.findGamePlayerIsIn((Player)sender) != null) { -            sender.sendMessage(ChatColor.RED + "You are already in a game! Leave it to join another one"); +        if (ArenaManager.findGamePlayerIsIn((Player)sender) != null) { +            sender.sendMessage(LanguageManager.fromKey("already-in-game"));          }          if (args.length < 1 || args[0] == null) { -            sender.sendMessage(ChatColor.RED + "Missing arena name"); +            sender.sendMessage(LanguageManager.fromKey("missing-arena-parameter"));              return false;          }          String arenaName = args[0]; -        if (!ConfigManager.arenas.containsKey(arenaName)) +        if (!ArenaManager.arenas.containsKey(arenaName))          { -            sender.sendMessage(ChatColor.RED + "This arena does not exist"); +            sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%", arenaName));              return false;          } -        Arena arena = ConfigManager.arenas.get(arenaName); +        Arena arena = ArenaManager.arenas.get(arenaName);          Game game;          if (args.length < 2 || args[1] == null) { @@ -66,7 +74,7 @@ public class Join implements CommandExecutor, TabCompleter {                  case "snowballs", "snowball" -> type = GameType.SNOWBALLS;                  case "mix", "mixed"          -> type = GameType.MIXED;                  default                      -> { -                    sender.sendMessage(ChatColor.RED + "Invalid game type"); +                    sender.sendMessage(LanguageManager.fromKey("invalid-type"));                      return false;                  }              } @@ -80,26 +88,30 @@ public class Join implements CommandExecutor, TabCompleter {                      game = arena.game;                  }                  else { -                    sender.sendMessage(ChatColor.RED + "A game of "+type+" is currently taking place in this arena, choose another arena or join it with /tumble:join "+arena.name+" "+type); +                    sender.sendMessage(LanguageManager.fromKey("another-type-in-arena") +                            .replace("%type%",type.toString()) +                            .replace("%arena%",arenaName));                      return false;                  }              }          }          if (game.gameState != GameState.WAITING) { -            sender.sendMessage(ChatColor.RED + "This game is still in progress, wait until it finishes or join another game"); +            sender.sendMessage(LanguageManager.fromKey("game-in-progress"));              return false;          }          game.addPlayer((Player)sender); -        sender.sendMessage(ChatColor.GREEN + "Joined game " + arena.name + " - " + game.type); +        sender.sendMessage(LanguageManager.fromKey("join-success") +                .replace("%type%", game.type.toString()) +                .replace("%arena%", arena.name));          return true;      }      @Override      public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {          if (args.length == 1) { -            return ConfigManager.arenas.keySet().stream().toList(); +            return ArenaManager.arenas.keySet().stream().toList();          }          if (args.length == 2) {              return Arrays.stream(GameType.values()).map(Objects::toString).collect(Collectors.toList()); diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Leave.java b/src/main/java/com/MylesAndMore/Tumble/commands/Leave.java index 1d00ca4..0250cc6 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Leave.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Leave.java @@ -1,8 +1,9 @@  package com.MylesAndMore.Tumble.commands;  import com.MylesAndMore.Tumble.game.Game; -import com.MylesAndMore.Tumble.plugin.ConfigManager; -import org.bukkit.ChatColor; +import com.MylesAndMore.Tumble.config.LanguageManager; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.plugin.SubCommand;  import org.bukkit.command.Command;  import org.bukkit.command.CommandExecutor;  import org.bukkit.command.CommandSender; @@ -13,28 +14,36 @@ import org.jetbrains.annotations.NotNull;  import java.util.ArrayList;  import java.util.List; -public class Leave implements CommandExecutor, TabCompleter { +public class Leave implements SubCommand, CommandExecutor, TabCompleter { + +    @Override +    public String getCommandName() { +        return "leave"; +    } + +    @Override +    public String getPermission() { +        return "tumble.leave"; +    } +      @Override      public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {          if (!(sender instanceof Player)) { -            sender.sendMessage(ChatColor.RED + "This cannot be run by the console"); -            return false; -        } - -        if (!sender.hasPermission("tumble.leave")) { -            sender.sendMessage(ChatColor.RED + "You do not have permission to perform this command!"); +            sender.sendMessage(LanguageManager.fromKey("not-for-console"));              return false;          } -        Game game = ConfigManager.findGamePlayerIsIn((Player)sender); +        Game game = ArenaManager.findGamePlayerIsIn((Player)sender);          if (game == null) { -            sender.sendMessage(ChatColor.RED + "You are not in a game."); +            sender.sendMessage(LanguageManager.fromKey("no-game-in-arena"));              return false;          }          game.removePlayer((Player) sender); -        sender.sendMessage(ChatColor.GREEN + "Game left."); +        sender.sendMessage(LanguageManager.fromKey("leave-success") +                .replace("%arena%", game.arena.name) +                .replace("%type%", game.type.toString()));          return true;      } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java b/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java index ca67a2e..66535d5 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java @@ -1,8 +1,9 @@  package com.MylesAndMore.Tumble.commands;  import com.MylesAndMore.Tumble.game.Arena; -import com.MylesAndMore.Tumble.plugin.ConfigManager; -import org.bukkit.ChatColor; +import com.MylesAndMore.Tumble.config.LanguageManager; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.plugin.SubCommand;  import org.bukkit.command.Command;  import org.bukkit.command.CommandExecutor;  import org.bukkit.command.CommandSender; @@ -14,23 +15,29 @@ import java.util.List;  import static com.MylesAndMore.Tumble.Main.plugin; -public class Reload implements CommandExecutor, TabCompleter { +public class Reload implements SubCommand, CommandExecutor, TabCompleter {      @Override -    public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { -        if (!sender.hasPermission("tumble.reload")) { -            sender.sendMessage(ChatColor.RED + "You do not have permission to perform this command!"); -            return false; -        } +    public String getCommandName() { +        return "reload"; +    } + +    @Override +    public String getPermission() { +        return "tumble.reload"; +    } + +    @Override +    public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { -        for (Arena a : ConfigManager.arenas.values()) { +        for (Arena a : ArenaManager.arenas.values()) {              if (a.game != null) {                  a.game.gameEnd();              }          }          plugin.onEnable(); -        sender.sendMessage(ChatColor.GREEN + "Tumble configuration reloaded. Check console for errors."); +        sender.sendMessage(LanguageManager.fromKey("reload-success"));          return true;      } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Remove.java b/src/main/java/com/MylesAndMore/Tumble/commands/Remove.java new file mode 100644 index 0000000..118aa77 --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Remove.java @@ -0,0 +1,53 @@ +package com.MylesAndMore.Tumble.commands; + +import com.MylesAndMore.Tumble.config.LanguageManager; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.plugin.SubCommand; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; + +public class Remove implements SubCommand, CommandExecutor, TabCompleter { + +    @Override +    public String getCommandName() { +        return "remove"; +    } + +    @Override +    public String getPermission() { +        return "tumble.remove"; +    } + +    @Override +    public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { +        if (args.length == 0 || args[0] == null || args[0].isEmpty()) { +            sender.sendMessage(LanguageManager.fromKey("missing-arena-parameter")); +            return false; +        } +        String arenaName = args[0]; + +        if (!ArenaManager.arenas.containsKey(arenaName)) { +            sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); +            return false; +        } + +        ArenaManager.arenas.remove(arenaName); +        sender.sendMessage(LanguageManager.fromKey("set-success")); +        return true; +    } + +    @Override +    public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { +        if (args.length == 1) { +            return ArenaManager.arenas.keySet().stream().toList(); +        } + +        return Collections.emptyList(); +    } +} diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetGameSpawn.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetGameSpawn.java new file mode 100644 index 0000000..62d22f8 --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetGameSpawn.java @@ -0,0 +1,60 @@ +package com.MylesAndMore.Tumble.commands; + +import com.MylesAndMore.Tumble.game.Arena; +import com.MylesAndMore.Tumble.config.LanguageManager; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.plugin.SubCommand; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; + +public class SetGameSpawn implements SubCommand, CommandExecutor, TabCompleter { +    @Override +    public String getCommandName() { +        return "setGameSpawn"; +    } + +    @Override +    public String getPermission() { +        return "tumble.setGameSpawn"; +    } + +    @Override +    public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { +        if (!(sender instanceof Player)) { +            sender.sendMessage(LanguageManager.fromKey("not-for-console")); +            return false; +        } + +        if (args.length == 0 || args[0] == null || args[0].isEmpty()) { +            sender.sendMessage(LanguageManager.fromKey("missing-arena-parameter")); +            return false; +        } +        String arenaName = args[0]; + +        if (!ArenaManager.arenas.containsKey(arenaName)) { +            sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); +            return false; +        } +        Arena arena = ArenaManager.arenas.get(arenaName); + +        arena.gameSpawn = ((Player)sender).getLocation(); +        sender.sendMessage(LanguageManager.fromKey("set-success")); +        return true; +    } + +    @Override +    public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { +        if (args.length == 1) { +            return ArenaManager.arenas.keySet().stream().toList(); +        } + +        return Collections.emptyList(); +    } +} diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetKillYCordinate.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetKillYCordinate.java new file mode 100644 index 0000000..0be156f --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetKillYCordinate.java @@ -0,0 +1,61 @@ +package com.MylesAndMore.Tumble.commands; + +import com.MylesAndMore.Tumble.game.Arena; +import com.MylesAndMore.Tumble.config.LanguageManager; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.plugin.SubCommand; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; + +public class SetKillYCordinate implements SubCommand, CommandExecutor, TabCompleter { + +    @Override +    public String getCommandName() { +        return "setKillYLevel"; +    } + +    @Override +    public String getPermission() { +        return "tumble.setKillYLevel"; +    } + +    @Override +    public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { +        if (!(sender instanceof Player)) { +            sender.sendMessage(LanguageManager.fromKey("not-for-console")); +            return false; +        } + +        if (args.length == 0 || args[0] == null || args[0].isEmpty()) { +            sender.sendMessage(LanguageManager.fromKey("missing-arena-parameter")); +            return false; +        } +        String arenaName = args[0]; + +        if (!ArenaManager.arenas.containsKey(arenaName)) { +            sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); +            return false; +        } +        Arena arena = ArenaManager.arenas.get(arenaName); + +        arena.killAtY = ((int) ((Player) sender).getLocation().getY()); +        sender.sendMessage(LanguageManager.fromKey("set-success")); +        return true; +    } + +    @Override +    public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { +        if (args.length == 1) { +            return ArenaManager.arenas.keySet().stream().toList(); +        } + +        return Collections.emptyList(); +    } +} diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetLobby.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetLobby.java new file mode 100644 index 0000000..dbb6b53 --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetLobby.java @@ -0,0 +1,60 @@ +package com.MylesAndMore.Tumble.commands; + +import com.MylesAndMore.Tumble.game.Arena; +import com.MylesAndMore.Tumble.config.LanguageManager; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.plugin.SubCommand; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; + +public class SetLobby implements SubCommand, CommandExecutor, TabCompleter { +    @Override +    public String getCommandName() { +        return "setLobby"; +    } + +    @Override +    public String getPermission() { +        return "tumble.setLobby"; +    } + +    @Override +    public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { +        if (!(sender instanceof Player)) { +            sender.sendMessage(LanguageManager.fromKey("not-for-console")); +            return false; +        } + +        if (args.length == 0 || args[0] == null || args[0].isEmpty()) { +            sender.sendMessage(LanguageManager.fromKey("missing-arena-parameter")); +            return false; +        } +        String arenaName = args[0]; + +        if (!ArenaManager.arenas.containsKey(arenaName)) { +            sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); +            return false; +        } +        Arena arena = ArenaManager.arenas.get(arenaName); + +        arena.lobby = ((Player)sender).getLocation(); +        sender.sendMessage(LanguageManager.fromKey("set-success")); +        return true; +    } + +    @Override +    public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { +        if (args.length == 1) { +            return ArenaManager.arenas.keySet().stream().toList(); +        } + +        return Collections.emptyList(); +    } +} diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetWaitArea.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetWaitArea.java new file mode 100644 index 0000000..f789658 --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetWaitArea.java @@ -0,0 +1,60 @@ +package com.MylesAndMore.Tumble.commands; + +import com.MylesAndMore.Tumble.game.Arena; +import com.MylesAndMore.Tumble.config.LanguageManager; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.plugin.SubCommand; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; + +public class SetWaitArea implements SubCommand, CommandExecutor, TabCompleter { +    @Override +    public String getCommandName() { +        return "setWaitArea"; +    } + +    @Override +    public String getPermission() { +        return "tumble.setWaitArea"; +    } + +    @Override +    public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { +        if (!(sender instanceof Player)) { +            sender.sendMessage(LanguageManager.fromKey("not-for-console")); +            return false; +        } + +        if (args.length == 0 || args[0] == null || args[0].isEmpty()) { +            sender.sendMessage(LanguageManager.fromKey("missing-arena-parameter")); +            return false; +        } +        String arenaName = args[0]; + +        if (!ArenaManager.arenas.containsKey(arenaName)) { +            sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); +            return false; +        } +        Arena arena = ArenaManager.arenas.get(arenaName); + +        arena.waitArea = ((Player)sender).getLocation(); +        sender.sendMessage(LanguageManager.fromKey("set-success")); +        return true; +    } + +    @Override +    public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { +        if (args.length == 1) { +            return ArenaManager.arenas.keySet().stream().toList(); +        } + +        return Collections.emptyList(); +    } +} diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLobby.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLobby.java new file mode 100644 index 0000000..01817b0 --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLobby.java @@ -0,0 +1,60 @@ +package com.MylesAndMore.Tumble.commands; + +import com.MylesAndMore.Tumble.game.Arena; +import com.MylesAndMore.Tumble.config.LanguageManager; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.plugin.SubCommand; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; + +public class SetWinnerLobby implements SubCommand, CommandExecutor, TabCompleter { +    @Override +    public String getCommandName() { +        return "setWinnerLobby"; +    } + +    @Override +    public String getPermission() { +        return "tumble.setWinnerLobby"; +    } + +    @Override +    public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { +        if (!(sender instanceof Player)) { +            sender.sendMessage(LanguageManager.fromKey("not-for-console")); +            return false; +        } + +        if (args.length == 0 || args[0] == null || args[0].isEmpty()) { +            sender.sendMessage(LanguageManager.fromKey("missing-arena-parameter")); +            return false; +        } +        String arenaName = args[0]; + +        if (!ArenaManager.arenas.containsKey(arenaName)) { +            sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); +            return false; +        } +        Arena arena = ArenaManager.arenas.get(arenaName); + +        arena.winnerLobby = ((Player)sender).getLocation(); +        sender.sendMessage(LanguageManager.fromKey("set-success")); +        return true; +    } + +    @Override +    public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { +        if (args.length == 1) { +            return ArenaManager.arenas.keySet().stream().toList(); +        } + +        return Collections.emptyList(); +    } +} diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java b/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java new file mode 100644 index 0000000..6e44352 --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java @@ -0,0 +1,85 @@ +package com.MylesAndMore.Tumble.commands; + +import com.MylesAndMore.Tumble.config.LanguageManager; +import com.MylesAndMore.Tumble.plugin.SubCommand; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +public class Tumble implements CommandExecutor, TabCompleter { + +    private static final Map<String, SubCommand> subCommands = Map.ofEntries( +            CmdNameAsKey(new Create()), +            CmdNameAsKey(new ForceStart()), +            CmdNameAsKey(new ForceStop()), +            CmdNameAsKey(new Join()), +            CmdNameAsKey(new Leave()), +            CmdNameAsKey(new Reload()), +            CmdNameAsKey(new Remove()), +            CmdNameAsKey(new SetGameSpawn()), +            CmdNameAsKey(new SetKillYCordinate()), +            CmdNameAsKey(new SetLobby()), +            CmdNameAsKey(new SetWaitArea()), +            CmdNameAsKey(new SetWinnerLobby()) +    ); + +    @Override +    public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { +        if (!subCommands.containsKey(args[0])) { +            sender.sendMessage(LanguageManager.fromKey("unknown-command")); +            return true; +        } + +        var subCmd = subCommands.get(args[0]); + +        if (!sender.hasPermission(subCmd.getPermission())) { +            sender.sendMessage(LanguageManager.fromKey("no-permission").replace("%permission%", subCmd.getPermission())); +            return false; +        } + +        subCmd.onCommand(sender, command, args[0], removeFirst(args)); +        return true; +    } + +    @Override +    public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { +        if (args.length == 1) { +            ArrayList<String> PermittedSubCmds = new ArrayList<>(); +            for (SubCommand subCmd: subCommands.values()) { +                if (sender.hasPermission(subCmd.getPermission())) { +                    PermittedSubCmds.add(subCmd.getCommandName()); +                } +            } +            return PermittedSubCmds; +        } + +        if (args.length > 1) { +            if (!subCommands.containsKey(args[0])) { +                return Collections.emptyList(); +            } + +            if (subCommands.get(args[0]) instanceof TabCompleter tcmp) { +                return tcmp.onTabComplete(sender, command, args[0], removeFirst(args)); +            } +            else { +                return null; +            } +        } + +        return Collections.emptyList(); +    } + +    private String[] removeFirst(String[] arr) { +        ArrayList<String> tmp = new ArrayList<>(List.of(arr)); +        tmp.remove(0); +        return tmp.toArray(new String[0]); +    } + +    private static Map.Entry<String, SubCommand> CmdNameAsKey(SubCommand s) { +        return Map.entry(s.getCommandName(),s); +    } +} diff --git a/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java new file mode 100644 index 0000000..69a6c2c --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java @@ -0,0 +1,178 @@ +package com.MylesAndMore.Tumble.config; + +import com.MylesAndMore.Tumble.game.Arena; +import com.MylesAndMore.Tumble.game.Game; +import com.MylesAndMore.Tumble.plugin.Result; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Objects; + +import static com.MylesAndMore.Tumble.Main.plugin; + +public class ArenaManager { +    private static FileConfiguration config; +    public static HashMap<String, Arena> arenas; + +    public ArenaManager() { +        String fileName = "arenas.yml"; +        // create config +        File customConfigFile = new File(plugin.getDataFolder(), fileName); +        if (!customConfigFile.exists()) { +            customConfigFile.getParentFile().mkdirs(); +            plugin.saveResource(fileName, false); +        } + +        config = new YamlConfiguration(); +        try { +            config.load(customConfigFile); +        } catch (IOException | InvalidConfigurationException e) { +            e.printStackTrace(); +        } +        /* User Edit: +            Instead of the above Try/Catch, you can also use +            YamlConfiguration.loadConfiguration(customConfigFile) +        */ +        readConfig(); +    } + +    /** +     * Reads config file and populates values above +     */ +    public static void readConfig() { +        plugin.reloadConfig(); + +        // arenas +        ConfigurationSection arenasSection = config.getConfigurationSection("arenas"); +        if (arenasSection == null) { +            plugin.getLogger().warning("Section 'arenas' is missing from config"); +            return; +        } +        arenas = new HashMap<>(); +        for (String arenaName: arenasSection.getKeys(false)) { + +            ConfigurationSection anArenaSection = arenasSection.getConfigurationSection(arenaName); +            if (anArenaSection == null) { +                plugin.getLogger().warning("Failed to load arena "+arenaName+": Error loading config section"); +                continue; +            } + +            Arena arena = new Arena(arenaName); +            arenas.put(arena.name, arena); + +            int killAtY = anArenaSection.getInt("kill-at-y", 0); +            if (killAtY != 0) { +                arena.killAtY = killAtY; +            } + +            Result<Location> res = readWorld(anArenaSection.getConfigurationSection("game-spawn")); +            if (res.success) { +                arena.gameSpawn = res.value; +            } + +            Result<Location> lobbyRes = readWorld(anArenaSection.getConfigurationSection("lobby")); +            if (lobbyRes.success) { +                arena.lobby = lobbyRes.value; +            } + +            Result<Location> winnerLobbyRes = readWorld(anArenaSection.getConfigurationSection("winner-lobby")); +            if (winnerLobbyRes.success) { +                arena.winnerLobby = winnerLobbyRes.value; +            } + +            Result<Location> waitAreaRes = readWorld(anArenaSection.getConfigurationSection("wait-area")); +            if (waitAreaRes.success) { +                arena.waitArea = waitAreaRes.value; +            } + +        } +    } + +    /** +     * tries to convert a config section in the following format to a world +     * section: +     *   x:  +     *   y:  +     *   z: +     *   world: +     * @param section the section in the yaml with x, y, z, and world as its children +     * @return result of either:  +     *   success = true and a world +     *   success = false and an error string +     */ +    private static Result<Location> readWorld(@Nullable ConfigurationSection section) { + +        if (section == null) { +            return new Result<>("Section missing from config"); +        } + +        double x = section.getDouble("x"); +        double y = section.getDouble("y"); +        double z = section.getDouble("z"); +        if (x == 0 || y == 0 || z == 0) { +            return new Result<>("Arena coordinates are missing or are zero. Coordinates cannot be zero."); +        } + +        String worldName = section.getString("world"); +        if (worldName == null) { +            return new Result<>("World name is missing"); +        } + +        World world = Bukkit.getWorld(worldName); +        if (world == null) { +            return new Result<>("Failed to load world " + worldName); +        } + +        return new Result<>(new Location(world,x,y,z)); +    } + +    public static void WriteConfig() { + +        for (Arena arena: arenas.values()) { +            WriteWorld("arenas."+arena.name+".game-spawn", arena.gameSpawn); +            WriteWorld("arenas."+arena.name+".lobby", arena.lobby); +            WriteWorld("arenas."+arena.name+".winner-lobby", arena.winnerLobby); +            WriteWorld("arenas."+arena.name+".wait-area", arena.waitArea); +        } + +        plugin.saveConfig(); + +    } + +    private static void WriteWorld(String path, Location location) { +        ConfigurationSection section = config.getConfigurationSection(path); + +        if (section == null) { +            section = plugin.getConfig().createSection(path); +        } + +        section.set("x", location.getX()); +        section.set("y", location.getY()); +        section.set("z", location.getZ()); +        section.set("world", Objects.requireNonNull(location.getWorld()).getName()); +    } + +    /** +     * Searches all arenas for a game that player p is in +     * @param p Player to search for +     * @return the game the player is in, or null if not found +     */ +    public static Game findGamePlayerIsIn(Player p) { +        for (Arena a : arenas.values()) { +            if (a.game != null && a.game.gamePlayers.contains(p)) { +                return a.game; +            } +        } +        return null; +    } +} diff --git a/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java b/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java new file mode 100644 index 0000000..ea7414a --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java @@ -0,0 +1,23 @@ +package com.MylesAndMore.Tumble.config; + +import org.bukkit.configuration.file.FileConfiguration; + +import static com.MylesAndMore.Tumble.Main.plugin; + +public class ConfigManager { +    private static FileConfiguration config; + +    public static boolean HideLeaveJoin; +    public static int waitDuration; + +    public ConfigManager() { +        config = plugin.getConfig(); +    } + +    public static void readConfig() { +        HideLeaveJoin = config.getBoolean("hideJoinLeaveMessages", false); +        waitDuration = config.getInt("wait-duration", 15); +    } + + +} diff --git a/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java b/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java new file mode 100644 index 0000000..c050d3c --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java @@ -0,0 +1,50 @@ +package com.MylesAndMore.Tumble.config; + +import org.bukkit.ChatColor; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.awt.*; +import java.io.File; +import java.io.IOException; + +import static com.MylesAndMore.Tumble.Main.plugin; + +public class LanguageManager { +    private static FileConfiguration config; + +    public LanguageManager() { +        String fileName = "language.yml"; +        // create config +        File customConfigFile = new File(plugin.getDataFolder(), fileName); +        if (!customConfigFile.exists()) { +            customConfigFile.getParentFile().mkdirs(); +            plugin.saveResource(fileName, false); +        } + +        config = new YamlConfiguration(); +        try { +            config.load(customConfigFile); +        } catch (IOException | InvalidConfigurationException e) { +            e.printStackTrace(); +        } +        /* User Edit: +            Instead of the above Try/Catch, you can also use +            YamlConfiguration.loadConfiguration(customConfigFile) +        */ +    } + + +    public static String fromKey(String key) { +        return fromKeyNoPrefix("prefix") + fromKeyNoPrefix(key); +    } + +    public static String fromKeyNoPrefix(String key) { +        String tmp = config.getString(key, "LANG_ERR"); +        if (tmp.equals("LANG_ERR")) { +            plugin.getLogger().severe("There was an error getting key '"+ key +"' from language.yml"); +        } +        return ChatColor.translateAlternateColorCodes('&',tmp); +    } +} diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Arena.java b/src/main/java/com/MylesAndMore/Tumble/game/Arena.java index 831f251..71849e6 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Arena.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Arena.java @@ -8,21 +8,22 @@ import org.jetbrains.annotations.NotNull;   * An arena is the world and spawn location where a game can take place. An arena can only host one game at a time.   */  public class Arena { -    public Game game = null; -    public final World world; -    public Location location; +      public final String name; -    public Integer killAtY; + +    public Integer killAtY = null; +    public Location gameSpawn = null; +    public Location lobby = null; +    public Location winnerLobby = null; +    public Location waitArea = null; + +    public Game game = null;      /**       * Creates a new Arena       * @param name Name of the arena -     * @param location Center point / spawn point.       */ -    public Arena(@NotNull String name, @NotNull Location location, Integer killAtY) { -        this.location = location; -        this.world = location.getWorld(); +    public Arena(@NotNull String name) {          this.name = name; -        this.killAtY = killAtY;      }  } diff --git a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java index 28fb0d8..36d02b6 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java @@ -2,7 +2,8 @@ package com.MylesAndMore.Tumble.game;  import java.util.Objects; -import com.MylesAndMore.Tumble.plugin.ConfigManager; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.config.ConfigManager;  import com.MylesAndMore.Tumble.plugin.GameState;  import com.MylesAndMore.Tumble.plugin.GameType;  import org.bukkit.*; @@ -32,7 +33,7 @@ public class EventListener implements Listener {      Game game;      public EventListener(Game game) {          this.game = game; -        this.gameWorld = game.gameWorld; +        this.gameWorld = game.arena.gameSpawn.getWorld();      }      @EventHandler @@ -43,7 +44,7 @@ public class EventListener implements Listener {          }          if (event.getPlayer().getWorld() == gameWorld) {              // Send the player back to the lobby if they try to join in the middle of a game -            event.getPlayer().teleport(Objects.requireNonNull(ConfigManager.lobby)); +            event.getPlayer().teleport(Objects.requireNonNull(game.arena.lobby));          }      } @@ -54,7 +55,7 @@ public class EventListener implements Listener {              event.setQuitMessage(null);          }          if (event.getPlayer().getWorld() == gameWorld) { -            event.getPlayer().teleport(ConfigManager.lobby); +            event.getPlayer().teleport(game.arena.lobby);              game.removePlayer(event.getPlayer());          }      } @@ -101,7 +102,7 @@ public class EventListener implements Listener {              if (event.getEntity() instanceof Snowball) {                  if (event.getEntity().getShooter() instanceof Player p) {                      if (event.getHitBlock() != null) { -                        if (event.getHitBlock().getLocation().distanceSquared(Objects.requireNonNull(game.arena.location)) < 579) { +                        if (event.getHitBlock().getLocation().distanceSquared(Objects.requireNonNull(game.arena.gameSpawn)) < 579) {                              p.playEffect(                                  event.getHitBlock().getLocation(),                                  Effect.STEP_SOUND, @@ -209,7 +210,7 @@ public class EventListener implements Listener {      public void PlayerRespwanEvent(PlayerRespawnEvent event) {          // Make sure players respawn in the correct location          if (game.gamePlayers.contains(event.getPlayer())) { -            event.setRespawnLocation(game.arena.location); +            event.setRespawnLocation(game.arena.gameSpawn);          }      } diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index 113a5cd..dd13b31 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -1,6 +1,8 @@  package com.MylesAndMore.Tumble.game; -import com.MylesAndMore.Tumble.plugin.ConfigManager; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.config.ConfigManager; +import com.MylesAndMore.Tumble.config.LanguageManager;  import com.MylesAndMore.Tumble.plugin.GameState;  import com.MylesAndMore.Tumble.plugin.GameType;  import net.md_5.bungee.api.ChatMessageType; @@ -25,7 +27,6 @@ public class Game {      public final GameType type;      public final Arena arena; -    public final World gameWorld;      private final Location gameSpawn;      public final List<Player> gamePlayers = new ArrayList<>();      private final HashMap<Player, Integer> gameWins = new HashMap<>(); @@ -40,8 +41,7 @@ public class Game {      public Game(@NotNull Arena arena, @NotNull GameType type) {          this.arena = arena;          this.type = type; -        this.gameWorld = arena.world; -        this.gameSpawn = arena.location; +        this.gameSpawn = arena.gameSpawn;      } @@ -61,6 +61,10 @@ public class Game {          eventListener = new EventListener(this);          Bukkit.getServer().getPluginManager().registerEvents(eventListener, plugin); +        for (Player p : gamePlayers) { +            inventories.put(p, p.getInventory().getContents()); +        } +          roundStart();      } @@ -111,7 +115,7 @@ public class Game {                  gameID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> {                      clearInventories(gamePlayers);                      giveItems(gamePlayers, new ItemStack(Material.SNOWBALL)); -                    displayActionbar(gamePlayers, ChatColor.DARK_RED + "Showdown!"); +                    displayActionbar(gamePlayers, LanguageManager.fromKeyNoPrefix("showdown"));                      playSound(gamePlayers, Sound.ENTITY_ELDER_GUARDIAN_CURSE, SoundCategory.HOSTILE, 1, 1);                      // End the round in another 2m30s                      gameID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, this::roundEnd, 3000); @@ -150,12 +154,12 @@ public class Game {              }              // If that player doesn't have three wins, nobody else does, so we need another round              else { -                displayTitles(gamePlayers, ChatColor.RED + "Round over!", ChatColor.GOLD + winner.getName() + " has won the round!", 5, 60, 5); +                displayTitles(gamePlayers, LanguageManager.fromKeyNoPrefix("round-over"), LanguageManager.fromKeyNoPrefix("round-winner").replace("%winner%", winner.getDisplayName()), 5, 60, 5);                  Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, this::roundStart, 100);              }          }          else { -            displayTitles(gamePlayers, ChatColor.RED + "Round over!", ChatColor.GOLD + "Draw!", 5, 60, 5); +            displayTitles(gamePlayers, LanguageManager.fromKeyNoPrefix("round-over"), LanguageManager.fromKeyNoPrefix("round-draw"), 5, 60, 5);              Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, this::roundStart, 100);          }      } @@ -171,9 +175,9 @@ public class Game {              Player winner = getPlayerWithMostWins(gameWins);              if (winner != null) { -                displayTitles(gamePlayers, ChatColor.RED + "Game over!", ChatColor.GOLD + winner.getName() + " has won the game!", 5, 60, 5); +                displayTitles(gamePlayers, LanguageManager.fromKeyNoPrefix("game-over"), LanguageManager.fromKeyNoPrefix("game-winner").replace("%winner%",winner.getDisplayName()), 5, 60, 5);              } -            displayActionbar(gamePlayers, ChatColor.BLUE + "Returning to lobby in ten seconds..."); +            displayActionbar(gamePlayers, LanguageManager.fromKeyNoPrefix("lobby-in-10"));              // Wait 10s (200t), then              Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { @@ -186,11 +190,11 @@ public class Game {                          p.getInventory().setContents(inventories.get(p));                      } -                    if (p == winner && ConfigManager.winnerLobby != null) { -                        p.teleport(ConfigManager.winnerLobby); +                    if (p == winner && arena.winnerLobby != null) { +                        p.teleport(arena.winnerLobby);                      }                      else { -                        p.teleport(Objects.requireNonNull(ConfigManager.lobby)); +                        p.teleport(Objects.requireNonNull(arena.lobby));                      }                  } @@ -218,7 +222,7 @@ public class Game {          if (inventories.containsKey(p)) {              p.getInventory().setContents(inventories.get(p));          } -        p.teleport(ConfigManager.lobby); +        p.teleport(arena.lobby);      }      /** @@ -229,16 +233,15 @@ public class Game {      public void addPlayer(Player p) {          gamePlayers.add(p);          // save inventory -        inventories.put(p, p.getInventory().getContents()); -        if (ConfigManager.waitArea != null) { -            p.teleport(ConfigManager.waitArea); +        if (arena.waitArea != null) { +            p.teleport(arena.waitArea);              p.getInventory().clear();          }          if (gamePlayers.size() >= 2 && gameState == GameState.WAITING) {              autoStart();          }          else { -            displayActionbar(Collections.singletonList(p), ChatColor.YELLOW + "Waiting for players"); +            displayActionbar(Collections.singletonList(p), LanguageManager.fromKeyNoPrefix("waiting-for-players"));          }      } @@ -249,7 +252,7 @@ public class Game {          // Wait for the player to load in          int waitDuration = ConfigManager.waitDuration;          Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { -            displayActionbar(gamePlayers, ChatColor.GREEN + "Game will begin in "+waitDuration+" seconds!"); +            displayActionbar(gamePlayers, LanguageManager.fromKeyNoPrefix("time-till-start").replace("%wait%",waitDuration+""));              playSound(gamePlayers, Sound.BLOCK_NOTE_BLOCK_CHIME, SoundCategory.BLOCKS, 1, 1);              // Schedule a process to start the game in 300t (15s) and save the PID so we can cancel it later if needed              autoStartID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, this::gameStart, waitDuration * 20L); @@ -280,6 +283,7 @@ public class Game {          double x = gameSpawn.getX();          double y = gameSpawn.getY();          double z = gameSpawn.getZ(); +        World gameWorld = gameSpawn.getWorld();          // Create the scatter locations based off the game's spawn          List<Location> scatterLocations = new ArrayList<>(List.of(                  new Location(gameWorld, (x - 14.5), y, (z + 0.5), -90, 0), @@ -303,16 +307,16 @@ public class Game {       */      private void countdown(Runnable doAfter) {          playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1); -        displayTitles(gamePlayers, ChatColor.DARK_GREEN + "3", null, 3, 10, 7); +        displayTitles(gamePlayers, LanguageManager.fromKeyNoPrefix("count-3"), null, 3, 10, 7);          Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> {              playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1); -            displayTitles(gamePlayers, ChatColor.YELLOW + "2", null, 3, 10, 7); +            displayTitles(gamePlayers, LanguageManager.fromKeyNoPrefix("count-2"), null, 3, 10, 7);              Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> {                  playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1); -                displayTitles(gamePlayers, ChatColor.DARK_RED + "1", null, 3, 10, 7); +                displayTitles(gamePlayers, LanguageManager.fromKeyNoPrefix("count-1"), null, 3, 10, 7);                  Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> {                      playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 2); -                    displayTitles(gamePlayers, ChatColor.GREEN + "Go!", null, 1, 5, 1); +                    displayTitles(gamePlayers, LanguageManager.fromKeyNoPrefix("count-go"), null, 1, 5, 1);                      doAfter.run();                  }, 20);              }, 20); diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java b/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java deleted file mode 100644 index c4c1943..0000000 --- a/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java +++ /dev/null @@ -1,194 +0,0 @@ -package com.MylesAndMore.Tumble.plugin; - -import com.MylesAndMore.Tumble.game.Arena; -import com.MylesAndMore.Tumble.game.Game; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; -import java.util.Objects; - -import static com.MylesAndMore.Tumble.Main.plugin; - -public class ConfigManager { -    public static HashMap<String, Arena> arenas; -    public static Location lobby; -    public static Location winnerLobby; -    public static Location waitArea; -    public static boolean HideLeaveJoin; -    public static int waitDuration; - -    /** -     * Reads config file and populates values above -     */ -    public static void readConfig() { -        plugin.reloadConfig(); -        FileConfiguration config = plugin.getConfig(); - -        HideLeaveJoin = config.getBoolean("hideJoinLeaveMessages", false); -        waitDuration = config.getInt("wait-duration", 15); - -        // wait area -        if (config.getBoolean("wait-area.enable", false)) { -            Result<Location>res = readWorld(config.getConfigurationSection("wait-area.spawn")); -            if (!res.success) { -                plugin.getLogger().warning("Failed to load winner lobby: "+res.error); -                waitArea = null; -            } -            else { -                waitArea = res.value; -            } -        } - -        // lobby -        { -            Result<Location>res = readWorld(config.getConfigurationSection("lobby.spawn")); -            if (!res.success) { -                plugin.getLogger().warning("Failed to load lobby: "+res.error); -                plugin.getLogger().severe("Lobby spawn is required! Lobby spawn will default to spawn in the default world. Run '/tumble-config set lobbyWorld' to change it"); -                lobby = Bukkit.getServer().getWorlds().get(0).getSpawnLocation(); -            } -            else { -                lobby = res.value; -            } -        } -         -        // winner lobby -        if (config.getBoolean("winner-lobby.enable", false)) { -            Result<Location>res = readWorld(config.getConfigurationSection("winner-lobby.spawn")); -            if (!res.success) { -                plugin.getLogger().warning("Failed to load winner lobby: "+res.error); -                winnerLobby = null; -            } -            else { -                winnerLobby = res.value; -            } -        } - -        // arenas -        ConfigurationSection arenasSection = config.getConfigurationSection("arenas"); -        if (arenasSection == null) { -            plugin.getLogger().warning("Section 'arenas' is missing from config"); -            return; -        } -        arenas = new HashMap<>(); -        for (String arenaName: arenasSection.getKeys(false)) { - -            ConfigurationSection anArenaSection = arenasSection.getConfigurationSection(arenaName); -            if (anArenaSection == null) { -                plugin.getLogger().warning("Failed to load arena "+arenaName+": Error loading config section"); -                continue; -            } - -            Integer killAtY = anArenaSection.getInt("kill-at-y", 0); -            if (killAtY == 0) { -                killAtY = null; -            } - -            Result<Location> res = readWorld(anArenaSection.getConfigurationSection("spawn")); -            if (!res.success) { -                plugin.getLogger().warning("Failed to load arena "+arenaName+": "+res.error); -                continue; -            } - -            arenas.put(arenaName, new Arena(arenaName, res.value, killAtY)); -        } -    } - -    /** -     * tries to convert a config section in the following format to a world -     * section: -     *   x:  -     *   y:  -     *   z: -     *   world: -     * @param section the section in the yaml with x, y, z, and world as its children -     * @return result of either:  -     *   success = true and a world -     *   success = false and an error string -     */ -    private static Result<Location> readWorld(@Nullable ConfigurationSection section) { - -        if (section == null) { -            return new Result<>("Section missing from config"); -        } - -        double x = section.getDouble("x"); -        double y = section.getDouble("y"); -        double z = section.getDouble("z"); -        if (x == 0 || y == 0 || z == 0) { -            return new Result<>("Arena coordinates are missing or are zero. Coordinates cannot be zero."); -        } - -        String worldName = section.getString("world"); -        if (worldName == null) { -            return new Result<>("World name is missing"); -        } - -        World world = Bukkit.getWorld(worldName); -        if (world == null) { -            return new Result<>("Failed to load world " + worldName); -        } - -        return new Result<>(new Location(world,x,y,z)); -    } - -    public static void WriteConfig() { -        if (waitArea != null) { -            WriteWorld(Objects.requireNonNull(plugin.getConfig().getConfigurationSection("wait-area")), waitArea); -            plugin.getConfig().set("wait-area.enable", true); -        } -        else { -            plugin.getConfig().set("wait-area.enable", false); -        } - -        if (lobby != null) { -            WriteWorld(Objects.requireNonNull(plugin.getConfig().getConfigurationSection("lobby.spawn")), lobby); -        } - -        if (winnerLobby != null) { -            WriteWorld(Objects.requireNonNull(plugin.getConfig().getConfigurationSection("winner-lobby.spawn")), winnerLobby); -            plugin.getConfig().set("winner-lobby.enable", true); -        } -        else { -            plugin.getConfig().set("winner-lobby.enable", false); -        } - -        for (String arenaName: arenas.keySet()) { -            ConfigurationSection c = plugin.getConfig().getConfigurationSection("arenas."+arenaName+".spawn"); -            if (c == null) { -                 c = plugin.getConfig().createSection("arenas."+arenaName+".spawn"); -            } -            WriteWorld(c, arenas.get(arenaName).location); -        } - -        plugin.saveConfig(); - -    } - -    private static void WriteWorld(ConfigurationSection section, Location location) { -        section.set("x", location.getX()); -        section.set("y", location.getY()); -        section.set("z", location.getZ()); -        section.set("world", Objects.requireNonNull(location.getWorld()).getName()); -    } - -    /** -     * Searches all arenas for a game that player p is in -     * @param p Player to search for -     * @return the game the player is in, or null if not found -     */ -    public static Game findGamePlayerIsIn(Player p) { -        for (Arena a : arenas.values()) { -            if (a.game != null && a.game.gamePlayers.contains(p)) { -                return a.game; -            } -        } -        return null; -    } -} diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/SubCommand.java b/src/main/java/com/MylesAndMore/Tumble/plugin/SubCommand.java new file mode 100644 index 0000000..2158584 --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/plugin/SubCommand.java @@ -0,0 +1,8 @@ +package com.MylesAndMore.Tumble.plugin; + +import org.bukkit.command.CommandExecutor; + +public interface SubCommand extends CommandExecutor { +    public String getCommandName(); +    public String getPermission(); +} diff --git a/src/main/resources/arenas.yml b/src/main/resources/arenas.yml new file mode 100644 index 0000000..7b902be --- /dev/null +++ b/src/main/resources/arenas.yml @@ -0,0 +1,23 @@ +arenas: +  'test': +    kill-at-y: 5 +    game-spawn: +      x: 100 +      y: 100 +      z: 100 +      world: world +    lobby: +      x: 0.5 +      y: 100 +      z: 0.5 +      world: world +    winner-lobby: +      x: 0.5 +      y: 100 +      z: 0.5 +      world: world +    wait-arena: +      x: 0.5 +      y: 100 +      z: 0.5 +      world: world
\ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index b13570e..97f0a12 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -3,42 +3,5 @@ hideJoinLeaveMessages: false  # Duration in seconds to wait for more players to join  wait-duration: 15 -# Teleport players somewhere while waiting for the game to start -# Keep in mind that these coordinates cannot be zero! Use something like 0.5 instead -wait-area: -  enable: false -  spawn: -    x: -    y: -    z: -    world: -# Place where everyone is teleported to after a game ends REQUIRED -# Keep in mind that these coordinates cannot be zero! Use something like 0.5 instead -lobby: -  spawn: -    x: 0.5 -    y: 100 -    z: 0.5 -    world: world -# Place that the winner is teleported after a game ends -# Keep in mind that these coordinates cannot be zero! Use something like 0.5 instead -winner-lobby: -  enable: false -  spawn: -    x: -    y: -    z: -    world: - -# Add/remove as you wish -# Keep in mind that these coordinates cannot be zero! Use something like 0.5 instead -arenas: -  'test': -    kill-at-y: 5 -    spawn: -      x: 100 -      y: 100 -      z: 100 -      world: world
\ No newline at end of file diff --git a/src/main/resources/language.yml b/src/main/resources/language.yml new file mode 100644 index 0000000..fa57b1c --- /dev/null +++ b/src/main/resources/language.yml @@ -0,0 +1,42 @@ +prefix: "&f[&eTumble&f] " + +unknown-command: "&4Unknown command '%command%'" +no-permission: "&4You do not have permission to perform this command! &7Required permission: '%permission%.'" +missing-arena-parameter: "&4Missing arena name!" +invalid-arena: "&4arena '%arena%' does not exist!" +invalid-type: "&4Invalid game type" +no-game-in-arena: "&4No game is currently running in this arena" +player-not-in-game: "&4You are not in a game!" +not-for-console: "&4This cannot be run by the console" +game-in-progress: "&4This game is still in progress!&7 wait until it finishes or join another game" +another-type-in-arena: "A game of %type% is currently taking place in this arena!&7 choose another arena or join it with &a/tmbl join %arena% %type%" +already-in-game: "&4You are already in a game! Leave it to join another one." + +create-success: "&aArena created successfully! &eBefore you can join, you must set a game spawn location with /tmbl setGameSpawn" +forcestart-success: "&aStarting game." +forcestop-success: "&aGame stopped." +join-success: "&aJoined game &d%arena% - %type%" +leave-success: "&aLeft game &d%arena% - %type%" +reload-success: "&aConfiguration reloaded. &eCheck console for errors." +remove-success: "&aArena removed." +set-success: "&aLocation set." + +showdown: "&4Showdown!" +lobby-in-10: "&9Returning to lobby in ten seconds..." +waiting-for-players: "&aWaiting for players" +time-till-start: "&aGame will begin in %wait% seconds!" +round-over: "&cRound over!" +round-winner: "&6%winner%  has won the round!" +round-draw: "&6Draw!" +game-over: "&Game over!" +game-winner: "&6%winner%  has won the Game!" +count-3: "&23" +count-2: "&e2" +count-1: "&41" +count-go: "&aGo!" + + + + + + diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 486fccf..ed246c5 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -7,31 +7,14 @@ load: POSTWORLD  author: MylesAndMore  website: https://github.com/MylesAndMore/Tumble  softdepend: [Multiverse-Core] +  commands: -  tumble-join: +  tumble:      description: Joins a Tumble match. -    usage: '§cUsage: /tumble-join <arenaName> [gameType]' +    usage: '§cUsage: /tumble'      permission: tumble.join -  tumble-leave: -    description: Quits a Tumble match. -    usage: '§cUsage: /tumble-leave' -    permission: tumble.leave -  tumble-forcestart: -    description: Force starts a Tumble match. -    usage: '§cUsage: /tumble-forcestart [arenaName]' -    permission: tumble.forcestart -  tumble-forcestop: -    description: Force stops a Tumble match. -    usage: '§cUsage: /tumble-forcestop [arenaName]' -    permission: tumble.forcestop -  tumble-config: -    description: Modify arenas and worlds. -    usage: '§cUsage: /tumble-config <add|set|disable|delete> <data>' -    permission: tumble.config -  tumble-reload: -    description: Reloads the plugin's config. -    usage: '§cUsage: /tumble-reload' -    permission: tumble.reload +    aliases: tmbl +  permissions:    tumble.join:      description: Allows you to join a Tumble match. | 
