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.*: