diff --git a/src/main/java/ca/recrown/islandsurvivalcraft/IslandSurvivalCraft.java b/src/main/java/ca/recrown/islandsurvivalcraft/IslandSurvivalCraft.java index 5156f77..f3e1930 100644 --- a/src/main/java/ca/recrown/islandsurvivalcraft/IslandSurvivalCraft.java +++ b/src/main/java/ca/recrown/islandsurvivalcraft/IslandSurvivalCraft.java @@ -1,30 +1,38 @@ package ca.recrown.islandsurvivalcraft; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.generator.ChunkGenerator; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; +import ca.recrown.islandsurvivalcraft.interaction.commands.CommandProcessor; import ca.recrown.islandsurvivalcraft.world.WorldInfoManager; import ca.recrown.islandsurvivalcraft.world.generation.GeneratorModes; public class IslandSurvivalCraft extends JavaPlugin implements Listener { private PluginManager pluginManager; - private WorldInfoManager worldInfos; + private WorldInfoManager worldInfoManager; + private CommandProcessor commandProcessor; public IslandSurvivalCraft() { - worldInfos = new WorldInfoManager(); + worldInfoManager = new WorldInfoManager(); } @Override public void onEnable() { pluginManager = getServer().getPluginManager(); - pluginManager.registerEvents(worldInfos, this); + pluginManager.registerEvents(worldInfoManager, this); + commandProcessor = new CommandProcessor(this); super.onEnable(); } @Override public void onDisable() { + HandlerList.unregisterAll(worldInfoManager); + commandProcessor = null; super.onDisable(); } @@ -36,7 +44,18 @@ public class IslandSurvivalCraft extends JavaPlugin implements Listener { } catch (NullPointerException | IllegalArgumentException e) { gID = GeneratorModes.UNIQUE; } - return worldInfos.getChunkGenerator(getServer().getWorld(worldName), gID); + return worldInfoManager.getChunkGenerator(getServer().getWorld(worldName), gID); } + /** + * @return the world information manager. + */ + public WorldInfoManager getWorldInfoManager() { + return worldInfoManager; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + return commandProcessor.onCommand(sender, command, label, args); + } } diff --git a/src/main/java/ca/recrown/islandsurvivalcraft/interaction/commands/CommandProcessor.java b/src/main/java/ca/recrown/islandsurvivalcraft/interaction/commands/CommandProcessor.java new file mode 100644 index 0000000..861da3b --- /dev/null +++ b/src/main/java/ca/recrown/islandsurvivalcraft/interaction/commands/CommandProcessor.java @@ -0,0 +1,45 @@ +package ca.recrown.islandsurvivalcraft.interaction.commands; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.concurrent.CompletableFuture; + +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import ca.recrown.islandsurvivalcraft.IslandSurvivalCraft; + +public class CommandProcessor implements CommandExecutor { + private IslandSurvivalCraft islandSurvivalCraft; + private HashSet initializedCommands = new HashSet<>(); + + public CommandProcessor(IslandSurvivalCraft islandSurvivalCraft) { + this.islandSurvivalCraft = islandSurvivalCraft; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (args.length < 1 || args[0].isEmpty()) return false; + Commands currentCommand = null; + try { + currentCommand = Commands.valueOf(args[0].toUpperCase()); + } catch (IllegalArgumentException e) { + sender.sendMessage(ChatColor.LIGHT_PURPLE + "This command was not understood. Refer to \"/IslandSurvivalCraft help\" for more info."); + } + if (initializedCommands.add(currentCommand)) currentCommand.initialize(islandSurvivalCraft); + String[] commandArguments = Arrays.copyOfRange(args, 1, args.length); + CompletableFuture results = currentCommand.invoke(sender, commandArguments); + results.thenAccept(s -> { + if (sender instanceof Player) { + Player player = (Player) sender; + if (!player.isOnline()) return; + } + sender.sendMessage(s != null ? s : ""); + }); + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/ca/recrown/islandsurvivalcraft/interaction/commands/Commands.java b/src/main/java/ca/recrown/islandsurvivalcraft/interaction/commands/Commands.java new file mode 100644 index 0000000..ade9adf --- /dev/null +++ b/src/main/java/ca/recrown/islandsurvivalcraft/interaction/commands/Commands.java @@ -0,0 +1,52 @@ +package ca.recrown.islandsurvivalcraft.interaction.commands; + +import ca.recrown.islandsurvivalcraft.interaction.commands.runnables.HighlightIslandCommand; + +import java.util.concurrent.CompletableFuture; + +import org.bukkit.command.CommandSender; + +import ca.recrown.islandsurvivalcraft.IslandSurvivalCraft; +import ca.recrown.islandsurvivalcraft.interaction.commands.runnables.CommandRunnable; +import ca.recrown.islandsurvivalcraft.interaction.commands.runnables.HelpCommand; + +public enum Commands implements CommandRunnable { + HIGHLIGHT(new HighlightIslandCommand()), + HELP(new HelpCommand()), + ; + + private final CommandRunnable commandRunnable; + private boolean initialized = false; + + private Commands(CommandRunnable runnable) { + this.commandRunnable = runnable; + } + + @Override + public String toString() { + return name().toLowerCase(); + } + + @Override + public CompletableFuture invoke(CommandSender sender, String[] args) { + return commandRunnable.invoke(sender, args); + } + + @Override + public void initialize(IslandSurvivalCraft islandSurvivalCraft) { + if (initialized) throw new IllegalStateException("Command already initialized."); + initialized = true; + commandRunnable.initialize(islandSurvivalCraft); + } + + @Override + public String getDescription() { + return commandRunnable.getDescription(); + } + + @Override + public String getDetailedDescription() { + return commandRunnable.getDetailedDescription(); + } + +} \ No newline at end of file diff --git a/src/main/java/ca/recrown/islandsurvivalcraft/interaction/commands/runnables/CommandRunnable.java b/src/main/java/ca/recrown/islandsurvivalcraft/interaction/commands/runnables/CommandRunnable.java new file mode 100644 index 0000000..ddadcf9 --- /dev/null +++ b/src/main/java/ca/recrown/islandsurvivalcraft/interaction/commands/runnables/CommandRunnable.java @@ -0,0 +1,34 @@ +package ca.recrown.islandsurvivalcraft.interaction.commands.runnables; + +import java.util.concurrent.CompletableFuture; + +import org.bukkit.command.CommandSender; + +import ca.recrown.islandsurvivalcraft.IslandSurvivalCraft; + +public interface CommandRunnable { + /** + * @return a string that gives users an idea of what this command does. Should be short and not include details on arguments. + */ + public String getDescription(); + + /** + * @return A more detailed description. Doesn't need to contain information already present in normal description as will print with that one. This string should describe proper usage arguments required. + */ + public String getDetailedDescription(); + + /** + * Invokes this particular command. + * Should check for correct arguments. + * @param sender The sender of this command. + * @param args The arguments given for this command. May be length of 0, in which case there were no arguments. + * @return The completable future for the command. The completable future should ultimately return a message in the form of the string letting the user know the the completed execution, or a failure message. + */ + public CompletableFuture invoke(CommandSender sender, String[] args); + + /** + * Initializes the command. Is run the first time this command is run. + * @param islandSurvivalCraft + */ + public void initialize(IslandSurvivalCraft islandSurvivalCraft); +} \ No newline at end of file diff --git a/src/main/java/ca/recrown/islandsurvivalcraft/interaction/commands/runnables/HelpCommand.java b/src/main/java/ca/recrown/islandsurvivalcraft/interaction/commands/runnables/HelpCommand.java new file mode 100644 index 0000000..52a7025 --- /dev/null +++ b/src/main/java/ca/recrown/islandsurvivalcraft/interaction/commands/runnables/HelpCommand.java @@ -0,0 +1,61 @@ +package ca.recrown.islandsurvivalcraft.interaction.commands.runnables; + +import java.util.concurrent.CompletableFuture; + +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; + +import ca.recrown.islandsurvivalcraft.IslandSurvivalCraft; +import ca.recrown.islandsurvivalcraft.interaction.commands.Commands; + +public class HelpCommand implements CommandRunnable { + private String helpMessage; + @Override + public String getDescription() { + return "Displays these help messages."; + } + + @Override + public CompletableFuture invoke(CommandSender sender, String[] args) { + CompletableFuture completableFuture = new CompletableFuture<>(); + if (args == null || args.length == 0) { + completableFuture.complete(helpMessage); + } else { + Commands command = null; + try { + command = Commands.valueOf(args[0].toUpperCase()); + } catch (IllegalArgumentException e) { + completableFuture.complete(ChatColor.LIGHT_PURPLE + String.format("The request %s does not exist. Please type \"/IslandSurvivalCraft help\" for more info.", args[0])); + return completableFuture; + } + StringBuilder sb = new StringBuilder(); + sb.append(ChatColor.YELLOW + args[0]); + sb.append(": " + ChatColor.GRAY); + sb.append(command.getDescription()); + sb.append("\n" + ChatColor.WHITE); + sb.append(command.getDetailedDescription()); + completableFuture.complete(sb.toString().trim()); + } + return completableFuture; + } + + @Override + public void initialize(IslandSurvivalCraft islandSurvivalCraft) { + StringBuilder stringBuilder = new StringBuilder(); + Commands[] commands = Commands.values(); + stringBuilder.append(ChatColor.YELLOW + "Commands:\n"); + for (int i = 0; i < commands.length; i++) { + stringBuilder.append(ChatColor.GREEN + commands[i].toString()); + stringBuilder.append(": " + ChatColor.WHITE); + stringBuilder.append(commands[i].getDescription()); + stringBuilder.append("\n"); + } + helpMessage = stringBuilder.toString().trim(); + } + + @Override + public String getDetailedDescription() { + return " \"islandsurvivalcraft help [command]\". Where [command] is a listed command, can optionally be added to get a detailed look at the command. Help is a command that lists all the other potential commands that may be run. Can also look at the detailed commands. But you knew this since your using help with help..."; + } + +} \ No newline at end of file diff --git a/src/main/java/ca/recrown/islandsurvivalcraft/interaction/commands/runnables/HighlightIslandCommand.java b/src/main/java/ca/recrown/islandsurvivalcraft/interaction/commands/runnables/HighlightIslandCommand.java new file mode 100644 index 0000000..85610c4 --- /dev/null +++ b/src/main/java/ca/recrown/islandsurvivalcraft/interaction/commands/runnables/HighlightIslandCommand.java @@ -0,0 +1,97 @@ +package ca.recrown.islandsurvivalcraft.interaction.commands.runnables; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.concurrent.CompletableFuture; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Particle; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import ca.recrown.islandsurvivalcraft.IslandSurvivalCraft; +import ca.recrown.islandsurvivalcraft.world.WorldInfo; + +public class HighlightIslandCommand implements CommandRunnable { + private final int CHECK_RADIUS = 3; + private final int PARTICLE_AMOUNT = 2; + private final Particle PARTICLE_TYPE = Particle.FIREWORKS_SPARK; + private final double OFFSET_X = 0.0d, OFFSET_Y = 0.0d, OFFSET_Z = 0.0d; + + HashSet playersHighlighting = new HashSet<>(); + LinkedList waitingList = new LinkedList<>(); + + @Override + public String getDescription() { + return "Displays particle effects at edge of islands."; + } + + @Override + public CompletableFuture invoke(CommandSender sender, String[] args) { + CompletableFuture completableFuture = new CompletableFuture<>(); + if (!(sender instanceof Player)) { + completableFuture.complete("This command can only be run as a player!"); + return completableFuture; + } + Player player = (Player) sender; + if (args[0].isEmpty()) { + completableFuture.complete("Arguments required. Please refer to \"/IslandSurvivalCraft help\" for more info."); + return completableFuture; + } + if (args[0].toLowerCase().equals("start")) { + if (playersHighlighting.add(player)) { + waitingList.add(player); + completableFuture.complete("You are now highlighting islands."); + } else { + completableFuture.complete("You are already highlighting islands."); + } + } else if (args[0].toLowerCase().equals("stop")) { + if (playersHighlighting.remove(player)) { + completableFuture.complete("You are no longer highlighting islands."); + } else { + completableFuture.complete("You weren't highlighting islands."); + } + } else { + completableFuture.complete(String.format("Argument \"%d\" was not understood. Please refer to \"/IslandSurvivalCraft help\" for more info.", args[0])); + } + return completableFuture; + } + + @Override + public void initialize(IslandSurvivalCraft islandsurvivalcraft) { + Bukkit.getScheduler().scheduleSyncRepeatingTask(islandsurvivalcraft, new Runnable() { + @Override + public void run() { + Player player = waitingList.poll(); + if (player == null || !player.isOnline() || !playersHighlighting.contains(player)) { + playersHighlighting.remove(player); + return; + } + World world = player.getWorld(); + WorldInfo worldInfo = islandsurvivalcraft.getWorldInfoManager().retrieve(world); + Location playerLocation = player.getLocation(); + int playerX = playerLocation.getBlockX(), playerY = playerLocation.getBlockY(), playerZ = playerLocation.getBlockZ(); + for (int x = 1; x < CHECK_RADIUS; x++) { + for (int z = 1; z < CHECK_RADIUS; z++) { + if (worldInfo.getIslandMap().isIsland(playerX + x, playerZ + z)) { + world.spawnParticle(PARTICLE_TYPE, x + playerX, playerY, z + playerZ, PARTICLE_AMOUNT, OFFSET_X, OFFSET_Y, OFFSET_Z); + } + if (worldInfo.getIslandMap().isIsland(playerX - x, playerZ - z)) { + world.spawnParticle(PARTICLE_TYPE, playerX - x, playerY, playerZ - z, PARTICLE_AMOUNT, OFFSET_X, OFFSET_Y, OFFSET_Z); + } + } + } + waitingList.add(player); + } + }, 0, 5); + + } + + @Override + public String getDetailedDescription() { + return "A helpful debugging command to analyze what the plugin considers an island by displaying particle effects around the player that is a part of the island. The argument is required to dicate whether or not to start the highlighting or stop it."; + } + +} \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 7046733..77310f9 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -6,4 +6,14 @@ description: Adds the gamemode IslandSurvivalCraft. depend: [] load: startup api-version: 1.15 -commands: \ No newline at end of file +commands: + IslandSurvivalCraft: + aliases: [isc] + description: The administration command for Island Survival Craft. + usage: Type "/IslandSurvivalCraft help" (or any of its aliases) for a list of commands. + permission: islandsurvivalcraft.admin + permission-message: This command isn't needed for normal gameplay, and therefore, by default, only operators have access to it. +permissions: + islandsurvivalcraft.admin: + description: Gives ability to configure and test Island Survival Craft features in game. + default: op \ No newline at end of file