summaryrefslogtreecommitdiffstats
path: root/src/main
diff options
context:
space:
mode:
authorrmichela <deltahat@gmail.com>2012-03-01 00:07:05 -0500
committerEvilSeph <evilseph@gmail.com>2012-03-01 04:28:21 -0500
commitb8444288a9863213b774aefddc94ce3775f1bbdc (patch)
treeb6859617ce81b5dcc1027083a4f698bfe5a2a0e1 /src/main
parent463856b8a01c869154627865866d6125683e259b (diff)
downloadbukkit-b8444288a9863213b774aefddc94ce3775f1bbdc.tar
bukkit-b8444288a9863213b774aefddc94ce3775f1bbdc.tar.gz
bukkit-b8444288a9863213b774aefddc94ce3775f1bbdc.tar.lz
bukkit-b8444288a9863213b774aefddc94ce3775f1bbdc.tar.xz
bukkit-b8444288a9863213b774aefddc94ce3775f1bbdc.zip
[Bleeding] Added Help API. Addresses BUKKIT-863
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/org/bukkit/Bukkit.java7
-rw-r--r--src/main/java/org/bukkit/Server.java10
-rw-r--r--src/main/java/org/bukkit/command/Command.java24
-rw-r--r--src/main/java/org/bukkit/command/CommandSender.java7
-rw-r--r--src/main/java/org/bukkit/command/MultipleCommandAlias.java4
-rw-r--r--src/main/java/org/bukkit/command/SimpleCommandMap.java28
-rw-r--r--src/main/java/org/bukkit/command/defaults/HelpCommand.java88
-rw-r--r--src/main/java/org/bukkit/command/defaults/TimeCommand.java1
-rw-r--r--src/main/java/org/bukkit/command/defaults/WhitelistCommand.java1
-rw-r--r--src/main/java/org/bukkit/help/HelpMap.java44
-rw-r--r--src/main/java/org/bukkit/help/HelpTopic.java90
-rw-r--r--src/main/java/org/bukkit/help/HelpTopicFactory.java27
-rw-r--r--src/main/java/org/bukkit/util/ChatPaginator.java137
-rw-r--r--src/main/java/org/bukkit/util/permissions/CommandPermissions.java2
14 files changed, 435 insertions, 35 deletions
diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
index b52166a0..9327ecbe 100644
--- a/src/main/java/org/bukkit/Bukkit.java
+++ b/src/main/java/org/bukkit/Bukkit.java
@@ -12,8 +12,9 @@ import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.entity.Player;
-import org.bukkit.inventory.ItemStack;
import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.help.HelpMap;
+import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.Recipe;
@@ -336,4 +337,8 @@ public final class Bukkit {
public static Inventory createInventory(InventoryHolder owner, int size, String title) {
return server.createInventory(owner, size, title);
}
+
+ public static HelpMap getHelpMap() {
+ return server.getHelpMap();
+ }
}
diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
index cf3a592b..537b65c0 100644
--- a/src/main/java/org/bukkit/Server.java
+++ b/src/main/java/org/bukkit/Server.java
@@ -14,6 +14,7 @@ import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.help.HelpMap;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
@@ -565,6 +566,13 @@ public interface Server extends PluginMessageRecipient {
public Messenger getMessenger();
/**
+ * Gets the {@link HelpMap} providing help topics for this server.
+ *
+ * @return The server's HelpMap.
+ */
+ public HelpMap getHelpMap();
+
+ /**
* Creates an empty inventory of the specified type. If the type is {@link InventoryType#CHEST},
* the new inventory has a size of 27; otherwise the new inventory has the normal size for
* its type.
@@ -592,4 +600,4 @@ public interface Server extends PluginMessageRecipient {
* @throws IllegalArgumentException If the size is not a multiple of 9.
*/
Inventory createInventory(InventoryHolder owner, int size, String title);
-}
+} \ No newline at end of file
diff --git a/src/main/java/org/bukkit/command/Command.java b/src/main/java/org/bukkit/command/Command.java
index 7c82fb0d..5a701591 100644
--- a/src/main/java/org/bukkit/command/Command.java
+++ b/src/main/java/org/bukkit/command/Command.java
@@ -83,7 +83,7 @@ public abstract class Command {
* @return true if they can use it, otherwise false
*/
public boolean testPermission(CommandSender target) {
- if ((permission == null) || (permission.length() == 0) || (target.hasPermission(permission))) {
+ if (testPermissionSilent(target)) {
return true;
}
@@ -99,6 +99,28 @@ public abstract class Command {
}
/**
+ * Tests the given {@link CommandSender} to see if they can perform this command.
+ * <p />
+ * No error is sent to the sender.
+ *
+ * @param target User to test
+ * @return true if they can use it, otherwise false
+ */
+ public boolean testPermissionSilent(CommandSender target) {
+ if ((permission == null) || (permission.length() == 0)) {
+ return true;
+ }
+
+ for (String p : permission.split(";")) {
+ if (target.hasPermission(p)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
* Returns the current lable for this command
*
* @return Label of this command or null if not registered
diff --git a/src/main/java/org/bukkit/command/CommandSender.java b/src/main/java/org/bukkit/command/CommandSender.java
index 7bc46c53..148756b9 100644
--- a/src/main/java/org/bukkit/command/CommandSender.java
+++ b/src/main/java/org/bukkit/command/CommandSender.java
@@ -13,6 +13,13 @@ public interface CommandSender extends Permissible {
public void sendMessage(String message);
/**
+ * Sends this sender multiple messages
+ *
+ * @param messages An array of messages to be displayed
+ */
+ public void sendMessage(String[] messages);
+
+ /**
* Returns the server instance that this command is running on
*
* @return Server instance
diff --git a/src/main/java/org/bukkit/command/MultipleCommandAlias.java b/src/main/java/org/bukkit/command/MultipleCommandAlias.java
index 59ac4c20..b1b83b1f 100644
--- a/src/main/java/org/bukkit/command/MultipleCommandAlias.java
+++ b/src/main/java/org/bukkit/command/MultipleCommandAlias.java
@@ -10,6 +10,10 @@ public class MultipleCommandAlias extends Command {
super(name);
this.commands = commands;
}
+
+ public Command[] getCommands() {
+ return commands;
+ }
@Override
public boolean execute(CommandSender sender, String commandLabel, String[] args) {
diff --git a/src/main/java/org/bukkit/command/SimpleCommandMap.java b/src/main/java/org/bukkit/command/SimpleCommandMap.java
index 4ce74d3c..e4a7ba3e 100644
--- a/src/main/java/org/bukkit/command/SimpleCommandMap.java
+++ b/src/main/java/org/bukkit/command/SimpleCommandMap.java
@@ -1,13 +1,9 @@
package org.bukkit.command;
import org.bukkit.command.defaults.*;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.Iterator;
+
+import java.util.*;
+
import org.bukkit.Server;
import static org.bukkit.util.Java15Compat.Arrays_copyOfRange;
@@ -143,6 +139,10 @@ public class SimpleCommandMap implements CommandMap {
return null;
}
+
+ public Set<VanillaCommand> getFallbackCommands() {
+ return Collections.unmodifiableSet(fallbackCommands);
+ }
/**
* {@inheritDoc}
@@ -156,9 +156,7 @@ public class SimpleCommandMap implements CommandMap {
String sentCommandLabel = args[0].toLowerCase();
Command target = getCommand(sentCommandLabel);
- if (target == null) {
- target = getFallback(commandLine.toLowerCase());
- }
+
if (target == null) {
return false;
}
@@ -186,7 +184,15 @@ public class SimpleCommandMap implements CommandMap {
}
public Command getCommand(String name) {
- return knownCommands.get(name.toLowerCase());
+ Command target = knownCommands.get(name.toLowerCase());
+ if (target == null) {
+ target = getFallback(name);
+ }
+ return target;
+ }
+
+ public Collection<Command> getCommands() {
+ return knownCommands.values();
}
public void registerServerAliases() {
diff --git a/src/main/java/org/bukkit/command/defaults/HelpCommand.java b/src/main/java/org/bukkit/command/defaults/HelpCommand.java
index 42b3579e..17867c49 100644
--- a/src/main/java/org/bukkit/command/defaults/HelpCommand.java
+++ b/src/main/java/org/bukkit/command/defaults/HelpCommand.java
@@ -1,12 +1,23 @@
package org.bukkit.command.defaults;
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.math.NumberUtils;
+import org.bukkit.Bukkit;
+import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
+import org.bukkit.command.ConsoleCommandSender;
+import org.bukkit.help.HelpMap;
+import org.bukkit.help.HelpTopic;
+import org.bukkit.util.ChatPaginator;
+
+import java.util.Arrays;
public class HelpCommand extends VanillaCommand {
public HelpCommand() {
super("help");
this.description = "Shows the help menu";
- this.usageMessage = "/help";
+ this.usageMessage = "/help <pageNumber>\n/help <topic>\n/help <topic> <pageNumber>";
this.setPermission("bukkit.command.help");
}
@@ -14,25 +25,62 @@ public class HelpCommand extends VanillaCommand {
public boolean execute(CommandSender sender, String currentAlias, String[] args) {
if (!testPermission(sender)) return true;
- sender.sendMessage("help or ? shows this message");
- sender.sendMessage("kick <player> removes a player from the server");
- sender.sendMessage("ban <player> bans a player from the server");
- sender.sendMessage("pardon <player> pardons a banned player so that they can connect again");
- sender.sendMessage("ban-ip <ip> bans an IP address from the server");
- sender.sendMessage("pardon-ip <ip> pardons a banned IP address so that they can connect again");
- sender.sendMessage("op <player> turns a player into an op");
- sender.sendMessage("deop <player> removes op status from a player");
- sender.sendMessage("tp <player1> <player2> moves one player to the same location as another player");
- sender.sendMessage("give <player> <id> [num] gives a player a resource");
- sender.sendMessage("tell <player> <message> sends a private message to a player");
- sender.sendMessage("stop gracefully stops the server");
- sender.sendMessage("save-all forces a server-wide level save");
- sender.sendMessage("save-off disables terrain saving (useful for backup scripts)");
- sender.sendMessage("save-on re-enables terrain saving");
- sender.sendMessage("list lists all currently connected players");
- sender.sendMessage("say <message> broadcasts a message to all players");
- sender.sendMessage("time <add|set> <amount> adds to or sets the world time (0-24000)");
- sender.sendMessage("gamemode <player> <mode> sets player\'s game mode (0 or 1)");
+ String command;
+ int pageNumber;
+ int pageHeight;
+ int pageWidth;
+
+ if (args.length == 0) {
+ command = "";
+ pageNumber = 1;
+ } else if (NumberUtils.isDigits(args[args.length - 1])) {
+ command = StringUtils.join(ArrayUtils.subarray(args, 0, args.length - 1), " ");
+ pageNumber = NumberUtils.createInteger(args[args.length - 1]);
+ } else {
+ command = StringUtils.join(args, " ");
+ pageNumber = 1;
+ }
+
+ if (sender instanceof ConsoleCommandSender) {
+ pageHeight = ChatPaginator.UNBOUNDED_PAGE_HEIGHT;
+ pageWidth = ChatPaginator.UNBOUNDED_PAGE_WIDTH;
+ } else {
+ pageHeight = ChatPaginator.CLOSED_CHAT_PAGE_HEIGHT - 1;
+ pageWidth = ChatPaginator.AVERAGE_CHAT_PAGE_WIDTH;
+ }
+
+ HelpMap helpMap = Bukkit.getServer().getHelpMap();
+ HelpTopic topic = helpMap.getHelpTopic(command);
+
+ if (topic == null) {
+ topic = helpMap.getHelpTopic("/" + command);
+ }
+
+ if (topic == null || !topic.canSee(sender)) {
+ sender.sendMessage(ChatColor.RED + "No help for " + command);
+ return true;
+ }
+
+ ChatPaginator.ChatPage page = ChatPaginator.paginate(topic.getFullText(sender), pageNumber, pageWidth, pageHeight);
+
+ StringBuilder header = new StringBuilder();
+ header.append(ChatColor.GREEN);
+ header.append("===== Help: ");
+ header.append(topic.getName());
+ header.append(" ");
+ if (page.getTotalPages() > 1) {
+ header.append("(");
+ header.append(page.getPageNumber());
+ header.append(" of ");
+ header.append(page.getTotalPages());
+ header.append(") ");
+ }
+ for (int i = header.length(); i < ChatPaginator.GUARANTEED_NO_WRAP_CHAT_PAGE_WIDTH; i++) {
+ header.append("=");
+ }
+ sender.sendMessage(header.toString());
+
+ sender.sendMessage(page.getLines());
return true;
}
diff --git a/src/main/java/org/bukkit/command/defaults/TimeCommand.java b/src/main/java/org/bukkit/command/defaults/TimeCommand.java
index 347c3489..77ec55bb 100644
--- a/src/main/java/org/bukkit/command/defaults/TimeCommand.java
+++ b/src/main/java/org/bukkit/command/defaults/TimeCommand.java
@@ -11,6 +11,7 @@ public class TimeCommand extends VanillaCommand {
super("time");
this.description = "Changes the time on each world";
this.usageMessage = "/time set <value>\n/time add <value>";
+ this.setPermission("bukkit.command.time.add;bukkit.command.time.set");
}
@Override
diff --git a/src/main/java/org/bukkit/command/defaults/WhitelistCommand.java b/src/main/java/org/bukkit/command/defaults/WhitelistCommand.java
index 42e22105..eb928877 100644
--- a/src/main/java/org/bukkit/command/defaults/WhitelistCommand.java
+++ b/src/main/java/org/bukkit/command/defaults/WhitelistCommand.java
@@ -11,6 +11,7 @@ public class WhitelistCommand extends VanillaCommand {
super("whitelist");
this.description = "Prevents the specified player from using this server";
this.usageMessage = "/whitelist (add|remove) <player>\n/whitelist (on|off|list|reload)";
+ this.setPermission("bukkit.command.whitelist.reload;bukkit.command.whitelist.enable;bukkit.command.whitelist.disable;bukkit.command.whitelist.list;bukkit.command.whitelist.add;bukkit.command.whitelist.remove");
}
@Override
diff --git a/src/main/java/org/bukkit/help/HelpMap.java b/src/main/java/org/bukkit/help/HelpMap.java
new file mode 100644
index 00000000..f76f4278
--- /dev/null
+++ b/src/main/java/org/bukkit/help/HelpMap.java
@@ -0,0 +1,44 @@
+package org.bukkit.help;
+
+/**
+ * The HelpMap tracks all help topics registered in a Bukkit server. When the server starts up or is reloaded,
+ * help is processed and topics are added in the following order:
+ *
+ * 1. General topics are loaded from the help.yml
+ * 2. Plugins load and optionally call {@code addTopic()}
+ * 3. Registered plugin commands are processed by {@link HelpTopicFactory} objects to create topics
+ * 4. Topic contents are amended as directed in help.yml
+ */
+public interface HelpMap {
+ /**
+ * Returns a help topic for a given topic name.
+ *
+ * @param topicName The help topic name to look up.
+ * @return A {@link HelpTopic} object matching the topic name or null if none can be found.
+ */
+ public HelpTopic getHelpTopic(String topicName);
+
+ /**
+ * Adds a topic to the server's help index.
+ *
+ * @param topic The new help topic to add.
+ */
+ public void addTopic(HelpTopic topic);
+
+ /**
+ * Clears out the contents of the help index. Normally called during server reload.
+ */
+ public void clear();
+
+ /**
+ * Associates a {@link HelpTopicFactory} object with given command base class. Plugins typically
+ * call this method during {@code onLoad()}. Once registered, the custom HelpTopicFactory will
+ * be used to create a custom {@link HelpTopic} for all commands deriving from the {@code commandClass}
+ * base class.
+ *
+ * @param commandClass The class for which the custom HelpTopicFactory applies. Must derive from {@link org.bukkit.command.Command}.
+ * @param factory The {@link HelpTopicFactory} implementation to associate with the {@code commandClass}.
+ * @throws IllegalArgumentException Thrown if {@code commandClass} does not derive from Command.
+ */
+ public void registerHelpTopicFactory(Class commandClass, HelpTopicFactory factory);
+}
diff --git a/src/main/java/org/bukkit/help/HelpTopic.java b/src/main/java/org/bukkit/help/HelpTopic.java
new file mode 100644
index 00000000..885ae23b
--- /dev/null
+++ b/src/main/java/org/bukkit/help/HelpTopic.java
@@ -0,0 +1,90 @@
+package org.bukkit.help;
+
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+/**
+ * HelpTopic implementations are displayed to the user when the user uses the /help command.
+ *
+ * Custom implementations of this class can work at two levels. A simple implementation only
+ * needs to set the value of {@code name}, {@code shortText}, and {@code fullText} int the
+ * constructor. This base class will take care of the rest.
+ *
+ * Complex implementations can be created by overriding the behavior of all the methods in
+ * this class.
+ */
+public abstract class HelpTopic {
+ protected String name;
+ protected String shortText;
+ protected String fullText;
+
+ /**
+ * Determines if a {@link Player} is allowed to see this help topic.
+ *
+ * @param player The Player in question.
+ * @return True of the Player can see this help topic, false otherwise.
+ */
+ public abstract boolean canSee(CommandSender player);
+
+ /**
+ * Returns the name of this help topic.
+ * @return The topic name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns a brief description that will be displayed in the topic index.
+ * @return A brief topic description.
+ */
+ public String getShortText() {
+ return shortText;
+ }
+
+ /**
+ * Returns the full description of this help topic that is displayed when the user requests this topic's details.
+ * The result will be paginated to properly fit the user's client.
+ *
+ * @param forWho The player or console requesting the full text. Useful for further security trimming
+ * the command's full text based on sub-permissions in custom implementations.
+ *
+ * @return A full topic description.
+ */
+ public String getFullText(CommandSender forWho) {
+ return fullText;
+ }
+
+ /**
+ * Allows the server admin (or another plugin) to add or replace the contents of a help topic. A null in
+ * either parameter will leave that part of the topic unchanged. In either amending parameter, the string
+ * {@literal <text>} is replaced with the existing contents in the help topic. Use this to append or prepend
+ * additional content into an automatically generated help topic.
+ *
+ * @param amendedShortText The new topic short text to use, or null to leave alone.
+ * @param amendedFullText The new topic full text to use, or null to leave alone.
+ */
+ public void amendTopic(String amendedShortText, String amendedFullText) {
+ shortText = applyAmendment(shortText, amendedShortText);
+ fullText = applyAmendment(fullText, amendedFullText);
+ }
+
+ /**
+ * Developers implementing their own custom HelpTopic implementations can use this utility method to ensure
+ * their implementations comply with the expected behavior of the {@link HelpTopic#amendTopic(String, String)}
+ * method.
+ *
+ * @param baseText The existing text of the help topic.
+ * @param amendment The amending text from the amendTopic() method.
+ *
+ * @return The application of the amending text to the existing text, according to the expected rules of
+ * amendTopic().
+ */
+ protected String applyAmendment(String baseText, String amendment) {
+ if (amendment == null) {
+ return baseText;
+ } else {
+ return amendment.replaceAll("<text>", baseText);
+ }
+ }
+}
diff --git a/src/main/java/org/bukkit/help/HelpTopicFactory.java b/src/main/java/org/bukkit/help/HelpTopicFactory.java
new file mode 100644
index 00000000..58639510
--- /dev/null
+++ b/src/main/java/org/bukkit/help/HelpTopicFactory.java
@@ -0,0 +1,27 @@
+package org.bukkit.help;
+
+import org.bukkit.command.Command;
+
+/**
+ * A HelpTopicFactory is used to create custom {@link HelpTopic} objects from commands that inherit from a
+ * common base class. You can use a custom HelpTopic to change the way all the commands in your plugin display
+ * in the help. If your plugin implements a complex permissions system, a custom help topic may also be appropriate.
+ *
+ * To automatically bind your plugin's commands to your custom HelpTopic implementation, first make sure all your
+ * commands derive from a custom base class (it doesn't have to do anything). Next implement a custom HelpTopicFactory
+ * for that accepts your custom command base class and instantiates an instance of your custom HelpTopic from it.
+ * Finally, register your HelpTopicFactory against your command base class using the {@link HelpMap#registerHelpTopicFactory(Class, HelpTopicFactory)}
+ * method.
+ *
+ * @param <TCommand> The base class for your custom commands.
+ */
+public interface HelpTopicFactory<TCommand extends Command> {
+ /**
+ * This method accepts a command deriving from a custom command base class and constructs a custom HelpTopic
+ * for it.
+ *
+ * @param command The custom command to build a help topic for.
+ * @return A new custom help topic.
+ */
+ public HelpTopic createTopic(TCommand command);
+}
diff --git a/src/main/java/org/bukkit/util/ChatPaginator.java b/src/main/java/org/bukkit/util/ChatPaginator.java
new file mode 100644
index 00000000..9fdf1bbd
--- /dev/null
+++ b/src/main/java/org/bukkit/util/ChatPaginator.java
@@ -0,0 +1,137 @@
+package org.bukkit.util;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * The ChatPaginator takes a raw string of arbitrary length and breaks it down into an array of strings appropriate
+ * for displaying on the Minecraft player console.
+ */
+public class ChatPaginator {
+ public static final int GUARANTEED_NO_WRAP_CHAT_PAGE_WIDTH = 55; // Will never wrap, even with the largest characters
+ public static final int AVERAGE_CHAT_PAGE_WIDTH = 65; // Will typically not wrap using an average character distribution
+ public static final int UNBOUNDED_PAGE_WIDTH = Integer.MAX_VALUE;
+ public static final int OPEN_CHAT_PAGE_HEIGHT = 20; // The height of an expanded chat window
+ public static final int CLOSED_CHAT_PAGE_HEIGHT = 10; // The height of the default chat window
+ public static final int UNBOUNDED_PAGE_HEIGHT = Integer.MAX_VALUE;
+
+ /**
+ * Breaks a raw string up into pages using the default width and height.
+ * @param unpaginatedString The raw string to break.
+ * @param pageNumber The page number to fetch.
+ * @return A single chat page.
+ */
+ public static ChatPage paginate(String unpaginatedString, int pageNumber) {
+ return paginate(unpaginatedString, pageNumber, GUARANTEED_NO_WRAP_CHAT_PAGE_WIDTH, CLOSED_CHAT_PAGE_HEIGHT);
+ }
+
+ /**
+ * Breaks a raw string up into pages using a provided width and height.
+ * @param unpaginatedString The raw string to break.
+ * @param pageNumber The page number to fetch.
+ * @param lineLength The desired width of a chat line.
+ * @param pageHeight The desired number of lines in a page.
+ * @return A single chat page.
+ */
+ public static ChatPage paginate(String unpaginatedString, int pageNumber, int lineLength, int pageHeight) {
+ String[] lines = wordWrap(unpaginatedString, lineLength);
+
+ int totalPages = lines.length / pageHeight + (lines.length % pageHeight == 0 ? 0 : 1);
+ int actualPageNumber = pageNumber <= totalPages ? pageNumber : totalPages;
+
+ int from = (actualPageNumber - 1) * pageHeight;
+ int to = from + pageHeight <= lines.length ? from + pageHeight : lines.length;
+ String[] selectedLines = Arrays.copyOfRange(lines, from, to);
+
+ return new ChatPage(selectedLines, actualPageNumber, totalPages);
+ }
+
+ /**
+ * Breaks a raw string up into a series of lines. Words are wrapped using spaces as decimeters and the newline
+ * character is respected.
+ * @param rawString The raw string to break.
+ * @param lineLength The length of a line of text.
+ * @return An array of word-wrapped lines.
+ */
+ public static String[] wordWrap(String rawString, int lineLength) {
+ // A null string is a single line
+ if (rawString == null) {
+ return new String[] {""};
+ }
+
+ // A string shorter than the lineWidth is a single line
+ if (rawString.length() <= lineLength && !rawString.contains("\n")) {
+ return new String[] {rawString};
+ }
+
+ char[] rawChars = (rawString + ' ').toCharArray(); // add a trailing space to trigger pagination
+ StringBuilder word = new StringBuilder();
+ StringBuilder line = new StringBuilder();
+ List<String> lines = new LinkedList<String>();
+
+ for (char c : rawChars) {
+ if (c == ' ' || c == '\n') {
+ if (line.length() == 0 && word.length() > lineLength) { // special case: extremely long word begins a line
+ for (String partialWord : word.toString().split("(?<=\\G.{" + lineLength + "})")) {
+ lines.add(partialWord);
+ }
+ } else if (line.length() + word.length() == lineLength) { // Line exactly the correct length...newline
+ line.append(word);
+ lines.add(line.toString());
+ line = new StringBuilder();
+ } else if (line.length() + 1 + word.length() > lineLength) { // Line too long...break the line
+ for (String partialWord : word.toString().split("(?<=\\G.{" + lineLength + "})")) {
+ lines.add(line.toString());
+ line = new StringBuilder(partialWord);
+ }
+ } else {
+ if (line.length() > 0) {
+ line.append(' ');
+ }
+ line.append(word);
+ }
+ word = new StringBuilder();
+
+ if (c == '\n') { // Newline forces the line to flush
+ lines.add(line.toString());
+ line = new StringBuilder();
+ }
+ } else {
+ word.append(c);
+ }
+ }
+
+ if(line.length() > 0) { // Only add the last line if there is anything to add
+ lines.add(line.toString());
+ }
+
+ return lines.toArray(new String[0]);
+ }
+
+ public static class ChatPage {
+
+ private String[] lines;
+ private int pageNumber;
+ private int totalPages;
+
+ public ChatPage(String[] lines, int pageNumber, int totalPages) {
+ this.lines = lines;
+ this.pageNumber = pageNumber;
+ this.totalPages = totalPages;
+ }
+
+ public int getPageNumber() {
+ return pageNumber;
+ }
+
+ public int getTotalPages() {
+ return totalPages;
+ }
+
+ public String[] getLines() {
+
+ return lines;
+ }
+ }
+}
diff --git a/src/main/java/org/bukkit/util/permissions/CommandPermissions.java b/src/main/java/org/bukkit/util/permissions/CommandPermissions.java
index e81e5aef..b6b505bd 100644
--- a/src/main/java/org/bukkit/util/permissions/CommandPermissions.java
+++ b/src/main/java/org/bukkit/util/permissions/CommandPermissions.java
@@ -99,7 +99,7 @@ public final class CommandPermissions {
DefaultPermissions.registerPermission(PREFIX + "kick", "Allows the user to kick players", PermissionDefault.OP, commands);
DefaultPermissions.registerPermission(PREFIX + "stop", "Allows the user to stop the server", PermissionDefault.OP, commands);
DefaultPermissions.registerPermission(PREFIX + "list", "Allows the user to list all online players", PermissionDefault.OP, commands);
- DefaultPermissions.registerPermission(PREFIX + "help", "Allows the user to view the vanilla help menu", PermissionDefault.OP, commands);
+ DefaultPermissions.registerPermission(PREFIX + "help", "Allows the user to view the vanilla help menu", PermissionDefault.TRUE, commands);
DefaultPermissions.registerPermission(PREFIX + "plugins", "Allows the user to view the list of plugins running on this server", PermissionDefault.TRUE, commands);
DefaultPermissions.registerPermission(PREFIX + "reload", "Allows the user to reload the server settings", PermissionDefault.OP, commands);
DefaultPermissions.registerPermission(PREFIX + "version", "Allows the user to view the version of the server", PermissionDefault.TRUE, commands);