diff options
author | sowgro <tpoke.ferrari@gmail.com> | 2024-07-02 01:55:07 -0400 |
---|---|---|
committer | sowgro <tpoke.ferrari@gmail.com> | 2024-07-02 01:55:07 -0400 |
commit | ec048c36764120a0f912a775474d5a028f48d9e3 (patch) | |
tree | 81046c47b2ce2571fb45dafe972e69c00d88fc13 /src/main | |
parent | 3b63c0e4ef7b14fd89d94170e5e6aa683374f0f8 (diff) | |
download | Tumble-ec048c36764120a0f912a775474d5a028f48d9e3.tar.gz Tumble-ec048c36764120a0f912a775474d5a028f48d9e3.tar.bz2 Tumble-ec048c36764120a0f912a775474d5a028f48d9e3.zip |
Add javadoc comments and other code cleanup
Diffstat (limited to 'src/main')
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 class ForceStart implements SubCommand, CommandExecutor, TabCompleter { 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 class ForceStop implements SubCommand, CommandExecutor, TabCompleter { 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 class Join implements SubCommand, CommandExecutor, TabCompleter { 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 class Join implements SubCommand, CommandExecutor, TabCompleter { } 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 class Join implements SubCommand, CommandExecutor, TabCompleter { } } + // 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 class Tumble implements CommandExecutor, TabCompleter { return false; } + // pass command action through to subCommand subCmd.onCommand(sender, command, args[0], removeFirst(args)); return true; } @@ -49,6 +50,7 @@ public class Tumble implements CommandExecutor, TabCompleter { @Override public List<String> 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<String> PermittedSubCmds = new ArrayList<>(); for (SubCommand subCmd: subCommands.values()) { if (sender.hasPermission(subCmd.getPermission())) { @@ -63,6 +65,7 @@ public class Tumble implements CommandExecutor, TabCompleter { 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 class Tumble implements CommandExecutor, TabCompleter { 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<String> tmp = new ArrayList<>(List.of(arr)); tmp.remove(0); return tmp.toArray(new String[0]); } - private static Map.Entry<String, SubCommand> CmdNameAsKey(SubCommand s) { - return Map.entry(s.getCommandName(),s); + /** + * 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<String, SubCommand> 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 java.util.Objects; import static com.MylesAndMore.Tumble.Main.plugin; +/** + * Manages arenas.yml and stores list of arenas + */ public class ArenaManager { public HashMap<String, Arena> 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 class ArenaManager { } } + /** + * Write arenas from this.arenas to arenas.yml + */ public void WriteConfig() { config.set("arenas", null); // clear everything @@ -118,16 +130,16 @@ public class ArenaManager { } /** - * 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<Location> readWorld(@Nullable ConfigurationSection section) { @@ -155,6 +167,16 @@ public class ArenaManager { 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 java.util.Objects; 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 class ConfigManager { } } + /** + * 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 java.util.Objects; 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 class LanguageManager { } } + /** + * 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.configManager; 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<Player> 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 class Game { 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 class Game { } /** - * 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 @@ public class Game { /** * 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 @@ public class Game { 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 @@ public class Game { 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 @@ public class Game { 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 @@ public class Game { 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 class Game { * @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 java.util.logging.Level; 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 @@ package com.MylesAndMore.Tumble.plugin; 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(); } |