From 2ea268f90f6ee5640bde8df4c708e11f0e025bee Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 3 Jun 2018 13:36:48 -0700 Subject: [PATCH] Added confirmation code to CompositeCommand How does this look? --- locales/en-US.yml | 15 ++- .../api/commands/CompositeCommand.java | 111 +++++++++++++++--- .../commands/admin/AdminSchemCommand.java | 5 +- .../commands/island/IslandResetCommand.java | 34 +----- .../bskyblock/island/builders/Clipboard.java | 29 +++-- 5 files changed, 133 insertions(+), 61 deletions(-) diff --git a/locales/en-US.yml b/locales/en-US.yml index 027e520b5..20ee290a0 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -17,6 +17,9 @@ banner: general: success: "&aSuccess!" + request-cancelled: "&cConfirmation timeout - &brequest cancelled" + previous-request-cancelled: "&6Previous confirmation request cancelled" + confirm: "&cType command again within &b[seconds]s&c to confirm" errors: command-cancelled: "&cCommand cancelled" no-permission: "&cYou don't have permission to execute this command." @@ -121,6 +124,16 @@ commands: description: "set a player's rank on their island" unknown-rank: "&cUnknown rank!" rank-set: "&aRank set from [from] to [to]." + schem: + parameters: "" + description: "manipulate schems" + copy-first: "&cCopy a schem first!" + no-such-file: "&cNo such file!" + could-not-load: "&cCould not load that file!" + set-pos1: "&aPosition 1 set at [vector]" + set-pos2: "&aPosition 2 set at [vector]" + need-pos1-pos2: "&cSet pos1 and pos2 first!" + copied-blocks: "&bCopied [number] blocks to clipboard" island: about: description: "display copyright and license info" @@ -147,8 +160,6 @@ commands: must-remove-members: "You must remove all members from your island before you can restart it (/island kick )." none-left: "&cYou have no more resets left!" resets-left: "&cYou have [number] resets left" - confirm: "&cType &b/[label] reset confirm&c within [seconds]s to confirm reset" - cancelled: "&bReset cancelled" sethome: description: "set your teleport point for /island" must-be-on-your-island: "You must be on your island to set home!" diff --git a/src/main/java/us/tastybento/bskyblock/api/commands/CompositeCommand.java b/src/main/java/us/tastybento/bskyblock/api/commands/CompositeCommand.java index 7771c0781..7fd4ee60a 100644 --- a/src/main/java/us/tastybento/bskyblock/api/commands/CompositeCommand.java +++ b/src/main/java/us/tastybento/bskyblock/api/commands/CompositeCommand.java @@ -2,6 +2,7 @@ package us.tastybento.bskyblock.api.commands; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; @@ -16,6 +17,7 @@ import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.PluginIdentifiableCommand; import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitTask; import us.tastybento.bskyblock.BSkyBlock; import us.tastybento.bskyblock.Settings; @@ -34,7 +36,7 @@ import us.tastybento.bskyblock.util.Util; */ public abstract class CompositeCommand extends Command implements PluginIdentifiableCommand, BSBCommand { - private BSkyBlock plugin; + private final BSkyBlock plugin; /** * True if the command is for the player only (not for the console) @@ -69,28 +71,30 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi * The command chain from the very top, e.g., island team promote */ private String usage; - + /** * The prefix to be used in this command */ private String permissionPrefix = ""; - + /** * The world that this command operates in. This is an overworld and will cover any associated nether or end * If the world value does not exist, then the command is general across worlds */ private World world; - + /** * The addon creating this command, if any */ private Addon addon; - + /** * The top level label */ private String topLabel = ""; + private static Map toBeConfirmed = new HashMap<>(); + /** * Used only for testing.... */ @@ -135,7 +139,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi new DefaultHelpCommand(this); } } - + /** * This is the top-level command constructor for commands that have no parent. * @param label - string for this command @@ -224,14 +228,13 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi if (event.isCancelled()) { return true; } - // Execute and trim args return cmd.execute(user, Arrays.asList(args).subList(cmd.subCommandLevel, args.length)); } /** * Get the current composite command based on the arguments - * @param args + * @param args - arguments * @return the current composite command based on the arguments */ private CompositeCommand getCommandFromArgs(String[] args) { @@ -239,7 +242,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi // Run through any arguments for (String arg : args) { // get the subcommand corresponding to the arg - if (subCommand.hasSubCommmands()) { + if (subCommand.hasSubCommands()) { Optional sub = subCommand.getSubCommand(arg); if (!sub.isPresent()) { return subCommand; @@ -374,7 +377,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi /** * Check if this command has a specific sub command - * @param subCommand + * @param subCommand - sub command * @return true if this command has this sub command */ protected boolean hasSubCommand(String subCommand) { @@ -385,7 +388,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi * Check if this command has any sub commands * @return true if this command has subcommands */ - protected boolean hasSubCommmands() { + protected boolean hasSubCommands() { return !subCommands.isEmpty(); } @@ -418,7 +421,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi /** * Set whether this command is only for players - * @param onlyPlayer + * @param onlyPlayer - true if command only for players */ public void setOnlyPlayer(boolean onlyPlayer) { this.onlyPlayer = onlyPlayer; @@ -426,7 +429,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi /** * Sets the command parameters to be shown in help - * @param parameters + * @param parameters - string of parameters */ public void setParameters(String parameters) { this.parameters = parameters; @@ -439,7 +442,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi public void setPermission(String permission) { this.permission = permissionPrefix + permission; } - + /** * Inherits the permission from parent command */ @@ -479,7 +482,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi // Add any tab completion from the subcommand options.addAll(cmd.tabComplete(User.getInstance(sender), alias, new LinkedList<>(Arrays.asList(args))).orElse(new ArrayList<>())); // Add any sub-commands automatically - if (cmd.hasSubCommmands()) { + if (cmd.hasSubCommands()) { // Check if subcommands are visible to this sender for (CompositeCommand subCommand: cmd.getSubCommands().values()) { if (sender instanceof Player) { @@ -505,7 +508,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi /** * Show help - * @param command + * @param command - command that this help is for * @param user - the User * @return result of help command or false if no help defined */ @@ -561,11 +564,85 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi public Addon getAddon() { return addon; } - + /** * @return top level label, e.g., island */ public String getTopLabel() { return topLabel; } + + /** + * Tells user to confirm command by retyping + * @param user - user + * @param confirmed - runnable to be executed if confirmed + */ + public void askConfirmation(User user, Runnable confirmed) { + // Check for pending confirmations + if (toBeConfirmed.containsKey(user)) { + if (toBeConfirmed.get(user).getTopLabel().equals(getTopLabel()) && toBeConfirmed.get(user).getLabel().equalsIgnoreCase(getLabel())) { + toBeConfirmed.get(user).getTask().cancel(); + getPlugin().getServer().getScheduler().runTask(getPlugin(), toBeConfirmed.get(user).getRunnable()); + toBeConfirmed.remove(user); + return; + } else { + // Player has another outstanding confirmation request that will now be cancelled + user.sendMessage("general.previous-request-cancelled"); + } + } + // Tell user that they need to confirm + user.sendMessage("general.confirm", "[seconds]", String.valueOf(getSettings().getConfirmationTime())); + // Set up a cancellation task + BukkitTask task = getPlugin().getServer().getScheduler().runTaskLater(getPlugin(), () -> { + user.sendMessage("general.request-cancelled"); + toBeConfirmed.remove(user); + }, getPlugin().getSettings().getConfirmationTime() * 20L); + + // Add to the global confirmation map + toBeConfirmed.put(user, new Confirmer(getTopLabel(), getLabel(), confirmed, task)); + } + + private class Confirmer { + private final String topLabel; + private final String label; + private final Runnable runnable; + private final BukkitTask task; + + /** + * @param label - command label + * @param runnable - runnable to run when confirmed + * @param task - task ID to cancel when confirmed + */ + Confirmer(String topLabel, String label, Runnable runnable, BukkitTask task) { + this.topLabel = topLabel; + this.label = label; + this.runnable = runnable; + this.task = task; + } + /** + * @return the topLabel + */ + public String getTopLabel() { + return topLabel; + } + /** + * @return the label + */ + public String getLabel() { + return label; + } + /** + * @return the runnable + */ + public Runnable getRunnable() { + return runnable; + } + /** + * @return the task + */ + public BukkitTask getTask() { + return task; + } + + } } diff --git a/src/main/java/us/tastybento/bskyblock/commands/admin/AdminSchemCommand.java b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminSchemCommand.java index 38f06f76e..74be92a46 100644 --- a/src/main/java/us/tastybento/bskyblock/commands/admin/AdminSchemCommand.java +++ b/src/main/java/us/tastybento/bskyblock/commands/admin/AdminSchemCommand.java @@ -51,13 +51,16 @@ public class AdminSchemCommand extends CompositeCommand { if (args.get(0).equalsIgnoreCase("load")) { if (args.size() == 2) { - File file = new File(schemFolder, args.get(1) + ".schem"); + File file = new File(schemFolder, args.get(1)); if (file.exists()) { try { cb.load(file); + user.sendMessage("general.success"); + clipboards.put(user.getUniqueId(), cb); return true; } catch (Exception e) { user.sendMessage("commands.admin.schem.could-not-load"); + e.printStackTrace(); return false; } } else { diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/IslandResetCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/IslandResetCommand.java index 58fd53d4b..841285139 100644 --- a/src/main/java/us/tastybento/bskyblock/commands/island/IslandResetCommand.java +++ b/src/main/java/us/tastybento/bskyblock/commands/island/IslandResetCommand.java @@ -6,7 +6,6 @@ import java.util.List; import java.util.Map; import java.util.UUID; -import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.entity.Player; @@ -63,40 +62,11 @@ public class IslandResetCommand extends CompositeCommand { user.sendMessage("commands.island.reset.resets-left", "[number]", String.valueOf(getPlayers().getResetsLeft(user.getUniqueId()))); } } - // Check for non-confirm command - if (!args.isEmpty() && !(confirm.containsKey(user.getUniqueId()) && args.get(0).equalsIgnoreCase("confirm"))) { - showHelp(this, user); - return false; - } - - // Check confirmation or reset immediately if no confirmation required - if (!getSettings().isResetConfirmation() || (confirm.containsKey(user.getUniqueId()) && args.size() == 1 && args.get(0).equalsIgnoreCase("confirm"))) { - return resetIsland(user); - } - - // Confirmation required - if (!confirm.containsKey(user.getUniqueId())) { - requestConfirmation(user); - } else { - // Show how many seconds left to confirm - int time = (int)((confirm.get(user.getUniqueId()) - System.currentTimeMillis()) / 1000D); - user.sendMessage("commands.island.reset.confirm", "[label]", getTopLabel(), SECONDS_PLACEHOLDER, String.valueOf(time)); - } + // Request confirmation + this.askConfirmation(user, () -> resetIsland(user)); return true; } - private void requestConfirmation(User user) { - user.sendMessage("commands.island.reset.confirm", "[label]", getTopLabel(), SECONDS_PLACEHOLDER, String.valueOf(getSettings().getConfirmationTime())); - // Require confirmation - confirm.put(user.getUniqueId(), System.currentTimeMillis() + getSettings().getConfirmationTime() * 1000L); - Bukkit.getScheduler().runTaskLater(getPlugin(), () -> { - if (confirm.containsKey(user.getUniqueId())) { - user.sendMessage("commands.island.reset.cancelled"); - confirm.remove(user.getUniqueId()); - } - }, getSettings().getConfirmationTime() * 20L); - } - private boolean resetIsland(User user) { // Remove the confirmation confirm.remove(user.getUniqueId()); diff --git a/src/main/java/us/tastybento/bskyblock/island/builders/Clipboard.java b/src/main/java/us/tastybento/bskyblock/island/builders/Clipboard.java index de672ffd5..daee9c051 100644 --- a/src/main/java/us/tastybento/bskyblock/island/builders/Clipboard.java +++ b/src/main/java/us/tastybento/bskyblock/island/builders/Clipboard.java @@ -28,6 +28,7 @@ import org.bukkit.block.banner.PatternType; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; import org.bukkit.material.Attachable; @@ -100,6 +101,8 @@ public class Clipboard { user.sendMessage("commands.admin.schem.need-pos1-pos2"); return false; } + // Clear the clipboard + blockConfig = new YamlConfiguration(); int count = 0; for (int x = Math.min(pos1.getBlockX(), pos2.getBlockX()); x <= Math.max(pos1.getBlockX(),pos2.getBlockX()); x++) { for (int y = Math.min(pos1.getBlockY(), pos2.getBlockY()); y <= Math.max(pos1.getBlockY(),pos2.getBlockY()); y++) { @@ -221,15 +224,12 @@ public class Clipboard { } bs.update(true, false); + if (bs instanceof InventoryHolder) { Bukkit.getLogger().info("Inventory holder " + s.getCurrentPath()); - - InventoryHolder ih = (InventoryHolder)bs; - @SuppressWarnings("unchecked") - List items = (List) s.get("inventory"); - for (int i = 0; i < ih.getInventory().getSize(); i++) { - ih.getInventory().setItem(i, items.get(i)); - } + Inventory ih = ((InventoryHolder)bs).getInventory(); + ConfigurationSection inv = s.getConfigurationSection("inventory"); + inv.getKeys(false).forEach(i -> ih.setItem(Integer.valueOf(i), (ItemStack)inv.get(i))); } } @@ -306,10 +306,20 @@ public class Clipboard { if (bs instanceof InventoryHolder) { Bukkit.getLogger().info("Inventory holder"); InventoryHolder ih = (InventoryHolder)bs; - s.set("inventory", ih.getInventory().getContents()); + for (int index = 0; index < ih.getInventory().getSize(); index++) { + ItemStack i = ih.getInventory().getItem(index); + if (i != null) { + s.set("inventory." + index, i); + } + } } } + + private void set(ItemStack i) { + + } + /** * @return the blockConfig */ @@ -325,6 +335,7 @@ public class Clipboard { */ public void load(File file) throws IOException, InvalidConfigurationException { unzip(file.getAbsolutePath()); + blockConfig = new YamlConfiguration(); blockConfig.load(file); copied = true; Files.delete(file.toPath()); @@ -374,7 +385,7 @@ public class Clipboard { } private void zip(File targetFile) throws IOException { - try (ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(targetFile.getAbsolutePath() + ".schem"))) { + try (ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(targetFile.getAbsolutePath() + ".zip"))) { zipOutputStream.putNextEntry(new ZipEntry(targetFile.getName())); try (FileInputStream inputStream = new FileInputStream(targetFile)) {