From 2664109ebcc022e0f9e02c2bd173b70608a68a72 Mon Sep 17 00:00:00 2001 From: sowgro Date: Wed, 5 Jun 2024 13:32:26 -0400 Subject: [PATCH 01/51] Downgrade to 1.16.5 --- build.gradle | 4 ++-- src/main/java/com/MylesAndMore/Tumble/Main.java | 6 ------ src/main/java/com/MylesAndMore/Tumble/game/Game.java | 4 ++-- src/main/java/com/MylesAndMore/Tumble/game/Layers.java | 2 +- src/main/resources/plugin.yml | 2 +- 5 files changed, 6 insertions(+), 12 deletions(-) diff --git a/build.gradle b/build.gradle index 8791ad3..58b2dc8 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { java { toolchain { - languageVersion = JavaLanguageVersion.of(17) + languageVersion = JavaLanguageVersion.of(16) } } @@ -18,7 +18,7 @@ repositories { } dependencies { - compileOnly('org.spigotmc:spigot-api:1.19.2-R0.1-SNAPSHOT') + compileOnly('org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT') compileOnly('com.onarandombox.multiversecore:Multiverse-Core:4.3.1') implementation('org.bstats:bstats-bukkit:3.0.2') implementation('com.jeff_media:SpigotUpdateChecker:3.0.3') diff --git a/src/main/java/com/MylesAndMore/Tumble/Main.java b/src/main/java/com/MylesAndMore/Tumble/Main.java index c264498..a9a07ee 100644 --- a/src/main/java/com/MylesAndMore/Tumble/Main.java +++ b/src/main/java/com/MylesAndMore/Tumble/Main.java @@ -35,12 +35,6 @@ public void onEnable() { Bukkit.getServer().getLogger().info("[Tumble] If this is your first time running the plugin, you may disregard this message."); } - new UpdateChecker(this, UpdateCheckSource.SPIGET, "106721") - .setDownloadLink("https://github.com/MylesAndMore/Tumble/releases") - .setNotifyByPermissionOnJoin("tumble.update") // only this permission node is notified NOT all OPs so people can unsubscribe if they wish - .checkEveryXHours(336) // (every 2 weeks) - .checkNow(); - Bukkit.getServer().getLogger().info("[Tumble] Tumble successfully enabled!"); } } \ No newline at end of file diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index 0ea74f5..cdaede9 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -12,8 +12,8 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import javax.annotation.Nullable; import java.util.*; /** @@ -396,7 +396,7 @@ private void displayActionbar(List players, String message) { */ private void playSound(@NotNull List players, @NotNull Sound sound, @NotNull SoundCategory category, float volume, float pitch) { for (Player aPlayer : players) { - aPlayer.playSound(aPlayer, sound, category, volume, pitch); + aPlayer.playSound(aPlayer.getLocation(), sound, category, volume, pitch); } } diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Layers.java b/src/main/java/com/MylesAndMore/Tumble/game/Layers.java index ed92dc9..b87df26 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Layers.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Layers.java @@ -202,7 +202,7 @@ public Layers() { matList.add(gen10); List gen12 = new ArrayList<>() {{ add(Material.DIRT); - add(Material.DIRT_PATH); + add(Material.GRASS_PATH); add(Material.GRASS_BLOCK); add(Material.OAK_SLAB); add(Material.BRICK_WALL); diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 23d50ca..d070c3e 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -2,7 +2,7 @@ main: com.MylesAndMore.Tumble.Main name: Tumble version: 1.0.4 description: 'A Minecraft: Java Edition plugin recreating the Tumble minigame from Minecraft Legacy Console Edition.' -api-version: 1.19 +api-version: 1.16 load: STARTUP author: MylesAndMore website: https://github.com/MylesAndMore/Tumble From baee001a9eceeae9f12a5f701c138123ff21b177 Mon Sep 17 00:00:00 2001 From: sowgro Date: Sun, 9 Jun 2024 14:29:05 -0400 Subject: [PATCH 02/51] Implement multi arena support, and various other improvements (pretty much a rewrite) --- README.md | 12 +- .../java/com/MylesAndMore/Tumble/Main.java | 38 +- .../MylesAndMore/Tumble/commands/Config.java | 35 + .../Tumble/commands/ForceStart.java | 48 ++ .../Tumble/commands/ForceStop.java | 48 ++ .../MylesAndMore/Tumble/commands/Join.java | 103 +++ .../MylesAndMore/Tumble/commands/Leave.java | 53 ++ .../MylesAndMore/Tumble/commands/Reload.java | 26 +- .../Tumble/commands/SetAutoStart.java | 94 --- .../Tumble/commands/SetWinnerLoc.java | 110 --- .../Tumble/commands/SetWorldConfig.java | 74 -- .../Tumble/commands/StartGame.java | 85 --- .../com/MylesAndMore/Tumble/game/Arena.java | 26 + .../Tumble/game/EventListener.java | 204 ++++++ .../com/MylesAndMore/Tumble/game/Game.java | 652 +++++++----------- .../MylesAndMore/Tumble/game/Generator.java | 135 +++- .../Tumble/plugin/ConfigManager.java | 152 ++++ .../MylesAndMore/Tumble/plugin/Constants.java | 25 - .../Tumble/plugin/EventListener.java | 222 ------ .../MylesAndMore/Tumble/plugin/GameState.java | 7 + .../MylesAndMore/Tumble/plugin/GameType.java | 11 + .../MylesAndMore/Tumble/plugin/Result.java | 8 + src/main/resources/config.yml | 49 +- src/main/resources/plugin.yml | 72 +- 24 files changed, 1187 insertions(+), 1102 deletions(-) create mode 100644 src/main/java/com/MylesAndMore/Tumble/commands/Config.java create mode 100644 src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java create mode 100644 src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java create mode 100644 src/main/java/com/MylesAndMore/Tumble/commands/Join.java create mode 100644 src/main/java/com/MylesAndMore/Tumble/commands/Leave.java delete mode 100644 src/main/java/com/MylesAndMore/Tumble/commands/SetAutoStart.java delete mode 100644 src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLoc.java delete mode 100644 src/main/java/com/MylesAndMore/Tumble/commands/SetWorldConfig.java delete mode 100644 src/main/java/com/MylesAndMore/Tumble/commands/StartGame.java create mode 100644 src/main/java/com/MylesAndMore/Tumble/game/Arena.java create mode 100644 src/main/java/com/MylesAndMore/Tumble/game/EventListener.java create mode 100644 src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java delete mode 100644 src/main/java/com/MylesAndMore/Tumble/plugin/Constants.java delete mode 100644 src/main/java/com/MylesAndMore/Tumble/plugin/EventListener.java create mode 100644 src/main/java/com/MylesAndMore/Tumble/plugin/GameState.java create mode 100644 src/main/java/com/MylesAndMore/Tumble/plugin/GameType.java create mode 100644 src/main/java/com/MylesAndMore/Tumble/plugin/Result.java diff --git a/README.md b/README.md index a2b349b..d06fefa 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Tumble is a Spigot/Paper plugin that aims to recreate the Tumble minigame from t ## What *is* Tumble? -If you've never heard of it, [Tumble](https://minecraft-archive.fandom.com/wiki/Tumble_Mode) is a twist on the classic Minecraft minigame of spleef, where the objective is to break the blocks under your opponents. But in Tumble, you play on randomly generated layers of blocks, using shovels, snowballs, or both to try and eliminate your opponents. +If you've never heard of it, [Tumble](https://minecraft.wiki/w/Tumble) is a twist on the classic Minecraft minigame of spleef, where the objective is to break the blocks under your opponents. But in Tumble, you play on randomly generated layers of blocks, using shovels, snowballs, or both to try and eliminate your opponents. ## Features @@ -17,19 +17,19 @@ If you've never heard of it, [Tumble](https://minecraft-archive.fandom.com/wiki/ - Support for 2-8 players - Highly customizable - Open-source codebase +- Multiple arenas and concurrent games ## Setup -1. Simply [download](https://github.com/MylesAndMore/Tumble/releases) the plugin's JAR file and place it in your server's plugins directory. - - - *Note: Multiverse is also required for the plugin to run, you may download it [here](https://www.spigotmc.org/resources/multiverse-core.390/).* - +1. Simply [download](https://github.com/MylesAndMore/Tumble/releases) the plugin's JAR file and place it in your server's plugins directory. 2. Make sure that you have at least two worlds in your world directory! One is for your lobby world, and the other is for your game arena. - If you would like an experience similar to the original game, see [my guide](https://github.com/MylesAndMore/tumble/blob/main/og-guide.md) for using the original worlds. 3. Start your server. The plugin will generate a couple of warnings, these are normal. -4. Ensure that you have imported your worlds into Multiverse. This can be done with the command ```/mv import normal```. +4. Ensure that you have imported your worlds using a plugin like Multiverse. This can be done with the command ```/mv import normal```. + +[//]: # (TODO: finish this once config system is done) 5. Now you need to tell Tumble which world is your lobby and which world is your game arena. You can do this with ```/tumble:link lobby``` and ```/tumble:link game``` respectively. 6. **VERY IMPORTANT:** The plugin will teleport players to the world spawn point of each world, and generate the game's blocks around the spawn point of the game world. Ensure that your spawn points are clear of any obstructions, and that a 20x20x20 cube is cleared out from the spawn of whatever game world you are using. **Any blocks in this area will be destroyed when the game begins.** 7. You're done! You can now start games with the command ```/tumble:start```. diff --git a/src/main/java/com/MylesAndMore/Tumble/Main.java b/src/main/java/com/MylesAndMore/Tumble/Main.java index a9a07ee..f3ea52e 100644 --- a/src/main/java/com/MylesAndMore/Tumble/Main.java +++ b/src/main/java/com/MylesAndMore/Tumble/Main.java @@ -1,39 +1,33 @@ package com.MylesAndMore.Tumble; import com.MylesAndMore.Tumble.commands.*; -import com.MylesAndMore.Tumble.plugin.Constants; -import com.MylesAndMore.Tumble.plugin.EventListener; - -import com.jeff_media.updatechecker.UpdateCheckSource; -import com.jeff_media.updatechecker.UpdateChecker; +import com.MylesAndMore.Tumble.plugin.ConfigManager; import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; +import java.util.Objects; + public class Main extends JavaPlugin{ + public static Main plugin; + @Override public void onEnable() { - // Register setup items - getServer().getPluginManager().registerEvents(new EventListener(), this); - this.getCommand("reload").setExecutor(new Reload()); - this.getCommand("link").setExecutor(new SetWorldConfig()); - this.getCommand("start").setExecutor(new StartGame()); - this.getCommand("winlocation").setExecutor(new SetWinnerLoc()); - this.getCommand("autostart").setExecutor(new SetAutoStart()); + plugin = this; + + Objects.requireNonNull(this.getCommand("reload")).setExecutor(new Reload()); + Objects.requireNonNull(this.getCommand("config")).setExecutor(new Config()); + Objects.requireNonNull(this.getCommand("forcestart")).setExecutor(new ForceStart()); + Objects.requireNonNull(this.getCommand("join")).setExecutor(new Join()); + Objects.requireNonNull(this.getCommand("leave")).setExecutor(new Leave()); + Objects.requireNonNull(this.getCommand("forcestop")).setExecutor(new ForceStop()); new Metrics(this, 16940); - this.saveDefaultConfig(); // Saves the default config file (packaged in the JAR) if we haven't already + // TODO: change command format - // Check if worlds are null in config and throw warnings if so - if (Constants.getGameWorld() == null) { - Bukkit.getServer().getLogger().warning("[Tumble] It appears you have not configured a game world for Tumble."); - Bukkit.getServer().getLogger().info("[Tumble] If this is your first time running the plugin, you may disregard this message."); - } - if (Constants.getLobbyWorld() == null) { - Bukkit.getServer().getLogger().warning("[Tumble] It appears you have not configured a lobby world for Tumble."); - Bukkit.getServer().getLogger().info("[Tumble] If this is your first time running the plugin, you may disregard this message."); - } + 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 new file mode 100644 index 0000000..d91a5b5 --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Config.java @@ -0,0 +1,35 @@ +package com.MylesAndMore.Tumble.commands; + +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.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; + } + + sender.sendMessage(ChatColor.RED + "Not implemented yet"); // TODO + return true; + } + + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { + return new ArrayList<>(); + } +} diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java new file mode 100644 index 0000000..78ff183 --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java @@ -0,0 +1,48 @@ +package com.MylesAndMore.Tumble.commands; + +import com.MylesAndMore.Tumble.game.Game; +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.List; + +public class ForceStart implements CommandExecutor, TabCompleter { + @Override + public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { + + if (!sender.hasPermission("tumble.forcestart")) { + sender.sendMessage(ChatColor.RED + "You do not have permission to perform this command!"); + return false; + } + + Game game; + if (args.length < 1 || args[0] == null) { + game = ConfigManager.findGamePlayerIsIn((Player)sender); + if (game == null) { + sender.sendMessage(ChatColor.RED + "Missing arena name"); + return false; + } + } + else { + game = ConfigManager.arenas.get(args[0]).game; + } + + game.startGame(); + return true; + } + + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { + if (args.length == 1) { + return ConfigManager.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 new file mode 100644 index 0000000..7f266d7 --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java @@ -0,0 +1,48 @@ +package com.MylesAndMore.Tumble.commands; + +import com.MylesAndMore.Tumble.game.Game; +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.List; + +public class ForceStop implements CommandExecutor, TabCompleter { + @Override + public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { + + if (!sender.hasPermission("tumble.forcestop")) { + sender.sendMessage(ChatColor.RED + "You do not have permission to perform this command!"); + return false; + } + + Game game; + if (args.length < 1 || args[0] == null) { + game = ConfigManager.findGamePlayerIsIn((Player)sender); + if (game == null) { + sender.sendMessage(ChatColor.RED + "Missing arena name"); + return false; + } + } + else { + game = ConfigManager.arenas.get(args[0]).game; + } + + game.killGame(); + return true; + } + + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { + if (args.length == 1) { + return ConfigManager.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 new file mode 100644 index 0000000..4dd4ef8 --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java @@ -0,0 +1,103 @@ +package com.MylesAndMore.Tumble.commands; + +import com.MylesAndMore.Tumble.game.Arena; +import com.MylesAndMore.Tumble.plugin.ConfigManager; +import com.MylesAndMore.Tumble.game.Game; +import com.MylesAndMore.Tumble.plugin.GameState; +import com.MylesAndMore.Tumble.plugin.GameType; +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; +import java.util.Objects; +import java.util.stream.Collectors; + +public class Join 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.join")) { + sender.sendMessage(ChatColor.RED + "You do not have permission to perform this command!"); + 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 (args.length < 1 || args[0] == null) { + sender.sendMessage(ChatColor.RED + "Missing arena name"); + return false; + } + String arenaName = args[0]; + if (!ConfigManager.arenas.containsKey(arenaName)) + { + sender.sendMessage(ChatColor.RED + "This arena does not exist"); + return false; + } + Arena arena = ConfigManager.arenas.get(arenaName); + + Game game; + if (args.length < 2 || args[1] == null) { + if (arena.game == null) { + sender.sendMessage(ChatColor.RED + "no game is currently taking place in this arena, specify the game type to start one"); + return false; + } + else { + game = arena.game; + } + } + else { + GameType type; + switch (args[1]) { + case "shovels", "shovel" -> type = GameType.SHOVELS; + case "snowballs", "snowball" -> type = GameType.SNOWBALLS; + case "mix", "mixed" -> type = GameType.MIXED; + default -> { + sender.sendMessage(ChatColor.RED + "Invalid game type"); + return false; + } + } + + if (arena.game == null) { + game = arena.game = new Game(arena, type); + } + 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); + 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"); + return false; + } + + game.addPlayer((Player)sender); + sender.sendMessage(ChatColor.GREEN + "Joined game " + arena.name + " - " + game.type); + return true; + } + + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { + if (args.length == 1) { + return ConfigManager.arenas.keySet().stream().toList(); + } + if (args.length == 2) { + return Arrays.stream(GameType.values()).map(Objects::toString).collect(Collectors.toList()); + } + return new ArrayList<>(); + } +} \ No newline at end of file diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Leave.java b/src/main/java/com/MylesAndMore/Tumble/commands/Leave.java new file mode 100644 index 0000000..94255a4 --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Leave.java @@ -0,0 +1,53 @@ +package com.MylesAndMore.Tumble.commands; + +import com.MylesAndMore.Tumble.game.Game; +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.List; + +public class Leave 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.leave")) { + sender.sendMessage(ChatColor.RED + "You do not have permission to perform this command!"); + return false; + } + + Game game; + if (args.length < 1 || args[0] == null) { + game = ConfigManager.findGamePlayerIsIn((Player)sender); + if (game == null) { + sender.sendMessage(ChatColor.RED + "Missing arena name"); + return false; + } + } + else { + game = ConfigManager.arenas.get(args[0]).game; + } + + game.removePlayer((Player) sender); + return true; + } + + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { + if (args.length == 1) { + return ConfigManager.arenas.keySet().stream().toList(); + } + return new ArrayList<>(); + } +} \ No newline at end of file diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java b/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java index ffc6dd8..5d35a03 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java @@ -1,22 +1,32 @@ package com.MylesAndMore.Tumble.commands; -import com.MylesAndMore.Tumble.plugin.Constants; 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.jetbrains.annotations.NotNull; -public class Reload implements CommandExecutor { +import java.util.ArrayList; +import java.util.List; + +import static com.MylesAndMore.Tumble.Main.plugin; + +public class Reload implements CommandExecutor, TabCompleter { + @Override public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { - if (sender.hasPermission("tumble.reload")) { - Constants.getPlugin().reloadConfig(); - sender.sendMessage(ChatColor.GREEN + "Tumble configuration reloaded successfully."); - } - else { - sender.sendMessage(ChatColor.RED + Constants.getPermissionMessage()); + if (!sender.hasPermission("tumble.reload")) { + sender.sendMessage(ChatColor.RED + "You do not have permission to perform this command!"); + return false; } + plugin.onEnable(); + sender.sendMessage(ChatColor.GREEN + "Tumble configuration reloaded successfully."); return true; } + + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { + return new ArrayList<>(); + } } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetAutoStart.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetAutoStart.java deleted file mode 100644 index b3da74e..0000000 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetAutoStart.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.MylesAndMore.Tumble.commands; - -import com.MylesAndMore.Tumble.plugin.Constants; -import org.bukkit.ChatColor; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.jetbrains.annotations.NotNull; - -import java.util.Objects; - -public class SetAutoStart implements CommandExecutor{ - @Override - public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { - if (sender.hasPermission("tumble.autostart")) { - if (Constants.getGameWorld() != null) { - if (Constants.getLobbyWorld() != null) { - if (args.length == 2) { - // Check the player # argument and parse it into an int - int args0; - try { - args0 = Integer.parseInt(args[0]); - } catch (NumberFormatException nfe){ - sender.sendMessage(ChatColor.RED + "Player amount must be a valid number."); - return true; - } catch (Exception e){ - sender.sendMessage(ChatColor.RED + "Invalid player amount."); - return true; - } - // PlayerAmount & enable/disable were entered - if ((args0 >= 2) && (args0 <= 8)) { - if (Objects.equals(args[1], "enable")) { - // Write values to the config - Constants.getPlugin().getConfig().set("autoStart.players", args0); - Constants.getPlugin().getConfig().set("autoStart.enabled", true); - Constants.getPlugin().saveConfig(); - sender.sendMessage(ChatColor.GREEN + "Configuration saved!"); - sender.sendMessage(ChatColor.GREEN + "Run " + ChatColor.GRAY + "/tumble:reload " + ChatColor.GREEN + "the changes to take effect."); - } - else if (Objects.equals(args[1], "disable")) { - Constants.getPlugin().getConfig().set("autoStart.players", args0); - Constants.getPlugin().getConfig().set("autoStart.enabled", false); - Constants.getPlugin().saveConfig(); - sender.sendMessage(ChatColor.GREEN + "Configuration saved!"); - sender.sendMessage(ChatColor.GREEN + "Run " + ChatColor.GRAY + "/tumble:reload " + ChatColor.GREEN + "the changes to take effect."); - } - else { - return false; - } - } - else { - sender.sendMessage(ChatColor.RED + "Please enter a player amount between two and eight!"); - } - } - else if (args.length == 1) { - // Only PlayerAmount was entered - int args0; - try { - args0 = Integer.parseInt(args[0]); - } catch (NumberFormatException nfe){ - sender.sendMessage(ChatColor.RED + "Player amount must be a valid number."); - return true; - } catch (Exception e){ - sender.sendMessage(ChatColor.RED + "Invalid player amount."); - return true; - } - if ((args0 >= 2) && (args0 <= 8)) { - Constants.getPlugin().getConfig().set("autoStart.players", args0); - Constants.getPlugin().saveConfig(); - sender.sendMessage(ChatColor.GREEN + "Configuration saved!"); - sender.sendMessage(ChatColor.GREEN + "Run " + ChatColor.GRAY + "/tumble:reload " + ChatColor.GREEN + "the changes to take effect."); - } - else { - sender.sendMessage(ChatColor.RED + "Please enter a player amount between two and eight!"); - } - } - else { - return false; - } - } - else { - sender.sendMessage(ChatColor.RED + "Please link a lobby world first!"); - } - } - else { - sender.sendMessage(ChatColor.RED + "Please link a game world first!"); - } - } - else { - sender.sendMessage(ChatColor.RED + Constants.getPermissionMessage()); - } - return true; - } -} diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLoc.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLoc.java deleted file mode 100644 index 38e6444..0000000 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLoc.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.MylesAndMore.Tumble.commands; - -import com.MylesAndMore.Tumble.plugin.Constants; -import org.bukkit.ChatColor; -import org.bukkit.Location; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; - -public class SetWinnerLoc implements CommandExecutor { - @Override - public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { - if (sender.hasPermission("tumble.winlocation")) { - if (Constants.getLobbyWorld() != null) { - if (sender instanceof Player) { - // Check the sender entered the correct number of args - if (args.length == 3) { - double args0 = 0; - double args1 = 0; - double args2 = 0; - try { - args0 = Double.parseDouble(args[0]); - args1 = Double.parseDouble(args[1]); - args2 = Double.parseDouble(args[2]); - } catch (NumberFormatException nfe){ - sender.sendMessage(ChatColor.RED + "Input arguments must be valid numbers."); - } catch (Exception e){ - sender.sendMessage(ChatColor.RED + "Invalid input arguments."); - } - // Check if any of the args were 0 (this will cause future problems, so we prevent it here) - if (!((args0 == 0) || (args1 == 0) || (args2 == 0))) { - Constants.getPlugin().getConfig().set("winnerTeleport.x", args0); - Constants.getPlugin().getConfig().set("winnerTeleport.y", args1); - Constants.getPlugin().getConfig().set("winnerTeleport.z", args2); - Constants.getPlugin().saveConfig(); - sender.sendMessage(ChatColor.GREEN + "Win location successfully set!"); - sender.sendMessage(ChatColor.GREEN + "Run " + ChatColor.GRAY + "/tumble:reload " + ChatColor.GREEN + "the changes to take effect."); - } - else { - sender.sendMessage(ChatColor.RED + "Your coordinates cannot be zero!"); - sender.sendMessage(ChatColor.RED + "Use something like 0.5 (the middle of the block) instead."); - } - } - // If the sender entered no args, use their current location - else if (args.length == 0) { - Location senderPos = ((Player) sender).getLocation(); - // if so, check if any of their locations are zero - if (!((senderPos.getX() == 0) || (senderPos.getY() == 0) || (senderPos.getZ() == 0))) { - // set the config values to their current pos - Constants.getPlugin().getConfig().set("winnerTeleport.x", senderPos.getX()); - Constants.getPlugin().getConfig().set("winnerTeleport.y", senderPos.getY()); - Constants.getPlugin().getConfig().set("winnerTeleport.z", senderPos.getZ()); - Constants.getPlugin().saveConfig(); - sender.sendMessage(ChatColor.GREEN + "Win location successfully set!"); - sender.sendMessage(ChatColor.GREEN + "Run " + ChatColor.GRAY + "/tumble:reload " + ChatColor.GREEN + "the changes to take effect."); - } - else { - sender.sendMessage(ChatColor.RED + "Your coordinates cannot be zero!"); - sender.sendMessage(ChatColor.RED + "Use something like 0.5 (the middle of the block) instead."); - } - } - else { - return false; - } - } - else if (sender instanceof ConsoleCommandSender) { - if (args.length == 3) { - double args0 = 0; - double args1 = 0; - double args2 = 0; - try { - args0 = Double.parseDouble(args[0]); - args1 = Double.parseDouble(args[1]); - args2 = Double.parseDouble(args[2]); - } catch (NumberFormatException nfe){ - sender.sendMessage(ChatColor.RED + "Input arguments must be valid numbers."); - } catch (Exception e){ - sender.sendMessage(ChatColor.RED + "Invalid input arguments."); - } - if (!((args0 == 0) || (args1 == 0) || (args2 == 0))) { - Constants.getPlugin().getConfig().set("winnerTeleport.x", args0); - Constants.getPlugin().getConfig().set("winnerTeleport.y", args1); - Constants.getPlugin().getConfig().set("winnerTeleport.z", args2); - Constants.getPlugin().saveConfig(); - sender.sendMessage(ChatColor.GREEN + "Win location successfully set!"); - sender.sendMessage(ChatColor.GREEN + "Run " + ChatColor.GRAY + "/tumble:reload " + ChatColor.GREEN + "the changes to take effect."); - } - else { - sender.sendMessage(ChatColor.RED + "Your coordinates cannot be zero!"); - sender.sendMessage(ChatColor.RED + "Use something like 0.5 (the middle of the block) instead."); - } - } - else { - return false; - } - } - } - else { - sender.sendMessage(ChatColor.RED + "Please link a lobby world first!"); - } - } - else { - sender.sendMessage(ChatColor.RED + Constants.getPermissionMessage()); - } - return true; - } -} diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetWorldConfig.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetWorldConfig.java deleted file mode 100644 index 90e0a96..0000000 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetWorldConfig.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.MylesAndMore.Tumble.commands; - -import com.MylesAndMore.Tumble.plugin.Constants; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.GameRule; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.jetbrains.annotations.NotNull; - -import java.util.Objects; - -public class SetWorldConfig implements CommandExecutor { - @Override - public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { - // Catch for null arguments - if (args.length == 2) { - if (sender.hasPermission("tumble.link")){ - // Initialize vars for their respective command arguments - String world = args[0]; - String worldType = args[1]; - if (Objects.equals(worldType, "lobby")) { - // Check if the world is actually a world on the server - if (Bukkit.getWorld(world) != null) { - // Check if the world has already been configured - if (!Objects.equals(Constants.getGameWorld(), world)) { - // Set the specified value of the world in the config under lobbyWorld - Constants.getPlugin().getConfig().set("lobbyWorld", world); - Constants.getPlugin().saveConfig(); - sender.sendMessage(ChatColor.GREEN + "Lobby world successfully linked: " + ChatColor.GRAY + world); - sender.sendMessage(ChatColor.GREEN + "Please restart your server for the changes to take effect; " + ChatColor.RED + "reloading the plugin is insufficient!"); - } - else { - sender.sendMessage(ChatColor.RED + "That world has already been linked, please choose/create another world!"); - } - } - else { - sender.sendMessage(ChatColor.RED + "Failed to find a world named " + ChatColor.GRAY + world); - } - } - else if (Objects.equals(args[1], "game")) { - if (Bukkit.getWorld(world) != null) { - if (!Objects.equals(Constants.getLobbyWorld(), world)) { - Constants.getPlugin().getConfig().set("gameWorld", world); - Constants.getPlugin().saveConfig(); - // Set the gamerule of doImmediateRespawn in the gameWorld for later - Objects.requireNonNull(Bukkit.getWorld(world)).setGameRule(GameRule.DO_IMMEDIATE_RESPAWN, true); - Objects.requireNonNull(Bukkit.getWorld(world)).setGameRule(GameRule.KEEP_INVENTORY, true); - sender.sendMessage(ChatColor.GREEN + "Game world successfully linked: " + ChatColor.GRAY + world); - sender.sendMessage(ChatColor.GREEN + "Please restart your server for the changes to take effect; " + ChatColor.RED + "reloading the plugin is insufficient!"); - } - else { - sender.sendMessage(ChatColor.RED + "That world has already been linked, please choose/create another world!"); - } - } - else { - sender.sendMessage(ChatColor.RED + "Failed to find a world named " + ChatColor.GRAY + world); - } - } - else { - sender.sendMessage(ChatColor.RED + "Allowed world types are " + ChatColor.GRAY + "lobby " + ChatColor.RED + "and " + ChatColor.GRAY + "game" + ChatColor.RED + "."); - } - } - else { - sender.sendMessage(ChatColor.RED + Constants.getPermissionMessage()); - } - } - else { - return false; - } - return true; - } -} diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/StartGame.java b/src/main/java/com/MylesAndMore/Tumble/commands/StartGame.java deleted file mode 100644 index 706b33a..0000000 --- a/src/main/java/com/MylesAndMore/Tumble/commands/StartGame.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.MylesAndMore.Tumble.commands; - -import com.MylesAndMore.Tumble.game.Game; -import com.MylesAndMore.Tumble.plugin.Constants; -import org.bukkit.ChatColor; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.jetbrains.annotations.NotNull; - -import java.util.Objects; - -public class StartGame implements CommandExecutor { - @Override - public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { - if (sender.hasPermission("tumble.start")) { - if (Constants.getLobbyWorld() != null) { - if (Constants.getPlayersInLobby().size() > 1) { - if (Constants.getGameWorld() != null) { - if (!Objects.equals(Game.getGame().getGameState(), "waiting")) { - sender.sendMessage(ChatColor.BLUE + "Generating layers, please wait."); - // Use multiverse to load game world--if the load was successful, start game - if (Constants.getMVWorldManager().loadWorld(Constants.getGameWorld())) { - // If there is no starting argument, - if (args.length == 0) { - // pull which gamemode to initiate from the config file - if (!Game.getGame().startGame(Constants.getGameType())) { - // Sender feedback for if the game failed to start - if (Objects.equals(Game.getGame().getGameState(), "starting")) { - sender.sendMessage(ChatColor.RED + "A game is already starting!"); - } - else if (Objects.equals(Game.getGame().getGameState(), "running")) { - sender.sendMessage(ChatColor.RED + "A game is already running!"); - } - else { - sender.sendMessage(ChatColor.RED + "Failed to recognize game of type " + ChatColor.GRAY + Constants.getPlugin().getConfig().getString("gameMode")); - } - } - } - // If there was an argument for gameType, pass that instead - else { - if (!Game.getGame().startGame(args[0])) { - // Sender feedback for if the game failed to start - if (Objects.equals(Game.getGame().getGameState(), "starting")) { - sender.sendMessage(ChatColor.RED + "A game is already starting!"); - } - else if (Objects.equals(Game.getGame().getGameState(), "running")) { - sender.sendMessage(ChatColor.RED + "A game is already running!"); - } - else { - sender.sendMessage(ChatColor.RED + "Failed to recognize game of type " + ChatColor.GRAY + args[0]); - } - } - } - } - // If load was unsuccessful, give feedback - // Note: this should not occur unless the config file was edited externally, - // because the plugin prevents adding "worlds" that are not actually present to the config. - else { - sender.sendMessage(ChatColor.RED + "Failed to find a world named " + ChatColor.GRAY + Constants.getGameWorld()); - sender.sendMessage(ChatColor.RED + "Is the configuration file correct?"); - } - } - else { - sender.sendMessage(ChatColor.RED + "A game is already queued to begin!"); - } - } - else { - sender.sendMessage(ChatColor.RED + "Please link a game world first!"); - } - } - else { - sender.sendMessage(ChatColor.RED + "You can't start a game with yourself!"); - } - } - else { - sender.sendMessage(ChatColor.RED + "Please link a lobby world first!"); - } - } - else { - sender.sendMessage(ChatColor.RED + Constants.getPermissionMessage()); - } - return true; - } -} diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Arena.java b/src/main/java/com/MylesAndMore/Tumble/game/Arena.java new file mode 100644 index 0000000..ef477d2 --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/game/Arena.java @@ -0,0 +1,26 @@ +package com.MylesAndMore.Tumble.game; + +import org.bukkit.Location; +import org.bukkit.World; +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 final Location location; + public final String name; + + /** + * Creates a new Arena + * @param name Name of the arena + * @param location Center point / spawn point. + */ + public Arena(@NotNull String name, @NotNull Location location) { + this.location = location; + this.world = location.getWorld(); + this.name = name; + } +} diff --git a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java new file mode 100644 index 0000000..16698f4 --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java @@ -0,0 +1,204 @@ +package com.MylesAndMore.Tumble.game; + +import java.util.Objects; + +import com.MylesAndMore.Tumble.plugin.ConfigManager; +import com.MylesAndMore.Tumble.plugin.GameState; +import org.bukkit.*; +import org.bukkit.entity.Player; +import org.bukkit.entity.Snowball; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockDropItemEvent; +import org.bukkit.event.entity.*; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.player.*; +import org.bukkit.util.Vector; + +import org.bukkit.inventory.ItemStack; + +import static com.MylesAndMore.Tumble.Main.plugin; + +/** + * Tumble event listener for all plugin and game-related events. + */ +public class EventListener implements Listener { + + World gameWorld; + Game game; + public EventListener(Game game) { + this.game = game; + this.gameWorld = game.gameWorld; + } + + @EventHandler + public void PlayerJoinEvent(PlayerJoinEvent event) { + // Hide/show join message accordingly + if (ConfigManager.HideLeaveJoin) { + event.setJoinMessage(null); + } + 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)); + } + } + + @EventHandler + public void PlayerQuitEvent(PlayerQuitEvent event) { + // Hide/show leave message accordingly + if (ConfigManager.HideLeaveJoin) { + event.setQuitMessage(null); + } + if (event.getPlayer().getWorld() == gameWorld) { + game.removePlayer(event.getPlayer()); + } + } + + @EventHandler + public void PlayerDeathEvent(PlayerDeathEvent event) { + if (event.getEntity().getWorld() == gameWorld) { + game.playerDeath(event.getEntity()); + } + } + + @EventHandler + public void PlayerItemDamageEvent(PlayerItemDamageEvent event) { + // Remove item damage within games + if (event.getPlayer().getWorld() == gameWorld) { + event.setCancelled(true); + } + } + + @EventHandler + public void ProjectileLaunchEvent(ProjectileLaunchEvent event) { + if (event.getEntity().getWorld() == gameWorld + && event.getEntity() instanceof Snowball + && event.getEntity().getShooter() instanceof Player player) { + + // Prevent projectiles (snowballs) from being thrown before the game starts + if (Objects.equals(game.gameState, GameState.STARTING)) { + event.setCancelled(true); + } + else { + // Give players a snowball when they've used one (infinite snowballs) + Bukkit.getServer().getScheduler().runTask(plugin, () -> player.getInventory().addItem(new ItemStack(Material.SNOWBALL, 1))); + } + } + } + + @EventHandler + public void ProjectileHitEvent(ProjectileHitEvent event) { + if (event.getHitBlock() == null) { return; } + // Removes blocks that snowballs thrown by players have hit in the game world + if (event.getHitBlock().getWorld() == gameWorld) { + 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) { + p.playEffect( + event.getHitBlock().getLocation(), + Effect.STEP_SOUND, + event.getHitBlock().getType()); + event.getHitBlock().setType(Material.AIR); + } + } + else if (event.getHitEntity() != null) { + if (event.getHitEntity() instanceof Player hitPlayer) { + // Also cancel any knockback + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> hitPlayer.setVelocity(new Vector())); + } + } + } + } + } + } + + @EventHandler + public void PlayerDropItemEvent(PlayerDropItemEvent event) { + // Don't allow items to drop in the game world + if (event.getPlayer().getWorld() == gameWorld) { + event.setCancelled(true); + } + } + + @EventHandler + public void PlayerMoveEvent(PlayerMoveEvent event) { + // Cancel movement if the game is starting (so players can't move before the game starts) + if (Objects.equals(game.gameState, GameState.STARTING) + && event.getPlayer().getWorld().equals(gameWorld)) { + event.setCancelled(true); + } + } + + @EventHandler + public void BlockDropItemEvent(BlockDropItemEvent event) { + // If a block was going to drop an item (ex. snow dropping snowballs) in the game world, cancel it + if (event.getBlock().getWorld() == gameWorld) { + event.setCancelled(true); + } + } + + @EventHandler + public void PlayerInteractEvent(PlayerInteractEvent event) { + // Remove blocks when clicked in the game world (all gamemodes require this functionality) + if (event.getAction() == Action.LEFT_CLICK_BLOCK + && Objects.requireNonNull(event.getClickedBlock()).getWorld() == gameWorld) { + event.getPlayer().playEffect( + event.getClickedBlock().getLocation(), + Effect.STEP_SOUND, + event.getClickedBlock().getType() + ); + event.getClickedBlock().setType(Material.AIR); + } + } + + @EventHandler + public void BlockBreakEvent(BlockBreakEvent event) { + // This just doesn't allow blocks to break in the gameWorld; the PlayerInteractEvent will take care of everything + // This prevents any weird client-server desync + if (event.getBlock().getWorld() == gameWorld) { + event.setCancelled(true); + } + } + + @EventHandler + public void FoodLevelChangeEvent(FoodLevelChangeEvent event) { + // INFINITE FOOD (YAY!!!!) + if (event.getEntity().getWorld() == gameWorld) { + event.setCancelled(true); + } + } + + @EventHandler + public void EntityDamageEvent(EntityDamageEvent event) { + // Check to see if a player got damaged by another entity (player, snowball, etc) in the gameWorld, if so, cancel it + if (event.getEntity().getWorld() == gameWorld) { + if (event.getEntity() instanceof Player) { + if (event.getCause() == EntityDamageEvent.DamageCause.ENTITY_ATTACK + && event.getCause() == EntityDamageEvent.DamageCause.ENTITY_SWEEP_ATTACK + && event.getCause() == EntityDamageEvent.DamageCause.FALL) { + event.setCancelled(true); + } + } + } + } + + @EventHandler + public void InventoryDragEvent(InventoryDragEvent event) { + if (event.getWhoClicked().getWorld() == gameWorld) { + event.setCancelled(true); + } + } + + @EventHandler + public void PlayerRespwanEvent(PlayerRespawnEvent event) { + // Make sure players respawn in the correct location + if (game.gamePlayers.contains(event.getPlayer())) { + event.setRespawnLocation(game.arena.location); + } + } + + // TODO: stop tile drops for pistons +} diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index cdaede9..8fa9289 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -1,113 +1,222 @@ package com.MylesAndMore.Tumble.game; -import com.MylesAndMore.Tumble.plugin.Constants; - +import com.MylesAndMore.Tumble.plugin.ConfigManager; +import com.MylesAndMore.Tumble.plugin.GameState; +import com.MylesAndMore.Tumble.plugin.GameType; import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.*; import org.bukkit.enchantments.Enchantment; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Item; import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; +import static com.MylesAndMore.Tumble.Main.plugin; + /** * Everything relating to the Tumble game */ public class Game { - // Singleton class logic - private static Game gameInstance; - private Game() { - gameWorld = Bukkit.getWorld(Constants.getGameWorld()); - gameSpawn = Objects.requireNonNull(gameWorld).getSpawnLocation(); - } - public static Game getGame() { - if (gameInstance == null) { - gameInstance = new Game(); - } - return gameInstance; - } - // Define local game vars - private String gameState; - private String gameType; + public final GameType type; + public final Arena arena; + public final World gameWorld; + public final List gamePlayers = new ArrayList<>(); + private final Location gameSpawn; + private final HashMap gameWins = new HashMap<>(); + public GameState gameState = GameState.WAITING; + public GameType roundType; private int gameID = -1; private int autoStartID = -1; - private final World gameWorld; - private final Location gameSpawn; - private List gamePlayers; - private List roundPlayers; - private List gameWins; + private List playersAlive; + private EventListener eventListener; + + public Game(@NotNull Arena arena, @NotNull GameType type) { + this.arena = arena; + this.type = type; + this.gameWorld = arena.world; + this.gameSpawn = arena.location; - private final Random Random = new Random(); + } /** * Creates a new Game - * @param type The type of game - * @return true if the game succeeds creation, and false if not */ - public boolean startGame(@NotNull String type) { + public void startGame() { + // Check if the game is starting or running - if (Objects.equals(gameState, "starting")) { return false; } - else if (Objects.equals(gameState, "running")) { return false; } + if (gameState != GameState.WAITING) { + return; + } + + Bukkit.getServer().getScheduler().cancelTask(autoStartID); + autoStartID = -1; +// if (waitingPlayers.size() < 2) { +// return false; +// } + + eventListener = new EventListener(this); + Bukkit.getServer().getPluginManager().registerEvents(eventListener, plugin); + + // clear area in case it did not get properly cleared + roundStart(); + } + + /** + * Starts a new round + */ + private void roundStart() { + gameState = GameState.STARTING; + playersAlive = new ArrayList<>(gamePlayers); + // Put all players in spectator to prevent them from getting kicked for flying + setGamemode(gamePlayers, GameMode.SPECTATOR); + scatterPlayers(gamePlayers); + clearInventories(gamePlayers); + clearArena(); + prepareGameType(type); + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { + // Begin the countdown sequence + countdown(() -> { + setGamemode(gamePlayers, GameMode.SURVIVAL); + gameState = GameState.RUNNING; + }); + }, 100); + } + + /** + * Type specific setup: Generating layers and giving items + * @param type can be either "shovels", "snowballs", or "mixed" + */ + private void prepareGameType(GameType type) { + roundType = type; // note: may need deepcopy this for it to work properly + if (roundType.equals(GameType.MIXED)) { + // Randomly select either shovels or snowballs and re-run the method + Random random = new Random(); + switch (random.nextInt(2)) { + case 0 -> roundType = GameType.SHOVELS; + case 1 -> roundType = GameType.SNOWBALLS; + } + } + + switch (roundType) { + case SHOVELS -> { + Generator.generateLayersShovels(gameSpawn.clone()); + ItemStack shovel = new ItemStack(Material.IRON_SHOVEL); + shovel.addEnchantment(Enchantment.SILK_TOUCH, 1); + giveItems(gamePlayers, shovel); + // Schedule a process to give snowballs after 2m30s (so people can't island, the OG game had this); add 160t because of the countdown + gameID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { + clearInventories(gamePlayers); + giveItems(gamePlayers, new ItemStack(Material.SNOWBALL)); + displayActionbar(gamePlayers, ChatColor.DARK_RED + "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); + }, 3160); + } + case SNOWBALLS -> { + Generator.generateLayersSnowballs(gameSpawn.clone()); + giveItems(gamePlayers, new ItemStack(Material.SNOWBALL)); + + // End the round in 5m + gameID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, this::roundEnd, 6160); + } + } + } + + /** + * Round end stuff: Finds and displays winner, starts next round if necessary + */ + private void roundEnd() { + // Cancel the tasks that auto-end the round + Bukkit.getServer().getScheduler().cancelTask(gameID); + // Clear old layers (as a fill command, this would be /fill ~-20 ~-20 ~-20 ~20 ~ ~20 relative to spawn) + playSound(gamePlayers, Sound.BLOCK_NOTE_BLOCK_PLING, SoundCategory.BLOCKS, 5, 0); + // Check if there was a definite winner or not + if (!playersAlive.isEmpty()) { + Player winner = playersAlive.get(0); + // Set the wins of the player to their current # of wins + 1 + if (!gameWins.containsKey(winner)) { + gameWins.put(winner, 0); + } + gameWins.put(winner, gameWins.get(winner)+1); + if (gameWins.get(winner) == 3) { + gameEnd(); + } + // 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); + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, this::roundStart, 100); + } + } else { - // Define the gameType - switch (type) { - case "shovels", "snowballs", "mixed" -> { - gameState = "starting"; - // Set the type to gameType since it won't change for this mode - gameType = type; - // Clear the players' inventories so they can't bring any items into the game - clearInventories(Constants.getPlayersInLobby()); - // Generate the correct layers for a Shovels game - // The else statement is just in case the generator fails; this command will fail - if (generateLayers(type)) { - // Send all players from lobby to the game - scatterPlayers(Constants.getPlayersInLobby()); - } else { - return false; - } + displayTitles(gamePlayers, ChatColor.RED + "Round over!", ChatColor.GOLD + "Draw!", 5, 60, 5); + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, this::roundStart, 100); + } + } + + /** + * Game end stuff: Displays overall winner and teleports players to lobby + */ + private void gameEnd() { + if (!gamePlayers.isEmpty()) { + Player winner = getPlayerWithMostWins(gameWins); + setGamemode(gamePlayers, GameMode.SPECTATOR); + clearInventories(gamePlayers); + if (winner != null) { + displayTitles(gamePlayers, ChatColor.RED + "Game over!", ChatColor.GOLD + winner.getName() + " has won the game!", 5, 60, 5); + } + displayActionbar(gamePlayers, ChatColor.BLUE + "Returning to lobby in ten seconds..."); + // Wait 10s (200t), then + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { + // First, check to see if there is a separate location to tp the winner to + if (ConfigManager.winnerLobby != null && winner != null) { + winner.teleport(ConfigManager.winnerLobby); + // Remove the winner from the game so they don't get double-tp'd + gamePlayers.remove(winner); } - default -> { - // The game type in the config did not match a specified game type - return false; + // Send all players back to lobby (spawn) + for (Player aPlayer : gamePlayers) { + aPlayer.teleport(Objects.requireNonNull(ConfigManager.lobby)); } - } - // Update the game/round players for later - gamePlayers = new ArrayList<>(Constants.getPlayersInGame()); - roundPlayers = new ArrayList<>(Constants.getPlayersInGame()); - // Create a list that will later keep track of each player's wins - gameWins = new ArrayList<>(); - gameWins.addAll(List.of(0,0,0,0,0,0,0,0)); - // Put all players in spectator to prevent them from getting kicked for flying (this needs a delay bc servers are slow) - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> setGamemode(gamePlayers, GameMode.SPECTATOR), 25); - // Wait 5s (100t) for the clients to load in - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> { - // Begin the countdown sequence - playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1); - displayTitles(gamePlayers, ChatColor.DARK_GREEN + "3", null, 3, 10, 7); - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> { - playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1); - displayTitles(gamePlayers, ChatColor.YELLOW + "2", null, 3, 10, 7); - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> { - playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1); - displayTitles(gamePlayers, ChatColor.DARK_RED + "1", null, 3, 10, 7); - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> { - playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 2); - displayTitles(gamePlayers, ChatColor.GREEN + "Go!", null, 1, 5, 1); - setGamemode(gamePlayers, GameMode.SURVIVAL); - gameState = "running"; - }, 20); - }, 20); - }, 20); - }, 100); + }, 200); + } + HandlerList.unregisterAll(eventListener); + arena.game = null; + } + + /** + * Force stops a game + */ + public void killGame() { + Bukkit.getServer().getScheduler().cancelTask(gameID); + HandlerList.unregisterAll(eventListener); + arena.game = null; + } + + /** + * Removes a player from the game + * Called when a player leaves the server, or if they issue the leave command + * @param p Player to remove + */ + public void removePlayer(Player p) { + gamePlayers.remove(p); + if (gamePlayers.size() < 2) { + gameEnd(); + } + p.teleport(ConfigManager.lobby); + } + + public void addPlayer(Player p) { + gamePlayers.add(p); + if (gamePlayers.size() >= 2 && gameState == GameState.WAITING) { + autoStart(); } - return true; } /** @@ -115,217 +224,92 @@ public boolean startGame(@NotNull String type) { */ public void autoStart() { // Wait for the player to load in - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> { - gameState = "waiting"; - displayActionbar(Constants.getPlayersInLobby(), ChatColor.GREEN + "Game will begin in 15 seconds!"); - playSound(Constants.getPlayersInLobby(), Sound.BLOCK_NOTE_BLOCK_CHIME, SoundCategory.BLOCKS, 1, 1); - Constants.getMVWorldManager().loadWorld(Constants.getGameWorld()); + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { + displayActionbar(gamePlayers, ChatColor.GREEN + "Game will begin in 15 seconds!"); + 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(Constants.getPlugin(), () -> startGame(Constants.getGameType()), 300); + autoStartID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, this::startGame, 300); }, 50); } - /** - * Cancels a "waiting" automatic start - */ - public void cancelStart() { - Bukkit.getServer().getScheduler().cancelTask(Game.getGame().getAutoStartID()); - displayActionbar(Constants.getPlayersInLobby(), ChatColor.RED + "Game start cancelled!"); - playSound(Constants.getPlayersInLobby(), Sound.BLOCK_NOTE_BLOCK_BASS, SoundCategory.BLOCKS, 1, 1); - gameState = null; - autoStartID = -1; - } - /** * This method should be called on the death of one of the Game's players * @param player The player who died */ public void playerDeath(Player player) { player.setGameMode(GameMode.SPECTATOR); - // Add a delay to tp them to the gameWorld just in case they have a bed in another world (yes you Jacob) - // Delay is needed because instant respawn is a lie (it's not actually instant) - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> { - player.teleport(gameSpawn); - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> player.setGameMode(GameMode.SPECTATOR), 5); - }, 5); // remove that player (who just died) from the roundPlayersArray, effectively eliminating them, - roundPlayers.remove(player); + playersAlive.remove(player); // If there are less than 2 players in the game (1 just died), - if (roundPlayers.size() < 2) { - roundEnd(roundPlayers.get(0)); + if (playersAlive.size() < 2) { + roundEnd(); } } - // Methods to get the game type and game state for other classes outside the Game + // utility functions /** - * @return The game's current state as a String ("waiting", "starting", "running", "complete") - * Can also be null if not initialized. + * Teleports a list of players to the specified scatter locations in the gameWorld + * @param players a List of Players to teleport */ - public String getGameState() { return gameState; } + private void scatterPlayers(List players) { + double x = gameSpawn.getX(); + double y = gameSpawn.getY(); + double z = gameSpawn.getZ(); + // Create the scatter locations based off the game's spawn + List scatterLocations = new ArrayList<>(List.of( + new Location(gameWorld, (x - 14.5), y, (z + 0.5), -90, 0), + new Location(gameWorld, (x + 0.5), y, (z - 14.5), 0, 0), + new Location(gameWorld, (x + 15.5), y, (z + 0.5), 90, 0), + new Location(gameWorld, (x + 0.5), y, (z + 15.5), 180, 0), + new Location(gameWorld, (x - 10.5), y, (z - 10.5), -45, 0), + new Location(gameWorld, (x - 10.5), y, (z + 11.5), -135, 0), + new Location(gameWorld, (x + 11.5), y, (z - 10.5), 45, 0), + new Location(gameWorld, (x + 11.5), y, (z + 11.5), 135, 0))); + Collections.shuffle(scatterLocations); + for (Player aPlayer : players) { + if (!aPlayer.teleport(scatterLocations.get(0))) { + plugin.getLogger().info("dbg: FAILED TELEPORT"); + } + scatterLocations.remove(0); // Remove that location so multiple players won't get the same one + } + } /** - * @return The Bukkit process ID of the autostart process, if applicable - * Can also be null if not initialized, or -1 if the process failed to schedule. + * Displays the 3, 2, 1 countdown + * @param doAfter Will be executed after the countdown */ - public int getAutoStartID() { return autoStartID; } - + 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); + 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); + 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); + 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); + doAfter.run(); + }, 20); + }, 20); + }, 20); + } - private final Layers layers = new Layers(); /** - * Generates the layers in the gameWorld for a certain gameType - * @param type can be either "shovels", "snowballs", or "mixed", anything else will fail generation - * @return true if gameType was recognized and layers were (hopefully) generated, false if unrecognized + * Finds the player with the most wins + * @param list List of players and their number of wins + * @return Player with the most wins */ - private boolean generateLayers(String type) { - // Create a new Location for the layers to work with--this is so that we don't modify the actual gameSpawn var - Location layer = new Location(gameSpawn.getWorld(), gameSpawn.getX(), gameSpawn.getY(), gameSpawn.getZ(), gameSpawn.getYaw(), gameSpawn.getPitch()); - if (Objects.equals(type, "shovels")) { - layer.setY(layer.getY() - 1); - // Choose a random type of generation; a circular layer, a square layer, or a multi-tiered layer of either variety - if (Random.nextInt(4) == 0) { - // Circular layer - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.SNOW_BLOCK), layers.getSafeMaterialList()); - } - else if (Random.nextInt(4) == 1) { - // Square layer - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.SNOW_BLOCK), layers.getSafeMaterialList()); + private Player getPlayerWithMostWins(HashMap list) { + Player largest = null; + for (Player p: list.keySet()) { + if (largest == null || list.get(p) > list.get(largest)) { + largest = p; } - else if (Random.nextInt(4) == 2) { - // Multi-tiered circle - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.SNOW_BLOCK), layers.getSafeMaterialList()); - Generator.generateLayer(layer, 13, 1, Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRASS_BLOCK), layers.getMaterialList()); - Generator.generateLayer(layer, 4, 1, Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.PODZOL), layers.getMaterialList()); - } - else { - // Multi-tiered square - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.SNOW_BLOCK), layers.getSafeMaterialList()); - Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRASS_BLOCK), layers.getMaterialList()); - Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.PODZOL), layers.getMaterialList()); - } - ItemStack shovel = new ItemStack(Material.IRON_SHOVEL); - shovel.addEnchantment(Enchantment.SILK_TOUCH, 1); - if (Objects.equals(gameState, "running")) { - giveItems(Constants.getPlayersInGame(), shovel); - } - else if (Objects.equals(gameState, "starting")) { - giveItems(Constants.getPlayersInLobby(), shovel); - } - // Schedule a process to give snowballs after 2m30s (so people can't island, the OG game had this); add 160t because of the countdown - gameID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> { - clearInventories(gamePlayers); - giveItems(gamePlayers, new ItemStack(Material.SNOWBALL)); - displayActionbar(gamePlayers, ChatColor.DARK_RED + "Showdown!"); - playSound(gamePlayers, Sound.ENTITY_ELDER_GUARDIAN_CURSE, SoundCategory.HOSTILE, 1, 1); - // End the round in another 2m30s - gameID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> roundEnd(null), 3000); - }, 3160); } - else if (Objects.equals(type, "snowballs")) { - layer.setY(layer.getY() - 1); - // Similar generation to shovels, except there are three layers - if (Random.nextInt(4) == 0) { - // Circular layer - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getSafeMaterialList()); - layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getMaterialList()); - layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getMaterialList()); - } - else if (Random.nextInt(4) == 1) { - // Square layer - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getSafeMaterialList()); - layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getMaterialList()); - layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getMaterialList()); - } - else if (Random.nextInt(4) == 2) { - // Multi-tiered circle - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getSafeMaterialList()); - Generator.generateLayer(layer, 13, 1, Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRANITE), layers.getMaterialList()); - Generator.generateLayer(layer, 4, 1, Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); - layer.setY(layer.getY() - 6); - - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getSafeMaterialList()); - Generator.generateLayer(layer, 13, 1, Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRANITE), layers.getMaterialList()); - Generator.generateLayer(layer, 4, 1, Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); - layer.setY(layer.getY() - 6); - - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getSafeMaterialList()); - Generator.generateLayer(layer, 13, 1, Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRANITE), layers.getMaterialList()); - Generator.generateLayer(layer, 4, 1, Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); - } - else { - // Multi-tiered square - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getSafeMaterialList()); - Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRANITE), layers.getMaterialList()); - Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); - layer.setY(layer.getY() - 6); - - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getSafeMaterialList()); - Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRANITE), layers.getMaterialList()); - Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); - layer.setY(layer.getY() - 6); - - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getSafeMaterialList()); - Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRANITE), layers.getMaterialList()); - Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); - } - if (Objects.equals(gameState, "running")) { - giveItems(Constants.getPlayersInGame(), new ItemStack(Material.SNOWBALL)); - } - else if (Objects.equals(gameState, "starting")) { - giveItems(Constants.getPlayersInLobby(), new ItemStack(Material.SNOWBALL)); - } - // End the round in 5m - gameID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> roundEnd(null), 6160); - } - else if (Objects.equals(type, "mixed")) { - // Randomly select either shovels or snowballs and re-run the method - if (Random.nextInt(2) == 0) { - generateLayers("shovels"); - } else { - generateLayers("snowballs"); - } - } - // Game type was invalid - else { - return false; - } - return true; + return largest; } /** @@ -401,139 +385,13 @@ private void playSound(@NotNull List players, @NotNull Sound sound, @Not } /** - * Teleports a list of players to the specified scatter locations in the gameWorld - * @param players a List of Players to teleport + * Clears old layers + * (as a fill command, this would be /fill ~-20 ~-20 ~-20 ~20 ~ ~20 relative to spawn) */ - private void scatterPlayers(List players) { - double x = gameSpawn.getX(); - double y = gameSpawn.getY(); - double z = gameSpawn.getZ(); - // Create the scatter locations based off the game's spawn - List scatterLocations = new ArrayList<>(List.of( - new Location(gameWorld, (x - 14.5), y, (z + 0.5), -90, 0), - new Location(gameWorld, (x + 0.5), y, (z - 14.5), 0, 0), - new Location(gameWorld, (x + 15.5), y, (z + 0.5), 90, 0), - new Location(gameWorld, (x + 0.5), y, (z + 15.5), 180, 0), - new Location(gameWorld, (x - 10.5), y, (z - 10.5), -45, 0), - new Location(gameWorld, (x - 10.5), y, (z + 11.5), -135, 0), - new Location(gameWorld, (x + 11.5), y, (z - 10.5), 45, 0), - new Location(gameWorld, (x + 11.5), y, (z + 11.5), 135, 0))); - Collections.shuffle(scatterLocations); - for (Player aPlayer : players) { - aPlayer.teleport(scatterLocations.get(0)); - scatterLocations.remove(0); // Remove that location so multiple players won't get the same one - } - } - - private void roundEnd(@Nullable Player winner) { - // Cancel the tasks that auto-end the round - Bukkit.getServer().getScheduler().cancelTask(gameID); - // Clear old layers (as a fill command, this would be /fill ~-20 ~-20 ~-20 ~20 ~ ~20 relative to spawn) - Generator.generateCuboid(new Location(gameSpawn.getWorld(), gameSpawn.getX() - 20, gameSpawn.getY() - 20, gameSpawn.getZ() - 20), new Location(gameSpawn.getWorld(), gameSpawn.getX() + 20, gameSpawn.getY(), gameSpawn.getZ() + 20), Material.AIR); - playSound(gamePlayers, Sound.BLOCK_NOTE_BLOCK_PLING, SoundCategory.BLOCKS, 5, 0); - // Check if there was a definite winner or not - if (winner != null) { - // Set the wins of the player to their current # of wins + 1 - gameWins.set(gamePlayers.indexOf(winner), (gameWins.get(gamePlayers.indexOf(winner)) + 1)); - // If the player has three wins, they won the game, so initiate the gameEnd - if (gameWins.get(gamePlayers.indexOf(winner)) == 3) { - gameEnd(winner); - } - // If that player doesn't have three wins, nobody else does, so we need another round - else { - roundPlayers.get(0).setGameMode(GameMode.SPECTATOR); - roundPlayers.remove(0); - roundPlayers.addAll(gamePlayers); - clearInventories(gamePlayers); - displayTitles(gamePlayers, ChatColor.RED + "Round over!", ChatColor.GOLD + winner.getName() + " has won the round!", 5, 60, 5); - // Wait for the player to respawn before completely lagging the server ._. - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> { - generateLayers(gameType); - // Wait 5s (100t) for tp method - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> { - // Kill all items (pistons are weird) - for (Entity entity : gameWorld.getEntities()) { - if (entity instanceof Item) { - entity.remove(); - } - } - gameState = "starting"; - scatterPlayers(gamePlayers); - playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1); - displayTitles(gamePlayers, ChatColor.DARK_GREEN + "3", null, 3, 10, 7); - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> { - playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1); - displayTitles(gamePlayers, ChatColor.YELLOW + "2", null, 3, 10, 7); - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> { - playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1); - displayTitles(gamePlayers, ChatColor.DARK_RED + "1", null, 3, 10, 7); - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> { - playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 2); - displayTitles(gamePlayers, ChatColor.GREEN + "Go!", null, 1, 5, 1); - setGamemode(gamePlayers, GameMode.SURVIVAL); - gameState = "running"; - }, 20); - }, 20); - }, 20); - }, 100); - }, 1); - } - } - else { - setGamemode(gamePlayers, GameMode.SPECTATOR); - roundPlayers.clear(); - roundPlayers.addAll(gamePlayers); - clearInventories(gamePlayers); - displayTitles(gamePlayers, ChatColor.RED + "Round over!", ChatColor.GOLD + "Draw!", 5, 60, 5); - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> { - generateLayers(gameType); - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> { - for (Entity entity : gameWorld.getEntities()) { - if (entity instanceof Item) { - entity.remove(); - } - } - gameState = "starting"; - scatterPlayers(gamePlayers); - playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1); - displayTitles(gamePlayers, ChatColor.DARK_GREEN + "3", null, 3, 10, 7); - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> { - playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1); - displayTitles(gamePlayers, ChatColor.YELLOW + "2", null, 3, 10, 7); - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> { - playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1); - displayTitles(gamePlayers, ChatColor.DARK_RED + "1", null, 3, 10, 7); - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> { - playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 2); - displayTitles(gamePlayers, ChatColor.GREEN + "Go!", null, 1, 5, 1); - setGamemode(gamePlayers, GameMode.SURVIVAL); - gameState = "running"; - }, 20); - }, 20); - }, 20); - }, 100); - }, 1); - } - } - - private void gameEnd(Player winner) { - winner.setGameMode(GameMode.SPECTATOR); - clearInventories(gamePlayers); - displayTitles(gamePlayers, ChatColor.RED + "Game over!", ChatColor.GOLD + winner.getName() + " has won the game!", 5, 60, 5); - displayActionbar(gamePlayers, ChatColor.BLUE + "Returning to lobby in ten seconds..."); - // Wait 10s (200t), then - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> { - // First, check to see if there is a separate location to tp the winner to - if ((Constants.getPlugin().getConfig().getDouble("winnerTeleport.x") != 0) && (Constants.getPlugin().getConfig().getDouble("winnerTeleport.y") != 0) && (Constants.getPlugin().getConfig().getDouble("winnerTeleport.z") != 0)) { - winner.teleport(new Location(Bukkit.getWorld(Constants.getLobbyWorld()), Constants.getPlugin().getConfig().getDouble("winnerTeleport.x"), Constants.getPlugin().getConfig().getDouble("winnerTeleport.y"), Constants.getPlugin().getConfig().getDouble("winnerTeleport.z"))); - // Remove the winner from the gamePlayers so they don't get double-tp'd - gamePlayers.remove(winner); - } - // Send all players back to lobby (spawn) - for (Player aPlayer : gamePlayers) { - aPlayer.teleport(Objects.requireNonNull(Bukkit.getWorld(Constants.getLobbyWorld())).getSpawnLocation()); - } - }, 200); - gameState = "complete"; + private void clearArena() { + Generator.generateCuboid( + new Location(gameSpawn.getWorld(), gameSpawn.getX() - 20, gameSpawn.getY() - 20, gameSpawn.getZ() - 20), + new Location(gameSpawn.getWorld(), gameSpawn.getX() + 20, gameSpawn.getY(), gameSpawn.getZ() + 20), + Material.AIR); } } diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Generator.java b/src/main/java/com/MylesAndMore/Tumble/game/Generator.java index ecaa1b7..c8ecb06 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Generator.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Generator.java @@ -1,8 +1,6 @@ package com.MylesAndMore.Tumble.game; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; +import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; @@ -12,6 +10,131 @@ * Holds the methods that generate blocks in-game such as cylinders, cuboids, and block clumps. */ public class Generator { + + /** + * Generates layers for a round of type shovels + * @param layer Location where the layers should start + */ + public static void generateLayersShovels(Location layer) { + Random random = new Random(); + Layers layers = new Layers(); + + layer.setY(layer.getY() - 1); + // Choose a random type of generation; a circular layer, a square layer, or a multi-tiered layer of either variety + if (random.nextInt(4) == 0) { + // Circular layer + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.SNOW_BLOCK), layers.getSafeMaterialList()); + } + else if (random.nextInt(4) == 1) { + // Square layer + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.SNOW_BLOCK), layers.getSafeMaterialList()); + } + else if (random.nextInt(4) == 2) { + // Multi-tiered circle + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.SNOW_BLOCK), layers.getSafeMaterialList()); + Generator.generateLayer(layer, 13, 1, Material.AIR); + layer.setY(layer.getY() - 1); + Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRASS_BLOCK), layers.getMaterialList()); + Generator.generateLayer(layer, 4, 1, Material.AIR); + layer.setY(layer.getY() - 1); + Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.PODZOL), layers.getMaterialList()); + } + else { + // Multi-tiered square + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.SNOW_BLOCK), layers.getSafeMaterialList()); + Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR); + layer.setY(layer.getY() - 1); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRASS_BLOCK), layers.getMaterialList()); + Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR); + layer.setY(layer.getY() - 1); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.PODZOL), layers.getMaterialList()); + } + } + + /** + * Generates layers for round of type snowballs + * @param layer Location where the layers should start + */ + public static void generateLayersSnowballs(Location layer) { + Random random = new Random(); + Layers layers = new Layers(); + + layer.setY(layer.getY() - 1); + // Similar generation to shovels, except there are three layers + if (random.nextInt(4) == 0) { + // Circular layer + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getSafeMaterialList()); + layer.setY(layer.getY() - 6); + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getMaterialList()); + layer.setY(layer.getY() - 6); + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getMaterialList()); + } + else if (random.nextInt(4) == 1) { + // Square layer + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getSafeMaterialList()); + layer.setY(layer.getY() - 6); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getMaterialList()); + layer.setY(layer.getY() - 6); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getMaterialList()); + } + else if (random.nextInt(4) == 2) { + // Multi-tiered circle + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getSafeMaterialList()); + Generator.generateLayer(layer, 13, 1, Material.AIR); + layer.setY(layer.getY() - 1); + Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRANITE), layers.getMaterialList()); + Generator.generateLayer(layer, 4, 1, Material.AIR); + layer.setY(layer.getY() - 1); + Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); + layer.setY(layer.getY() - 6); + + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getSafeMaterialList()); + Generator.generateLayer(layer, 13, 1, Material.AIR); + layer.setY(layer.getY() - 1); + Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRANITE), layers.getMaterialList()); + Generator.generateLayer(layer, 4, 1, Material.AIR); + layer.setY(layer.getY() - 1); + Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); + layer.setY(layer.getY() - 6); + + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getSafeMaterialList()); + Generator.generateLayer(layer, 13, 1, Material.AIR); + layer.setY(layer.getY() - 1); + Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRANITE), layers.getMaterialList()); + Generator.generateLayer(layer, 4, 1, Material.AIR); + layer.setY(layer.getY() - 1); + Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); + } + else { + // Multi-tiered square + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getSafeMaterialList()); + Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR); + layer.setY(layer.getY() - 1); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRANITE), layers.getMaterialList()); + Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR); + layer.setY(layer.getY() - 1); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); + layer.setY(layer.getY() - 6); + + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getSafeMaterialList()); + Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR); + layer.setY(layer.getY() - 1); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRANITE), layers.getMaterialList()); + Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR); + layer.setY(layer.getY() - 1); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); + layer.setY(layer.getY() - 6); + + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getSafeMaterialList()); + Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR); + layer.setY(layer.getY() - 1); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRANITE), layers.getMaterialList()); + Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR); + layer.setY(layer.getY() - 1); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); + } + } + /** * Generates a layer (basically just a cylinder) as good as possible with blocks * @param center The center of the layer (Location) @@ -21,7 +144,7 @@ public class Generator { * * @return A list of Blocks containing all the blocks it just changed */ - public static List generateLayer(Location center, int radius, int height, Material material) { + private static List generateLayer(Location center, int radius, int height, Material material) { int Cx = center.getBlockX(); int Cy = center.getBlockY(); int Cz = center.getBlockZ(); @@ -77,13 +200,13 @@ public static List generateCuboid(Location firstPos, Location secondPos, * Keep in mind that not all Materials may be used, the amount used depends on the size of the layer. * More Materials = more randomization */ - public static void generateClumps(List blockList, List materialList) { + private static void generateClumps(List blockList, List materialList) { Random random = new Random(); // Make new lists so we can manipulate them List blocks = new ArrayList<>(blockList); List materials = new ArrayList<>(materialList); Collections.shuffle(materials); - while (blocks.size() > 0) { + while (!blocks.isEmpty()) { Material randomMaterial = materials.get(random.nextInt(materials.size())); Block aBlock = blocks.get(0); aBlock.setType(randomMaterial); diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java b/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java new file mode 100644 index 0000000..dae6dd5 --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java @@ -0,0 +1,152 @@ +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 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() { + FileConfiguration config = plugin.getConfig(); + + // arenas + if (config.getConfigurationSection("arenas") == null) { + plugin.getLogger().warning("Section arenas is missing from config"); + return; + } + arenas = new HashMap<>(); + for (String arenaName: Objects.requireNonNull(config.getConfigurationSection("arenas")).getKeys(false)) { + ConfigurationSection section = Objects.requireNonNull(config.getConfigurationSection("arenas")).getConfigurationSection(arenaName); + + Result res = readWorld(section); + if (!res.success) { + plugin.getLogger().warning("Failed to load arena "+arenaName+": "+res.error); + continue; + } + + arenas.put(arenaName, new Arena(arenaName, res.value)); + } + + // lobby + { + Resultres = readWorld(config.getConfigurationSection("lobby-spawn")); + if (!res.success) { + plugin.getLogger().warning("Failed to load lobby: "+res.error); + plugin.getLogger().severe("^ THIS IS REQUIRED, PLEASE FIX TO AVOID UNDEFINED BEHAVIOR"); + } + + lobby = res.value; + } + + // winner lobby + if (config.getBoolean("enable-winner-lobby-spawn")) { + Resultres = readWorld(config.getConfigurationSection("winner-lobby-spawn")); + if (!res.success) { + plugin.getLogger().warning("Failed to load winner lobby: "+res.error); + } + + winnerLobby = res.value; + } + + // wait area + if (config.getBoolean("enable-wait-area")) { + Resultres = readWorld(config.getConfigurationSection("wait-area")); + if (!res.success) { + plugin.getLogger().warning("Failed to load winner lobby: "+res.error); + } + + waitArea = res.value; + } + + // other + HideLeaveJoin = config.getBoolean("hideJoinLeaveMessages"); + waitDuration = config.getInt("wait-duration", 15); + } + + /** + * 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 readWorld(@Nullable ConfigurationSection section) { + + if (section == null) { + Result res = new Result<>(); + res.success = false; + res.error = "Section missing from config"; + return res; + } + + double x = section.getDouble("x"); + double y = section.getDouble("y"); + double z = section.getDouble("x"); + if (x==0 || y == 0 || z == 0) { + Result res = new Result<>(); + res.success = false; + res.error = "Arena coordinates are missing or are zero. Coordinates cannot be zero."; + return res; + } + + String worldName = section.getString("world"); + if (worldName == null) { + Result res = new Result<>(); + res.success = false; + res.error = "World name is missing"; + return res; + } + + World world = Bukkit.getWorld(worldName); + if (world == null) { + Result res = new Result<>(); + res.success = false; + res.error = "Failed to load world " + worldName; + return res; + } + + Result res = new Result<>(); + res.success = true; + res.value = new Location(world,x,y,z); + return res; + } + + /** + * 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/Constants.java b/src/main/java/com/MylesAndMore/Tumble/plugin/Constants.java deleted file mode 100644 index 118af23..0000000 --- a/src/main/java/com/MylesAndMore/Tumble/plugin/Constants.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.MylesAndMore.Tumble.plugin; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MVWorldManager; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.plugin.Plugin; - -import java.util.List; -import java.util.Objects; - -public class Constants { - public static Plugin getPlugin() { - return Bukkit.getServer().getPluginManager().getPlugin("tumble"); - } - public static String getPermissionMessage() { return Constants.getPlugin().getConfig().getString("permissionMessage"); } - public static String getGameWorld() { return Constants.getPlugin().getConfig().getString("gameWorld"); } - public static String getLobbyWorld() { return Constants.getPlugin().getConfig().getString("lobbyWorld"); } - public static String getGameType() { return Constants.getPlugin().getConfig().getString("gameMode"); } - public static List getPlayersInGame() { return Objects.requireNonNull(Bukkit.getServer().getWorld(Constants.getGameWorld())).getPlayers(); } - public static List getPlayersInLobby() { return Objects.requireNonNull(Bukkit.getServer().getWorld(Constants.getLobbyWorld())).getPlayers(); } - - public static MultiverseCore getMV() { return (MultiverseCore) Bukkit.getServer().getPluginManager().getPlugin("Multiverse-Core"); } - public static MVWorldManager getMVWorldManager() { return getMV().getMVWorldManager(); } -} diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/EventListener.java b/src/main/java/com/MylesAndMore/Tumble/plugin/EventListener.java deleted file mode 100644 index 9a4dd62..0000000 --- a/src/main/java/com/MylesAndMore/Tumble/plugin/EventListener.java +++ /dev/null @@ -1,222 +0,0 @@ -package com.MylesAndMore.Tumble.plugin; - -import java.util.Objects; - -import com.MylesAndMore.Tumble.game.Game; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.entity.Snowball; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.block.Action; -import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.block.BlockDropItemEvent; -import org.bukkit.event.entity.*; -import org.bukkit.event.inventory.InventoryDragEvent; -import org.bukkit.event.player.*; -import org.bukkit.inventory.ItemStack; -import org.bukkit.util.Vector; - -/** - * Tumble event listener for all plugin and game-related events. - */ -public class EventListener implements Listener { - @EventHandler - public void PlayerJoinEvent(PlayerJoinEvent event) { - // Hide/show join message accordingly - if (Constants.getPlugin().getConfig().getBoolean("hideJoinLeaveMessages")) { - event.setJoinMessage(null); - } - // Check if either of the worlds are not defined in config, if so, end to avoid any NPEs later on - if (Constants.getGameWorld() == null || Constants.getLobbyWorld() == null) { return; } - if (event.getPlayer().getWorld() == Bukkit.getWorld(Constants.getGameWorld())) { - // Send the player back to the lobby if they try to join in the middle of a game - event.getPlayer().teleport(Objects.requireNonNull(Bukkit.getWorld(Constants.getLobbyWorld())).getSpawnLocation()); - } - if (Constants.getPlugin().getConfig().getBoolean("autoStart.enabled")) { - if (Constants.getPlayersInLobby().size() == Constants.getPlugin().getConfig().getInt("autoStart.players")) { - // The autoStart should begin if it is already enabled and the amount of players is correct; pass this to the Game - Game.getGame().autoStart(); - } - } - } - - @EventHandler - public void PlayerChangedWorldEvent(PlayerChangedWorldEvent event) { - if (Constants.getGameWorld() == null || Constants.getLobbyWorld() == null) { - return; - } - if (event.getPlayer().getWorld() == Bukkit.getWorld(Constants.getLobbyWorld())) { - // Another event on which autostart could be triggered - if (Constants.getPlugin().getConfig().getBoolean("autoStart.enabled")) { - if (Constants.getPlayersInLobby().size() == Constants.getPlugin().getConfig().getInt("autoStart.players")) { - Game.getGame().autoStart(); - } - } - } - // Also check if the player left to another world and cancel autostart - else if (event.getFrom() == Bukkit.getWorld(Constants.getLobbyWorld())) { - if (Objects.equals(Game.getGame().getGameState(), "waiting")) { - Game.getGame().cancelStart(); - } - } - } - - @EventHandler - public void PlayerQuitEvent(PlayerQuitEvent event) { - // Hide/show leave message accordingly - if (Constants.getPlugin().getConfig().getBoolean("hideJoinLeaveMessages")) { - event.setQuitMessage(null); - } - if (Constants.getLobbyWorld() == null) { return; } - if (event.getPlayer().getWorld() == Bukkit.getWorld(Constants.getLobbyWorld())) { - // Check if the game is in the process of autostarting, if so cancel - if (Objects.equals(Game.getGame().getGameState(), "waiting")) { - Game.getGame().cancelStart(); - } - } - } - - @EventHandler - public void PlayerDeathEvent(PlayerDeathEvent event) { - if (Constants.getGameWorld() == null) { return; } - // Pass game deaths to the Game - if (event.getEntity().getWorld() == Bukkit.getWorld(Constants.getGameWorld())) { - Game.getGame().playerDeath(event.getEntity()); - } - } - - @EventHandler - public void PlayerItemDamageEvent(PlayerItemDamageEvent event) { - if (Constants.getGameWorld() == null) { return; } - // Remove item damage within games - if (event.getPlayer().getWorld() == Bukkit.getWorld(Constants.getGameWorld())) { - event.setCancelled(true); - } - } - - @EventHandler - public void ProjectileLaunchEvent(ProjectileLaunchEvent event) { - if (Constants.getGameWorld() == null) { - return; - } - if (event.getEntity().getWorld() == Bukkit.getWorld(Constants.getGameWorld())) { - if (event.getEntity() instanceof Snowball) { - if (event.getEntity().getShooter() instanceof Player player) { - // Prevent projectiles (snowballs) from being thrown before the game starts - if (Objects.equals(Game.getGame().getGameState(), "starting")) { - event.setCancelled(true); - } - else { - // Give players a snowball when they've used one (infinite snowballs) - Bukkit.getServer().getScheduler().runTask(Constants.getPlugin(), () -> player.getInventory().addItem(new ItemStack(Material.SNOWBALL, 1))); - } - } - } - } - } - - @EventHandler - public void ProjectileHitEvent(ProjectileHitEvent event) { - if (Constants.getGameWorld() == null) { return; } - else if (event.getHitBlock() == null) { return; } - // Removes blocks that snowballs thrown by players have hit in the game world - if (event.getHitBlock().getWorld() == Bukkit.getWorld(Constants.getGameWorld())) { - if (event.getEntity() instanceof Snowball) { - if (event.getEntity().getShooter() instanceof Player) { - if (event.getHitBlock() != null) { - if (event.getHitBlock().getLocation().distanceSquared(Objects.requireNonNull(Bukkit.getWorld(Constants.getGameWorld())).getSpawnLocation()) < 579) { - event.getHitBlock().setType(Material.AIR); - } - } - else if (event.getHitEntity() != null) { - if (event.getHitEntity() instanceof Player hitPlayer) { - // Also cancel any knockback - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> hitPlayer.setVelocity(new Vector())); - } - } - } - } - } - } - - @EventHandler - public void PlayerDropItemEvent(PlayerDropItemEvent event) { - if (Constants.getGameWorld() == null) { return; } - // Don't allow items to drop in the game world - if (event.getPlayer().getWorld() == Bukkit.getWorld((Constants.getGameWorld()))) { - event.setCancelled(true); - } - } - - @EventHandler - public void PlayerMoveEvent(PlayerMoveEvent event) { - if (Constants.getGameWorld() == null) { return; } - // Cancel movement if the game is starting (so players can't move before the game starts) - if (Objects.equals(Game.getGame().getGameState(), "starting")) { - event.setCancelled(true); - } - } - - @EventHandler - public void BlockDropItemEvent(BlockDropItemEvent event) { - if (Constants.getGameWorld() == null) { return; } - // If a block was going to drop an item (ex. snow dropping snowballs) in the game world, cancel it - if (event.getBlock().getWorld() == Bukkit.getWorld(Constants.getGameWorld())) { - event.setCancelled(true); - } - } - - @EventHandler - public void PlayerInteractEvent(PlayerInteractEvent event) { - if (Constants.getGameWorld() == null) { return; } - // Remove blocks when clicked in the game world (all gamemodes require this functionality) - if (event.getAction() == Action.LEFT_CLICK_BLOCK) { - if (Objects.requireNonNull(event.getClickedBlock()).getWorld() == Bukkit.getWorld(Constants.getGameWorld())) { - event.getClickedBlock().setType(Material.AIR); - } - } - } - - @EventHandler - public void BlockBreakEvent(BlockBreakEvent event) { - if (Constants.getGameWorld() == null) { return; } - // This just doesn't allow blocks to break in the gameWorld; the PlayerInteractEvent will take care of everything - // This prevents any weird client-server desync - if (event.getBlock().getWorld() == Bukkit.getWorld(Constants.getGameWorld())) { - event.setCancelled(true); - } - } - - @EventHandler - public void FoodLevelChangeEvent(FoodLevelChangeEvent event) { - if (Constants.getGameWorld() == null) { return; } - // INFINITE FOOD (YAY!!!!) - if (event.getEntity().getWorld() == Bukkit.getWorld(Constants.getGameWorld())) { - event.setCancelled(true); - } - } - - @EventHandler - public void EntityDamageEvent(EntityDamageEvent event) { - if (Constants.getGameWorld() == null) { return; } - // Check to see if a player got damaged by another entity (player, snowball, etc) in the gameWorld, if so, cancel it - if (event.getEntity().getWorld() == Bukkit.getWorld(Constants.getGameWorld())) { - if (event.getEntity() instanceof Player) { - if (event.getCause() == EntityDamageEvent.DamageCause.ENTITY_ATTACK || event.getCause() == EntityDamageEvent.DamageCause.ENTITY_SWEEP_ATTACK || event.getCause() == EntityDamageEvent.DamageCause.FALL) { - event.setCancelled(true); - } - } - } - } - - @EventHandler - public void InventoryDragEvent(InventoryDragEvent event) { - if (Constants.getGameWorld() == null) { return; } - if (event.getWhoClicked().getWorld() == Bukkit.getWorld((Constants.getGameWorld()))) { - event.setCancelled(true); - } - } - -} diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/GameState.java b/src/main/java/com/MylesAndMore/Tumble/plugin/GameState.java new file mode 100644 index 0000000..879a1f5 --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/plugin/GameState.java @@ -0,0 +1,7 @@ +package com.MylesAndMore.Tumble.plugin; + +public enum GameState { + WAITING, + STARTING, + RUNNING, +} diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/GameType.java b/src/main/java/com/MylesAndMore/Tumble/plugin/GameType.java new file mode 100644 index 0000000..cf01c88 --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/plugin/GameType.java @@ -0,0 +1,11 @@ +package com.MylesAndMore.Tumble.plugin; + +public enum GameType { + SHOVELS, + SNOWBALLS, + MIXED; + + public String toString() { + return this.name().toLowerCase(); + } +} diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/Result.java b/src/main/java/com/MylesAndMore/Tumble/plugin/Result.java new file mode 100644 index 0000000..68e1133 --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/plugin/Result.java @@ -0,0 +1,8 @@ +package com.MylesAndMore.Tumble.plugin; + +// java does not have result types (i miss rust { + public boolean success; + public T value; + public String error; +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 68b4e3d..8413843 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,24 +1,39 @@ -# Customize the default game mode; options include: shovels, snowballs, mixed -gameMode: mixed - -# Customize the auto start feature of Tumble; players can be up to 8 -autoStart: - enabled: false - players: 2 - # Hides player join/leave messages in public chat hideJoinLeaveMessages: false -# Customize the message that displays when the player does not have permission to execute a command from this plugin -permissionMessage: You do not have permission to perform this command! +# 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 +enable-wait-area: false +wait-area: + 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 -# Customize the place that the winner is teleported after a game ends -# Keep in mind that these coordinates cannot be zero! The teleport will fail if any of them are; use something like 0.5 instead -winnerTeleport: +# 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 +enable-winner-lobby-spawn: false +winner-lobby-spawn: x: y: - z: + z: + world: -# The plugin will populate these fields automatically -lobbyWorld: -gameWorld: \ No newline at end of file +# Add/remove as you wish +# Keep in mind that these coordinates cannot be zero! Use something like 0.5 instead +arenas: + 'test': + x: 0.5 + y: 60 + z: 0.5 + world: world \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index d070c3e..a890bdd 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -3,51 +3,51 @@ name: Tumble version: 1.0.4 description: 'A Minecraft: Java Edition plugin recreating the Tumble minigame from Minecraft Legacy Console Edition.' api-version: 1.16 -load: STARTUP +load: POSTWORLD author: MylesAndMore website: https://github.com/MylesAndMore/Tumble -depend: - - Multiverse-Core commands: + join: + description: Joins a Tumble match. + usage: '§cUsage: /tumble:join [gameType]' + permission: tumble.join + leave: + description: Quits a Tumble match. + usage: '§cUsage: /tumble:leave [arenaName]' + permission: tumble.leave + forcestart: + description: Force starts a Tumble match. + usage: '§cUsage: /tumble:forcestart [arenaName]' + permission: tumble.forcestart + forcestop: + description: Force stops a Tumble match. + usage: '§cUsage: /tumble:forcestop [arenaName]' + permission: tumble.forcestop + config: + description: Modify arenas and settings + usage: '§cUsage: /tumble:config *not implemented yet*' + permission: tumble.config reload: description: Reloads the plugin's config. usage: '§cUsage: /tumble:reload' permission: tumble.reload - link: - description: Links a world on the server as a lobby/game world. - usage: '§cUsage: /tumble:link (lobby|game)' - permission: tumble.link - aliases: [linkworld, link-world] - start: - description: Force starts a Tumble match with an optional game type. - usage: '§cUsage: /tumble:start [gameType]' - permission: tumble.start - winlocation: - description: Links the location to teleport the winning player of a game. - usage: '§cUsage: /tumble:winlocation [x] [y] [z]' - permission: tumble.winlocation - aliases: [win-location, winloc, win-loc] - autostart: - description: Configures the auto start functions of Tumble. - usage: '§cUsage: /tumble:autostart [enable|disable]' - permission: tumble.autostart - aliases: [auto-start] permissions: - tumble.reload: - description: Allows you to reload the plugin's config. - default: op - tumble.link: - description: Allows you to link a world on the server as a lobby/game world. - default: op - tumble.start: - description: Allows you to start a Tumble match. + tumble.join: + description: Allows you to join a Tumble match. + default: true + tumble.leave: + description: Allows you to leave a Tumble match. + default: true + tumble.forcestart: + description: Allows you to force start a Tumble match. default: op - tumble.winlocation: - description: Allows you to link a win location. + tumble.forcestop: + description: Allows you to force stop a Tumble match. default: op - tumble.autostart: - description: Allows you to set the autostart details of Tumble. - default: op - tumble.update: + tumble.config: description: Allows you to get a notification if Tumble is out of date. default: op + tumble.reload: + description: Allows you to reload the plugin's config. + default: op + From e88880050faa1bafb7ac49ccbc53df6039dd112e Mon Sep 17 00:00:00 2001 From: sowgro Date: Sun, 9 Jun 2024 14:36:22 -0400 Subject: [PATCH 03/51] Fix wait duration --- src/main/java/com/MylesAndMore/Tumble/game/Game.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index 8fa9289..6e46f8d 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -1,5 +1,6 @@ package com.MylesAndMore.Tumble.game; +import com.MylesAndMore.Tumble.commands.Config; import com.MylesAndMore.Tumble.plugin.ConfigManager; import com.MylesAndMore.Tumble.plugin.GameState; import com.MylesAndMore.Tumble.plugin.GameType; @@ -224,11 +225,12 @@ public void addPlayer(Player p) { */ public void autoStart() { // Wait for the player to load in + int waitDuration = ConfigManager.waitDuration; Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { - displayActionbar(gamePlayers, ChatColor.GREEN + "Game will begin in 15 seconds!"); + displayActionbar(gamePlayers, ChatColor.GREEN + "Game will begin in "+waitDuration+" seconds!"); 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::startGame, 300); + autoStartID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, this::startGame, waitDuration * 20L); }, 50); } From 4d4974d9b5f6d2778da559f5f9b1082eb5e35cda Mon Sep 17 00:00:00 2001 From: sowgro Date: Sun, 9 Jun 2024 14:53:55 -0400 Subject: [PATCH 04/51] various fixes --- .../java/com/MylesAndMore/Tumble/commands/ForceStart.java | 1 + .../java/com/MylesAndMore/Tumble/commands/ForceStop.java | 1 + src/main/java/com/MylesAndMore/Tumble/commands/Leave.java | 1 + .../java/com/MylesAndMore/Tumble/game/EventListener.java | 5 ++++- src/main/java/com/MylesAndMore/Tumble/game/Game.java | 8 +++++++- 5 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java index 78ff183..a862268 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java @@ -35,6 +35,7 @@ public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNul } game.startGame(); + sender.sendMessage(ChatColor.GREEN + "Starting game"); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java index 7f266d7..8845e25 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java @@ -35,6 +35,7 @@ public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNul } game.killGame(); + sender.sendMessage(ChatColor.GREEN + "Game stopped."); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Leave.java b/src/main/java/com/MylesAndMore/Tumble/commands/Leave.java index 94255a4..abf0592 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Leave.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Leave.java @@ -40,6 +40,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command } game.removePlayer((Player) sender); + sender.sendMessage(ChatColor.GREEN + "Game left."); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java index 16698f4..7e099e7 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java @@ -4,6 +4,7 @@ import com.MylesAndMore.Tumble.plugin.ConfigManager; import com.MylesAndMore.Tumble.plugin.GameState; +import com.MylesAndMore.Tumble.plugin.GameType; import org.bukkit.*; import org.bukkit.entity.Player; import org.bukkit.entity.Snowball; @@ -73,6 +74,7 @@ public void PlayerItemDamageEvent(PlayerItemDamageEvent event) { @EventHandler public void ProjectileLaunchEvent(ProjectileLaunchEvent event) { + if (game.roundType != GameType.SNOWBALLS) { return; } if (event.getEntity().getWorld() == gameWorld && event.getEntity() instanceof Snowball && event.getEntity().getShooter() instanceof Player player) { @@ -90,7 +92,7 @@ public void ProjectileLaunchEvent(ProjectileLaunchEvent event) { @EventHandler public void ProjectileHitEvent(ProjectileHitEvent event) { - if (event.getHitBlock() == null) { return; } + if (event.getHitBlock() == null && game.roundType != GameType.SNOWBALLS) { return; } // Removes blocks that snowballs thrown by players have hit in the game world if (event.getHitBlock().getWorld() == gameWorld) { if (event.getEntity() instanceof Snowball) { @@ -142,6 +144,7 @@ public void BlockDropItemEvent(BlockDropItemEvent event) { @EventHandler public void PlayerInteractEvent(PlayerInteractEvent event) { + if (game.roundType != GameType.SHOVELS) {return;} // Remove blocks when clicked in the game world (all gamemodes require this functionality) if (event.getAction() == Action.LEFT_CLICK_BLOCK && Objects.requireNonNull(event.getClickedBlock()).getWorld() == gameWorld) { diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index 6e46f8d..c58313e 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -1,6 +1,5 @@ package com.MylesAndMore.Tumble.game; -import com.MylesAndMore.Tumble.commands.Config; import com.MylesAndMore.Tumble.plugin.ConfigManager; import com.MylesAndMore.Tumble.plugin.GameState; import com.MylesAndMore.Tumble.plugin.GameType; @@ -197,6 +196,12 @@ private void gameEnd() { public void killGame() { Bukkit.getServer().getScheduler().cancelTask(gameID); HandlerList.unregisterAll(eventListener); + clearInventories(gamePlayers); + for (Player aPlayer : gamePlayers) { + if (aPlayer.getWorld().equals(arena.world)) { + aPlayer.teleport(Objects.requireNonNull(ConfigManager.lobby)); + } + } arena.game = null; } @@ -210,6 +215,7 @@ public void removePlayer(Player p) { if (gamePlayers.size() < 2) { gameEnd(); } + p.getInventory().clear(); p.teleport(ConfigManager.lobby); } From 2bb2b1afa6426cc0490f7083d349671c08a1394f Mon Sep 17 00:00:00 2001 From: sowgro Date: Sun, 9 Jun 2024 15:10:49 -0400 Subject: [PATCH 05/51] make players auto respawn, fix bug --- .../java/com/MylesAndMore/Tumble/game/EventListener.java | 7 +++++-- src/main/java/com/MylesAndMore/Tumble/game/Game.java | 4 +--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java index 7e099e7..f538159 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java @@ -59,8 +59,11 @@ public void PlayerQuitEvent(PlayerQuitEvent event) { @EventHandler public void PlayerDeathEvent(PlayerDeathEvent event) { - if (event.getEntity().getWorld() == gameWorld) { + if (game.gamePlayers.contains(event.getEntity())) { game.playerDeath(event.getEntity()); + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { + event.getEntity().spigot().respawn(); + }, 10); } } @@ -92,7 +95,7 @@ public void ProjectileLaunchEvent(ProjectileLaunchEvent event) { @EventHandler public void ProjectileHitEvent(ProjectileHitEvent event) { - if (event.getHitBlock() == null && game.roundType != GameType.SNOWBALLS) { return; } + if (event.getHitBlock() == null || game.roundType != GameType.SNOWBALLS) { return; } // Removes blocks that snowballs thrown by players have hit in the game world if (event.getHitBlock().getWorld() == gameWorld) { if (event.getEntity() instanceof Snowball) { diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index c58313e..3efcd80 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -56,9 +56,6 @@ public void startGame() { Bukkit.getServer().getScheduler().cancelTask(autoStartID); autoStartID = -1; -// if (waitingPlayers.size() < 2) { -// return false; -// } eventListener = new EventListener(this); Bukkit.getServer().getPluginManager().registerEvents(eventListener, plugin); @@ -175,6 +172,7 @@ private void gameEnd() { // Wait 10s (200t), then Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { // First, check to see if there is a separate location to tp the winner to + clearArena(); if (ConfigManager.winnerLobby != null && winner != null) { winner.teleport(ConfigManager.winnerLobby); // Remove the winner from the game so they don't get double-tp'd From 8cb97b0bc66f6d29770e6994ee19cd0c3835055c Mon Sep 17 00:00:00 2001 From: sowgro Date: Sun, 9 Jun 2024 15:21:02 -0400 Subject: [PATCH 06/51] Fix waiting area --- src/main/java/com/MylesAndMore/Tumble/game/EventListener.java | 4 +--- src/main/java/com/MylesAndMore/Tumble/game/Game.java | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java index f538159..07cd77a 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java @@ -61,9 +61,7 @@ public void PlayerQuitEvent(PlayerQuitEvent event) { public void PlayerDeathEvent(PlayerDeathEvent event) { if (game.gamePlayers.contains(event.getEntity())) { game.playerDeath(event.getEntity()); - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { - event.getEntity().spigot().respawn(); - }, 10); + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> event.getEntity().spigot().respawn(), 10); } } diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index 3efcd80..2774ce7 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -219,6 +219,9 @@ public void removePlayer(Player p) { public void addPlayer(Player p) { gamePlayers.add(p); + if (ConfigManager.waitArea != null) { + p.teleport(ConfigManager.waitArea); + } if (gamePlayers.size() >= 2 && gameState == GameState.WAITING) { autoStart(); } From 2aa28e390b8a9fa763baef30e204af78e1a28456 Mon Sep 17 00:00:00 2001 From: sowgro Date: Mon, 10 Jun 2024 00:42:55 -0400 Subject: [PATCH 07/51] Add config comand and file writing --- .../MylesAndMore/Tumble/commands/Config.java | 89 ++++++++++++++++++- .../MylesAndMore/Tumble/commands/Leave.java | 17 +--- .../com/MylesAndMore/Tumble/game/Arena.java | 2 +- .../Tumble/plugin/ConfigManager.java | 42 ++++++++- src/main/resources/plugin.yml | 6 +- 5 files changed, 137 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Config.java b/src/main/java/com/MylesAndMore/Tumble/commands/Config.java index d91a5b5..256b45e 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Config.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Config.java @@ -1,5 +1,7 @@ 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; @@ -9,6 +11,7 @@ import org.jetbrains.annotations.NotNull; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; public class Config implements CommandExecutor, TabCompleter { @@ -24,12 +27,96 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command return false; } - sender.sendMessage(ChatColor.RED + "Not implemented yet"); // TODO + 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())); + 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 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 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/Leave.java b/src/main/java/com/MylesAndMore/Tumble/commands/Leave.java index abf0592..1d00ca4 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Leave.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Leave.java @@ -27,16 +27,10 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command return false; } - Game game; - if (args.length < 1 || args[0] == null) { - game = ConfigManager.findGamePlayerIsIn((Player)sender); - if (game == null) { - sender.sendMessage(ChatColor.RED + "Missing arena name"); - return false; - } - } - else { - game = ConfigManager.arenas.get(args[0]).game; + Game game = ConfigManager.findGamePlayerIsIn((Player)sender); + if (game == null) { + sender.sendMessage(ChatColor.RED + "You are not in a game."); + return false; } game.removePlayer((Player) sender); @@ -46,9 +40,6 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command @Override public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { - if (args.length == 1) { - return ConfigManager.arenas.keySet().stream().toList(); - } return new ArrayList<>(); } } \ No newline at end of file diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Arena.java b/src/main/java/com/MylesAndMore/Tumble/game/Arena.java index ef477d2..499fa6c 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Arena.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Arena.java @@ -10,7 +10,7 @@ public class Arena { public Game game = null; public final World world; - public final Location location; + public Location location; public final String name; /** diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java b/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java index dae6dd5..3207e1b 100644 --- a/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java @@ -52,7 +52,7 @@ public static void readConfig() { Resultres = readWorld(config.getConfigurationSection("lobby-spawn")); if (!res.success) { plugin.getLogger().warning("Failed to load lobby: "+res.error); - plugin.getLogger().severe("^ THIS IS REQUIRED, PLEASE FIX TO AVOID UNDEFINED BEHAVIOR"); + plugin.getLogger().severe("Lobby world is required. Run '/tumble:config set lobbyWorld' ASAP"); } lobby = res.value; @@ -136,6 +136,46 @@ private static Result readWorld(@Nullable ConfigurationSection section return res; } + public static void WriteConfig() { + if (waitArea != null) { + WriteWorld(plugin.getConfig().getConfigurationSection("wait-area"), waitArea); + plugin.getConfig().set("enable-wait-area", true); + } + else { + plugin.getConfig().set("enable-wait-area", false); + } + + if (lobby != null) { + WriteWorld(plugin.getConfig().getConfigurationSection("lobby-spawn"), lobby); + } + + if (winnerLobby != null) { + WriteWorld(plugin.getConfig().getConfigurationSection("winner-spawn"), winnerLobby); + plugin.getConfig().set("enable-winner-lobby", true); + } + else { + plugin.getConfig().set("enable-winner-lobby", true); + } + + for (String arenaName: arenas.keySet()) { + ConfigurationSection c = plugin.getConfig().getConfigurationSection("arenas."+arenaName); + if (c == null) { + c = plugin.getConfig().createSection("arenas."+arenaName); + } + 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 diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index a890bdd..0d9b70c 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -13,7 +13,7 @@ commands: permission: tumble.join leave: description: Quits a Tumble match. - usage: '§cUsage: /tumble:leave [arenaName]' + usage: '§cUsage: /tumble:leave' permission: tumble.leave forcestart: description: Force starts a Tumble match. @@ -24,8 +24,8 @@ commands: usage: '§cUsage: /tumble:forcestop [arenaName]' permission: tumble.forcestop config: - description: Modify arenas and settings - usage: '§cUsage: /tumble:config *not implemented yet*' + description: Modify arenas and worlds. + usage: '§cUsage: /tumble:config ' permission: tumble.config reload: description: Reloads the plugin's config. From a8f5a1457a3da4b99a982ab0e400f96c0d91c9f4 Mon Sep 17 00:00:00 2001 From: sowgro Date: Mon, 10 Jun 2024 01:05:22 -0400 Subject: [PATCH 08/51] Change command format --- .../java/com/MylesAndMore/Tumble/Main.java | 12 +++++----- .../Tumble/plugin/ConfigManager.java | 11 +++++---- src/main/resources/plugin.yml | 24 +++++++++---------- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/MylesAndMore/Tumble/Main.java b/src/main/java/com/MylesAndMore/Tumble/Main.java index f3ea52e..8221973 100644 --- a/src/main/java/com/MylesAndMore/Tumble/Main.java +++ b/src/main/java/com/MylesAndMore/Tumble/Main.java @@ -17,12 +17,12 @@ public class Main extends JavaPlugin{ public void onEnable() { plugin = this; - Objects.requireNonNull(this.getCommand("reload")).setExecutor(new Reload()); - Objects.requireNonNull(this.getCommand("config")).setExecutor(new Config()); - Objects.requireNonNull(this.getCommand("forcestart")).setExecutor(new ForceStart()); - Objects.requireNonNull(this.getCommand("join")).setExecutor(new Join()); - Objects.requireNonNull(this.getCommand("leave")).setExecutor(new Leave()); - Objects.requireNonNull(this.getCommand("forcestop")).setExecutor(new ForceStop()); + 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()); new Metrics(this, 16940); // TODO: change command format diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java b/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java index 3207e1b..c0f6686 100644 --- a/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java @@ -27,6 +27,7 @@ public class ConfigManager { * Reads config file and populates values above */ public static void readConfig() { + plugin.reloadConfig(); FileConfiguration config = plugin.getConfig(); // arenas @@ -52,7 +53,7 @@ public static void readConfig() { Resultres = readWorld(config.getConfigurationSection("lobby-spawn")); if (!res.success) { plugin.getLogger().warning("Failed to load lobby: "+res.error); - plugin.getLogger().severe("Lobby world is required. Run '/tumble:config set lobbyWorld' ASAP"); + plugin.getLogger().severe("Lobby world is required. Run '/tumble-config set lobbyWorld' ASAP"); } lobby = res.value; @@ -107,7 +108,7 @@ private static Result readWorld(@Nullable ConfigurationSection section double x = section.getDouble("x"); double y = section.getDouble("y"); double z = section.getDouble("x"); - if (x==0 || y == 0 || z == 0) { + if (x == 0 || y == 0 || z == 0) { Result res = new Result<>(); res.success = false; res.error = "Arena coordinates are missing or are zero. Coordinates cannot be zero."; @@ -138,7 +139,7 @@ private static Result readWorld(@Nullable ConfigurationSection section public static void WriteConfig() { if (waitArea != null) { - WriteWorld(plugin.getConfig().getConfigurationSection("wait-area"), waitArea); + WriteWorld(Objects.requireNonNull(plugin.getConfig().getConfigurationSection("wait-area")), waitArea); plugin.getConfig().set("enable-wait-area", true); } else { @@ -146,11 +147,11 @@ public static void WriteConfig() { } if (lobby != null) { - WriteWorld(plugin.getConfig().getConfigurationSection("lobby-spawn"), lobby); + WriteWorld(Objects.requireNonNull(plugin.getConfig().getConfigurationSection("lobby-spawn")), lobby); } if (winnerLobby != null) { - WriteWorld(plugin.getConfig().getConfigurationSection("winner-spawn"), winnerLobby); + WriteWorld(Objects.requireNonNull(plugin.getConfig().getConfigurationSection("winner-spawn")), winnerLobby); plugin.getConfig().set("enable-winner-lobby", true); } else { diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 0d9b70c..8bb306e 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -7,29 +7,29 @@ load: POSTWORLD author: MylesAndMore website: https://github.com/MylesAndMore/Tumble commands: - join: + tumble-join: description: Joins a Tumble match. - usage: '§cUsage: /tumble:join [gameType]' + usage: '§cUsage: /tumble-join [gameType]' permission: tumble.join - leave: + tumble-leave: description: Quits a Tumble match. - usage: '§cUsage: /tumble:leave' + usage: '§cUsage: /tumble-leave' permission: tumble.leave - forcestart: + tumble-forcestart: description: Force starts a Tumble match. - usage: '§cUsage: /tumble:forcestart [arenaName]' + usage: '§cUsage: /tumble-forcestart [arenaName]' permission: tumble.forcestart - forcestop: + tumble-forcestop: description: Force stops a Tumble match. - usage: '§cUsage: /tumble:forcestop [arenaName]' + usage: '§cUsage: /tumble-forcestop [arenaName]' permission: tumble.forcestop - config: + tumble-config: description: Modify arenas and worlds. - usage: '§cUsage: /tumble:config ' + usage: '§cUsage: /tumble-config ' permission: tumble.config - reload: + tumble-reload: description: Reloads the plugin's config. - usage: '§cUsage: /tumble:reload' + usage: '§cUsage: /tumble-reload' permission: tumble.reload permissions: tumble.join: From a3bdc62507c1f71806d11b80fdd4f9975c0236a8 Mon Sep 17 00:00:00 2001 From: sowgro Date: Mon, 10 Jun 2024 01:48:13 -0400 Subject: [PATCH 09/51] Fix config reading bug --- README.md | 68 ++++++++----------- .../Tumble/plugin/ConfigManager.java | 4 +- src/main/resources/config.yml | 6 +- 3 files changed, 34 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index d06fefa..a034629 100644 --- a/README.md +++ b/README.md @@ -28,53 +28,43 @@ If you've never heard of it, [Tumble](https://minecraft.wiki/w/Tumble) is a twis 3. Start your server. The plugin will generate a couple of warnings, these are normal. 4. Ensure that you have imported your worlds using a plugin like Multiverse. This can be done with the command ```/mv import normal```. - -[//]: # (TODO: finish this once config system is done) -5. Now you need to tell Tumble which world is your lobby and which world is your game arena. You can do this with ```/tumble:link lobby``` and ```/tumble:link game``` respectively. -6. **VERY IMPORTANT:** The plugin will teleport players to the world spawn point of each world, and generate the game's blocks around the spawn point of the game world. Ensure that your spawn points are clear of any obstructions, and that a 20x20x20 cube is cleared out from the spawn of whatever game world you are using. **Any blocks in this area will be destroyed when the game begins.** -7. You're done! You can now start games with the command ```/tumble:start```. +5. Now you need to tell Tumble where your lobby is and where your game arena is. You can do this by going to the center positions and running ```/tumble-config set lobbyWorld``` and ```/tumble-config add ``` respectively. +6. **VERY IMPORTANT:** The plugin will teleport players to the world and generate the game's blocks around the point you set. Ensure that your spawn points are clear of any obstructions, and that a 20x20x20 cube is cleared out **Any blocks in this area will be destroyed when the game begins.** +7. You're done! You can now start games with the command ```/tumble-start mixed```. Scroll down for more options to configure your game. ## Commands/Permissions -- ```/tumble:reload``` - - - *Description:* Reloads the plugin's configuration. - - *Usage:* ```/tumble:reload``` - - *Permission:* ```tumble.reload``` -- ```/tumble:link``` - - *Description:* Links a world on the server as a lobby or game world. - - *Usage:* ```/tumble:link (lobby|game)``` - - *Permission:* ```tumble.link``` -- ```/tumble:start``` - - *Description:* Force starts a Tumble match (with an optional game type). - - *Usage:* ```/tumble:start [game-type]``` - - *Permission:* ```tumble.start``` -- ```/tumble:winlocation``` - - *Description:* Sets the location to teleport the winning player of a game. Uses the player's location if no arguments are specified. - - *Usage:* ```/tumble:winlocation [x] [y] [z]``` - - *Permission:* ```tumble.winlocation``` -- ```/tumble:autostart``` - - *Description:* Configures the auto start functions of Tumble. - - *Usage:* ```/tumble:autostart [enable|disable]``` - - *Permission:* ```tumble.autostart``` -- *Permission:* ```tumble.update``` - - Players with this permission will receive a notification upon joining if Tumble is out of date. +- **tumble-join**: + - Description: Joins a Tumble match. + - Usage: `/tumble-join [gameType]` + - Permission: tumble.join +- **tumble-leave**: + - Description: Quits a Tumble match. + - Usage: `/tumble-leave` + - Permission: tumble.leave +- **tumble-forcestart**: + - Description: Force starts a Tumble match. + - Usage: `/tumble-forcestart [arenaName]` + - Permission: tumble.forcestart +- **tumble-forcestop**: + - Description: Force stops a Tumble match. + - Usage: `/tumble-forcestop [arenaName]` + - Permission: tumble.forcestop +- **tumble-config**: + - Description: Modify arenas and worlds. + - Usage: `/tumble-config ` + - Permission: tumble.config +- **tumble-reload**: + - Description: Reloads the plugin's config. + - Usage: `/tumble-reload` + - Permission: tumble.reload ## Configuration +Use `/tumble-config` for modifying the config in-game. See available options with the tab auto complete feature -- ```gameMode``` - - Customize the default game mode of Tumble. - - Acceptable options include: shovels, snowballs, mixed - - *Default:* ```mixed``` - -- ```hideJoinLeaveMessages``` - - Hides join/leave messages in public chat. - - *Default:* ```false``` - -- ```permissionMessage``` - - Customize the message that displays when the player does not have permission to execute a command from this plugin. +See the comments inside config,yml for manual editing ## Issues & Feedback diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java b/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java index c0f6686..66bcb09 100644 --- a/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java @@ -152,10 +152,10 @@ public static void WriteConfig() { if (winnerLobby != null) { WriteWorld(Objects.requireNonNull(plugin.getConfig().getConfigurationSection("winner-spawn")), winnerLobby); - plugin.getConfig().set("enable-winner-lobby", true); + plugin.getConfig().set("enable-winner-lobby-spawn", true); } else { - plugin.getConfig().set("enable-winner-lobby", true); + plugin.getConfig().set("enable-winner-lobby-spawn", true); } for (String arenaName: arenas.keySet()) { diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 8413843..b81d268 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -33,7 +33,7 @@ winner-lobby-spawn: # Keep in mind that these coordinates cannot be zero! Use something like 0.5 instead arenas: 'test': - x: 0.5 - y: 60 - z: 0.5 + x: 100 + y: 100 + z: 100 world: world \ No newline at end of file From 0f0bbd20fec271ec8f7a3d14ed6091a92653967b Mon Sep 17 00:00:00 2001 From: sowgro Date: Mon, 10 Jun 2024 21:43:55 -0400 Subject: [PATCH 10/51] add "waiting for players" text --- og-guide.md | 23 +++++++++++-------- .../com/MylesAndMore/Tumble/game/Game.java | 3 +++ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/og-guide.md b/og-guide.md index 9c613a8..22c6c54 100644 --- a/og-guide.md +++ b/og-guide.md @@ -9,18 +9,21 @@ In this guide, I'll go over how to set up the Tumble plugin with the original ga 1. Download the worlds and unzip them into your server's main/root directory. **Ensure you download the Java and not the Bedrock versions**! A huge thanks to *Catmanjoe* for porting these worlds! This game would not be the same without you! - - [Lobby (new edition)](https://mcpedl.com/mc-2017-new-mini-games-lobby-download-map/) - - [Lobby (old edition)](https://mcpedl.com/minecraft-2016-classic-mini-games-lobby-map/) - - [Arena](https://www.planetminecraft.com/project/minecraft-classic-tumble-mode-arena-download-java/) + - [Lobby (2017)](https://www.theminecraftarchitect.com/mini-game-maps/2017-mini-game-lobby) + - [Lobby (2016)](https://www.theminecraftarchitect.com/mini-game-maps/2016-mini-game-lobby) + - [Normal Arena](https://www.planetminecraft.com/project/minecraft-classic-tumble-mode-arena-download-java/) + - [Festive Arena (Download coming soon)]() + - [Halloween Arena (Download coming soon)]() + - [Birthday Arena (Download coming soon)]() 2. Take note of the names of the world folders (you may rename them), we will need this in a moment. 3. Start and join your server. -4. Import both worlds into Multiverse. You can do this by running the command ```/mv import normal``` for both worlds. -5. Now you can link each world! Do this with ```/tumble:link lobby``` and ```/tumble:link game``` respectively. -6. Teleport to your new lobby world by using ```/mvtp ```. -7. Set the correct spawn location in this world using ```/setworldspawn```. For me, the correct coordinates were ```/setworldspawn -341.5 58 -340.5```, but your results may vary. -8. Set the location that the winner will be teleported using ```/tumble:winloc```. Again, the correct coordinates were ```/tumble:winloc -362.5 76 -340.5``` in my case. -9. Now, teleport to the game world. Use ```/mvtp ```. -10. Set the correct spawn point of this world. This is also where the game will generate its blocks. My preferred position is ```/setworldspawn 0 60 0```, but you may place the spawn whereever you like. +4. Set your lobby spawn by going to the location and running `/tumble-config set lobbySpawn`. In the 2017 console lobby this is at `-341.5 58 -340.5` +5. If you want to have a separate lobby spawn for the winner, set it with `/tumble-config set winnerLobbySpawn`. In the 2017 console lobby this is at `-362.5 76 -340.5` +6. Import your arena world into Multiverse. You can do this by running the command `/mv import normal` +7. Teleport to the arena world. Use `/mvtp `. +8. Now you can create the arena! Do this by going to the spawn location and running `/tumble-config add `. In the console arena this is at `0 60 0` +9. Repeat steps 4, 5 and 6 for each arena +10. Join the game by using `/tumble-join Mixed`(or whichever game mode you want). You're done! diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index 2774ce7..f266375 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -225,6 +225,9 @@ public void addPlayer(Player p) { if (gamePlayers.size() >= 2 && gameState == GameState.WAITING) { autoStart(); } + else { + displayActionbar(Collections.singletonList(p), ChatColor.YELLOW + "Waiting for players"); + } } /** From 519cf806ea12fcbfffa6e8fbae0efc0c617b87ed Mon Sep 17 00:00:00 2001 From: sowgro Date: Tue, 11 Jun 2024 02:49:15 -0400 Subject: [PATCH 11/51] Fix world loading issue and tp player to spawn after death --- src/main/java/com/MylesAndMore/Tumble/game/EventListener.java | 1 + src/main/resources/plugin.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java index 07cd77a..6f6a1ca 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java @@ -53,6 +53,7 @@ public void PlayerQuitEvent(PlayerQuitEvent event) { event.setQuitMessage(null); } if (event.getPlayer().getWorld() == gameWorld) { + event.getPlayer().teleport(ConfigManager.lobby); game.removePlayer(event.getPlayer()); } } diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 8bb306e..486fccf 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -6,6 +6,7 @@ api-version: 1.16 load: POSTWORLD author: MylesAndMore website: https://github.com/MylesAndMore/Tumble +softdepend: [Multiverse-Core] commands: tumble-join: description: Joins a Tumble match. From c78d343eed0ff8f37e1f77cfe12fec0d47e47be0 Mon Sep 17 00:00:00 2001 From: sowgro Date: Wed, 12 Jun 2024 00:46:56 -0400 Subject: [PATCH 12/51] New config format, kill-at-y setting and more bug fixes --- .../MylesAndMore/Tumble/commands/Config.java | 2 +- .../Tumble/commands/ForceStop.java | 5 + .../MylesAndMore/Tumble/commands/Reload.java | 11 +- .../com/MylesAndMore/Tumble/game/Arena.java | 4 +- .../Tumble/game/EventListener.java | 30 ++++- .../com/MylesAndMore/Tumble/game/Game.java | 11 +- .../com/MylesAndMore/Tumble/game/Layers.java | 3 - .../Tumble/plugin/ConfigManager.java | 119 +++++++++--------- .../MylesAndMore/Tumble/plugin/GameState.java | 1 + .../MylesAndMore/Tumble/plugin/Result.java | 18 ++- src/main/resources/config.yml | 45 ++++--- 11 files changed, 154 insertions(+), 95 deletions(-) diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Config.java b/src/main/java/com/MylesAndMore/Tumble/commands/Config.java index 256b45e..5fc2de6 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Config.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Config.java @@ -35,7 +35,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command switch (args[0]) { case "add" -> { String arenaName = args[1]; - ConfigManager.arenas.put(arenaName, new Arena(arenaName, ((Player)sender).getLocation())); + ConfigManager.arenas.put(arenaName, new Arena(arenaName, ((Player)sender).getLocation(), null)); sender.sendMessage(ChatColor.GREEN + "Arena added."); } case "set" -> { diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java index 8845e25..d862586 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java @@ -34,6 +34,11 @@ public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNul game = ConfigManager.arenas.get(args[0]).game; } + if (game == null) { + sender.sendMessage(ChatColor.RED + "No game is currently running in this arena"); + return false; + } + game.killGame(); sender.sendMessage(ChatColor.GREEN + "Game stopped."); 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 5d35a03..a19b04b 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java @@ -1,5 +1,7 @@ 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; @@ -20,8 +22,15 @@ public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNul sender.sendMessage(ChatColor.RED + "You do not have permission to perform this command!"); return false; } + + for (Arena a : ConfigManager.arenas.values()) { + if (a.game != null) { + a.game.killGame(); + } + } + plugin.onEnable(); - sender.sendMessage(ChatColor.GREEN + "Tumble configuration reloaded successfully."); + sender.sendMessage(ChatColor.GREEN + "Tumble configuration reloaded. Check console for errors."); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Arena.java b/src/main/java/com/MylesAndMore/Tumble/game/Arena.java index 499fa6c..831f251 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Arena.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Arena.java @@ -12,15 +12,17 @@ public class Arena { public final World world; public Location location; public final String name; + public Integer killAtY; /** * Creates a new Arena * @param name Name of the arena * @param location Center point / spawn point. */ - public Arena(@NotNull String name, @NotNull Location location) { + public Arena(@NotNull String name, @NotNull Location location, Integer killAtY) { this.location = location; this.world = location.getWorld(); 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 6f6a1ca..28fb0d8 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java @@ -19,6 +19,7 @@ import org.bukkit.util.Vector; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; import static com.MylesAndMore.Tumble.Main.plugin; @@ -131,9 +132,16 @@ public void PlayerDropItemEvent(PlayerDropItemEvent event) { public void PlayerMoveEvent(PlayerMoveEvent event) { // Cancel movement if the game is starting (so players can't move before the game starts) if (Objects.equals(game.gameState, GameState.STARTING) - && event.getPlayer().getWorld().equals(gameWorld)) { + && event.getPlayer().getWorld().equals(gameWorld) + && !equalPosition(event.getFrom(),event.getTo())) { event.setCancelled(true); } + // kill player if they are below a Y level + if (event.getPlayer().getWorld().equals(gameWorld) && game.arena.killAtY != null) { + if (event.getPlayer().getLocation().getY() <= game.arena.killAtY) { + event.getPlayer().setHealth(0); + } + } } @EventHandler @@ -146,7 +154,7 @@ public void BlockDropItemEvent(BlockDropItemEvent event) { @EventHandler public void PlayerInteractEvent(PlayerInteractEvent event) { - if (game.roundType != GameType.SHOVELS) {return;} +// if (game.roundType != GameType.SHOVELS) {return;} // Remove blocks when clicked in the game world (all gamemodes require this functionality) if (event.getAction() == Action.LEFT_CLICK_BLOCK && Objects.requireNonNull(event.getClickedBlock()).getWorld() == gameWorld) { @@ -182,8 +190,8 @@ public void EntityDamageEvent(EntityDamageEvent event) { if (event.getEntity().getWorld() == gameWorld) { if (event.getEntity() instanceof Player) { if (event.getCause() == EntityDamageEvent.DamageCause.ENTITY_ATTACK - && event.getCause() == EntityDamageEvent.DamageCause.ENTITY_SWEEP_ATTACK - && event.getCause() == EntityDamageEvent.DamageCause.FALL) { + || event.getCause() == EntityDamageEvent.DamageCause.ENTITY_SWEEP_ATTACK + || event.getCause() == EntityDamageEvent.DamageCause.FALL) { event.setCancelled(true); } } @@ -205,5 +213,15 @@ public void PlayerRespwanEvent(PlayerRespawnEvent event) { } } - // TODO: stop tile drops for pistons -} + // TODO: stop tile drops for pistons, stop player from getting stuck in the waiting area after they leave + + public static boolean equalPosition(Location l1, @Nullable Location l2) { + if (l2 == null) { + return true; + } + return (l1.getX() == l2.getX()) && + (l1.getY() == l2.getY()) && + (l1.getZ() == l2.getZ()); + } + +} \ No newline at end of file diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index f266375..c8244d9 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -131,7 +131,9 @@ private void prepareGameType(GameType type) { */ private void roundEnd() { // Cancel the tasks that auto-end the round + gameState = GameState.ENDING; Bukkit.getServer().getScheduler().cancelTask(gameID); + gameID = -1; // Clear old layers (as a fill command, this would be /fill ~-20 ~-20 ~-20 ~20 ~ ~20 relative to spawn) playSound(gamePlayers, Sound.BLOCK_NOTE_BLOCK_PLING, SoundCategory.BLOCKS, 5, 0); // Check if there was a definite winner or not @@ -162,6 +164,10 @@ private void roundEnd() { */ private void gameEnd() { if (!gamePlayers.isEmpty()) { + Bukkit.getServer().getScheduler().cancelTask(gameID); + gameID = -1; + Bukkit.getServer().getScheduler().cancelTask(autoStartID); + autoStartID = -1; Player winner = getPlayerWithMostWins(gameWins); setGamemode(gamePlayers, GameMode.SPECTATOR); clearInventories(gamePlayers); @@ -193,6 +199,9 @@ private void gameEnd() { */ public void killGame() { Bukkit.getServer().getScheduler().cancelTask(gameID); + gameID = -1; + Bukkit.getServer().getScheduler().cancelTask(autoStartID); + autoStartID = -1; HandlerList.unregisterAll(eventListener); clearInventories(gamePlayers); for (Player aPlayer : gamePlayers) { @@ -253,7 +262,7 @@ public void playerDeath(Player player) { // remove that player (who just died) from the roundPlayersArray, effectively eliminating them, playersAlive.remove(player); // If there are less than 2 players in the game (1 just died), - if (playersAlive.size() < 2) { + if (playersAlive.size() < 2 && gameState == GameState.RUNNING) { roundEnd(); } } diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Layers.java b/src/main/java/com/MylesAndMore/Tumble/game/Layers.java index b87df26..9c60393 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Layers.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Layers.java @@ -190,12 +190,9 @@ public Layers() { add(Material.REDSTONE_BLOCK); add(Material.REDSTONE_LAMP); add(Material.TARGET); - add(Material.DAYLIGHT_DETECTOR); add(Material.PISTON); - add(Material.STICKY_PISTON); add(Material.SLIME_BLOCK); add(Material.OBSERVER); - add(Material.HOPPER); }}; matList.add(gen8); matList.add(gen9); diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java b/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java index 66bcb09..855e755 100644 --- a/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java @@ -29,59 +29,75 @@ public class ConfigManager { public static void readConfig() { plugin.reloadConfig(); FileConfiguration config = plugin.getConfig(); - - // arenas - if (config.getConfigurationSection("arenas") == null) { - plugin.getLogger().warning("Section arenas is missing from config"); - return; - } - arenas = new HashMap<>(); - for (String arenaName: Objects.requireNonNull(config.getConfigurationSection("arenas")).getKeys(false)) { - ConfigurationSection section = Objects.requireNonNull(config.getConfigurationSection("arenas")).getConfigurationSection(arenaName); - Result res = readWorld(section); + HideLeaveJoin = config.getBoolean("hideJoinLeaveMessages", false); + waitDuration = config.getInt("wait-duration", 15); + + // wait area + if (config.getBoolean("wait-area.enable", false)) { + Resultres = readWorld(config.getConfigurationSection("wait-area.spawn")); if (!res.success) { - plugin.getLogger().warning("Failed to load arena "+arenaName+": "+res.error); - continue; + plugin.getLogger().warning("Failed to load winner lobby: "+res.error); + waitArea = null; + } + else { + waitArea = res.value; } - - arenas.put(arenaName, new Arena(arenaName, res.value)); } // lobby { - Resultres = readWorld(config.getConfigurationSection("lobby-spawn")); + Resultres = readWorld(config.getConfigurationSection("lobby.spawn")); if (!res.success) { plugin.getLogger().warning("Failed to load lobby: "+res.error); - plugin.getLogger().severe("Lobby world is required. Run '/tumble-config set lobbyWorld' ASAP"); + 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; } - - lobby = res.value; } // winner lobby - if (config.getBoolean("enable-winner-lobby-spawn")) { - Resultres = readWorld(config.getConfigurationSection("winner-lobby-spawn")); + if (config.getBoolean("winner-lobby.enable", false)) { + Resultres = 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; + } + } - winnerLobby = res.value; + // arenas + ConfigurationSection arenasSection = config.getConfigurationSection("arenas"); + if (arenasSection == null) { + plugin.getLogger().warning("Section 'arenas' is missing from config"); + return; } - - // wait area - if (config.getBoolean("enable-wait-area")) { - Resultres = readWorld(config.getConfigurationSection("wait-area")); + 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 res = readWorld(anArenaSection.getConfigurationSection("spawn")); if (!res.success) { - plugin.getLogger().warning("Failed to load winner lobby: "+res.error); + plugin.getLogger().warning("Failed to load arena "+arenaName+": "+res.error); + continue; } - waitArea = res.value; + arenas.put(arenaName, new Arena(arenaName, res.value, killAtY)); } - - // other - HideLeaveJoin = config.getBoolean("hideJoinLeaveMessages"); - waitDuration = config.getInt("wait-duration", 15); } /** @@ -99,69 +115,54 @@ public static void readConfig() { private static Result readWorld(@Nullable ConfigurationSection section) { if (section == null) { - Result res = new Result<>(); - res.success = false; - res.error = "Section missing from config"; - return res; + return new Result<>("Section missing from config"); } double x = section.getDouble("x"); double y = section.getDouble("y"); double z = section.getDouble("x"); if (x == 0 || y == 0 || z == 0) { - Result res = new Result<>(); - res.success = false; - res.error = "Arena coordinates are missing or are zero. Coordinates cannot be zero."; - return res; + return new Result<>("Arena coordinates are missing or are zero. Coordinates cannot be zero."); } String worldName = section.getString("world"); if (worldName == null) { - Result res = new Result<>(); - res.success = false; - res.error = "World name is missing"; - return res; + return new Result<>("World name is missing"); } World world = Bukkit.getWorld(worldName); if (world == null) { - Result res = new Result<>(); - res.success = false; - res.error = "Failed to load world " + worldName; - return res; + return new Result<>("Failed to load world " + worldName); } - Result res = new Result<>(); - res.success = true; - res.value = new Location(world,x,y,z); - return res; + 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("enable-wait-area", true); + plugin.getConfig().set("wait-area.enable", true); } else { - plugin.getConfig().set("enable-wait-area", false); + plugin.getConfig().set("wait-area.enable", false); } if (lobby != null) { - WriteWorld(Objects.requireNonNull(plugin.getConfig().getConfigurationSection("lobby-spawn")), lobby); + WriteWorld(Objects.requireNonNull(plugin.getConfig().getConfigurationSection("lobby.spawn")), lobby); } if (winnerLobby != null) { - WriteWorld(Objects.requireNonNull(plugin.getConfig().getConfigurationSection("winner-spawn")), winnerLobby); - plugin.getConfig().set("enable-winner-lobby-spawn", true); + WriteWorld(Objects.requireNonNull(plugin.getConfig().getConfigurationSection("winner-lobby.spawn")), winnerLobby); + plugin.getConfig().set("winner-lobby.enable", true); } else { - plugin.getConfig().set("enable-winner-lobby-spawn", true); + plugin.getConfig().set("winner-lobby.enable", false); } for (String arenaName: arenas.keySet()) { - ConfigurationSection c = plugin.getConfig().getConfigurationSection("arenas."+arenaName); + ConfigurationSection c = plugin.getConfig().getConfigurationSection("arenas."+arenaName+".spawn"); if (c == null) { - c = plugin.getConfig().createSection("arenas."+arenaName); + c = plugin.getConfig().createSection("arenas."+arenaName+".spawn"); } WriteWorld(c, arenas.get(arenaName).location); } diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/GameState.java b/src/main/java/com/MylesAndMore/Tumble/plugin/GameState.java index 879a1f5..06b2abe 100644 --- a/src/main/java/com/MylesAndMore/Tumble/plugin/GameState.java +++ b/src/main/java/com/MylesAndMore/Tumble/plugin/GameState.java @@ -4,4 +4,5 @@ public enum GameState { WAITING, STARTING, RUNNING, + ENDING } diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/Result.java b/src/main/java/com/MylesAndMore/Tumble/plugin/Result.java index 68e1133..9a07771 100644 --- a/src/main/java/com/MylesAndMore/Tumble/plugin/Result.java +++ b/src/main/java/com/MylesAndMore/Tumble/plugin/Result.java @@ -2,7 +2,19 @@ // java does not have result types (i miss rust { - public boolean success; - public T value; - public String error; + public final boolean success; + public final T value; + public final String error; + + public Result(String error) { + success = false; + this.error = error; + this.value = null; + } + + public Result(T value) { + success = true; + this.value = value; + this.error = null; + } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index b81d268..b13570e 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -5,35 +5,40 @@ hideJoinLeaveMessages: false 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 -enable-wait-area: false wait-area: - x: - y: - z: - world: + 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 +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 -enable-winner-lobby-spawn: false -winner-lobby-spawn: - x: - y: - z: - world: +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': - x: 100 - y: 100 - z: 100 - world: world \ No newline at end of file + kill-at-y: 5 + spawn: + x: 100 + y: 100 + z: 100 + world: world \ No newline at end of file From 0caa663a5ff175af4785247ce81b2ebb12cd0a07 Mon Sep 17 00:00:00 2001 From: sowgro Date: Thu, 13 Jun 2024 16:56:18 -0400 Subject: [PATCH 13/51] small fixes --- og-guide.md => OG_GUIDE.md | 2 +- src/main/java/com/MylesAndMore/Tumble/Main.java | 1 - src/main/java/com/MylesAndMore/Tumble/game/Game.java | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) rename og-guide.md => OG_GUIDE.md (97%) diff --git a/og-guide.md b/OG_GUIDE.md similarity index 97% rename from og-guide.md rename to OG_GUIDE.md index 22c6c54..bd041e0 100644 --- a/og-guide.md +++ b/OG_GUIDE.md @@ -20,7 +20,7 @@ A huge thanks to *Catmanjoe* for porting these worlds! This game would not be th 4. Set your lobby spawn by going to the location and running `/tumble-config set lobbySpawn`. In the 2017 console lobby this is at `-341.5 58 -340.5` 5. If you want to have a separate lobby spawn for the winner, set it with `/tumble-config set winnerLobbySpawn`. In the 2017 console lobby this is at `-362.5 76 -340.5` 6. Import your arena world into Multiverse. You can do this by running the command `/mv import normal` -7. Teleport to the arena world. Use `/mvtp `. +7. Teleport to the arena world. Use `/mvtp `. 8. Now you can create the arena! Do this by going to the spawn location and running `/tumble-config add `. In the console arena this is at `0 60 0` 9. Repeat steps 4, 5 and 6 for each arena 10. Join the game by using `/tumble-join Mixed`(or whichever game mode you want). diff --git a/src/main/java/com/MylesAndMore/Tumble/Main.java b/src/main/java/com/MylesAndMore/Tumble/Main.java index 8221973..274765e 100644 --- a/src/main/java/com/MylesAndMore/Tumble/Main.java +++ b/src/main/java/com/MylesAndMore/Tumble/Main.java @@ -24,7 +24,6 @@ public void onEnable() { Objects.requireNonNull(this.getCommand("tumble-leave")).setExecutor(new Leave()); Objects.requireNonNull(this.getCommand("tumble-forcestop")).setExecutor(new ForceStop()); new Metrics(this, 16940); - // TODO: change command format this.saveDefaultConfig(); // Saves the default config file (packaged in the JAR) if we haven't already ConfigManager.readConfig(); diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index c8244d9..c530489 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -60,7 +60,6 @@ public void startGame() { eventListener = new EventListener(this); Bukkit.getServer().getPluginManager().registerEvents(eventListener, plugin); - // clear area in case it did not get properly cleared roundStart(); } From 39ce49b6c25492bd1268c8ce9bba2731f013aab2 Mon Sep 17 00:00:00 2001 From: sowgro Date: Thu, 13 Jun 2024 17:03:26 -0400 Subject: [PATCH 14/51] Fix bug where gamemode would be switched before the player was in the world --- src/main/java/com/MylesAndMore/Tumble/game/Game.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index c530489..9bb2bc8 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -69,9 +69,9 @@ public void startGame() { private void roundStart() { gameState = GameState.STARTING; playersAlive = new ArrayList<>(gamePlayers); + scatterPlayers(gamePlayers); // Put all players in spectator to prevent them from getting kicked for flying setGamemode(gamePlayers, GameMode.SPECTATOR); - scatterPlayers(gamePlayers); clearInventories(gamePlayers); clearArena(); prepareGameType(type); From d849ab8d085675715622dcef212d32239eb5d4bb Mon Sep 17 00:00:00 2001 From: sowgro Date: Thu, 13 Jun 2024 21:50:11 -0400 Subject: [PATCH 15/51] Clean up code and fix join cmd --- .../Tumble/commands/ForceStart.java | 2 +- .../Tumble/commands/ForceStop.java | 2 +- .../MylesAndMore/Tumble/commands/Join.java | 12 ++- .../MylesAndMore/Tumble/commands/Reload.java | 2 +- .../com/MylesAndMore/Tumble/game/Game.java | 78 ++++++++++--------- .../com/MylesAndMore/Tumble/game/Layers.java | 1 - .../Tumble/plugin/ConfigManager.java | 2 +- 7 files changed, 53 insertions(+), 46 deletions(-) diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java index a862268..bdead44 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java @@ -34,7 +34,7 @@ public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNul game = ConfigManager.arenas.get(args[0]).game; } - game.startGame(); + game.gameStart(); sender.sendMessage(ChatColor.GREEN + "Starting game"); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java index d862586..ddd5826 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java @@ -39,7 +39,7 @@ public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNul return false; } - game.killGame(); + game.gameEnd(); sender.sendMessage(ChatColor.GREEN + "Game stopped."); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Join.java b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java index 4dd4ef8..de44da1 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Join.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java @@ -74,9 +74,15 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command if (arena.game == null) { game = arena.game = new Game(arena, type); } - 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); - return false; + else + { + if (arena.game.type == type) { + 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); + return false; + } } } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java b/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java index a19b04b..ca67a2e 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java @@ -25,7 +25,7 @@ public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNul for (Arena a : ConfigManager.arenas.values()) { if (a.game != null) { - a.game.killGame(); + a.game.gameEnd(); } } diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index 9bb2bc8..113a5cd 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -26,9 +26,10 @@ public class Game { public final GameType type; public final Arena arena; public final World gameWorld; - public final List gamePlayers = new ArrayList<>(); private final Location gameSpawn; + public final List gamePlayers = new ArrayList<>(); private final HashMap gameWins = new HashMap<>(); + private final HashMap inventories = new HashMap<>(); public GameState gameState = GameState.WAITING; public GameType roundType; private int gameID = -1; @@ -47,7 +48,7 @@ public Game(@NotNull Arena arena, @NotNull GameType type) { /** * Creates a new Game */ - public void startGame() { + public void gameStart() { // Check if the game is starting or running if (gameState != GameState.WAITING) { @@ -72,6 +73,7 @@ private void roundStart() { scatterPlayers(gamePlayers); // Put all players in spectator to prevent them from getting kicked for flying setGamemode(gamePlayers, GameMode.SPECTATOR); + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> setGamemode(gamePlayers, GameMode.SPECTATOR), 10); clearInventories(gamePlayers); clearArena(); prepareGameType(type); @@ -126,7 +128,7 @@ private void prepareGameType(GameType type) { } /** - * Round end stuff: Finds and displays winner, starts next round if necessary + * Ends round: Finds and displays winner, starts next round if necessary */ private void roundEnd() { // Cancel the tasks that auto-end the round @@ -159,60 +161,51 @@ private void roundEnd() { } /** - * Game end stuff: Displays overall winner and teleports players to lobby + * Ends game: Displays overall winner and teleports players to lobby */ - private void gameEnd() { + public void gameEnd() { if (!gamePlayers.isEmpty()) { - Bukkit.getServer().getScheduler().cancelTask(gameID); - gameID = -1; - Bukkit.getServer().getScheduler().cancelTask(autoStartID); - autoStartID = -1; - Player winner = getPlayerWithMostWins(gameWins); + setGamemode(gamePlayers, GameMode.SPECTATOR); clearInventories(gamePlayers); + + Player winner = getPlayerWithMostWins(gameWins); if (winner != null) { displayTitles(gamePlayers, ChatColor.RED + "Game over!", ChatColor.GOLD + winner.getName() + " has won the game!", 5, 60, 5); } displayActionbar(gamePlayers, ChatColor.BLUE + "Returning to lobby in ten seconds..."); + // Wait 10s (200t), then Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { - // First, check to see if there is a separate location to tp the winner to + clearArena(); - if (ConfigManager.winnerLobby != null && winner != null) { - winner.teleport(ConfigManager.winnerLobby); - // Remove the winner from the game so they don't get double-tp'd - gamePlayers.remove(winner); - } - // Send all players back to lobby (spawn) - for (Player aPlayer : gamePlayers) { - aPlayer.teleport(Objects.requireNonNull(ConfigManager.lobby)); + + for (Player p : gamePlayers) { + // Restore inventories + if (inventories.containsKey(p)) { + p.getInventory().setContents(inventories.get(p)); + } + + if (p == winner && ConfigManager.winnerLobby != null) { + p.teleport(ConfigManager.winnerLobby); + } + else { + p.teleport(Objects.requireNonNull(ConfigManager.lobby)); + } } + }, 200); } - HandlerList.unregisterAll(eventListener); - arena.game = null; - } - - /** - * Force stops a game - */ - public void killGame() { Bukkit.getServer().getScheduler().cancelTask(gameID); gameID = -1; Bukkit.getServer().getScheduler().cancelTask(autoStartID); autoStartID = -1; HandlerList.unregisterAll(eventListener); - clearInventories(gamePlayers); - for (Player aPlayer : gamePlayers) { - if (aPlayer.getWorld().equals(arena.world)) { - aPlayer.teleport(Objects.requireNonNull(ConfigManager.lobby)); - } - } arena.game = null; } /** - * Removes a player from the game + * Removes a player from the game. * Called when a player leaves the server, or if they issue the leave command * @param p Player to remove */ @@ -222,13 +215,24 @@ public void removePlayer(Player p) { gameEnd(); } p.getInventory().clear(); + if (inventories.containsKey(p)) { + p.getInventory().setContents(inventories.get(p)); + } p.teleport(ConfigManager.lobby); } + /** + * Adds a player to the wait area. Called from /tumble-join + * Precondition: the game is in state WAITING + * @param p Player to add + */ public void addPlayer(Player p) { gamePlayers.add(p); + // save inventory + inventories.put(p, p.getInventory().getContents()); if (ConfigManager.waitArea != null) { p.teleport(ConfigManager.waitArea); + p.getInventory().clear(); } if (gamePlayers.size() >= 2 && gameState == GameState.WAITING) { autoStart(); @@ -248,7 +252,7 @@ public void autoStart() { displayActionbar(gamePlayers, ChatColor.GREEN + "Game will begin in "+waitDuration+" seconds!"); 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::startGame, waitDuration * 20L); + autoStartID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, this::gameStart, waitDuration * 20L); }, 50); } @@ -288,9 +292,7 @@ private void scatterPlayers(List players) { new Location(gameWorld, (x + 11.5), y, (z + 11.5), 135, 0))); Collections.shuffle(scatterLocations); for (Player aPlayer : players) { - if (!aPlayer.teleport(scatterLocations.get(0))) { - plugin.getLogger().info("dbg: FAILED TELEPORT"); - } + aPlayer.teleport(scatterLocations.get(0)); scatterLocations.remove(0); // Remove that location so multiple players won't get the same one } } diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Layers.java b/src/main/java/com/MylesAndMore/Tumble/game/Layers.java index 9c60393..05e289a 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Layers.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Layers.java @@ -190,7 +190,6 @@ public Layers() { add(Material.REDSTONE_BLOCK); add(Material.REDSTONE_LAMP); add(Material.TARGET); - add(Material.PISTON); add(Material.SLIME_BLOCK); add(Material.OBSERVER); }}; diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java b/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java index 855e755..c4c1943 100644 --- a/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java @@ -120,7 +120,7 @@ private static Result readWorld(@Nullable ConfigurationSection section double x = section.getDouble("x"); double y = section.getDouble("y"); - double z = section.getDouble("x"); + 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."); } From 02cc74cc5ac06a69ec59d6277234ce69031402f4 Mon Sep 17 00:00:00 2001 From: sowgro Date: Thu, 27 Jun 2024 02:40:06 -0400 Subject: [PATCH 16/51] overhaul command and config formats (again) --- README.md | 68 +++--- .../java/com/MylesAndMore/Tumble/Main.java | 15 +- .../MylesAndMore/Tumble/commands/Config.java | 122 ----------- .../MylesAndMore/Tumble/commands/Create.java | 44 ++++ .../Tumble/commands/ForceStart.java | 33 +-- .../Tumble/commands/ForceStop.java | 35 ++-- .../MylesAndMore/Tumble/commands/Join.java | 50 +++-- .../MylesAndMore/Tumble/commands/Leave.java | 33 +-- .../MylesAndMore/Tumble/commands/Reload.java | 27 ++- .../MylesAndMore/Tumble/commands/Remove.java | 53 +++++ .../Tumble/commands/SetGameSpawn.java | 60 ++++++ .../Tumble/commands/SetKillYCordinate.java | 61 ++++++ .../Tumble/commands/SetLobby.java | 60 ++++++ .../Tumble/commands/SetWaitArea.java | 60 ++++++ .../Tumble/commands/SetWinnerLobby.java | 60 ++++++ .../MylesAndMore/Tumble/commands/Tumble.java | 85 ++++++++ .../Tumble/config/ArenaManager.java | 178 ++++++++++++++++ .../Tumble/config/ConfigManager.java | 23 +++ .../Tumble/config/LanguageManager.java | 50 +++++ .../com/MylesAndMore/Tumble/game/Arena.java | 19 +- .../Tumble/game/EventListener.java | 13 +- .../com/MylesAndMore/Tumble/game/Game.java | 48 +++-- .../Tumble/plugin/ConfigManager.java | 194 ------------------ .../Tumble/plugin/SubCommand.java | 8 + src/main/resources/arenas.yml | 23 +++ src/main/resources/config.yml | 37 ---- src/main/resources/language.yml | 42 ++++ src/main/resources/plugin.yml | 27 +-- 28 files changed, 1005 insertions(+), 523 deletions(-) delete mode 100644 src/main/java/com/MylesAndMore/Tumble/commands/Config.java create mode 100644 src/main/java/com/MylesAndMore/Tumble/commands/Create.java create mode 100644 src/main/java/com/MylesAndMore/Tumble/commands/Remove.java create mode 100644 src/main/java/com/MylesAndMore/Tumble/commands/SetGameSpawn.java create mode 100644 src/main/java/com/MylesAndMore/Tumble/commands/SetKillYCordinate.java create mode 100644 src/main/java/com/MylesAndMore/Tumble/commands/SetLobby.java create mode 100644 src/main/java/com/MylesAndMore/Tumble/commands/SetWaitArea.java create mode 100644 src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLobby.java create mode 100644 src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java create mode 100644 src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java create mode 100644 src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java create mode 100644 src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java delete mode 100644 src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java create mode 100644 src/main/java/com/MylesAndMore/Tumble/plugin/SubCommand.java create mode 100644 src/main/resources/arenas.yml create mode 100644 src/main/resources/language.yml diff --git a/README.md b/README.md index a034629..878e08f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,17 @@ +## temporary stuff (not ready for merge) +### known issues +- [ ] file writing not fully implemented (just need to call ArenaManager.write() somewhere) +- [ ] issues with join command +- [ ] no config validation + +### todo +- [ ] improve inventory saving +- [ ] improve Game.leave() method +- [ ] perhaps replace spectator mode with survival flight +- [ ] clean up and make config managers uniform in structure +- [ ] improve language flow +- [ ] put javadoc comments everywhere + # Tumble ## Overview @@ -6,11 +20,11 @@ Tumble is a Spigot/Paper plugin that aims to recreate the Tumble minigame from t ## What *is* Tumble? -If you've never heard of it, [Tumble](https://minecraft.wiki/w/Tumble) is a twist on the classic Minecraft minigame of spleef, where the objective is to break the blocks under your opponents. But in Tumble, you play on randomly generated layers of blocks, using shovels, snowballs, or both to try and eliminate your opponents. +If you've never heard of it, [Tumble](https://minecraft.wiki/w/tmbl) is a twist on the classic Minecraft minigame of spleef, where the objective is to break the blocks under your opponents. But in Tumble, you play on randomly generated layers of blocks, using shovels, snowballs, or both to try and eliminate your opponents. ## Features -- Choose from three different game modes present in the original game--shovels, snowballs, and mixed +- Choose from three different game modes present in the original game: shovels, snowballs, and mixed - Four types of random layer generation - 15 unique, themed layer varieties - Quick and easy setup and use @@ -21,51 +35,35 @@ If you've never heard of it, [Tumble](https://minecraft.wiki/w/Tumble) is a twis ## Setup -1. Simply [download](https://github.com/MylesAndMore/Tumble/releases) the plugin's JAR file and place it in your server's plugins directory. -2. Make sure that you have at least two worlds in your world directory! One is for your lobby world, and the other is for your game arena. +1. Simply [download](https://github.com/MylesAndMore/tmbl/releases) the plugin's JAR file and place it in your server's plugins directory. +2. Load the worlds for your lobby and arenas. - - If you would like an experience similar to the original game, see [my guide](https://github.com/MylesAndMore/tumble/blob/main/og-guide.md) for using the original worlds. + - If you would like an experience similar to the original game, see [my guide](https://github.com/MylesAndMore/tmbl/blob/main/OG-GUIDE.md) for using the original worlds. 3. Start your server. The plugin will generate a couple of warnings, these are normal. 4. Ensure that you have imported your worlds using a plugin like Multiverse. This can be done with the command ```/mv import normal```. -5. Now you need to tell Tumble where your lobby is and where your game arena is. You can do this by going to the center positions and running ```/tumble-config set lobbyWorld``` and ```/tumble-config add ``` respectively. +5. Now you need to tell Tumble where your lobby is and where your game arena is. You can do this by going to the center positions and running ```/tmbl-config set lobbyWorld``` and ```/tmbl-config add ``` respectively. 6. **VERY IMPORTANT:** The plugin will teleport players to the world and generate the game's blocks around the point you set. Ensure that your spawn points are clear of any obstructions, and that a 20x20x20 cube is cleared out **Any blocks in this area will be destroyed when the game begins.** -7. You're done! You can now start games with the command ```/tumble-start mixed```. +7. You're done! You can now start games with the command ```/tmbl start mixed```. Scroll down for more options to configure your game. -## Commands/Permissions - -- **tumble-join**: - - Description: Joins a Tumble match. - - Usage: `/tumble-join [gameType]` - - Permission: tumble.join -- **tumble-leave**: - - Description: Quits a Tumble match. - - Usage: `/tumble-leave` - - Permission: tumble.leave -- **tumble-forcestart**: - - Description: Force starts a Tumble match. - - Usage: `/tumble-forcestart [arenaName]` - - Permission: tumble.forcestart -- **tumble-forcestop**: - - Description: Force stops a Tumble match. - - Usage: `/tumble-forcestop [arenaName]` - - Permission: tumble.forcestop -- **tumble-config**: - - Description: Modify arenas and worlds. - - Usage: `/tumble-config ` - - Permission: tumble.config -- **tumble-reload**: - - Description: Reloads the plugin's config. - - Usage: `/tumble-reload` - - Permission: tumble.reload +## Commands / Permissions + +| Command | Description | Permission | +|------------------------------------------------|-----------------------------------|---------------------| +| `/tmbl join [gameType]` | Join a Tumble match. | `tumble.join` | +| `/tmbl leave` | Quit a Tumble match. | `tumble.leave` | +| `/tmbl forcestart [arenaName]` | Force start a Tumble match. | `tumble.forcestart` | +| `/tmbl forcestop [arenaName]` | Force stop a Tumble match. | `tumble.forcestop` | +| `/tmbl config ` | Modify arenas and worlds in game. | `tumble.config` | +| `/tmbl reload` | Reload the plugin's config. | `tumble.reload` | ## Configuration -Use `/tumble-config` for modifying the config in-game. See available options with the tab auto complete feature +Use `/tmbl config` for modifying the config in-game. See available options with the tab auto complete feature See the comments inside config,yml for manual editing ## Issues & Feedback -Feel free to report any bugs, leave feedback, ask questions, or submit ideas for new features on our [GitHub issues page](https://github.com/MylesAndMore/tumble/issues/new)! +Feel free to report any bugs, leave feedback, ask questions, or submit ideas for new features on our [GitHub issues page](https://github.com/MylesAndMore/tmbl/issues/new)! 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 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 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 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 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 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 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 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.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 boolean onCommand(@NotNull CommandSender sender, @NotNull Command command 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 boolean onCommand(@NotNull CommandSender sender, @NotNull Command command 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 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 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 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 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 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 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 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 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 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 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 onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { + if (args.length == 1) { + ArrayList 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 tmp = new ArrayList<>(List.of(arr)); + tmp.remove(0); + return tmp.toArray(new String[0]); + } + + private static Map.Entry 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 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 res = readWorld(anArenaSection.getConfigurationSection("game-spawn")); + if (res.success) { + arena.gameSpawn = res.value; + } + + Result lobbyRes = readWorld(anArenaSection.getConfigurationSection("lobby")); + if (lobbyRes.success) { + arena.lobby = lobbyRes.value; + } + + Result winnerLobbyRes = readWorld(anArenaSection.getConfigurationSection("winner-lobby")); + if (winnerLobbyRes.success) { + arena.winnerLobby = winnerLobbyRes.value; + } + + Result 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 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 @@ * 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 @@ 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 void PlayerJoinEvent(PlayerJoinEvent event) { } 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 void PlayerQuitEvent(PlayerQuitEvent event) { 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 void ProjectileHitEvent(ProjectileHitEvent event) { 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 void InventoryDragEvent(InventoryDragEvent event) { 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 gamePlayers = new ArrayList<>(); private final HashMap 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 void gameStart() { 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 @@ private void prepareGameType(GameType type) { 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 @@ private void roundEnd() { } // 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 void gameEnd() { 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 void gameEnd() { 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 void removePlayer(Player p) { if (inventories.containsKey(p)) { p.getInventory().setContents(inventories.get(p)); } - p.teleport(ConfigManager.lobby); + p.teleport(arena.lobby); } /** @@ -229,16 +233,15 @@ public void removePlayer(Player p) { 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 void autoStart() { // 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 @@ private void scatterPlayers(List players) { 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 scatterLocations = new ArrayList<>(List.of( new Location(gameWorld, (x - 14.5), y, (z + 0.5), -90, 0), @@ -303,16 +307,16 @@ private void scatterPlayers(List players) { */ 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 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)) { - Resultres = 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 - { - Resultres = 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)) { - Resultres = 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 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 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 [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 ' - 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. From b7f79527534c8edac418dee0791281ff784bec72 Mon Sep 17 00:00:00 2001 From: sowgro Date: Fri, 28 Jun 2024 01:45:24 -0400 Subject: [PATCH 17/51] Improve documentation, tweak commands --- OG_GUIDE.md | 108 +++++++++++++++--- README.md | 94 +++++++++++---- .../java/com/MylesAndMore/Tumble/Main.java | 6 +- .../MylesAndMore/Tumble/commands/Create.java | 1 + .../Tumble/commands/ForceStart.java | 12 +- .../Tumble/commands/ForceStop.java | 9 +- .../MylesAndMore/Tumble/commands/Remove.java | 1 + .../Tumble/commands/SetGameSpawn.java | 1 + ...KillYCordinate.java => SetKillYLevel.java} | 3 +- .../Tumble/commands/SetLobby.java | 1 + .../Tumble/commands/SetWaitArea.java | 1 + .../Tumble/commands/SetWinnerLobby.java | 1 + .../MylesAndMore/Tumble/commands/Tumble.java | 2 +- .../Tumble/config/ArenaManager.java | 4 +- .../Tumble/config/ConfigManager.java | 5 +- .../Tumble/config/LanguageManager.java | 3 +- src/main/resources/config.yml | 2 +- src/main/resources/language.yml | 2 +- 18 files changed, 200 insertions(+), 56 deletions(-) rename src/main/java/com/MylesAndMore/Tumble/commands/{SetKillYCordinate.java => SetKillYLevel.java} (94%) diff --git a/OG_GUIDE.md b/OG_GUIDE.md index bd041e0..91dab2e 100644 --- a/OG_GUIDE.md +++ b/OG_GUIDE.md @@ -6,30 +6,100 @@ In this guide, I'll go over how to set up the Tumble plugin with the original ga ## Steps -1. Download the worlds and unzip them into your server's main/root directory. **Ensure you download the Java and not the Bedrock versions**! -A huge thanks to *Catmanjoe* for porting these worlds! This game would not be the same without you! - - - [Lobby (2017)](https://www.theminecraftarchitect.com/mini-game-maps/2017-mini-game-lobby) - - [Lobby (2016)](https://www.theminecraftarchitect.com/mini-game-maps/2016-mini-game-lobby) - - [Normal Arena](https://www.planetminecraft.com/project/minecraft-classic-tumble-mode-arena-download-java/) - - [Festive Arena (Download coming soon)]() - - [Halloween Arena (Download coming soon)]() - - [Birthday Arena (Download coming soon)]() -2. Take note of the names of the world folders (you may rename them), we will need this in a moment. +1. Download the worlds and unzip them into your server's worlds directory. + - [Lobby]() + - [Normal Arena]() + - [Festive Arena]() + - [Halloween Arena]() + - [Birthday Arena]() + +2. Take note of the names of the world folders, we will need this in a moment. 3. Start and join your server. -4. Set your lobby spawn by going to the location and running `/tumble-config set lobbySpawn`. In the 2017 console lobby this is at `-341.5 58 -340.5` -5. If you want to have a separate lobby spawn for the winner, set it with `/tumble-config set winnerLobbySpawn`. In the 2017 console lobby this is at `-362.5 76 -340.5` -6. Import your arena world into Multiverse. You can do this by running the command `/mv import normal` -7. Teleport to the arena world. Use `/mvtp `. -8. Now you can create the arena! Do this by going to the spawn location and running `/tumble-config add `. In the console arena this is at `0 60 0` -9. Repeat steps 4, 5 and 6 for each arena -10. Join the game by using `/tumble-join Mixed`(or whichever game mode you want). +4. Import your arena worlds. This can be done with the multiverse command `/mv import normal` + +5. Paste the arena config below into `plugins/tumble/arenas.yml`: + ```yaml + arenas: + basic: + kill-at-y: 24 + game-spawn: + x: 0.5 + y: 60.0 + z: 0.5 + world: tmbl-basic + lobby: + x: -341.5 + y: 58 + z: -340.5 + world: lobby + winner-lobby: + x: -362.5 + y: 76 + Z: -340.5 + world: lobby + birthday: + kill-at-y: 27 + game-spawn: + x: 0.5 + y: 60 + z: 0.5 + world: tmbl-birthday + lobby: + x: -341.5 + y: 58 + z: -340.5 + world: lobby + winner-lobby: + x: -362.5 + y: 76 + Z: -340.5 + world: lobby + festive: + kill-at-y: 20 + game-spawn: + x: 0.5 + y: 60 + z: 0.5 + world: tmbl-festive + lobby: + x: -341.5 + y: 58 + z: -340.5 + world: lobby + winner-lobby: + x: -362.5 + y: 76 + Z: -340.5 + world: lobby + halloween: + kill-at-y: 23 + game-spawn: + x: 0.5 + y: 60 + z: -0.5 + world: tmbl-halloween + lobby: + x: -341.5 + y: 58 + z: -340.5 + world: lobby + winner-lobby: + x: -362.5 + y: 76 + Z: -340.5 + world: lobby + ``` +6. Reload the plugin with `/tmbl reload`. + +7. Join the game by using `/tmbl join basic Mixed` +(swap the arena and game type for whichever one you want to play). You're done! -## Continuation +## Suggestions -With this, the setup for this plugin is complete, but there still may be more for you to do. There are other plugins out there to fine-tune your experience even more. Plugins like [WorldGuard](https://dev.bukkit.org/projects/worldguard) and [CyberWorldReset](https://www.spigotmc.org/resources/cyberworldreset-standard-%E2%9C%A8-regenerate-worlds-scheduled-resets-lag-optimized%E3%80%8C1-8-1-19%E3%80%8D.96834/) can protect players from breaking blocks in the lobby and reset any redstone they activated, while others like [ViaVersion](https://www.spigotmc.org/resources/viaversion.19254/) can allow you to play Tumble from your favorite Minecraft version (yes, you, 1.8.9 players). +With this, the setup for this plugin is complete, but there still may be more for you to do. +There are other plugins out there to fine-tune your experience even more. Plugins like [WorldGuard](https://dev.bukkit.org/projects/worldguard) and [CyberWorldReset](https://www.spigotmc.org/resources/cyberworldreset-standard-%E2%9C%A8-regenerate-worlds-scheduled-resets-lag-optimized%E3%80%8C1-8-1-19%E3%80%8D.96834/) can protect players from breaking blocks in the lobby and reset any redstone they activated, while others like [ViaVersion](https://www.spigotmc.org/resources/viaversion.19254/) can allow you to play Tumble from your favorite Minecraft version (yes, you, 1.8.9 players). Whatever you choose, the experience is up to you. diff --git a/README.md b/README.md index 878e08f..2293d97 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@ ## temporary stuff (not ready for merge) ### known issues -- [ ] file writing not fully implemented (just need to call ArenaManager.write() somewhere) +- [x] file writing not fully implemented (just need to call ArenaManager.write() somewhere) - [ ] issues with join command - [ ] no config validation ### todo +- [ ] finish og-guide - [ ] improve inventory saving - [ ] improve Game.leave() method - [ ] perhaps replace spectator mode with survival flight @@ -20,7 +21,7 @@ Tumble is a Spigot/Paper plugin that aims to recreate the Tumble minigame from t ## What *is* Tumble? -If you've never heard of it, [Tumble](https://minecraft.wiki/w/tmbl) is a twist on the classic Minecraft minigame of spleef, where the objective is to break the blocks under your opponents. But in Tumble, you play on randomly generated layers of blocks, using shovels, snowballs, or both to try and eliminate your opponents. +If you've never heard of it, [Tumble](https://minecraft.wiki/w/tumble) is a twist on the classic Minecraft minigame of spleef, where the objective is to break the blocks under your opponents. But in Tumble, you play on randomly generated layers of blocks, using shovels, snowballs, or both to try and eliminate your opponents. ## Features @@ -32,37 +33,90 @@ If you've never heard of it, [Tumble](https://minecraft.wiki/w/tmbl) is a twist - Highly customizable - Open-source codebase - Multiple arenas and concurrent games +- Heavily configurable ## Setup -1. Simply [download](https://github.com/MylesAndMore/tmbl/releases) the plugin's JAR file and place it in your server's plugins directory. -2. Load the worlds for your lobby and arenas. - +1. [Download](https://github.com/MylesAndMore/tmbl/releases) the plugin's JAR file and place it in your server's plugins directory. +2. Place the worlds for your lobby and arenas in your plugins worlds directory. - If you would like an experience similar to the original game, see [my guide](https://github.com/MylesAndMore/tmbl/blob/main/OG-GUIDE.md) for using the original worlds. -3. Start your server. The plugin will generate a couple of warnings, these are normal. -4. Ensure that you have imported your worlds using a plugin like Multiverse. This can be done with the command ```/mv import normal```. -5. Now you need to tell Tumble where your lobby is and where your game arena is. You can do this by going to the center positions and running ```/tmbl-config set lobbyWorld``` and ```/tmbl-config add ``` respectively. -6. **VERY IMPORTANT:** The plugin will teleport players to the world and generate the game's blocks around the point you set. Ensure that your spawn points are clear of any obstructions, and that a 20x20x20 cube is cleared out **Any blocks in this area will be destroyed when the game begins.** -7. You're done! You can now start games with the command ```/tmbl start mixed```. +3. Start your server. +4. Import your worlds using a plugin like Multiverse. ```/mv import myWorld normal```. +5. Create your first arena `/tmbl create myArena` + - **Note**: The layers will generate relative to this location. Ensure that the area is clear, 20 blocks in each direction. + +7. You're done! You can now join the game ```/tmbl join myArena mixed```. Scroll down for more options to configure your game. ## Commands / Permissions -| Command | Description | Permission | -|------------------------------------------------|-----------------------------------|---------------------| -| `/tmbl join [gameType]` | Join a Tumble match. | `tumble.join` | -| `/tmbl leave` | Quit a Tumble match. | `tumble.leave` | -| `/tmbl forcestart [arenaName]` | Force start a Tumble match. | `tumble.forcestart` | -| `/tmbl forcestop [arenaName]` | Force stop a Tumble match. | `tumble.forcestop` | -| `/tmbl config ` | Modify arenas and worlds in game. | `tumble.config` | -| `/tmbl reload` | Reload the plugin's config. | `tumble.reload` | +| Command | Description | Permission | +|-------------------------------------|------------------------------------------------------------------------------------|-------------------------| +| `/tmbl join [gameType]` | Join a Tumble match. Can infer game type if a game is already started in the arena | `tumble.join` | +| `/tmbl leave` | Quit a Tumble match | `tumble.leave` | +| `/tmbl forcestart [arenaName]` | Force start a Tumble match. Can infer arena if you are in one | `tumble.forcestart` | +| `/tmbl forcestop [arenaName]` | Force stop a Tumble match. Can infer arena if you are in one | `tumble.forcestop` | +| `/tmbl reload` | Reload the plugin's configs. | `tumble.reload` | +| `/tmbl create ` | Create a new arena | `tumble.create` | +| `/tmbl remove ` | Remove an arena | `tumble.remove` | +| `/tmbl setGameSpawn ` | Set game spawn to your current position | `tumble.setGameSpawn` | +| `/tmbl setKillYLevel ` | Set the arena's Y-level to kill players at to current Y coordinate | `tumble.setKillYLevel` | +| `/tmbl setLobby ` | Set the arena's lobby to current location | `tumble.setLobby` | +| `/tmbl setWaitArea ` | Set the arena's wait area to the current location | `tumble.setWaitArea` | +| `/tmbl setWinnerLobby ` | Set the arena's lobby to the current location | `tumble.setWinnerLobby` | + ## Configuration -Use `/tmbl config` for modifying the config in-game. See available options with the tab auto complete feature +Configuration for this plugin is stored in three files. + +### config.yml +Stores common settings + +| Option | Type | Default value | +|----------------------------|-----------------|---------------| +| `hide-join-leave-messages` | `boolean` | `false` | +| `wait-duration` | `int` (seconds) | `15` | + + +### arenas.yml +Stores data for each arena. You may add and remove arenas as you wish. + +Each arena can contain the following locations: + +| Location | Description | +|--------------------------|-------------------------------------------------------------------------------------| +| `game-spawn` **Required* | The location where players will be teleported, and the layers will generate around. | +| `wait-area` | The location where players will be teleported to before the game begins | +| `lobby` | The location where players will be teleported to after the game | +| `winner-lobby` | The location where the winner will be teleported after the game | + +Locations are stored using the following format: +```yaml + location: + x: 0.5 + y: 100 + z: 0.5 + world: worldName +``` + +Each arena can also contain the following option: + +| Option | Type | Description | +|-------------|--------|-----------------------------------------------------------------| +| `kill-at-y` | double | When a player falls below this Y-level, they will be eliminated | + +### language.yml +Most of this plugin's strings are configurable through this file. (Excluding from some console errors) + +All plugin chat messages will have the `prefix` prepended to them. + +Colors can be added using alternate color codes: +``` +&cRed Text +``` -See the comments inside config,yml for manual editing ## Issues & Feedback diff --git a/src/main/java/com/MylesAndMore/Tumble/Main.java b/src/main/java/com/MylesAndMore/Tumble/Main.java index f098a12..6e2123c 100644 --- a/src/main/java/com/MylesAndMore/Tumble/Main.java +++ b/src/main/java/com/MylesAndMore/Tumble/Main.java @@ -18,9 +18,9 @@ public class Main extends JavaPlugin{ @Override public void onEnable() { plugin = this; - new ArenaManager(); - new ConfigManager(); - new LanguageManager(); + ArenaManager.loadConfig(); + ConfigManager.loadConfig(); + LanguageManager.loadConfig(); Objects.requireNonNull(this.getCommand("tumble")).setExecutor(new Tumble()); new Metrics(this, 16940); diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Create.java b/src/main/java/com/MylesAndMore/Tumble/commands/Create.java index 3340cc6..aa39db3 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Create.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Create.java @@ -33,6 +33,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command String arenaName = args[0]; ArenaManager.arenas.put(arenaName, new Arena(arenaName)); + ArenaManager.WriteConfig(); sender.sendMessage(LanguageManager.fromKey("create-success")); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java index c8042bc..905e5da 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java @@ -38,7 +38,17 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command } } else { - game = ArenaManager.arenas.get(args[0]).game; + String arenaName = args[0]; + if (!ArenaManager.arenas.containsKey(arenaName)) { + sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); + return false; + } + game = ArenaManager.arenas.get(arenaName).game; + } + + if (game == null) { + sender.sendMessage(LanguageManager.fromKey("no-game-in-arena")); + return false; } game.gameStart(); diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java index 96e8334..3d98d91 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java @@ -27,7 +27,7 @@ public String getPermission() { } @Override - public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { Game game; if (args.length < 1 || args[0] == null) { @@ -38,7 +38,12 @@ public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNul } } else { - game = ArenaManager.arenas.get(args[0]).game; + String arenaName = args[0]; + if (!ArenaManager.arenas.containsKey(arenaName)) { + sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); + return false; + } + game = ArenaManager.arenas.get(arenaName).game; } if (game == null) { diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Remove.java b/src/main/java/com/MylesAndMore/Tumble/commands/Remove.java index 118aa77..1d28993 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Remove.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Remove.java @@ -38,6 +38,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command } ArenaManager.arenas.remove(arenaName); + ArenaManager.WriteConfig(); sender.sendMessage(LanguageManager.fromKey("set-success")); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetGameSpawn.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetGameSpawn.java index 62d22f8..783434c 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetGameSpawn.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetGameSpawn.java @@ -45,6 +45,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Arena arena = ArenaManager.arenas.get(arenaName); arena.gameSpawn = ((Player)sender).getLocation(); + ArenaManager.WriteConfig(); sender.sendMessage(LanguageManager.fromKey("set-success")); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetKillYCordinate.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetKillYLevel.java similarity index 94% rename from src/main/java/com/MylesAndMore/Tumble/commands/SetKillYCordinate.java rename to src/main/java/com/MylesAndMore/Tumble/commands/SetKillYLevel.java index 0be156f..fec52ea 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetKillYCordinate.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetKillYLevel.java @@ -14,7 +14,7 @@ import java.util.Collections; import java.util.List; -public class SetKillYCordinate implements SubCommand, CommandExecutor, TabCompleter { +public class SetKillYLevel implements SubCommand, CommandExecutor, TabCompleter { @Override public String getCommandName() { @@ -46,6 +46,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Arena arena = ArenaManager.arenas.get(arenaName); arena.killAtY = ((int) ((Player) sender).getLocation().getY()); + ArenaManager.WriteConfig(); sender.sendMessage(LanguageManager.fromKey("set-success")); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetLobby.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetLobby.java index dbb6b53..e50b3e0 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetLobby.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetLobby.java @@ -45,6 +45,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Arena arena = ArenaManager.arenas.get(arenaName); arena.lobby = ((Player)sender).getLocation(); + ArenaManager.WriteConfig(); sender.sendMessage(LanguageManager.fromKey("set-success")); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetWaitArea.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetWaitArea.java index f789658..d251d3f 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetWaitArea.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetWaitArea.java @@ -45,6 +45,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Arena arena = ArenaManager.arenas.get(arenaName); arena.waitArea = ((Player)sender).getLocation(); + ArenaManager.WriteConfig(); sender.sendMessage(LanguageManager.fromKey("set-success")); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLobby.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLobby.java index 01817b0..64a0386 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLobby.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLobby.java @@ -45,6 +45,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Arena arena = ArenaManager.arenas.get(arenaName); arena.winnerLobby = ((Player)sender).getLocation(); + ArenaManager.WriteConfig(); sender.sendMessage(LanguageManager.fromKey("set-success")); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java b/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java index 6e44352..f1d2e38 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java @@ -21,7 +21,7 @@ public class Tumble implements CommandExecutor, TabCompleter { CmdNameAsKey(new Reload()), CmdNameAsKey(new Remove()), CmdNameAsKey(new SetGameSpawn()), - CmdNameAsKey(new SetKillYCordinate()), + CmdNameAsKey(new SetKillYLevel()), CmdNameAsKey(new SetLobby()), CmdNameAsKey(new SetWaitArea()), CmdNameAsKey(new SetWinnerLobby()) diff --git a/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java index 69a6c2c..4d5b782 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java @@ -24,7 +24,7 @@ public class ArenaManager { private static FileConfiguration config; public static HashMap arenas; - public ArenaManager() { + public static void loadConfig() { String fileName = "arenas.yml"; // create config File customConfigFile = new File(plugin.getDataFolder(), fileName); @@ -50,8 +50,6 @@ public ArenaManager() { * Reads config file and populates values above */ public static void readConfig() { - plugin.reloadConfig(); - // arenas ConfigurationSection arenasSection = config.getConfigurationSection("arenas"); if (arenasSection == null) { diff --git a/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java b/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java index ea7414a..9dfce99 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java @@ -10,12 +10,13 @@ public class ConfigManager { public static boolean HideLeaveJoin; public static int waitDuration; - public ConfigManager() { + public static void loadConfig() { config = plugin.getConfig(); + readConfig(); } public static void readConfig() { - HideLeaveJoin = config.getBoolean("hideJoinLeaveMessages", false); + HideLeaveJoin = config.getBoolean("hide-join-leave-messages", 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 index c050d3c..dc49aa5 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java @@ -5,7 +5,6 @@ import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; -import java.awt.*; import java.io.File; import java.io.IOException; @@ -14,7 +13,7 @@ public class LanguageManager { private static FileConfiguration config; - public LanguageManager() { + public static void loadConfig() { String fileName = "language.yml"; // create config File customConfigFile = new File(plugin.getDataFolder(), fileName); diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 97f0a12..ded50ef 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,5 +1,5 @@ # Hides player join/leave messages in public chat -hideJoinLeaveMessages: false +hide-join-leave-messages: false # Duration in seconds to wait for more players to join wait-duration: 15 diff --git a/src/main/resources/language.yml b/src/main/resources/language.yml index fa57b1c..e48023b 100644 --- a/src/main/resources/language.yml +++ b/src/main/resources/language.yml @@ -17,7 +17,7 @@ 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." +reload-success: "&aConfig files reloaded. &eCheck console for errors." remove-success: "&aArena removed." set-success: "&aLocation set." From 3ad28de96cec9a419c04248f412805bfc2d246ca Mon Sep 17 00:00:00 2001 From: Tyler Ferrari <69283684+Sowgro@users.noreply.github.com> Date: Fri, 28 Jun 2024 17:53:25 -0400 Subject: [PATCH 18/51] Update OG_GUIDE.md --- OG_GUIDE.md | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/OG_GUIDE.md b/OG_GUIDE.md index 91dab2e..4ab3d24 100644 --- a/OG_GUIDE.md +++ b/OG_GUIDE.md @@ -6,13 +6,16 @@ In this guide, I'll go over how to set up the Tumble plugin with the original ga ## Steps +1. Download this plugin and [Multiverse-Core](https://www.spigotmc.org/resources/multiverse-core.390/). Place them in your plugins directory 1. Download the worlds and unzip them into your server's worlds directory. - - [Lobby]() - - [Normal Arena]() - - [Festive Arena]() - - [Halloween Arena]() - - [Birthday Arena]() + - [Lobby](https://www.theminecraftarchitect.com/mini-game-maps/2017-mini-game-lobby) (Rename folder to 'lobby' after unzipping) + - [Normal Arena](https://publicfiles.sowgro.net/console-minigame-maps/java/tumble/) + - [Festive Arena](https://publicfiles.sowgro.net/console-minigame-maps/java/tumble/) + - [Halloween Arena](https://publicfiles.sowgro.net/console-minigame-maps/java/tumble/) + - [Birthday Arena](https://publicfiles.sowgro.net/console-minigame-maps/java/tumble/) +Tip: set a specific directory to store your worlds in with the `world-container` setting in `bukkit.yml` +1. Set `level-name` in server.properities to `lobby` 2. Take note of the names of the world folders, we will need this in a moment. 3. Start and join your server. 4. Import your arena worlds. This can be done with the multiverse command `/mv import normal` @@ -26,7 +29,7 @@ In this guide, I'll go over how to set up the Tumble plugin with the original ga x: 0.5 y: 60.0 z: 0.5 - world: tmbl-basic + world: basic lobby: x: -341.5 y: 58 @@ -43,7 +46,7 @@ In this guide, I'll go over how to set up the Tumble plugin with the original ga x: 0.5 y: 60 z: 0.5 - world: tmbl-birthday + world: birthday lobby: x: -341.5 y: 58 @@ -60,7 +63,7 @@ In this guide, I'll go over how to set up the Tumble plugin with the original ga x: 0.5 y: 60 z: 0.5 - world: tmbl-festive + world: festive lobby: x: -341.5 y: 58 @@ -77,7 +80,7 @@ In this guide, I'll go over how to set up the Tumble plugin with the original ga x: 0.5 y: 60 z: -0.5 - world: tmbl-halloween + world: halloween lobby: x: -341.5 y: 58 @@ -94,13 +97,20 @@ In this guide, I'll go over how to set up the Tumble plugin with the original ga 7. Join the game by using `/tmbl join basic Mixed` (swap the arena and game type for whichever one you want to play). -You're done! +You're done! Happy playing! -## Suggestions +## Recommended plugins -With this, the setup for this plugin is complete, but there still may be more for you to do. -There are other plugins out there to fine-tune your experience even more. Plugins like [WorldGuard](https://dev.bukkit.org/projects/worldguard) and [CyberWorldReset](https://www.spigotmc.org/resources/cyberworldreset-standard-%E2%9C%A8-regenerate-worlds-scheduled-resets-lag-optimized%E3%80%8C1-8-1-19%E3%80%8D.96834/) can protect players from breaking blocks in the lobby and reset any redstone they activated, while others like [ViaVersion](https://www.spigotmc.org/resources/viaversion.19254/) can allow you to play Tumble from your favorite Minecraft version (yes, you, 1.8.9 players). +- [WorldGuard](https://dev.bukkit.org/projects/worldguard) and [CyberWorldReset](https://www.spigotmc.org/resources/cyberworldreset-standard-%E2%9C%A8-regenerate-worlds-scheduled-resets-lag-optimized%E3%80%8C1-8-1-19%E3%80%8D.96834/) +Protect players from breaking blocks in the lobby and reset any redstone they activated. + +- [ViaVersion](https://www.spigotmc.org/resources/viaversion.19254/) and [ViaBackwards](https://www.spigotmc.org/resources/viabackwards.27448/) +Allow older and newer clients to connect to your server. + +- [Geyser](https://geysermc.org/download#geyser) and [Floodgate](https://geysermc.org/download#floodgate) +Allow Bedrock clients to connect to your server. + +- [ProtectEnviromnemt](https://www.spigotmc.org/resources/protectenvironment.82736/) +Stop water and lava flow (useful for Halloween map) -Whatever you choose, the experience is up to you. -Happy playing! From 02d16e11b2d854d8a76074fad48b46f28501b5bd Mon Sep 17 00:00:00 2001 From: Tyler Ferrari <69283684+Sowgro@users.noreply.github.com> Date: Fri, 28 Jun 2024 17:54:18 -0400 Subject: [PATCH 19/51] Update OG_GUIDE.md --- OG_GUIDE.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OG_GUIDE.md b/OG_GUIDE.md index 4ab3d24..32b57a8 100644 --- a/OG_GUIDE.md +++ b/OG_GUIDE.md @@ -13,7 +13,8 @@ In this guide, I'll go over how to set up the Tumble plugin with the original ga - [Festive Arena](https://publicfiles.sowgro.net/console-minigame-maps/java/tumble/) - [Halloween Arena](https://publicfiles.sowgro.net/console-minigame-maps/java/tumble/) - [Birthday Arena](https://publicfiles.sowgro.net/console-minigame-maps/java/tumble/) -Tip: set a specific directory to store your worlds in with the `world-container` setting in `bukkit.yml` + + Tip: set a specific directory to store your worlds in with the `world-container` setting in `bukkit.yml` 1. Set `level-name` in server.properities to `lobby` 2. Take note of the names of the world folders, we will need this in a moment. From ef13a2cc5d579c96bd1461c678502cf44a8989ae Mon Sep 17 00:00:00 2001 From: Tyler Ferrari <69283684+Sowgro@users.noreply.github.com> Date: Fri, 28 Jun 2024 18:00:33 -0400 Subject: [PATCH 20/51] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2293d97..aed17a3 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ - [ ] no config validation ### todo -- [ ] finish og-guide +- [x] finish og-guide - [ ] improve inventory saving - [ ] improve Game.leave() method - [ ] perhaps replace spectator mode with survival flight @@ -39,7 +39,7 @@ If you've never heard of it, [Tumble](https://minecraft.wiki/w/tumble) is a twis 1. [Download](https://github.com/MylesAndMore/tmbl/releases) the plugin's JAR file and place it in your server's plugins directory. 2. Place the worlds for your lobby and arenas in your plugins worlds directory. - - If you would like an experience similar to the original game, see [my guide](https://github.com/MylesAndMore/tmbl/blob/main/OG-GUIDE.md) for using the original worlds. + - If you would like an experience similar to the original game, see [my guide](OG_GUIDE.md) for using the original worlds. 3. Start your server. 4. Import your worlds using a plugin like Multiverse. ```/mv import myWorld normal```. From 4a48e1a3a32f9a3b0b72be897377443c65ea55e8 Mon Sep 17 00:00:00 2001 From: sowgro Date: Sat, 29 Jun 2024 23:45:42 -0400 Subject: [PATCH 21/51] Make file writing actually work, reorganize managers --- .../java/com/MylesAndMore/Tumble/Main.java | 9 +- .../MylesAndMore/Tumble/commands/Remove.java | 2 +- .../Tumble/config/ArenaManager.java | 93 ++++++++----------- .../Tumble/config/ConfigManager.java | 10 +- .../Tumble/config/CustomConfig.java | 56 +++++++++++ .../Tumble/config/LanguageManager.java | 30 +----- 6 files changed, 110 insertions(+), 90 deletions(-) create mode 100644 src/main/java/com/MylesAndMore/Tumble/config/CustomConfig.java diff --git a/src/main/java/com/MylesAndMore/Tumble/Main.java b/src/main/java/com/MylesAndMore/Tumble/Main.java index 6e2123c..eff7a10 100644 --- a/src/main/java/com/MylesAndMore/Tumble/Main.java +++ b/src/main/java/com/MylesAndMore/Tumble/Main.java @@ -18,15 +18,14 @@ public class Main extends JavaPlugin{ @Override public void onEnable() { plugin = this; - ArenaManager.loadConfig(); - ConfigManager.loadConfig(); - LanguageManager.loadConfig(); + + LanguageManager.init(); + ArenaManager.init(); + ConfigManager.init(); 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 - Bukkit.getServer().getLogger().info("[Tumble] Tumble successfully enabled!"); } } \ No newline at end of file diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Remove.java b/src/main/java/com/MylesAndMore/Tumble/commands/Remove.java index 1d28993..32ee232 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Remove.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Remove.java @@ -39,7 +39,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command ArenaManager.arenas.remove(arenaName); ArenaManager.WriteConfig(); - sender.sendMessage(LanguageManager.fromKey("set-success")); + sender.sendMessage(LanguageManager.fromKey("remove-success")); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java index 4d5b782..911b02e 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java @@ -7,42 +7,24 @@ 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 arenas; - public static void loadConfig() { - String fileName = "arenas.yml"; - // create config - File customConfigFile = new File(plugin.getDataFolder(), fileName); - if (!customConfigFile.exists()) { - customConfigFile.getParentFile().mkdirs(); - plugin.saveResource(fileName, false); - } + private static final CustomConfig customConfig = new CustomConfig("arenas.yml"); + private static final FileConfiguration config = customConfig.getConfig(); - 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 void init() { + customConfig.saveDefaultConfig(); readConfig(); } @@ -50,6 +32,7 @@ public static void loadConfig() { * Reads config file and populates values above */ public static void readConfig() { + // arenas ConfigurationSection arenasSection = config.getConfigurationSection("arenas"); if (arenasSection == null) { @@ -96,15 +79,43 @@ public static void readConfig() { } } + public static void WriteConfig() { + config.set("arenas", null); // clear everything + + 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); + } + + customConfig.saveConfig(); + + } + + /** + * 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; + } + /** * tries to convert a config section in the following format to a world * section: - * x: - * y: + * x: + * y: * z: * world: * @param section the section in the yaml with x, y, z, and world as its children - * @return result of either: + * @return result of either: * success = true and a world * success = false and an error string */ @@ -134,43 +145,21 @@ private static Result readWorld(@Nullable ConfigurationSection section 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); + private static void WriteWorld(String path, @Nullable Location location) { + if (location == null) { + return; } - plugin.saveConfig(); - - } - - private static void WriteWorld(String path, Location location) { ConfigurationSection section = config.getConfigurationSection(path); if (section == null) { - section = plugin.getConfig().createSection(path); + section = config.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 index 9dfce99..322f949 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java @@ -2,16 +2,15 @@ import org.bukkit.configuration.file.FileConfiguration; -import static com.MylesAndMore.Tumble.Main.plugin; - public class ConfigManager { - private static FileConfiguration config; + private static final CustomConfig customConfig = new CustomConfig("config.yml"); + private static final FileConfiguration config = customConfig.getConfig(); public static boolean HideLeaveJoin; public static int waitDuration; - public static void loadConfig() { - config = plugin.getConfig(); + public static void init() { + customConfig.saveDefaultConfig(); readConfig(); } @@ -20,5 +19,4 @@ public static void readConfig() { waitDuration = config.getInt("wait-duration", 15); } - } diff --git a/src/main/java/com/MylesAndMore/Tumble/config/CustomConfig.java b/src/main/java/com/MylesAndMore/Tumble/config/CustomConfig.java new file mode 100644 index 0000000..20a0aa5 --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/config/CustomConfig.java @@ -0,0 +1,56 @@ +package com.MylesAndMore.Tumble.config; + +import com.google.common.base.Charsets; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.logging.Level; + +import static com.MylesAndMore.Tumble.Main.plugin; + +public class CustomConfig { + private FileConfiguration newConfig = null; + private final File configFile; + private final String fileName; + + public CustomConfig(String fileName) { + this.fileName = fileName; + this.configFile = new File(plugin.getDataFolder(), fileName); + } + + public FileConfiguration getConfig() { + if (newConfig == null) { + reloadConfig(); + } + return newConfig; + } + + public void reloadConfig() { + newConfig = YamlConfiguration.loadConfiguration(configFile); + + final InputStream defConfigStream = plugin.getResource(fileName); + if (defConfigStream == null) { + return; + } + + newConfig.setDefaults(YamlConfiguration.loadConfiguration(new InputStreamReader(defConfigStream, Charsets.UTF_8))); + } + + public void saveConfig() { + try { + getConfig().save(configFile); + } catch (IOException ex) { + plugin.getLogger().log(Level.SEVERE, "Could not save config to " + configFile, ex); + } + } + + public void saveDefaultConfig() { + if (!configFile.exists()) { + plugin.saveResource(fileName, false); + } + } +} diff --git a/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java b/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java index dc49aa5..f214902 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java @@ -1,40 +1,18 @@ 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.io.File; -import java.io.IOException; import static com.MylesAndMore.Tumble.Main.plugin; public class LanguageManager { - private static FileConfiguration config; - - public static void loadConfig() { - String fileName = "language.yml"; - // create config - File customConfigFile = new File(plugin.getDataFolder(), fileName); - if (!customConfigFile.exists()) { - customConfigFile.getParentFile().mkdirs(); - plugin.saveResource(fileName, false); - } + private static final CustomConfig customConfig = new CustomConfig("language.yml"); + private static final FileConfiguration config = customConfig.getConfig(); - 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 void init() { + customConfig.saveDefaultConfig(); } - public static String fromKey(String key) { return fromKeyNoPrefix("prefix") + fromKeyNoPrefix(key); } From e1c1d130937c0e56535f8ab20fa0b2719b759a01 Mon Sep 17 00:00:00 2001 From: sowgro Date: Mon, 1 Jul 2024 01:08:26 -0400 Subject: [PATCH 22/51] config file validation, and non static managers --- README.md | 11 +--- .../java/com/MylesAndMore/Tumble/Main.java | 10 ++- .../MylesAndMore/Tumble/commands/Create.java | 13 ++-- .../Tumble/commands/ForceStart.java | 21 +++--- .../Tumble/commands/ForceStop.java | 21 +++--- .../MylesAndMore/Tumble/commands/Join.java | 29 +++++---- .../MylesAndMore/Tumble/commands/Leave.java | 13 ++-- .../MylesAndMore/Tumble/commands/Reload.java | 8 +-- .../MylesAndMore/Tumble/commands/Remove.java | 19 +++--- .../Tumble/commands/SetGameSpawn.java | 21 +++--- .../Tumble/commands/SetKillYLevel.java | 21 +++--- .../Tumble/commands/SetLobby.java | 21 +++--- .../Tumble/commands/SetWaitArea.java | 21 +++--- .../Tumble/commands/SetWinnerLobby.java | 21 +++--- .../MylesAndMore/Tumble/commands/Tumble.java | 7 +- .../Tumble/config/ArenaManager.java | 49 ++++++++------ .../Tumble/config/ConfigManager.java | 37 ++++++++--- .../Tumble/config/LanguageManager.java | 39 +++++++---- .../Tumble/game/EventListener.java | 7 +- .../com/MylesAndMore/Tumble/game/Game.java | 65 +++++++++++-------- .../{config => plugin}/CustomConfig.java | 2 +- 21 files changed, 258 insertions(+), 198 deletions(-) rename src/main/java/com/MylesAndMore/Tumble/{config => plugin}/CustomConfig.java (97%) diff --git a/README.md b/README.md index aed17a3..9c9f671 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,9 @@ ## temporary stuff (not ready for merge) -### known issues -- [x] file writing not fully implemented (just need to call ArenaManager.write() somewhere) -- [ ] issues with join command -- [ ] no config validation - ### todo -- [x] finish og-guide +- [ ] separate waiting state code +- [ ] fix join command - [ ] improve inventory saving - [ ] improve Game.leave() method -- [ ] perhaps replace spectator mode with survival flight -- [ ] clean up and make config managers uniform in structure -- [ ] improve language flow - [ ] put javadoc comments everywhere # Tumble diff --git a/src/main/java/com/MylesAndMore/Tumble/Main.java b/src/main/java/com/MylesAndMore/Tumble/Main.java index eff7a10..067b839 100644 --- a/src/main/java/com/MylesAndMore/Tumble/Main.java +++ b/src/main/java/com/MylesAndMore/Tumble/Main.java @@ -15,13 +15,17 @@ public class Main extends JavaPlugin{ public static Main plugin; + public static LanguageManager languageManager; + public static ArenaManager arenaManager; + public static ConfigManager configManager; + @Override public void onEnable() { plugin = this; - LanguageManager.init(); - ArenaManager.init(); - ConfigManager.init(); + languageManager = new LanguageManager(); + arenaManager = new ArenaManager(); + configManager = new ConfigManager(); Objects.requireNonNull(this.getCommand("tumble")).setExecutor(new Tumble()); new Metrics(this, 16940); diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Create.java b/src/main/java/com/MylesAndMore/Tumble/commands/Create.java index aa39db3..d21f8a4 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Create.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Create.java @@ -1,8 +1,6 @@ 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; @@ -13,6 +11,9 @@ import java.util.Collections; import java.util.List; +import static com.MylesAndMore.Tumble.Main.arenaManager; +import static com.MylesAndMore.Tumble.Main.languageManager; + public class Create implements SubCommand, CommandExecutor, TabCompleter { @Override public String getCommandName() { @@ -27,14 +28,14 @@ public String getPermission() { @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")); + sender.sendMessage(languageManager.fromKey("missing-arena-parameter")); return false; } String arenaName = args[0]; - ArenaManager.arenas.put(arenaName, new Arena(arenaName)); - ArenaManager.WriteConfig(); - sender.sendMessage(LanguageManager.fromKey("create-success")); + arenaManager.arenas.put(arenaName, new Arena(arenaName)); + arenaManager.WriteConfig(); + sender.sendMessage(languageManager.fromKey("create-success")); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java index 905e5da..2d39c83 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java @@ -1,8 +1,6 @@ package com.MylesAndMore.Tumble.commands; import com.MylesAndMore.Tumble.game.Game; -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; @@ -14,6 +12,9 @@ import java.util.ArrayList; import java.util.List; +import static com.MylesAndMore.Tumble.Main.arenaManager; +import static com.MylesAndMore.Tumble.Main.languageManager; + public class ForceStart implements SubCommand, CommandExecutor, TabCompleter { @Override @@ -31,35 +32,35 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Game game; if (args.length < 1 || args[0] == null) { - game = ArenaManager.findGamePlayerIsIn((Player)sender); + game = arenaManager.findGamePlayerIsIn((Player)sender); if (game == null) { - sender.sendMessage(LanguageManager.fromKey("missing-arena-parameter")); + sender.sendMessage(languageManager.fromKey("missing-arena-parameter")); return false; } } else { String arenaName = args[0]; - if (!ArenaManager.arenas.containsKey(arenaName)) { - sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); + if (!arenaManager.arenas.containsKey(arenaName)) { + sender.sendMessage(languageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); return false; } - game = ArenaManager.arenas.get(arenaName).game; + game = arenaManager.arenas.get(arenaName).game; } if (game == null) { - sender.sendMessage(LanguageManager.fromKey("no-game-in-arena")); + sender.sendMessage(languageManager.fromKey("no-game-in-arena")); return false; } game.gameStart(); - sender.sendMessage(LanguageManager.fromKey("forcestart-success")); + sender.sendMessage(languageManager.fromKey("forcestart-success")); return true; } @Override public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (args.length == 1) { - return ArenaManager.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 3d98d91..5ca856b 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java @@ -1,8 +1,6 @@ package com.MylesAndMore.Tumble.commands; import com.MylesAndMore.Tumble.game.Game; -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; @@ -14,6 +12,9 @@ import java.util.ArrayList; import java.util.List; +import static com.MylesAndMore.Tumble.Main.arenaManager; +import static com.MylesAndMore.Tumble.Main.languageManager; + public class ForceStop implements SubCommand, CommandExecutor, TabCompleter { @Override @@ -31,35 +32,35 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Game game; if (args.length < 1 || args[0] == null) { - game = ArenaManager.findGamePlayerIsIn((Player)sender); + game = arenaManager.findGamePlayerIsIn((Player)sender); if (game == null) { - sender.sendMessage(LanguageManager.fromKey("missing-arena-parameter")); + sender.sendMessage(languageManager.fromKey("missing-arena-parameter")); return false; } } else { String arenaName = args[0]; - if (!ArenaManager.arenas.containsKey(arenaName)) { - sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); + if (!arenaManager.arenas.containsKey(arenaName)) { + sender.sendMessage(languageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); return false; } - game = ArenaManager.arenas.get(arenaName).game; + game = arenaManager.arenas.get(arenaName).game; } if (game == null) { - sender.sendMessage(LanguageManager.fromKey("no-game-in-arena")); + sender.sendMessage(languageManager.fromKey("no-game-in-arena")); return false; } game.gameEnd(); - sender.sendMessage(LanguageManager.fromKey("forcestop-success")); + sender.sendMessage(languageManager.fromKey("forcestop-success")); return true; } @Override public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (args.length == 1) { - return ArenaManager.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 a887c99..3bda96c 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Join.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java @@ -1,8 +1,6 @@ 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.game.Game; import com.MylesAndMore.Tumble.plugin.GameState; import com.MylesAndMore.Tumble.plugin.GameType; @@ -21,6 +19,9 @@ import java.util.Objects; import java.util.stream.Collectors; +import static com.MylesAndMore.Tumble.Main.arenaManager; +import static com.MylesAndMore.Tumble.Main.languageManager; + public class Join implements SubCommand, CommandExecutor, TabCompleter { @Override @@ -37,25 +38,25 @@ public String getPermission() { 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")); + sender.sendMessage(languageManager.fromKey("not-for-console")); return false; } - if (ArenaManager.findGamePlayerIsIn((Player)sender) != null) { - sender.sendMessage(LanguageManager.fromKey("already-in-game")); + if (arenaManager.findGamePlayerIsIn((Player)sender) != null) { + sender.sendMessage(languageManager.fromKey("already-in-game")); } if (args.length < 1 || args[0] == null) { - sender.sendMessage(LanguageManager.fromKey("missing-arena-parameter")); + sender.sendMessage(languageManager.fromKey("missing-arena-parameter")); return false; } String arenaName = args[0]; - if (!ArenaManager.arenas.containsKey(arenaName)) + if (!arenaManager.arenas.containsKey(arenaName)) { - sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%", arenaName)); + sender.sendMessage(languageManager.fromKey("invalid-arena").replace("%arena%", arenaName)); return false; } - Arena arena = ArenaManager.arenas.get(arenaName); + Arena arena = arenaManager.arenas.get(arenaName); Game game; if (args.length < 2 || args[1] == null) { @@ -74,7 +75,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command case "snowballs", "snowball" -> type = GameType.SNOWBALLS; case "mix", "mixed" -> type = GameType.MIXED; default -> { - sender.sendMessage(LanguageManager.fromKey("invalid-type")); + sender.sendMessage(languageManager.fromKey("invalid-type")); return false; } } @@ -88,7 +89,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command game = arena.game; } else { - sender.sendMessage(LanguageManager.fromKey("another-type-in-arena") + sender.sendMessage(languageManager.fromKey("another-type-in-arena") .replace("%type%",type.toString()) .replace("%arena%",arenaName)); return false; @@ -97,12 +98,12 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command } if (game.gameState != GameState.WAITING) { - sender.sendMessage(LanguageManager.fromKey("game-in-progress")); + sender.sendMessage(languageManager.fromKey("game-in-progress")); return false; } game.addPlayer((Player)sender); - sender.sendMessage(LanguageManager.fromKey("join-success") + sender.sendMessage(languageManager.fromKey("join-success") .replace("%type%", game.type.toString()) .replace("%arena%", arena.name)); return true; @@ -111,7 +112,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command @Override public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (args.length == 1) { - return ArenaManager.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 0250cc6..f5a4eeb 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Leave.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Leave.java @@ -1,8 +1,6 @@ package com.MylesAndMore.Tumble.commands; import com.MylesAndMore.Tumble.game.Game; -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; @@ -14,6 +12,9 @@ import java.util.ArrayList; import java.util.List; +import static com.MylesAndMore.Tumble.Main.arenaManager; +import static com.MylesAndMore.Tumble.Main.languageManager; + public class Leave implements SubCommand, CommandExecutor, TabCompleter { @Override @@ -30,18 +31,18 @@ public String getPermission() { 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")); + sender.sendMessage(languageManager.fromKey("not-for-console")); return false; } - Game game = ArenaManager.findGamePlayerIsIn((Player)sender); + Game game = arenaManager.findGamePlayerIsIn((Player)sender); if (game == null) { - sender.sendMessage(LanguageManager.fromKey("no-game-in-arena")); + sender.sendMessage(languageManager.fromKey("no-game-in-arena")); return false; } game.removePlayer((Player) sender); - sender.sendMessage(LanguageManager.fromKey("leave-success") + 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 66535d5..7d6041e 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java @@ -1,8 +1,6 @@ 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; @@ -13,7 +11,7 @@ import java.util.ArrayList; import java.util.List; -import static com.MylesAndMore.Tumble.Main.plugin; +import static com.MylesAndMore.Tumble.Main.*; public class Reload implements SubCommand, CommandExecutor, TabCompleter { @@ -30,14 +28,14 @@ public String getPermission() { @Override public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { - for (Arena a : ArenaManager.arenas.values()) { + for (Arena a : arenaManager.arenas.values()) { if (a.game != null) { a.game.gameEnd(); } } plugin.onEnable(); - sender.sendMessage(LanguageManager.fromKey("reload-success")); + 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 index 32ee232..2d6f9eb 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Remove.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Remove.java @@ -1,7 +1,5 @@ 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; @@ -12,6 +10,9 @@ import java.util.Collections; import java.util.List; +import static com.MylesAndMore.Tumble.Main.arenaManager; +import static com.MylesAndMore.Tumble.Main.languageManager; + public class Remove implements SubCommand, CommandExecutor, TabCompleter { @Override @@ -27,26 +28,26 @@ public String getPermission() { @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")); + 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)); + if (!arenaManager.arenas.containsKey(arenaName)) { + sender.sendMessage(languageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); return false; } - ArenaManager.arenas.remove(arenaName); - ArenaManager.WriteConfig(); - sender.sendMessage(LanguageManager.fromKey("remove-success")); + arenaManager.arenas.remove(arenaName); + arenaManager.WriteConfig(); + sender.sendMessage(languageManager.fromKey("remove-success")); return true; } @Override public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (args.length == 1) { - return ArenaManager.arenas.keySet().stream().toList(); + 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 index 783434c..b98d186 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetGameSpawn.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetGameSpawn.java @@ -1,8 +1,6 @@ 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; @@ -14,6 +12,9 @@ import java.util.Collections; import java.util.List; +import static com.MylesAndMore.Tumble.Main.arenaManager; +import static com.MylesAndMore.Tumble.Main.languageManager; + public class SetGameSpawn implements SubCommand, CommandExecutor, TabCompleter { @Override public String getCommandName() { @@ -28,32 +29,32 @@ public String getPermission() { @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")); + 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")); + 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)); + if (!arenaManager.arenas.containsKey(arenaName)) { + sender.sendMessage(languageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); return false; } - Arena arena = ArenaManager.arenas.get(arenaName); + Arena arena = arenaManager.arenas.get(arenaName); arena.gameSpawn = ((Player)sender).getLocation(); - ArenaManager.WriteConfig(); - sender.sendMessage(LanguageManager.fromKey("set-success")); + arenaManager.WriteConfig(); + sender.sendMessage(languageManager.fromKey("set-success")); return true; } @Override public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (args.length == 1) { - return ArenaManager.arenas.keySet().stream().toList(); + return arenaManager.arenas.keySet().stream().toList(); } return Collections.emptyList(); diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetKillYLevel.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetKillYLevel.java index fec52ea..c6f7891 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetKillYLevel.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetKillYLevel.java @@ -1,8 +1,6 @@ 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; @@ -14,6 +12,9 @@ import java.util.Collections; import java.util.List; +import static com.MylesAndMore.Tumble.Main.arenaManager; +import static com.MylesAndMore.Tumble.Main.languageManager; + public class SetKillYLevel implements SubCommand, CommandExecutor, TabCompleter { @Override @@ -29,32 +30,32 @@ public String getPermission() { @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")); + 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")); + 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)); + if (!arenaManager.arenas.containsKey(arenaName)) { + sender.sendMessage(languageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); return false; } - Arena arena = ArenaManager.arenas.get(arenaName); + Arena arena = arenaManager.arenas.get(arenaName); arena.killAtY = ((int) ((Player) sender).getLocation().getY()); - ArenaManager.WriteConfig(); - sender.sendMessage(LanguageManager.fromKey("set-success")); + arenaManager.WriteConfig(); + sender.sendMessage(languageManager.fromKey("set-success")); return true; } @Override public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (args.length == 1) { - return ArenaManager.arenas.keySet().stream().toList(); + 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 index e50b3e0..85e9fd8 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetLobby.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetLobby.java @@ -1,8 +1,6 @@ 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; @@ -14,6 +12,9 @@ import java.util.Collections; import java.util.List; +import static com.MylesAndMore.Tumble.Main.arenaManager; +import static com.MylesAndMore.Tumble.Main.languageManager; + public class SetLobby implements SubCommand, CommandExecutor, TabCompleter { @Override public String getCommandName() { @@ -28,32 +29,32 @@ public String getPermission() { @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")); + 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")); + 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)); + if (!arenaManager.arenas.containsKey(arenaName)) { + sender.sendMessage(languageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); return false; } - Arena arena = ArenaManager.arenas.get(arenaName); + Arena arena = arenaManager.arenas.get(arenaName); arena.lobby = ((Player)sender).getLocation(); - ArenaManager.WriteConfig(); - sender.sendMessage(LanguageManager.fromKey("set-success")); + arenaManager.WriteConfig(); + sender.sendMessage(languageManager.fromKey("set-success")); return true; } @Override public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (args.length == 1) { - return ArenaManager.arenas.keySet().stream().toList(); + 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 index d251d3f..be97431 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetWaitArea.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetWaitArea.java @@ -1,8 +1,6 @@ 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; @@ -14,6 +12,9 @@ import java.util.Collections; import java.util.List; +import static com.MylesAndMore.Tumble.Main.arenaManager; +import static com.MylesAndMore.Tumble.Main.languageManager; + public class SetWaitArea implements SubCommand, CommandExecutor, TabCompleter { @Override public String getCommandName() { @@ -28,32 +29,32 @@ public String getPermission() { @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")); + 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")); + 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)); + if (!arenaManager.arenas.containsKey(arenaName)) { + sender.sendMessage(languageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); return false; } - Arena arena = ArenaManager.arenas.get(arenaName); + Arena arena = arenaManager.arenas.get(arenaName); arena.waitArea = ((Player)sender).getLocation(); - ArenaManager.WriteConfig(); - sender.sendMessage(LanguageManager.fromKey("set-success")); + arenaManager.WriteConfig(); + sender.sendMessage(languageManager.fromKey("set-success")); return true; } @Override public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (args.length == 1) { - return ArenaManager.arenas.keySet().stream().toList(); + 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 index 64a0386..6e4550f 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLobby.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLobby.java @@ -1,8 +1,6 @@ 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; @@ -14,6 +12,9 @@ import java.util.Collections; import java.util.List; +import static com.MylesAndMore.Tumble.Main.arenaManager; +import static com.MylesAndMore.Tumble.Main.languageManager; + public class SetWinnerLobby implements SubCommand, CommandExecutor, TabCompleter { @Override public String getCommandName() { @@ -28,32 +29,32 @@ public String getPermission() { @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")); + 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")); + 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)); + if (!arenaManager.arenas.containsKey(arenaName)) { + sender.sendMessage(languageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); return false; } - Arena arena = ArenaManager.arenas.get(arenaName); + Arena arena = arenaManager.arenas.get(arenaName); arena.winnerLobby = ((Player)sender).getLocation(); - ArenaManager.WriteConfig(); - sender.sendMessage(LanguageManager.fromKey("set-success")); + arenaManager.WriteConfig(); + sender.sendMessage(languageManager.fromKey("set-success")); return true; } @Override public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (args.length == 1) { - return ArenaManager.arenas.keySet().stream().toList(); + 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 index f1d2e38..2cf5b90 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java @@ -1,6 +1,5 @@ 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; @@ -10,6 +9,8 @@ import java.util.*; +import static com.MylesAndMore.Tumble.Main.languageManager; + public class Tumble implements CommandExecutor, TabCompleter { private static final Map subCommands = Map.ofEntries( @@ -30,14 +31,14 @@ public class Tumble implements CommandExecutor, TabCompleter { @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")); + 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())); + sender.sendMessage(languageManager.fromKey("no-permission").replace("%permission%", subCmd.getPermission())); return false; } diff --git a/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java index 911b02e..aa29cf1 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java @@ -2,6 +2,7 @@ import com.MylesAndMore.Tumble.game.Arena; import com.MylesAndMore.Tumble.game.Game; +import com.MylesAndMore.Tumble.plugin.CustomConfig; import com.MylesAndMore.Tumble.plugin.Result; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -9,6 +10,7 @@ import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.HashMap; @@ -18,20 +20,17 @@ public class ArenaManager { - public static HashMap arenas; + public HashMap arenas; - private static final CustomConfig customConfig = new CustomConfig("arenas.yml"); - private static final FileConfiguration config = customConfig.getConfig(); + private final CustomConfig arenasYml = new CustomConfig("arenas.yml"); + private final FileConfiguration config = arenasYml.getConfig(); - public static void init() { - customConfig.saveDefaultConfig(); + public ArenaManager() { + arenasYml.saveDefaultConfig(); readConfig(); } - /** - * Reads config file and populates values above - */ - public static void readConfig() { + public void readConfig() { // arenas ConfigurationSection arenasSection = config.getConfigurationSection("arenas"); @@ -79,17 +78,28 @@ public static void readConfig() { } } - public static void WriteConfig() { + public void WriteConfig() { config.set("arenas", null); // clear everything 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); + if (arena.killAtY != null) { + config.set("arenas." + arena.name + ".kill-at-y", arena.killAtY); + } + if (arena.gameSpawn != null) { + WriteWorld("arenas." + arena.name + ".game-spawn", arena.gameSpawn); + } + if (arena.lobby != null) { + WriteWorld("arenas." + arena.name + ".lobby", arena.lobby); + } + if (arena.winnerLobby != null) { + WriteWorld("arenas." + arena.name + ".winner-lobby", arena.winnerLobby); + } + if (arena.waitArea != null) { + WriteWorld("arenas." + arena.name + ".wait-area", arena.waitArea); + } } - customConfig.saveConfig(); + arenasYml.saveConfig(); } @@ -98,7 +108,7 @@ public static void WriteConfig() { * @param p Player to search for * @return the game the player is in, or null if not found */ - public static Game findGamePlayerIsIn(Player p) { + public Game findGamePlayerIsIn(Player p) { for (Arena a : arenas.values()) { if (a.game != null && a.game.gamePlayers.contains(p)) { return a.game; @@ -119,7 +129,7 @@ public static Game findGamePlayerIsIn(Player p) { * success = true and a world * success = false and an error string */ - private static Result readWorld(@Nullable ConfigurationSection section) { + private Result readWorld(@Nullable ConfigurationSection section) { if (section == null) { return new Result<>("Section missing from config"); @@ -145,10 +155,7 @@ private static Result readWorld(@Nullable ConfigurationSection section return new Result<>(new Location(world,x,y,z)); } - private static void WriteWorld(String path, @Nullable Location location) { - if (location == null) { - return; - } + private void WriteWorld(String path, @NotNull Location location) { ConfigurationSection section = config.getConfigurationSection(path); diff --git a/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java b/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java index 322f949..b5fbba6 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java @@ -1,20 +1,41 @@ package com.MylesAndMore.Tumble.config; -import org.bukkit.configuration.file.FileConfiguration; +import com.MylesAndMore.Tumble.plugin.CustomConfig; +import org.bukkit.configuration.Configuration; + +import java.util.Objects; + +import static com.MylesAndMore.Tumble.Main.plugin; public class ConfigManager { - private static final CustomConfig customConfig = new CustomConfig("config.yml"); - private static final FileConfiguration config = customConfig.getConfig(); + private final CustomConfig configYml = new CustomConfig("config.yml"); + private final Configuration config = configYml.getConfig(); + private final Configuration defaultConfig = Objects.requireNonNull(config.getDefaults()); + - public static boolean HideLeaveJoin; - public static int waitDuration; + public boolean HideLeaveJoin; + public int waitDuration; - public static void init() { - customConfig.saveDefaultConfig(); + public ConfigManager() { + configYml.saveDefaultConfig(); + validate(); readConfig(); } - public static void readConfig() { + public void validate() { + boolean invalid = false; + for (String key : defaultConfig.getKeys(true)) { + if (!config.contains(key,true)) { + plugin.getLogger().warning("config.yml is missing key '" + key + "'."); + invalid = true; + } + } + if (invalid) { + plugin.getLogger().severe("Errors were found in config.yml, default values will be used."); + } + } + + public void readConfig() { HideLeaveJoin = config.getBoolean("hide-join-leave-messages", 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 index f214902..cd96551 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java @@ -1,27 +1,42 @@ package com.MylesAndMore.Tumble.config; +import com.MylesAndMore.Tumble.plugin.CustomConfig; import org.bukkit.ChatColor; -import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.Configuration; + +import java.util.Objects; import static com.MylesAndMore.Tumble.Main.plugin; public class LanguageManager { - private static final CustomConfig customConfig = new CustomConfig("language.yml"); - private static final FileConfiguration config = customConfig.getConfig(); + private final CustomConfig languageYml = new CustomConfig("language.yml"); + private final Configuration config = languageYml.getConfig(); + private final Configuration defaultConfig = Objects.requireNonNull(config.getDefaults()); + + public LanguageManager() { + languageYml.saveDefaultConfig(); + validate(); + } - public static void init() { - customConfig.saveDefaultConfig(); + public void validate() { + boolean invalid = false; + for (String key : defaultConfig.getKeys(true)) { + if (!config.contains(key,true)) { + plugin.getLogger().warning("language.yml is missing key '" + key + "'."); + invalid = true; + } + } + if (invalid) { + plugin.getLogger().severe("Errors were found in language.yml, default values will be used."); + } } - public static String fromKey(String key) { + public 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); + public String fromKeyNoPrefix(String key) { + String val = config.getString(key, "LANG_ERR"); + return ChatColor.translateAlternateColorCodes('&',val); } } diff --git a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java index 36d02b6..7abf774 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java @@ -2,8 +2,6 @@ import java.util.Objects; -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.*; @@ -22,6 +20,7 @@ import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Nullable; +import static com.MylesAndMore.Tumble.Main.configManager; import static com.MylesAndMore.Tumble.Main.plugin; /** @@ -39,7 +38,7 @@ public EventListener(Game game) { @EventHandler public void PlayerJoinEvent(PlayerJoinEvent event) { // Hide/show join message accordingly - if (ConfigManager.HideLeaveJoin) { + if (configManager.HideLeaveJoin) { event.setJoinMessage(null); } if (event.getPlayer().getWorld() == gameWorld) { @@ -51,7 +50,7 @@ public void PlayerJoinEvent(PlayerJoinEvent event) { @EventHandler public void PlayerQuitEvent(PlayerQuitEvent event) { // Hide/show leave message accordingly - if (ConfigManager.HideLeaveJoin) { + if (configManager.HideLeaveJoin) { event.setQuitMessage(null); } if (event.getPlayer().getWorld() == gameWorld) { diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index dd13b31..5988180 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -1,8 +1,5 @@ package com.MylesAndMore.Tumble.game; -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; @@ -18,7 +15,7 @@ import java.util.*; -import static com.MylesAndMore.Tumble.Main.plugin; +import static com.MylesAndMore.Tumble.Main.*; /** * Everything relating to the Tumble game @@ -62,7 +59,9 @@ public void gameStart() { Bukkit.getServer().getPluginManager().registerEvents(eventListener, plugin); for (Player p : gamePlayers) { - inventories.put(p, p.getInventory().getContents()); + if (!inventories.containsKey(p)) { + inventories.put(p, p.getInventory().getContents()); + } } roundStart(); @@ -115,7 +114,7 @@ private void prepareGameType(GameType type) { gameID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { clearInventories(gamePlayers); giveItems(gamePlayers, new ItemStack(Material.SNOWBALL)); - displayActionbar(gamePlayers, LanguageManager.fromKeyNoPrefix("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); @@ -154,12 +153,12 @@ private void roundEnd() { } // If that player doesn't have three wins, nobody else does, so we need another round else { - displayTitles(gamePlayers, LanguageManager.fromKeyNoPrefix("round-over"), LanguageManager.fromKeyNoPrefix("round-winner").replace("%winner%", winner.getDisplayName()), 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, LanguageManager.fromKeyNoPrefix("round-over"), LanguageManager.fromKeyNoPrefix("round-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); } } @@ -175,9 +174,9 @@ public void gameEnd() { Player winner = getPlayerWithMostWins(gameWins); if (winner != null) { - displayTitles(gamePlayers, LanguageManager.fromKeyNoPrefix("game-over"), LanguageManager.fromKeyNoPrefix("game-winner").replace("%winner%",winner.getDisplayName()), 5, 60, 5); + displayTitles(gamePlayers, languageManager.fromKeyNoPrefix("game-over"), languageManager.fromKeyNoPrefix("game-winner").replace("%winner%",winner.getDisplayName()), 5, 60, 5); } - displayActionbar(gamePlayers, LanguageManager.fromKeyNoPrefix("lobby-in-10")); + displayActionbar(gamePlayers, languageManager.fromKeyNoPrefix("lobby-in-10")); // Wait 10s (200t), then Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { @@ -214,15 +213,28 @@ public void gameEnd() { * @param p Player to remove */ public void removePlayer(Player p) { - gamePlayers.remove(p); - if (gamePlayers.size() < 2) { - gameEnd(); + + if (gameState == GameState.WAITING) { + gamePlayers.remove(p); + if (gamePlayers.size() < 2) { + displayActionbar(gamePlayers, languageManager.fromKeyNoPrefix("waiting-for-players")); + } + + if (arena.waitArea != null) { + p.teleport(arena.lobby); + } } - p.getInventory().clear(); - if (inventories.containsKey(p)) { - p.getInventory().setContents(inventories.get(p)); + else { + gamePlayers.remove(p); + if (gamePlayers.size() < 2) { + gameEnd(); + } + p.getInventory().clear(); + if (inventories.containsKey(p)) { + p.getInventory().setContents(inventories.get(p)); + } + p.teleport(arena.lobby); } - p.teleport(arena.lobby); } /** @@ -234,6 +246,7 @@ public void addPlayer(Player p) { gamePlayers.add(p); // save inventory if (arena.waitArea != null) { + inventories.put(p,p.getInventory().getContents()); p.teleport(arena.waitArea); p.getInventory().clear(); } @@ -241,7 +254,7 @@ public void addPlayer(Player p) { autoStart(); } else { - displayActionbar(Collections.singletonList(p), LanguageManager.fromKeyNoPrefix("waiting-for-players")); + displayActionbar(Collections.singletonList(p), languageManager.fromKeyNoPrefix("waiting-for-players")); } } @@ -250,9 +263,9 @@ public void addPlayer(Player p) { */ public void autoStart() { // Wait for the player to load in - int waitDuration = ConfigManager.waitDuration; + int waitDuration = configManager.waitDuration; Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { - displayActionbar(gamePlayers, LanguageManager.fromKeyNoPrefix("time-till-start").replace("%wait%",waitDuration+"")); + 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); @@ -307,16 +320,16 @@ private void scatterPlayers(List players) { */ private void countdown(Runnable doAfter) { playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1); - displayTitles(gamePlayers, LanguageManager.fromKeyNoPrefix("count-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, LanguageManager.fromKeyNoPrefix("count-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, LanguageManager.fromKeyNoPrefix("count-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, LanguageManager.fromKeyNoPrefix("count-go"), null, 1, 5, 1); + displayTitles(gamePlayers, languageManager.fromKeyNoPrefix("count-go"), null, 1, 5, 1); doAfter.run(); }, 20); }, 20); @@ -405,9 +418,7 @@ private void displayActionbar(List players, String message) { * @param pitch The pitch of the sound */ private void playSound(@NotNull List players, @NotNull Sound sound, @NotNull SoundCategory category, float volume, float pitch) { - for (Player aPlayer : players) { - aPlayer.playSound(aPlayer.getLocation(), sound, category, volume, pitch); - } + players.forEach(player -> player.playSound(player.getLocation(), sound, category, volume, pitch)); } /** diff --git a/src/main/java/com/MylesAndMore/Tumble/config/CustomConfig.java b/src/main/java/com/MylesAndMore/Tumble/plugin/CustomConfig.java similarity index 97% rename from src/main/java/com/MylesAndMore/Tumble/config/CustomConfig.java rename to src/main/java/com/MylesAndMore/Tumble/plugin/CustomConfig.java index 20a0aa5..b77a59d 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/CustomConfig.java +++ b/src/main/java/com/MylesAndMore/Tumble/plugin/CustomConfig.java @@ -1,4 +1,4 @@ -package com.MylesAndMore.Tumble.config; +package com.MylesAndMore.Tumble.plugin; import com.google.common.base.Charsets; import org.bukkit.configuration.file.FileConfiguration; From 3b63c0e4ef7b14fd89d94170e5e6aa683374f0f8 Mon Sep 17 00:00:00 2001 From: sowgro Date: Mon, 1 Jul 2024 03:38:14 -0400 Subject: [PATCH 23/51] Various fixes --- README.md | 8 ----- .../Tumble/commands/ForceStop.java | 6 ++-- .../MylesAndMore/Tumble/commands/Join.java | 13 +++++++-- .../MylesAndMore/Tumble/commands/Reload.java | 2 +- .../Tumble/config/ConfigManager.java | 7 ++--- .../Tumble/config/LanguageManager.java | 11 ++++++- .../com/MylesAndMore/Tumble/game/Game.java | 13 ++++++++- src/main/resources/language.yml | 3 ++ src/main/resources/plugin.yml | 29 ++++++++++++------- 9 files changed, 60 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 9c9f671..d40a481 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,3 @@ -## temporary stuff (not ready for merge) -### todo -- [ ] separate waiting state code -- [ ] fix join command -- [ ] improve inventory saving -- [ ] improve Game.leave() method -- [ ] put javadoc comments everywhere - # Tumble ## Overview diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java index 5ca856b..24f430c 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java @@ -19,12 +19,12 @@ public class ForceStop implements SubCommand, CommandExecutor, TabCompleter { @Override public String getCommandName() { - return "forcestop"; + return "forceStop"; } @Override public String getPermission() { - return "tumble.forcestop"; + return "tumble.forceStop"; } @Override @@ -52,7 +52,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command return false; } - game.gameEnd(); + game.stopGame(); sender.sendMessage(languageManager.fromKey("forcestop-success")); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Join.java b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java index 3bda96c..1dbbad6 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Join.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java @@ -5,7 +5,6 @@ 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; import org.bukkit.command.CommandSender; @@ -37,7 +36,7 @@ public String getPermission() { @Override public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { - if (!(sender instanceof Player)) { + if (!(sender instanceof Player p)) { sender.sendMessage(languageManager.fromKey("not-for-console")); return false; } @@ -61,7 +60,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Game game; if (args.length < 2 || args[1] == null) { if (arena.game == null) { - sender.sendMessage(ChatColor.RED + "no game is currently taking place in this arena, specify the game type to start one"); + sender.sendMessage(languageManager.fromKey("specify-game-type")); return false; } else { @@ -97,6 +96,14 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command } } + if (game.arena.gameSpawn == null) { + if (p.isOp()) { + sender.sendMessage(languageManager.fromKey("arena-not-ready-op")); + } else { + sender.sendMessage(languageManager.fromKey("arena-not-ready")); + } + } + if (game.gameState != GameState.WAITING) { sender.sendMessage(languageManager.fromKey("game-in-progress")); return false; diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java b/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java index 7d6041e..03ce42d 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java @@ -30,7 +30,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command for (Arena a : arenaManager.arenas.values()) { if (a.game != null) { - a.game.gameEnd(); + a.game.stopGame(); } } diff --git a/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java b/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java index b5fbba6..3b56f7f 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java @@ -8,14 +8,13 @@ import static com.MylesAndMore.Tumble.Main.plugin; public class ConfigManager { + public boolean HideLeaveJoin; + public int waitDuration; + private final CustomConfig configYml = new CustomConfig("config.yml"); private final Configuration config = configYml.getConfig(); private final Configuration defaultConfig = Objects.requireNonNull(config.getDefaults()); - - public boolean HideLeaveJoin; - public int waitDuration; - public ConfigManager() { configYml.saveDefaultConfig(); validate(); diff --git a/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java b/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java index cd96551..917ce78 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java @@ -36,7 +36,16 @@ public String fromKey(String key) { } public String fromKeyNoPrefix(String key) { - String val = config.getString(key, "LANG_ERR"); + String val = config.getString(key); + + if (val == null) { + val = defaultConfig.getString(key); + } + + if (val == null) { + val = "LANG_ERR"; + } + return ChatColor.translateAlternateColorCodes('&',val); } } diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index 5988180..20d8869 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -166,7 +166,8 @@ private void roundEnd() { /** * Ends game: Displays overall winner and teleports players to lobby */ - public void gameEnd() { + private void gameEnd() { + if (!gamePlayers.isEmpty()) { setGamemode(gamePlayers, GameMode.SPECTATOR); @@ -207,6 +208,16 @@ public void gameEnd() { arena.game = null; } + public void stopGame() { + gamePlayers.forEach(this::removePlayer); + Bukkit.getServer().getScheduler().cancelTask(gameID); + gameID = -1; + Bukkit.getServer().getScheduler().cancelTask(autoStartID); + autoStartID = -1; + HandlerList.unregisterAll(eventListener); + arena.game = null; + } + /** * Removes a player from the game. * Called when a player leaves the server, or if they issue the leave command diff --git a/src/main/resources/language.yml b/src/main/resources/language.yml index e48023b..7b67114 100644 --- a/src/main/resources/language.yml +++ b/src/main/resources/language.yml @@ -11,6 +11,9 @@ 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." +arena-not-ready: "&4This arena is not finished being set up" +arena-not-ready-op: "&4Incomplete arena. &7Set a game spawn with /tmbl setGameSpawn" +specify-game-type: "&4No game is currently taking place in this arena, provide the game type to start one" create-success: "&aArena created successfully! &eBefore you can join, you must set a game spawn location with /tmbl setGameSpawn" forcestart-success: "&aStarting game." diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index ed246c5..feac687 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -10,9 +10,8 @@ softdepend: [Multiverse-Core] commands: tumble: - description: Joins a Tumble match. - usage: '§cUsage: /tumble' - permission: tumble.join + description: Base command for Rumble + usage: "/tmbl ..." aliases: tmbl permissions: @@ -22,16 +21,24 @@ permissions: tumble.leave: description: Allows you to leave a Tumble match. default: true - tumble.forcestart: - description: Allows you to force start a Tumble match. + tumble.forceStart: default: op - tumble.forcestop: - description: Allows you to force stop a Tumble match. - default: op - tumble.config: - description: Allows you to get a notification if Tumble is out of date. + tumble.forceStop: default: op tumble.reload: - description: Allows you to reload the plugin's config. + default: op + tumble.create: + default: op + tumble.remove: + default: op + tumble.setGameSpawn: + default: op + tumble.setKillYLevel: + default: op + tumble.setLobby: + default: op + tumble.setWaitArea: + default: op + tumble.setWinnerLobby: default: op From ec048c36764120a0f912a775474d5a028f48d9e3 Mon Sep 17 00:00:00 2001 From: sowgro Date: Tue, 2 Jul 2024 01:55:07 -0400 Subject: [PATCH 24/51] Add javadoc comments and other code cleanup --- .../Tumble/commands/ForceStart.java | 1 + .../Tumble/commands/ForceStop.java | 1 + .../MylesAndMore/Tumble/commands/Join.java | 4 + .../MylesAndMore/Tumble/commands/Tumble.java | 17 ++- .../Tumble/config/ArenaManager.java | 32 +++- .../Tumble/config/ConfigManager.java | 12 ++ .../Tumble/config/LanguageManager.java | 21 +++ .../Tumble/game/EventListener.java | 8 +- .../com/MylesAndMore/Tumble/game/Game.java | 137 +++++++++++------- .../Tumble/plugin/CustomConfig.java | 8 + .../Tumble/plugin/SubCommand.java | 8 +- 11 files changed, 187 insertions(+), 62 deletions(-) diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java index 2d39c83..bf130ea 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java @@ -32,6 +32,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Game game; if (args.length < 1 || args[0] == null) { + // no arena passed in, try to infer from game player is in game = arenaManager.findGamePlayerIsIn((Player)sender); if (game == null) { sender.sendMessage(languageManager.fromKey("missing-arena-parameter")); diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java index 24f430c..8ab48c2 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java @@ -32,6 +32,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Game game; if (args.length < 1 || args[0] == null) { + // no arena passed in, try to infer from game player is in game = arenaManager.findGamePlayerIsIn((Player)sender); if (game == null) { sender.sendMessage(languageManager.fromKey("missing-arena-parameter")); diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Join.java b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java index 1dbbad6..3177f3e 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Join.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java @@ -59,6 +59,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Game game; if (args.length < 2 || args[1] == null) { + // try to infer game type from game taking place in the arena if (arena.game == null) { sender.sendMessage(languageManager.fromKey("specify-game-type")); return false; @@ -80,10 +81,12 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command } if (arena.game == null) { + // no game is taking place in this arena, start one game = arena.game = new Game(arena, type); } else { + // a game is taking place in this arena, check that it is the right type if (arena.game.type == type) { game = arena.game; } @@ -96,6 +99,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command } } + // check to make sure the arena has a game spawn if (game.arena.gameSpawn == null) { if (p.isOp()) { sender.sendMessage(languageManager.fromKey("arena-not-ready-op")); diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java b/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java index 2cf5b90..67213ee 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java @@ -42,6 +42,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command return false; } + // pass command action through to subCommand subCmd.onCommand(sender, command, args[0], removeFirst(args)); return true; } @@ -49,6 +50,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command @Override public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (args.length == 1) { + // show only subCommands the user has permission for ArrayList PermittedSubCmds = new ArrayList<>(); for (SubCommand subCmd: subCommands.values()) { if (sender.hasPermission(subCmd.getPermission())) { @@ -63,6 +65,7 @@ public List onTabComplete(@NotNull CommandSender sender, @NotNull Comman return Collections.emptyList(); } + // pass tab complete through to subCommand if (subCommands.get(args[0]) instanceof TabCompleter tcmp) { return tcmp.onTabComplete(sender, command, args[0], removeFirst(args)); } @@ -74,13 +77,23 @@ public List onTabComplete(@NotNull CommandSender sender, @NotNull Comman return Collections.emptyList(); } + /** + * Create a copy of an array with the first element removed + * @param arr the source array + * @return the source without the first element + */ private String[] removeFirst(String[] arr) { ArrayList tmp = new ArrayList<>(List.of(arr)); tmp.remove(0); return tmp.toArray(new String[0]); } - private static Map.Entry CmdNameAsKey(SubCommand s) { - return Map.entry(s.getCommandName(),s); + /** + * Creates a map entry with the name of the subCommand as the key and the subCommand itself as the value + * @param cmd The subCommand to use + * @return A map entry from the subCommand + */ + private static Map.Entry CmdNameAsKey(SubCommand cmd) { + return Map.entry(cmd.getCommandName(),cmd); } } diff --git a/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java index aa29cf1..b4364d3 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java @@ -18,6 +18,9 @@ import static com.MylesAndMore.Tumble.Main.plugin; +/** + * Manages arenas.yml and stores list of arenas + */ public class ArenaManager { public HashMap arenas; @@ -25,11 +28,17 @@ public class ArenaManager { private final CustomConfig arenasYml = new CustomConfig("arenas.yml"); private final FileConfiguration config = arenasYml.getConfig(); + /** + * Create an ArenaManager + */ public ArenaManager() { arenasYml.saveDefaultConfig(); readConfig(); } + /** + * Read arenas from arenas.ynl and populate this.arenas + */ public void readConfig() { // arenas @@ -78,6 +87,9 @@ public void readConfig() { } } + /** + * Write arenas from this.arenas to arenas.yml + */ public void WriteConfig() { config.set("arenas", null); // clear everything @@ -118,16 +130,16 @@ public Game findGamePlayerIsIn(Player p) { } /** - * tries to convert a config section in the following format to a world + * 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 + * @param section The section in the yaml with x, y, z, and world as its children + * @return Result of either: + * Result#success = true and Result#value OR + * Result#success = false and Result#error */ private Result readWorld(@Nullable ConfigurationSection section) { @@ -155,6 +167,16 @@ private Result readWorld(@Nullable ConfigurationSection section) { return new Result<>(new Location(world,x,y,z)); } + /** + * Write a location into the config using the following format: + * section: + * x: + * y: + * z: + * world: + * @param path The path of the section to write + * @param location The location to write + */ private void WriteWorld(String path, @NotNull Location location) { ConfigurationSection section = config.getConfigurationSection(path); diff --git a/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java b/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java index 3b56f7f..13cc779 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java @@ -7,6 +7,9 @@ import static com.MylesAndMore.Tumble.Main.plugin; +/** + * Manages config.yml and stores its options + */ public class ConfigManager { public boolean HideLeaveJoin; public int waitDuration; @@ -15,12 +18,18 @@ public class ConfigManager { private final Configuration config = configYml.getConfig(); private final Configuration defaultConfig = Objects.requireNonNull(config.getDefaults()); + /** + * Create a config manager + */ public ConfigManager() { configYml.saveDefaultConfig(); validate(); readConfig(); } + /** + * Check keys of config.yml against the defaults + */ public void validate() { boolean invalid = false; for (String key : defaultConfig.getKeys(true)) { @@ -34,6 +43,9 @@ public void validate() { } } + /** + * Reads options in from config.yml + */ public void readConfig() { HideLeaveJoin = config.getBoolean("hide-join-leave-messages", 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 index 917ce78..7c82664 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java @@ -8,16 +8,25 @@ import static com.MylesAndMore.Tumble.Main.plugin; +/** + * Manages language.yml and allows retrieval of keys + */ public class LanguageManager { private final CustomConfig languageYml = new CustomConfig("language.yml"); private final Configuration config = languageYml.getConfig(); private final Configuration defaultConfig = Objects.requireNonNull(config.getDefaults()); + /** + * Creates a new LanguageManager + */ public LanguageManager() { languageYml.saveDefaultConfig(); validate(); } + /** + * Check keys of language.yml against the defaults + */ public void validate() { boolean invalid = false; for (String key : defaultConfig.getKeys(true)) { @@ -31,10 +40,22 @@ public void validate() { } } + /** + * Gets a key from language.yml and prepends the prefix. + * If it is not present, a default value will be returned + * @param key The key representing the message + * @return The message from the key + */ public String fromKey(String key) { return fromKeyNoPrefix("prefix") + fromKeyNoPrefix(key); } + /** + * Gets a key from language.yml. + * If it is not present, a default value will be returned + * @param key The key representing the message + * @return The message from the key + */ public String fromKeyNoPrefix(String key) { String val = config.getString(key); diff --git a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java index 7abf774..1bc31b3 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java @@ -24,12 +24,16 @@ import static com.MylesAndMore.Tumble.Main.plugin; /** - * Tumble event listener for all plugin and game-related events. + * An event listener for a game of tumble. */ public class EventListener implements Listener { - World gameWorld; Game game; + + /** + * Create a new EventListener + * @param game The game that the EventListener belongs to. + */ public EventListener(Game game) { this.game = game; this.gameWorld = game.arena.gameSpawn.getWorld(); diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index 20d8869..169ce33 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -35,15 +35,41 @@ public class Game { private List playersAlive; private EventListener eventListener; + /** + * Create a new Game + * @param arena The arena the game is taking place in + * @param type The game type + */ public Game(@NotNull Arena arena, @NotNull GameType type) { this.arena = arena; this.type = type; this.gameSpawn = arena.gameSpawn; + } + + /** + * Adds a player to the wait area. Called from /tmbl join + * Precondition: the game is in state WAITING + * @param p Player to add + */ + public void addPlayer(Player p) { + gamePlayers.add(p); + if (arena.waitArea != null) { + inventories.put(p,p.getInventory().getContents()); + p.teleport(arena.waitArea); + p.getInventory().clear(); + } + if (gamePlayers.size() >= 2 && gameState == GameState.WAITING) { + autoStart(); + } + else { + displayActionbar(Collections.singletonList(p), languageManager.fromKeyNoPrefix("waiting-for-players")); + } } /** - * Creates a new Game + * Starts the game + * Called from /tmbl forceStart or after the wait counter finishes */ public void gameStart() { @@ -52,12 +78,15 @@ public void gameStart() { return; } + // cancel wait timer Bukkit.getServer().getScheduler().cancelTask(autoStartID); autoStartID = -1; + // register event listener eventListener = new EventListener(this); Bukkit.getServer().getPluginManager().registerEvents(eventListener, plugin); + // save inventories (if not already done) for (Player p : gamePlayers) { if (!inventories.containsKey(p)) { inventories.put(p, p.getInventory().getContents()); @@ -68,20 +97,26 @@ public void gameStart() { } /** - * Starts a new round + * Starts a round */ private void roundStart() { gameState = GameState.STARTING; playersAlive = new ArrayList<>(gamePlayers); + scatterPlayers(gamePlayers); // Put all players in spectator to prevent them from getting kicked for flying setGamemode(gamePlayers, GameMode.SPECTATOR); - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> setGamemode(gamePlayers, GameMode.SPECTATOR), 10); + // do it again in case they were not in the world yet + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { + setGamemode(gamePlayers, GameMode.SPECTATOR); + }, 10); + clearInventories(gamePlayers); clearArena(); prepareGameType(type); + + // Begin the countdown sequence Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { - // Begin the countdown sequence countdown(() -> { setGamemode(gamePlayers, GameMode.SURVIVAL); gameState = GameState.RUNNING; @@ -91,42 +126,45 @@ private void roundStart() { /** * Type specific setup: Generating layers and giving items - * @param type can be either "shovels", "snowballs", or "mixed" + * @param type game type, */ private void prepareGameType(GameType type) { - roundType = type; // note: may need deepcopy this for it to work properly - if (roundType.equals(GameType.MIXED)) { - // Randomly select either shovels or snowballs and re-run the method - Random random = new Random(); - switch (random.nextInt(2)) { - case 0 -> roundType = GameType.SHOVELS; - case 1 -> roundType = GameType.SNOWBALLS; - } - } - - switch (roundType) { + roundType = type; + switch (type) { case SHOVELS -> { Generator.generateLayersShovels(gameSpawn.clone()); + ItemStack shovel = new ItemStack(Material.IRON_SHOVEL); shovel.addEnchantment(Enchantment.SILK_TOUCH, 1); giveItems(gamePlayers, shovel); - // Schedule a process to give snowballs after 2m30s (so people can't island, the OG game had this); add 160t because of the countdown + + // Schedule a process to give snowballs after 2m30s (so people can't island, the OG game had this); + // add 160t because of the countdown gameID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { clearInventories(gamePlayers); giveItems(gamePlayers, new ItemStack(Material.SNOWBALL)); 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); }, 3160); } case SNOWBALLS -> { Generator.generateLayersSnowballs(gameSpawn.clone()); + giveItems(gamePlayers, new ItemStack(Material.SNOWBALL)); // End the round in 5m gameID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, this::roundEnd, 6160); } + case MIXED -> { + Random random = new Random(); + switch (random.nextInt(2)) { + case 0 -> prepareGameType(GameType.SHOVELS); + case 1 -> prepareGameType(GameType.SNOWBALLS); + } + } } } @@ -138,8 +176,9 @@ private void roundEnd() { gameState = GameState.ENDING; Bukkit.getServer().getScheduler().cancelTask(gameID); gameID = -1; - // Clear old layers (as a fill command, this would be /fill ~-20 ~-20 ~-20 ~20 ~ ~20 relative to spawn) + playSound(gamePlayers, Sound.BLOCK_NOTE_BLOCK_PLING, SoundCategory.BLOCKS, 5, 0); + // Check if there was a definite winner or not if (!playersAlive.isEmpty()) { Player winner = playersAlive.get(0); @@ -148,11 +187,11 @@ private void roundEnd() { gameWins.put(winner, 0); } gameWins.put(winner, gameWins.get(winner)+1); + if (gameWins.get(winner) == 3) { gameEnd(); } - // If that player doesn't have three wins, nobody else does, so we need another round - else { + else { // If that player doesn't have three wins, nobody else does, so we need another round 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); } @@ -173,33 +212,35 @@ private void gameEnd() { setGamemode(gamePlayers, GameMode.SPECTATOR); clearInventories(gamePlayers); + // display winner Player winner = getPlayerWithMostWins(gameWins); if (winner != null) { displayTitles(gamePlayers, languageManager.fromKeyNoPrefix("game-over"), languageManager.fromKeyNoPrefix("game-winner").replace("%winner%",winner.getDisplayName()), 5, 60, 5); } + displayActionbar(gamePlayers, languageManager.fromKeyNoPrefix("lobby-in-10")); // Wait 10s (200t), then Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { - clearArena(); + // teleport player back and restore inventory for (Player p : gamePlayers) { - // Restore inventories - if (inventories.containsKey(p)) { - p.getInventory().setContents(inventories.get(p)); - } - if (p == winner && arena.winnerLobby != null) { p.teleport(arena.winnerLobby); } else { p.teleport(Objects.requireNonNull(arena.lobby)); } + + if (inventories.containsKey(p)) { + p.getInventory().setContents(inventories.get(p)); + } } }, 200); } + Bukkit.getServer().getScheduler().cancelTask(gameID); gameID = -1; Bukkit.getServer().getScheduler().cancelTask(autoStartID); @@ -208,8 +249,13 @@ private void gameEnd() { arena.game = null; } + /** + * Stops the game, usually while it is still going + * called if too many players leave, or from /tmbl forceStop + */ public void stopGame() { gamePlayers.forEach(this::removePlayer); + Bukkit.getServer().getScheduler().cancelTask(gameID); gameID = -1; Bukkit.getServer().getScheduler().cancelTask(autoStartID); @@ -224,48 +270,37 @@ public void stopGame() { * @param p Player to remove */ public void removePlayer(Player p) { + gamePlayers.remove(p); + // check if the game has not started yet if (gameState == GameState.WAITING) { - gamePlayers.remove(p); + + // inform player that there are no longer enough players to start if (gamePlayers.size() < 2) { displayActionbar(gamePlayers, languageManager.fromKeyNoPrefix("waiting-for-players")); } + // teleport player back and restore inventory if (arena.waitArea != null) { + p.getInventory().clear(); p.teleport(arena.lobby); + if (inventories.containsKey(p)) { + p.getInventory().setContents(inventories.get(p)); + } } } else { - gamePlayers.remove(p); + // stop the game if there are not enough players if (gamePlayers.size() < 2) { - gameEnd(); + stopGame(); } + + // teleport player back and restore inventory p.getInventory().clear(); + p.teleport(arena.lobby); if (inventories.containsKey(p)) { p.getInventory().setContents(inventories.get(p)); } - p.teleport(arena.lobby); - } - } - - /** - * Adds a player to the wait area. Called from /tumble-join - * Precondition: the game is in state WAITING - * @param p Player to add - */ - public void addPlayer(Player p) { - gamePlayers.add(p); - // save inventory - if (arena.waitArea != null) { - inventories.put(p,p.getInventory().getContents()); - p.teleport(arena.waitArea); - p.getInventory().clear(); - } - if (gamePlayers.size() >= 2 && gameState == GameState.WAITING) { - autoStart(); - } - else { - displayActionbar(Collections.singletonList(p), languageManager.fromKeyNoPrefix("waiting-for-players")); } } diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/CustomConfig.java b/src/main/java/com/MylesAndMore/Tumble/plugin/CustomConfig.java index b77a59d..8ef4638 100644 --- a/src/main/java/com/MylesAndMore/Tumble/plugin/CustomConfig.java +++ b/src/main/java/com/MylesAndMore/Tumble/plugin/CustomConfig.java @@ -12,11 +12,19 @@ import static com.MylesAndMore.Tumble.Main.plugin; +/** + * Allows additional configs to be created with the same saving methods as the default config + * Most code is copied from {@link org.bukkit.plugin.java.JavaPlugin} + */ public class CustomConfig { private FileConfiguration newConfig = null; private final File configFile; private final String fileName; + /** + * Create a new CustomConfig + * @param fileName Name of the YAML file to create + */ public CustomConfig(String fileName) { this.fileName = fileName; this.configFile = new File(plugin.getDataFolder(), fileName); diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/SubCommand.java b/src/main/java/com/MylesAndMore/Tumble/plugin/SubCommand.java index 2158584..cc09527 100644 --- a/src/main/java/com/MylesAndMore/Tumble/plugin/SubCommand.java +++ b/src/main/java/com/MylesAndMore/Tumble/plugin/SubCommand.java @@ -2,7 +2,11 @@ import org.bukkit.command.CommandExecutor; +/** + * Requires that subCommands have a commandName and permission getter. + * This allows the permission and commandName to be checked from the base command. + */ public interface SubCommand extends CommandExecutor { - public String getCommandName(); - public String getPermission(); + String getCommandName(); + String getPermission(); } From ce0445896f5a175446e027e568d5232b11780e53 Mon Sep 17 00:00:00 2001 From: sowgro Date: Tue, 2 Jul 2024 02:07:28 -0400 Subject: [PATCH 25/51] remove camelcase command names due to issues with autocomplete --- OG_GUIDE.md | 16 ++++++++-------- README.md | 14 ++++++++------ .../MylesAndMore/Tumble/commands/ForceStart.java | 4 ++-- .../MylesAndMore/Tumble/commands/ForceStop.java | 4 ++-- .../Tumble/commands/SetGameSpawn.java | 4 ++-- .../Tumble/commands/SetKillYLevel.java | 4 ++-- .../MylesAndMore/Tumble/commands/SetLobby.java | 4 ++-- .../Tumble/commands/SetWaitArea.java | 4 ++-- .../Tumble/commands/SetWinnerLobby.java | 4 ++-- src/main/resources/plugin.yml | 14 +++++++------- 10 files changed, 37 insertions(+), 35 deletions(-) diff --git a/OG_GUIDE.md b/OG_GUIDE.md index 32b57a8..1ddf924 100644 --- a/OG_GUIDE.md +++ b/OG_GUIDE.md @@ -7,7 +7,7 @@ In this guide, I'll go over how to set up the Tumble plugin with the original ga ## Steps 1. Download this plugin and [Multiverse-Core](https://www.spigotmc.org/resources/multiverse-core.390/). Place them in your plugins directory -1. Download the worlds and unzip them into your server's worlds directory. +2. Download the worlds and unzip them into your server's worlds directory. - [Lobby](https://www.theminecraftarchitect.com/mini-game-maps/2017-mini-game-lobby) (Rename folder to 'lobby' after unzipping) - [Normal Arena](https://publicfiles.sowgro.net/console-minigame-maps/java/tumble/) - [Festive Arena](https://publicfiles.sowgro.net/console-minigame-maps/java/tumble/) @@ -16,12 +16,12 @@ In this guide, I'll go over how to set up the Tumble plugin with the original ga Tip: set a specific directory to store your worlds in with the `world-container` setting in `bukkit.yml` -1. Set `level-name` in server.properities to `lobby` -2. Take note of the names of the world folders, we will need this in a moment. -3. Start and join your server. -4. Import your arena worlds. This can be done with the multiverse command `/mv import normal` +3. Set `level-name` in server.properities to `lobby` +4. Take note of the names of the world folders, we will need this in a moment. +5. Start and join your server. +6. Import your arena worlds. This can be done with the multiverse command `/mv import normal` -5. Paste the arena config below into `plugins/tumble/arenas.yml`: +7. Paste the arena config below into `plugins/tumble/arenas.yml`: ```yaml arenas: basic: @@ -93,9 +93,9 @@ In this guide, I'll go over how to set up the Tumble plugin with the original ga Z: -340.5 world: lobby ``` -6. Reload the plugin with `/tmbl reload`. +8. Reload the plugin with `/tmbl reload`. -7. Join the game by using `/tmbl join basic Mixed` +9. Join the game using `/tmbl join basic mixed` (swap the arena and game type for whichever one you want to play). You're done! Happy playing! diff --git a/README.md b/README.md index d40a481..5a83f02 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ Tumble is a Spigot/Paper plugin that aims to recreate the Tumble minigame from t ## What *is* Tumble? -If you've never heard of it, [Tumble](https://minecraft.wiki/w/tumble) is a twist on the classic Minecraft minigame of spleef, where the objective is to break the blocks under your opponents. But in Tumble, you play on randomly generated layers of blocks, using shovels, snowballs, or both to try and eliminate your opponents. +If you've never heard of it, [Tumble](https://minecraft.wiki/w/tumble) is a twist on the classic Minecraft minigame of spleef, where the objective is to break the blocks under your opponents. +But in Tumble, you play on randomly generated layers of blocks, using shovels, snowballs, or both to try and eliminate your opponents. ## Features @@ -29,6 +30,7 @@ If you've never heard of it, [Tumble](https://minecraft.wiki/w/tumble) is a twis 3. Start your server. 4. Import your worlds using a plugin like Multiverse. ```/mv import myWorld normal```. 5. Create your first arena `/tmbl create myArena` +6. Set the spawn point of the arena `/tmbl setgamespawn myArena` - **Note**: The layers will generate relative to this location. Ensure that the area is clear, 20 blocks in each direction. 7. You're done! You can now join the game ```/tmbl join myArena mixed```. @@ -46,11 +48,11 @@ Scroll down for more options to configure your game. | `/tmbl reload` | Reload the plugin's configs. | `tumble.reload` | | `/tmbl create ` | Create a new arena | `tumble.create` | | `/tmbl remove ` | Remove an arena | `tumble.remove` | -| `/tmbl setGameSpawn ` | Set game spawn to your current position | `tumble.setGameSpawn` | -| `/tmbl setKillYLevel ` | Set the arena's Y-level to kill players at to current Y coordinate | `tumble.setKillYLevel` | -| `/tmbl setLobby ` | Set the arena's lobby to current location | `tumble.setLobby` | -| `/tmbl setWaitArea ` | Set the arena's wait area to the current location | `tumble.setWaitArea` | -| `/tmbl setWinnerLobby ` | Set the arena's lobby to the current location | `tumble.setWinnerLobby` | +| `/tmbl setgamespawn ` | Set game spawn to your current position | `tumble.setgamespawn` | +| `/tmbl setkillylevel ` | Set the arena's Y-level to kill players at to current Y coordinate | `tumble.setkillylevel` | +| `/tmbl setlobby ` | Set the arena's lobby to current location | `tumble.setlobby` | +| `/tmbl setwaitarea ` | Set the arena's wait area to the current location | `tumble.setwaitarea` | +| `/tmbl setwinnerlobby ` | Set the arena's lobby to the current location | `tumble.setwinnerlobby` | ## Configuration diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java index bf130ea..d05155d 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java @@ -19,12 +19,12 @@ public class ForceStart implements SubCommand, CommandExecutor, TabCompleter { @Override public String getCommandName() { - return "forceStart"; + return "forcestart"; } @Override public String getPermission() { - return "tumble.forceStart"; + return "tumble.forcestart"; } @Override diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java index 8ab48c2..a69779a 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java @@ -19,12 +19,12 @@ public class ForceStop implements SubCommand, CommandExecutor, TabCompleter { @Override public String getCommandName() { - return "forceStop"; + return "forcestop"; } @Override public String getPermission() { - return "tumble.forceStop"; + return "tumble.forcestop"; } @Override diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetGameSpawn.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetGameSpawn.java index b98d186..7711de9 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetGameSpawn.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetGameSpawn.java @@ -18,12 +18,12 @@ public class SetGameSpawn implements SubCommand, CommandExecutor, TabCompleter { @Override public String getCommandName() { - return "setGameSpawn"; + return "setgamespawn"; } @Override public String getPermission() { - return "tumble.setGameSpawn"; + return "tumble.setgamespawn"; } @Override diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetKillYLevel.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetKillYLevel.java index c6f7891..6e2a59e 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetKillYLevel.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetKillYLevel.java @@ -19,12 +19,12 @@ public class SetKillYLevel implements SubCommand, CommandExecutor, TabCompleter @Override public String getCommandName() { - return "setKillYLevel"; + return "setkillylevel"; } @Override public String getPermission() { - return "tumble.setKillYLevel"; + return "tumble.setkillylevel"; } @Override diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetLobby.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetLobby.java index 85e9fd8..3dedf50 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetLobby.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetLobby.java @@ -18,12 +18,12 @@ public class SetLobby implements SubCommand, CommandExecutor, TabCompleter { @Override public String getCommandName() { - return "setLobby"; + return "setlobby"; } @Override public String getPermission() { - return "tumble.setLobby"; + return "tumble.setlobby"; } @Override diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetWaitArea.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetWaitArea.java index be97431..d00a0cb 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetWaitArea.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetWaitArea.java @@ -18,12 +18,12 @@ public class SetWaitArea implements SubCommand, CommandExecutor, TabCompleter { @Override public String getCommandName() { - return "setWaitArea"; + return "setwaitarea"; } @Override public String getPermission() { - return "tumble.setWaitArea"; + return "tumble.setwaitarea"; } @Override diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLobby.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLobby.java index 6e4550f..f40344e 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLobby.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLobby.java @@ -18,12 +18,12 @@ public class SetWinnerLobby implements SubCommand, CommandExecutor, TabCompleter { @Override public String getCommandName() { - return "setWinnerLobby"; + return "setwinnerlobby"; } @Override public String getPermission() { - return "tumble.setWinnerLobby"; + return "tumble.setwinnerlobby"; } @Override diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index feac687..c8085b8 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -21,9 +21,9 @@ permissions: tumble.leave: description: Allows you to leave a Tumble match. default: true - tumble.forceStart: + tumble.forcestart: default: op - tumble.forceStop: + tumble.forcestop: default: op tumble.reload: default: op @@ -31,14 +31,14 @@ permissions: default: op tumble.remove: default: op - tumble.setGameSpawn: + tumble.setgamespawn: default: op - tumble.setKillYLevel: + tumble.setkillylevel: default: op - tumble.setLobby: + tumble.setlobby: default: op - tumble.setWaitArea: + tumble.setwaitarea: default: op - tumble.setWinnerLobby: + tumble.setwinnerlobby: default: op From eb36fc31873f1b2479b11d9f2612c15df4082d1b Mon Sep 17 00:00:00 2001 From: sowgro Date: Tue, 2 Jul 2024 23:21:42 -0400 Subject: [PATCH 26/51] Clean up arena reading code --- .../java/com/MylesAndMore/Tumble/Main.java | 2 +- .../MylesAndMore/Tumble/commands/Join.java | 1 + .../Tumble/config/ArenaManager.java | 67 +++++++++---------- .../com/MylesAndMore/Tumble/game/Game.java | 2 +- .../MylesAndMore/Tumble/plugin/Result.java | 20 ------ src/main/resources/language.yml | 18 ++--- 6 files changed, 43 insertions(+), 67 deletions(-) delete mode 100644 src/main/java/com/MylesAndMore/Tumble/plugin/Result.java diff --git a/src/main/java/com/MylesAndMore/Tumble/Main.java b/src/main/java/com/MylesAndMore/Tumble/Main.java index 067b839..e4c9452 100644 --- a/src/main/java/com/MylesAndMore/Tumble/Main.java +++ b/src/main/java/com/MylesAndMore/Tumble/Main.java @@ -24,8 +24,8 @@ public void onEnable() { plugin = this; languageManager = new LanguageManager(); - arenaManager = new ArenaManager(); configManager = new ConfigManager(); + arenaManager = new ArenaManager(); Objects.requireNonNull(this.getCommand("tumble")).setExecutor(new Tumble()); new Metrics(this, 16940); diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Join.java b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java index 3177f3e..3989960 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Join.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java @@ -106,6 +106,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command } else { sender.sendMessage(languageManager.fromKey("arena-not-ready")); } + return false; } if (game.gameState != GameState.WAITING) { diff --git a/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java index b4364d3..186184c 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java @@ -3,7 +3,6 @@ import com.MylesAndMore.Tumble.game.Arena; import com.MylesAndMore.Tumble.game.Game; import com.MylesAndMore.Tumble.plugin.CustomConfig; -import com.MylesAndMore.Tumble.plugin.Result; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; @@ -11,7 +10,6 @@ import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.HashMap; import java.util.Objects; @@ -44,46 +42,38 @@ public void readConfig() { // arenas ConfigurationSection arenasSection = config.getConfigurationSection("arenas"); if (arenasSection == null) { - plugin.getLogger().warning("Section 'arenas' is missing from config"); + plugin.getLogger().warning("config.yml is missing key 'arenas'"); 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; + if (config.contains("arenas." + arenaName + ".kill-at-y", true)) { + arena.killAtY = config.getInt("arenas." + arenaName + ".kill-at-y"); } - - Result res = readWorld(anArenaSection.getConfigurationSection("game-spawn")); - if (res.success) { - arena.gameSpawn = res.value; + if (config.contains("arenas." + arenaName + ".game-spawn")) { + arena.gameSpawn = readWorld("arenas." + arenaName + ".game-spawn"); } - - Result lobbyRes = readWorld(anArenaSection.getConfigurationSection("lobby")); - if (lobbyRes.success) { - arena.lobby = lobbyRes.value; + if (config.contains("arenas." + arenaName + ".lobby")) { + arena.lobby = readWorld("arenas." + arenaName + ".lobby"); } - - Result winnerLobbyRes = readWorld(anArenaSection.getConfigurationSection("winner-lobby")); - if (winnerLobbyRes.success) { - arena.winnerLobby = winnerLobbyRes.value; + if (config.contains("arenas." + arenaName + ".winner-lobby")) { + arena.winnerLobby = readWorld("arenas." + arenaName + ".winner-lobby"); + } + if (config.contains("arenas." + arenaName + ".wait-area")) { + arena.waitArea = readWorld("arenas." + arenaName + ".wait-area"); } - Result waitAreaRes = readWorld(anArenaSection.getConfigurationSection("wait-area")); - if (waitAreaRes.success) { - arena.waitArea = waitAreaRes.value; + // validate + if (arena.gameSpawn == null) { + plugin.getLogger().severe("arenas.yml: Arena " + arenaName + " is missing a game spawn, before you can join you must set it with '/tmbl setgamespawn'."); + } + if (arena.gameSpawn == null) { + plugin.getLogger().severe("arenas.yml: Arena " + arenaName + " is missing a lobby location. The spawn point of the default world will be used."); } + arenas.put(arena.name, arena); } } @@ -136,35 +126,40 @@ public Game findGamePlayerIsIn(Player p) { * y: * z: * world: - * @param section The section in the yaml with x, y, z, and world as its children + * @param path The section in the yaml with x, y, z, and world as its children * @return Result of either: * Result#success = true and Result#value OR * Result#success = false and Result#error */ - private Result readWorld(@Nullable ConfigurationSection section) { + private Location readWorld(String path) { + ConfigurationSection section = config.getConfigurationSection(path); if (section == null) { - return new Result<>("Section missing from config"); + plugin.getLogger().warning("arenas.yml: Error loading location at '" + path + "' - " + "Section is null"); + return null; } 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."); + plugin.getLogger().warning("arenas.yml: Error loading location at '" + path + "' - " + "Arena coordinates are missing or are zero. Coordinates cannot be zero."); + return null; } String worldName = section.getString("world"); if (worldName == null) { - return new Result<>("World name is missing"); + plugin.getLogger().warning("arenas.yml: Error loading location at '" + path +"' - " + "World name is missing"); + return null; } World world = Bukkit.getWorld(worldName); if (world == null) { - return new Result<>("Failed to load world " + worldName); + plugin.getLogger().warning("arenas.yml: Error loading location at '" + path + "' - " + "Failed to load world '" + worldName + "'"); + return null; } - return new Result<>(new Location(world,x,y,z)); + return new Location(world,x,y,z); } /** diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index 169ce33..e32ba92 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -43,7 +43,7 @@ public class Game { public Game(@NotNull Arena arena, @NotNull GameType type) { this.arena = arena; this.type = type; - this.gameSpawn = arena.gameSpawn; + this.gameSpawn = Objects.requireNonNull(arena.gameSpawn); } /** diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/Result.java b/src/main/java/com/MylesAndMore/Tumble/plugin/Result.java deleted file mode 100644 index 9a07771..0000000 --- a/src/main/java/com/MylesAndMore/Tumble/plugin/Result.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.MylesAndMore.Tumble.plugin; - -// java does not have result types (i miss rust { - public final boolean success; - public final T value; - public final String error; - - public Result(String error) { - success = false; - this.error = error; - this.value = null; - } - - public Result(T value) { - success = true; - this.value = value; - this.error = null; - } -} diff --git a/src/main/resources/language.yml b/src/main/resources/language.yml index 7b67114..835d577 100644 --- a/src/main/resources/language.yml +++ b/src/main/resources/language.yml @@ -4,18 +4,18 @@ 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" +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%" +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 '/tmbl join %arena% %type%'." already-in-game: "&4You are already in a game! Leave it to join another one." -arena-not-ready: "&4This arena is not finished being set up" -arena-not-ready-op: "&4Incomplete arena. &7Set a game spawn with /tmbl setGameSpawn" -specify-game-type: "&4No game is currently taking place in this arena, provide the game type to start one" +arena-not-ready: "&4This arena is not finished being set up!" +arena-not-ready-op: "&4Incomplete arena. &7Set a game spawn with '/tmbl setGameSpawn'." +specify-game-type: "&4No game is currently taking place in this arena! &7Provide the game type to start one." -create-success: "&aArena created successfully! &eBefore you can join, you must set a game spawn location with /tmbl setGameSpawn" +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%" From 8493680d8b53604acf3775ae8176ea6b8a9751a6 Mon Sep 17 00:00:00 2001 From: sowgro Date: Wed, 3 Jul 2024 01:34:15 -0400 Subject: [PATCH 27/51] More code clean up and comments --- README.md | 14 +- .../java/com/MylesAndMore/Tumble/Main.java | 11 ++ .../Tumble/commands/ForceStart.java | 5 + .../MylesAndMore/Tumble/commands/Join.java | 9 +- .../Tumble/game/EventListener.java | 135 +++++++++--------- .../com/MylesAndMore/Tumble/game/Game.java | 4 + .../com/MylesAndMore/Tumble/game/Layers.java | 4 +- 7 files changed, 100 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index 5a83f02..525e01f 100644 --- a/README.md +++ b/README.md @@ -61,10 +61,10 @@ Configuration for this plugin is stored in three files. ### config.yml Stores common settings -| Option | Type | Default value | -|----------------------------|-----------------|---------------| -| `hide-join-leave-messages` | `boolean` | `false` | -| `wait-duration` | `int` (seconds) | `15` | +| Option | Type | Default value | +|----------------------------|-------------------|---------------| +| `hide-join-leave-messages` | Boolean | `false` | +| `wait-duration` | Integer (seconds) | `15` | ### arenas.yml @@ -90,9 +90,9 @@ Locations are stored using the following format: Each arena can also contain the following option: -| Option | Type | Description | -|-------------|--------|-----------------------------------------------------------------| -| `kill-at-y` | double | When a player falls below this Y-level, they will be eliminated | +| Option | Type | Description | +|-------------|---------|-----------------------------------------------------------------| +| `kill-at-y` | Integer | When a player falls below this Y-level, they will be eliminated | ### language.yml Most of this plugin's strings are configurable through this file. (Excluding from some console errors) diff --git a/src/main/java/com/MylesAndMore/Tumble/Main.java b/src/main/java/com/MylesAndMore/Tumble/Main.java index e4c9452..ca1c4f7 100644 --- a/src/main/java/com/MylesAndMore/Tumble/Main.java +++ b/src/main/java/com/MylesAndMore/Tumble/Main.java @@ -5,6 +5,7 @@ import com.MylesAndMore.Tumble.config.ConfigManager; import com.MylesAndMore.Tumble.config.LanguageManager; +import com.MylesAndMore.Tumble.game.Arena; import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; @@ -32,4 +33,14 @@ public void onEnable() { Bukkit.getServer().getLogger().info("[Tumble] Tumble successfully enabled!"); } + + @Override + public void onDisable() { + // stop running games + for (Arena a : arenaManager.arenas.values()) { + if (a.game != null) { + a.game.stopGame(); + } + } + } } \ No newline at end of file diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java index d05155d..121b946 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java @@ -1,6 +1,7 @@ package com.MylesAndMore.Tumble.commands; import com.MylesAndMore.Tumble.game.Game; +import com.MylesAndMore.Tumble.plugin.GameState; import com.MylesAndMore.Tumble.plugin.SubCommand; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; @@ -53,6 +54,10 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command return false; } + if (game.gameState != GameState.WAITING) { + return false; + } + game.gameStart(); sender.sendMessage(languageManager.fromKey("forcestart-success")); return true; diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Join.java b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java index 3989960..6b5494d 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Join.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java @@ -59,16 +59,17 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Game game; if (args.length < 2 || args[1] == null) { - // try to infer game type from game taking place in the arena + // no type specified: try to infer game type from game taking place in the arena if (arena.game == null) { + // cant infer if no game is taking place sender.sendMessage(languageManager.fromKey("specify-game-type")); return false; } - else { - game = arena.game; - } + + game = arena.game; } else { + // type specified GameType type; switch (args[1]) { case "shovels", "shovel" -> type = GameType.SHOVELS; diff --git a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java index 1bc31b3..28fd314 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java @@ -27,28 +27,23 @@ * An event listener for a game of tumble. */ public class EventListener implements Listener { - World gameWorld; Game game; /** - * Create a new EventListener + * Create a new EventListener for a game. + * This should be active when the game starts (not while it is waiting) * @param game The game that the EventListener belongs to. */ public EventListener(Game game) { this.game = game; - this.gameWorld = game.arena.gameSpawn.getWorld(); } - + @EventHandler public void PlayerJoinEvent(PlayerJoinEvent event) { // Hide/show join message accordingly if (configManager.HideLeaveJoin) { event.setJoinMessage(null); } - 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(game.arena.lobby)); - } } @EventHandler @@ -57,15 +52,17 @@ public void PlayerQuitEvent(PlayerQuitEvent event) { if (configManager.HideLeaveJoin) { event.setQuitMessage(null); } - if (event.getPlayer().getWorld() == gameWorld) { - event.getPlayer().teleport(game.arena.lobby); + + // remove player from game if they leave during a game + if (game.gamePlayers.contains(event.getPlayer())) { game.removePlayer(event.getPlayer()); } } @EventHandler public void PlayerDeathEvent(PlayerDeathEvent event) { - if (game.gamePlayers.contains(event.getEntity())) { + // inform the game that the player died and respawn them + if (game.gamePlayers.contains(event.getEntity()) && game.gameState == GameState.RUNNING) { game.playerDeath(event.getEntity()); Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> event.getEntity().spigot().respawn(), 10); } @@ -74,51 +71,47 @@ public void PlayerDeathEvent(PlayerDeathEvent event) { @EventHandler public void PlayerItemDamageEvent(PlayerItemDamageEvent event) { // Remove item damage within games - if (event.getPlayer().getWorld() == gameWorld) { + if (game.gamePlayers.contains(event.getPlayer()) && game.gameState == GameState.RUNNING) { event.setCancelled(true); } } @EventHandler public void ProjectileLaunchEvent(ProjectileLaunchEvent event) { - if (game.roundType != GameType.SNOWBALLS) { return; } - if (event.getEntity().getWorld() == gameWorld - && event.getEntity() instanceof Snowball - && event.getEntity().getShooter() instanceof Player player) { + if (!(event.getEntity().getShooter() instanceof Player p)) { return; } + if (!game.gamePlayers.contains(p)) { return; } - // Prevent projectiles (snowballs) from being thrown before the game starts - if (Objects.equals(game.gameState, GameState.STARTING)) { - event.setCancelled(true); - } - else { + if (game.roundType == GameType.SNOWBALLS && event.getEntity() instanceof Snowball) { + if (game.gameState == GameState.RUNNING) { // Give players a snowball when they've used one (infinite snowballs) - Bukkit.getServer().getScheduler().runTask(plugin, () -> player.getInventory().addItem(new ItemStack(Material.SNOWBALL, 1))); + Bukkit.getServer().getScheduler().runTask(plugin, () -> p.getInventory().addItem(new ItemStack(Material.SNOWBALL, 1))); + } else { + // Prevent projectiles (snowballs) from being thrown before the game starts + event.setCancelled(true); } } } @EventHandler public void ProjectileHitEvent(ProjectileHitEvent event) { - if (event.getHitBlock() == null || game.roundType != GameType.SNOWBALLS) { return; } + if (!(event.getEntity().getShooter() instanceof Player p)) { return; } + if (!game.gamePlayers.contains(p)) { return; } + // Removes blocks that snowballs thrown by players have hit in the game world - if (event.getHitBlock().getWorld() == gameWorld) { - 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.gameSpawn)) < 579) { - p.playEffect( - event.getHitBlock().getLocation(), - Effect.STEP_SOUND, - event.getHitBlock().getType()); - event.getHitBlock().setType(Material.AIR); - } - } - else if (event.getHitEntity() != null) { - if (event.getHitEntity() instanceof Player hitPlayer) { - // Also cancel any knockback - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> hitPlayer.setVelocity(new Vector())); - } - } + if (game.roundType == GameType.SNOWBALLS && event.getEntity() instanceof Snowball) { + if (event.getHitBlock() != null) { + if (event.getHitBlock().getLocation().distanceSquared(game.arena.gameSpawn) < 579) { + p.playEffect( + event.getHitBlock().getLocation(), + Effect.STEP_SOUND, + event.getHitBlock().getType()); + event.getHitBlock().setType(Material.AIR); + } + } + else if (event.getHitEntity() != null) { + if (event.getHitEntity() instanceof Player hitPlayer) { + // Also cancel any knockback + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> hitPlayer.setVelocity(new Vector())); } } } @@ -126,22 +119,21 @@ else if (event.getHitEntity() != null) { @EventHandler public void PlayerDropItemEvent(PlayerDropItemEvent event) { - // Don't allow items to drop in the game world - if (event.getPlayer().getWorld() == gameWorld) { + // Don't allow items to drop during the game + if (game.gamePlayers.contains(event.getPlayer())) { event.setCancelled(true); } } @EventHandler public void PlayerMoveEvent(PlayerMoveEvent event) { + if (!game.gamePlayers.contains(event.getPlayer())) { return; } // Cancel movement if the game is starting (so players can't move before the game starts) - if (Objects.equals(game.gameState, GameState.STARTING) - && event.getPlayer().getWorld().equals(gameWorld) - && !equalPosition(event.getFrom(),event.getTo())) { + if (Objects.equals(game.gameState, GameState.STARTING) && !equalPosition(event.getFrom(),event.getTo())) { event.setCancelled(true); } // kill player if they are below a Y level - if (event.getPlayer().getWorld().equals(gameWorld) && game.arena.killAtY != null) { + if (game.arena.killAtY != null && game.gameState == GameState.RUNNING) { if (event.getPlayer().getLocation().getY() <= game.arena.killAtY) { event.getPlayer().setHealth(0); } @@ -151,17 +143,17 @@ public void PlayerMoveEvent(PlayerMoveEvent event) { @EventHandler public void BlockDropItemEvent(BlockDropItemEvent event) { // If a block was going to drop an item (ex. snow dropping snowballs) in the game world, cancel it - if (event.getBlock().getWorld() == gameWorld) { + if (event.getBlock().getWorld() == game.arena.gameSpawn.getWorld()) { event.setCancelled(true); } } @EventHandler public void PlayerInteractEvent(PlayerInteractEvent event) { -// if (game.roundType != GameType.SHOVELS) {return;} - // Remove blocks when clicked in the game world (all gamemodes require this functionality) - if (event.getAction() == Action.LEFT_CLICK_BLOCK - && Objects.requireNonNull(event.getClickedBlock()).getWorld() == gameWorld) { + if (!game.gamePlayers.contains(event.getPlayer())) { return; } + + // Remove blocks when clicked in the game world (all game types require this functionality) + if (event.getAction() == Action.LEFT_CLICK_BLOCK && event.getClickedBlock() != null) { event.getPlayer().playEffect( event.getClickedBlock().getLocation(), Effect.STEP_SOUND, @@ -175,38 +167,38 @@ public void PlayerInteractEvent(PlayerInteractEvent event) { public void BlockBreakEvent(BlockBreakEvent event) { // This just doesn't allow blocks to break in the gameWorld; the PlayerInteractEvent will take care of everything // This prevents any weird client-server desync - if (event.getBlock().getWorld() == gameWorld) { + if (game.gamePlayers.contains(event.getPlayer())) { event.setCancelled(true); } } @EventHandler public void FoodLevelChangeEvent(FoodLevelChangeEvent event) { + if (!(event.getEntity() instanceof Player p)) { return; } + if (!game.gamePlayers.contains(p)) { return; } // INFINITE FOOD (YAY!!!!) - if (event.getEntity().getWorld() == gameWorld) { - event.setCancelled(true); - } + event.setCancelled(true); } @EventHandler public void EntityDamageEvent(EntityDamageEvent event) { + if (!(event.getEntity() instanceof Player p)) { return; } + if (!game.gamePlayers.contains(p)) { return; } + // Check to see if a player got damaged by another entity (player, snowball, etc) in the gameWorld, if so, cancel it - if (event.getEntity().getWorld() == gameWorld) { - if (event.getEntity() instanceof Player) { - if (event.getCause() == EntityDamageEvent.DamageCause.ENTITY_ATTACK - || event.getCause() == EntityDamageEvent.DamageCause.ENTITY_SWEEP_ATTACK - || event.getCause() == EntityDamageEvent.DamageCause.FALL) { - event.setCancelled(true); - } - } + if (event.getCause() == EntityDamageEvent.DamageCause.ENTITY_ATTACK + || event.getCause() == EntityDamageEvent.DamageCause.ENTITY_SWEEP_ATTACK + || event.getCause() == EntityDamageEvent.DamageCause.FALL) { + event.setCancelled(true); } } @EventHandler public void InventoryDragEvent(InventoryDragEvent event) { - if (event.getWhoClicked().getWorld() == gameWorld) { - event.setCancelled(true); - } + if (!(event.getWhoClicked() instanceof Player p)) { return; } + if (!game.gamePlayers.contains(p)) { return; } + // Disable inventory dragging + event.setCancelled(true); } @EventHandler @@ -217,8 +209,13 @@ public void PlayerRespwanEvent(PlayerRespawnEvent event) { } } - // TODO: stop tile drops for pistons, stop player from getting stuck in the waiting area after they leave - + /** + * Check to see if two locations are in the same place. + * A location also includes where the player is facing which is why this is used instead of .equals() + * @param l1 The first location + * @param l2 The second location + * @return True if they are in the same place + */ public static boolean equalPosition(Location l1, @Nullable Location l2) { if (l2 == null) { return true; diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index e32ba92..f46d907 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -226,6 +226,8 @@ private void gameEnd() { // teleport player back and restore inventory for (Player p : gamePlayers) { + p.getInventory().clear(); + p.setGameMode(GameMode.SURVIVAL); if (p == winner && arena.winnerLobby != null) { p.teleport(arena.winnerLobby); } @@ -283,6 +285,7 @@ public void removePlayer(Player p) { // teleport player back and restore inventory if (arena.waitArea != null) { p.getInventory().clear(); + p.setGameMode(GameMode.SURVIVAL); p.teleport(arena.lobby); if (inventories.containsKey(p)) { p.getInventory().setContents(inventories.get(p)); @@ -297,6 +300,7 @@ public void removePlayer(Player p) { // teleport player back and restore inventory p.getInventory().clear(); + p.setGameMode(GameMode.SURVIVAL); p.teleport(arena.lobby); if (inventories.containsKey(p)) { p.getInventory().setContents(inventories.get(p)); diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Layers.java b/src/main/java/com/MylesAndMore/Tumble/game/Layers.java index 05e289a..99f3aef 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Layers.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Layers.java @@ -146,13 +146,13 @@ public Layers() { List gen3 = new ArrayList<>() {{ add(Material.PACKED_ICE); add(Material.PACKED_ICE); - add(Material.NOTE_BLOCK); + add(Material.JUKEBOX); add(Material.TNT); add(Material.LIGHT_BLUE_CONCRETE); add(Material.GLASS); add(Material.PACKED_ICE); add(Material.PACKED_ICE); - add(Material.NOTE_BLOCK); + add(Material.JUKEBOX); add(Material.TNT); add(Material.LIGHT_BLUE_CONCRETE); add(Material.GLASS); From 13b5a6bbfb246906caf98c77589718fefa091084 Mon Sep 17 00:00:00 2001 From: sowgro Date: Thu, 4 Jul 2024 02:45:57 -0400 Subject: [PATCH 28/51] Fix default config loading and og_guide config --- OG_GUIDE.md | 8 +++--- .../Tumble/config/ArenaManager.java | 13 +++++----- .../Tumble/config/ConfigManager.java | 9 ++++--- .../Tumble/config/LanguageManager.java | 9 ++++--- .../com/MylesAndMore/Tumble/game/Layers.java | 2 +- .../Tumble/plugin/CustomConfig.java | 1 + src/main/resources/arenas.yml | 25 ++----------------- 7 files changed, 27 insertions(+), 40 deletions(-) diff --git a/OG_GUIDE.md b/OG_GUIDE.md index 1ddf924..f7302a1 100644 --- a/OG_GUIDE.md +++ b/OG_GUIDE.md @@ -39,7 +39,7 @@ In this guide, I'll go over how to set up the Tumble plugin with the original ga winner-lobby: x: -362.5 y: 76 - Z: -340.5 + z: -340.5 world: lobby birthday: kill-at-y: 27 @@ -56,7 +56,7 @@ In this guide, I'll go over how to set up the Tumble plugin with the original ga winner-lobby: x: -362.5 y: 76 - Z: -340.5 + z: -340.5 world: lobby festive: kill-at-y: 20 @@ -73,7 +73,7 @@ In this guide, I'll go over how to set up the Tumble plugin with the original ga winner-lobby: x: -362.5 y: 76 - Z: -340.5 + z: -340.5 world: lobby halloween: kill-at-y: 23 @@ -90,7 +90,7 @@ In this guide, I'll go over how to set up the Tumble plugin with the original ga winner-lobby: x: -362.5 y: 76 - Z: -340.5 + z: -340.5 world: lobby ``` 8. Reload the plugin with `/tmbl reload`. diff --git a/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java index 186184c..00bc2e6 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java @@ -20,17 +20,19 @@ * Manages arenas.yml and stores list of arenas */ public class ArenaManager { - public HashMap arenas; - private final CustomConfig arenasYml = new CustomConfig("arenas.yml"); - private final FileConfiguration config = arenasYml.getConfig(); + private final CustomConfig arenasYml; + private final FileConfiguration config; /** * Create an ArenaManager */ public ArenaManager() { + arenasYml = new CustomConfig("arenas.yml"); arenasYml.saveDefaultConfig(); + config = arenasYml.getConfig(); + readConfig(); } @@ -38,14 +40,13 @@ public ArenaManager() { * Read arenas from arenas.ynl and populate this.arenas */ public void readConfig() { + arenas = new HashMap<>(); - // arenas ConfigurationSection arenasSection = config.getConfigurationSection("arenas"); if (arenasSection == null) { - plugin.getLogger().warning("config.yml is missing key 'arenas'"); + plugin.getLogger().warning("arenas.yml is missing key 'arenas'"); return; } - arenas = new HashMap<>(); for (String arenaName: arenasSection.getKeys(false)) { Arena arena = new Arena(arenaName); diff --git a/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java b/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java index 13cc779..d0510c9 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java @@ -14,15 +14,18 @@ public class ConfigManager { public boolean HideLeaveJoin; public int waitDuration; - private final CustomConfig configYml = new CustomConfig("config.yml"); - private final Configuration config = configYml.getConfig(); - private final Configuration defaultConfig = Objects.requireNonNull(config.getDefaults()); + private final Configuration config; + private final Configuration defaultConfig; /** * Create a config manager */ public ConfigManager() { + CustomConfig configYml = new CustomConfig("config.yml"); configYml.saveDefaultConfig(); + config = configYml.getConfig(); + defaultConfig = Objects.requireNonNull(config.getDefaults()); + validate(); readConfig(); } diff --git a/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java b/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java index 7c82664..2b11edc 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java @@ -12,15 +12,18 @@ * Manages language.yml and allows retrieval of keys */ public class LanguageManager { - private final CustomConfig languageYml = new CustomConfig("language.yml"); - private final Configuration config = languageYml.getConfig(); - private final Configuration defaultConfig = Objects.requireNonNull(config.getDefaults()); + private final Configuration config; + private final Configuration defaultConfig; /** * Creates a new LanguageManager */ public LanguageManager() { + CustomConfig languageYml = new CustomConfig("language.yml"); languageYml.saveDefaultConfig(); + config = languageYml.getConfig(); + defaultConfig = Objects.requireNonNull(config.getDefaults()); + validate(); } diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Layers.java b/src/main/java/com/MylesAndMore/Tumble/game/Layers.java index 99f3aef..8954732 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Layers.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Layers.java @@ -206,7 +206,7 @@ public Layers() { }}; matList.add(gen12); List gen14 = new ArrayList<>() {{ - add(Material.LECTERN); + add(Material.OAK_PLANKS); add(Material.OBSIDIAN); add(Material.SPONGE); add(Material.BEEHIVE); diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/CustomConfig.java b/src/main/java/com/MylesAndMore/Tumble/plugin/CustomConfig.java index 8ef4638..0ebc82f 100644 --- a/src/main/java/com/MylesAndMore/Tumble/plugin/CustomConfig.java +++ b/src/main/java/com/MylesAndMore/Tumble/plugin/CustomConfig.java @@ -59,6 +59,7 @@ public void saveConfig() { public void saveDefaultConfig() { if (!configFile.exists()) { plugin.saveResource(fileName, false); + reloadConfig(); } } } diff --git a/src/main/resources/arenas.yml b/src/main/resources/arenas.yml index 7b902be..3b755c3 100644 --- a/src/main/resources/arenas.yml +++ b/src/main/resources/arenas.yml @@ -1,23 +1,2 @@ -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 +# NOTE: Coordinates cannot be zero! Use something like 0.5 +arenas: {} \ No newline at end of file From ac7778112934593d65e2e7c06794a141ed47ab63 Mon Sep 17 00:00:00 2001 From: sowgro Date: Wed, 31 Jul 2024 22:11:38 -0400 Subject: [PATCH 29/51] Make managers static again --- .../java/com/MylesAndMore/Tumble/Main.java | 12 +++---- .../MylesAndMore/Tumble/commands/Create.java | 13 ++++--- .../Tumble/commands/ForceStart.java | 21 ++++++----- .../Tumble/commands/ForceStop.java | 21 ++++++----- .../MylesAndMore/Tumble/commands/Join.java | 36 +++++++++---------- .../MylesAndMore/Tumble/commands/Leave.java | 13 ++++--- .../MylesAndMore/Tumble/commands/Reload.java | 8 +++-- .../MylesAndMore/Tumble/commands/Remove.java | 19 +++++----- .../Tumble/commands/SetGameSpawn.java | 21 ++++++----- .../Tumble/commands/SetKillYLevel.java | 21 ++++++----- .../Tumble/commands/SetLobby.java | 21 ++++++----- .../Tumble/commands/SetWaitArea.java | 21 ++++++----- .../Tumble/commands/SetWinnerLobby.java | 21 ++++++----- .../MylesAndMore/Tumble/commands/Tumble.java | 12 ++++--- .../Tumble/config/ArenaManager.java | 26 +++++--------- .../Tumble/config/ConfigManager.java | 27 +++++--------- .../Tumble/config/LanguageManager.java | 15 ++++---- .../Tumble/game/EventListener.java | 18 +++++----- .../com/MylesAndMore/Tumble/game/Game.java | 31 ++++++++-------- 19 files changed, 175 insertions(+), 202 deletions(-) diff --git a/src/main/java/com/MylesAndMore/Tumble/Main.java b/src/main/java/com/MylesAndMore/Tumble/Main.java index ca1c4f7..eb1c4e0 100644 --- a/src/main/java/com/MylesAndMore/Tumble/Main.java +++ b/src/main/java/com/MylesAndMore/Tumble/Main.java @@ -16,17 +16,13 @@ public class Main extends JavaPlugin{ public static Main plugin; - public static LanguageManager languageManager; - public static ArenaManager arenaManager; - public static ConfigManager configManager; - @Override public void onEnable() { plugin = this; - languageManager = new LanguageManager(); - configManager = new ConfigManager(); - arenaManager = new ArenaManager(); + LanguageManager.readConfig(); + ConfigManager.readConfig(); + ArenaManager.readConfig(); Objects.requireNonNull(this.getCommand("tumble")).setExecutor(new Tumble()); new Metrics(this, 16940); @@ -37,7 +33,7 @@ public void onEnable() { @Override public void onDisable() { // stop running games - for (Arena a : arenaManager.arenas.values()) { + for (Arena a : ArenaManager.arenas.values()) { if (a.game != null) { a.game.stopGame(); } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Create.java b/src/main/java/com/MylesAndMore/Tumble/commands/Create.java index d21f8a4..667be5e 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Create.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Create.java @@ -1,5 +1,7 @@ package com.MylesAndMore.Tumble.commands; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.config.LanguageManager; import com.MylesAndMore.Tumble.game.Arena; import com.MylesAndMore.Tumble.plugin.SubCommand; import org.bukkit.command.Command; @@ -11,9 +13,6 @@ import java.util.Collections; import java.util.List; -import static com.MylesAndMore.Tumble.Main.arenaManager; -import static com.MylesAndMore.Tumble.Main.languageManager; - public class Create implements SubCommand, CommandExecutor, TabCompleter { @Override public String getCommandName() { @@ -28,14 +27,14 @@ public String getPermission() { @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")); + sender.sendMessage(LanguageManager.fromKey("missing-arena-parameter")); return false; } String arenaName = args[0]; - arenaManager.arenas.put(arenaName, new Arena(arenaName)); - arenaManager.WriteConfig(); - sender.sendMessage(languageManager.fromKey("create-success")); + ArenaManager.arenas.put(arenaName, new Arena(arenaName)); + ArenaManager.WriteConfig(); + sender.sendMessage(LanguageManager.fromKey("create-success")); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java index 121b946..3953bea 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java @@ -1,5 +1,7 @@ package com.MylesAndMore.Tumble.commands; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.config.LanguageManager; import com.MylesAndMore.Tumble.game.Game; import com.MylesAndMore.Tumble.plugin.GameState; import com.MylesAndMore.Tumble.plugin.SubCommand; @@ -13,9 +15,6 @@ import java.util.ArrayList; import java.util.List; -import static com.MylesAndMore.Tumble.Main.arenaManager; -import static com.MylesAndMore.Tumble.Main.languageManager; - public class ForceStart implements SubCommand, CommandExecutor, TabCompleter { @Override @@ -34,23 +33,23 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Game game; if (args.length < 1 || args[0] == null) { // no arena passed in, try to infer from game player is in - game = arenaManager.findGamePlayerIsIn((Player)sender); + game = ArenaManager.findGamePlayerIsIn((Player)sender); if (game == null) { - sender.sendMessage(languageManager.fromKey("missing-arena-parameter")); + sender.sendMessage(LanguageManager.fromKey("missing-arena-parameter")); return false; } } else { String arenaName = args[0]; - if (!arenaManager.arenas.containsKey(arenaName)) { - sender.sendMessage(languageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); + if (!ArenaManager.arenas.containsKey(arenaName)) { + sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); return false; } - game = arenaManager.arenas.get(arenaName).game; + game = ArenaManager.arenas.get(arenaName).game; } if (game == null) { - sender.sendMessage(languageManager.fromKey("no-game-in-arena")); + sender.sendMessage(LanguageManager.fromKey("no-game-in-arena")); return false; } @@ -59,14 +58,14 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command } game.gameStart(); - sender.sendMessage(languageManager.fromKey("forcestart-success")); + sender.sendMessage(LanguageManager.fromKey("forcestart-success")); return true; } @Override public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (args.length == 1) { - return arenaManager.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 a69779a..37d3465 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java @@ -1,5 +1,7 @@ package com.MylesAndMore.Tumble.commands; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.config.LanguageManager; import com.MylesAndMore.Tumble.game.Game; import com.MylesAndMore.Tumble.plugin.SubCommand; import org.bukkit.command.Command; @@ -12,9 +14,6 @@ import java.util.ArrayList; import java.util.List; -import static com.MylesAndMore.Tumble.Main.arenaManager; -import static com.MylesAndMore.Tumble.Main.languageManager; - public class ForceStop implements SubCommand, CommandExecutor, TabCompleter { @Override @@ -33,35 +32,35 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Game game; if (args.length < 1 || args[0] == null) { // no arena passed in, try to infer from game player is in - game = arenaManager.findGamePlayerIsIn((Player)sender); + game = ArenaManager.findGamePlayerIsIn((Player)sender); if (game == null) { - sender.sendMessage(languageManager.fromKey("missing-arena-parameter")); + sender.sendMessage(LanguageManager.fromKey("missing-arena-parameter")); return false; } } else { String arenaName = args[0]; - if (!arenaManager.arenas.containsKey(arenaName)) { - sender.sendMessage(languageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); + if (!ArenaManager.arenas.containsKey(arenaName)) { + sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); return false; } - game = arenaManager.arenas.get(arenaName).game; + game = ArenaManager.arenas.get(arenaName).game; } if (game == null) { - sender.sendMessage(languageManager.fromKey("no-game-in-arena")); + sender.sendMessage(LanguageManager.fromKey("no-game-in-arena")); return false; } game.stopGame(); - sender.sendMessage(languageManager.fromKey("forcestop-success")); + sender.sendMessage(LanguageManager.fromKey("forcestop-success")); return true; } @Override public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (args.length == 1) { - return arenaManager.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 6b5494d..6ce33c3 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Join.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java @@ -1,5 +1,7 @@ package com.MylesAndMore.Tumble.commands; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.config.LanguageManager; import com.MylesAndMore.Tumble.game.Arena; import com.MylesAndMore.Tumble.game.Game; import com.MylesAndMore.Tumble.plugin.GameState; @@ -18,9 +20,6 @@ import java.util.Objects; import java.util.stream.Collectors; -import static com.MylesAndMore.Tumble.Main.arenaManager; -import static com.MylesAndMore.Tumble.Main.languageManager; - public class Join implements SubCommand, CommandExecutor, TabCompleter { @Override @@ -37,32 +36,33 @@ public String getPermission() { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (!(sender instanceof Player p)) { - sender.sendMessage(languageManager.fromKey("not-for-console")); + sender.sendMessage(LanguageManager.fromKey("not-for-console")); return false; } - if (arenaManager.findGamePlayerIsIn((Player)sender) != null) { - sender.sendMessage(languageManager.fromKey("already-in-game")); + if (ArenaManager.findGamePlayerIsIn((Player)sender) != null) { + sender.sendMessage(LanguageManager.fromKey("already-in-game")); + return false; } if (args.length < 1 || args[0] == null) { - sender.sendMessage(languageManager.fromKey("missing-arena-parameter")); + sender.sendMessage(LanguageManager.fromKey("missing-arena-parameter")); return false; } String arenaName = args[0]; - if (!arenaManager.arenas.containsKey(arenaName)) + if (!ArenaManager.arenas.containsKey(arenaName)) { - sender.sendMessage(languageManager.fromKey("invalid-arena").replace("%arena%", arenaName)); + sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%", arenaName)); return false; } - Arena arena = arenaManager.arenas.get(arenaName); + Arena arena = ArenaManager.arenas.get(arenaName); Game game; if (args.length < 2 || args[1] == null) { // no type specified: try to infer game type from game taking place in the arena if (arena.game == null) { // cant infer if no game is taking place - sender.sendMessage(languageManager.fromKey("specify-game-type")); + sender.sendMessage(LanguageManager.fromKey("specify-game-type")); return false; } @@ -76,7 +76,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command case "snowballs", "snowball" -> type = GameType.SNOWBALLS; case "mix", "mixed" -> type = GameType.MIXED; default -> { - sender.sendMessage(languageManager.fromKey("invalid-type")); + sender.sendMessage(LanguageManager.fromKey("invalid-type")); return false; } } @@ -92,7 +92,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command game = arena.game; } else { - sender.sendMessage(languageManager.fromKey("another-type-in-arena") + sender.sendMessage(LanguageManager.fromKey("another-type-in-arena") .replace("%type%",type.toString()) .replace("%arena%",arenaName)); return false; @@ -103,20 +103,20 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command // check to make sure the arena has a game spawn if (game.arena.gameSpawn == null) { if (p.isOp()) { - sender.sendMessage(languageManager.fromKey("arena-not-ready-op")); + sender.sendMessage(LanguageManager.fromKey("arena-not-ready-op")); } else { - sender.sendMessage(languageManager.fromKey("arena-not-ready")); + sender.sendMessage(LanguageManager.fromKey("arena-not-ready")); } return false; } if (game.gameState != GameState.WAITING) { - sender.sendMessage(languageManager.fromKey("game-in-progress")); + sender.sendMessage(LanguageManager.fromKey("game-in-progress")); return false; } game.addPlayer((Player)sender); - sender.sendMessage(languageManager.fromKey("join-success") + sender.sendMessage(LanguageManager.fromKey("join-success") .replace("%type%", game.type.toString()) .replace("%arena%", arena.name)); return true; @@ -125,7 +125,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command @Override public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (args.length == 1) { - return arenaManager.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 f5a4eeb..693f122 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Leave.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Leave.java @@ -1,5 +1,7 @@ package com.MylesAndMore.Tumble.commands; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.config.LanguageManager; import com.MylesAndMore.Tumble.game.Game; import com.MylesAndMore.Tumble.plugin.SubCommand; import org.bukkit.command.Command; @@ -12,9 +14,6 @@ import java.util.ArrayList; import java.util.List; -import static com.MylesAndMore.Tumble.Main.arenaManager; -import static com.MylesAndMore.Tumble.Main.languageManager; - public class Leave implements SubCommand, CommandExecutor, TabCompleter { @Override @@ -31,18 +30,18 @@ public String getPermission() { 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")); + sender.sendMessage(LanguageManager.fromKey("not-for-console")); return false; } - Game game = arenaManager.findGamePlayerIsIn((Player)sender); + Game game = ArenaManager.findGamePlayerIsIn((Player)sender); if (game == null) { - sender.sendMessage(languageManager.fromKey("no-game-in-arena")); + sender.sendMessage(LanguageManager.fromKey("no-game-in-arena")); return false; } game.removePlayer((Player) sender); - sender.sendMessage(languageManager.fromKey("leave-success") + 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 03ce42d..79faef9 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java @@ -1,5 +1,7 @@ package com.MylesAndMore.Tumble.commands; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.config.LanguageManager; import com.MylesAndMore.Tumble.game.Arena; import com.MylesAndMore.Tumble.plugin.SubCommand; import org.bukkit.command.Command; @@ -11,7 +13,7 @@ import java.util.ArrayList; import java.util.List; -import static com.MylesAndMore.Tumble.Main.*; +import static com.MylesAndMore.Tumble.Main.plugin; public class Reload implements SubCommand, CommandExecutor, TabCompleter { @@ -28,14 +30,14 @@ public String getPermission() { @Override public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { - for (Arena a : arenaManager.arenas.values()) { + for (Arena a : ArenaManager.arenas.values()) { if (a.game != null) { a.game.stopGame(); } } plugin.onEnable(); - sender.sendMessage(languageManager.fromKey("reload-success")); + 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 index 2d6f9eb..c2aa149 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Remove.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Remove.java @@ -1,5 +1,7 @@ package com.MylesAndMore.Tumble.commands; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.config.LanguageManager; import com.MylesAndMore.Tumble.plugin.SubCommand; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; @@ -10,9 +12,6 @@ import java.util.Collections; import java.util.List; -import static com.MylesAndMore.Tumble.Main.arenaManager; -import static com.MylesAndMore.Tumble.Main.languageManager; - public class Remove implements SubCommand, CommandExecutor, TabCompleter { @Override @@ -28,26 +27,26 @@ public String getPermission() { @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")); + 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)); + if (!ArenaManager.arenas.containsKey(arenaName)) { + sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); return false; } - arenaManager.arenas.remove(arenaName); - arenaManager.WriteConfig(); - sender.sendMessage(languageManager.fromKey("remove-success")); + ArenaManager.arenas.remove(arenaName); + ArenaManager.WriteConfig(); + sender.sendMessage(LanguageManager.fromKey("remove-success")); return true; } @Override public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (args.length == 1) { - return arenaManager.arenas.keySet().stream().toList(); + 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 index 7711de9..21bdfe8 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetGameSpawn.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetGameSpawn.java @@ -1,5 +1,7 @@ package com.MylesAndMore.Tumble.commands; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.config.LanguageManager; import com.MylesAndMore.Tumble.game.Arena; import com.MylesAndMore.Tumble.plugin.SubCommand; import org.bukkit.command.Command; @@ -12,9 +14,6 @@ import java.util.Collections; import java.util.List; -import static com.MylesAndMore.Tumble.Main.arenaManager; -import static com.MylesAndMore.Tumble.Main.languageManager; - public class SetGameSpawn implements SubCommand, CommandExecutor, TabCompleter { @Override public String getCommandName() { @@ -29,32 +28,32 @@ public String getPermission() { @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")); + 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")); + 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)); + if (!ArenaManager.arenas.containsKey(arenaName)) { + sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); return false; } - Arena arena = arenaManager.arenas.get(arenaName); + Arena arena = ArenaManager.arenas.get(arenaName); arena.gameSpawn = ((Player)sender).getLocation(); - arenaManager.WriteConfig(); - sender.sendMessage(languageManager.fromKey("set-success")); + ArenaManager.WriteConfig(); + sender.sendMessage(LanguageManager.fromKey("set-success")); return true; } @Override public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (args.length == 1) { - return arenaManager.arenas.keySet().stream().toList(); + return ArenaManager.arenas.keySet().stream().toList(); } return Collections.emptyList(); diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetKillYLevel.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetKillYLevel.java index 6e2a59e..142ffdd 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetKillYLevel.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetKillYLevel.java @@ -1,5 +1,7 @@ package com.MylesAndMore.Tumble.commands; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.config.LanguageManager; import com.MylesAndMore.Tumble.game.Arena; import com.MylesAndMore.Tumble.plugin.SubCommand; import org.bukkit.command.Command; @@ -12,9 +14,6 @@ import java.util.Collections; import java.util.List; -import static com.MylesAndMore.Tumble.Main.arenaManager; -import static com.MylesAndMore.Tumble.Main.languageManager; - public class SetKillYLevel implements SubCommand, CommandExecutor, TabCompleter { @Override @@ -30,32 +29,32 @@ public String getPermission() { @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")); + 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")); + 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)); + if (!ArenaManager.arenas.containsKey(arenaName)) { + sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); return false; } - Arena arena = arenaManager.arenas.get(arenaName); + Arena arena = ArenaManager.arenas.get(arenaName); arena.killAtY = ((int) ((Player) sender).getLocation().getY()); - arenaManager.WriteConfig(); - sender.sendMessage(languageManager.fromKey("set-success")); + ArenaManager.WriteConfig(); + sender.sendMessage(LanguageManager.fromKey("set-success")); return true; } @Override public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (args.length == 1) { - return arenaManager.arenas.keySet().stream().toList(); + 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 index 3dedf50..a080d08 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetLobby.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetLobby.java @@ -1,5 +1,7 @@ package com.MylesAndMore.Tumble.commands; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.config.LanguageManager; import com.MylesAndMore.Tumble.game.Arena; import com.MylesAndMore.Tumble.plugin.SubCommand; import org.bukkit.command.Command; @@ -12,9 +14,6 @@ import java.util.Collections; import java.util.List; -import static com.MylesAndMore.Tumble.Main.arenaManager; -import static com.MylesAndMore.Tumble.Main.languageManager; - public class SetLobby implements SubCommand, CommandExecutor, TabCompleter { @Override public String getCommandName() { @@ -29,32 +28,32 @@ public String getPermission() { @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")); + 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")); + 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)); + if (!ArenaManager.arenas.containsKey(arenaName)) { + sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); return false; } - Arena arena = arenaManager.arenas.get(arenaName); + Arena arena = ArenaManager.arenas.get(arenaName); arena.lobby = ((Player)sender).getLocation(); - arenaManager.WriteConfig(); - sender.sendMessage(languageManager.fromKey("set-success")); + ArenaManager.WriteConfig(); + sender.sendMessage(LanguageManager.fromKey("set-success")); return true; } @Override public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (args.length == 1) { - return arenaManager.arenas.keySet().stream().toList(); + 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 index d00a0cb..a96e1a9 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetWaitArea.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetWaitArea.java @@ -1,5 +1,7 @@ package com.MylesAndMore.Tumble.commands; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.config.LanguageManager; import com.MylesAndMore.Tumble.game.Arena; import com.MylesAndMore.Tumble.plugin.SubCommand; import org.bukkit.command.Command; @@ -12,9 +14,6 @@ import java.util.Collections; import java.util.List; -import static com.MylesAndMore.Tumble.Main.arenaManager; -import static com.MylesAndMore.Tumble.Main.languageManager; - public class SetWaitArea implements SubCommand, CommandExecutor, TabCompleter { @Override public String getCommandName() { @@ -29,32 +28,32 @@ public String getPermission() { @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")); + 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")); + 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)); + if (!ArenaManager.arenas.containsKey(arenaName)) { + sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); return false; } - Arena arena = arenaManager.arenas.get(arenaName); + Arena arena = ArenaManager.arenas.get(arenaName); arena.waitArea = ((Player)sender).getLocation(); - arenaManager.WriteConfig(); - sender.sendMessage(languageManager.fromKey("set-success")); + ArenaManager.WriteConfig(); + sender.sendMessage(LanguageManager.fromKey("set-success")); return true; } @Override public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (args.length == 1) { - return arenaManager.arenas.keySet().stream().toList(); + 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 index f40344e..98a535b 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLobby.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLobby.java @@ -1,5 +1,7 @@ package com.MylesAndMore.Tumble.commands; +import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.config.LanguageManager; import com.MylesAndMore.Tumble.game.Arena; import com.MylesAndMore.Tumble.plugin.SubCommand; import org.bukkit.command.Command; @@ -12,9 +14,6 @@ import java.util.Collections; import java.util.List; -import static com.MylesAndMore.Tumble.Main.arenaManager; -import static com.MylesAndMore.Tumble.Main.languageManager; - public class SetWinnerLobby implements SubCommand, CommandExecutor, TabCompleter { @Override public String getCommandName() { @@ -29,32 +28,32 @@ public String getPermission() { @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")); + 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")); + 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)); + if (!ArenaManager.arenas.containsKey(arenaName)) { + sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); return false; } - Arena arena = arenaManager.arenas.get(arenaName); + Arena arena = ArenaManager.arenas.get(arenaName); arena.winnerLobby = ((Player)sender).getLocation(); - arenaManager.WriteConfig(); - sender.sendMessage(languageManager.fromKey("set-success")); + ArenaManager.WriteConfig(); + sender.sendMessage(LanguageManager.fromKey("set-success")); return true; } @Override public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (args.length == 1) { - return arenaManager.arenas.keySet().stream().toList(); + 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 index 67213ee..8b4f925 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java @@ -1,5 +1,6 @@ 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; @@ -7,9 +8,10 @@ import org.bukkit.command.TabCompleter; import org.jetbrains.annotations.NotNull; -import java.util.*; - -import static com.MylesAndMore.Tumble.Main.languageManager; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; public class Tumble implements CommandExecutor, TabCompleter { @@ -31,14 +33,14 @@ public class Tumble implements CommandExecutor, TabCompleter { @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")); + 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())); + sender.sendMessage(LanguageManager.fromKey("no-permission").replace("%permission%", subCmd.getPermission())); return false; } diff --git a/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java index 00bc2e6..ac7767a 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java @@ -20,26 +20,18 @@ * Manages arenas.yml and stores list of arenas */ public class ArenaManager { - public HashMap arenas; + public static HashMap arenas; - private final CustomConfig arenasYml; - private final FileConfiguration config; + private static CustomConfig arenasYml; + private static FileConfiguration config; /** - * Create an ArenaManager + * Read arenas from arenas.yml and populate this.arenas */ - public ArenaManager() { + public static void readConfig() { arenasYml = new CustomConfig("arenas.yml"); arenasYml.saveDefaultConfig(); config = arenasYml.getConfig(); - - readConfig(); - } - - /** - * Read arenas from arenas.ynl and populate this.arenas - */ - public void readConfig() { arenas = new HashMap<>(); ConfigurationSection arenasSection = config.getConfigurationSection("arenas"); @@ -81,7 +73,7 @@ public void readConfig() { /** * Write arenas from this.arenas to arenas.yml */ - public void WriteConfig() { + public static void WriteConfig() { config.set("arenas", null); // clear everything for (Arena arena: arenas.values()) { @@ -111,7 +103,7 @@ public void WriteConfig() { * @param p Player to search for * @return the game the player is in, or null if not found */ - public Game findGamePlayerIsIn(Player p) { + public static Game findGamePlayerIsIn(Player p) { for (Arena a : arenas.values()) { if (a.game != null && a.game.gamePlayers.contains(p)) { return a.game; @@ -132,7 +124,7 @@ public Game findGamePlayerIsIn(Player p) { * Result#success = true and Result#value OR * Result#success = false and Result#error */ - private Location readWorld(String path) { + private static Location readWorld(String path) { ConfigurationSection section = config.getConfigurationSection(path); if (section == null) { @@ -173,7 +165,7 @@ private Location readWorld(String path) { * @param path The path of the section to write * @param location The location to write */ - private void WriteWorld(String path, @NotNull Location location) { + private static void WriteWorld(String path, @NotNull Location location) { ConfigurationSection section = config.getConfigurationSection(path); diff --git a/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java b/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java index d0510c9..59d653a 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java @@ -11,29 +11,29 @@ * Manages config.yml and stores its options */ public class ConfigManager { - public boolean HideLeaveJoin; - public int waitDuration; + public static boolean HideLeaveJoin; + public static int waitDuration; - private final Configuration config; - private final Configuration defaultConfig; + private static Configuration config; + private static Configuration defaultConfig; /** - * Create a config manager + * Reads options in from config.yml */ - public ConfigManager() { + public static void readConfig() { CustomConfig configYml = new CustomConfig("config.yml"); configYml.saveDefaultConfig(); config = configYml.getConfig(); defaultConfig = Objects.requireNonNull(config.getDefaults()); - + HideLeaveJoin = config.getBoolean("hide-join-leave-messages", false); + waitDuration = config.getInt("wait-duration", 15); validate(); - readConfig(); } /** * Check keys of config.yml against the defaults */ - public void validate() { + public static void validate() { boolean invalid = false; for (String key : defaultConfig.getKeys(true)) { if (!config.contains(key,true)) { @@ -45,13 +45,4 @@ public void validate() { plugin.getLogger().severe("Errors were found in config.yml, default values will be used."); } } - - /** - * Reads options in from config.yml - */ - public void readConfig() { - HideLeaveJoin = config.getBoolean("hide-join-leave-messages", 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 index 2b11edc..6625b26 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java @@ -12,13 +12,10 @@ * Manages language.yml and allows retrieval of keys */ public class LanguageManager { - private final Configuration config; - private final Configuration defaultConfig; + private static Configuration config; + private static Configuration defaultConfig; - /** - * Creates a new LanguageManager - */ - public LanguageManager() { + public static void readConfig() { CustomConfig languageYml = new CustomConfig("language.yml"); languageYml.saveDefaultConfig(); config = languageYml.getConfig(); @@ -30,7 +27,7 @@ public LanguageManager() { /** * Check keys of language.yml against the defaults */ - public void validate() { + public static void validate() { boolean invalid = false; for (String key : defaultConfig.getKeys(true)) { if (!config.contains(key,true)) { @@ -49,7 +46,7 @@ public void validate() { * @param key The key representing the message * @return The message from the key */ - public String fromKey(String key) { + public static String fromKey(String key) { return fromKeyNoPrefix("prefix") + fromKeyNoPrefix(key); } @@ -59,7 +56,7 @@ public String fromKey(String key) { * @param key The key representing the message * @return The message from the key */ - public String fromKeyNoPrefix(String key) { + public static String fromKeyNoPrefix(String key) { String val = config.getString(key); if (val == null) { diff --git a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java index 28fd314..9f1456e 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java @@ -1,10 +1,12 @@ package com.MylesAndMore.Tumble.game; -import java.util.Objects; - +import com.MylesAndMore.Tumble.config.ConfigManager; import com.MylesAndMore.Tumble.plugin.GameState; import com.MylesAndMore.Tumble.plugin.GameType; -import org.bukkit.*; +import org.bukkit.Bukkit; +import org.bukkit.Effect; +import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.entity.Snowball; import org.bukkit.event.EventHandler; @@ -15,12 +17,12 @@ import org.bukkit.event.entity.*; import org.bukkit.event.inventory.InventoryDragEvent; import org.bukkit.event.player.*; -import org.bukkit.util.Vector; - import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; import org.jetbrains.annotations.Nullable; -import static com.MylesAndMore.Tumble.Main.configManager; +import java.util.Objects; + import static com.MylesAndMore.Tumble.Main.plugin; /** @@ -41,7 +43,7 @@ public EventListener(Game game) { @EventHandler public void PlayerJoinEvent(PlayerJoinEvent event) { // Hide/show join message accordingly - if (configManager.HideLeaveJoin) { + if (ConfigManager.HideLeaveJoin) { event.setJoinMessage(null); } } @@ -49,7 +51,7 @@ public void PlayerJoinEvent(PlayerJoinEvent event) { @EventHandler public void PlayerQuitEvent(PlayerQuitEvent event) { // Hide/show leave message accordingly - if (configManager.HideLeaveJoin) { + if (ConfigManager.HideLeaveJoin) { event.setQuitMessage(null); } diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index f46d907..e9b7223 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -1,10 +1,11 @@ package com.MylesAndMore.Tumble.game; +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; import net.md_5.bungee.api.chat.TextComponent; - import org.bukkit.*; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; @@ -15,7 +16,7 @@ import java.util.*; -import static com.MylesAndMore.Tumble.Main.*; +import static com.MylesAndMore.Tumble.Main.plugin; /** * Everything relating to the Tumble game @@ -63,7 +64,7 @@ public void addPlayer(Player p) { autoStart(); } else { - displayActionbar(Collections.singletonList(p), languageManager.fromKeyNoPrefix("waiting-for-players")); + displayActionbar(Collections.singletonList(p), LanguageManager.fromKeyNoPrefix("waiting-for-players")); } } @@ -143,7 +144,7 @@ private void prepareGameType(GameType type) { gameID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { clearInventories(gamePlayers); giveItems(gamePlayers, new ItemStack(Material.SNOWBALL)); - displayActionbar(gamePlayers, languageManager.fromKeyNoPrefix("showdown")); + displayActionbar(gamePlayers, LanguageManager.fromKeyNoPrefix("showdown")); playSound(gamePlayers, Sound.ENTITY_ELDER_GUARDIAN_CURSE, SoundCategory.HOSTILE, 1, 1); // End the round in another 2m30s @@ -192,12 +193,12 @@ private void roundEnd() { gameEnd(); } else { // If that player doesn't have three wins, nobody else does, so we need another round - displayTitles(gamePlayers, languageManager.fromKeyNoPrefix("round-over"), languageManager.fromKeyNoPrefix("round-winner").replace("%winner%", winner.getDisplayName()), 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, languageManager.fromKeyNoPrefix("round-over"), languageManager.fromKeyNoPrefix("round-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); } } @@ -215,10 +216,10 @@ private void gameEnd() { // display winner Player winner = getPlayerWithMostWins(gameWins); if (winner != null) { - displayTitles(gamePlayers, languageManager.fromKeyNoPrefix("game-over"), languageManager.fromKeyNoPrefix("game-winner").replace("%winner%",winner.getDisplayName()), 5, 60, 5); + displayTitles(gamePlayers, LanguageManager.fromKeyNoPrefix("game-over"), LanguageManager.fromKeyNoPrefix("game-winner").replace("%winner%",winner.getDisplayName()), 5, 60, 5); } - displayActionbar(gamePlayers, languageManager.fromKeyNoPrefix("lobby-in-10")); + displayActionbar(gamePlayers, LanguageManager.fromKeyNoPrefix("lobby-in-10")); // Wait 10s (200t), then Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { @@ -279,7 +280,7 @@ public void removePlayer(Player p) { // inform player that there are no longer enough players to start if (gamePlayers.size() < 2) { - displayActionbar(gamePlayers, languageManager.fromKeyNoPrefix("waiting-for-players")); + displayActionbar(gamePlayers, LanguageManager.fromKeyNoPrefix("waiting-for-players")); } // teleport player back and restore inventory @@ -313,9 +314,9 @@ public void removePlayer(Player p) { */ public void autoStart() { // Wait for the player to load in - int waitDuration = configManager.waitDuration; + int waitDuration = ConfigManager.waitDuration; Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { - displayActionbar(gamePlayers, languageManager.fromKeyNoPrefix("time-till-start").replace("%wait%",waitDuration+"")); + 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); @@ -370,16 +371,16 @@ private void scatterPlayers(List players) { */ private void countdown(Runnable doAfter) { playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1); - displayTitles(gamePlayers, languageManager.fromKeyNoPrefix("count-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, languageManager.fromKeyNoPrefix("count-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, languageManager.fromKeyNoPrefix("count-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, languageManager.fromKeyNoPrefix("count-go"), null, 1, 5, 1); + displayTitles(gamePlayers, LanguageManager.fromKeyNoPrefix("count-go"), null, 1, 5, 1); doAfter.run(); }, 20); }, 20); From 908e3347eafb1c5c2d214dc434b9bb0a07133444 Mon Sep 17 00:00:00 2001 From: Tyler Ferrari <69283684+Sowgro@users.noreply.github.com> Date: Wed, 31 Jul 2024 23:40:39 -0400 Subject: [PATCH 30/51] fix typo (embarrassing) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 525e01f..e1a7e76 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ But in Tumble, you play on randomly generated layers of blocks, using shovels, s ## Setup 1. [Download](https://github.com/MylesAndMore/tmbl/releases) the plugin's JAR file and place it in your server's plugins directory. -2. Place the worlds for your lobby and arenas in your plugins worlds directory. +2. Place the worlds for your lobby and arenas in your server's worlds directory. - If you would like an experience similar to the original game, see [my guide](OG_GUIDE.md) for using the original worlds. 3. Start your server. From b9295115ff968b3d6d941ac3af91afe598420202 Mon Sep 17 00:00:00 2001 From: Myles Date: Thu, 1 Aug 2024 00:45:40 -0500 Subject: [PATCH 31/51] standardize style and language - standardize code style across PR - change some language/fix typos in documentation (code + external) --- OG_GUIDE.md | 20 ++--- README.md | 88 +++++++++---------- .../java/com/MylesAndMore/Tumble/Main.java | 4 +- .../Tumble/commands/ForceStart.java | 7 +- .../Tumble/commands/ForceStop.java | 5 +- .../MylesAndMore/Tumble/commands/Join.java | 18 ++-- .../MylesAndMore/Tumble/commands/Reload.java | 1 - .../MylesAndMore/Tumble/commands/Tumble.java | 9 +- .../Tumble/config/ArenaManager.java | 8 +- .../Tumble/config/ConfigManager.java | 1 + .../com/MylesAndMore/Tumble/game/Arena.java | 1 - .../Tumble/game/EventListener.java | 12 ++- .../com/MylesAndMore/Tumble/game/Game.java | 61 ++++++------- .../MylesAndMore/Tumble/game/Generator.java | 20 ++--- .../com/MylesAndMore/Tumble/game/Layers.java | 5 +- .../MylesAndMore/Tumble/plugin/GameState.java | 2 +- src/main/resources/arenas.yml | 4 +- src/main/resources/config.yml | 6 +- src/main/resources/language.yml | 10 ++- src/main/resources/plugin.yml | 16 +++- 20 files changed, 141 insertions(+), 157 deletions(-) diff --git a/OG_GUIDE.md b/OG_GUIDE.md index f7302a1..da75d93 100644 --- a/OG_GUIDE.md +++ b/OG_GUIDE.md @@ -16,7 +16,7 @@ In this guide, I'll go over how to set up the Tumble plugin with the original ga Tip: set a specific directory to store your worlds in with the `world-container` setting in `bukkit.yml` -3. Set `level-name` in server.properities to `lobby` +3. Set `level-name` in server.properties to `lobby` 4. Take note of the names of the world folders, we will need this in a moment. 5. Start and join your server. 6. Import your arena worlds. This can be done with the multiverse command `/mv import normal` @@ -93,25 +93,25 @@ In this guide, I'll go over how to set up the Tumble plugin with the original ga z: -340.5 world: lobby ``` -8. Reload the plugin with `/tmbl reload`. +8. Reload the plugin with `/tumble reload`. -9. Join the game using `/tmbl join basic mixed` +9. Join the game using `/tumble join basic mixed` (swap the arena and game type for whichever one you want to play). You're done! Happy playing! ## Recommended plugins +These plugins, while not required, can help to provide a more thorough minigame experience. + - [WorldGuard](https://dev.bukkit.org/projects/worldguard) and [CyberWorldReset](https://www.spigotmc.org/resources/cyberworldreset-standard-%E2%9C%A8-regenerate-worlds-scheduled-resets-lag-optimized%E3%80%8C1-8-1-19%E3%80%8D.96834/) -Protect players from breaking blocks in the lobby and reset any redstone they activated. + - Protect players from breaking blocks in the lobby and reset any redstone they activated. - [ViaVersion](https://www.spigotmc.org/resources/viaversion.19254/) and [ViaBackwards](https://www.spigotmc.org/resources/viabackwards.27448/) -Allow older and newer clients to connect to your server. + - Allow older and newer players to connect to your server. - [Geyser](https://geysermc.org/download#geyser) and [Floodgate](https://geysermc.org/download#floodgate) -Allow Bedrock clients to connect to your server. - -- [ProtectEnviromnemt](https://www.spigotmc.org/resources/protectenvironment.82736/) -Stop water and lava flow (useful for Halloween map) - + - Allow Bedrock clients to connect to your server. +- [ProtectEnvironment](https://www.spigotmc.org/resources/protectenvironment.82736/) + - Stop water and lava flow (useful for Halloween map). diff --git a/README.md b/README.md index e1a7e76..fe8f925 100644 --- a/README.md +++ b/README.md @@ -11,73 +11,73 @@ But in Tumble, you play on randomly generated layers of blocks, using shovels, s ## Features -- Choose from three different game modes present in the original game: shovels, snowballs, and mixed -- Four types of random layer generation +- Choose from three different game modes present in the original game: shovels, snowballs, and mixed +- Four types of random layer generation - 15 unique, themed layer varieties - Quick and easy setup and use -- Support for 2-8 players -- Highly customizable -- Open-source codebase +- Support for 2-8 players - Multiple arenas and concurrent games -- Heavily configurable +- Highly customizable, heavily configurable +- Open-source codebase ## Setup -1. [Download](https://github.com/MylesAndMore/tmbl/releases) the plugin's JAR file and place it in your server's plugins directory. +1. [Download](https://github.com/MylesAndMore/tumble/releases) the plugin's JAR file and place it in your server's plugins directory. 2. Place the worlds for your lobby and arenas in your server's worlds directory. - If you would like an experience similar to the original game, see [my guide](OG_GUIDE.md) for using the original worlds. 3. Start your server. 4. Import your worlds using a plugin like Multiverse. ```/mv import myWorld normal```. -5. Create your first arena `/tmbl create myArena` -6. Set the spawn point of the arena `/tmbl setgamespawn myArena` +5. Create your first arena `/tumble create myArena` +6. Set the spawn point of the arena `/tumble setgamespawn myArena` - **Note**: The layers will generate relative to this location. Ensure that the area is clear, 20 blocks in each direction. -7. You're done! You can now join the game ```/tmbl join myArena mixed```. +7. You're done! You can now join the game ```/tumble join myArena mixed```. Scroll down for more options to configure your game. ## Commands / Permissions -| Command | Description | Permission | -|-------------------------------------|------------------------------------------------------------------------------------|-------------------------| -| `/tmbl join [gameType]` | Join a Tumble match. Can infer game type if a game is already started in the arena | `tumble.join` | -| `/tmbl leave` | Quit a Tumble match | `tumble.leave` | -| `/tmbl forcestart [arenaName]` | Force start a Tumble match. Can infer arena if you are in one | `tumble.forcestart` | -| `/tmbl forcestop [arenaName]` | Force stop a Tumble match. Can infer arena if you are in one | `tumble.forcestop` | -| `/tmbl reload` | Reload the plugin's configs. | `tumble.reload` | -| `/tmbl create ` | Create a new arena | `tumble.create` | -| `/tmbl remove ` | Remove an arena | `tumble.remove` | -| `/tmbl setgamespawn ` | Set game spawn to your current position | `tumble.setgamespawn` | -| `/tmbl setkillylevel ` | Set the arena's Y-level to kill players at to current Y coordinate | `tumble.setkillylevel` | -| `/tmbl setlobby ` | Set the arena's lobby to current location | `tumble.setlobby` | -| `/tmbl setwaitarea ` | Set the arena's wait area to the current location | `tumble.setwaitarea` | -| `/tmbl setwinnerlobby ` | Set the arena's lobby to the current location | `tumble.setwinnerlobby` | - +| Command | Description | Permission | +|---------------------------------------|-------------------------------------------------------------------------------------|-------------------------| +| `/tumble join [gameType]` | Join a Tumble match. Can infer game type if a game is already started in the arena. | `tumble.join` | +| `/tumble leave` | Leave a Tumble match. | `tumble.leave` | +| `/tumble forcestart [arenaName]` | Force start a Tumble match. Can infer arena if you are in one. | `tumble.forcestart` | +| `/tumble forcestop [arenaName]` | Force stop a Tumble match. Can infer arena if you are in one. | `tumble.forcestop` | +| `/tumble reload` | Reload the plugin's configuration. | `tumble.reload` | +| `/tumble create ` | Create a new arena. | `tumble.create` | +| `/tumble remove ` | Remove an arena. | `tumble.remove` | +| `/tumble setgamespawn ` | Set the arena's game spawn to your current position. | `tumble.setgamespawn` | +| `/tumble setkillylevel ` | Set the arena's Y-level to kill players at to current Y coordinate. | `tumble.setkillylevel` | +| `/tumble setlobby ` | Set the arena's lobby to current location. | `tumble.setlobby` | +| `/tumble setwaitarea ` | Set the arena's wait area to the current location. | `tumble.setwaitarea` | +| `/tumble setwinnerlobby ` | Set the arena's lobby to the current location. | `tumble.setwinnerlobby` | + +Note that the `/tmbl` command can be used as a shorter alias to `/tumble`. ## Configuration -Configuration for this plugin is stored in three files. +Configuration for this plugin is stored in three files: ### config.yml -Stores common settings - -| Option | Type | Default value | -|----------------------------|-------------------|---------------| -| `hide-join-leave-messages` | Boolean | `false` | -| `wait-duration` | Integer (seconds) | `15` | +Stores general settings. +| Option | Type | Description | Default value | +|----------------------------|---------|--------------------------------------------------------------------------------|---------------| +| `hide-join-leave-messages` | Boolean | Hides player join and leave messages in public chat. | `false` | +| `wait-duration` | Integer | Duration (in seconds) to wait for more players to join a game before starting. | `15` | ### arenas.yml -Stores data for each arena. You may add and remove arenas as you wish. +Stores data about each arena. +Arenas may be added and removed as you wish, either via the commands detailed above or by editing the `arenas.yml` file directly. Each arena can contain the following locations: | Location | Description | |--------------------------|-------------------------------------------------------------------------------------| | `game-spawn` **Required* | The location where players will be teleported, and the layers will generate around. | -| `wait-area` | The location where players will be teleported to before the game begins | -| `lobby` | The location where players will be teleported to after the game | -| `winner-lobby` | The location where the winner will be teleported after the game | +| `wait-area` | The location where players will be teleported to before the game begins. | +| `lobby` | The location where players will be teleported to after the game. | +| `winner-lobby` | The location where the winner will be teleported after the game. | Locations are stored using the following format: ```yaml @@ -90,21 +90,17 @@ Locations are stored using the following format: Each arena can also contain the following option: -| Option | Type | Description | -|-------------|---------|-----------------------------------------------------------------| -| `kill-at-y` | Integer | When a player falls below this Y-level, they will be eliminated | +| Option | Type | Description | +|-------------|---------|------------------------------------------------------------------| +| `kill-at-y` | Integer | When a player falls below this Y-level, they will be eliminated. | ### language.yml -Most of this plugin's strings are configurable through this file. (Excluding from some console errors) +Most of this plugin's messages are configurable through this file (excluding some console errors). All plugin chat messages will have the `prefix` prepended to them. -Colors can be added using alternate color codes: -``` -&cRed Text -``` - +Colors can be added using alternate color codes; for example, `&cRed Text` will appear red in-game. ## Issues & Feedback -Feel free to report any bugs, leave feedback, ask questions, or submit ideas for new features on our [GitHub issues page](https://github.com/MylesAndMore/tmbl/issues/new)! +Feel free to report any bugs, leave feedback, ask questions, or submit ideas for new features on the Tumble [GitHub issues page](https://github.com/MylesAndMore/tumble/issues/new)! diff --git a/src/main/java/com/MylesAndMore/Tumble/Main.java b/src/main/java/com/MylesAndMore/Tumble/Main.java index eb1c4e0..3bfa6c0 100644 --- a/src/main/java/com/MylesAndMore/Tumble/Main.java +++ b/src/main/java/com/MylesAndMore/Tumble/Main.java @@ -13,7 +13,7 @@ import java.util.Objects; -public class Main extends JavaPlugin{ +public class Main extends JavaPlugin { public static Main plugin; @Override @@ -32,7 +32,7 @@ public void onEnable() { @Override public void onDisable() { - // stop running games + // Stop any running games for (Arena a : ArenaManager.arenas.values()) { if (a.game != null) { a.game.stopGame(); diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java index 3953bea..29583a4 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java @@ -29,17 +29,16 @@ public String getPermission() { @Override public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { - Game game; + if (args.length < 1 || args[0] == null) { - // no arena passed in, try to infer from game player is in + // No arena passed in, try to infer from game player is in game = ArenaManager.findGamePlayerIsIn((Player)sender); if (game == null) { sender.sendMessage(LanguageManager.fromKey("missing-arena-parameter")); return false; } - } - else { + } else { String arenaName = args[0]; if (!ArenaManager.arenas.containsKey(arenaName)) { sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java index 37d3465..f308979 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java @@ -31,14 +31,13 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Game game; if (args.length < 1 || args[0] == null) { - // no arena passed in, try to infer from game player is in + // No arena passed in, try to infer from game player is in game = ArenaManager.findGamePlayerIsIn((Player)sender); if (game == null) { sender.sendMessage(LanguageManager.fromKey("missing-arena-parameter")); return false; } - } - else { + } else { String arenaName = args[0]; if (!ArenaManager.arenas.containsKey(arenaName)) { sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%",arenaName)); diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Join.java b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java index 6ce33c3..3f1a8fb 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Join.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java @@ -50,8 +50,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command return false; } String arenaName = args[0]; - if (!ArenaManager.arenas.containsKey(arenaName)) - { + if (!ArenaManager.arenas.containsKey(arenaName)) { sender.sendMessage(LanguageManager.fromKey("invalid-arena").replace("%arena%", arenaName)); return false; } @@ -59,17 +58,16 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Game game; if (args.length < 2 || args[1] == null) { - // no type specified: try to infer game type from game taking place in the arena + // No type specified: try to infer game type from game taking place in the arena if (arena.game == null) { - // cant infer if no game is taking place + // Can't infer if no game is taking place sender.sendMessage(LanguageManager.fromKey("specify-game-type")); return false; } game = arena.game; - } - else { - // type specified + } else { + // Game type specified GameType type; switch (args[1]) { case "shovels", "shovel" -> type = GameType.SHOVELS; @@ -84,9 +82,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command if (arena.game == null) { // no game is taking place in this arena, start one game = arena.game = new Game(arena, type); - } - else - { + } else { // a game is taking place in this arena, check that it is the right type if (arena.game.type == type) { game = arena.game; @@ -100,7 +96,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command } } - // check to make sure the arena has a game spawn + // Check to make sure the arena has a game spawn if (game.arena.gameSpawn == null) { if (p.isOp()) { sender.sendMessage(LanguageManager.fromKey("arena-not-ready-op")); diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java b/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java index 79faef9..588755f 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java @@ -29,7 +29,6 @@ public String getPermission() { @Override public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { - for (Arena a : ArenaManager.arenas.values()) { if (a.game != null) { a.game.stopGame(); diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java b/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java index 8b4f925..e7d1cf4 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java @@ -44,7 +44,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command return false; } - // pass command action through to subCommand + // Pass command action through to subCommand subCmd.onCommand(sender, command, args[0], removeFirst(args)); return true; } @@ -52,7 +52,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command @Override public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (args.length == 1) { - // show only subCommands the user has permission for + // Show only subCommands the user has permission for ArrayList PermittedSubCmds = new ArrayList<>(); for (SubCommand subCmd: subCommands.values()) { if (sender.hasPermission(subCmd.getPermission())) { @@ -67,11 +67,10 @@ public List onTabComplete(@NotNull CommandSender sender, @NotNull Comman return Collections.emptyList(); } - // pass tab complete through to subCommand + // Pass tab complete through to subCommand if (subCommands.get(args[0]) instanceof TabCompleter tcmp) { return tcmp.onTabComplete(sender, command, args[0], removeFirst(args)); - } - else { + } else { return null; } } diff --git a/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java index ac7767a..2f21a32 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java @@ -58,12 +58,12 @@ public static void readConfig() { arena.waitArea = readWorld("arenas." + arenaName + ".wait-area"); } - // validate + // Validate arena locations if (arena.gameSpawn == null) { - plugin.getLogger().severe("arenas.yml: Arena " + arenaName + " is missing a game spawn, before you can join you must set it with '/tmbl setgamespawn'."); + plugin.getLogger().severe("arenas.yml: Arena " + arenaName + " is missing a game spawn, before you can join you must set it with '/tumble setgamespawn'."); } - if (arena.gameSpawn == null) { - plugin.getLogger().severe("arenas.yml: Arena " + arenaName + " is missing a lobby location. The spawn point of the default world will be used."); + if (arena.lobby == null) { + plugin.getLogger().warning("arenas.yml: Arena " + arenaName + " is missing a lobby location. The spawn point of the default world will be used."); } arenas.put(arena.name, arena); diff --git a/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java b/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java index 59d653a..2d2bd4c 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java @@ -27,6 +27,7 @@ public static void readConfig() { defaultConfig = Objects.requireNonNull(config.getDefaults()); HideLeaveJoin = config.getBoolean("hide-join-leave-messages", false); waitDuration = config.getInt("wait-duration", 15); + validate(); } diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Arena.java b/src/main/java/com/MylesAndMore/Tumble/game/Arena.java index 71849e6..157c59c 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Arena.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Arena.java @@ -1,7 +1,6 @@ package com.MylesAndMore.Tumble.game; import org.bukkit.Location; -import org.bukkit.World; import org.jetbrains.annotations.NotNull; /** diff --git a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java index 9f1456e..c8331f7 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java @@ -26,7 +26,7 @@ import static com.MylesAndMore.Tumble.Main.plugin; /** - * An event listener for a game of tumble. + * An event listener for a game of Tumble. */ public class EventListener implements Listener { Game game; @@ -55,7 +55,7 @@ public void PlayerQuitEvent(PlayerQuitEvent event) { event.setQuitMessage(null); } - // remove player from game if they leave during a game + // Remove player from game if they leave during a game if (game.gamePlayers.contains(event.getPlayer())) { game.removePlayer(event.getPlayer()); } @@ -63,7 +63,7 @@ public void PlayerQuitEvent(PlayerQuitEvent event) { @EventHandler public void PlayerDeathEvent(PlayerDeathEvent event) { - // inform the game that the player died and respawn them + // Inform the game that the player died and respawn them if (game.gamePlayers.contains(event.getEntity()) && game.gameState == GameState.RUNNING) { game.playerDeath(event.getEntity()); Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> event.getEntity().spigot().respawn(), 10); @@ -109,8 +109,7 @@ public void ProjectileHitEvent(ProjectileHitEvent event) { event.getHitBlock().getType()); event.getHitBlock().setType(Material.AIR); } - } - else if (event.getHitEntity() != null) { + } else if (event.getHitEntity() != null) { if (event.getHitEntity() instanceof Player hitPlayer) { // Also cancel any knockback Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> hitPlayer.setVelocity(new Vector())); @@ -134,7 +133,7 @@ public void PlayerMoveEvent(PlayerMoveEvent event) { if (Objects.equals(game.gameState, GameState.STARTING) && !equalPosition(event.getFrom(),event.getTo())) { event.setCancelled(true); } - // kill player if they are below a Y level + // Kill player if they are below configured Y level if (game.arena.killAtY != null && game.gameState == GameState.RUNNING) { if (event.getPlayer().getLocation().getY() <= game.arena.killAtY) { event.getPlayer().setHealth(0); @@ -226,5 +225,4 @@ public static boolean equalPosition(Location l1, @Nullable Location l2) { (l1.getY() == l2.getY()) && (l1.getZ() == l2.getZ()); } - } \ No newline at end of file diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index e9b7223..5ccb757 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -48,7 +48,7 @@ public Game(@NotNull Arena arena, @NotNull GameType type) { } /** - * Adds a player to the wait area. Called from /tmbl join + * Adds a player to the wait area. Called from /tumble join * Precondition: the game is in state WAITING * @param p Player to add */ @@ -62,15 +62,14 @@ public void addPlayer(Player p) { } if (gamePlayers.size() >= 2 && gameState == GameState.WAITING) { autoStart(); - } - else { + } else { displayActionbar(Collections.singletonList(p), LanguageManager.fromKeyNoPrefix("waiting-for-players")); } } /** * Starts the game - * Called from /tmbl forceStart or after the wait counter finishes + * Called from /tumble forceStart or after the wait counter finishes */ public void gameStart() { @@ -79,15 +78,15 @@ public void gameStart() { return; } - // cancel wait timer + // Cancel wait timer Bukkit.getServer().getScheduler().cancelTask(autoStartID); autoStartID = -1; - // register event listener + // Register event listener eventListener = new EventListener(this); Bukkit.getServer().getPluginManager().registerEvents(eventListener, plugin); - // save inventories (if not already done) + // Save inventories (if not already done) for (Player p : gamePlayers) { if (!inventories.containsKey(p)) { inventories.put(p, p.getInventory().getContents()); @@ -107,22 +106,18 @@ private void roundStart() { scatterPlayers(gamePlayers); // Put all players in spectator to prevent them from getting kicked for flying setGamemode(gamePlayers, GameMode.SPECTATOR); - // do it again in case they were not in the world yet - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { - setGamemode(gamePlayers, GameMode.SPECTATOR); - }, 10); + // Do it again in a bit in case they were not in the world yet + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> setGamemode(gamePlayers, GameMode.SPECTATOR), 10); clearInventories(gamePlayers); clearArena(); prepareGameType(type); // Begin the countdown sequence - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { - countdown(() -> { - setGamemode(gamePlayers, GameMode.SURVIVAL); - gameState = GameState.RUNNING; - }); - }, 100); + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> countdown(() -> { + setGamemode(gamePlayers, GameMode.SURVIVAL); + gameState = GameState.RUNNING; + }), 100); } /** @@ -140,7 +135,7 @@ private void prepareGameType(GameType type) { giveItems(gamePlayers, shovel); // Schedule a process to give snowballs after 2m30s (so people can't island, the OG game had this); - // add 160t because of the countdown + // Also add 160t because of the countdown gameID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { clearInventories(gamePlayers); giveItems(gamePlayers, new ItemStack(Material.SNOWBALL)); @@ -191,13 +186,11 @@ private void roundEnd() { if (gameWins.get(winner) == 3) { gameEnd(); - } - else { // If that player doesn't have three wins, nobody else does, so we need another round + } else { // If that player doesn't have three wins, nobody else does, so we need another round 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 { + } else { displayTitles(gamePlayers, LanguageManager.fromKeyNoPrefix("round-over"), LanguageManager.fromKeyNoPrefix("round-draw"), 5, 60, 5); Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, this::roundStart, 100); } @@ -207,13 +200,12 @@ private void roundEnd() { * Ends game: Displays overall winner and teleports players to lobby */ private void gameEnd() { - if (!gamePlayers.isEmpty()) { setGamemode(gamePlayers, GameMode.SPECTATOR); clearInventories(gamePlayers); - // display winner + // Display winner Player winner = getPlayerWithMostWins(gameWins); if (winner != null) { displayTitles(gamePlayers, LanguageManager.fromKeyNoPrefix("game-over"), LanguageManager.fromKeyNoPrefix("game-winner").replace("%winner%",winner.getDisplayName()), 5, 60, 5); @@ -231,8 +223,7 @@ private void gameEnd() { p.setGameMode(GameMode.SURVIVAL); if (p == winner && arena.winnerLobby != null) { p.teleport(arena.winnerLobby); - } - else { + } else { p.teleport(Objects.requireNonNull(arena.lobby)); } @@ -254,7 +245,7 @@ private void gameEnd() { /** * Stops the game, usually while it is still going - * called if too many players leave, or from /tmbl forceStop + * Called if too many players leave, or from /tumble forceStop */ public void stopGame() { gamePlayers.forEach(this::removePlayer); @@ -275,15 +266,15 @@ public void stopGame() { public void removePlayer(Player p) { gamePlayers.remove(p); - // check if the game has not started yet + // Check if the game has not started yet if (gameState == GameState.WAITING) { - // inform player that there are no longer enough players to start + // Inform player that there are no longer enough players to start if (gamePlayers.size() < 2) { displayActionbar(gamePlayers, LanguageManager.fromKeyNoPrefix("waiting-for-players")); } - // teleport player back and restore inventory + // Teleport player back and restore inventory if (arena.waitArea != null) { p.getInventory().clear(); p.setGameMode(GameMode.SURVIVAL); @@ -292,14 +283,12 @@ public void removePlayer(Player p) { p.getInventory().setContents(inventories.get(p)); } } - } - else { - // stop the game if there are not enough players + } else { + // Stop the game if there are not enough players if (gamePlayers.size() < 2) { stopGame(); } - // teleport player back and restore inventory p.getInventory().clear(); p.setGameMode(GameMode.SURVIVAL); p.teleport(arena.lobby); @@ -329,7 +318,7 @@ public void autoStart() { */ public void playerDeath(Player player) { player.setGameMode(GameMode.SPECTATOR); - // remove that player (who just died) from the roundPlayersArray, effectively eliminating them, + // Remove that player (who just died) from the alive players, effectively eliminating them, playersAlive.remove(player); // If there are less than 2 players in the game (1 just died), if (playersAlive.size() < 2 && gameState == GameState.RUNNING) { @@ -337,7 +326,7 @@ public void playerDeath(Player player) { } } - // utility functions + // -- Utility functions -- /** * Teleports a list of players to the specified scatter locations in the gameWorld diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Generator.java b/src/main/java/com/MylesAndMore/Tumble/game/Generator.java index c8ecb06..d91c9e6 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Generator.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Generator.java @@ -6,6 +6,8 @@ import java.util.*; +// TODO: clean up generator (when layers refactor is done) + /** * Holds the methods that generate blocks in-game such as cylinders, cuboids, and block clumps. */ @@ -24,12 +26,10 @@ public static void generateLayersShovels(Location layer) { if (random.nextInt(4) == 0) { // Circular layer Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.SNOW_BLOCK), layers.getSafeMaterialList()); - } - else if (random.nextInt(4) == 1) { + } else if (random.nextInt(4) == 1) { // Square layer Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.SNOW_BLOCK), layers.getSafeMaterialList()); - } - else if (random.nextInt(4) == 2) { + } else if (random.nextInt(4) == 2) { // Multi-tiered circle Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.SNOW_BLOCK), layers.getSafeMaterialList()); Generator.generateLayer(layer, 13, 1, Material.AIR); @@ -38,8 +38,7 @@ else if (random.nextInt(4) == 2) { Generator.generateLayer(layer, 4, 1, Material.AIR); layer.setY(layer.getY() - 1); Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.PODZOL), layers.getMaterialList()); - } - else { + } else { // Multi-tiered square Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.SNOW_BLOCK), layers.getSafeMaterialList()); Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR); @@ -68,16 +67,14 @@ public static void generateLayersSnowballs(Location layer) { Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getMaterialList()); layer.setY(layer.getY() - 6); Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getMaterialList()); - } - else if (random.nextInt(4) == 1) { + } else if (random.nextInt(4) == 1) { // Square layer Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getSafeMaterialList()); layer.setY(layer.getY() - 6); Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getMaterialList()); layer.setY(layer.getY() - 6); Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getMaterialList()); - } - else if (random.nextInt(4) == 2) { + } else if (random.nextInt(4) == 2) { // Multi-tiered circle Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getSafeMaterialList()); Generator.generateLayer(layer, 13, 1, Material.AIR); @@ -104,8 +101,7 @@ else if (random.nextInt(4) == 2) { Generator.generateLayer(layer, 4, 1, Material.AIR); layer.setY(layer.getY() - 1); Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); - } - else { + } else { // Multi-tiered square Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getSafeMaterialList()); Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR); diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Layers.java b/src/main/java/com/MylesAndMore/Tumble/game/Layers.java index 8954732..1685843 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Layers.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Layers.java @@ -6,6 +6,8 @@ import java.util.List; import java.util.Random; +// TODO: make layers user-configurable through a LayerManager and a yaml + /** * Stores the different types of layers that can be generated */ @@ -284,8 +286,7 @@ public Layers() { safeMatList.add(gen11); // Troll glass layer } - // Define Random class - Random random = new Random(); + final Random random = new Random(); /** * @return A random predefined List of Materials that are okay to use in the clump generator */ diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/GameState.java b/src/main/java/com/MylesAndMore/Tumble/plugin/GameState.java index 06b2abe..e27c728 100644 --- a/src/main/java/com/MylesAndMore/Tumble/plugin/GameState.java +++ b/src/main/java/com/MylesAndMore/Tumble/plugin/GameState.java @@ -4,5 +4,5 @@ public enum GameState { WAITING, STARTING, RUNNING, - ENDING + ENDING, } diff --git a/src/main/resources/arenas.yml b/src/main/resources/arenas.yml index 3b755c3..6d3e3bb 100644 --- a/src/main/resources/arenas.yml +++ b/src/main/resources/arenas.yml @@ -1,2 +1,2 @@ -# NOTE: Coordinates cannot be zero! Use something like 0.5 -arenas: {} \ No newline at end of file +# NOTE: No coordinate can be equal to zero! Use 0.5 instead if needed. +arenas: {} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index ded50ef..b15acaf 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,7 +1,5 @@ -# Hides player join/leave messages in public chat +# Hides player join and leave messages in public chat hide-join-leave-messages: false -# Duration in seconds to wait for more players to join +# Duration (in seconds) to wait for more players to join a game before starting wait-duration: 15 - - diff --git a/src/main/resources/language.yml b/src/main/resources/language.yml index 835d577..dabb5f4 100644 --- a/src/main/resources/language.yml +++ b/src/main/resources/language.yml @@ -1,5 +1,7 @@ +# All plugin chat messages will have this message prepended to them prefix: "&f[&eTumble&f] " +# Error messages 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!" @@ -9,13 +11,14 @@ 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 '/tmbl join %arena% %type%'." +another-type-in-arena: "A game of %type% is currently taking place in this arena!&7 choose another arena or join it with '/tumble join %arena% %type%'." already-in-game: "&4You are already in a game! Leave it to join another one." arena-not-ready: "&4This arena is not finished being set up!" -arena-not-ready-op: "&4Incomplete arena. &7Set a game spawn with '/tmbl setGameSpawn'." +arena-not-ready-op: "&4Incomplete arena. &7Set a game spawn with '/tumble setGameSpawn'." specify-game-type: "&4No game is currently taking place in this arena! &7Provide the game type to start one." -create-success: "&aArena created successfully! &eBefore you can join, you must set a game spawn location with '/tmbl setgamespawn'." +# Success messages +create-success: "&aArena created successfully! &eBefore you can join, you must set a game spawn location with '/tumble setgamespawn'." forcestart-success: "&aStarting game." forcestop-success: "&aGame stopped." join-success: "&aJoined game &d%arena% - %type%" @@ -24,6 +27,7 @@ reload-success: "&aConfig files reloaded. &eCheck console for errors." remove-success: "&aArena removed." set-success: "&aLocation set." +# Game messages showdown: "&4Showdown!" lobby-in-10: "&9Returning to lobby in ten seconds..." waiting-for-players: "&aWaiting for players" diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index c8085b8..da7f8bd 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,6 +1,6 @@ main: com.MylesAndMore.Tumble.Main name: Tumble -version: 1.0.4 +version: 2.0.0 description: 'A Minecraft: Java Edition plugin recreating the Tumble minigame from Minecraft Legacy Console Edition.' api-version: 1.16 load: POSTWORLD @@ -10,8 +10,8 @@ softdepend: [Multiverse-Core] commands: tumble: - description: Base command for Rumble - usage: "/tmbl ..." + description: Base command for Tumble + usage: "/tumble ..." aliases: tmbl permissions: @@ -22,23 +22,33 @@ permissions: description: Allows you to leave a Tumble match. default: true tumble.forcestart: + description: Allows you to forcibly start a Tumble match. default: op tumble.forcestop: + description: Allows you to forcibly stop a Tumble match. default: op tumble.reload: + description: Allows you to reload the Tumble configuration. default: op tumble.create: + description: Allows you to create Tumble arenas. default: op tumble.remove: + description: Allows you to remove Tumble arenas. default: op tumble.setgamespawn: + description: Allows you to set the game spawn location for Tumble arenas. default: op tumble.setkillylevel: + description: Allows you to set the kill Y-level for Tumble arenas. default: op tumble.setlobby: + description: Allows you to set the lobby location for Tumble arenas. default: op tumble.setwaitarea: + description: Allows you to set the wait area location for Tumble arenas. default: op tumble.setwinnerlobby: + description: Allows you to set the winner lobby location for Tumble arenas. default: op From fa20be5af2c8d260393a26ba01c2193eba047ba6 Mon Sep 17 00:00:00 2001 From: sowgro Date: Thu, 1 Aug 2024 15:08:43 -0400 Subject: [PATCH 32/51] layers.yml (not tested) --- .../java/com/MylesAndMore/Tumble/Main.java | 2 + .../Tumble/config/ArenaManager.java | 4 +- .../Tumble/config/LanguageManager.java | 2 +- .../Tumble/config/LayerManager.java | 66 ++++ .../MylesAndMore/Tumble/game/Generator.java | 67 ++-- .../com/MylesAndMore/Tumble/game/Layers.java | 310 ------------------ src/main/resources/layers.yml | 202 ++++++++++++ 7 files changed, 305 insertions(+), 348 deletions(-) create mode 100644 src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java delete mode 100644 src/main/java/com/MylesAndMore/Tumble/game/Layers.java create mode 100644 src/main/resources/layers.yml diff --git a/src/main/java/com/MylesAndMore/Tumble/Main.java b/src/main/java/com/MylesAndMore/Tumble/Main.java index eb1c4e0..b85636d 100644 --- a/src/main/java/com/MylesAndMore/Tumble/Main.java +++ b/src/main/java/com/MylesAndMore/Tumble/Main.java @@ -5,6 +5,7 @@ import com.MylesAndMore.Tumble.config.ConfigManager; import com.MylesAndMore.Tumble.config.LanguageManager; +import com.MylesAndMore.Tumble.config.LayerManager; import com.MylesAndMore.Tumble.game.Arena; import org.bstats.bukkit.Metrics; @@ -23,6 +24,7 @@ public void onEnable() { LanguageManager.readConfig(); ConfigManager.readConfig(); ArenaManager.readConfig(); + LayerManager.readConfig(); Objects.requireNonNull(this.getCommand("tumble")).setExecutor(new Tumble()); new Metrics(this, 16940); diff --git a/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java index ac7767a..5e6e571 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java @@ -120,9 +120,7 @@ public static Game findGamePlayerIsIn(Player p) { * z: * world: * @param path The section in the yaml with x, y, z, and world as its children - * @return Result of either: - * Result#success = true and Result#value OR - * Result#success = false and Result#error + * @return The location specified by the section, or null if the location is not valid */ private static Location readWorld(String path) { diff --git a/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java b/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java index 6625b26..843d7c0 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java @@ -12,11 +12,11 @@ * Manages language.yml and allows retrieval of keys */ public class LanguageManager { + private static final CustomConfig languageYml = new CustomConfig("language.yml"); private static Configuration config; private static Configuration defaultConfig; public static void readConfig() { - CustomConfig languageYml = new CustomConfig("language.yml"); languageYml.saveDefaultConfig(); config = languageYml.getConfig(); defaultConfig = Objects.requireNonNull(config.getDefaults()); diff --git a/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java b/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java new file mode 100644 index 0000000..d995305 --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java @@ -0,0 +1,66 @@ +package com.MylesAndMore.Tumble.config; + +import com.MylesAndMore.Tumble.plugin.CustomConfig; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import static com.MylesAndMore.Tumble.Main.plugin; + +public class LayerManager { + public static List> layers; + + private static final CustomConfig layersYml = new CustomConfig("layers.yml"); + private static final FileConfiguration config = layersYml.getConfig(); + + /** + * Read layers from layers.yml and populate this.layers + */ + public static void readConfig() { + layersYml.saveDefaultConfig(); + + ConfigurationSection layersSection = config.getConfigurationSection("layers"); + if (layersSection == null) { + plugin.getLogger().warning("layers.yml is missing section 'layers'"); + return; + } + for (String layerPath : layersSection.getKeys(false)) { + List layer = readLayer(layerPath); + if (layer == null) { + plugin.getLogger().warning("layers.yml: error loading layer at'"+layerPath+"'"); + } else { + layers.add(layer); + } + } + } + + /** + * Read the list of materials for a layer. + * @param path The path of the layer in the config + * @return The list of materials for the layer to be composed of + */ + public static List readLayer(String path) { + List list = (List) config.getList(path); + if (list == null) { + return null; + } + List layer = new ArrayList<>(); + for (String entry : list) { + Material tmp = Material.getMaterial(entry); + if (tmp == null) { + + return null; + } + layer.add(tmp); + } + return layer; + } + + public static List getRandom() { + return layers.get(new Random().nextInt(layers.size())); + } +} diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Generator.java b/src/main/java/com/MylesAndMore/Tumble/game/Generator.java index c8ecb06..2ef0957 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Generator.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Generator.java @@ -1,5 +1,6 @@ package com.MylesAndMore.Tumble.game; +import com.MylesAndMore.Tumble.config.LayerManager; import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; @@ -17,37 +18,36 @@ public class Generator { */ public static void generateLayersShovels(Location layer) { Random random = new Random(); - Layers layers = new Layers(); layer.setY(layer.getY() - 1); // Choose a random type of generation; a circular layer, a square layer, or a multi-tiered layer of either variety if (random.nextInt(4) == 0) { // Circular layer - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.SNOW_BLOCK), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.SNOW_BLOCK), LayerManager.getRandom()); } else if (random.nextInt(4) == 1) { // Square layer - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.SNOW_BLOCK), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.SNOW_BLOCK), LayerManager.getRandom()); } else if (random.nextInt(4) == 2) { // Multi-tiered circle - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.SNOW_BLOCK), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.SNOW_BLOCK), LayerManager.getRandom()); Generator.generateLayer(layer, 13, 1, Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRASS_BLOCK), layers.getMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRASS_BLOCK), LayerManager.getRandom()); Generator.generateLayer(layer, 4, 1, Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.PODZOL), layers.getMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.PODZOL), LayerManager.getRandom()); } else { // Multi-tiered square - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.SNOW_BLOCK), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.SNOW_BLOCK), LayerManager.getRandom()); Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRASS_BLOCK), layers.getMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRASS_BLOCK), LayerManager.getRandom()); Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.PODZOL), layers.getMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.PODZOL), LayerManager.getRandom()); } } @@ -57,81 +57,80 @@ else if (random.nextInt(4) == 2) { */ public static void generateLayersSnowballs(Location layer) { Random random = new Random(); - Layers layers = new Layers(); layer.setY(layer.getY() - 1); // Similar generation to shovels, except there are three layers if (random.nextInt(4) == 0) { // Circular layer - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), LayerManager.getRandom()); layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), LayerManager.getRandom()); layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), LayerManager.getRandom()); } else if (random.nextInt(4) == 1) { // Square layer - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), LayerManager.getRandom()); layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), LayerManager.getRandom()); layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), LayerManager.getRandom()); } else if (random.nextInt(4) == 2) { // Multi-tiered circle - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), LayerManager.getRandom()); Generator.generateLayer(layer, 13, 1, Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRANITE), layers.getMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRANITE), LayerManager.getRandom()); Generator.generateLayer(layer, 4, 1, Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.LIME_GLAZED_TERRACOTTA), LayerManager.getRandom()); layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), LayerManager.getRandom()); Generator.generateLayer(layer, 13, 1, Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRANITE), layers.getMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRANITE), LayerManager.getRandom()); Generator.generateLayer(layer, 4, 1, Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.LIME_GLAZED_TERRACOTTA), LayerManager.getRandom()); layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), LayerManager.getRandom()); Generator.generateLayer(layer, 13, 1, Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRANITE), layers.getMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRANITE), LayerManager.getRandom()); Generator.generateLayer(layer, 4, 1, Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.LIME_GLAZED_TERRACOTTA), LayerManager.getRandom()); } else { // Multi-tiered square - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), LayerManager.getRandom()); Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRANITE), layers.getMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRANITE), LayerManager.getRandom()); Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.LIME_GLAZED_TERRACOTTA), LayerManager.getRandom()); layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), LayerManager.getRandom()); Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRANITE), layers.getMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRANITE), LayerManager.getRandom()); Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.LIME_GLAZED_TERRACOTTA), LayerManager.getRandom()); layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), LayerManager.getRandom()); Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRANITE), layers.getMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRANITE), LayerManager.getRandom()); Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.LIME_GLAZED_TERRACOTTA), LayerManager.getRandom()); } } diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Layers.java b/src/main/java/com/MylesAndMore/Tumble/game/Layers.java deleted file mode 100644 index 8954732..0000000 --- a/src/main/java/com/MylesAndMore/Tumble/game/Layers.java +++ /dev/null @@ -1,310 +0,0 @@ -package com.MylesAndMore.Tumble.game; - -import org.bukkit.Material; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -/** - * Stores the different types of layers that can be generated - */ -public class Layers { - - public Layers() { - List gen2 = new ArrayList<>() {{ - add(Material.PINK_TERRACOTTA); - add(Material.PURPLE_TERRACOTTA); - add(Material.GRAY_TERRACOTTA); - add(Material.BLUE_TERRACOTTA); - add(Material.LIGHT_BLUE_TERRACOTTA); - add(Material.WHITE_TERRACOTTA); - add(Material.BROWN_TERRACOTTA); - add(Material.GREEN_TERRACOTTA); - add(Material.YELLOW_TERRACOTTA); - add(Material.PINK_TERRACOTTA); - add(Material.PURPLE_TERRACOTTA); - add(Material.GRAY_TERRACOTTA); - add(Material.BLUE_TERRACOTTA); - add(Material.LIGHT_BLUE_TERRACOTTA); - add(Material.WHITE_TERRACOTTA); - add(Material.BROWN_TERRACOTTA); - add(Material.GREEN_TERRACOTTA); - add(Material.YELLOW_TERRACOTTA); - add(Material.WHITE_STAINED_GLASS); - add(Material.HONEYCOMB_BLOCK); - add(Material.HONEYCOMB_BLOCK); - }}; - List gen4 = new ArrayList<>() {{ - add(Material.DIAMOND_BLOCK); - add(Material.GOLD_BLOCK); - add(Material.REDSTONE_BLOCK); - add(Material.REDSTONE_BLOCK); - add(Material.LAPIS_BLOCK); - add(Material.LAPIS_BLOCK); - add(Material.IRON_BLOCK); - add(Material.COAL_BLOCK); - add(Material.IRON_BLOCK); - add(Material.COAL_BLOCK); - add(Material.IRON_BLOCK); - add(Material.COAL_BLOCK); - add(Material.COAL_BLOCK); - }}; - List gen5 = new ArrayList<>() {{ - add(Material.WHITE_TERRACOTTA); - add(Material.BLUE_ICE); - add(Material.SOUL_SAND); - add(Material.STONE_SLAB); - add(Material.WHITE_TERRACOTTA); - add(Material.BLUE_ICE); - add(Material.SOUL_SAND); - add(Material.STONE_SLAB); - add(Material.WHITE_TERRACOTTA); - add(Material.BLUE_ICE); - add(Material.SOUL_SAND); - add(Material.STONE_SLAB); - add(Material.GLOWSTONE); - add(Material.GLOWSTONE); - add(Material.HONEY_BLOCK); - add(Material.SLIME_BLOCK); - }}; - List gen7 = new ArrayList<>() {{ - add(Material.END_STONE); - add(Material.END_STONE_BRICKS); - add(Material.END_STONE); - add(Material.END_STONE_BRICKS); - add(Material.END_STONE); - add(Material.END_STONE_BRICKS); - add(Material.END_STONE); - add(Material.END_STONE_BRICKS); - add(Material.OBSIDIAN); - add(Material.PURPUR_BLOCK); - add(Material.PURPUR_PILLAR); - add(Material.COBBLESTONE); - }}; - List gen9 = new ArrayList<>() {{ - add(Material.PRISMARINE); - add(Material.DARK_PRISMARINE); - add(Material.BLUE_STAINED_GLASS); - add(Material.WET_SPONGE); - add(Material.PRISMARINE_BRICKS); - add(Material.PRISMARINE_BRICK_SLAB); - add(Material.DARK_PRISMARINE); - add(Material.SEA_LANTERN); - add(Material.TUBE_CORAL_BLOCK); - add(Material.BRAIN_CORAL_BLOCK); - add(Material.BUBBLE_CORAL_BLOCK); - }}; - List gen10 = new ArrayList<>() {{ - add(Material.OAK_LOG); - add(Material.SPRUCE_LOG); - add(Material.ACACIA_LOG); - add(Material.STRIPPED_OAK_LOG); - add(Material.STRIPPED_SPRUCE_LOG); - add(Material.STRIPPED_ACACIA_LOG); - add(Material.OAK_WOOD); - add(Material.SPRUCE_WOOD); - add(Material.ACACIA_WOOD); - add(Material.OAK_LEAVES); - add(Material.SPRUCE_LEAVES); - add(Material.ACACIA_LEAVES); - add(Material.OAK_LEAVES); - add(Material.SPRUCE_LEAVES); - add(Material.ACACIA_LEAVES); - }}; - List gen1 = new ArrayList<>() {{ - add(Material.YELLOW_GLAZED_TERRACOTTA); - add(Material.LIGHT_BLUE_GLAZED_TERRACOTTA); - add(Material.GRAY_GLAZED_TERRACOTTA); - add(Material.PODZOL); - add(Material.PODZOL); - add(Material.PODZOL); - add(Material.ORANGE_GLAZED_TERRACOTTA); - }}; - for (int i = 0; i < 3; i++) { - List gen0 = new ArrayList<>() {{ - add(Material.COAL_ORE); - add(Material.COAL_ORE); - add(Material.COAL_ORE); - add(Material.COAL_ORE); - add(Material.COAL_ORE); - add(Material.IRON_ORE); - add(Material.REDSTONE_ORE); - add(Material.EMERALD_ORE); - add(Material.GOLD_ORE); - add(Material.LAPIS_ORE); - add(Material.DIAMOND_ORE); - add(Material.GRASS_BLOCK); - add(Material.GRASS_BLOCK); - add(Material.GRASS_BLOCK); - add(Material.GRASS_BLOCK); - add(Material.COBWEB); - }}; - matList.add(gen0); - matList.add(gen1); - matList.add(gen2); - List gen3 = new ArrayList<>() {{ - add(Material.PACKED_ICE); - add(Material.PACKED_ICE); - add(Material.JUKEBOX); - add(Material.TNT); - add(Material.LIGHT_BLUE_CONCRETE); - add(Material.GLASS); - add(Material.PACKED_ICE); - add(Material.PACKED_ICE); - add(Material.JUKEBOX); - add(Material.TNT); - add(Material.LIGHT_BLUE_CONCRETE); - add(Material.GLASS); - add(Material.SOUL_SAND); - }}; - matList.add(gen3); - matList.add(gen4); - matList.add(gen5); - List gen6 = new ArrayList<>() {{ - add(Material.NETHERRACK); - add(Material.NETHERRACK); - add(Material.NETHERRACK); - add(Material.NETHER_BRICKS); - add(Material.NETHER_BRICKS); - add(Material.NETHERRACK); - add(Material.NETHERRACK); - add(Material.NETHERRACK); - add(Material.NETHER_BRICKS); - add(Material.NETHER_BRICKS); - add(Material.NETHER_GOLD_ORE); - add(Material.NETHER_GOLD_ORE); - add(Material.CRIMSON_NYLIUM); - add(Material.WARPED_NYLIUM); - add(Material.SOUL_SOIL); - add(Material.CRACKED_NETHER_BRICKS); - add(Material.RED_NETHER_BRICKS); - add(Material.NETHER_WART_BLOCK); - add(Material.CRYING_OBSIDIAN); - add(Material.MAGMA_BLOCK); - }}; - matList.add(gen6); - matList.add(gen7); - List gen8 = new ArrayList<>() {{ - add(Material.REDSTONE_BLOCK); - add(Material.REDSTONE_BLOCK); - add(Material.REDSTONE_LAMP); - add(Material.TARGET); - add(Material.SLIME_BLOCK); - add(Material.OBSERVER); - }}; - matList.add(gen8); - matList.add(gen9); - matList.add(gen10); - List gen12 = new ArrayList<>() {{ - add(Material.DIRT); - add(Material.GRASS_PATH); - add(Material.GRASS_BLOCK); - add(Material.OAK_SLAB); - add(Material.BRICK_WALL); - add(Material.BRICK_STAIRS); - }}; - matList.add(gen12); - List gen14 = new ArrayList<>() {{ - add(Material.OAK_PLANKS); - add(Material.OBSIDIAN); - add(Material.SPONGE); - add(Material.BEEHIVE); - add(Material.DRIED_KELP_BLOCK); - }}; - matList.add(gen14); - List gen15 = new ArrayList<>() {{ - add(Material.SANDSTONE); - add(Material.SANDSTONE_SLAB); - add(Material.RED_SANDSTONE); - add(Material.RED_SANDSTONE_SLAB); - add(Material.RED_TERRACOTTA); - add(Material.TERRACOTTA); - add(Material.YELLOW_TERRACOTTA); - }}; - matList.add(gen15); - List gen16 = new ArrayList<>() {{ - add(Material.JUNGLE_LOG); - add(Material.STRIPPED_JUNGLE_LOG); - add(Material.JUNGLE_WOOD); - add(Material.STRIPPED_JUNGLE_WOOD); - add(Material.MOSSY_COBBLESTONE); - add(Material.MOSSY_COBBLESTONE); - add(Material.MOSSY_COBBLESTONE); - add(Material.JUNGLE_LEAVES); - add(Material.JUNGLE_SLAB); - add(Material.JUNGLE_TRAPDOOR); - }}; - matList.add(gen16); - } - List gen11 = new ArrayList<>() {{ - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.GLASS); - add(Material.WHITE_STAINED_GLASS); - }}; - matList.add(gen11); // Troll glass layer - - for (int i = 0; i < 2; i++) { - safeMatList.add(gen1); - safeMatList.add(gen2); - safeMatList.add(gen4); - safeMatList.add(gen5); - safeMatList.add(gen7); - safeMatList.add(gen9); - safeMatList.add(gen10); - } - safeMatList.add(gen11); // Troll glass layer - } - - // Define Random class - Random random = new Random(); - /** - * @return A random predefined List of Materials that are okay to use in the clump generator - */ - public List getMaterialList() { - return matList.get(random.nextInt(matList.size())); - } - - /** - * @return A random predefined List of Materials that are okay to spawn players on top of - */ - public List getSafeMaterialList() { return safeMatList.get(random.nextInt(safeMatList.size())); } - - // Template: - // private final List gen = new ArrayList<>() {{ - // add(Material. - // }}; - - private final List> matList = new ArrayList<>(); - - private final List> safeMatList = new ArrayList<>(); - -} diff --git a/src/main/resources/layers.yml b/src/main/resources/layers.yml new file mode 100644 index 0000000..acc5be9 --- /dev/null +++ b/src/main/resources/layers.yml @@ -0,0 +1,202 @@ +layers: + 1: + - YELLOW_GLAZED_TERRACOTTA + - LIGHT_BLUE_GLAZED_TERRACOTTA + - GRAY_GLAZED_TERRACOTTA + - PODZOL + - PODZOL + - PODZOL + - ORANGE_GLAZED_TERRACOTTA + 2: + - PINK_TERRACOTTA + - PURPLE_TERRACOTTA + - GRAY_TERRACOTTA + - BLUE_TERRACOTTA + - LIGHT_BLUE_TERRACOTTA + - WHITE_TERRACOTTA + - BROWN_TERRACOTTA + - GREEN_TERRACOTTA + - YELLOW_TERRACOTTA + - PINK_TERRACOTTA + - PURPLE_TERRACOTTA + - GRAY_TERRACOTTA + - BLUE_TERRACOTTA + - LIGHT_BLUE_TERRACOTTA + - WHITE_TERRACOTTA + - BROWN_TERRACOTTA + - GREEN_TERRACOTTA + - YELLOW_TERRACOTTA + - WHITE_STAINED_GLASS + - HONEYCOMB_BLOCK + - HONEYCOMB_BLOCK + 3: + - DIAMOND_BLOCK + - GOLD_BLOCK + - REDSTONE_BLOCK + - REDSTONE_BLOCK + - LAPIS_BLOCK + - LAPIS_BLOCK + - IRON_BLOCK + - COAL_BLOCK + - IRON_BLOCK + - COAL_BLOCK + - IRON_BLOCK + - COAL_BLOCK + - COAL_BLOCK + 4: + - WHITE_TERRACOTTA + - BLUE_ICE + - SOUL_SAND + - STONE_SLAB + - WHITE_TERRACOTTA + - BLUE_ICE + - SOUL_SAND + - STONE_SLAB + - WHITE_TERRACOTTA + - BLUE_ICE + - SOUL_SAND + - STONE_SLAB + - GLOWSTONE + - GLOWSTONE + - HONEY_BLOCK + - SLIME_BLOCK + 5: + - END_STONE + - END_STONE_BRICKS + - END_STONE + - END_STONE_BRICKS + - END_STONE + - END_STONE_BRICKS + - END_STONE + - END_STONE_BRICKS + - OBSIDIAN + - PURPUR_BLOCK + - PURPUR_PILLAR + - COBBLESTONE + 6: + - PRISMARINE + - DARK_PRISMARINE + - BLUE_STAINED_GLASS + - WET_SPONGE + - PRISMARINE_BRICKS + - PRISMARINE_BRICK_SLAB + - DARK_PRISMARINE + - SEA_LANTERN + - TUBE_CORAL_BLOCK + - BRAIN_CORAL_BLOCK + - BUBBLE_CORAL_BLOCK + 7: + - OAK_LOG + - SPRUCE_LOG + - ACACIA_LOG + - STRIPPED_OAK_LOG + - STRIPPED_SPRUCE_LOG + - STRIPPED_ACACIA_LOG + - OAK_WOOD + - SPRUCE_WOOD + - ACACIA_WOOD + - OAK_LEAVES + - SPRUCE_LEAVES + - ACACIA_LEAVES + - OAK_LEAVES + - SPRUCE_LEAVES + - ACACIA_LEAVES + 8: + - YELLOW_GLAZED_TERRACOTTA + - LIGHT_BLUE_GLAZED_TERRACOTTA + - GRAY_GLAZED_TERRACOTTA + - PODZOL + - PODZOL + - PODZOL + - ORANGE_GLAZED_TERRACOTTA + 9: + - COAL_ORE + - COAL_ORE + - COAL_ORE + - COAL_ORE + - COAL_ORE + - IRON_ORE + - REDSTONE_ORE + - EMERALD_ORE + - GOLD_ORE + - LAPIS_ORE + - DIAMOND_ORE + - GRASS_BLOCK + - GRASS_BLOCK + - GRASS_BLOCK + - GRASS_BLOCK + - COBWEB + 10: + - PACKED_ICE + - PACKED_ICE + - JUKEBOX + - TNT + - LIGHT_BLUE_CONCRETE + - GLASS + - PACKED_ICE + - PACKED_ICE + - JUKEBOX + - TNT + - LIGHT_BLUE_CONCRETE + - GLASS + - SOUL_SAND + 11: + - NETHERRACK + - NETHERRACK + - NETHERRACK + - NETHER_BRICKS + - NETHER_BRICKS + - NETHERRACK + - NETHERRACK + - NETHERRACK + - NETHER_BRICKS + - NETHER_BRICKS + - NETHER_GOLD_ORE + - NETHER_GOLD_ORE + - CRIMSON_NYLIUM + - WARPED_NYLIUM + - SOUL_SOIL + - CRACKED_NETHER_BRICKS + - RED_NETHER_BRICKS + - NETHER_WART_BLOCK + - CRYING_OBSIDIAN + - MAGMA_BLOCK + 12: + - REDSTONE_BLOCK + - REDSTONE_BLOCK + - REDSTONE_LAMP + - TARGET + - SLIME_BLOCK + - OBSERVER + 13: + - DIRT + - GRASS_PATH + - GRASS_BLOCK + - OAK_SLAB + - BRICK_WALL + - BRICK_STAIRS + 14: + - OAK_PLANKS + - OBSIDIAN + - SPONGE + - BEEHIVE + - DRIED_KELP_BLOCK + 15: + - SANDSTONE + - SANDSTONE_SLAB + - RED_SANDSTONE + - RED_SANDSTONE_SLAB + - RED_TERRACOTTA + - TERRACOTTA + - YELLOW_TERRACOTTA + 16: + - JUNGLE_LOG + - STRIPPED_JUNGLE_LOG + - JUNGLE_WOOD + - STRIPPED_JUNGLE_WOOD + - MOSSY_COBBLESTONE + - MOSSY_COBBLESTONE + - MOSSY_COBBLESTONE + - JUNGLE_LEAVES + - JUNGLE_SLAB + - JUNGLE_TRAPDOOR \ No newline at end of file From cadddd7499f92b4529ce34231ff4a379011515ff Mon Sep 17 00:00:00 2001 From: Myles Date: Thu, 1 Aug 2024 16:17:01 -0500 Subject: [PATCH 33/51] bugfixes and more stylistic changes - remove update checker and multiverse from build dependencies - add config option to hide death messages - ensure join/leave/death messages only apply for tumble games - fix a bunch of random command bugs I found - layers no longer generate with a placeholder material before being clumped - misc refactoring --- README.md | 1 + build.gradle | 10 +-- .../MylesAndMore/Tumble/commands/Create.java | 2 +- .../MylesAndMore/Tumble/commands/Join.java | 25 +++---- .../MylesAndMore/Tumble/commands/Remove.java | 2 +- .../Tumble/commands/SetGameSpawn.java | 2 +- .../Tumble/commands/SetKillYLevel.java | 2 +- .../Tumble/commands/SetLobby.java | 2 +- .../Tumble/commands/SetWaitArea.java | 2 +- .../Tumble/commands/SetWinnerLobby.java | 2 +- .../Tumble/config/ArenaManager.java | 39 +++++----- .../Tumble/config/ConfigManager.java | 6 +- .../Tumble/game/EventListener.java | 13 ++-- .../com/MylesAndMore/Tumble/game/Game.java | 71 +++++++++---------- .../MylesAndMore/Tumble/game/Generator.java | 64 ++++++++--------- src/main/resources/arenas.yml | 2 +- src/main/resources/config.yml | 6 +- src/main/resources/language.yml | 40 +++++------ 18 files changed, 148 insertions(+), 143 deletions(-) diff --git a/README.md b/README.md index fe8f925..52dc9c2 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,7 @@ Locations are stored using the following format: z: 0.5 world: worldName ``` +If a location is not specified, players will not be teleported by the plugin. Each arena can also contain the following option: diff --git a/build.gradle b/build.gradle index 58b2dc8..41f2c92 100644 --- a/build.gradle +++ b/build.gradle @@ -10,18 +10,15 @@ java { } repositories { - mavenCentral() // Use Maven Central for resolving dependencies. + mavenCentral() // Use Maven Central for resolving dependencies maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } maven { url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' } - maven { url "https://repo.onarandombox.com/content/groups/public/" } - maven { url "https://repo.jeff-media.com/public/"} } dependencies { + compileOnly('org.jetbrains:annotations:24.1.0') compileOnly('org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT') - compileOnly('com.onarandombox.multiversecore:Multiverse-Core:4.3.1') implementation('org.bstats:bstats-bukkit:3.0.2') - implementation('com.jeff_media:SpigotUpdateChecker:3.0.3') } // Disable generation of the normal JAR to reduce confusion and only generate the correctly shadowed JAR including bStats @@ -30,7 +27,6 @@ jar.finalizedBy(shadowJar) shadowJar { archiveBaseName.set('Tumble') archiveClassifier.set('') - archiveVersion.set('1.0.4') + archiveVersion.set('2.0.0') relocate 'org.bstats', 'com.MylesAndMore.bstats' - relocate 'com.jeff_media.updatechecker', 'com.MylesAndMore.updatechecker' } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Create.java b/src/main/java/com/MylesAndMore/Tumble/commands/Create.java index 667be5e..d60ca64 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Create.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Create.java @@ -33,7 +33,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command String arenaName = args[0]; ArenaManager.arenas.put(arenaName, new Arena(arenaName)); - ArenaManager.WriteConfig(); + ArenaManager.writeConfig(); sender.sendMessage(LanguageManager.fromKey("create-success")); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Join.java b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java index 3f1a8fb..f26d4e5 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Join.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java @@ -56,6 +56,16 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command } Arena arena = ArenaManager.arenas.get(arenaName); + // Check to make sure this arena has a game spawn + if (arena.gameSpawn == null) { + if (p.isOp()) { + sender.sendMessage(LanguageManager.fromKey("arena-not-ready-op")); + } else { + sender.sendMessage(LanguageManager.fromKey("arena-not-ready")); + } + return false; + } + Game game; if (args.length < 2 || args[1] == null) { // No type specified: try to infer game type from game taking place in the arena @@ -80,10 +90,10 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command } if (arena.game == null) { - // no game is taking place in this arena, start one + // No game is taking place in this arena, start one game = arena.game = new Game(arena, type); } else { - // a game is taking place in this arena, check that it is the right type + // A game is taking place in this arena, check that it is the right type if (arena.game.type == type) { game = arena.game; } @@ -96,16 +106,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command } } - // Check to make sure the arena has a game spawn - if (game.arena.gameSpawn == null) { - if (p.isOp()) { - sender.sendMessage(LanguageManager.fromKey("arena-not-ready-op")); - } else { - sender.sendMessage(LanguageManager.fromKey("arena-not-ready")); - } - return false; - } - + // Make sure the game isn't in progress before adding the player if (game.gameState != GameState.WAITING) { sender.sendMessage(LanguageManager.fromKey("game-in-progress")); return false; diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Remove.java b/src/main/java/com/MylesAndMore/Tumble/commands/Remove.java index c2aa149..9abc784 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Remove.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Remove.java @@ -38,7 +38,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command } ArenaManager.arenas.remove(arenaName); - ArenaManager.WriteConfig(); + ArenaManager.writeConfig(); sender.sendMessage(LanguageManager.fromKey("remove-success")); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetGameSpawn.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetGameSpawn.java index 21bdfe8..817a1a4 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetGameSpawn.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetGameSpawn.java @@ -45,7 +45,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Arena arena = ArenaManager.arenas.get(arenaName); arena.gameSpawn = ((Player)sender).getLocation(); - ArenaManager.WriteConfig(); + ArenaManager.writeConfig(); sender.sendMessage(LanguageManager.fromKey("set-success")); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetKillYLevel.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetKillYLevel.java index 142ffdd..27cc410 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetKillYLevel.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetKillYLevel.java @@ -46,7 +46,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Arena arena = ArenaManager.arenas.get(arenaName); arena.killAtY = ((int) ((Player) sender).getLocation().getY()); - ArenaManager.WriteConfig(); + ArenaManager.writeConfig(); sender.sendMessage(LanguageManager.fromKey("set-success")); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetLobby.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetLobby.java index a080d08..5708643 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetLobby.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetLobby.java @@ -45,7 +45,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Arena arena = ArenaManager.arenas.get(arenaName); arena.lobby = ((Player)sender).getLocation(); - ArenaManager.WriteConfig(); + ArenaManager.writeConfig(); sender.sendMessage(LanguageManager.fromKey("set-success")); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetWaitArea.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetWaitArea.java index a96e1a9..7b90099 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetWaitArea.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetWaitArea.java @@ -45,7 +45,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Arena arena = ArenaManager.arenas.get(arenaName); arena.waitArea = ((Player)sender).getLocation(); - ArenaManager.WriteConfig(); + ArenaManager.writeConfig(); sender.sendMessage(LanguageManager.fromKey("set-success")); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLobby.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLobby.java index 98a535b..e0d2bea 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLobby.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLobby.java @@ -45,7 +45,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Arena arena = ArenaManager.arenas.get(arenaName); arena.winnerLobby = ((Player)sender).getLocation(); - ArenaManager.WriteConfig(); + ArenaManager.writeConfig(); sender.sendMessage(LanguageManager.fromKey("set-success")); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java index 2f21a32..583f333 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java @@ -58,44 +58,51 @@ public static void readConfig() { arena.waitArea = readWorld("arenas." + arenaName + ".wait-area"); } - // Validate arena locations - if (arena.gameSpawn == null) { - plugin.getLogger().severe("arenas.yml: Arena " + arenaName + " is missing a game spawn, before you can join you must set it with '/tumble setgamespawn'."); - } - if (arena.lobby == null) { - plugin.getLogger().warning("arenas.yml: Arena " + arenaName + " is missing a lobby location. The spawn point of the default world will be used."); - } - arenas.put(arena.name, arena); } + validate(); // Validate arenas } /** * Write arenas from this.arenas to arenas.yml */ - public static void WriteConfig() { - config.set("arenas", null); // clear everything + public static void writeConfig() { + config.set("arenas", null); // Clear everything for (Arena arena: arenas.values()) { if (arena.killAtY != null) { config.set("arenas." + arena.name + ".kill-at-y", arena.killAtY); } if (arena.gameSpawn != null) { - WriteWorld("arenas." + arena.name + ".game-spawn", arena.gameSpawn); + writeWorld("arenas." + arena.name + ".game-spawn", arena.gameSpawn); } if (arena.lobby != null) { - WriteWorld("arenas." + arena.name + ".lobby", arena.lobby); + writeWorld("arenas." + arena.name + ".lobby", arena.lobby); } if (arena.winnerLobby != null) { - WriteWorld("arenas." + arena.name + ".winner-lobby", arena.winnerLobby); + writeWorld("arenas." + arena.name + ".winner-lobby", arena.winnerLobby); } if (arena.waitArea != null) { - WriteWorld("arenas." + arena.name + ".wait-area", arena.waitArea); + writeWorld("arenas." + arena.name + ".wait-area", arena.waitArea); } } + validate(); arenasYml.saveConfig(); + } + /** + * Check that all arenas are valid + */ + public static void validate() { + for (Arena arena: arenas.values()) { + if (arena.gameSpawn == null) { + plugin.getLogger().severe("arenas.yml: Arena '" + arena.name + "' is missing a game spawn, before it is usable you must set a spawn with '/tumble setgamespawn'."); + } + if (arena.lobby == null) { + plugin.getLogger().warning("arenas.yml: Arena '" + arena.name + "' is missing a lobby location. The spawn point of the default world will be used."); + } + } } /** @@ -125,7 +132,6 @@ public static Game findGamePlayerIsIn(Player p) { * Result#success = false and Result#error */ private static Location readWorld(String path) { - ConfigurationSection section = config.getConfigurationSection(path); if (section == null) { plugin.getLogger().warning("arenas.yml: Error loading location at '" + path + "' - " + "Section is null"); @@ -165,8 +171,7 @@ private static Location readWorld(String path) { * @param path The path of the section to write * @param location The location to write */ - private static void WriteWorld(String path, @NotNull Location location) { - + private static void writeWorld(String path, @NotNull Location location) { ConfigurationSection section = config.getConfigurationSection(path); if (section == null) { diff --git a/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java b/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java index 2d2bd4c..c279a22 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java @@ -11,7 +11,8 @@ * Manages config.yml and stores its options */ public class ConfigManager { - public static boolean HideLeaveJoin; + public static boolean hideLeaveJoin; + public static boolean hideDeathMessages; public static int waitDuration; private static Configuration config; @@ -25,7 +26,8 @@ public static void readConfig() { configYml.saveDefaultConfig(); config = configYml.getConfig(); defaultConfig = Objects.requireNonNull(config.getDefaults()); - HideLeaveJoin = config.getBoolean("hide-join-leave-messages", false); + hideLeaveJoin = config.getBoolean("hide-join-leave-messages", false); + hideDeathMessages = config.getBoolean("hide-death-messages", false); waitDuration = config.getInt("wait-duration", 15); validate(); diff --git a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java index c8331f7..0b23779 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java @@ -29,7 +29,7 @@ * An event listener for a game of Tumble. */ public class EventListener implements Listener { - Game game; + final Game game; /** * Create a new EventListener for a game. @@ -43,7 +43,7 @@ public EventListener(Game game) { @EventHandler public void PlayerJoinEvent(PlayerJoinEvent event) { // Hide/show join message accordingly - if (ConfigManager.HideLeaveJoin) { + if (event.getPlayer().getWorld() == game.arena.gameSpawn.getWorld() && ConfigManager.hideLeaveJoin) { event.setJoinMessage(null); } } @@ -51,7 +51,7 @@ public void PlayerJoinEvent(PlayerJoinEvent event) { @EventHandler public void PlayerQuitEvent(PlayerQuitEvent event) { // Hide/show leave message accordingly - if (ConfigManager.HideLeaveJoin) { + if (event.getPlayer().getWorld() == game.arena.gameSpawn.getWorld() && ConfigManager.hideLeaveJoin) { event.setQuitMessage(null); } @@ -63,6 +63,11 @@ public void PlayerQuitEvent(PlayerQuitEvent event) { @EventHandler public void PlayerDeathEvent(PlayerDeathEvent event) { + // Hide death messages if configured + if (event.getEntity().getWorld() == game.arena.gameSpawn.getWorld() && ConfigManager.hideDeathMessages) { + event.setDeathMessage(null); + } + // Inform the game that the player died and respawn them if (game.gamePlayers.contains(event.getEntity()) && game.gameState == GameState.RUNNING) { game.playerDeath(event.getEntity()); @@ -203,7 +208,7 @@ public void InventoryDragEvent(InventoryDragEvent event) { } @EventHandler - public void PlayerRespwanEvent(PlayerRespawnEvent event) { + public void PlayerRespawnEvent(PlayerRespawnEvent event) { // Make sure players respawn in the correct location if (game.gamePlayers.contains(event.getPlayer())) { 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 5ccb757..b309d42 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -212,26 +212,12 @@ private void gameEnd() { } displayActionbar(gamePlayers, LanguageManager.fromKeyNoPrefix("lobby-in-10")); - - // Wait 10s (200t), then + // Wait 10s (200t), then clear the arena and teleport players back Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { clearArena(); - - // teleport player back and restore inventory for (Player p : gamePlayers) { - p.getInventory().clear(); - p.setGameMode(GameMode.SURVIVAL); - if (p == winner && arena.winnerLobby != null) { - p.teleport(arena.winnerLobby); - } else { - p.teleport(Objects.requireNonNull(arena.lobby)); - } - - if (inventories.containsKey(p)) { - p.getInventory().setContents(inventories.get(p)); - } + sendToLobby(p, p == winner); } - }, 200); } @@ -248,7 +234,9 @@ private void gameEnd() { * Called if too many players leave, or from /tumble forceStop */ public void stopGame() { - gamePlayers.forEach(this::removePlayer); + // A new list must be created to avoid removing elements while iterating + List players = new ArrayList<>(gamePlayers); + players.forEach(this::removePlayer); Bukkit.getServer().getScheduler().cancelTask(gameID); gameID = -1; @@ -268,46 +256,32 @@ public void removePlayer(Player p) { // Check if the game has not started yet if (gameState == GameState.WAITING) { - // Inform player that there are no longer enough players to start if (gamePlayers.size() < 2) { displayActionbar(gamePlayers, LanguageManager.fromKeyNoPrefix("waiting-for-players")); } - // Teleport player back and restore inventory - if (arena.waitArea != null) { - p.getInventory().clear(); - p.setGameMode(GameMode.SURVIVAL); - p.teleport(arena.lobby); - if (inventories.containsKey(p)) { - p.getInventory().setContents(inventories.get(p)); - } - } + sendToLobby(p, false); } else { - // Stop the game if there are not enough players + // Stop the game if there are no longer enough players if (gamePlayers.size() < 2) { stopGame(); } - p.getInventory().clear(); - p.setGameMode(GameMode.SURVIVAL); - p.teleport(arena.lobby); - if (inventories.containsKey(p)) { - p.getInventory().setContents(inventories.get(p)); - } + sendToLobby(p, false); // You can never win if you quit, remember that kids!! } } /** - * Initiates an automatic start of a Tumble game + * Attempts to initiate an automatic start of a Tumble game */ public void autoStart() { - // Wait for the player to load in int waitDuration = ConfigManager.waitDuration; + if (waitDuration <= 0) { return; } Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { 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 + // Schedule a process to start the game in the specified waitDuration and save the PID so we can cancel it later if needed autoStartID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, this::gameStart, waitDuration * 20L); }, 50); } @@ -471,4 +445,27 @@ private void clearArena() { new Location(gameSpawn.getWorld(), gameSpawn.getX() + 20, gameSpawn.getY(), gameSpawn.getZ() + 20), Material.AIR); } + + /** + * Teleports a player to the lobby and restores their inventory + * @param p Player to teleport + * @param winner Whether the player is the winner + */ + private void sendToLobby(Player p, boolean winner) { + p.getInventory().clear(); + p.setGameMode(GameMode.SURVIVAL); + if (winner && arena.winnerLobby != null) { + p.teleport(arena.winnerLobby); + } else { + // Use default world spawn if lobby is not set + if (arena.lobby == null) { + p.teleport(Objects.requireNonNull(Bukkit.getWorlds().get(0)).getSpawnLocation()); + } else { + p.teleport(Objects.requireNonNull(arena.lobby)); + } + } + if (inventories.containsKey(p)) { + p.getInventory().setContents(inventories.get(p)); + } + } } diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Generator.java b/src/main/java/com/MylesAndMore/Tumble/game/Generator.java index d91c9e6..3136a93 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Generator.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Generator.java @@ -25,28 +25,28 @@ public static void generateLayersShovels(Location layer) { // Choose a random type of generation; a circular layer, a square layer, or a multi-tiered layer of either variety if (random.nextInt(4) == 0) { // Circular layer - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.SNOW_BLOCK), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.AIR), layers.getSafeMaterialList()); } else if (random.nextInt(4) == 1) { // Square layer - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.SNOW_BLOCK), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.AIR), layers.getSafeMaterialList()); } else if (random.nextInt(4) == 2) { // Multi-tiered circle - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.SNOW_BLOCK), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.AIR), layers.getSafeMaterialList()); Generator.generateLayer(layer, 13, 1, Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRASS_BLOCK), layers.getMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.AIR), layers.getMaterialList()); Generator.generateLayer(layer, 4, 1, Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.PODZOL), layers.getMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.AIR), layers.getMaterialList()); } else { // Multi-tiered square - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.SNOW_BLOCK), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.AIR), layers.getSafeMaterialList()); Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRASS_BLOCK), layers.getMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR), layers.getMaterialList()); Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.PODZOL), layers.getMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR), layers.getMaterialList()); } } @@ -62,72 +62,72 @@ public static void generateLayersSnowballs(Location layer) { // Similar generation to shovels, except there are three layers if (random.nextInt(4) == 0) { // Circular layer - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.AIR), layers.getSafeMaterialList()); layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.AIR), layers.getMaterialList()); layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.AIR), layers.getMaterialList()); } else if (random.nextInt(4) == 1) { // Square layer - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.AIR), layers.getSafeMaterialList()); layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.AIR), layers.getMaterialList()); layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.AIR), layers.getMaterialList()); } else if (random.nextInt(4) == 2) { // Multi-tiered circle - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.AIR), layers.getSafeMaterialList()); Generator.generateLayer(layer, 13, 1, Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRANITE), layers.getMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.AIR), layers.getMaterialList()); Generator.generateLayer(layer, 4, 1, Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.AIR), layers.getMaterialList()); layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.AIR), layers.getSafeMaterialList()); Generator.generateLayer(layer, 13, 1, Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRANITE), layers.getMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.AIR), layers.getMaterialList()); Generator.generateLayer(layer, 4, 1, Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.AIR), layers.getMaterialList()); layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.AIR), layers.getSafeMaterialList()); Generator.generateLayer(layer, 13, 1, Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRANITE), layers.getMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.AIR), layers.getMaterialList()); Generator.generateLayer(layer, 4, 1, Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); + Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.AIR), layers.getMaterialList()); } else { // Multi-tiered square - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.AIR), layers.getSafeMaterialList()); Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRANITE), layers.getMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR), layers.getMaterialList()); Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR), layers.getMaterialList()); layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.AIR), layers.getSafeMaterialList()); Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRANITE), layers.getMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR), layers.getMaterialList()); Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR), layers.getMaterialList()); layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getSafeMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.AIR), layers.getSafeMaterialList()); Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRANITE), layers.getMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR), layers.getMaterialList()); Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR); layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList()); + Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR), layers.getMaterialList()); } } diff --git a/src/main/resources/arenas.yml b/src/main/resources/arenas.yml index 6d3e3bb..7c26e08 100644 --- a/src/main/resources/arenas.yml +++ b/src/main/resources/arenas.yml @@ -1,2 +1,2 @@ # NOTE: No coordinate can be equal to zero! Use 0.5 instead if needed. -arenas: {} +arenas: diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index b15acaf..28cc4e2 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,5 +1,9 @@ -# Hides player join and leave messages in public chat +# Hides player join and leave messages in public chat during games hide-join-leave-messages: false +# Hides player death messages in public chat during games +hide-death-messages: false + # Duration (in seconds) to wait for more players to join a game before starting +# Set to 0 to disable wait-duration: 15 diff --git a/src/main/resources/language.yml b/src/main/resources/language.yml index dabb5f4..388c975 100644 --- a/src/main/resources/language.yml +++ b/src/main/resources/language.yml @@ -2,35 +2,35 @@ prefix: "&f[&eTumble&f] " # Error messages -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." +unknown-command: "&cUnknown command '%command%'" +no-permission: "&cYou do not have permission to perform this command! &7Required permission: '%permission%.'" +missing-arena-parameter: "&cMissing arena name!" +invalid-arena: "&carena '%arena%' does not exist!" +invalid-type: "&cInvalid game type!" +no-game-in-arena: "&cNo game is currently running in this arena!" +player-not-in-game: "&cYou are not in a game!" +not-for-console: "&cThis cannot be run by the console!" +game-in-progress: "&cThis 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 '/tumble join %arena% %type%'." -already-in-game: "&4You are already in a game! Leave it to join another one." -arena-not-ready: "&4This arena is not finished being set up!" -arena-not-ready-op: "&4Incomplete arena. &7Set a game spawn with '/tumble setGameSpawn'." -specify-game-type: "&4No game is currently taking place in this arena! &7Provide the game type to start one." +already-in-game: "&cYou are already in a game! Leave it to join another one." +arena-not-ready: "&cThis arena is not yet set up!" +arena-not-ready-op: "&cIncomplete arena. &7Set a game spawn with '/tumble setGameSpawn'." +specify-game-type: "&cNo game is currently taking place in this arena! &7Provide the game type to start one." # Success messages -create-success: "&aArena created successfully! &eBefore you can join, you must set a game spawn location with '/tumble setgamespawn'." -forcestart-success: "&aStarting game." +create-success: "&aArena created successfully! &eBefore this arena is usable, you must set a game spawn location with '/tumble 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: "&aConfig files reloaded. &eCheck console for errors." +reload-success: "&aConfig files reloaded. &eCheck console for possible errors." remove-success: "&aArena removed." set-success: "&aLocation set." # Game messages showdown: "&4Showdown!" lobby-in-10: "&9Returning to lobby in ten seconds..." -waiting-for-players: "&aWaiting for players" +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!" @@ -41,9 +41,3 @@ count-3: "&23" count-2: "&e2" count-1: "&41" count-go: "&aGo!" - - - - - - From 8c9889930cf61f959cc60e5a0098ece4215c59f2 Mon Sep 17 00:00:00 2001 From: sowgro Date: Thu, 1 Aug 2024 17:30:57 -0400 Subject: [PATCH 34/51] layers.yml but it builds now --- .../java/com/MylesAndMore/Tumble/config/LayerManager.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java b/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java index d995305..f834a24 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java @@ -44,10 +44,7 @@ public static void readConfig() { * @return The list of materials for the layer to be composed of */ public static List readLayer(String path) { - List list = (List) config.getList(path); - if (list == null) { - return null; - } + List list = config.getStringList(path); List layer = new ArrayList<>(); for (String entry : list) { Material tmp = Material.getMaterial(entry); From 839be68b900d46d52899c224a779fa90d06d6c74 Mon Sep 17 00:00:00 2001 From: sowgro Date: Thu, 1 Aug 2024 17:39:59 -0400 Subject: [PATCH 35/51] small fix --- src/main/java/com/MylesAndMore/Tumble/game/Generator.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Generator.java b/src/main/java/com/MylesAndMore/Tumble/game/Generator.java index dac0162..a3aa169 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Generator.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Generator.java @@ -56,7 +56,6 @@ public static void generateLayersShovels(Location layer) { */ public static void generateLayersSnowballs(Location layer) { Random random = new Random(); - Layers layers = new Layers(); layer.setY(layer.getY() - 1); // Similar generation to shovels, except there are three layers From d3c2467476f92ad3c5bcbe8fb5aa8b568c971518 Mon Sep 17 00:00:00 2001 From: Myles Date: Thu, 1 Aug 2024 20:52:15 -0500 Subject: [PATCH 36/51] LayerManager bugfixes/improvements - test and fix LayerManager - add layer and within-layer weights - add docs for layers.tml in readme --- README.md | 29 +- .../java/com/MylesAndMore/Tumble/Main.java | 11 +- .../Tumble/config/ArenaManager.java | 1 + .../Tumble/config/LayerManager.java | 64 ++- src/main/resources/layers.yml | 432 ++++++++++-------- 5 files changed, 315 insertions(+), 222 deletions(-) diff --git a/README.md b/README.md index 52dc9c2..0f208d5 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ But in Tumble, you play on randomly generated layers of blocks, using shovels, s ## Setup -1. [Download](https://github.com/MylesAndMore/tumble/releases) the plugin's JAR file and place it in your server's plugins directory. +1. [Download](https://github.com/MylesAndMore/Tumble/releases) the plugin's JAR file and place it in your server's plugins directory. 2. Place the worlds for your lobby and arenas in your server's worlds directory. - If you would like an experience similar to the original game, see [my guide](OG_GUIDE.md) for using the original worlds. @@ -81,11 +81,11 @@ Each arena can contain the following locations: Locations are stored using the following format: ```yaml - location: - x: 0.5 - y: 100 - z: 0.5 - world: worldName +location: + x: 0.5 + y: 100 + z: 0.5 + world: worldName ``` If a location is not specified, players will not be teleported by the plugin. @@ -102,6 +102,23 @@ All plugin chat messages will have the `prefix` prepended to them. Colors can be added using alternate color codes; for example, `&cRed Text` will appear red in-game. +### layers.yml +Stores data about the layers that will be generated in the game. + +Layers are stored using the following format: +```yaml +ores: # User-specified name of the layer + weight: 5 # Optional integer weight of the layer (1-...), used to determine how often it will be selected + materials: # List of materials (blocks) that will be used to generate the layer + - material: COBBLESTONE + weight: 5 # Optional integer weight of the material (1-...), used to determine how often it will be selected within the layer + - material: COAL_ORE + weight: 3 + - material: IRON_ORE + # No weight specified, defaults to 1 + # More materials... +``` + ## Issues & Feedback Feel free to report any bugs, leave feedback, ask questions, or submit ideas for new features on the Tumble [GitHub issues page](https://github.com/MylesAndMore/tumble/issues/new)! diff --git a/src/main/java/com/MylesAndMore/Tumble/Main.java b/src/main/java/com/MylesAndMore/Tumble/Main.java index 56a46c1..b6fdb1f 100644 --- a/src/main/java/com/MylesAndMore/Tumble/Main.java +++ b/src/main/java/com/MylesAndMore/Tumble/Main.java @@ -10,6 +10,7 @@ import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; +import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.plugin.java.JavaPlugin; import java.util.Objects; @@ -24,12 +25,18 @@ public void onEnable() { LanguageManager.readConfig(); ConfigManager.readConfig(); ArenaManager.readConfig(); - LayerManager.readConfig(); + try { + LayerManager.readConfig(); + } catch (InvalidConfigurationException e) { + this.getLogger().severe(e.getMessage()); + Bukkit.getServer().getPluginManager().disablePlugin(this); + return; + } Objects.requireNonNull(this.getCommand("tumble")).setExecutor(new Tumble()); new Metrics(this, 16940); - Bukkit.getServer().getLogger().info("[Tumble] Tumble successfully enabled!"); + this.getLogger().info("Tumble successfully enabled!"); } @Override diff --git a/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java index af85b9f..84f5595 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/ArenaManager.java @@ -61,6 +61,7 @@ public static void readConfig() { arenas.put(arena.name, arena); } validate(); // Validate arenas + plugin.getLogger().info("arenas.yml: Loaded " + arenas.size() + (arenas.size() > 1 ? " arenas" : " arena")); } /** diff --git a/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java b/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java index f834a24..73b301f 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java @@ -3,16 +3,18 @@ import com.MylesAndMore.Tumble.plugin.CustomConfig; import org.bukkit.Material; import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.FileConfiguration; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Random; import static com.MylesAndMore.Tumble.Main.plugin; public class LayerManager { - public static List> layers; + public static List> layers = new ArrayList<>(); private static final CustomConfig layersYml = new CustomConfig("layers.yml"); private static final FileConfiguration config = layersYml.getConfig(); @@ -20,22 +22,30 @@ public class LayerManager { /** * Read layers from layers.yml and populate this.layers */ - public static void readConfig() { + public static void readConfig() throws InvalidConfigurationException { layersYml.saveDefaultConfig(); ConfigurationSection layersSection = config.getConfigurationSection("layers"); if (layersSection == null) { - plugin.getLogger().warning("layers.yml is missing section 'layers'"); - return; + throw new InvalidConfigurationException("layers.yml is missing section 'layers'"); } for (String layerPath : layersSection.getKeys(false)) { List layer = readLayer(layerPath); if (layer == null) { - plugin.getLogger().warning("layers.yml: error loading layer at'"+layerPath+"'"); + plugin.getLogger().warning("layers.yml: Failed to load layer '" + layerPath + "'"); } else { - layers.add(layer); + int weight = getLayerWeight(layerPath); + for (int i = 0; i < weight; i++) { + layers.add(layer); + } } } + + if (layers.isEmpty()) { + throw new InvalidConfigurationException("No layers were found in layers.yml"); + } + int numLayers = layersSection.getKeys(false).size(); // Don't use layers.size() because it includes duplicates for weighted layers + plugin.getLogger().info("layers.yml: Loaded " + numLayers + (numLayers > 1 ? " layers" : " layer")); } /** @@ -44,17 +54,45 @@ public static void readConfig() { * @return The list of materials for the layer to be composed of */ public static List readLayer(String path) { - List list = config.getStringList(path); - List layer = new ArrayList<>(); - for (String entry : list) { - Material tmp = Material.getMaterial(entry); - if (tmp == null) { + List> materialsSection = config.getMapList("layers." + path + ".materials"); + if (materialsSection.isEmpty()) { + plugin.getLogger().warning("layers.yml: Layer '" + path + "' is missing section 'materials'"); + return null; + } + List materials = new ArrayList<>(); + + for (Map materialMap : materialsSection) { + String matName = (String)materialMap.get("material"); + Material mat = Material.getMaterial(matName); + + Object weightObj = materialMap.get("weight"); + int weight = 1; + if (weightObj != null) { + if (weightObj instanceof Integer) { + weight = (Integer)weightObj; + } else { + plugin.getLogger().warning("layers.yml: Invalid weight in layer '" + path + "'"); + return null; + } + } + if (mat == null) { + plugin.getLogger().warning("layers.yml: Invalid material '" + matName + "' in layer '" + path + "'"); return null; } - layer.add(tmp); + if (weight < 1) { + plugin.getLogger().warning("layers.yml: Invalid weight '" + weight + "' in layer '" + path + "'"); + return null; + } + for (int i = 0; i < weight; i++) { + materials.add(mat); + } } - return layer; + return materials; + } + + public static int getLayerWeight(String path) { + return config.getInt("layers." + path + ".weight", 1); } public static List getRandom() { diff --git a/src/main/resources/layers.yml b/src/main/resources/layers.yml index acc5be9..ff0f353 100644 --- a/src/main/resources/layers.yml +++ b/src/main/resources/layers.yml @@ -1,202 +1,232 @@ layers: - 1: - - YELLOW_GLAZED_TERRACOTTA - - LIGHT_BLUE_GLAZED_TERRACOTTA - - GRAY_GLAZED_TERRACOTTA - - PODZOL - - PODZOL - - PODZOL - - ORANGE_GLAZED_TERRACOTTA - 2: - - PINK_TERRACOTTA - - PURPLE_TERRACOTTA - - GRAY_TERRACOTTA - - BLUE_TERRACOTTA - - LIGHT_BLUE_TERRACOTTA - - WHITE_TERRACOTTA - - BROWN_TERRACOTTA - - GREEN_TERRACOTTA - - YELLOW_TERRACOTTA - - PINK_TERRACOTTA - - PURPLE_TERRACOTTA - - GRAY_TERRACOTTA - - BLUE_TERRACOTTA - - LIGHT_BLUE_TERRACOTTA - - WHITE_TERRACOTTA - - BROWN_TERRACOTTA - - GREEN_TERRACOTTA - - YELLOW_TERRACOTTA - - WHITE_STAINED_GLASS - - HONEYCOMB_BLOCK - - HONEYCOMB_BLOCK - 3: - - DIAMOND_BLOCK - - GOLD_BLOCK - - REDSTONE_BLOCK - - REDSTONE_BLOCK - - LAPIS_BLOCK - - LAPIS_BLOCK - - IRON_BLOCK - - COAL_BLOCK - - IRON_BLOCK - - COAL_BLOCK - - IRON_BLOCK - - COAL_BLOCK - - COAL_BLOCK - 4: - - WHITE_TERRACOTTA - - BLUE_ICE - - SOUL_SAND - - STONE_SLAB - - WHITE_TERRACOTTA - - BLUE_ICE - - SOUL_SAND - - STONE_SLAB - - WHITE_TERRACOTTA - - BLUE_ICE - - SOUL_SAND - - STONE_SLAB - - GLOWSTONE - - GLOWSTONE - - HONEY_BLOCK - - SLIME_BLOCK - 5: - - END_STONE - - END_STONE_BRICKS - - END_STONE - - END_STONE_BRICKS - - END_STONE - - END_STONE_BRICKS - - END_STONE - - END_STONE_BRICKS - - OBSIDIAN - - PURPUR_BLOCK - - PURPUR_PILLAR - - COBBLESTONE - 6: - - PRISMARINE - - DARK_PRISMARINE - - BLUE_STAINED_GLASS - - WET_SPONGE - - PRISMARINE_BRICKS - - PRISMARINE_BRICK_SLAB - - DARK_PRISMARINE - - SEA_LANTERN - - TUBE_CORAL_BLOCK - - BRAIN_CORAL_BLOCK - - BUBBLE_CORAL_BLOCK - 7: - - OAK_LOG - - SPRUCE_LOG - - ACACIA_LOG - - STRIPPED_OAK_LOG - - STRIPPED_SPRUCE_LOG - - STRIPPED_ACACIA_LOG - - OAK_WOOD - - SPRUCE_WOOD - - ACACIA_WOOD - - OAK_LEAVES - - SPRUCE_LEAVES - - ACACIA_LEAVES - - OAK_LEAVES - - SPRUCE_LEAVES - - ACACIA_LEAVES - 8: - - YELLOW_GLAZED_TERRACOTTA - - LIGHT_BLUE_GLAZED_TERRACOTTA - - GRAY_GLAZED_TERRACOTTA - - PODZOL - - PODZOL - - PODZOL - - ORANGE_GLAZED_TERRACOTTA - 9: - - COAL_ORE - - COAL_ORE - - COAL_ORE - - COAL_ORE - - COAL_ORE - - IRON_ORE - - REDSTONE_ORE - - EMERALD_ORE - - GOLD_ORE - - LAPIS_ORE - - DIAMOND_ORE - - GRASS_BLOCK - - GRASS_BLOCK - - GRASS_BLOCK - - GRASS_BLOCK - - COBWEB - 10: - - PACKED_ICE - - PACKED_ICE - - JUKEBOX - - TNT - - LIGHT_BLUE_CONCRETE - - GLASS - - PACKED_ICE - - PACKED_ICE - - JUKEBOX - - TNT - - LIGHT_BLUE_CONCRETE - - GLASS - - SOUL_SAND - 11: - - NETHERRACK - - NETHERRACK - - NETHERRACK - - NETHER_BRICKS - - NETHER_BRICKS - - NETHERRACK - - NETHERRACK - - NETHERRACK - - NETHER_BRICKS - - NETHER_BRICKS - - NETHER_GOLD_ORE - - NETHER_GOLD_ORE - - CRIMSON_NYLIUM - - WARPED_NYLIUM - - SOUL_SOIL - - CRACKED_NETHER_BRICKS - - RED_NETHER_BRICKS - - NETHER_WART_BLOCK - - CRYING_OBSIDIAN - - MAGMA_BLOCK - 12: - - REDSTONE_BLOCK - - REDSTONE_BLOCK - - REDSTONE_LAMP - - TARGET - - SLIME_BLOCK - - OBSERVER - 13: - - DIRT - - GRASS_PATH - - GRASS_BLOCK - - OAK_SLAB - - BRICK_WALL - - BRICK_STAIRS - 14: - - OAK_PLANKS - - OBSIDIAN - - SPONGE - - BEEHIVE - - DRIED_KELP_BLOCK - 15: - - SANDSTONE - - SANDSTONE_SLAB - - RED_SANDSTONE - - RED_SANDSTONE_SLAB - - RED_TERRACOTTA - - TERRACOTTA - - YELLOW_TERRACOTTA - 16: - - JUNGLE_LOG - - STRIPPED_JUNGLE_LOG - - JUNGLE_WOOD - - STRIPPED_JUNGLE_WOOD - - MOSSY_COBBLESTONE - - MOSSY_COBBLESTONE - - MOSSY_COBBLESTONE - - JUNGLE_LEAVES - - JUNGLE_SLAB - - JUNGLE_TRAPDOOR \ No newline at end of file + ores: + weight: 5 + materials: + - material: COBBLESTONE + weight: 5 + - material: COAL_ORE + weight: 3 + - material: GRASS_BLOCK + weight: 2 + - material: IRON_ORE + - material: GOLD_ORE + - material: REDSTONE_ORE + - material: EMERALD_ORE + - material: LAPIS_ORE + - material: DIAMOND_ORE + - material: COBWEB + ore_blocks: + weight: 5 + materials: + - material: COAL_BLOCK + weight: 4 + - material: IRON_BLOCK + weight: 3 + - material: GOLD_BLOCK + weight: 2 + - material: REDSTONE_BLOCK + weight: 2 + - material: LAPIS_BLOCK + - material: DIAMOND_BLOCK + nether: + weight: 5 + materials: + - material: NETHERRACK + weight: 6 + - material: NETHER_BRICKS + weight: 4 + - material: NETHER_GOLD_ORE + weight: 2 + - material: NETHER_QUARTZ_ORE + weight: 2 + - material: CRIMSON_NYLIUM + - material: WARPED_NYLIUM + - material: CRACKED_NETHER_BRICKS + - material: RED_NETHER_BRICKS + - material: NETHER_WART_BLOCK + - material: CRYING_OBSIDIAN + - material: SHROOMLIGHT + - material: BLACKSTONE + - material: BASALT + - material: SOUL_SAND + end: + weight: 5 + materials: + - material: END_STONE + weight: 4 + - material: END_STONE_BRICKS + weight: 2 + - material: PURPUR_BLOCK + weight: 2 + - material: PURPUR_PILLAR + - material: OBSIDIAN + - material: COBBLESTONE + redstone: + weight: 4 + materials: + - material: REDSTONE_BLOCK + weight: 2 + - material: REDSTONE_LAMP + - material: TARGET + - material: SLIME_BLOCK + - material: OBSERVER + - material: DAYLIGHT_DETECTOR + ocean: + weight: 4 + materials: + - material: PRISMARINE + weight: 2 + - material: DARK_PRISMARINE + - material: PRISMARINE_BRICKS + - material: PRISMARINE_BRICK_SLAB + - material: BLUE_STAINED_GLASS + - material: SEA_LANTERN + - material: SPONGE + - material: TUBE_CORAL_BLOCK + - material: BRAIN_CORAL_BLOCK + - material: BUBBLE_CORAL_BLOCK + desert: + weight: 4 + materials: + - material: SANDSTONE + weight: 2 + - material: RED_SANDSTONE + weight: 2 + - material: CHISELED_SANDSTONE + - material: SMOOTH_SANDSTONE + - material: CUT_SANDSTONE + - material: SANDSTONE_SLAB + - material: RED_SANDSTONE_SLAB + - material: RED_TERRACOTTA + - material: ORANGE_TERRACOTTA + - material: YELLOW_TERRACOTTA + - material: TERRACOTTA + forest: + weight: 4 + materials: + - material: OAK_LEAVES + weight: 2 + - material: SPRUCE_LEAVES + weight: 2 + - material: ACACIA_LEAVES + weight: 2 + - material: OAK_LOG + - material: SPRUCE_LOG + - material: ACACIA_LOG + - material: STRIPPED_OAK_LOG + - material: STRIPPED_SPRUCE_LOG + - material: STRIPPED_ACACIA_LOG + - material: OAK_WOOD + - material: SPRUCE_WOOD + - material: ACACIA_WOOD + jungle: + weight: 3 + materials: + - material: MOSSY_COBBLESTONE + weight: 3 + - material: COBBLESTONE + weight: 2 + - material: JUNGLE_LEAVES + weight: 2 + - material: JUNGLE_LOG + weight: 2 + - material: STRIPPED_JUNGLE_LOG + - material: JUNGLE_WOOD + - material: STRIPPED_JUNGLE_WOOD + - material: JUNGLE_PLANKS + - material: JUNGLE_SLAB + overworld: + weight: 3 + materials: + - material: DIRT + - material: COARSE_DIRT + - material: GRASS_BLOCK + - material: DIRT_PATH + - material: MYCELIUM + - material: PODZOL + - material: OAK_SLAB + - material: BRICK_WALL + - material: BRICK_STAIRS + terracotta: + weight: 3 + materials: + - material: PINK_TERRACOTTA + weight: 2 + - material: PURPLE_TERRACOTTA + weight: 2 + - material: GRAY_TERRACOTTA + weight: 2 + - material: BLUE_TERRACOTTA + weight: 2 + - material: LIGHT_BLUE_TERRACOTTA + weight: 2 + - material: WHITE_TERRACOTTA + weight: 2 + - material: BROWN_TERRACOTTA + weight: 2 + - material: GREEN_TERRACOTTA + weight: 2 + - material: YELLOW_TERRACOTTA + weight: 2 + - material: HONEYCOMB_BLOCK + weight: 2 + - material: WHITE_STAINED_GLASS + glazed_terracotta: + weight: 3 + materials: + - material: PODZOL + weight: 3 + - material: YELLOW_GLAZED_TERRACOTTA + - material: LIGHT_BLUE_GLAZED_TERRACOTTA + - material: GRAY_GLAZED_TERRACOTTA + sticky: + weight: 3 + materials: + - material: WHITE_TERRACOTTA + weight: 3 + - material: BLUE_ICE + weight: 3 + - material: STONE_SLAB + weight: 3 + - material: SOUL_SOIL + weight: 2 + - material: GLOWSTONE + weight: 2 + - material: SLIME_BLOCK + - material: HONEY_BLOCK + annoying_movement: + weight: 2 + materials: + - material: PACKED_ICE + weight: 4 + - material: JUKEBOX + weight: 2 + - material: TNT + weight: 2 + - material: LIGHT_BLUE_CONCRETE + weight: 2 + - material: GLASS + weight: 2 + - material: SMOOTH_STONE_SLAB + weight: 2 + - material: SOUL_SAND + insanity: + weight: 2 + materials: + - material: OAK_PLANKS + - material: OBSIDIAN + - material: SPONGE + - material: BEEHIVE + - material: DRIED_KELP_BLOCK + glass: + weight: 1 + materials: + - material: GLASS + weight: 30 + - material: WHITE_STAINED_GLASS + +# : +# weight: +# materials: +# - material: +# weight: From 37f47461a83365ea6be1d37d4fbe2e39fee3d8ed Mon Sep 17 00:00:00 2001 From: Myles Date: Fri, 2 Aug 2024 02:15:21 -0500 Subject: [PATCH 37/51] Generator refactor + more fixes - I no longer die when I look at the Generator - fix showdown mode not working properly - fix some language bugs --- .../MylesAndMore/Tumble/commands/Tumble.java | 28 +- .../com/MylesAndMore/Tumble/game/Game.java | 10 +- .../MylesAndMore/Tumble/game/Generator.java | 258 ++++++++---------- src/main/resources/language.yml | 6 +- 4 files changed, 140 insertions(+), 162 deletions(-) diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java b/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java index e7d1cf4..336733e 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java @@ -16,24 +16,24 @@ public class Tumble implements CommandExecutor, TabCompleter { private static final Map 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 SetKillYLevel()), - CmdNameAsKey(new SetLobby()), - CmdNameAsKey(new SetWaitArea()), - CmdNameAsKey(new SetWinnerLobby()) + 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 SetKillYLevel()), + 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")); + sender.sendMessage(LanguageManager.fromKey("unknown-command").replace("%command%", args[0])); return true; } @@ -94,7 +94,7 @@ private String[] removeFirst(String[] arr) { * @param cmd The subCommand to use * @return A map entry from the subCommand */ - private static Map.Entry CmdNameAsKey(SubCommand cmd) { + private static Map.Entry cmdNameAsKey(SubCommand cmd) { return Map.entry(cmd.getCommandName(),cmd); } } diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index b309d42..0f462aa 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -122,14 +122,12 @@ private void roundStart() { /** * Type specific setup: Generating layers and giving items - * @param type game type, + * @param type Game type */ private void prepareGameType(GameType type) { roundType = type; switch (type) { case SHOVELS -> { - Generator.generateLayersShovels(gameSpawn.clone()); - ItemStack shovel = new ItemStack(Material.IRON_SHOVEL); shovel.addEnchantment(Enchantment.SILK_TOUCH, 1); giveItems(gamePlayers, shovel); @@ -139,6 +137,7 @@ private void prepareGameType(GameType type) { gameID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { clearInventories(gamePlayers); giveItems(gamePlayers, new ItemStack(Material.SNOWBALL)); + roundType = GameType.SNOWBALLS; displayActionbar(gamePlayers, LanguageManager.fromKeyNoPrefix("showdown")); playSound(gamePlayers, Sound.ENTITY_ELDER_GUARDIAN_CURSE, SoundCategory.HOSTILE, 1, 1); @@ -147,8 +146,6 @@ private void prepareGameType(GameType type) { }, 3160); } case SNOWBALLS -> { - Generator.generateLayersSnowballs(gameSpawn.clone()); - giveItems(gamePlayers, new ItemStack(Material.SNOWBALL)); // End the round in 5m @@ -160,8 +157,10 @@ private void prepareGameType(GameType type) { case 0 -> prepareGameType(GameType.SHOVELS); case 1 -> prepareGameType(GameType.SNOWBALLS); } + return; } } + Generator.generateLayers(gameSpawn, type); } /** @@ -237,6 +236,7 @@ public void stopGame() { // A new list must be created to avoid removing elements while iterating List players = new ArrayList<>(gamePlayers); players.forEach(this::removePlayer); + clearArena(); Bukkit.getServer().getScheduler().cancelTask(gameID); gameID = -1; diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Generator.java b/src/main/java/com/MylesAndMore/Tumble/game/Generator.java index a3aa169..c678082 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Generator.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Generator.java @@ -1,153 +1,63 @@ package com.MylesAndMore.Tumble.game; import com.MylesAndMore.Tumble.config.LayerManager; +import com.MylesAndMore.Tumble.plugin.GameType; import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import java.util.*; -// TODO: clean up generator (when layers refactor is done) - /** - * Holds the methods that generate blocks in-game such as cylinders, cuboids, and block clumps. + * The Generator can generate basic shapes and layers for the game */ public class Generator { - /** - * Generates layers for a round of type shovels - * @param layer Location where the layers should start - */ - public static void generateLayersShovels(Location layer) { - Random random = new Random(); - - layer.setY(layer.getY() - 1); - // Choose a random type of generation; a circular layer, a square layer, or a multi-tiered layer of either variety - if (random.nextInt(4) == 0) { - // Circular layer - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.AIR), LayerManager.getRandom()); - } else if (random.nextInt(4) == 1) { - // Square layer - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.AIR), LayerManager.getRandom()); - } else if (random.nextInt(4) == 2) { - // Multi-tiered circle - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.AIR), LayerManager.getRandom()); - Generator.generateLayer(layer, 13, 1, Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.AIR), LayerManager.getRandom()); - Generator.generateLayer(layer, 4, 1, Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.AIR), LayerManager.getRandom()); - } else { - // Multi-tiered square - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.AIR), LayerManager.getRandom()); - Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR), LayerManager.getRandom()); - Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR), LayerManager.getRandom()); - } - } + private static final int CIRCLE_RADIUS = 17; + private static final int SQUARE_RADIUS = 17; + private static final int MULTI_TIER_RADIUS1 = 17; + private static final int MULTI_TIER_RADIUS2 = 13; + private static final int MULTI_TIER_RADIUS3_CIRCULAR = 4; + private static final int MULTI_TIER_RADIUS3_SQUARE = 7; + private static final int LAYER_DROP_HEIGHT = 6; // How far down the next layer should be generated in multi-layer generation /** - * Generates layers for round of type snowballs - * @param layer Location where the layers should start + * Generates layers for a round + * @param center The center of the layers + * @param type The type of the round (either shovels or snowballs) */ - public static void generateLayersSnowballs(Location layer) { + public static void generateLayers(Location center, GameType type) { + if (type == GameType.MIXED) { return; } // Cannot infer generation type from mixed Random random = new Random(); - + Location layer = center.clone(); + // The only difference between shovel and snowball generation is the amount of layers + int numLayers = type == GameType.SNOWBALLS ? 3 : 1; + // Move down one block before generating layer.setY(layer.getY() - 1); - // Similar generation to shovels, except there are three layers - if (random.nextInt(4) == 0) { - // Circular layer - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.AIR), LayerManager.getRandom()); - layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.AIR), LayerManager.getRandom()); - layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.AIR), LayerManager.getRandom()); - } else if (random.nextInt(4) == 1) { - // Square layer - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.AIR), LayerManager.getRandom()); - layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.AIR), LayerManager.getRandom()); - layer.setY(layer.getY() - 6); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.AIR), LayerManager.getRandom()); - } else if (random.nextInt(4) == 2) { - // Multi-tiered circle - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.AIR), LayerManager.getRandom()); - Generator.generateLayer(layer, 13, 1, Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.AIR), LayerManager.getRandom()); - Generator.generateLayer(layer, 4, 1, Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.AIR), LayerManager.getRandom()); - layer.setY(layer.getY() - 6); - - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.AIR), LayerManager.getRandom()); - Generator.generateLayer(layer, 13, 1, Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.AIR), LayerManager.getRandom()); - Generator.generateLayer(layer, 4, 1, Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.AIR), LayerManager.getRandom()); - layer.setY(layer.getY() - 6); - - Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.AIR), LayerManager.getRandom()); - Generator.generateLayer(layer, 13, 1, Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.AIR), LayerManager.getRandom()); - Generator.generateLayer(layer, 4, 1, Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.AIR), LayerManager.getRandom()); - } else { - // Multi-tiered square - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.AIR), LayerManager.getRandom()); - Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR), LayerManager.getRandom()); - Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR), LayerManager.getRandom()); - layer.setY(layer.getY() - 6); - - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.AIR), LayerManager.getRandom()); - Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR), LayerManager.getRandom()); - Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR), LayerManager.getRandom()); - layer.setY(layer.getY() - 6); - - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.AIR), LayerManager.getRandom()); - Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR), LayerManager.getRandom()); - Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR), LayerManager.getRandom()); + switch (random.nextInt(4)) { + case 0 -> generateCircularLayers(layer, new int[]{CIRCLE_RADIUS}, numLayers); // Single circular layer + case 1 -> generateSquareLayers(layer, new int[]{SQUARE_RADIUS}, numLayers); // Single square layer + case 2 -> generateCircularLayers(layer, new int[]{MULTI_TIER_RADIUS1, MULTI_TIER_RADIUS2, MULTI_TIER_RADIUS3_CIRCULAR}, numLayers); // Multi-tiered circular layer + case 3 -> generateSquareLayers(layer, new int[]{MULTI_TIER_RADIUS1, MULTI_TIER_RADIUS2, MULTI_TIER_RADIUS3_SQUARE}, numLayers); // Multi-tiered square layer } } /** - * Generates a layer (basically just a cylinder) as good as possible with blocks + * Generates a cylinder * @param center The center of the layer (Location) - * @param radius The whole number radius of the circle - * @param height The whole number height of the circle (1 for a flat layer) + * @param radius The radius of the layer + * @param height The height of the layer (1 for a flat layer) * @param material The Material to use for generation - * - * @return A list of Blocks containing all the blocks it just changed + * @return A list containing all changed blocks */ - private static List generateLayer(Location center, int radius, int height, Material material) { + public static List generateCylinder(Location center, int radius, int height, Material material) { int Cx = center.getBlockX(); int Cy = center.getBlockY(); int Cz = center.getBlockZ(); + int rSq = radius * radius; World world = center.getWorld(); List blocks = new ArrayList<>(); - int rSq = radius * radius; - for (int y = Cy; y < Cy + height; y++) { for (int x = Cx - radius; x <= Cx + radius; x++) { for (int z = Cz - radius; z <= Cz + radius; z++) { @@ -162,10 +72,11 @@ private static List generateLayer(Location center, int radius, int height } /** - * Generates a cuboid (literally just a ripoff fill command) - * @param firstPos The first Location to fill (first three coords in a fill command) + * Generates a cuboid + * @param firstPos The first Location to fill from (first three coords in a fill command) * @param secondPos The second Location to fill to (second three coords) * @param material The Material to fill + * @return A list containing all changed blocks */ public static List generateCuboid(Location firstPos, Location secondPos, Material material) { World world = firstPos.getWorld(); @@ -189,8 +100,8 @@ public static List generateCuboid(Location firstPos, Location secondPos, } /** - * Generates clumps in a pre-generated layer. - * @param blockList A list of block Locations that this method is allowed to edit + * Generates clumps in a pre-generated layer + * @param blockList A list of Blocks that this method is allowed to edit * @param materialList A list of Materials for the generator to randomly choose from. * Keep in mind that not all Materials may be used, the amount used depends on the size of the layer. * More Materials = more randomization @@ -203,26 +114,93 @@ private static void generateClumps(List blockList, List materia Collections.shuffle(materials); while (!blocks.isEmpty()) { Material randomMaterial = materials.get(random.nextInt(materials.size())); - Block aBlock = blocks.get(0); - aBlock.setType(randomMaterial); - // Get the blocks around that and change it to that same material (this is the basis of "clumps") - if (blocks.contains(aBlock.getRelative(BlockFace.NORTH))) { - aBlock.getRelative(BlockFace.NORTH).setType(randomMaterial); - blocks.remove(aBlock.getRelative(BlockFace.NORTH)); - } - if (blocks.contains(aBlock.getRelative(BlockFace.SOUTH))) { - aBlock.getRelative(BlockFace.SOUTH).setType(randomMaterial); - blocks.remove(aBlock.getRelative(BlockFace.SOUTH)); + Block block = blocks.get(random.nextInt(blocks.size())); + block.setType(randomMaterial); + setRelativeBlocks(blocks, block); + } + } + + /** + * Sets all Blocks adjacent to `block` in `blocks` to the same Material as `block`. + * All modified blocks are removed from `blocks`. + * @param blocks The list of blocks to modify + * @param block The reference block + */ + private static void setRelativeBlocks(List blocks, Block block) { + BlockFace[] faces = {BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST}; + for (BlockFace face : faces) { + Block relativeBlock = block.getRelative(face); + if (blocks.contains(relativeBlock)) { + relativeBlock.setType(block.getBlockData().getMaterial()); + blocks.remove(relativeBlock); } - if (blocks.contains(aBlock.getRelative(BlockFace.EAST))) { - aBlock.getRelative(BlockFace.EAST).setType(randomMaterial); - blocks.remove(aBlock.getRelative(BlockFace.EAST)); + } + blocks.remove(block); + } + + /** + * Generates a (optionally multi-tiered) circular layer + * @param center The center of the layer + * @param radii The radii of the layer(s) + */ + private static void generateCircularLayer(Location center, int[] radii) { + for (int i = 0; i < radii.length; i++) { + // First generate the basic shape (in this case a circle), + // then fill that shape with clumps from a randomly selected Material list + generateClumps(generateCylinder(center, radii[i], 1, Material.AIR), LayerManager.getRandom()); + if (i < radii.length - 1) { + // Another layer will be generated below the current one + // Set that area to AIR on the current level... + generateCylinder(center, radii[i + 1], 1, Material.AIR); + // ...then move down one block to prepare for the next layer + center.setY(center.getY() - 1); } - if (blocks.contains(aBlock.getRelative(BlockFace.WEST))) { - aBlock.getRelative(BlockFace.WEST).setType(randomMaterial); - blocks.remove(aBlock.getRelative(BlockFace.WEST)); + } + } + + /** + * Generates a (optionally multi-tiered) square layer + * @param center The center of the layer + * @param radii The radii of the layer(s) + */ + private static void generateSquareLayer(Location center, int[] radii) { + for (int i = 0; i < radii.length; i++) { + // Square generation is similar to circle generation, just with a bit more math + Location pos1 = new Location(center.getWorld(), center.getX() - radii[i], center.getY(), center.getZ() - radii[i]); + Location pos2 = new Location(center.getWorld(), center.getX() + radii[i], center.getY(), center.getZ() + radii[i]); + generateClumps(generateCuboid(pos1, pos2, Material.AIR), LayerManager.getRandom()); + if (i < radii.length - 1) { + pos1 = new Location(center.getWorld(), center.getX() - radii[i + 1], center.getY(), center.getZ() - radii[i + 1]); + pos2 = new Location(center.getWorld(), center.getX() + radii[i + 1], center.getY(), center.getZ() + radii[i + 1]); + generateCuboid(pos1, pos2, Material.AIR); + center.setY(center.getY() - 1); } - blocks.remove(aBlock); + } + } + + /** + * Generates multiple circular layer(s), each seperated by `LAYER_DROP_HEIGHT` + * @param center The center of the layer(s) + * @param radii The radii of the layer(s) + * @param layers The amount of layers to generate + */ + private static void generateCircularLayers(Location center, int[] radii, int layers) { + for (int i = 0; i < layers; i++) { + generateCircularLayer(center, radii); + center.setY(center.getY() - Generator.LAYER_DROP_HEIGHT); + } + } + + /** + * Generates multiple square layer(s), each seperated by `LAYER_DROP_HEIGHT` + * @param center The center of the layer(s) + * @param radii The radii of the layer(s) + * @param layers The amount of layers to generate + */ + private static void generateSquareLayers(Location center, int[] radii, int layers) { + for (int i = 0; i < layers; i++) { + generateSquareLayer(center, radii); + center.setY(center.getY() - Generator.LAYER_DROP_HEIGHT); } } } diff --git a/src/main/resources/language.yml b/src/main/resources/language.yml index 388c975..f95d3f1 100644 --- a/src/main/resources/language.yml +++ b/src/main/resources/language.yml @@ -33,10 +33,10 @@ 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-winner: "&6%winner% has won the round!" round-draw: "&6Draw!" -game-over: "&Game over!" -game-winner: "&6%winner% has won the Game!" +game-over: "&6Game over!" +game-winner: "&6%winner% has won the game!" count-3: "&23" count-2: "&e2" count-1: "&41" From ec6245a466e285ac479bb1c5f32ec9d9aeaf65ec Mon Sep 17 00:00:00 2001 From: sowgro Date: Fri, 2 Aug 2024 17:51:28 -0400 Subject: [PATCH 38/51] Add compact material weight notation for layers.yml --- .../java/com/MylesAndMore/Tumble/Main.java | 10 +- .../Tumble/config/LayerManager.java | 114 ++++--- src/main/resources/layers.yml | 313 ++++++++---------- 3 files changed, 196 insertions(+), 241 deletions(-) diff --git a/src/main/java/com/MylesAndMore/Tumble/Main.java b/src/main/java/com/MylesAndMore/Tumble/Main.java index b6fdb1f..7cf3628 100644 --- a/src/main/java/com/MylesAndMore/Tumble/Main.java +++ b/src/main/java/com/MylesAndMore/Tumble/Main.java @@ -9,8 +9,6 @@ import com.MylesAndMore.Tumble.game.Arena; import org.bstats.bukkit.Metrics; -import org.bukkit.Bukkit; -import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.plugin.java.JavaPlugin; import java.util.Objects; @@ -25,13 +23,7 @@ public void onEnable() { LanguageManager.readConfig(); ConfigManager.readConfig(); ArenaManager.readConfig(); - try { - LayerManager.readConfig(); - } catch (InvalidConfigurationException e) { - this.getLogger().severe(e.getMessage()); - Bukkit.getServer().getPluginManager().disablePlugin(this); - return; - } + LayerManager.readConfig(); Objects.requireNonNull(this.getCommand("tumble")).setExecutor(new Tumble()); new Metrics(this, 16940); diff --git a/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java b/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java index 73b301f..f20df6e 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java @@ -2,14 +2,10 @@ import com.MylesAndMore.Tumble.plugin.CustomConfig; import org.bukkit.Material; +import org.bukkit.configuration.Configuration; import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.InvalidConfigurationException; -import org.bukkit.configuration.file.FileConfiguration; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Random; +import java.util.*; import static com.MylesAndMore.Tumble.Main.plugin; @@ -17,84 +13,100 @@ public class LayerManager { public static List> layers = new ArrayList<>(); private static final CustomConfig layersYml = new CustomConfig("layers.yml"); - private static final FileConfiguration config = layersYml.getConfig(); + private static final Configuration config = layersYml.getConfig(); + private static final Configuration defaultConfig = Objects.requireNonNull(config.getDefaults()); + private static int layerCount = 0; /** * Read layers from layers.yml and populate this.layers */ - public static void readConfig() throws InvalidConfigurationException { + public static void readConfig() { layersYml.saveDefaultConfig(); - ConfigurationSection layersSection = config.getConfigurationSection("layers"); - if (layersSection == null) { - throw new InvalidConfigurationException("layers.yml is missing section 'layers'"); + readLayers(config.getConfigurationSection("layers")); + if (layers.isEmpty()) { + plugin.getLogger().warning("layers.yml: No layers were found, using defaults"); + readLayers(defaultConfig.getConfigurationSection("layers")); } - for (String layerPath : layersSection.getKeys(false)) { - List layer = readLayer(layerPath); + + // Don't use layers.size() because it includes duplicates for weighted layers + plugin.getLogger().info("layers.yml: Loaded " + layerCount + (layerCount == 1 ? " layer" : " layers")); + } + + public static void readLayers(ConfigurationSection section) { + + if (section == null) { + plugin.getLogger().warning("layers.yml is missing section 'layers', using defaults"); + return; + } + + for (String layerPath : section.getKeys(false)) { + ConfigurationSection layerSection = section.getConfigurationSection(layerPath); + if (layerSection == null) { + plugin.getLogger().warning("layers.yml: Layer '" + layerPath + "' is null"); + continue; + } + List layer = readLayer(layerSection); if (layer == null) { plugin.getLogger().warning("layers.yml: Failed to load layer '" + layerPath + "'"); - } else { - int weight = getLayerWeight(layerPath); - for (int i = 0; i < weight; i++) { - layers.add(layer); - } + continue; } - } - if (layers.isEmpty()) { - throw new InvalidConfigurationException("No layers were found in layers.yml"); + int weight = layerSection.getInt("layers." + layerPath + ".weight", 1); + layerCount++; + for (int i = 0; i < weight; i++) { + layers.add(layer); + } } - int numLayers = layersSection.getKeys(false).size(); // Don't use layers.size() because it includes duplicates for weighted layers - plugin.getLogger().info("layers.yml: Loaded " + numLayers + (numLayers > 1 ? " layers" : " layer")); } /** * Read the list of materials for a layer. - * @param path The path of the layer in the config + * @param section The path of the layer in the config * @return The list of materials for the layer to be composed of */ - public static List readLayer(String path) { - List> materialsSection = config.getMapList("layers." + path + ".materials"); - if (materialsSection.isEmpty()) { - plugin.getLogger().warning("layers.yml: Layer '" + path + "' is missing section 'materials'"); + public static List readLayer(ConfigurationSection section) { + + List materialsList = section.getStringList("materials"); + if (materialsList.isEmpty()) { + plugin.getLogger().warning("layers.yml: Layer '" + section.getCurrentPath() + "' is missing section 'materials'"); return null; } + List materials = new ArrayList<>(); + for (String s : materialsList) { + String[] sp = s.split(" "); - for (Map materialMap : materialsSection) { - String matName = (String)materialMap.get("material"); + if (sp.length < 1) { + plugin.getLogger().warning("layers.yml: Invalid format in layer '" + section.getCurrentPath() + "'"); + continue; + } + String matName = sp[0]; Material mat = Material.getMaterial(matName); + if (mat == null) { + plugin.getLogger().warning("layers.yml: Invalid material '" + matName + "' in layer '" + section.getCurrentPath() + "'"); + continue; + } - Object weightObj = materialMap.get("weight"); - int weight = 1; - if (weightObj != null) { - if (weightObj instanceof Integer) { - weight = (Integer)weightObj; - } else { - plugin.getLogger().warning("layers.yml: Invalid weight in layer '" + path + "'"); - return null; + int matWeight; + if (sp.length < 2) { + matWeight = 1; + } else { + try { + matWeight = Integer.parseInt(sp[1]); + } catch (NumberFormatException e) { + plugin.getLogger().warning("layers.yml: Invalid weight '" + sp[1] + "' in layer '" + section.getCurrentPath() + "'"); + matWeight = 1; } } - if (mat == null) { - plugin.getLogger().warning("layers.yml: Invalid material '" + matName + "' in layer '" + path + "'"); - return null; - } - if (weight < 1) { - plugin.getLogger().warning("layers.yml: Invalid weight '" + weight + "' in layer '" + path + "'"); - return null; - } - for (int i = 0; i < weight; i++) { + for (int i = 0; i < matWeight; i++) { materials.add(mat); } } return materials; } - public static int getLayerWeight(String path) { - return config.getInt("layers." + path + ".weight", 1); - } - public static List getRandom() { return layers.get(new Random().nextInt(layers.size())); } diff --git a/src/main/resources/layers.yml b/src/main/resources/layers.yml index ff0f353..d388759 100644 --- a/src/main/resources/layers.yml +++ b/src/main/resources/layers.yml @@ -2,231 +2,182 @@ layers: ores: weight: 5 materials: - - material: COBBLESTONE - weight: 5 - - material: COAL_ORE - weight: 3 - - material: GRASS_BLOCK - weight: 2 - - material: IRON_ORE - - material: GOLD_ORE - - material: REDSTONE_ORE - - material: EMERALD_ORE - - material: LAPIS_ORE - - material: DIAMOND_ORE - - material: COBWEB + - COBBLESTONE 5 + - COAL_ORE 3 + - GRASS_BLOCK 2 + - IRON_ORE + - GOLD_ORE + - REDSTONE_ORE + - EMERALD_ORE + - LAPIS_ORE + - DIAMOND_ORE + - COBWEB ore_blocks: weight: 5 materials: - - material: COAL_BLOCK - weight: 4 - - material: IRON_BLOCK - weight: 3 - - material: GOLD_BLOCK - weight: 2 - - material: REDSTONE_BLOCK - weight: 2 - - material: LAPIS_BLOCK - - material: DIAMOND_BLOCK + - COAL_BLOCK 4 + - IRON_BLOCK 3 + - GOLD_BLOCK 2 + - REDSTONE_BLOCK 2 + - LAPIS_BLOCK + - DIAMOND_BLOCK nether: weight: 5 materials: - - material: NETHERRACK - weight: 6 - - material: NETHER_BRICKS - weight: 4 - - material: NETHER_GOLD_ORE - weight: 2 - - material: NETHER_QUARTZ_ORE - weight: 2 - - material: CRIMSON_NYLIUM - - material: WARPED_NYLIUM - - material: CRACKED_NETHER_BRICKS - - material: RED_NETHER_BRICKS - - material: NETHER_WART_BLOCK - - material: CRYING_OBSIDIAN - - material: SHROOMLIGHT - - material: BLACKSTONE - - material: BASALT - - material: SOUL_SAND + - NETHERRACK 6 + - NETHER_BRICKS 4 + - NETHER_GOLD_ORE 2 + - NETHER_QUARTZ_ORE 2 + - CRIMSON_NYLIUM + - WARPED_NYLIUM + - CRACKED_NETHER_BRICKS + - RED_NETHER_BRICKS + - NETHER_WART_BLOCK + - CRYING_OBSIDIAN + - SHROOMLIGHT + - BLACKSTONE + - BASALT + - SOUL_SAND end: weight: 5 materials: - - material: END_STONE - weight: 4 - - material: END_STONE_BRICKS - weight: 2 - - material: PURPUR_BLOCK - weight: 2 - - material: PURPUR_PILLAR - - material: OBSIDIAN - - material: COBBLESTONE + - END_STONE 4 + - END_STONE_BRICKS 2 + - PURPUR_BLOCK 2 + - PURPUR_PILLAR + - OBSIDIAN + - COBBLESTONE redstone: weight: 4 materials: - - material: REDSTONE_BLOCK - weight: 2 - - material: REDSTONE_LAMP - - material: TARGET - - material: SLIME_BLOCK - - material: OBSERVER - - material: DAYLIGHT_DETECTOR + - REDSTONE_BLOCK 2 + - REDSTONE_LAMP + - TARGET + - SLIME_BLOCK + - OBSERVER + - DAYLIGHT_DETECTOR ocean: weight: 4 materials: - - material: PRISMARINE - weight: 2 - - material: DARK_PRISMARINE - - material: PRISMARINE_BRICKS - - material: PRISMARINE_BRICK_SLAB - - material: BLUE_STAINED_GLASS - - material: SEA_LANTERN - - material: SPONGE - - material: TUBE_CORAL_BLOCK - - material: BRAIN_CORAL_BLOCK - - material: BUBBLE_CORAL_BLOCK + - PRISMARINE 2 + - DARK_PRISMARINE + - PRISMARINE_BRICKS + - PRISMARINE_BRICK_SLAB + - BLUE_STAINED_GLASS + - SEA_LANTERN + - SPONGE + - TUBE_CORAL_BLOCK + - BRAIN_CORAL_BLOCK + - BUBBLE_CORAL_BLOCK desert: weight: 4 materials: - - material: SANDSTONE - weight: 2 - - material: RED_SANDSTONE - weight: 2 - - material: CHISELED_SANDSTONE - - material: SMOOTH_SANDSTONE - - material: CUT_SANDSTONE - - material: SANDSTONE_SLAB - - material: RED_SANDSTONE_SLAB - - material: RED_TERRACOTTA - - material: ORANGE_TERRACOTTA - - material: YELLOW_TERRACOTTA - - material: TERRACOTTA + - SANDSTONE 2 + - RED_SANDSTONE 2 + - CHISELED_SANDSTONE + - SMOOTH_SANDSTONE + - CUT_SANDSTONE + - SANDSTONE_SLAB + - RED_SANDSTONE_SLAB + - RED_TERRACOTTA + - ORANGE_TERRACOTTA + - YELLOW_TERRACOTTA + - TERRACOTTA forest: weight: 4 materials: - - material: OAK_LEAVES - weight: 2 - - material: SPRUCE_LEAVES - weight: 2 - - material: ACACIA_LEAVES - weight: 2 - - material: OAK_LOG - - material: SPRUCE_LOG - - material: ACACIA_LOG - - material: STRIPPED_OAK_LOG - - material: STRIPPED_SPRUCE_LOG - - material: STRIPPED_ACACIA_LOG - - material: OAK_WOOD - - material: SPRUCE_WOOD - - material: ACACIA_WOOD + - OAK_LEAVES 2 + - SPRUCE_LEAVES 2 + - ACACIA_LEAVES 2 + - OAK_LOG + - SPRUCE_LOG + - ACACIA_LOG + - STRIPPED_OAK_LOG + - STRIPPED_SPRUCE_LOG + - STRIPPED_ACACIA_LOG + - OAK_WOOD + - SPRUCE_WOOD + - ACACIA_WOOD jungle: weight: 3 materials: - - material: MOSSY_COBBLESTONE - weight: 3 - - material: COBBLESTONE - weight: 2 - - material: JUNGLE_LEAVES - weight: 2 - - material: JUNGLE_LOG - weight: 2 - - material: STRIPPED_JUNGLE_LOG - - material: JUNGLE_WOOD - - material: STRIPPED_JUNGLE_WOOD - - material: JUNGLE_PLANKS - - material: JUNGLE_SLAB + - MOSSY_COBBLESTONE 3 + - COBBLESTONE 2 + - JUNGLE_LEAVES 2 + - JUNGLE_LOG 2 + - STRIPPED_JUNGLE_LOG + - JUNGLE_WOOD + - STRIPPED_JUNGLE_WOOD + - JUNGLE_PLANKS + - JUNGLE_SLAB overworld: weight: 3 materials: - - material: DIRT - - material: COARSE_DIRT - - material: GRASS_BLOCK - - material: DIRT_PATH - - material: MYCELIUM - - material: PODZOL - - material: OAK_SLAB - - material: BRICK_WALL - - material: BRICK_STAIRS + - DIRT + - COARSE_DIRT + - GRASS_BLOCK + - GRASS_PATH + - MYCELIUM + - PODZOL + - OAK_SLAB + - BRICK_WALL + - BRICK_STAIRS terracotta: weight: 3 materials: - - material: PINK_TERRACOTTA - weight: 2 - - material: PURPLE_TERRACOTTA - weight: 2 - - material: GRAY_TERRACOTTA - weight: 2 - - material: BLUE_TERRACOTTA - weight: 2 - - material: LIGHT_BLUE_TERRACOTTA - weight: 2 - - material: WHITE_TERRACOTTA - weight: 2 - - material: BROWN_TERRACOTTA - weight: 2 - - material: GREEN_TERRACOTTA - weight: 2 - - material: YELLOW_TERRACOTTA - weight: 2 - - material: HONEYCOMB_BLOCK - weight: 2 - - material: WHITE_STAINED_GLASS + - PINK_TERRACOTTA 2 + - PURPLE_TERRACOTTA 2 + - GRAY_TERRACOTTA 2 + - BLUE_TERRACOTTA 2 + - LIGHT_BLUE_TERRACOTTA 2 + - WHITE_TERRACOTTA 2 + - BROWN_TERRACOTTA 2 + - GREEN_TERRACOTTA 2 + - YELLOW_TERRACOTTA 2 + - HONEYCOMB_BLOCK 2 + - WHITE_STAINED_GLASS glazed_terracotta: weight: 3 materials: - - material: PODZOL - weight: 3 - - material: YELLOW_GLAZED_TERRACOTTA - - material: LIGHT_BLUE_GLAZED_TERRACOTTA - - material: GRAY_GLAZED_TERRACOTTA + - PODZOL 3 + - YELLOW_GLAZED_TERRACOTTA + - LIGHT_BLUE_GLAZED_TERRACOTTA + - GRAY_GLAZED_TERRACOTTA sticky: weight: 3 materials: - - material: WHITE_TERRACOTTA - weight: 3 - - material: BLUE_ICE - weight: 3 - - material: STONE_SLAB - weight: 3 - - material: SOUL_SOIL - weight: 2 - - material: GLOWSTONE - weight: 2 - - material: SLIME_BLOCK - - material: HONEY_BLOCK + - WHITE_TERRACOTTA 3 + - BLUE_ICE 3 + - STONE_SLAB 3 + - SOUL_SOIL 2 + - GLOWSTONE 2 + - SLIME_BLOCK + - HONEY_BLOCK annoying_movement: weight: 2 materials: - - material: PACKED_ICE - weight: 4 - - material: JUKEBOX - weight: 2 - - material: TNT - weight: 2 - - material: LIGHT_BLUE_CONCRETE - weight: 2 - - material: GLASS - weight: 2 - - material: SMOOTH_STONE_SLAB - weight: 2 - - material: SOUL_SAND + - PACKED_ICE 4 + - JUKEBOX 2 + - TNT 2 + - LIGHT_BLUE_CONCRETE 2 + - GLASS 2 + - SMOOTH_STONE_SLAB 2 + - SOUL_SAND insanity: weight: 2 materials: - - material: OAK_PLANKS - - material: OBSIDIAN - - material: SPONGE - - material: BEEHIVE - - material: DRIED_KELP_BLOCK + - OAK_PLANKS + - OBSIDIAN + - SPONGE + - BEEHIVE + - DRIED_KELP_BLOCK glass: weight: 1 materials: - - material: GLASS - weight: 30 - - material: WHITE_STAINED_GLASS + - GLASS 30 + - WHITE_STAINED_GLASS -# : -# weight: +# : +# weight: # materials: -# - material: -# weight: +# - From b093175bb7fd180163b3c322e5ee9052f10f184e Mon Sep 17 00:00:00 2001 From: Myles Date: Fri, 2 Aug 2024 19:07:09 -0500 Subject: [PATCH 39/51] layers.yml fixes, more Generator updates - fix layer weights not being accounted for - use 1.17+ path material - update readme with new layer yaml format - improve clump generation - join command fix --- README.md | 13 ++++------ .../MylesAndMore/Tumble/commands/Join.java | 7 +++-- .../Tumble/config/LayerManager.java | 16 +++++++----- .../MylesAndMore/Tumble/game/Generator.java | 26 ++++++++++++++----- src/main/resources/layers.yml | 2 +- 5 files changed, 38 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 0f208d5..282e471 100644 --- a/README.md +++ b/README.md @@ -108,14 +108,11 @@ Stores data about the layers that will be generated in the game. Layers are stored using the following format: ```yaml ores: # User-specified name of the layer - weight: 5 # Optional integer weight of the layer (1-...), used to determine how often it will be selected - materials: # List of materials (blocks) that will be used to generate the layer - - material: COBBLESTONE - weight: 5 # Optional integer weight of the material (1-...), used to determine how often it will be selected within the layer - - material: COAL_ORE - weight: 3 - - material: IRON_ORE - # No weight specified, defaults to 1 + weight: 5 # Optional integer weight of the layer (1-...), used to determine how often it will be selected + materials: # List of materials (blocks) that will be used to generate the layer + - COBBLESTONE 6 # Optional integer weight of the material (1-...), used to determine how often it will be selected within the layer + - COAL_ORE 3 + - IRON_ORE # No weight specified, defaults to 1 # More materials... ``` diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Join.java b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java index f26d4e5..8d14ed3 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Join.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java @@ -96,11 +96,10 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command // A game is taking place in this arena, check that it is the right type if (arena.game.type == type) { game = arena.game; - } - else { + } else { sender.sendMessage(LanguageManager.fromKey("another-type-in-arena") - .replace("%type%",type.toString()) - .replace("%arena%",arenaName)); + .replace("%type%", arena.game.type.toString()) + .replace("%arena%", arenaName)); return false; } } diff --git a/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java b/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java index f20df6e..442f3b0 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java @@ -10,17 +10,19 @@ import static com.MylesAndMore.Tumble.Main.plugin; public class LayerManager { - public static List> layers = new ArrayList<>(); + public static List> layers; + private static int layerCount; private static final CustomConfig layersYml = new CustomConfig("layers.yml"); private static final Configuration config = layersYml.getConfig(); private static final Configuration defaultConfig = Objects.requireNonNull(config.getDefaults()); - private static int layerCount = 0; /** * Read layers from layers.yml and populate this.layers */ public static void readConfig() { + layers = new ArrayList<>(); + layerCount = 0; layersYml.saveDefaultConfig(); readLayers(config.getConfigurationSection("layers")); @@ -33,8 +35,11 @@ public static void readConfig() { plugin.getLogger().info("layers.yml: Loaded " + layerCount + (layerCount == 1 ? " layer" : " layers")); } + /** + * Read the layers from the layers.yml file + * @param section The 'layers' section of the config + */ public static void readLayers(ConfigurationSection section) { - if (section == null) { plugin.getLogger().warning("layers.yml is missing section 'layers', using defaults"); return; @@ -52,7 +57,7 @@ public static void readLayers(ConfigurationSection section) { continue; } - int weight = layerSection.getInt("layers." + layerPath + ".weight", 1); + int weight = layerSection.getInt("weight", 1); layerCount++; for (int i = 0; i < weight; i++) { layers.add(layer); @@ -61,12 +66,11 @@ public static void readLayers(ConfigurationSection section) { } /** - * Read the list of materials for a layer. + * Read the list of materials for a layer * @param section The path of the layer in the config * @return The list of materials for the layer to be composed of */ public static List readLayer(ConfigurationSection section) { - List materialsList = section.getStringList("materials"); if (materialsList.isEmpty()) { plugin.getLogger().warning("layers.yml: Layer '" + section.getCurrentPath() + "' is missing section 'materials'"); diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Generator.java b/src/main/java/com/MylesAndMore/Tumble/game/Generator.java index c678082..51cb0a7 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Generator.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Generator.java @@ -108,34 +108,46 @@ public static List generateCuboid(Location firstPos, Location secondPos, */ private static void generateClumps(List blockList, List materialList) { Random random = new Random(); - // Make new lists so we can manipulate them List blocks = new ArrayList<>(blockList); List materials = new ArrayList<>(materialList); Collections.shuffle(materials); + while (!blocks.isEmpty()) { Material randomMaterial = materials.get(random.nextInt(materials.size())); Block block = blocks.get(random.nextInt(blocks.size())); block.setType(randomMaterial); - setRelativeBlocks(blocks, block); + List modifiedBlocks = setRelativeBlocks(blocks, block); + blocks.removeAll(modifiedBlocks); + // There is a 50% (then 25%, 12.5%, ...) chance to continue modifying blocks aka growing the clump + double probability = 0.5; + while (!modifiedBlocks.isEmpty() && random.nextDouble() < probability) { + Block nextBlock = modifiedBlocks.get(random.nextInt(modifiedBlocks.size())); + nextBlock.setType(randomMaterial); + modifiedBlocks = setRelativeBlocks(blocks, nextBlock); + blocks.removeAll(modifiedBlocks); + probability /= 2; + } } } /** - * Sets all Blocks adjacent to `block` in `blocks` to the same Material as `block`. - * All modified blocks are removed from `blocks`. + * Sets all Blocks adjacent to `block` in `blocks` to the same Material as `block` * @param blocks The list of blocks to modify * @param block The reference block + * @return A list of all modified blocks, including `block` */ - private static void setRelativeBlocks(List blocks, Block block) { + private static List setRelativeBlocks(List blocks, Block block) { + List modifiedBlocks = new ArrayList<>(); BlockFace[] faces = {BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST}; for (BlockFace face : faces) { Block relativeBlock = block.getRelative(face); if (blocks.contains(relativeBlock)) { relativeBlock.setType(block.getBlockData().getMaterial()); - blocks.remove(relativeBlock); + modifiedBlocks.add(relativeBlock); } } - blocks.remove(block); + modifiedBlocks.add(block); + return modifiedBlocks; } /** diff --git a/src/main/resources/layers.yml b/src/main/resources/layers.yml index d388759..1d39527 100644 --- a/src/main/resources/layers.yml +++ b/src/main/resources/layers.yml @@ -116,7 +116,7 @@ layers: - DIRT - COARSE_DIRT - GRASS_BLOCK - - GRASS_PATH + - DIRT_PATH # On 1.16.x this must be changed to GRASS_PATH - MYCELIUM - PODZOL - OAK_SLAB From 965fa37ca0828ed007dc956d6433f628485ecfe3 Mon Sep 17 00:00:00 2001 From: sowgro Date: Fri, 2 Aug 2024 21:12:09 -0400 Subject: [PATCH 40/51] Fix break sound and particle so that it plays for everyone --- src/main/java/com/MylesAndMore/Tumble/game/EventListener.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java index 0b23779..68b9d33 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java @@ -108,10 +108,10 @@ public void ProjectileHitEvent(ProjectileHitEvent event) { if (game.roundType == GameType.SNOWBALLS && event.getEntity() instanceof Snowball) { if (event.getHitBlock() != null) { if (event.getHitBlock().getLocation().distanceSquared(game.arena.gameSpawn) < 579) { - p.playEffect( + game.gamePlayers.forEach(pl -> pl.playEffect( event.getHitBlock().getLocation(), Effect.STEP_SOUND, - event.getHitBlock().getType()); + event.getHitBlock().getType())); event.getHitBlock().setType(Material.AIR); } } else if (event.getHitEntity() != null) { From f6a0b6a9680bc2b262c5eccb72d468234474ec7a Mon Sep 17 00:00:00 2001 From: sowgro Date: Fri, 2 Aug 2024 21:39:48 -0400 Subject: [PATCH 41/51] Add ArenaManager.getRandomLayerSafe() --- .../Tumble/config/LayerManager.java | 24 ++++++++++++++++++- .../MylesAndMore/Tumble/game/Generator.java | 4 ++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java b/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java index 442f3b0..56c4177 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java @@ -17,6 +17,14 @@ public class LayerManager { private static final Configuration config = layersYml.getConfig(); private static final Configuration defaultConfig = Objects.requireNonNull(config.getDefaults()); + private static final List unsafeMaterials = List.of( + Material.COBWEB, + Material.MAGMA_BLOCK, + Material.CAMPFIRE, + Material.VINE, + Material.AIR + ); + /** * Read layers from layers.yml and populate this.layers */ @@ -111,7 +119,21 @@ public static List readLayer(ConfigurationSection section) { return materials; } - public static List getRandom() { + /** + * Selects a random layer for use in the generator. + * @return A random layer + */ + public static List getRandomLayer() { return layers.get(new Random().nextInt(layers.size())); } + + /** + * Selects a random layer and removes materials that are unsafe for players to stand on. + * @return A random safe layer + */ + public static List getRandomLayerSafe() { + List ret = new ArrayList<>(getRandomLayer()); // deep copy + ret.removeAll(unsafeMaterials); + return ret; + } } diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Generator.java b/src/main/java/com/MylesAndMore/Tumble/game/Generator.java index 51cb0a7..8374897 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Generator.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Generator.java @@ -159,7 +159,7 @@ private static void generateCircularLayer(Location center, int[] radii) { for (int i = 0; i < radii.length; i++) { // First generate the basic shape (in this case a circle), // then fill that shape with clumps from a randomly selected Material list - generateClumps(generateCylinder(center, radii[i], 1, Material.AIR), LayerManager.getRandom()); + generateClumps(generateCylinder(center, radii[i], 1, Material.AIR), LayerManager.getRandomLayer()); if (i < radii.length - 1) { // Another layer will be generated below the current one // Set that area to AIR on the current level... @@ -180,7 +180,7 @@ private static void generateSquareLayer(Location center, int[] radii) { // Square generation is similar to circle generation, just with a bit more math Location pos1 = new Location(center.getWorld(), center.getX() - radii[i], center.getY(), center.getZ() - radii[i]); Location pos2 = new Location(center.getWorld(), center.getX() + radii[i], center.getY(), center.getZ() + radii[i]); - generateClumps(generateCuboid(pos1, pos2, Material.AIR), LayerManager.getRandom()); + generateClumps(generateCuboid(pos1, pos2, Material.AIR), LayerManager.getRandomLayer()); if (i < radii.length - 1) { pos1 = new Location(center.getWorld(), center.getX() - radii[i + 1], center.getY(), center.getZ() - radii[i + 1]); pos2 = new Location(center.getWorld(), center.getX() + radii[i + 1], center.getY(), center.getZ() + radii[i + 1]); From 107149850b5840cfcdea15270170e80292400473 Mon Sep 17 00:00:00 2001 From: sowgro Date: Fri, 2 Aug 2024 22:05:28 -0400 Subject: [PATCH 42/51] Update READNE.md --- README.md | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 282e471..f713b68 100644 --- a/README.md +++ b/README.md @@ -103,18 +103,15 @@ All plugin chat messages will have the `prefix` prepended to them. Colors can be added using alternate color codes; for example, `&cRed Text` will appear red in-game. ### layers.yml -Stores data about the layers that will be generated in the game. +Stores data about the layers that will be generated during gameplay. + +Each layer contains a weight and a list of materials (blocks). + +| Option | Type | Description | +|-------------------------|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `weight` | Integer | A weight to influence how often the layer is randomly chosen. Default: 1 | +| `materials` **Required* | List of Materials | The palette of blocks that the layer will be composed of. Use the block names [listed here](https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/Material.html). Optionally, a weight can be added after the block name like so: `STONE 5`. |` -Layers are stored using the following format: -```yaml -ores: # User-specified name of the layer - weight: 5 # Optional integer weight of the layer (1-...), used to determine how often it will be selected - materials: # List of materials (blocks) that will be used to generate the layer - - COBBLESTONE 6 # Optional integer weight of the material (1-...), used to determine how often it will be selected within the layer - - COAL_ORE 3 - - IRON_ORE # No weight specified, defaults to 1 - # More materials... -``` ## Issues & Feedback From 3a9b5e3e0cbd4107cb7852cbc9c46a65bc319b3c Mon Sep 17 00:00:00 2001 From: sowgro Date: Sat, 3 Aug 2024 01:28:14 -0400 Subject: [PATCH 43/51] Add old config migrator --- README.md | 2 +- .../java/com/MylesAndMore/Tumble/Main.java | 75 ++++++++++++++++++- ...onfigManager.java => SettingsManager.java} | 18 ++--- .../Tumble/game/EventListener.java | 8 +- .../com/MylesAndMore/Tumble/game/Game.java | 4 +- .../resources/{config.yml => settings.yml} | 0 6 files changed, 89 insertions(+), 18 deletions(-) rename src/main/java/com/MylesAndMore/Tumble/config/{ConfigManager.java => SettingsManager.java} (67%) rename src/main/resources/{config.yml => settings.yml} (100%) diff --git a/README.md b/README.md index f713b68..133f142 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ Note that the `/tmbl` command can be used as a shorter alias to `/tumble`. ## Configuration Configuration for this plugin is stored in three files: -### config.yml +### settings.yml Stores general settings. | Option | Type | Description | Default value | diff --git a/src/main/java/com/MylesAndMore/Tumble/Main.java b/src/main/java/com/MylesAndMore/Tumble/Main.java index 7cf3628..37996d4 100644 --- a/src/main/java/com/MylesAndMore/Tumble/Main.java +++ b/src/main/java/com/MylesAndMore/Tumble/Main.java @@ -3,14 +3,20 @@ import com.MylesAndMore.Tumble.commands.*; import com.MylesAndMore.Tumble.config.ArenaManager; -import com.MylesAndMore.Tumble.config.ConfigManager; +import com.MylesAndMore.Tumble.config.SettingsManager; import com.MylesAndMore.Tumble.config.LanguageManager; import com.MylesAndMore.Tumble.config.LayerManager; import com.MylesAndMore.Tumble.game.Arena; +import com.MylesAndMore.Tumble.plugin.CustomConfig; import org.bstats.bukkit.Metrics; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.plugin.java.JavaPlugin; +import java.io.File; import java.util.Objects; public class Main extends JavaPlugin { @@ -20,8 +26,9 @@ public class Main extends JavaPlugin { public void onEnable() { plugin = this; + migrateConfig(); LanguageManager.readConfig(); - ConfigManager.readConfig(); + SettingsManager.readConfig(); ArenaManager.readConfig(); LayerManager.readConfig(); @@ -40,4 +47,68 @@ public void onDisable() { } } } + + /** + * Populate the new configs with the data from the old config.yml and rename to config_old.yml + */ + private void migrateConfig() { + boolean needsMigration = new File(plugin.getDataFolder(), "config.yml").exists() && !new File(plugin.getDataFolder(), "arenas.yml").exists(); + if (!needsMigration) { + return; + } + plugin.getLogger().info("Converting config.yml..."); + + CustomConfig configYml = new CustomConfig("config.yml"); + FileConfiguration oldConfig = configYml.getConfig(); + + //collect data from old config + boolean hideJoinLeaveMessages = oldConfig.getBoolean("hideJoinLeaveMessages"); + String permissionMessage = oldConfig.getString("permissionMessage"); + double winnerTeleportX = oldConfig.getDouble("winnerTeleport.x"); + double winnerTeleportY = oldConfig.getDouble("winnerTeleport.y"); + double winnerTeleportZ = oldConfig.getDouble("winnerTeleport.z"); + String lobbyWorldName = oldConfig.getString("lobbyWorld"); + String gameWorldName = oldConfig.getString("gameWorld"); + + World lobbyWorld = lobbyWorldName == null ? null : Bukkit.getWorld(lobbyWorldName); + World gameWorld = gameWorldName == null ? null : Bukkit.getWorld(gameWorldName); + + // create arena with info from config + Arena a = new Arena("default"); + if (lobbyWorld != null) { + a.lobby = new Location(lobbyWorld, lobbyWorld.getSpawnLocation().getX(), lobbyWorld.getSpawnLocation().getY(), lobbyWorld.getSpawnLocation().getZ()); + if (winnerTeleportX != 0 || winnerTeleportY != 0 || winnerTeleportZ != 0) { + a.winnerLobby = new Location(lobbyWorld, winnerTeleportX, winnerTeleportY, winnerTeleportZ); + } + } + if (gameWorld != null) { + a.gameSpawn = new Location(gameWorld, gameWorld.getSpawnLocation().getX(), gameWorld.getSpawnLocation().getY(), gameWorld.getSpawnLocation().getZ()); + // game world is required so the arena will only be added in this case + ArenaManager.readConfig(); + ArenaManager.arenas.put(a.name, a); + ArenaManager.writeConfig(); + } + + // move permission message to language.yml + if (permissionMessage != null && !permissionMessage.equals("You do not have permission to perform this command!")) { // skip migration if they left it at the old default + CustomConfig languagesYml = new CustomConfig("languages.yml"); + languagesYml.saveDefaultConfig(); + FileConfiguration languagesConfig = languagesYml.getConfig(); + languagesConfig.set("no-permission", permissionMessage); + languagesYml.saveConfig(); + } + + // wipe config and re-add hide-join-leave-messages under new its new name + if (hideJoinLeaveMessages) { + CustomConfig settingsYml = new CustomConfig("settings.yml"); + settingsYml.saveDefaultConfig(); + FileConfiguration settingsConfig = settingsYml.getConfig(); + settingsConfig.set("hide-join-leave-messages", true); + settingsYml.saveConfig(); + } + + if (!new File("config.yml").renameTo(new File(plugin.getDataFolder(), "config_old.yml"))) { + plugin.getLogger().severe("Failed to rename config.yml to config_old.yml. Please manually rename this to avoid data loss"); + } + } } \ No newline at end of file diff --git a/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java b/src/main/java/com/MylesAndMore/Tumble/config/SettingsManager.java similarity index 67% rename from src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java rename to src/main/java/com/MylesAndMore/Tumble/config/SettingsManager.java index c279a22..1ffc043 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/ConfigManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/SettingsManager.java @@ -8,9 +8,9 @@ import static com.MylesAndMore.Tumble.Main.plugin; /** - * Manages config.yml and stores its options + * Manages settings.yml and stores its options */ -public class ConfigManager { +public class SettingsManager { public static boolean hideLeaveJoin; public static boolean hideDeathMessages; public static int waitDuration; @@ -19,12 +19,12 @@ public class ConfigManager { private static Configuration defaultConfig; /** - * Reads options in from config.yml + * Reads options in from settings.yml */ public static void readConfig() { - CustomConfig configYml = new CustomConfig("config.yml"); - configYml.saveDefaultConfig(); - config = configYml.getConfig(); + CustomConfig settingsYml = new CustomConfig("settings.yml"); + settingsYml.saveDefaultConfig(); + config = settingsYml.getConfig(); defaultConfig = Objects.requireNonNull(config.getDefaults()); hideLeaveJoin = config.getBoolean("hide-join-leave-messages", false); hideDeathMessages = config.getBoolean("hide-death-messages", false); @@ -34,18 +34,18 @@ public static void readConfig() { } /** - * Check keys of config.yml against the defaults + * Check keys of settings.yml against the defaults */ public static void validate() { boolean invalid = false; for (String key : defaultConfig.getKeys(true)) { if (!config.contains(key,true)) { - plugin.getLogger().warning("config.yml is missing key '" + key + "'."); + plugin.getLogger().warning("settings.yml is missing key '" + key + "'."); invalid = true; } } if (invalid) { - plugin.getLogger().severe("Errors were found in config.yml, default values will be used."); + plugin.getLogger().severe("Errors were found in settings.yml, default values will be used."); } } } diff --git a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java index 68b9d33..81484a5 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java @@ -1,6 +1,6 @@ package com.MylesAndMore.Tumble.game; -import com.MylesAndMore.Tumble.config.ConfigManager; +import com.MylesAndMore.Tumble.config.SettingsManager; import com.MylesAndMore.Tumble.plugin.GameState; import com.MylesAndMore.Tumble.plugin.GameType; import org.bukkit.Bukkit; @@ -43,7 +43,7 @@ public EventListener(Game game) { @EventHandler public void PlayerJoinEvent(PlayerJoinEvent event) { // Hide/show join message accordingly - if (event.getPlayer().getWorld() == game.arena.gameSpawn.getWorld() && ConfigManager.hideLeaveJoin) { + if (event.getPlayer().getWorld() == game.arena.gameSpawn.getWorld() && SettingsManager.hideLeaveJoin) { event.setJoinMessage(null); } } @@ -51,7 +51,7 @@ public void PlayerJoinEvent(PlayerJoinEvent event) { @EventHandler public void PlayerQuitEvent(PlayerQuitEvent event) { // Hide/show leave message accordingly - if (event.getPlayer().getWorld() == game.arena.gameSpawn.getWorld() && ConfigManager.hideLeaveJoin) { + if (event.getPlayer().getWorld() == game.arena.gameSpawn.getWorld() && SettingsManager.hideLeaveJoin) { event.setQuitMessage(null); } @@ -64,7 +64,7 @@ public void PlayerQuitEvent(PlayerQuitEvent event) { @EventHandler public void PlayerDeathEvent(PlayerDeathEvent event) { // Hide death messages if configured - if (event.getEntity().getWorld() == game.arena.gameSpawn.getWorld() && ConfigManager.hideDeathMessages) { + if (event.getEntity().getWorld() == game.arena.gameSpawn.getWorld() && SettingsManager.hideDeathMessages) { event.setDeathMessage(null); } diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index 0f462aa..ee2e9d2 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -1,6 +1,6 @@ package com.MylesAndMore.Tumble.game; -import com.MylesAndMore.Tumble.config.ConfigManager; +import com.MylesAndMore.Tumble.config.SettingsManager; import com.MylesAndMore.Tumble.config.LanguageManager; import com.MylesAndMore.Tumble.plugin.GameState; import com.MylesAndMore.Tumble.plugin.GameType; @@ -276,7 +276,7 @@ public void removePlayer(Player p) { * Attempts to initiate an automatic start of a Tumble game */ public void autoStart() { - int waitDuration = ConfigManager.waitDuration; + int waitDuration = SettingsManager.waitDuration; if (waitDuration <= 0) { return; } Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { displayActionbar(gamePlayers, LanguageManager.fromKeyNoPrefix("time-till-start").replace("%wait%",waitDuration+"")); diff --git a/src/main/resources/config.yml b/src/main/resources/settings.yml similarity index 100% rename from src/main/resources/config.yml rename to src/main/resources/settings.yml From 940ae010f26266c6e5bdf1ef863eeb9b69ca6683 Mon Sep 17 00:00:00 2001 From: Tyler Ferrari <69283684+Sowgro@users.noreply.github.com> Date: Sat, 3 Aug 2024 08:44:17 -0400 Subject: [PATCH 44/51] fix a comment --- src/main/java/com/MylesAndMore/Tumble/Main.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/MylesAndMore/Tumble/Main.java b/src/main/java/com/MylesAndMore/Tumble/Main.java index 37996d4..875fde4 100644 --- a/src/main/java/com/MylesAndMore/Tumble/Main.java +++ b/src/main/java/com/MylesAndMore/Tumble/Main.java @@ -98,7 +98,7 @@ private void migrateConfig() { languagesYml.saveConfig(); } - // wipe config and re-add hide-join-leave-messages under new its new name + // move hide-join-leave-messages to settings.yml if (hideJoinLeaveMessages) { CustomConfig settingsYml = new CustomConfig("settings.yml"); settingsYml.saveDefaultConfig(); From 860db141ec7d1ea36a229c63d52e32ed1cfa7be9 Mon Sep 17 00:00:00 2001 From: Myles Date: Thu, 8 Aug 2024 01:07:56 -0500 Subject: [PATCH 45/51] squish some bugs of the pesky variety - disallow picking up items/leaf decayments in games - fix edge case in respawn at game end (delay eventlistener unregistration) - fix winner calculation in timeout draws - fix health/food levels in game - utilize safe layer generation - fix some lang typos - fix config migration, add autostart field migration --- .../java/com/MylesAndMore/Tumble/Main.java | 47 +++++++++++++------ .../MylesAndMore/Tumble/commands/Reload.java | 9 +--- .../Tumble/config/LayerManager.java | 12 ++--- .../Tumble/game/EventListener.java | 17 +++++++ .../com/MylesAndMore/Tumble/game/Game.java | 41 +++++++++------- .../MylesAndMore/Tumble/game/Generator.java | 17 ++++--- src/main/resources/language.yml | 6 +-- src/main/resources/plugin.yml | 1 - 8 files changed, 95 insertions(+), 55 deletions(-) diff --git a/src/main/java/com/MylesAndMore/Tumble/Main.java b/src/main/java/com/MylesAndMore/Tumble/Main.java index 875fde4..2dbdf5b 100644 --- a/src/main/java/com/MylesAndMore/Tumble/Main.java +++ b/src/main/java/com/MylesAndMore/Tumble/Main.java @@ -49,7 +49,7 @@ public void onDisable() { } /** - * Populate the new configs with the data from the old config.yml and rename to config_old.yml + * Populate the new configs with the data from the old (v1.x) config.yml and rename to config_old.yml if necessary */ private void migrateConfig() { boolean needsMigration = new File(plugin.getDataFolder(), "config.yml").exists() && !new File(plugin.getDataFolder(), "arenas.yml").exists(); @@ -61,7 +61,8 @@ private void migrateConfig() { CustomConfig configYml = new CustomConfig("config.yml"); FileConfiguration oldConfig = configYml.getConfig(); - //collect data from old config + // Collect data from old config + boolean autoStartEnabled = oldConfig.getBoolean("autoStart.enabled"); boolean hideJoinLeaveMessages = oldConfig.getBoolean("hideJoinLeaveMessages"); String permissionMessage = oldConfig.getString("permissionMessage"); double winnerTeleportX = oldConfig.getDouble("winnerTeleport.x"); @@ -73,42 +74,60 @@ private void migrateConfig() { World lobbyWorld = lobbyWorldName == null ? null : Bukkit.getWorld(lobbyWorldName); World gameWorld = gameWorldName == null ? null : Bukkit.getWorld(gameWorldName); - // create arena with info from config + // Create arena with info from config Arena a = new Arena("default"); if (lobbyWorld != null) { - a.lobby = new Location(lobbyWorld, lobbyWorld.getSpawnLocation().getX(), lobbyWorld.getSpawnLocation().getY(), lobbyWorld.getSpawnLocation().getZ()); + a.lobby = normalizeLocation(new Location(lobbyWorld, lobbyWorld.getSpawnLocation().getX(), lobbyWorld.getSpawnLocation().getY(), lobbyWorld.getSpawnLocation().getZ())); if (winnerTeleportX != 0 || winnerTeleportY != 0 || winnerTeleportZ != 0) { - a.winnerLobby = new Location(lobbyWorld, winnerTeleportX, winnerTeleportY, winnerTeleportZ); + a.winnerLobby = normalizeLocation(new Location(lobbyWorld, winnerTeleportX, winnerTeleportY, winnerTeleportZ)); } } if (gameWorld != null) { - a.gameSpawn = new Location(gameWorld, gameWorld.getSpawnLocation().getX(), gameWorld.getSpawnLocation().getY(), gameWorld.getSpawnLocation().getZ()); - // game world is required so the arena will only be added in this case + a.gameSpawn = normalizeLocation(new Location(gameWorld, gameWorld.getSpawnLocation().getX(), gameWorld.getSpawnLocation().getY(), gameWorld.getSpawnLocation().getZ())); + // Game world is required so the arena will only be added in this case ArenaManager.readConfig(); ArenaManager.arenas.put(a.name, a); ArenaManager.writeConfig(); } - // move permission message to language.yml - if (permissionMessage != null && !permissionMessage.equals("You do not have permission to perform this command!")) { // skip migration if they left it at the old default - CustomConfig languagesYml = new CustomConfig("languages.yml"); + // Move permission message to language.yml + // Skip migration if they left the message as the (old) default + if (permissionMessage != null && !permissionMessage.equals("You do not have permission to perform this command!")) { + CustomConfig languagesYml = new CustomConfig("language.yml"); languagesYml.saveDefaultConfig(); FileConfiguration languagesConfig = languagesYml.getConfig(); languagesConfig.set("no-permission", permissionMessage); languagesYml.saveConfig(); } - // move hide-join-leave-messages to settings.yml - if (hideJoinLeaveMessages) { + // Move hide-join-leave-messages and autostart to settings.yml + if (hideJoinLeaveMessages || autoStartEnabled) { CustomConfig settingsYml = new CustomConfig("settings.yml"); settingsYml.saveDefaultConfig(); FileConfiguration settingsConfig = settingsYml.getConfig(); settingsConfig.set("hide-join-leave-messages", true); + // wait-duration should stay as default unless autostart was disabled + if (!autoStartEnabled) { + settingsConfig.set("wait-duration", 0); + } settingsYml.saveConfig(); } - if (!new File("config.yml").renameTo(new File(plugin.getDataFolder(), "config_old.yml"))) { - plugin.getLogger().severe("Failed to rename config.yml to config_old.yml. Please manually rename this to avoid data loss"); + if (!new File(plugin.getDataFolder(), "config.yml").renameTo(new File(plugin.getDataFolder(), "config_old.yml"))) { + plugin.getLogger().severe("Failed to rename config.yml to config_old.yml. Please manually rename this to avoid data loss."); } + plugin.getLogger().info("Conversion complete! Please restart the server to apply changes."); + } + + /** + * Normalize a location to allow it to be saved to a config + * @param loc The location to normalize + * @return The normalized location + */ + private Location normalizeLocation(Location loc) { + return new Location(loc.getWorld(), + loc.getX() == 0.0 ? 0.5 : loc.getX(), + loc.getY() == 0.0 ? 0.5 : loc.getY(), + loc.getZ() == 0.0 ? 0.5 : loc.getZ()); } } \ No newline at end of file diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java b/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java index 588755f..ca7230c 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java @@ -1,8 +1,6 @@ package com.MylesAndMore.Tumble.commands; -import com.MylesAndMore.Tumble.config.ArenaManager; import com.MylesAndMore.Tumble.config.LanguageManager; -import com.MylesAndMore.Tumble.game.Arena; import com.MylesAndMore.Tumble.plugin.SubCommand; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; @@ -29,12 +27,7 @@ public String getPermission() { @Override public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { - for (Arena a : ArenaManager.arenas.values()) { - if (a.game != null) { - a.game.stopGame(); - } - } - + plugin.onDisable(); plugin.onEnable(); sender.sendMessage(LanguageManager.fromKey("reload-success")); return true; diff --git a/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java b/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java index 56c4177..7fe68ef 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java @@ -18,11 +18,11 @@ public class LayerManager { private static final Configuration defaultConfig = Objects.requireNonNull(config.getDefaults()); private static final List unsafeMaterials = List.of( - Material.COBWEB, - Material.MAGMA_BLOCK, - Material.CAMPFIRE, - Material.VINE, - Material.AIR + Material.COBWEB, + Material.MAGMA_BLOCK, + Material.CAMPFIRE, + Material.VINE, + Material.AIR ); /** @@ -132,7 +132,7 @@ public static List getRandomLayer() { * @return A random safe layer */ public static List getRandomLayerSafe() { - List ret = new ArrayList<>(getRandomLayer()); // deep copy + List ret = new ArrayList<>(getRandomLayer()); // Deep copy ret.removeAll(unsafeMaterials); return ret; } diff --git a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java index 81484a5..cb8a309 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java @@ -14,6 +14,7 @@ import org.bukkit.event.block.Action; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockDropItemEvent; +import org.bukkit.event.block.LeavesDecayEvent; import org.bukkit.event.entity.*; import org.bukkit.event.inventory.InventoryDragEvent; import org.bukkit.event.player.*; @@ -199,6 +200,14 @@ public void EntityDamageEvent(EntityDamageEvent event) { } } + @EventHandler + public void EntityPickupItemEvent(EntityPickupItemEvent event) { + if (!(event.getEntity() instanceof Player p)) { return; } + if (!game.gamePlayers.contains(p)) { return; } + // Disable picking up items during the game + event.setCancelled(true); + } + @EventHandler public void InventoryDragEvent(InventoryDragEvent event) { if (!(event.getWhoClicked() instanceof Player p)) { return; } @@ -215,6 +224,14 @@ public void PlayerRespawnEvent(PlayerRespawnEvent event) { } } + @EventHandler + public void LeavesDecayEvent(LeavesDecayEvent event) { + // Prevent leaves from decaying in the game world (edge case moment) + if (event.getBlock().getWorld() == game.arena.gameSpawn.getWorld()) { + event.setCancelled(true); + } + } + /** * Check to see if two locations are in the same place. * A location also includes where the player is facing which is why this is used instead of .equals() diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index ee2e9d2..fe51d29 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -72,7 +72,6 @@ public void addPlayer(Player p) { * Called from /tumble forceStart or after the wait counter finishes */ public void gameStart() { - // Check if the game is starting or running if (gameState != GameState.WAITING) { return; @@ -93,6 +92,12 @@ public void gameStart() { } } + // Give everyone full hunger and health + gamePlayers.forEach(p -> { + p.setHealth(20); + p.setFoodLevel(20); + }); + roundStart(); } @@ -175,7 +180,7 @@ private void roundEnd() { playSound(gamePlayers, Sound.BLOCK_NOTE_BLOCK_PLING, SoundCategory.BLOCKS, 5, 0); // Check if there was a definite winner or not - if (!playersAlive.isEmpty()) { + if (playersAlive.size() == 1) { Player winner = playersAlive.get(0); // Set the wins of the player to their current # of wins + 1 if (!gameWins.containsKey(winner)) { @@ -220,12 +225,7 @@ private void gameEnd() { }, 200); } - Bukkit.getServer().getScheduler().cancelTask(gameID); - gameID = -1; - Bukkit.getServer().getScheduler().cancelTask(autoStartID); - autoStartID = -1; - HandlerList.unregisterAll(eventListener); - arena.game = null; + cleanup(); } /** @@ -237,13 +237,7 @@ public void stopGame() { List players = new ArrayList<>(gamePlayers); players.forEach(this::removePlayer); clearArena(); - - Bukkit.getServer().getScheduler().cancelTask(gameID); - gameID = -1; - Bukkit.getServer().getScheduler().cancelTask(autoStartID); - autoStartID = -1; - HandlerList.unregisterAll(eventListener); - arena.game = null; + cleanup(); } /** @@ -292,9 +286,9 @@ public void autoStart() { */ public void playerDeath(Player player) { player.setGameMode(GameMode.SPECTATOR); - // Remove that player (who just died) from the alive players, effectively eliminating them, + // Remove that player (who just died) from the alive players, effectively eliminating them playersAlive.remove(player); - // If there are less than 2 players in the game (1 just died), + // If there are less than 2 players in the game (1 just died), end the round if (playersAlive.size() < 2 && gameState == GameState.RUNNING) { roundEnd(); } @@ -468,4 +462,17 @@ private void sendToLobby(Player p, boolean winner) { p.getInventory().setContents(inventories.get(p)); } } + + /** + * Cleans up the game's server resources + */ + private void cleanup() { + Bukkit.getServer().getScheduler().cancelTask(gameID); + gameID = -1; + Bukkit.getServer().getScheduler().cancelTask(autoStartID); + autoStartID = -1; + arena.game = null; + // Delay the unregistering of the event listener to prevent issues like players respawning in the wrong location, etc. + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> HandlerList.unregisterAll(eventListener), 20); + } } diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Generator.java b/src/main/java/com/MylesAndMore/Tumble/game/Generator.java index 8374897..0837edf 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Generator.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Generator.java @@ -154,12 +154,14 @@ private static List setRelativeBlocks(List blocks, Block block) { * Generates a (optionally multi-tiered) circular layer * @param center The center of the layer * @param radii The radii of the layer(s) + * @param safe Whether the layer should be generated without unsafe materials */ - private static void generateCircularLayer(Location center, int[] radii) { + private static void generateCircularLayer(Location center, int[] radii, boolean safe) { for (int i = 0; i < radii.length; i++) { // First generate the basic shape (in this case a circle), // then fill that shape with clumps from a randomly selected Material list - generateClumps(generateCylinder(center, radii[i], 1, Material.AIR), LayerManager.getRandomLayer()); + generateClumps(generateCylinder(center, radii[i], 1, Material.AIR), + safe ? LayerManager.getRandomLayerSafe() : LayerManager.getRandomLayer()); if (i < radii.length - 1) { // Another layer will be generated below the current one // Set that area to AIR on the current level... @@ -174,13 +176,15 @@ private static void generateCircularLayer(Location center, int[] radii) { * Generates a (optionally multi-tiered) square layer * @param center The center of the layer * @param radii The radii of the layer(s) + * @param safe Whether the layer should be generated without unsafe materials */ - private static void generateSquareLayer(Location center, int[] radii) { + private static void generateSquareLayer(Location center, int[] radii, boolean safe) { for (int i = 0; i < radii.length; i++) { // Square generation is similar to circle generation, just with a bit more math Location pos1 = new Location(center.getWorld(), center.getX() - radii[i], center.getY(), center.getZ() - radii[i]); Location pos2 = new Location(center.getWorld(), center.getX() + radii[i], center.getY(), center.getZ() + radii[i]); - generateClumps(generateCuboid(pos1, pos2, Material.AIR), LayerManager.getRandomLayer()); + generateClumps(generateCuboid(pos1, pos2, Material.AIR), + safe ? LayerManager.getRandomLayerSafe() : LayerManager.getRandomLayer()); if (i < radii.length - 1) { pos1 = new Location(center.getWorld(), center.getX() - radii[i + 1], center.getY(), center.getZ() - radii[i + 1]); pos2 = new Location(center.getWorld(), center.getX() + radii[i + 1], center.getY(), center.getZ() + radii[i + 1]); @@ -198,7 +202,8 @@ private static void generateSquareLayer(Location center, int[] radii) { */ private static void generateCircularLayers(Location center, int[] radii, int layers) { for (int i = 0; i < layers; i++) { - generateCircularLayer(center, radii); + // First layer should always be safe so player can spawn on it + generateCircularLayer(center, radii, i == 0); center.setY(center.getY() - Generator.LAYER_DROP_HEIGHT); } } @@ -211,7 +216,7 @@ private static void generateCircularLayers(Location center, int[] radii, int lay */ private static void generateSquareLayers(Location center, int[] radii, int layers) { for (int i = 0; i < layers; i++) { - generateSquareLayer(center, radii); + generateSquareLayer(center, radii, i == 0); center.setY(center.getY() - Generator.LAYER_DROP_HEIGHT); } } diff --git a/src/main/resources/language.yml b/src/main/resources/language.yml index f95d3f1..1ffec7e 100644 --- a/src/main/resources/language.yml +++ b/src/main/resources/language.yml @@ -5,13 +5,13 @@ prefix: "&f[&eTumble&f] " unknown-command: "&cUnknown command '%command%'" no-permission: "&cYou do not have permission to perform this command! &7Required permission: '%permission%.'" missing-arena-parameter: "&cMissing arena name!" -invalid-arena: "&carena '%arena%' does not exist!" +invalid-arena: "&cArena '%arena%' does not exist!" invalid-type: "&cInvalid game type!" no-game-in-arena: "&cNo game is currently running in this arena!" player-not-in-game: "&cYou are not in a game!" not-for-console: "&cThis cannot be run by the console!" -game-in-progress: "&cThis 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 '/tumble join %arena% %type%'." +game-in-progress: "&cThis game is still in progress! &7Wait until it finishes or join another game." +another-type-in-arena: "A game of '%type%' is currently taking place in this arena! &7Choose another arena or join it with '/tumble join %arena% %type%'." already-in-game: "&cYou are already in a game! Leave it to join another one." arena-not-ready: "&cThis arena is not yet set up!" arena-not-ready-op: "&cIncomplete arena. &7Set a game spawn with '/tumble setGameSpawn'." diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index da7f8bd..dbea900 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -51,4 +51,3 @@ permissions: tumble.setwinnerlobby: description: Allows you to set the winner lobby location for Tumble arenas. default: op - From 52caa9caf5b4a05b1aa29c5b2ae048d787efad9b Mon Sep 17 00:00:00 2001 From: Myles Date: Thu, 8 Aug 2024 01:15:53 -0500 Subject: [PATCH 46/51] fix a slight silly - plugin does not appreciate when you delay server shutdown I wonder why --- .../java/com/MylesAndMore/Tumble/game/Game.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index fe51d29..dcc6a1e 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -225,7 +225,7 @@ private void gameEnd() { }, 200); } - cleanup(); + cleanup(false); } /** @@ -237,7 +237,7 @@ public void stopGame() { List players = new ArrayList<>(gamePlayers); players.forEach(this::removePlayer); clearArena(); - cleanup(); + cleanup(true); } /** @@ -465,14 +465,19 @@ private void sendToLobby(Player p, boolean winner) { /** * Cleans up the game's server resources + * @param fast Whether to clean up quickly (avoid a graceful cleanup) */ - private void cleanup() { + private void cleanup(boolean fast) { Bukkit.getServer().getScheduler().cancelTask(gameID); gameID = -1; Bukkit.getServer().getScheduler().cancelTask(autoStartID); autoStartID = -1; arena.game = null; - // Delay the unregistering of the event listener to prevent issues like players respawning in the wrong location, etc. - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> HandlerList.unregisterAll(eventListener), 20); + if (fast) { + HandlerList.unregisterAll(eventListener); + } else { + // Delay the unregistering of the event listener to prevent issues like players respawning in the wrong location, etc. + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> HandlerList.unregisterAll(eventListener), 20); + } } } From 90ada8fff658200050117c62d38d20d83b884f61 Mon Sep 17 00:00:00 2001 From: Myles Date: Thu, 8 Aug 2024 15:45:13 -0500 Subject: [PATCH 47/51] fix another minor silly - how unfortunate it would be if leaves couldn't decay for like 3 minutes oh no --- .../java/com/MylesAndMore/Tumble/game/EventListener.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java index cb8a309..fd1b894 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java @@ -30,7 +30,9 @@ * An event listener for a game of Tumble. */ public class EventListener implements Listener { - final Game game; + + private final static int ARENA_SIZE_SQ = 579; // The size of the arena squared (used for distance checks) + private final Game game; /** * Create a new EventListener for a game. @@ -108,7 +110,7 @@ public void ProjectileHitEvent(ProjectileHitEvent event) { // Removes blocks that snowballs thrown by players have hit in the game world if (game.roundType == GameType.SNOWBALLS && event.getEntity() instanceof Snowball) { if (event.getHitBlock() != null) { - if (event.getHitBlock().getLocation().distanceSquared(game.arena.gameSpawn) < 579) { + if (event.getHitBlock().getLocation().distanceSquared(game.arena.gameSpawn) < ARENA_SIZE_SQ) { game.gamePlayers.forEach(pl -> pl.playEffect( event.getHitBlock().getLocation(), Effect.STEP_SOUND, @@ -226,8 +228,9 @@ public void PlayerRespawnEvent(PlayerRespawnEvent event) { @EventHandler public void LeavesDecayEvent(LeavesDecayEvent event) { + if (event.getBlock().getWorld() != game.arena.gameSpawn.getWorld()) { return; } // Prevent leaves from decaying in the game world (edge case moment) - if (event.getBlock().getWorld() == game.arena.gameSpawn.getWorld()) { + if (event.getBlock().getLocation().distanceSquared(game.arena.gameSpawn) < ARENA_SIZE_SQ) { event.setCancelled(true); } } From 6f2e23ce868cca2a039b1c11d600b2626f7efeed Mon Sep 17 00:00:00 2001 From: sowgro Date: Fri, 9 Aug 2024 01:39:54 -0400 Subject: [PATCH 48/51] Fix reloading of configs, handle case if subcommand is missing --- .../java/com/MylesAndMore/Tumble/Main.java | 96 +----------------- .../MylesAndMore/Tumble/commands/Tumble.java | 16 ++- .../Tumble/config/LanguageManager.java | 2 +- .../Tumble/config/LayerManager.java | 8 +- .../Tumble/config/OldConfigManager.java | 97 +++++++++++++++++++ src/main/resources/arenas.yml | 2 +- src/main/resources/language.yml | 1 + 7 files changed, 117 insertions(+), 105 deletions(-) create mode 100644 src/main/java/com/MylesAndMore/Tumble/config/OldConfigManager.java diff --git a/src/main/java/com/MylesAndMore/Tumble/Main.java b/src/main/java/com/MylesAndMore/Tumble/Main.java index 2dbdf5b..38c6d63 100644 --- a/src/main/java/com/MylesAndMore/Tumble/Main.java +++ b/src/main/java/com/MylesAndMore/Tumble/Main.java @@ -1,22 +1,13 @@ package com.MylesAndMore.Tumble; import com.MylesAndMore.Tumble.commands.*; -import com.MylesAndMore.Tumble.config.ArenaManager; +import com.MylesAndMore.Tumble.config.*; -import com.MylesAndMore.Tumble.config.SettingsManager; -import com.MylesAndMore.Tumble.config.LanguageManager; -import com.MylesAndMore.Tumble.config.LayerManager; import com.MylesAndMore.Tumble.game.Arena; -import com.MylesAndMore.Tumble.plugin.CustomConfig; import org.bstats.bukkit.Metrics; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.plugin.java.JavaPlugin; -import java.io.File; import java.util.Objects; public class Main extends JavaPlugin { @@ -26,7 +17,7 @@ public class Main extends JavaPlugin { public void onEnable() { plugin = this; - migrateConfig(); + OldConfigManager.migrateConfig(); LanguageManager.readConfig(); SettingsManager.readConfig(); ArenaManager.readConfig(); @@ -47,87 +38,4 @@ public void onDisable() { } } } - - /** - * Populate the new configs with the data from the old (v1.x) config.yml and rename to config_old.yml if necessary - */ - private void migrateConfig() { - boolean needsMigration = new File(plugin.getDataFolder(), "config.yml").exists() && !new File(plugin.getDataFolder(), "arenas.yml").exists(); - if (!needsMigration) { - return; - } - plugin.getLogger().info("Converting config.yml..."); - - CustomConfig configYml = new CustomConfig("config.yml"); - FileConfiguration oldConfig = configYml.getConfig(); - - // Collect data from old config - boolean autoStartEnabled = oldConfig.getBoolean("autoStart.enabled"); - boolean hideJoinLeaveMessages = oldConfig.getBoolean("hideJoinLeaveMessages"); - String permissionMessage = oldConfig.getString("permissionMessage"); - double winnerTeleportX = oldConfig.getDouble("winnerTeleport.x"); - double winnerTeleportY = oldConfig.getDouble("winnerTeleport.y"); - double winnerTeleportZ = oldConfig.getDouble("winnerTeleport.z"); - String lobbyWorldName = oldConfig.getString("lobbyWorld"); - String gameWorldName = oldConfig.getString("gameWorld"); - - World lobbyWorld = lobbyWorldName == null ? null : Bukkit.getWorld(lobbyWorldName); - World gameWorld = gameWorldName == null ? null : Bukkit.getWorld(gameWorldName); - - // Create arena with info from config - Arena a = new Arena("default"); - if (lobbyWorld != null) { - a.lobby = normalizeLocation(new Location(lobbyWorld, lobbyWorld.getSpawnLocation().getX(), lobbyWorld.getSpawnLocation().getY(), lobbyWorld.getSpawnLocation().getZ())); - if (winnerTeleportX != 0 || winnerTeleportY != 0 || winnerTeleportZ != 0) { - a.winnerLobby = normalizeLocation(new Location(lobbyWorld, winnerTeleportX, winnerTeleportY, winnerTeleportZ)); - } - } - if (gameWorld != null) { - a.gameSpawn = normalizeLocation(new Location(gameWorld, gameWorld.getSpawnLocation().getX(), gameWorld.getSpawnLocation().getY(), gameWorld.getSpawnLocation().getZ())); - // Game world is required so the arena will only be added in this case - ArenaManager.readConfig(); - ArenaManager.arenas.put(a.name, a); - ArenaManager.writeConfig(); - } - - // Move permission message to language.yml - // Skip migration if they left the message as the (old) default - if (permissionMessage != null && !permissionMessage.equals("You do not have permission to perform this command!")) { - CustomConfig languagesYml = new CustomConfig("language.yml"); - languagesYml.saveDefaultConfig(); - FileConfiguration languagesConfig = languagesYml.getConfig(); - languagesConfig.set("no-permission", permissionMessage); - languagesYml.saveConfig(); - } - - // Move hide-join-leave-messages and autostart to settings.yml - if (hideJoinLeaveMessages || autoStartEnabled) { - CustomConfig settingsYml = new CustomConfig("settings.yml"); - settingsYml.saveDefaultConfig(); - FileConfiguration settingsConfig = settingsYml.getConfig(); - settingsConfig.set("hide-join-leave-messages", true); - // wait-duration should stay as default unless autostart was disabled - if (!autoStartEnabled) { - settingsConfig.set("wait-duration", 0); - } - settingsYml.saveConfig(); - } - - if (!new File(plugin.getDataFolder(), "config.yml").renameTo(new File(plugin.getDataFolder(), "config_old.yml"))) { - plugin.getLogger().severe("Failed to rename config.yml to config_old.yml. Please manually rename this to avoid data loss."); - } - plugin.getLogger().info("Conversion complete! Please restart the server to apply changes."); - } - - /** - * Normalize a location to allow it to be saved to a config - * @param loc The location to normalize - * @return The normalized location - */ - private Location normalizeLocation(Location loc) { - return new Location(loc.getWorld(), - loc.getX() == 0.0 ? 0.5 : loc.getX(), - loc.getY() == 0.0 ? 0.5 : loc.getY(), - loc.getZ() == 0.0 ? 0.5 : loc.getZ()); - } } \ No newline at end of file diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java b/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java index 336733e..b3a2a6e 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java @@ -32,20 +32,26 @@ public class Tumble implements CommandExecutor, TabCompleter { @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").replace("%command%", args[0])); + if (args.length == 0 || args[0] == null || args[0].isEmpty()) { + sender.sendMessage(LanguageManager.fromKey("missing-subcommand")); return true; } + String subCmdName = args[0]; - var subCmd = subCommands.get(args[0]); + if (!subCommands.containsKey(subCmdName)) { + sender.sendMessage(LanguageManager.fromKey("unknown-command").replace("%command%", subCmdName)); + return true; + } + + var subCmd = subCommands.get(subCmdName); if (!sender.hasPermission(subCmd.getPermission())) { sender.sendMessage(LanguageManager.fromKey("no-permission").replace("%permission%", subCmd.getPermission())); - return false; + return true; } // Pass command action through to subCommand - subCmd.onCommand(sender, command, args[0], removeFirst(args)); + subCmd.onCommand(sender, command, subCmdName, removeFirst(args)); return true; } diff --git a/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java b/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java index 843d7c0..6625b26 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/LanguageManager.java @@ -12,11 +12,11 @@ * Manages language.yml and allows retrieval of keys */ public class LanguageManager { - private static final CustomConfig languageYml = new CustomConfig("language.yml"); private static Configuration config; private static Configuration defaultConfig; public static void readConfig() { + CustomConfig languageYml = new CustomConfig("language.yml"); languageYml.saveDefaultConfig(); config = languageYml.getConfig(); defaultConfig = Objects.requireNonNull(config.getDefaults()); diff --git a/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java b/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java index 7fe68ef..e2fe310 100644 --- a/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java +++ b/src/main/java/com/MylesAndMore/Tumble/config/LayerManager.java @@ -13,10 +13,6 @@ public class LayerManager { public static List> layers; private static int layerCount; - private static final CustomConfig layersYml = new CustomConfig("layers.yml"); - private static final Configuration config = layersYml.getConfig(); - private static final Configuration defaultConfig = Objects.requireNonNull(config.getDefaults()); - private static final List unsafeMaterials = List.of( Material.COBWEB, Material.MAGMA_BLOCK, @@ -29,6 +25,10 @@ public class LayerManager { * Read layers from layers.yml and populate this.layers */ public static void readConfig() { + CustomConfig layersYml = new CustomConfig("layers.yml"); + Configuration config = layersYml.getConfig(); + Configuration defaultConfig = Objects.requireNonNull(config.getDefaults()); + layers = new ArrayList<>(); layerCount = 0; layersYml.saveDefaultConfig(); diff --git a/src/main/java/com/MylesAndMore/Tumble/config/OldConfigManager.java b/src/main/java/com/MylesAndMore/Tumble/config/OldConfigManager.java new file mode 100644 index 0000000..d3dec2f --- /dev/null +++ b/src/main/java/com/MylesAndMore/Tumble/config/OldConfigManager.java @@ -0,0 +1,97 @@ +package com.MylesAndMore.Tumble.config; + +import com.MylesAndMore.Tumble.game.Arena; +import com.MylesAndMore.Tumble.plugin.CustomConfig; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.file.FileConfiguration; + +import java.io.File; + +import static com.MylesAndMore.Tumble.Main.plugin; + +public class OldConfigManager { + /** + * Populate the new configs with the data from the old (v1.x) config.yml and rename to config_old.yml if necessary + */ + public static void migrateConfig() { + boolean needsMigration = new File(plugin.getDataFolder(), "config.yml").exists() && !new File(plugin.getDataFolder(), "arenas.yml").exists(); + if (!needsMigration) { + return; + } + plugin.getLogger().info("Converting config.yml..."); + + CustomConfig configYml = new CustomConfig("config.yml"); + FileConfiguration oldConfig = configYml.getConfig(); + + // Collect data from old config + boolean autoStartEnabled = oldConfig.getBoolean("autoStart.enabled"); + boolean hideJoinLeaveMessages = oldConfig.getBoolean("hideJoinLeaveMessages"); + String permissionMessage = oldConfig.getString("permissionMessage"); + double winnerTeleportX = oldConfig.getDouble("winnerTeleport.x"); + double winnerTeleportY = oldConfig.getDouble("winnerTeleport.y"); + double winnerTeleportZ = oldConfig.getDouble("winnerTeleport.z"); + String lobbyWorldName = oldConfig.getString("lobbyWorld"); + String gameWorldName = oldConfig.getString("gameWorld"); + + World lobbyWorld = lobbyWorldName == null ? null : Bukkit.getWorld(lobbyWorldName); + World gameWorld = gameWorldName == null ? null : Bukkit.getWorld(gameWorldName); + + // Create arena with info from config + Arena a = new Arena("default"); + if (lobbyWorld != null) { + a.lobby = normalizeLocation(new Location(lobbyWorld, lobbyWorld.getSpawnLocation().getX(), lobbyWorld.getSpawnLocation().getY(), lobbyWorld.getSpawnLocation().getZ())); + if (winnerTeleportX != 0 || winnerTeleportY != 0 || winnerTeleportZ != 0) { + a.winnerLobby = normalizeLocation(new Location(lobbyWorld, winnerTeleportX, winnerTeleportY, winnerTeleportZ)); + } + } + if (gameWorld != null) { + a.gameSpawn = normalizeLocation(new Location(gameWorld, gameWorld.getSpawnLocation().getX(), gameWorld.getSpawnLocation().getY(), gameWorld.getSpawnLocation().getZ())); + // Game world is required so the arena will only be added in this case + ArenaManager.readConfig(); + ArenaManager.arenas.put(a.name, a); + ArenaManager.writeConfig(); + } + + // Move permission message to language.yml + // Skip migration if they left the message as the (old) default + if (permissionMessage != null && !permissionMessage.equals("You do not have permission to perform this command!")) { + CustomConfig languagesYml = new CustomConfig("language.yml"); + languagesYml.saveDefaultConfig(); + FileConfiguration languagesConfig = languagesYml.getConfig(); + languagesConfig.set("no-permission", permissionMessage); + languagesYml.saveConfig(); + } + + // Move hide-join-leave-messages and autostart to settings.yml + if (hideJoinLeaveMessages || autoStartEnabled) { + CustomConfig settingsYml = new CustomConfig("settings.yml"); + settingsYml.saveDefaultConfig(); + FileConfiguration settingsConfig = settingsYml.getConfig(); + settingsConfig.set("hide-join-leave-messages", true); + // wait-duration should stay as default unless autostart was disabled + if (!autoStartEnabled) { + settingsConfig.set("wait-duration", 0); + } + settingsYml.saveConfig(); + } + + if (!new File(plugin.getDataFolder(), "config.yml").renameTo(new File(plugin.getDataFolder(), "config_old.yml"))) { + plugin.getLogger().severe("Failed to rename config.yml to config_old.yml. Please manually rename this to avoid data loss."); + } + plugin.getLogger().info("Conversion complete! Please restart the server to apply changes."); + } + + /** + * Normalize a location to allow it to be saved to a config + * @param loc The location to normalize + * @return The normalized location + */ + private static Location normalizeLocation(Location loc) { + return new Location(loc.getWorld(), + loc.getX() == 0.0 ? 0.5 : loc.getX(), + loc.getY() == 0.0 ? 0.5 : loc.getY(), + loc.getZ() == 0.0 ? 0.5 : loc.getZ()); + } +} diff --git a/src/main/resources/arenas.yml b/src/main/resources/arenas.yml index 7c26e08..6d3e3bb 100644 --- a/src/main/resources/arenas.yml +++ b/src/main/resources/arenas.yml @@ -1,2 +1,2 @@ # NOTE: No coordinate can be equal to zero! Use 0.5 instead if needed. -arenas: +arenas: {} diff --git a/src/main/resources/language.yml b/src/main/resources/language.yml index 1ffec7e..578d9b7 100644 --- a/src/main/resources/language.yml +++ b/src/main/resources/language.yml @@ -4,6 +4,7 @@ prefix: "&f[&eTumble&f] " # Error messages unknown-command: "&cUnknown command '%command%'" no-permission: "&cYou do not have permission to perform this command! &7Required permission: '%permission%.'" +missing-subcommand: "&cMissing sub-command! &7Usage: '/tumble ...'" missing-arena-parameter: "&cMissing arena name!" invalid-arena: "&cArena '%arena%' does not exist!" invalid-type: "&cInvalid game type!" From d485e430adbae0f583a72291a88a517f303d8bf7 Mon Sep 17 00:00:00 2001 From: sowgro Date: Fri, 9 Aug 2024 11:18:22 -0400 Subject: [PATCH 49/51] add hide-death-messages to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 133f142..c3ef214 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ Stores general settings. | Option | Type | Description | Default value | |----------------------------|---------|--------------------------------------------------------------------------------|---------------| | `hide-join-leave-messages` | Boolean | Hides player join and leave messages in public chat. | `false` | +| `hide-death-messages` | Boolean | Hides player death messages in the public chat | | | `wait-duration` | Integer | Duration (in seconds) to wait for more players to join a game before starting. | `15` | ### arenas.yml From 8bea7b653407359208da720803cea6ac2abb8083 Mon Sep 17 00:00:00 2001 From: Myles Date: Fri, 9 Aug 2024 15:44:41 -0500 Subject: [PATCH 50/51] 2.0 release cleanup - update readme and og-guide a bit --- OG_GUIDE.md | 6 ++--- README.md | 18 +++++++------- .../MylesAndMore/Tumble/commands/Tumble.java | 24 +++++++++---------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/OG_GUIDE.md b/OG_GUIDE.md index da75d93..7850681 100644 --- a/OG_GUIDE.md +++ b/OG_GUIDE.md @@ -14,14 +14,14 @@ In this guide, I'll go over how to set up the Tumble plugin with the original ga - [Halloween Arena](https://publicfiles.sowgro.net/console-minigame-maps/java/tumble/) - [Birthday Arena](https://publicfiles.sowgro.net/console-minigame-maps/java/tumble/) - Tip: set a specific directory to store your worlds in with the `world-container` setting in `bukkit.yml` + *Tip: set a specific directory to store your worlds in with the `world-container` setting in `bukkit.yml`.* 3. Set `level-name` in server.properties to `lobby` -4. Take note of the names of the world folders, we will need this in a moment. +4. Take note of the names of the world folders, we will need these in step six. 5. Start and join your server. 6. Import your arena worlds. This can be done with the multiverse command `/mv import normal` -7. Paste the arena config below into `plugins/tumble/arenas.yml`: +7. Paste the following arena config below into `plugins/Tumble/arenas.yml`: ```yaml arenas: basic: diff --git a/README.md b/README.md index c3ef214..848c2f1 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ But in Tumble, you play on randomly generated layers of blocks, using shovels, s - Choose from three different game modes present in the original game: shovels, snowballs, and mixed - Four types of random layer generation -- 15 unique, themed layer varieties +- 16 unique, themed layer varieties, and the ability to add your own - Quick and easy setup and use - Support for 2-8 players - Multiple arenas and concurrent games @@ -23,16 +23,16 @@ But in Tumble, you play on randomly generated layers of blocks, using shovels, s ## Setup 1. [Download](https://github.com/MylesAndMore/Tumble/releases) the plugin's JAR file and place it in your server's plugins directory. -2. Place the worlds for your lobby and arenas in your server's worlds directory. - - If you would like an experience similar to the original game, see [my guide](OG_GUIDE.md) for using the original worlds. - +2. If you'd like to have your lobby and arena(s) in separate worlds... + - Place the worlds for your lobby and arena(s) in your server's worlds directory. + - Import your worlds using a plugin like Multiverse ```/mv import myWorld normal```. + - If you would like an experience similar to the original game, see [my guide](OG_GUIDE.md) for using the original worlds. 3. Start your server. -4. Import your worlds using a plugin like Multiverse. ```/mv import myWorld normal```. -5. Create your first arena `/tumble create myArena` -6. Set the spawn point of the arena `/tumble setgamespawn myArena` +4. Create your first arena `/tumble create myArena`. +5. Set the spawn point of the arena `/tumble setgamespawn myArena`. - **Note**: The layers will generate relative to this location. Ensure that the area is clear, 20 blocks in each direction. -7. You're done! You can now join the game ```/tumble join myArena mixed```. +6. You're done! You can now join the game ```/tumble join myArena mixed```. Scroll down for more options to configure your game. @@ -64,7 +64,7 @@ Stores general settings. | Option | Type | Description | Default value | |----------------------------|---------|--------------------------------------------------------------------------------|---------------| | `hide-join-leave-messages` | Boolean | Hides player join and leave messages in public chat. | `false` | -| `hide-death-messages` | Boolean | Hides player death messages in the public chat | | +| `hide-death-messages` | Boolean | Hides player death messages in public chat. | `false` | | `wait-duration` | Integer | Duration (in seconds) to wait for more players to join a game before starting. | `15` | ### arenas.yml diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java b/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java index b3a2a6e..73acf6b 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Tumble.java @@ -16,18 +16,18 @@ public class Tumble implements CommandExecutor, TabCompleter { private static final Map 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 SetKillYLevel()), - cmdNameAsKey(new SetLobby()), - cmdNameAsKey(new SetWaitArea()), - cmdNameAsKey(new SetWinnerLobby()) + 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 SetKillYLevel()), + cmdNameAsKey(new SetLobby()), + cmdNameAsKey(new SetWaitArea()), + cmdNameAsKey(new SetWinnerLobby()) ); @Override From 83a2a5eafa5e1a718af546d77dd0c1b8a2db0f40 Mon Sep 17 00:00:00 2001 From: Myles Date: Fri, 9 Aug 2024 16:31:00 -0500 Subject: [PATCH 51/51] game checks amount of players, fix typos --- README.md | 6 +++--- src/main/java/com/MylesAndMore/Tumble/commands/Join.java | 5 ++++- src/main/java/com/MylesAndMore/Tumble/game/Game.java | 5 ++++- src/main/resources/language.yml | 1 + 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 848c2f1..05085d6 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Scroll down for more options to configure your game. | `/tumble setkillylevel ` | Set the arena's Y-level to kill players at to current Y coordinate. | `tumble.setkillylevel` | | `/tumble setlobby ` | Set the arena's lobby to current location. | `tumble.setlobby` | | `/tumble setwaitarea ` | Set the arena's wait area to the current location. | `tumble.setwaitarea` | -| `/tumble setwinnerlobby ` | Set the arena's lobby to the current location. | `tumble.setwinnerlobby` | +| `/tumble setwinnerlobby ` | Set the arena's winner lobby to the current location. | `tumble.setwinnerlobby` | Note that the `/tmbl` command can be used as a shorter alias to `/tumble`. @@ -77,8 +77,8 @@ Each arena can contain the following locations: |--------------------------|-------------------------------------------------------------------------------------| | `game-spawn` **Required* | The location where players will be teleported, and the layers will generate around. | | `wait-area` | The location where players will be teleported to before the game begins. | -| `lobby` | The location where players will be teleported to after the game. | -| `winner-lobby` | The location where the winner will be teleported after the game. | +| `lobby` | The location where players will be teleported to after the game ends. | +| `winner-lobby` | The location where the winner will be teleported after the game ends. | Locations are stored using the following format: ```yaml diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Join.java b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java index 8d14ed3..1771163 100644 --- a/src/main/java/com/MylesAndMore/Tumble/commands/Join.java +++ b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java @@ -111,7 +111,10 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command return false; } - game.addPlayer((Player)sender); + if (!game.addPlayer((Player)sender)) { + p.sendMessage(LanguageManager.fromKey("game-full")); + return false; + } sender.sendMessage(LanguageManager.fromKey("join-success") .replace("%type%", game.type.toString()) .replace("%arena%", arena.name)); diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java index dcc6a1e..078cd13 100644 --- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java +++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java @@ -51,8 +51,10 @@ public Game(@NotNull Arena arena, @NotNull GameType type) { * Adds a player to the wait area. Called from /tumble join * Precondition: the game is in state WAITING * @param p Player to add + * @return Whether the player was successfully added */ - public void addPlayer(Player p) { + public boolean addPlayer(Player p) { + if (gamePlayers.size() > 8) { return false; } gamePlayers.add(p); if (arena.waitArea != null) { @@ -65,6 +67,7 @@ public void addPlayer(Player p) { } else { displayActionbar(Collections.singletonList(p), LanguageManager.fromKeyNoPrefix("waiting-for-players")); } + return true; } /** diff --git a/src/main/resources/language.yml b/src/main/resources/language.yml index 578d9b7..1217a02 100644 --- a/src/main/resources/language.yml +++ b/src/main/resources/language.yml @@ -10,6 +10,7 @@ invalid-arena: "&cArena '%arena%' does not exist!" invalid-type: "&cInvalid game type!" no-game-in-arena: "&cNo game is currently running in this arena!" player-not-in-game: "&cYou are not in a game!" +game-full: "&cThis game is full!" not-for-console: "&cThis cannot be run by the console!" game-in-progress: "&cThis game is still in progress! &7Wait until it finishes or join another game." another-type-in-arena: "A game of '%type%' is currently taking place in this arena! &7Choose another arena or join it with '/tumble join %arena% %type%'."