From cd9ee900ae7602c79a1970b7a4e307c130ae3f63 Mon Sep 17 00:00:00 2001 From: Brettflan Date: Thu, 6 Mar 2014 15:03:44 -0600 Subject: [PATCH] Major rework of command handling. This is mostly internal code changes rather than something noticeable to the end user, though on command input errors (wrong number or format of arguments, etc.) it will now have it show examples for the command the same as seen in the command list provided by the root wb command. Replacing the monolithic and crude "if...else if...else if..." command handler in WBCommand.java with every command crammed in there, each command is now handled in a separate subclass in the new "com.wimbli.WorldBorder.cmd" package. This is inspired by the way the Factions plugin handles subclassed commands. This is the first pass of the command handling rework. I still need to do some further testing, and I plan to further improve feedback for input errors to also show some basic help info along with the command examples. --- .../java/com/wimbli/WorldBorder/Config.java | 28 +- .../com/wimbli/WorldBorder/WBCommand.java | 1302 ++--------------- .../com/wimbli/WorldBorder/cmd/CmdBypass.java | 48 + .../wimbli/WorldBorder/cmd/CmdBypasslist.java | 26 + .../com/wimbli/WorldBorder/cmd/CmdClear.java | 66 + .../wimbli/WorldBorder/cmd/CmdCommands.java | 74 + .../com/wimbli/WorldBorder/cmd/CmdDebug.java | 32 + .../com/wimbli/WorldBorder/cmd/CmdDelay.java | 42 + .../wimbli/WorldBorder/cmd/CmdDenypearl.java | 32 + .../com/wimbli/WorldBorder/cmd/CmdDynmap.java | 32 + .../wimbli/WorldBorder/cmd/CmdDynmapmsg.java | 39 + .../com/wimbli/WorldBorder/cmd/CmdFill.java | 179 +++ .../WorldBorder/cmd/CmdFillautosave.java | 53 + .../com/wimbli/WorldBorder/cmd/CmdGetmsg.java | 29 + .../wimbli/WorldBorder/cmd/CmdKnockback.java | 42 + .../com/wimbli/WorldBorder/cmd/CmdList.java | 40 + .../com/wimbli/WorldBorder/cmd/CmdPortal.java | 32 + .../com/wimbli/WorldBorder/cmd/CmdRadius.java | 59 + .../com/wimbli/WorldBorder/cmd/CmdReload.java | 32 + .../wimbli/WorldBorder/cmd/CmdRemount.java | 51 + .../com/wimbli/WorldBorder/cmd/CmdSet.java | 136 ++ .../wimbli/WorldBorder/cmd/CmdSetcorners.java | 55 + .../com/wimbli/WorldBorder/cmd/CmdSetmsg.java | 41 + .../com/wimbli/WorldBorder/cmd/CmdShape.java | 39 + .../com/wimbli/WorldBorder/cmd/CmdTrim.java | 172 +++ .../com/wimbli/WorldBorder/cmd/CmdWhoosh.java | 32 + .../com/wimbli/WorldBorder/cmd/CmdWrap.java | 58 + .../com/wimbli/WorldBorder/cmd/CmdWshape.java | 66 + .../com/wimbli/WorldBorder/cmd/WBCmd.java | 130 ++ src/main/resources/plugin.yml | 29 +- 30 files changed, 1811 insertions(+), 1185 deletions(-) create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdBypass.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdBypasslist.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdClear.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdCommands.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdDebug.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdDelay.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdDenypearl.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdDynmap.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdDynmapmsg.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdFill.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdFillautosave.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdGetmsg.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdKnockback.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdList.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdPortal.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdRadius.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdReload.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdRemount.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdSet.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdSetcorners.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdSetmsg.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdShape.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdTrim.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdWhoosh.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdWrap.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/CmdWshape.java create mode 100644 src/main/java/com/wimbli/WorldBorder/cmd/WBCmd.java diff --git a/src/main/java/com/wimbli/WorldBorder/Config.java b/src/main/java/com/wimbli/WorldBorder/Config.java index dc852b7..dc99e9a 100644 --- a/src/main/java/com/wimbli/WorldBorder/Config.java +++ b/src/main/java/com/wimbli/WorldBorder/Config.java @@ -62,33 +62,38 @@ public class Config } - public static void setBorder(String world, BorderData border) + public static void setBorder(String world, BorderData border, boolean logIt) { borders.put(world, border); - log("Border set. " + BorderDescription(world)); + if (logIt) + log("Border set. " + BorderDescription(world)); save(true); DynMapFeatures.showBorder(world, border); } + public static void setBorder(String world, BorderData border) + { + setBorder(world, border, true); + } public static void setBorder(String world, int radiusX, int radiusZ, double x, double z, Boolean shapeRound) { BorderData old = Border(world); boolean oldWrap = (old != null) && old.getWrapping(); - setBorder(world, new BorderData(x, z, radiusX, radiusZ, shapeRound, oldWrap)); + setBorder(world, new BorderData(x, z, radiusX, radiusZ, shapeRound, oldWrap), true); } public static void setBorder(String world, int radiusX, int radiusZ, double x, double z) { BorderData old = Border(world); Boolean oldShape = (old == null) ? null : old.getShape(); boolean oldWrap = (old != null) && old.getWrapping(); - setBorder(world, new BorderData(x, z, radiusX, radiusZ, oldShape, oldWrap)); + setBorder(world, new BorderData(x, z, radiusX, radiusZ, oldShape, oldWrap), true); } // backwards-compatible methods from before elliptical/rectangular shapes were supported public static void setBorder(String world, int radius, double x, double z, Boolean shapeRound) { - setBorder(world, new BorderData(x, z, radius, radius, shapeRound)); + setBorder(world, new BorderData(x, z, radius, radius, shapeRound), true); } public static void setBorder(String world, int radius, double x, double z) { @@ -103,7 +108,7 @@ public class Config double radiusZ = Math.abs(z1 - z2) / 2; double x = ((x1 < x2) ? x1 : x2) + radiusX; double z = ((z1 < z2) ? z1 : z2) + radiusZ; - setBorder(world, new BorderData(x, z, (int)Math.round(radiusX), (int)Math.round(radiusZ), shapeRound, wrap)); + setBorder(world, new BorderData(x, z, (int)Math.round(radiusX), (int)Math.round(radiusZ), shapeRound, wrap), true); } public static void setBorderCorners(String world, double x1, double z1, double x2, double z2, Boolean shapeRound) { @@ -168,7 +173,6 @@ public class Config public static void setMessage(String msg) { updateMessage(msg); - log("Border message is now set to: " + MessageRaw()); save(true); } @@ -209,8 +213,10 @@ public class Config { return ShapeName(shapeRound); } - public static String ShapeName(boolean round) + public static String ShapeName(Boolean round) { + if (round == null) + return "default"; return round ? "elliptic/round" : "rectangular/square"; } @@ -312,9 +318,11 @@ public class Config if (remountDelayTicks == 0) log("Remount delay set to 0. Players will be left dismounted when knocked back from the border while on a vehicle."); else + { log("Remount delay set to " + remountDelayTicks + " tick(s). That is roughly " + (remountDelayTicks * 50) + "ms / " + (((double)remountDelayTicks * 50.0) / 1000.0) + " seconds."); - if (ticks < 10) - logWarn("setting the remount delay to less than 10 (and greater than 0) is not recommended. This can lead to nasty client glitches."); + if (ticks < 10) + logWarn("setting the remount delay to less than 10 (and greater than 0) is not recommended. This can lead to nasty client glitches."); + } save(true); } diff --git a/src/main/java/com/wimbli/WorldBorder/WBCommand.java b/src/main/java/com/wimbli/WorldBorder/WBCommand.java index 0e96481..edc33bd 100644 --- a/src/main/java/com/wimbli/WorldBorder/WBCommand.java +++ b/src/main/java/com/wimbli/WorldBorder/WBCommand.java @@ -1,32 +1,66 @@ package com.wimbli.WorldBorder; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; import org.bukkit.command.*; import org.bukkit.entity.Player; -import org.bukkit.Location; -import org.bukkit.World; + +import com.wimbli.WorldBorder.cmd.*; public class WBCommand implements CommandExecutor { - private WorldBorder plugin; - - // color values for strings - private final String clrCmd = ChatColor.AQUA.toString(); // main commands - private final String clrReq = ChatColor.GREEN.toString(); // required values - private final String clrOpt = ChatColor.DARK_GREEN.toString(); // optional values - private final String clrDesc = ChatColor.WHITE.toString(); // command descriptions - private final String clrHead = ChatColor.YELLOW.toString(); // command listing header - private final String clrErr = ChatColor.RED.toString(); // errors / notices + // map of all sub-commands with the command name (string) for quick reference + private Map subCommands = new LinkedHashMap(); + // ref. list of the commands which can have a world name in front of the command itself (ex. /wb _world_ radius 100) + private Set subCommandsWithWorldNames = new LinkedHashSet(); + // constructor public WBCommand (WorldBorder plugin) { - this.plugin = plugin; + addCmd(new CmdSet()); + addCmd(new CmdSetcorners()); + addCmd(new CmdRadius()); + addCmd(new CmdShape()); + addCmd(new CmdClear()); + addCmd(new CmdList()); + addCmd(new CmdFill()); + addCmd(new CmdTrim()); + addCmd(new CmdBypass()); + addCmd(new CmdBypasslist()); + addCmd(new CmdKnockback()); + addCmd(new CmdWrap()); + addCmd(new CmdWhoosh()); + addCmd(new CmdGetmsg()); + addCmd(new CmdSetmsg()); + addCmd(new CmdDelay()); + addCmd(new CmdWshape()); + addCmd(new CmdDynmap()); + addCmd(new CmdDynmapmsg()); + addCmd(new CmdRemount()); + addCmd(new CmdFillautosave()); + addCmd(new CmdPortal()); + addCmd(new CmdDenypearl()); + addCmd(new CmdReload()); + addCmd(new CmdDebug()); + + // this is the default command, which shows command example pages; should be last just in case + addCmd(new CmdCommands()); + } + + + private void addCmd(WBCmd cmd) + { + subCommands.put(cmd.name, cmd); + if (cmd.hasWorldNameInput) + subCommandsWithWorldNames.add(cmd.name); } @Override @@ -34,1181 +68,127 @@ public class WBCommand implements CommandExecutor { Player player = (sender instanceof Player) ? (Player)sender : null; - String cmd = clrCmd + ((player == null) ? "wb" : "/wb"); - String cmdW = clrCmd + ((player == null) ? "wb " + clrReq + "" : "/wb " + clrOpt + "[world]") + clrCmd; + // if world name is passed inside quotation marks, handle that, and get List instead of String[] + List params = concatenateQuotedWorldName(split); - // if world name is passed inside quotation marks, handle that - if (split.length > 2 && split[0].startsWith("\"")) + String worldName = null; + // is second parameter the command and first parameter a world name? definitely world name if it was in quotation marks + if (wasWorldQuotation || (params.size() > 1 && !subCommands.containsKey(params.get(0)) && subCommandsWithWorldNames.contains(params.get(1)))) + worldName = params.get(0); + + // no command specified? show command examples / help + if (params.isEmpty()) + params.add(0, "commands"); + + // determined the command name + String cmdName = (worldName == null) ? params.get(0).toLowerCase() : params.get(1).toLowerCase(); + + // remove command name and (if there) world name from front of param array + params.remove(0); + if (worldName != null) + params.remove(0); + + // make sure command is recognized, default to showing command examples / help if not; also check for specified page number + if (!subCommands.containsKey(cmdName)) { - if (split[0].endsWith("\"")) - { - split[0] = split[0].substring(1, split[0].length() - 1); - } - else - { - List args = new ArrayList(); - String quote = split[0]; - int loop; - for (loop = 1; loop < split.length; loop++) - { - quote += " " + split[loop]; - if (split[loop].endsWith("\"")) - break; - } - - if (loop < split.length || !split[loop].endsWith("\"")) - { - args.add(quote.substring(1, quote.length() - 1)); - loop++; - while (loop < split.length) - { - args.add(split[loop]); - loop++; - } - split = args.toArray(new String[0]); - } - } - } - - // "set" command from player or console, world specified - if ((split.length >= 4) && split[1].equalsIgnoreCase("set")) - { - if (!Config.HasPermission(player, "set")) return true; - - if (split.length == 4 && ! split[split.length - 1].equalsIgnoreCase("spawn")) - { // command can only be this short if "spawn" is specified rather than x + z or player name - sender.sendMessage(clrErr + "You have not provided a sufficient number of arguments. Check command list using root /wb command."); - return true; - } - - World world = sender.getServer().getWorld(split[0]); - if (world == null) - sender.sendMessage("The world you specified (\"" + split[0] + "\") could not be found on the server, but data for it will be stored anyway."); - - if(cmdSet(sender, world, player, split, 2)) - sender.sendMessage("Border has been set. " + Config.BorderDescription(split[0])); - } - - // "set" command from player using current world since it isn't specified, or allowed from console only if player name is specified - else if ((split.length >= 2) && split[0].equalsIgnoreCase("set")) - { - if (!Config.HasPermission(player, "set")) return true; - - if (player == null) - { - if (! split[split.length - 2].equalsIgnoreCase("player")) - { // command can only be called by console without world specified if player is specified instead - sender.sendMessage(clrErr + "You must specify a world name from console if not specifying a player name. Check command list using root \"wb\" command."); - return true; - } - player = Bukkit.getPlayer(split[split.length - 1]); - if (player == null || ! player.isOnline()) - { - sender.sendMessage(clrErr + "The player you specified (\"" + split[split.length - 1] + "\") does not appear to be online."); - return true; - } - } - - if (cmdSet(sender, player.getWorld(), player, split, 1)) - sender.sendMessage("Border has been set. " + Config.BorderDescription(player.getWorld().getName())); - } - - // "setcorners" command from player or console, world specified - else if (split.length == 6 && split[1].equalsIgnoreCase("setcorners")) - { - if (!Config.HasPermission(player, "set")) return true; - - String world = split[0]; - World worldTest = sender.getServer().getWorld(world); - if (worldTest == null) - sender.sendMessage("The world you specified (\"" + world + "\") could not be found on the server, but data for it will be stored anyway."); - - try - { - double x1 = Double.parseDouble(split[2]); - double z1 = Double.parseDouble(split[3]); - double x2 = Double.parseDouble(split[4]); - double z2 = Double.parseDouble(split[5]); - Config.setBorderCorners(world, x1, z1, x2, z2); - } - catch(NumberFormatException ex) - { - sender.sendMessage(clrErr + "The x1, z1, x2, and z2 values must be numerical."); - return true; - } - - if(player != null) - sender.sendMessage("Border has been set. " + Config.BorderDescription(world)); - } - - // "setcorners" command from player, using current world - else if (split.length == 5 && split[0].equalsIgnoreCase("setcorners") && player != null) - { - if (!Config.HasPermission(player, "set")) return true; - - String world = player.getWorld().getName(); - - try - { - double x1 = Double.parseDouble(split[1]); - double z1 = Double.parseDouble(split[2]); - double x2 = Double.parseDouble(split[3]); - double z2 = Double.parseDouble(split[4]); - Config.setBorderCorners(world, x1, z1, x2, z2); - } - catch(NumberFormatException ex) - { - sender.sendMessage(clrErr + "The x1, z1, x2, and z2 values must be numerical."); - return true; - } - - sender.sendMessage("Border has been set. " + Config.BorderDescription(world)); - } - - // "radius" command from player or console, world specified - else if ((split.length == 3 || split.length == 4) && split[1].equalsIgnoreCase("radius")) - { - if (!Config.HasPermission(player, "radius")) return true; - - String world = split[0]; - - BorderData border = Config.Border(world); - if (border == null) - { - sender.sendMessage(clrErr + "That world (\"" + world + "\") must first have a border set normally."); - return true; - } - - double x = border.getX(); - double z = border.getZ(); - int radiusX; - int radiusZ; - try - { - radiusX = Integer.parseInt(split[2]); - if (split.length == 4) - radiusZ = Integer.parseInt(split[3]); - else - radiusZ = radiusX; - } - catch(NumberFormatException ex) - { - sender.sendMessage(clrErr + "The radius value(s) must be integers."); - return true; - } - - Config.setBorder(world, radiusX, radiusZ, x, z); - - if (player != null) - sender.sendMessage("Radius has been set. " + Config.BorderDescription(world)); - } - - // "radius" command from player, using current world - else if ((split.length == 2 || split.length == 3) && split[0].equalsIgnoreCase("radius") && player != null) - { - if (!Config.HasPermission(player, "radius")) return true; - - String world = player.getWorld().getName(); - - BorderData border = Config.Border(world); - if (border == null) - { - sender.sendMessage(clrErr + "This world (\"" + world + "\") must first have a border set normally."); - return true; - } - - double x = border.getX(); - double z = border.getZ(); - int radiusX; - int radiusZ; - try - { - radiusX = Integer.parseInt(split[1]); - if (split.length == 3) - radiusZ = Integer.parseInt(split[2]); - else - radiusZ = radiusX; - } - catch(NumberFormatException ex) - { - sender.sendMessage(clrErr + "The radius value(s) must be integers."); - return true; - } - - Config.setBorder(world, radiusX, radiusZ, x, z); - sender.sendMessage("Radius has been set. " + Config.BorderDescription(world)); - } - - // "clear" command from player or console, world specified - else if (split.length == 2 && split[1].equalsIgnoreCase("clear")) - { - if (!Config.HasPermission(player, "clear")) return true; - - String world = split[0]; - BorderData border = Config.Border(world); - if (border == null) - { - sender.sendMessage("The world you specified (\"" + world + "\") does not have a border set."); - return true; - } - - Config.removeBorder(world); - - if (player != null) - sender.sendMessage("Border cleared for world \"" + world + "\"."); - } - - // "clear" command from player, using current world - else if (split.length == 1 && split[0].equalsIgnoreCase("clear") && player != null) - { - if (!Config.HasPermission(player, "clear")) return true; - - String world = player.getWorld().getName(); - BorderData border = Config.Border(world); - if (border == null) - { - sender.sendMessage(clrErr + "Your current world (\"" + world + "\") does not have a border set."); - return true; - } - - Config.removeBorder(world); - sender.sendMessage("Border cleared for world \"" + world + "\"."); - } - - // "clear all" command from player or console - else if (split.length == 2 && split[0].equalsIgnoreCase("clear") && split[1].equalsIgnoreCase("all")) - { - if (!Config.HasPermission(player, "clear")) return true; - - Config.removeAllBorders(); - - if (player != null) - sender.sendMessage("All borders cleared for all worlds."); - } - - // "list" command from player or console - else if (split.length == 1 && split[0].equalsIgnoreCase("list")) - { - if (!Config.HasPermission(player, "list")) return true; - - sender.sendMessage("Default border shape for all worlds is \"" + Config.ShapeName() + "\"."); - - Set list = Config.BorderDescriptions(); - - if (list.isEmpty()) - { - sender.sendMessage("There are no borders currently set."); - return true; - } - - for(String borderDesc : list) - { - sender.sendMessage(borderDesc); - } - } - - // "shape" command from player or console - else if (split.length == 2 && split[0].equalsIgnoreCase("shape")) - { - if (!Config.HasPermission(player, "shape")) return true; - - if (split[1].equalsIgnoreCase("rectangular") || split[1].equalsIgnoreCase("square")) - Config.setShape(false); - else if (split[1].equalsIgnoreCase("elliptic") || split[1].equalsIgnoreCase("round")) - Config.setShape(true); - else - { - sender.sendMessage("You must specify a shape of \"elliptic\"/\"round\" or \"rectangular\"/\"square\"."); - return true; - } - - if (player != null) - sender.sendMessage("Default border shape for all worlds is now set to \"" + Config.ShapeName() + "\"."); - } - - // "getmsg" command from player or console - else if (split.length == 1 && split[0].equalsIgnoreCase("getmsg")) - { - if (!Config.HasPermission(player, "getmsg")) return true; - - sender.sendMessage("Border message is currently set to:"); - sender.sendMessage(Config.MessageRaw()); - sender.sendMessage("Formatted border message:"); - sender.sendMessage(Config.Message()); - } - - // "setmsg" command from player or console - else if (split.length >= 2 && split[0].equalsIgnoreCase("setmsg")) - { - if (!Config.HasPermission(player, "setmsg")) return true; - - String message = ""; - for(int i = 1; i < split.length; i++) - { - if (i != 1) - message += ' '; - message += split[i]; - } - - Config.setMessage(message); - - if (player != null) - { - sender.sendMessage("Border message is now set to:"); - sender.sendMessage(Config.MessageRaw()); - sender.sendMessage("Formatted border message:"); - sender.sendMessage(Config.Message()); - } - } - - // "reload" command from player or console - else if (split.length == 1 && split[0].equalsIgnoreCase("reload")) - { - if (!Config.HasPermission(player, "reload")) return true; - - if (player != null) - Config.log("Reloading config file at the command of player \"" + player.getName() + "\"."); - - Config.load(plugin, true); - - if (player != null) - sender.sendMessage("WorldBorder configuration reloaded."); - } - - // "debug" command from player or console - else if (split.length == 2 && split[0].equalsIgnoreCase("debug")) - { - if (!Config.HasPermission(player, "debug")) return true; - - Config.setDebug(strAsBool(split[1])); - - if (player != null) - Config.log((Config.Debug() ? "Enabling" : "Disabling") + " debug output at the command of player \"" + player.getName() + "\"."); - - if (player != null) - sender.sendMessage("Debug mode " + enabledColored(Config.Debug()) + "."); - } - - // "whoosh" command from player or console - else if (split.length == 2 && split[0].equalsIgnoreCase("whoosh")) - { - if (!Config.HasPermission(player, "whoosh")) return true; - - Config.setWhooshEffect(strAsBool(split[1])); - - if (player != null) - { - Config.log((Config.whooshEffect() ? "Enabling" : "Disabling") + " \"whoosh\" knockback effect at the command of player \"" + player.getName() + "\"."); - sender.sendMessage("\"Whoosh\" knockback effect " + enabledColored(Config.whooshEffect()) + "."); - } - } - - // "denypearl" command from player or console - else if (split.length == 2 && split[0].equalsIgnoreCase("denypearl")) - { - if (!Config.HasPermission(player, "denypearl")) return true; - - Config.setDenyEnderpearl(strAsBool(split[1])); - - if (player != null) - { - Config.log((Config.getDenyEnderpearl() ? "Enabling" : "Disabling") + " direct cancellation of ender pearls thrown past the border at the command of player \"" + player.getName() + "\"."); - sender.sendMessage("Direct cancellation of ender pearls thrown past the border " + enabledColored(Config.getDenyEnderpearl()) + "."); - } - } - - // "knockback" command from player or console - else if (split.length == 2 && split[0].equalsIgnoreCase("knockback")) - { - if (!Config.HasPermission(player, "knockback")) return true; - - double numBlocks = 0.0; - try - { - numBlocks = Double.parseDouble(split[1]); - } - catch(NumberFormatException ex) - { - sender.sendMessage(clrErr + "The knockback must be a decimal value of at least 1.0, or it can be 0."); - return true; - } - - if (numBlocks < 0.0 || (numBlocks > 0.0 && numBlocks < 1.0)) - { - sender.sendMessage(clrErr + "The knockback must be a decimal value of at least 1.0, or it can be 0."); - return true; - } - - Config.setKnockBack(numBlocks); - - if (player != null) - sender.sendMessage("Knockback set to " + numBlocks + " blocks inside the border."); - } - - // "delay" command from player or console - else if (split.length == 2 && split[0].equalsIgnoreCase("delay")) - { - if (!Config.HasPermission(player, "delay")) return true; - - int delay = 0; - try - { - delay = Integer.parseInt(split[1]); - } - catch(NumberFormatException ex) - { - sender.sendMessage(clrErr + "The timer delay must be an integer of 1 or higher."); - return true; - } - if (delay < 1) - { - sender.sendMessage(clrErr + "The timer delay must be an integer of 1 or higher."); - return true; - } - - Config.setTimerTicks(delay); - - if (player != null) - sender.sendMessage("Timer delay set to " + delay + " tick(s). That is roughly " + (delay * 50) + "ms."); - } - - // "wshape" command from player or console, world specified - else if (split.length == 3 && split[0].equalsIgnoreCase("wshape")) - { - if (!Config.HasPermission(player, "wshape")) return true; - - String world = split[1]; - BorderData border = Config.Border(world); - if (border == null) - { - sender.sendMessage("The world you specified (\"" + world + "\") does not have a border set."); - return true; - } - - Boolean shape = null; - if (split[2].equalsIgnoreCase("rectangular") || split[2].equalsIgnoreCase("square")) - shape = false; - else if (split[2].equalsIgnoreCase("elliptic") || split[2].equalsIgnoreCase("round")) - shape = true; - - border.setShape(shape); - Config.setBorder(world, border); - - if (player != null) - sender.sendMessage("Border shape for world \"" + world + "\" is now set to \"" + (shape == null ? "default" : Config.ShapeName(shape.booleanValue())) + "\"."); - } - - // "wshape" command from player, using current world - else if (split.length == 2 && split[0].equalsIgnoreCase("wshape") && player != null) - { - if (!Config.HasPermission(player, "wshape")) return true; - - String world = player.getWorld().getName(); - BorderData border = Config.Border(world); - if (border == null) - { - sender.sendMessage("This world (\"" + world + "\") does not have a border set."); - return true; - } - - Boolean shape = null; - if (split[1].equalsIgnoreCase("rectangular") || split[1].equalsIgnoreCase("square")) - shape = false; - else if (split[1].equalsIgnoreCase("elliptic") || split[1].equalsIgnoreCase("round")) - shape = true; - - border.setShape(shape); - Config.setBorder(world, border); - - sender.sendMessage("Border shape for world \"" + world + "\" is now set to \"" + (shape == null ? "default" : Config.ShapeName(shape.booleanValue())) + "\"."); - } - - // "wrap" command from player or console, world specified - else if (split.length == 3 && split[0].equalsIgnoreCase("wrap")) - { - if (!Config.HasPermission(player, "wrap")) return true; - - String world = split[1]; - BorderData border = Config.Border(world); - if (border == null) - { - sender.sendMessage("The world you specified (\"" + world + "\") does not have a border set."); - return true; - } - - boolean wrap = strAsBool(split[2]); - border.setWrapping(wrap); - Config.setBorder(world, border); - - if (player != null) - sender.sendMessage("Border for world \"" + world + "\" is now set to " + (wrap ? "" : "not ") + "wrap around."); - } - - // "wrap" command from player, using current world - else if (split.length == 2 && split[0].equalsIgnoreCase("wrap") && player != null) - { - if (!Config.HasPermission(player, "wrap")) return true; - - String world = player.getWorld().getName(); - BorderData border = Config.Border(world); - if (border == null) - { - sender.sendMessage("This world (\"" + world + "\") does not have a border set."); - return true; - } - - boolean wrap = strAsBool(split[1]); - border.setWrapping(wrap); - Config.setBorder(world, border); - - sender.sendMessage("Border for world \"" + world + "\" is now set to " + (wrap ? "" : "not ") + "wrap around."); - } - - // "portal" command from player or console - else if (split.length == 2 && split[0].equalsIgnoreCase("portal")) - { - if (!Config.HasPermission(player, "portal")) return true; - - Config.setPortalRedirection(strAsBool(split[1])); - - if (player != null) - { - Config.log((Config.portalRedirection() ? "Enabling" : "Disabling") + " portal redirection at the command of player \"" + player.getName() + "\"."); - sender.sendMessage("Portal redirection " + enabledColored(Config.portalRedirection()) + "."); - } - } - - // "fill" command from player or console, world specified - else if (split.length >= 2 && split[1].equalsIgnoreCase("fill")) - { - if (!Config.HasPermission(player, "fill")) return true; - - boolean cancel = false, confirm = false, pause = false; - String frequency = ""; - if (split.length >= 3) - { - cancel = split[2].equalsIgnoreCase("cancel") || split[2].equalsIgnoreCase("stop"); - confirm = split[2].equalsIgnoreCase("confirm"); - pause = split[2].equalsIgnoreCase("pause"); - if (!cancel && !confirm && !pause) - frequency = split[2]; - } - String pad = (split.length >= 4) ? split[3] : ""; - String forceLoad = (split.length >= 5) ? split[4] : ""; - - String world = split[0]; - - cmdFill(sender, player, world, confirm, cancel, pause, pad, frequency, forceLoad); - } - - // "fill" command from player (or from console solely if using cancel or confirm), using current world - else if (split.length >= 1 && split[0].equalsIgnoreCase("fill")) - { - if (!Config.HasPermission(player, "fill")) return true; - - boolean cancel = false, confirm = false, pause = false; - String frequency = ""; - if (split.length >= 2) - { - cancel = split[1].equalsIgnoreCase("cancel") || split[1].equalsIgnoreCase("stop"); - confirm = split[1].equalsIgnoreCase("confirm"); - pause = split[1].equalsIgnoreCase("pause"); - if (!cancel && !confirm && !pause) - frequency = split[1]; - } - String pad = (split.length >= 3) ? split[2] : ""; - String forceLoad = (split.length >= 4) ? split[3] : ""; - - String world = ""; - if (player != null && !cancel && !confirm && !pause) - world = player.getWorld().getName(); - - if (!cancel && !confirm && !pause && world.isEmpty()) - { - sender.sendMessage("You must specify a world! Example: " + cmdW + " fill " + clrOpt + "[freq] [pad] [force]"); - return true; - } - - cmdFill(sender, player, world, confirm, cancel, pause, pad, frequency, forceLoad); - } - - // "trim" command from player or console, world specified - else if (split.length >= 2 && split[1].equalsIgnoreCase("trim")) - { - if (!Config.HasPermission(player, "trim")) return true; - - boolean cancel = false, confirm = false, pause = false; - String pad = "", frequency = ""; - if (split.length >= 3) - { - cancel = split[2].equalsIgnoreCase("cancel") || split[2].equalsIgnoreCase("stop"); - confirm = split[2].equalsIgnoreCase("confirm"); - pause = split[2].equalsIgnoreCase("pause"); - if (!cancel && !confirm && !pause) - frequency = split[2]; - } - if (split.length >= 4) - pad = split[3]; - - String world = split[0]; - - cmdTrim(sender, player, world, confirm, cancel, pause, pad, frequency); - } - - // "trim" command from player (or from console solely if using cancel or confirm), using current world - else if (split.length >= 1 && split[0].equalsIgnoreCase("trim")) - { - if (!Config.HasPermission(player, "trim")) return true; - - boolean cancel = false, confirm = false, pause = false; - String pad = "", frequency = ""; - if (split.length >= 2) - { - cancel = split[1].equalsIgnoreCase("cancel") || split[1].equalsIgnoreCase("stop"); - confirm = split[1].equalsIgnoreCase("confirm"); - pause = split[1].equalsIgnoreCase("pause"); - if (!cancel && !confirm && !pause) - frequency = split[1]; - } - if (split.length >= 3) - pad = split[2]; - - String world = ""; - if (player != null && !cancel && !confirm && !pause) - world = player.getWorld().getName(); - - if (!cancel && !confirm && !pause && world.isEmpty()) - { - sender.sendMessage("You must specify a world! Example: " + cmdW+" trim " + clrOpt + "[freq] [pad]"); - return true; - } - - cmdTrim(sender, player, world, confirm, cancel, pause, pad, frequency); - } - - // "remount" command from player or console - else if (split.length == 2 && split[0].equalsIgnoreCase("remount")) - { - if (!Config.HasPermission(player, "remount")) return true; - - int delay = 0; - try - { - delay = Integer.parseInt(split[1]); - if (delay < 0) - throw new NumberFormatException(); - } - catch(NumberFormatException ex) - { - sender.sendMessage(clrErr + "The remount delay must be an integer of 0 or higher. Setting to 0 will disable remounting."); - return true; - } - - Config.setRemountTicks(delay); - - if (player != null) - { - if (delay == 0) - sender.sendMessage("Remount delay set to 0. Players will be left dismounted when knocked back from the border while on a vehicle."); - else - { - sender.sendMessage("Remount delay set to " + delay + " tick(s). That is roughly " + (delay * 50) + "ms / " + (((double)delay * 50.0) / 1000.0) + " seconds. Setting to 0 would disable remounting."); - if (delay < 10) - sender.sendMessage(clrErr + "WARNING:" + clrDesc + " setting this to less than 10 (and greater than 0) is not recommended. This can lead to nasty client glitches."); - } - } - } - - // "fillautosave" command from player or console - else if (split.length == 2 && split[0].equalsIgnoreCase("fillautosave")) - { - if (!Config.HasPermission(player, "fillautosave")) return true; - - int seconds = 0; - try - { - seconds = Integer.parseInt(split[1]); - if (seconds < 0) - throw new NumberFormatException(); - } - catch(NumberFormatException ex) - { - sender.sendMessage(clrErr + "The world autosave frequency must be an integer of 0 or higher. Setting to 0 will disable autosaving of the world during the Fill process."); - return true; - } - - Config.setFillAutosaveFrequency(seconds); - - if (player != null) - { - if (seconds == 0) - { - sender.sendMessage("World autosave frequency during Fill process set to 0, disabling it."); - sender.sendMessage("Note that much progress can be lost this way if there is a bug or crash in the world generation process from Bukkit or any world generation plugin you use."); - } - else - { - sender.sendMessage("World autosave frequency during Fill process set to " + seconds + " seconds (rounded to a multiple of 5)."); - sender.sendMessage("New chunks generated by the Fill process will be forcibly saved to disk this often to prevent loss of progress due to bugs or crashes in the world generation process."); - } - } - } - - // "dynmap" command from player or console - else if (split.length == 2 && split[0].equalsIgnoreCase("dynmap")) - { - if (!Config.HasPermission(player, "dynmap")) return true; - - Config.setDynmapBorderEnabled(strAsBool(split[1])); - - sender.sendMessage("DynMap border display " + (Config.DynmapBorderEnabled() ? "enabled" : "disabled") + "."); - - if (player != null) - Config.log((Config.DynmapBorderEnabled() ? "Enabled" : "Disabled") + " DynMap border display at the command of player \"" + player.getName() + "\"."); - } - - // "dynmapmsg" command from player or console - else if (split.length >= 2 && split[0].equalsIgnoreCase("dynmapmsg")) - { - if (!Config.HasPermission(player, "dynmapmsg")) return true; - - String message = ""; - for(int i = 1; i < split.length; i++) - { - if (i != 1) - message += ' '; - message += split[i]; - } - - Config.setDynmapMessage(message); - - if (player != null) - { - sender.sendMessage("DynMap border label is now set to:"); - sender.sendMessage(clrErr + Config.DynmapMessage()); - } - } - - // "bypass" command from player or console, player specified, on/off optionally specified - else if (split.length >= 2 && split[0].equalsIgnoreCase("bypass")) - { - if (!Config.HasPermission(player, "bypass")) return true; - - String sPlayer = split[1]; - - boolean bypassing = !Config.isPlayerBypassing(sPlayer); - if (split.length > 2) - bypassing = strAsBool(split[2]); - - Config.setPlayerBypass(sPlayer, bypassing); - - Player target = Bukkit.getPlayer(sPlayer); - if (target != null && target.isOnline()) - target.sendMessage("Border bypass is now " + enabledColored(bypassing) + "."); - - Config.log("Border bypass for player \"" + sPlayer + "\" is " + (bypassing ? "enabled" : "disabled") + (player != null ? " at the command of player \"" + player.getName() + "\"" : "") + "."); - if (player != null && player != target) - sender.sendMessage("Border bypass for player \"" + sPlayer + "\" is " + enabledColored(bypassing) + "."); - } - - // "bypass" command from player, using them for player - else if (split.length == 1 && split[0].equalsIgnoreCase("bypass") && player != null) - { - if (!Config.HasPermission(player, "bypass")) return true; - - String sPlayer = player.getName(); - - boolean bypassing = !Config.isPlayerBypassing(sPlayer); - Config.setPlayerBypass(sPlayer, bypassing); - - Config.log("Border bypass is " + (bypassing ? "enabled" : "disabled") + " for player \"" + sPlayer + "\"."); - sender.sendMessage("Border bypass is now " + enabledColored(bypassing) + "."); - } - - // "bypasslist" command from player or console - else if (split.length == 1 && split[0].equalsIgnoreCase("bypasslist")) - { - if (!Config.HasPermission(player, "bypasslist")) return true; - - sender.sendMessage("Players with border bypass enabled: " + Config.getPlayerBypassList()); - } - - // we couldn't decipher any known commands, so show help - else - { - if (!Config.HasPermission(player, "help")) return true; - int page = (player == null) ? 0 : 1; - if (split.length == 1) + try { - try - { - page = Integer.parseInt(split[0]); - } - catch(NumberFormatException ignored) - { - } - if (page > 4) - page = 1; + page = Integer.parseInt(cmdName); } - - sender.sendMessage(clrHead + plugin.getDescription().getFullName() + " - commands (" + clrReq + " " + clrOpt + "[optional]" + clrHead + ")" + (page > 0 ? " " + page + "/4" : "") + ":"); - - if (page == 0 || page == 1) + catch(NumberFormatException ignored) { - if (player != null) - sender.sendMessage(cmd+" set " + clrReq + " " + clrOpt + "[radiusZ]" + clrDesc + " - set border, centered on you."); - sender.sendMessage(cmdW+" set " + clrReq + " " + clrOpt + "[radiusZ] " + clrDesc + " - set border."); - sender.sendMessage(cmdW+" set " + clrReq + " " + clrOpt + "[radiusZ] spawn" + clrDesc + " - use spawn point."); - sender.sendMessage(cmd+" set " + clrReq + " " + clrOpt + "[radiusZ] player " + clrDesc + " - center on player."); - sender.sendMessage(cmdW+" setcorners " + clrReq + " " + clrDesc + " - set by corners."); - sender.sendMessage(cmdW+" radius " + clrReq + " " + clrOpt + "[radiusZ]" + clrDesc + " - change radius."); - sender.sendMessage(cmd+" shape " + clrReq + "" + clrDesc + " - set the default shape."); - sender.sendMessage(cmd+" shape " + clrReq + "" + clrDesc + " - same as above."); - if (page == 1) - sender.sendMessage(cmd+" 2" + clrDesc + " - view second page of commands."); - } - if (page == 0 || page == 2) - { - sender.sendMessage(cmdW+" clear" + clrDesc + " - remove border for this world."); - sender.sendMessage(cmd+" clear all" + clrDesc + " - remove border for all worlds."); - sender.sendMessage(cmd+" list" + clrDesc + " - show border information for all worlds."); - sender.sendMessage(cmdW+" fill " + clrOpt + "[freq] [pad] [force]" + clrDesc + " - fill world to border."); - sender.sendMessage(cmdW+" trim " + clrOpt + "[freq] [pad]" + clrDesc + " - trim world outside of border."); - sender.sendMessage(cmd+" bypass " + ((player == null) ? clrReq + "" : clrOpt + "[player]") + clrOpt + " [on/off]" + clrDesc + " - let player go beyond border."); - sender.sendMessage(cmd+" knockback " + clrReq + "" + clrDesc + " - how far to move the player back."); - sender.sendMessage(cmd+" wrap " + ((player == null) ? clrReq + "" : clrOpt + "[world]") + clrReq + " " + clrDesc + " - can make border crossings wrap."); - if (page == 2) - sender.sendMessage(cmd+" 3" + clrDesc + " - view third page of commands."); - } - if (page == 0 || page == 3) - { - sender.sendMessage(cmd+" whoosh " + clrReq + "" + clrDesc + " - turn knockback effect on or off."); - sender.sendMessage(cmd+" getmsg" + clrDesc + " - display border message."); - sender.sendMessage(cmd+" setmsg " + clrReq + "" + clrDesc + " - set border message."); - sender.sendMessage(cmd+" delay " + clrReq + "" + clrDesc + " - time between border checks."); - sender.sendMessage(cmd+" remount " + clrReq + "" + clrDesc + " - player remount delay after knockback."); - sender.sendMessage(cmd+" wshape " + ((player == null) ? clrReq + "" : clrOpt + "[world]") + clrReq + " " + clrDesc + " - shape override for this world."); - // above command takes 2 lines, so only 7 commands total listed for this page - sender.sendMessage(cmd+" wshape " + ((player == null) ? clrReq + "" : clrOpt + "[world]") + clrReq + " " + clrDesc + " - same as above."); - if (page == 3) - sender.sendMessage(cmd+" 4" + clrDesc + " - view fourth page of commands."); - } - if (page == 0 || page == 4) - { - sender.sendMessage(cmd+" dynmap " + clrReq + "" + clrDesc + " - turn DynMap border display on or off."); - sender.sendMessage(cmd+" dynmapmsg " + clrReq + "" + clrDesc + " - DynMap border labels will show this."); - sender.sendMessage(cmd+" bypasslist " + clrDesc + " - list players with border bypass enabled."); - sender.sendMessage(cmd+" fillautosave " + clrReq + "" + clrDesc + " - world save interval for Fill."); - sender.sendMessage(cmd+" portal " + clrReq + "" + clrDesc + " - turn portal redirection on or off."); - sender.sendMessage(cmd+" denypearl " + clrReq + "" + clrDesc + " - stop ender pearls past the border."); - sender.sendMessage(cmd+" reload" + clrDesc + " - re-load data from config.yml."); - sender.sendMessage(cmd+" debug " + clrReq + "" + clrDesc + " - turn console debug output on or off."); - if (page == 4) - sender.sendMessage(cmd + clrDesc + " - view first page of commands."); + sender.sendMessage(WBCmd.clrErr + "Command not recognized. Showing command list."); } + cmdName = "commands"; + params.add(0, Integer.toString(page)); } + WBCmd subCommand = subCommands.get(cmdName); + + // check permission + if (!Config.HasPermission(player, subCommand.permission)) + return true; + + // if command requires world name when run by console, make sure that's in place + if (player == null && subCommand.hasWorldNameInput && subCommand.consoleRequiresWorldName && worldName == null) + { + sender.sendMessage(WBCmd.clrErr + "This command requires a world to be specified if run by the console."); + subCommand.sendCmdHelp(sender); + return true; + } + + // make sure valid number of parameters has been provided + if (params.size() < subCommand.minParams || params.size() > subCommand.maxParams) + { + sender.sendMessage(WBCmd.clrErr + "You have not provided a valid number of arguments."); + subCommand.sendCmdHelp(sender); + return true; + } + + // execute command + subCommand.execute(sender, player, params, worldName); + return true; } - private boolean strAsBool(String str) - { - str = str.toLowerCase(); - return str.startsWith("y") || str.startsWith("t") || str.startsWith("on") || str.startsWith("+") || str.startsWith("1"); - } + private boolean wasWorldQuotation = false; - private String enabledColored(boolean enabled) + // if world name is surrounded by quotation marks, combine it down and tag wasWorldQuotation if it's the first parameter + // also return List instead of input primitive String[] + private List concatenateQuotedWorldName(String[] split) { - return enabled ? clrReq+"enabled" : clrErr+"disabled"; - } + wasWorldQuotation = false; + List args = new ArrayList(Arrays.asList(split)); - private boolean cmdSet(CommandSender sender, World world, Player player, String[] data, int offset) - { - int radiusX, radiusZ; - double x, z; - int radiusCount = data.length - offset; - - try + int startIndex = -1; + for (int i = 0; i < args.size(); i++) { - if (data[data.length - 1].equalsIgnoreCase("spawn")) - { // "spawn" specified for x/z coordinates - Location loc = world.getSpawnLocation(); - x = loc.getX(); - z = loc.getZ(); - radiusCount -= 1; - } - else if (data[data.length - 2].equalsIgnoreCase("player")) - { // player name specified for x/z coordinates - Player playerT = Bukkit.getPlayer(data[data.length - 1]); - if (playerT == null || ! playerT.isOnline()) - { - sender.sendMessage(clrErr + "The player you specified (\"" + data[data.length - 1] + "\") does not appear to be online."); - return false; - } - world = playerT.getWorld(); - x = playerT.getLocation().getX(); - z = playerT.getLocation().getZ(); - radiusCount -= 2; - } - else + if (args.get(i).startsWith("\"")) { - if (player == null || radiusCount > 2) - { // x and z specified - x = Double.parseDouble(data[data.length - 2]); - z = Double.parseDouble(data[data.length - 1]); - radiusCount -= 2; - } - else - { // using coordinates of command sender (player) - x = player.getLocation().getX(); - z = player.getLocation().getZ(); - } + startIndex = i; + break; } - - radiusX = Integer.parseInt(data[offset]); - if (radiusCount < 2) - radiusZ = radiusX; - else - radiusZ = Integer.parseInt(data[offset+1]); } - catch(NumberFormatException ex) + if (startIndex == -1) + return args; + + if (args.get(startIndex).endsWith("\"")) { - sender.sendMessage(clrErr + "The radius value(s) must be integers and the x and z values must be numerical."); - return false; - } - - Config.setBorder(world.getName(), radiusX, radiusZ, x, z); - return true; - } - - - private String fillWorld = ""; - private int fillFrequency = 20; - private int fillPadding = CoordXZ.chunkToBlock(13); - private boolean fillForceLoad = false; - - private void fillDefaults() - { - fillWorld = ""; - fillFrequency = 20; - // with "view-distance=10" in server.properties and "Render Distance: Far" in client, hitting border during testing - // was loading 11 chunks beyond the border in a couple of directions (10 chunks in the other two directions); thus: - fillPadding = CoordXZ.chunkToBlock(13); - fillForceLoad = false; - } - - private boolean cmdFill(CommandSender sender, Player player, String world, boolean confirm, boolean cancel, boolean pause, String pad, String frequency, String forceLoad) - { - if (cancel) - { - sender.sendMessage(clrHead + "Cancelling the world map generation task."); - fillDefaults(); - Config.StopFillTask(); - return true; - } - - if (pause) - { - if (Config.fillTask == null || !Config.fillTask.valid()) - { - sender.sendMessage(clrHead + "The world map generation task is not currently running."); - return true; - } - Config.fillTask.pause(); - sender.sendMessage(clrHead + "The world map generation task is now " + (Config.fillTask.isPaused() ? "" : "un") + "paused."); - return true; - } - - if (Config.fillTask != null && Config.fillTask.valid()) - { - sender.sendMessage(clrHead + "The world map generation task is already running."); - return true; - } - - // set padding and/or delay if those were specified - try - { - if (!pad.isEmpty()) - fillPadding = Math.abs(Integer.parseInt(pad)); - if (!frequency.isEmpty()) - fillFrequency = Math.abs(Integer.parseInt(frequency)); - } - catch(NumberFormatException ex) - { - sender.sendMessage(clrErr + "The frequency and padding values must be integers."); - fillDefaults(); - return false; - } - if (fillFrequency <= 0) - { - sender.sendMessage(clrErr + "The frequency value must be greater than zero."); - fillDefaults(); - return false; - } - - if (!forceLoad.isEmpty()) - fillForceLoad = strAsBool(forceLoad); - - // set world if it was specified - if (!world.isEmpty()) - fillWorld = world; - - if (confirm) - { // command confirmed, go ahead with it - if (fillWorld.isEmpty()) - { - sender.sendMessage(clrErr + "You must first use this command successfully without confirming."); - return false; - } - - if (player != null) - Config.log("Filling out world to border at the command of player \"" + player.getName() + "\"."); - - int ticks = 1, repeats = 1; - if (fillFrequency > 20) - repeats = fillFrequency / 20; - else - ticks = 20 / fillFrequency; - - Config.fillTask = new WorldFillTask(plugin.getServer(), player, fillWorld, fillPadding, repeats, ticks, fillForceLoad); - if (Config.fillTask.valid()) - { - int task = plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, Config.fillTask, ticks, ticks); - Config.fillTask.setTaskID(task); - sender.sendMessage("WorldBorder map generation for world \"" + fillWorld + "\" task started."); - } - else - sender.sendMessage(clrErr + "The world map generation task failed to start."); - - fillDefaults(); + args.set(startIndex, args.get(startIndex).substring(1, args.get(startIndex).length() - 1)); + if (startIndex == 0) + wasWorldQuotation = true; } else { - if (fillWorld.isEmpty()) + List concat = new ArrayList(args); + Iterator concatI = concat.iterator(); + + // skip past any parameters in front of the one we're starting on + for (int i = 1; i < startIndex + 1; i++) { - sender.sendMessage(clrErr + "You must first specify a valid world."); - return false; + concatI.next(); } - String cmd = clrCmd + ((player == null) ? "wb" : "/wb"); - sender.sendMessage(clrHead + "World generation task is ready for world \"" + fillWorld + "\", padding the map out to " + fillPadding + " blocks beyond the border (default " + CoordXZ.chunkToBlock(13) + "), and the task will try to generate up to " + fillFrequency + " chunks per second (default 20). Parts of the world which are already fully generated will be " + (fillForceLoad ? "loaded anyway." : "skipped.")); - sender.sendMessage(clrHead + "This process can take a very long time depending on the world's border size. Also, depending on the chunk processing rate, players will likely experience severe lag for the duration."); - sender.sendMessage(clrDesc + "You should now use " + cmd + " fill confirm" + clrDesc + " to start the process."); - sender.sendMessage(clrDesc + "You can cancel at any time with " + cmd + " fill cancel" + clrDesc + ", or pause/unpause with " + cmd + " fill pause" + clrDesc + "."); + StringBuilder quote = new StringBuilder(concatI.next()); + while (concatI.hasNext()) + { + String next = concatI.next(); + concatI.remove(); + quote.append(" "); + quote.append(next); + if (next.endsWith("\"")) + { + concat.set(startIndex, quote.substring(1, quote.length() - 1)); + args = concat; + if (startIndex == 0) + wasWorldQuotation = true; + break; + } + } } - return true; + return args; } - - private String trimWorld = ""; - private int trimFrequency = 5000; - private int trimPadding = CoordXZ.chunkToBlock(13); - - private void trimDefaults() - { - trimWorld = ""; - trimFrequency = 5000; - trimPadding = CoordXZ.chunkToBlock(13); - } - - private boolean cmdTrim(CommandSender sender, Player player, String world, boolean confirm, boolean cancel, boolean pause, String pad, String frequency) - { - if (cancel) - { - sender.sendMessage(clrHead + "Cancelling the world map trimming task."); - trimDefaults(); - Config.StopTrimTask(); - return true; - } - - if (pause) - { - if (Config.trimTask == null || !Config.trimTask.valid()) - { - sender.sendMessage(clrHead + "The world map trimming task is not currently running."); - return true; - } - Config.trimTask.pause(); - sender.sendMessage(clrHead + "The world map trimming task is now " + (Config.trimTask.isPaused() ? "" : "un") + "paused."); - return true; - } - - if (Config.trimTask != null && Config.trimTask.valid()) - { - sender.sendMessage(clrHead + "The world map trimming task is already running."); - return true; - } - - // set padding and/or delay if those were specified - try - { - if (!pad.isEmpty()) - trimPadding = Math.abs(Integer.parseInt(pad)); - if (!frequency.isEmpty()) - trimFrequency = Math.abs(Integer.parseInt(frequency)); - } - catch(NumberFormatException ex) - { - sender.sendMessage(clrErr + "The frequency and padding values must be integers."); - trimDefaults(); - return false; - } - if (trimFrequency <= 0) - { - sender.sendMessage(clrErr + "The frequency value must be greater than zero."); - trimDefaults(); - return false; - } - - // set world if it was specified - if (!world.isEmpty()) - trimWorld = world; - - if (confirm) - { // command confirmed, go ahead with it - if (trimWorld.isEmpty()) - { - sender.sendMessage(clrErr + "You must first use this command successfully without confirming."); - return false; - } - - if (player != null) - Config.log("Trimming world beyond border at the command of player \"" + player.getName() + "\"."); - - int ticks = 1, repeats = 1; - if (trimFrequency > 20) - repeats = trimFrequency / 20; - else - ticks = 20 / trimFrequency; - - Config.trimTask = new WorldTrimTask(plugin.getServer(), player, trimWorld, trimPadding, repeats); - if (Config.trimTask.valid()) - { - int task = plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, Config.trimTask, ticks, ticks); - Config.trimTask.setTaskID(task); - sender.sendMessage("WorldBorder map trimming task for world \"" + trimWorld + "\" started."); - } - else - sender.sendMessage(clrErr + "The world map trimming task failed to start."); - - trimDefaults(); - } - else - { - if (trimWorld.isEmpty()) - { - sender.sendMessage(clrErr + "You must first specify a valid world."); - return false; - } - - String cmd = clrCmd + ((player == null) ? "wb" : "/wb"); - sender.sendMessage(clrHead + "World trimming task is ready for world \"" + trimWorld + "\", trimming the map past " + trimPadding + " blocks beyond the border (default " + CoordXZ.chunkToBlock(13) + "), and the task will try to process up to " + trimFrequency + " chunks per second (default 5000)."); - sender.sendMessage(clrHead + "This process can take a while depending on the world's overall size. Also, depending on the chunk processing rate, players may experience lag for the duration."); - sender.sendMessage(clrDesc + "You should now use " + cmd + " trim confirm" + clrDesc + " to start the process."); - sender.sendMessage(clrDesc + "You can cancel at any time with " + cmd + " trim cancel" + clrDesc + ", or pause/unpause with " + cmd + " trim pause" + clrDesc + "."); - } - return true; - } } \ No newline at end of file diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdBypass.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdBypass.java new file mode 100644 index 0000000..8a007ad --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdBypass.java @@ -0,0 +1,48 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdBypass extends WBCmd +{ + public CmdBypass() + { + name = permission = "bypass"; + minParams = 0; + maxParams = 2; + + addCmdExample(nameEmphasized() + "{player} [on/off] - let player go beyond border."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + if (player == null && params.isEmpty()) + { + sendErrorAndHelp(sender, "When running this command from console, you must specify a player."); + return; + } + + String sPlayer = (params.isEmpty()) ? player.getName() : params.get(0); + + boolean bypassing = !Config.isPlayerBypassing(sPlayer); + if (params.size() > 1) + bypassing = strAsBool(params.get(1)); + + Config.setPlayerBypass(sPlayer, bypassing); + + Player target = Bukkit.getPlayer(sPlayer); + if (target != null && target.isOnline()) + target.sendMessage("Border bypass is now " + enabledColored(bypassing) + "."); + + Config.log("Border bypass for player \"" + sPlayer + "\" is " + (bypassing ? "enabled" : "disabled") + (player != null ? " at the command of player \"" + player.getName() + "\"" : "") + "."); + if (player != null && player != target) + sender.sendMessage("Border bypass for player \"" + sPlayer + "\" is " + enabledColored(bypassing) + "."); + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdBypasslist.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdBypasslist.java new file mode 100644 index 0000000..05d942d --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdBypasslist.java @@ -0,0 +1,26 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdBypasslist extends WBCmd +{ + public CmdBypasslist() + { + name = permission = "bypasslist"; + minParams = maxParams = 0; + + addCmdExample(nameEmphasized() + "- list players with border bypass enabled."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + sender.sendMessage("Players with border bypass enabled: " + Config.getPlayerBypassList()); + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdClear.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdClear.java new file mode 100644 index 0000000..5b7fb2a --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdClear.java @@ -0,0 +1,66 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdClear extends WBCmd +{ + public CmdClear() + { + name = permission = "clear"; + hasWorldNameInput = true; + consoleRequiresWorldName = false; + minParams = 0; + maxParams = 1; + + addCmdExample(nameEmphasizedW() + "- remove border for this world."); + addCmdExample(nameEmphasized() + "^all - remove border for all worlds."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + // handle "clear all" command separately + if (params.size() == 1 && params.get(0).equalsIgnoreCase("all")) + { + if (worldName != null) + { + sendErrorAndHelp(sender, "You should not specify a world with \"clear all\"."); + return; + } + + Config.removeAllBorders(); + + if (player != null) + sender.sendMessage("All borders have been cleared for all worlds."); + return; + } + + if (worldName == null) + { + if (player == null) + { + sendErrorAndHelp(sender, "You must specify a world name from console if not using \"clear all\"."); + return; + } + worldName = player.getWorld().getName(); + } + + BorderData border = Config.Border(worldName); + if (border == null) + { + sendErrorAndHelp(sender, "This world (\"" + worldName + "\") does not have a border set."); + return; + } + + Config.removeBorder(worldName); + + if (player != null) + sender.sendMessage("Border cleared for world \"" + worldName + "\"."); + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdCommands.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdCommands.java new file mode 100644 index 0000000..42431cf --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdCommands.java @@ -0,0 +1,74 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdCommands extends WBCmd +{ + private static int pageSize = 8; // examples to list per page; 10 lines available, 1 for header, 1 for footer + + public CmdCommands() + { + name = "commands"; + permission = "help"; + hasWorldNameInput = false; + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + // determine which page we're viewing + int page = (player == null) ? 0 : 1; + if (!params.isEmpty()) + { + try + { + page = Integer.parseInt(params.get(0)); + } + catch(NumberFormatException ignored) {} + } + + // see whether we're showing examples to player or to console, and determine number of pages available + List examples = (player == null) ? cmdExamplesConsole : cmdExamplesPlayer; + int pageCount = (int) Math.ceil(examples.size() / (double) pageSize); + + // if specified page number is negative or higher than we have available, default back to first page + if (page < 0 || page > pageCount) + page = (player == null) ? 0 : 1; + + // send command example header + sender.sendMessage( clrHead + WorldBorder.plugin.getDescription().getFullName() + " - key: " + + commandEmphasized("command") + clrReq + " " + clrOpt + "[optional]" ); + + if (page > 0) + { + // send examples for this page + int first = ((page - 1) * pageSize); + int count = Math.min(pageSize, examples.size() - first); + for(int i = first; i < first + count; i++) + { + sender.sendMessage(examples.get(i)); + } + + // send page footer, if relevant; manual spacing to get right side lined up near edge is crude, but sufficient + String footer = clrHead + " (Page " + page + "/" + pageCount + ") " + ((player == null) ? cmdC : cmdP); + if (page < pageCount) + sender.sendMessage(footer + Integer.toString(page + 1) + clrDesc + " - view next page of commands."); + else if (page > 1) + sender.sendMessage(footer + clrDesc + "- view first page of commands."); + } + else + { + // if page "0" is specified, send all examples; done by default for console but can be specified by player + for (String example : examples) + { + sender.sendMessage(example); + } + } + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdDebug.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdDebug.java new file mode 100644 index 0000000..8579258 --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdDebug.java @@ -0,0 +1,32 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdDebug extends WBCmd +{ + public CmdDebug() + { + name = permission = "debug"; + minParams = maxParams = 1; + + addCmdExample(nameEmphasized() + " - turn console debug output on or off."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + Config.setDebug(strAsBool(params.get(0))); + + if (player != null) + { + Config.log((Config.Debug() ? "Enabled" : "Disabled") + " debug output at the command of player \"" + player.getName() + "\"."); + sender.sendMessage("Debug mode " + enabledColored(Config.Debug()) + "."); + } + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdDelay.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdDelay.java new file mode 100644 index 0000000..23765f2 --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdDelay.java @@ -0,0 +1,42 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdDelay extends WBCmd +{ + public CmdDelay() + { + name = permission = "delay"; + minParams = maxParams = 1; + + addCmdExample(nameEmphasized() + " - time between border checks."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + int delay = 0; + try + { + delay = Integer.parseInt(params.get(0)); + if (delay < 1) + throw new NumberFormatException(); + } + catch(NumberFormatException ex) + { + sendErrorAndHelp(sender, "The timer delay must be an integer of 1 or higher."); + return; + } + + Config.setTimerTicks(delay); + + if (player != null) + sender.sendMessage("Timer delay set to " + delay + " tick(s). That is roughly " + (delay * 50) + "ms."); + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdDenypearl.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdDenypearl.java new file mode 100644 index 0000000..e6c50ea --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdDenypearl.java @@ -0,0 +1,32 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdDenypearl extends WBCmd +{ + public CmdDenypearl() + { + name = permission = "denypearl"; + minParams = maxParams = 1; + + addCmdExample(nameEmphasized() + " - stop ender pearls past the border."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + Config.setDenyEnderpearl(strAsBool(params.get(0))); + + if (player != null) + { + Config.log((Config.getDenyEnderpearl() ? "Enabled" : "Disabled") + " direct cancellation of ender pearls thrown past the border at the command of player \"" + player.getName() + "\"."); + sender.sendMessage("Direct cancellation of ender pearls thrown past the border " + enabledColored(Config.getDenyEnderpearl()) + "."); + } + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdDynmap.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdDynmap.java new file mode 100644 index 0000000..8e8d3dc --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdDynmap.java @@ -0,0 +1,32 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdDynmap extends WBCmd +{ + public CmdDynmap() + { + name = permission = "dynmap"; + minParams = maxParams = 1; + + addCmdExample(nameEmphasized() + " - turn DynMap border display on or off."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + Config.setDynmapBorderEnabled(strAsBool(params.get(0))); + + if (player != null) + { + sender.sendMessage("DynMap border display " + (Config.DynmapBorderEnabled() ? "enabled" : "disabled") + "."); + Config.log((Config.DynmapBorderEnabled() ? "Enabled" : "Disabled") + " DynMap border display at the command of player \"" + player.getName() + "\"."); + } + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdDynmapmsg.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdDynmapmsg.java new file mode 100644 index 0000000..691f8f8 --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdDynmapmsg.java @@ -0,0 +1,39 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdDynmapmsg extends WBCmd +{ + public CmdDynmapmsg() + { + name = permission = "dynmapmsg"; + minParams = 1; + + addCmdExample(nameEmphasized() + " - DynMap border labels will show this."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + StringBuilder message = new StringBuilder(); + boolean first = true; + for (String param : params) + { + if (!first) + message.append(" "); + message.append(param); + first = false; + } + + Config.setDynmapMessage(message.toString()); + + if (player != null) + sender.sendMessage("DynMap border label is now set to: " + clrErr + Config.DynmapMessage()); + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdFill.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdFill.java new file mode 100644 index 0000000..aa82d7f --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdFill.java @@ -0,0 +1,179 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdFill extends WBCmd +{ + public CmdFill() + { + name = permission = "fill"; + hasWorldNameInput = true; + consoleRequiresWorldName = false; + minParams = 0; + maxParams = 3; + + addCmdExample(nameEmphasizedW() + "[freq] [pad] [force] - fill world to border."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + boolean confirm = false; + // check for "cancel", "pause", or "confirm" + if (params.size() >= 1) + { + String check = params.get(0).toLowerCase(); + + if (check.equals("cancel") || check.equals("stop")) + { + if (!makeSureFillIsRunning(sender)) + return; + sender.sendMessage(clrHead + "Cancelling the world map generation task."); + fillDefaults(); + Config.StopFillTask(); + return; + } + else if (check.equals("pause")) + { + if (!makeSureFillIsRunning(sender)) + return; + Config.fillTask.pause(); + sender.sendMessage(clrHead + "The world map generation task is now " + (Config.fillTask.isPaused() ? "" : "un") + "paused."); + return; + } + + confirm = check.equals("confirm"); + } + + // if not just confirming, make sure a world name is available + if (worldName == null && !confirm) + { + if (player != null) + worldName = player.getWorld().getName(); + else + { + sendErrorAndHelp(sender, "You must specify a world!"); + return; + } + } + + // colorized "/wb fill " + String cmd = ((player == null) ? cmdC : cmdP) + nameEmphasized() + clrCmd; + + // make sure Fill isn't already running + if (Config.fillTask != null && Config.fillTask.valid()) + { + sender.sendMessage(clrErr + "The world map generation task is already running."); + sender.sendMessage(clrDesc + "You can cancel at any time with " + cmd + "cancel" + clrDesc + ", or pause/unpause with " + cmd + "pause" + clrDesc + "."); + return; + } + + // set frequency and/or padding if those were specified + try + { + if (params.size() >= 1 && !confirm) + fillFrequency = Math.abs(Integer.parseInt(params.get(0))); + if (params.size() >= 2 && !confirm) + fillPadding = Math.abs(Integer.parseInt(params.get(1))); + } + catch(NumberFormatException ex) + { + sendErrorAndHelp(sender, "The frequency and padding values must be integers."); + fillDefaults(); + return; + } + if (fillFrequency <= 0) + { + sendErrorAndHelp(sender, "The frequency value must be greater than zero."); + fillDefaults(); + return; + } + + // see if the command specifies to load even chunks which should already be fully generated + if (params.size() == 3) + fillForceLoad = strAsBool(params.get(2)); + + // set world if it was specified + if (worldName != null) + fillWorld = worldName; + + if (confirm) + { // command confirmed, go ahead with it + if (fillWorld.isEmpty()) + { + sendErrorAndHelp(sender, "You must first use this command successfully without confirming."); + return; + } + + if (player != null) + Config.log("Filling out world to border at the command of player \"" + player.getName() + "\"."); + + int ticks = 1, repeats = 1; + if (fillFrequency > 20) + repeats = fillFrequency / 20; + else + ticks = 20 / fillFrequency; + +/* */ Config.log("world: " + fillWorld + " padding: " + fillPadding + " repeats: " + repeats + " ticks: " + ticks); + Config.fillTask = new WorldFillTask(Bukkit.getServer(), player, fillWorld, fillPadding, repeats, ticks, fillForceLoad); + if (Config.fillTask.valid()) + { + int task = Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(WorldBorder.plugin, Config.fillTask, ticks, ticks); + Config.fillTask.setTaskID(task); + sender.sendMessage("WorldBorder map generation task for world \"" + fillWorld + "\" started."); + } + else + sender.sendMessage(clrErr + "The world map generation task failed to start."); + + fillDefaults(); + } + else + { + if (fillWorld.isEmpty()) + { + sendErrorAndHelp(sender, "You must first specify a valid world."); + return; + } + + sender.sendMessage(clrHead + "World generation task is ready for world \"" + fillWorld + "\", attempting to process up to " + fillFrequency + " chunks per second (default 20). The map will be padded out " + fillPadding + " blocks beyond the border (default " + defaultPadding + "). Parts of the world which are already fully generated will be " + (fillForceLoad ? "loaded anyway." : "skipped.")); + sender.sendMessage(clrHead + "This process can take a very long time depending on the world's border size. Also, depending on the chunk processing rate, players will likely experience severe lag for the duration."); + sender.sendMessage(clrDesc + "You should now use " + cmd + "confirm" + clrDesc + " to start the process."); + sender.sendMessage(clrDesc + "You can cancel at any time with " + cmd + "cancel" + clrDesc + ", or pause/unpause with " + cmd + "pause" + clrDesc + "."); + } + } + + + /* with "view-distance=10" in server.properties on a fast VM test server and "Render Distance: Far" in client, + * hitting border during testing was loading 11+ chunks beyond the border in a couple of directions (10 chunks in + * the other two directions). This could be worse on a more loaded or worse server, so: + */ + private final int defaultPadding = CoordXZ.chunkToBlock(13); + + private String fillWorld = ""; + private int fillFrequency = 20; + private int fillPadding = defaultPadding; + private boolean fillForceLoad = false; + + private void fillDefaults() + { + fillWorld = ""; + fillFrequency = 20; + fillPadding = defaultPadding; + fillForceLoad = false; + } + + private boolean makeSureFillIsRunning(CommandSender sender) + { + if (Config.fillTask != null && Config.fillTask.valid()) + return true; + sendErrorAndHelp(sender, "The world map generation task is not currently running."); + return false; + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdFillautosave.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdFillautosave.java new file mode 100644 index 0000000..6b0ce99 --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdFillautosave.java @@ -0,0 +1,53 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdFillautosave extends WBCmd +{ + public CmdFillautosave() + { + name = permission = "fillautosave"; + minParams = maxParams = 1; + + addCmdExample(nameEmphasized() + " - world save interval for Fill."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + int seconds = 0; + try + { + seconds = Integer.parseInt(params.get(0)); + if (seconds < 0) + throw new NumberFormatException(); + } + catch(NumberFormatException ex) + { + sendErrorAndHelp(sender, "The world autosave frequency must be an integer of 0 or higher. Setting to 0 will disable autosaving of the world during the Fill process."); + return; + } + + Config.setFillAutosaveFrequency(seconds); + + if (player != null) + { + if (seconds == 0) + { + sender.sendMessage("World autosave frequency during Fill process set to 0, disabling it."); + sender.sendMessage("Note that much progress can be lost this way if there is a bug or crash in the world generation process from Bukkit or any world generation plugin you use."); + } + else + { + sender.sendMessage("World autosave frequency during Fill process set to " + seconds + " seconds (rounded to a multiple of 5)."); + sender.sendMessage("New chunks generated by the Fill process will be forcibly saved to disk this often to prevent loss of progress due to bugs or crashes in the world generation process."); + } + } + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdGetmsg.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdGetmsg.java new file mode 100644 index 0000000..c8e9f81 --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdGetmsg.java @@ -0,0 +1,29 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdGetmsg extends WBCmd +{ + public CmdGetmsg() + { + name = permission = "getmsg"; + minParams = maxParams = 0; + + addCmdExample(nameEmphasized() + "- display border message."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + sender.sendMessage("Border message is currently set to:"); + sender.sendMessage(Config.MessageRaw()); + sender.sendMessage("Formatted border message:"); + sender.sendMessage(Config.Message()); + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdKnockback.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdKnockback.java new file mode 100644 index 0000000..e9e448f --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdKnockback.java @@ -0,0 +1,42 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdKnockback extends WBCmd +{ + public CmdKnockback() + { + name = permission = "knockback"; + minParams = maxParams = 1; + + addCmdExample(nameEmphasized() + " - how far to move the player back."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + double numBlocks = 0.0; + try + { + numBlocks = Double.parseDouble(params.get(0)); + if (numBlocks < 0.0 || (numBlocks > 0.0 && numBlocks < 1.0)) + throw new NumberFormatException(); + } + catch(NumberFormatException ex) + { + sendErrorAndHelp(sender, "The knockback must be a decimal value of at least 1.0, or it can be 0."); + return; + } + + Config.setKnockBack(numBlocks); + + if (player != null) + sender.sendMessage("Knockback set to " + numBlocks + " blocks inside the border."); + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdList.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdList.java new file mode 100644 index 0000000..a0d09a8 --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdList.java @@ -0,0 +1,40 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; +import java.util.Set; + +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdList extends WBCmd +{ + public CmdList() + { + name = permission = "list"; + minParams = maxParams = 0; + + addCmdExample(nameEmphasized() + "- show border information for all worlds."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + sender.sendMessage("Default border shape for all worlds is \"" + Config.ShapeName() + "\"."); + + Set list = Config.BorderDescriptions(); + + if (list.isEmpty()) + { + sender.sendMessage("There are no borders currently set."); + return; + } + + for(String borderDesc : list) + { + sender.sendMessage(borderDesc); + } + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdPortal.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdPortal.java new file mode 100644 index 0000000..1f5f409 --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdPortal.java @@ -0,0 +1,32 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdPortal extends WBCmd +{ + public CmdPortal() + { + name = permission = "portal"; + minParams = maxParams = 1; + + addCmdExample(nameEmphasized() + " - turn portal redirection on or off."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + Config.setPortalRedirection(strAsBool(params.get(0))); + + if (player != null) + { + Config.log((Config.portalRedirection() ? "Enabled" : "Disabled") + " portal redirection at the command of player \"" + player.getName() + "\"."); + sender.sendMessage("Portal redirection " + enabledColored(Config.portalRedirection()) + "."); + } + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdRadius.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdRadius.java new file mode 100644 index 0000000..73aca0c --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdRadius.java @@ -0,0 +1,59 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdRadius extends WBCmd +{ + public CmdRadius() + { + name = permission = "radius"; + hasWorldNameInput = true; + minParams = 1; + maxParams = 2; + + addCmdExample(nameEmphasizedW() + " [radiusZ] - change radius."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + if (worldName == null) + worldName = player.getWorld().getName(); + + BorderData border = Config.Border(worldName); + if (border == null) + { + sendErrorAndHelp(sender, "This world (\"" + worldName + "\") must first have a border set normally."); + return; + } + + double x = border.getX(); + double z = border.getZ(); + int radiusX; + int radiusZ; + try + { + radiusX = Integer.parseInt(params.get(0)); + if (params.size() == 2) + radiusZ = Integer.parseInt(params.get(1)); + else + radiusZ = radiusX; + } + catch(NumberFormatException ex) + { + sendErrorAndHelp(sender, "The radius value(s) must be integers."); + return; + } + + Config.setBorder(worldName, radiusX, radiusZ, x, z); + + if (player != null) + sender.sendMessage("Radius has been set. " + Config.BorderDescription(worldName)); + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdReload.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdReload.java new file mode 100644 index 0000000..396710b --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdReload.java @@ -0,0 +1,32 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdReload extends WBCmd +{ + public CmdReload() + { + name = permission = "reload"; + minParams = maxParams = 0; + + addCmdExample(nameEmphasized() + "- re-load data from config.yml."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + if (player != null) + Config.log("Reloading config file at the command of player \"" + player.getName() + "\"."); + + Config.load(WorldBorder.plugin, true); + + if (player != null) + sender.sendMessage("WorldBorder configuration reloaded."); + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdRemount.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdRemount.java new file mode 100644 index 0000000..a49dbe5 --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdRemount.java @@ -0,0 +1,51 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdRemount extends WBCmd +{ + public CmdRemount() + { + name = permission = "remount"; + minParams = maxParams = 1; + + addCmdExample(nameEmphasized() + " - player remount delay after knockback."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + int delay = 0; + try + { + delay = Integer.parseInt(params.get(0)); + if (delay < 0) + throw new NumberFormatException(); + } + catch(NumberFormatException ex) + { + sendErrorAndHelp(sender, "The remount delay must be an integer of 0 or higher. Setting to 0 will disable remounting."); + return; + } + + Config.setRemountTicks(delay); + + if (player != null) + { + if (delay == 0) + sender.sendMessage("Remount delay set to 0. Players will be left dismounted when knocked back from the border while on a vehicle."); + else + { + sender.sendMessage("Remount delay set to " + delay + " tick(s). That is roughly " + (delay * 50) + "ms / " + (((double)delay * 50.0) / 1000.0) + " seconds. Setting to 0 would disable remounting."); + if (delay < 10) + sender.sendMessage(clrErr + "WARNING:" + clrDesc + " setting this to less than 10 (and greater than 0) is not recommended. This can lead to nasty client glitches."); + } + } + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdSet.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdSet.java new file mode 100644 index 0000000..774a887 --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdSet.java @@ -0,0 +1,136 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +import org.bukkit.Location; +import org.bukkit.World; + +import com.wimbli.WorldBorder.*; + + +public class CmdSet extends WBCmd +{ + public CmdSet() + { + name = permission = "set"; + hasWorldNameInput = true; + consoleRequiresWorldName = false; + minParams = 1; + maxParams = 4; + + addCmdExample(nameEmphasized() + " [radiusZ] - set border, centered on you.", true, false, true); + addCmdExample(nameEmphasizedW() + " [radiusZ] - use x/z coords."); + addCmdExample(nameEmphasizedW() + " [radiusZ] ^spawn - use spawn point."); + addCmdExample(nameEmphasized() + " [radiusZ] ^player - center on player."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + // passsing a single parameter (radiusX) is only acceptable from player + if ((params.size() == 1) && player == null) + { + sendErrorAndHelp(sender, "You have not provided a sufficient number of arguments."); + return; + } + + // "set" command from player or console, world specified + if (worldName != null) + { + if (params.size() == 2 && ! params.get(params.size() - 1).equalsIgnoreCase("spawn")) + { // command can only be this short if "spawn" is specified rather than x + z or player name + sendErrorAndHelp(sender, "You have not provided a sufficient number of arguments."); + return; + } + + World world = sender.getServer().getWorld(worldName); + if (world == null) + { + if (params.get(params.size() - 1).equalsIgnoreCase("spawn")) + { + sendErrorAndHelp(sender, "The world you specified (\"" + worldName + "\") could not be found on the server, so the spawn point cannot be determined."); + return; + } + sender.sendMessage("The world you specified (\"" + worldName + "\") could not be found on the server, but data for it will be stored anyway."); + } + } + // "set" command from player using current world since it isn't specified, or allowed from console only if player name is specified + else + { + if (player == null) + { + if (! params.get(params.size() - 2).equalsIgnoreCase("player")) + { // command can only be called by console without world specified if player is specified instead + sendErrorAndHelp(sender, "You must specify a world name from console if not specifying a player name."); + return; + } + player = Bukkit.getPlayer(params.get(params.size() - 1)); + if (player == null || ! player.isOnline()) + { + sendErrorAndHelp(sender, "The player you specified (\"" + params.get(params.size() - 1) + "\") does not appear to be online."); + return; + } + } + worldName = player.getWorld().getName(); + } + + int radiusX, radiusZ; + double x, z; + int radiusCount = params.size(); + + try + { + if (params.get(params.size() - 1).equalsIgnoreCase("spawn")) + { // "spawn" specified for x/z coordinates + Location loc = sender.getServer().getWorld(worldName).getSpawnLocation(); + x = loc.getX(); + z = loc.getZ(); + radiusCount -= 1; + } + else if (params.get(params.size() - 2).equalsIgnoreCase("player")) + { // player name specified for x/z coordinates + Player playerT = Bukkit.getPlayer(params.get(params.size() - 1)); + if (playerT == null || ! playerT.isOnline()) + { + sendErrorAndHelp(sender, "The player you specified (\"" + params.get(params.size() - 1) + "\") does not appear to be online."); + return; + } + worldName = playerT.getWorld().getName(); + x = playerT.getLocation().getX(); + z = playerT.getLocation().getZ(); + radiusCount -= 2; + } + else + { + if (player == null || radiusCount > 2) + { // x and z specified + x = Double.parseDouble(params.get(params.size() - 2)); + z = Double.parseDouble(params.get(params.size() - 1)); + radiusCount -= 2; + } + else + { // using coordinates of command sender (player) + x = player.getLocation().getX(); + z = player.getLocation().getZ(); + } + } + + radiusX = Integer.parseInt(params.get(0)); + if (radiusCount < 2) + radiusZ = radiusX; + else + radiusZ = Integer.parseInt(params.get(1)); + } + catch(NumberFormatException ex) + { + sendErrorAndHelp(sender, "The radius value(s) must be integers and the x and z values must be numerical."); + return; + } + + Config.setBorder(worldName, radiusX, radiusZ, x, z); + sender.sendMessage("Border has been set. " + Config.BorderDescription(worldName)); + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdSetcorners.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdSetcorners.java new file mode 100644 index 0000000..6194bfa --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdSetcorners.java @@ -0,0 +1,55 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.command.*; +import org.bukkit.entity.Player; +import org.bukkit.World; + +import com.wimbli.WorldBorder.*; + + +public class CmdSetcorners extends WBCmd +{ + public CmdSetcorners() + { + name = "setcorners"; + permission = "set"; + hasWorldNameInput = true; + minParams = maxParams = 4; + + addCmdExample(nameEmphasizedW() + " - corner coords."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + if (worldName == null) + { + worldName = player.getWorld().getName(); + } + else + { + World worldTest = sender.getServer().getWorld(worldName); + if (worldTest == null) + sender.sendMessage("The world you specified (\"" + worldName + "\") could not be found on the server, but data for it will be stored anyway."); + } + + try + { + double x1 = Double.parseDouble(params.get(0)); + double z1 = Double.parseDouble(params.get(1)); + double x2 = Double.parseDouble(params.get(2)); + double z2 = Double.parseDouble(params.get(3)); + Config.setBorderCorners(worldName, x1, z1, x2, z2); + } + catch(NumberFormatException ex) + { + sendErrorAndHelp(sender, "The x1, z1, x2, and z2 coordinate values must be numerical."); + return; + } + + if(player != null) + sender.sendMessage("Border has been set. " + Config.BorderDescription(worldName)); + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdSetmsg.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdSetmsg.java new file mode 100644 index 0000000..3a18cb3 --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdSetmsg.java @@ -0,0 +1,41 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdSetmsg extends WBCmd +{ + public CmdSetmsg() + { + name = permission = "setmsg"; + minParams = 1; + + addCmdExample(nameEmphasized() + " - set border message."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + StringBuilder message = new StringBuilder(); + boolean first = true; + for (String param : params) + { + if (!first) + message.append(" "); + message.append(param); + first = false; + } + + Config.setMessage(message.toString()); + + sender.sendMessage("Border message is now set to:"); + sender.sendMessage(Config.MessageRaw()); + sender.sendMessage("Formatted border message:"); + sender.sendMessage(Config.Message()); + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdShape.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdShape.java new file mode 100644 index 0000000..78830fd --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdShape.java @@ -0,0 +1,39 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdShape extends WBCmd +{ + public CmdShape() + { + name = permission = "shape"; + minParams = maxParams = 1; + + addCmdExample(nameEmphasized() + " - set the default border shape."); + addCmdExample(nameEmphasized() + " - same as above."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + String shape = params.get(0).toLowerCase(); + if (shape.equals("rectangular") || shape.equals("square")) + Config.setShape(false); + else if (shape.equals("elliptic") || shape.equals("round")) + Config.setShape(true); + else + { + sendErrorAndHelp(sender, "You must specify one of the 4 valid shape names as indicated below."); + return; + } + + if (player != null) + sender.sendMessage("Default border shape for all worlds is now set to \"" + Config.ShapeName() + "\"."); + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdTrim.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdTrim.java new file mode 100644 index 0000000..78361da --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdTrim.java @@ -0,0 +1,172 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdTrim extends WBCmd +{ + public CmdTrim() + { + name = permission = "trim"; + hasWorldNameInput = true; + consoleRequiresWorldName = false; + minParams = 0; + maxParams = 2; + + addCmdExample(nameEmphasizedW() + "[freq] [pad] - trim world outside of border."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + boolean confirm = false; + // check for "cancel", "pause", or "confirm" + if (params.size() >= 1) + { + String check = params.get(0).toLowerCase(); + + if (check.equals("cancel") || check.equals("stop")) + { + if (!makeSureTrimIsRunning(sender)) + return; + sender.sendMessage(clrHead + "Cancelling the world map trimming task."); + trimDefaults(); + Config.StopTrimTask(); + return; + } + else if (check.equals("pause")) + { + if (!makeSureTrimIsRunning(sender)) + return; + Config.trimTask.pause(); + sender.sendMessage(clrHead + "The world map trimming task is now " + (Config.trimTask.isPaused() ? "" : "un") + "paused."); + return; + } + + confirm = check.equals("confirm"); + } + + // if not just confirming, make sure a world name is available + if (worldName == null && !confirm) + { + if (player != null) + worldName = player.getWorld().getName(); + else + { + sendErrorAndHelp(sender, "You must specify a world!"); + return; + } + } + + // colorized "/wb trim " + String cmd = ((player == null) ? cmdC : cmdP) + nameEmphasized() + clrCmd; + + // make sure Trim isn't already running + if (Config.trimTask != null && Config.trimTask.valid()) + { + sender.sendMessage(clrErr + "The world map trimming task is already running."); + sender.sendMessage(clrDesc + "You can cancel at any time with " + cmd + "cancel" + clrDesc + ", or pause/unpause with " + cmd + "pause" + clrDesc + "."); + return; + } + + // set frequency and/or padding if those were specified + try + { + if (params.size() >= 1 && !confirm) + trimFrequency = Math.abs(Integer.parseInt(params.get(0))); + if (params.size() >= 2 && !confirm) + trimPadding = Math.abs(Integer.parseInt(params.get(1))); + } + catch(NumberFormatException ex) + { + sendErrorAndHelp(sender, "The frequency and padding values must be integers."); + trimDefaults(); + return; + } + if (trimFrequency <= 0) + { + sendErrorAndHelp(sender, "The frequency value must be greater than zero."); + trimDefaults(); + return; + } + + // set world if it was specified + if (worldName != null) + trimWorld = worldName; + + if (confirm) + { // command confirmed, go ahead with it + if (trimWorld.isEmpty()) + { + sendErrorAndHelp(sender, "You must first use this command successfully without confirming."); + return; + } + + if (player != null) + Config.log("Trimming world beyond border at the command of player \"" + player.getName() + "\"."); + + int ticks = 1, repeats = 1; + if (trimFrequency > 20) + repeats = trimFrequency / 20; + else + ticks = 20 / trimFrequency; + + Config.trimTask = new WorldTrimTask(Bukkit.getServer(), player, trimWorld, trimPadding, repeats); + if (Config.trimTask.valid()) + { + int task = Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(WorldBorder.plugin, Config.trimTask, ticks, ticks); + Config.trimTask.setTaskID(task); + sender.sendMessage("WorldBorder map trimming task for world \"" + trimWorld + "\" started."); + } + else + sender.sendMessage(clrErr + "The world map trimming task failed to start."); + + trimDefaults(); + } + else + { + if (trimWorld.isEmpty()) + { + sendErrorAndHelp(sender, "You must first specify a valid world."); + return; + } + + sender.sendMessage(clrHead + "World trimming task is ready for world \"" + trimWorld + "\", attempting to process up to " + trimFrequency + " chunks per second (default 20). The map will be trimmed past " + trimPadding + " blocks beyond the border (default " + defaultPadding + ")."); + sender.sendMessage(clrHead + "This process can take a very long time depending on the world's overall size. Also, depending on the chunk processing rate, players may experience lag for the duration."); + sender.sendMessage(clrDesc + "You should now use " + cmd + "confirm" + clrDesc + " to start the process."); + sender.sendMessage(clrDesc + "You can cancel at any time with " + cmd + "cancel" + clrDesc + ", or pause/unpause with " + cmd + "pause" + clrDesc + "."); + } + } + + + /* with "view-distance=10" in server.properties on a fast VM test server and "Render Distance: Far" in client, + * hitting border during testing was loading 11+ chunks beyond the border in a couple of directions (10 chunks in + * the other two directions). This could be worse on a more loaded or worse server, so: + */ + private final int defaultPadding = CoordXZ.chunkToBlock(13); + + private String trimWorld = ""; + private int trimFrequency = 5000; + private int trimPadding = defaultPadding; + + private void trimDefaults() + { + trimWorld = ""; + trimFrequency = 5000; + trimPadding = defaultPadding; + } + + private boolean makeSureTrimIsRunning(CommandSender sender) + { + if (Config.trimTask != null && Config.trimTask.valid()) + return true; + sendErrorAndHelp(sender, "The world map trimming task is not currently running."); + return false; + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdWhoosh.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdWhoosh.java new file mode 100644 index 0000000..1a60a34 --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdWhoosh.java @@ -0,0 +1,32 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdWhoosh extends WBCmd +{ + public CmdWhoosh() + { + name = permission = "whoosh"; + minParams = maxParams = 1; + + addCmdExample(nameEmphasized() + " - turn knockback effect on or off."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + Config.setWhooshEffect(strAsBool(params.get(0))); + + if (player != null) + { + Config.log((Config.whooshEffect() ? "Enabled" : "Disabled") + " \"whoosh\" knockback effect at the command of player \"" + player.getName() + "\"."); + sender.sendMessage("\"Whoosh\" knockback effect " + enabledColored(Config.whooshEffect()) + "."); + } + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdWrap.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdWrap.java new file mode 100644 index 0000000..5aed90e --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdWrap.java @@ -0,0 +1,58 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdWrap extends WBCmd +{ + public CmdWrap() + { + name = permission = "wrap"; + minParams = 1; + maxParams = 2; + + addCmdExample(nameEmphasized() + "{world} - can make border crossings wrap."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + if (player == null && params.size() == 1) + { + sendErrorAndHelp(sender, "When running this command from console, you must specify a world."); + return; + } + + boolean wrap = false; + + // world and wrap on/off specified + if (params.size() == 2) + { + worldName = params.get(0); + wrap = strAsBool(params.get(1)); + } + // no world specified, just wrap on/off + else + { + worldName = player.getWorld().getName(); + wrap = strAsBool(params.get(0)); + } + + BorderData border = Config.Border(worldName); + if (border == null) + { + sendErrorAndHelp(sender, "This world (\"" + worldName + "\") does not have a border set."); + return; + } + + border.setWrapping(wrap); + Config.setBorder(worldName, border, false); + + sender.sendMessage("Border for world \"" + worldName + "\" is now set to " + (wrap ? "" : "not ") + "wrap around."); + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/CmdWshape.java b/src/main/java/com/wimbli/WorldBorder/cmd/CmdWshape.java new file mode 100644 index 0000000..7fd5c73 --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/CmdWshape.java @@ -0,0 +1,66 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.List; + +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +import com.wimbli.WorldBorder.*; + + +public class CmdWshape extends WBCmd +{ + public CmdWshape() + { + name = permission = "wshape"; + minParams = 1; + maxParams = 2; + + addCmdExample(nameEmphasized() + "{world} - shape"); + addCmdExample(clrDesc + " override for a single world.", true, true, false); + addCmdExample(nameEmphasized() + "{world} - same as above."); + } + + @Override + public void execute(CommandSender sender, Player player, List params, String worldName) + { + if (player == null && params.size() == 1) + { + sendErrorAndHelp(sender, "When running this command from console, you must specify a world."); + return; + } + + String shapeName = ""; + + // world and shape specified + if (params.size() == 2) + { + worldName = params.get(0); + shapeName = params.get(1).toLowerCase(); + } + // no world specified, just shape + else + { + worldName = player.getWorld().getName(); + shapeName = params.get(0).toLowerCase(); + } + + BorderData border = Config.Border(worldName); + if (border == null) + { + sendErrorAndHelp(sender, "This world (\"" + worldName + "\") does not have a border set."); + return; + } + + Boolean shape = null; + if (shapeName.equals("rectangular") || shapeName.equals("square")) + shape = false; + else if (shapeName.equals("elliptic") || shapeName.equals("round")) + shape = true; + + border.setShape(shape); + Config.setBorder(worldName, border, false); + + sender.sendMessage("Border shape for world \"" + worldName + "\" is now set to \"" + Config.ShapeName(shape) + "\"."); + } +} diff --git a/src/main/java/com/wimbli/WorldBorder/cmd/WBCmd.java b/src/main/java/com/wimbli/WorldBorder/cmd/WBCmd.java new file mode 100644 index 0000000..284eea1 --- /dev/null +++ b/src/main/java/com/wimbli/WorldBorder/cmd/WBCmd.java @@ -0,0 +1,130 @@ +package com.wimbli.WorldBorder.cmd; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.ChatColor; +import org.bukkit.command.*; +import org.bukkit.entity.Player; + + +public abstract class WBCmd +{ + /* + * Primary variables, should be set as needed in constructors for the subclassed commands + */ + + // command name, command permission; normally the same thing + public String name = ""; + public String permission = null; + + // whether command can accept a world name before itself + public boolean hasWorldNameInput = false; + public boolean consoleRequiresWorldName = true; + + // minimum and maximum number of accepted parameters + public int minParams = 0; + public int maxParams = 9999; + + /* + * The guts of the command run in here; needs to be overriden in the subclassed commands + */ + public abstract void execute(CommandSender sender, Player player, List params, String worldName); + + + /* + * Helper variables and methods + */ + + // color values for strings + public final static String clrCmd = ChatColor.AQUA.toString(); // main commands + public final static String clrDesc = ChatColor.WHITE.toString(); // command descriptions + public final static String clrErr = ChatColor.RED.toString(); // errors / notices + public final static String clrHead = ChatColor.YELLOW.toString(); // command listing header + public final static String clrOpt = ChatColor.DARK_GREEN.toString(); // optional values + public final static String clrReq = ChatColor.GREEN.toString(); // required values + + // colorized root command, for console and for player + public final static String cmdC = clrCmd + "wb "; + public final static String cmdP = clrCmd + "/wb "; + + // list of command examples for this command to be displayed as usage reference, separate between players and console + // ... these generally should be set indirectly using addCmdExample() within the constructor for each command class + public List cmdExamplePlayer = new ArrayList(); + public List cmdExampleConsole = new ArrayList(); + + // much like the above, but used for displaying command list from root /wb command, listing all commands + public final static List cmdExamplesConsole = new ArrayList(48); // 48 command capacity, 6 full pages + public final static List cmdExamplesPlayer = new ArrayList(48); // still, could need to increase later + + + // add command examples for use the default "/wb" command list and for internal usage reference, formatted and colorized + public void addCmdExample(String example) + { + addCmdExample(example, true, true, true); + } + public void addCmdExample(String example, boolean forPlayer, boolean forConsole, boolean prefix) + { + // go ahead and colorize required "<>" and optional "[]" parameters, extra command words, and description + example = example.replace("<", clrReq+"<").replace("[", clrOpt+"[").replace("^", clrCmd).replace("- ", clrDesc+"- "); + + // all "{}" are replaced by "[]" (optional) for player, "<>" (required) for console + if (forPlayer) + { + String exampleP = (prefix ? cmdP : "") + example.replace("{", clrOpt + "[").replace("}", "]"); + cmdExamplePlayer.add(exampleP); + cmdExamplesPlayer.add(exampleP); + } + if (forConsole) + { + String exampleC = (prefix ? cmdC : "") + example.replace("{", clrReq + "<").replace("}", ">"); + cmdExampleConsole.add(exampleC); + cmdExamplesConsole.add(exampleC); + } + } + + // formatted and colorized text, intended for marking command name + public String commandEmphasized(String text) + { + return clrCmd + ChatColor.UNDERLINE + text + ChatColor.RESET + " "; + } + + // returns green "enabled" or red "disabled" text + public String enabledColored(boolean enabled) + { + return enabled ? clrReq+"enabled" : clrErr+"disabled"; + } + + // formatted and colorized command name, optionally prefixed with "[world]" (for player) / "" (for console) + public String nameEmphasized() + { + return commandEmphasized(name); + } + public String nameEmphasizedW() + { + return "{world} " + nameEmphasized(); + } + + // send command example message(s) + public void sendCmdHelp(CommandSender sender) + { + for (String example : ((sender instanceof Player) ? cmdExamplePlayer : cmdExampleConsole)) + { + sender.sendMessage(example); + } + } + + // send error message followed by command example message(s) + public void sendErrorAndHelp(CommandSender sender, String error) + { + sender.sendMessage(clrErr + error); + sendCmdHelp(sender); + } + + // interpret string as boolean value (yes/no, true/false, on/off, +/-, 1/0) + public boolean strAsBool(String str) + { + str = str.toLowerCase(); + return str.startsWith("y") || str.startsWith("t") || str.startsWith("on") || str.startsWith("+") || str.startsWith("1"); + } +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 5cbbe82..26fa666 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -17,29 +17,30 @@ commands: / set [radiusZ] player - center on player. / [world] setcorners - set border from corners. / [world] radius [radiusZ] - change border's radius. + / shape - set the default border shape. + / shape - same as above, backwards compatible. / [world] clear - remove border for this world. / clear all - remove border for all worlds. / list - show border information for all worlds. - / shape - set the default border shape. - / shape - same as above, backwards compatible. - / getmsg - display border message. - / setmsg - set border message. - / knockback - how far to move the player back. - / whoosh - turn knockback effect on or off. - / portal - turn portal redirection on or off. - / delay - time between border checks. - / wshape [world] - override shape. - / wshape [world] - same as above values. - / wrap [world] - can make border crossings wrap around. / [world] fill [freq] [pad] [force] - generate world to border. / [world] trim [freq] [pad] - trim world outside of border. / bypass [player] [on/off] - let player go beyond border. / bypasslist - list players with border bypass enabled. - / remount - delay before remounting after knockback. - / fillautosave - world save interval for Fill process. - / denypearl - stop ender pearls thrown past the border. + / knockback - how far to move the player back. + / wrap [world] - can make border crossings wrap around. + / whoosh - turn knockback effect on or off. + / getmsg - display border message. + / setmsg - set border message. + / delay - time between border checks. + / wshape [world] - override shape. + / wshape [world] - same as above values. / dynmap - turn DynMap border display on or off. / dynmapmsg - DynMap border labels will show this. + / remount - delay before remounting after knockback. + / fillautosave - world save interval for Fill process. + / portal - turn portal redirection on or off. + / denypearl - stop ender pearls thrown past the border. + / reload - re-load data from config.yml. / debug - turn debug mode on or off. permissions: worldborder.*: