From b8444288a9863213b774aefddc94ce3775f1bbdc Mon Sep 17 00:00:00 2001 From: rmichela Date: Thu, 1 Mar 2012 00:07:05 -0500 Subject: [Bleeding] Added Help API. Addresses BUKKIT-863 --- src/main/java/org/bukkit/Bukkit.java | 7 +- src/main/java/org/bukkit/Server.java | 10 +- src/main/java/org/bukkit/command/Command.java | 24 +++- .../java/org/bukkit/command/CommandSender.java | 7 + .../org/bukkit/command/MultipleCommandAlias.java | 4 + .../java/org/bukkit/command/SimpleCommandMap.java | 28 ++-- .../org/bukkit/command/defaults/HelpCommand.java | 88 +++++++++--- .../org/bukkit/command/defaults/TimeCommand.java | 1 + .../bukkit/command/defaults/WhitelistCommand.java | 1 + src/main/java/org/bukkit/help/HelpMap.java | 44 ++++++ src/main/java/org/bukkit/help/HelpTopic.java | 90 ++++++++++++ .../java/org/bukkit/help/HelpTopicFactory.java | 27 ++++ src/main/java/org/bukkit/util/ChatPaginator.java | 137 ++++++++++++++++++ .../util/permissions/CommandPermissions.java | 2 +- src/test/java/org/bukkit/ChatPaginatorTest.java | 157 +++++++++++++++++++++ .../org/bukkit/plugin/messaging/TestPlayer.java | 4 + 16 files changed, 596 insertions(+), 35 deletions(-) create mode 100644 src/main/java/org/bukkit/help/HelpMap.java create mode 100644 src/main/java/org/bukkit/help/HelpTopic.java create mode 100644 src/main/java/org/bukkit/help/HelpTopicFactory.java create mode 100644 src/main/java/org/bukkit/util/ChatPaginator.java create mode 100644 src/test/java/org/bukkit/ChatPaginatorTest.java (limited to 'src') 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; @@ -564,6 +565,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 @@ -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; } @@ -98,6 +98,28 @@ public abstract class Command { return false; } + /** + * Tests the given {@link CommandSender} to see if they can perform this command. + *

+ * 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 * 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 @@ -12,6 +12,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 * 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 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 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 \n/help \n/help "; 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 removes a player from the server"); - sender.sendMessage("ban bans a player from the server"); - sender.sendMessage("pardon pardons a banned player so that they can connect again"); - sender.sendMessage("ban-ip bans an IP address from the server"); - sender.sendMessage("pardon-ip pardons a banned IP address so that they can connect again"); - sender.sendMessage("op turns a player into an op"); - sender.sendMessage("deop removes op status from a player"); - sender.sendMessage("tp moves one player to the same location as another player"); - sender.sendMessage("give [num] gives a player a resource"); - sender.sendMessage("tell 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 broadcasts a message to all players"); - sender.sendMessage("time adds to or sets the world time (0-24000)"); - sender.sendMessage("gamemode 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 \n/time add "; + 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) \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 } 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("", 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 The base class for your custom commands. + */ +public interface HelpTopicFactory { + /** + * 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 lines = new LinkedList(); + + 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); diff --git a/src/test/java/org/bukkit/ChatPaginatorTest.java b/src/test/java/org/bukkit/ChatPaginatorTest.java new file mode 100644 index 00000000..6e20456e --- /dev/null +++ b/src/test/java/org/bukkit/ChatPaginatorTest.java @@ -0,0 +1,157 @@ +package org.bukkit; + +import org.bukkit.util.ChatPaginator; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; + +/** + */ +public class ChatPaginatorTest { + @Test + public void testWordWrap1() { + String rawString = "123456789 123456789 123456789"; + String[] lines = ChatPaginator.wordWrap(rawString, 19); + + assertThat(lines.length, is(2)); + assertThat(lines[0], is("123456789 123456789")); + assertThat(lines[1], is("123456789")); + } + + @Test + public void testWordWrap2() { + String rawString = "123456789 123456789 123456789"; + String[] lines = ChatPaginator.wordWrap(rawString, 22); + + assertThat(lines.length, is(2)); + assertThat(lines[0], is("123456789 123456789")); + assertThat(lines[1], is("123456789")); + } + + @Test + public void testWordWrap3() { + String rawString = "123456789 123456789 123456789"; + String[] lines = ChatPaginator.wordWrap(rawString, 16); + + assertThat(lines.length, is(3)); + assertThat(lines[0], is("123456789")); + assertThat(lines[1], is("123456789")); + assertThat(lines[2], is("123456789")); + } + + @Test + public void testWordWrap4() { + String rawString = "123456789 123456789 123456789 12345"; + String[] lines = ChatPaginator.wordWrap(rawString, 19); + + assertThat(lines.length, is(2)); + assertThat(lines[0], is("123456789 123456789")); + assertThat(lines[1], is("123456789 12345")); + } + + @Test + public void testWordWrap5() { + String rawString = "123456789\n123456789 123456789"; + String[] lines = ChatPaginator.wordWrap(rawString, 19); + + assertThat(lines.length, is(2)); + assertThat(lines[0], is("123456789")); + assertThat(lines[1], is("123456789 123456789")); + } + + @Test + public void testWordWrap6() { + String rawString = "12345678 23456789 123456789"; + String[] lines = ChatPaginator.wordWrap(rawString, 19); + + assertThat(lines.length, is(2)); + assertThat(lines[0], is("12345678 23456789")); + assertThat(lines[1], is("123456789")); + } + + @Test + public void testWordWrap7() { + String rawString = "12345678 23456789 123456789"; + String[] lines = ChatPaginator.wordWrap(rawString, 19); + + assertThat(lines.length, is(2)); + assertThat(lines[0], is("12345678 23456789")); + assertThat(lines[1], is("123456789")); + } + + @Test + public void testWordWrap8() { + String rawString = "123456789 123456789 123456789"; + String[] lines = ChatPaginator.wordWrap(rawString, 6); + + assertThat(lines.length, is(6)); + assertThat(lines[0], is("123456")); + assertThat(lines[1], is("789")); + assertThat(lines[2], is("123456")); + assertThat(lines[3], is("789")); + assertThat(lines[4], is("123456")); + assertThat(lines[5], is("789")); + } + + @Test + public void testWordWrap9() { + String rawString = "1234 123456789 123456789 123456789"; + String[] lines = ChatPaginator.wordWrap(rawString, 6); + + assertThat(lines.length, is(7)); + assertThat(lines[0], is("1234")); + assertThat(lines[1], is("123456")); + assertThat(lines[2], is("789")); + assertThat(lines[3], is("123456")); + assertThat(lines[4], is("789")); + assertThat(lines[5], is("123456")); + assertThat(lines[6], is("789")); + } + + @Test + public void testWordWrap10() { + String rawString = "123456789\n123456789"; + String[] lines = ChatPaginator.wordWrap(rawString, 19); + + assertThat(lines.length, is(2)); + assertThat(lines[0], is("123456789")); + assertThat(lines[1], is("123456789")); + } + + @Test + public void testPaginate1() { + String rawString = "1234 123456789 123456789 123456789"; + ChatPaginator.ChatPage page = ChatPaginator.paginate(rawString, 1, 6, 2); + + assertThat(page.getPageNumber(), is(1)); + assertThat(page.getTotalPages(), is(4)); + assertThat(page.getLines().length, is(2)); + assertThat(page.getLines()[0], is("1234")); + assertThat(page.getLines()[1], is("123456")); + } + + @Test + public void testPaginate2() { + String rawString = "1234 123456789 123456789 123456789"; + ChatPaginator.ChatPage page = ChatPaginator.paginate(rawString, 2, 6, 2); + + assertThat(page.getPageNumber(), is(2)); + assertThat(page.getTotalPages(), is(4)); + assertThat(page.getLines().length, is(2)); + assertThat(page.getLines()[0], is("789")); + assertThat(page.getLines()[1], is("123456")); + } + + @Test + public void testPaginate3() { + String rawString = "1234 123456789 123456789 123456789"; + ChatPaginator.ChatPage page = ChatPaginator.paginate(rawString, 4, 6, 2); + + assertThat(page.getPageNumber(), is(4)); + assertThat(page.getTotalPages(), is(4)); + assertThat(page.getLines().length, is(1)); + assertThat(page.getLines()[0], is("789")); + } +} diff --git a/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java b/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java index 2ad65732..9b5aa5e4 100644 --- a/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java +++ b/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java @@ -556,6 +556,10 @@ public class TestPlayer implements Player { throw new UnsupportedOperationException("Not supported yet."); } + public void sendMessage(String[] messages) { + throw new UnsupportedOperationException("Not supported yet."); + } + public boolean isOnline() { throw new UnsupportedOperationException("Not supported yet."); } -- cgit v1.2.3