mirror of
https://github.com/PaperMC/Paper.git
synced 2025-01-08 11:24:11 +01:00
[Bleeding] Added Help API. Addresses BUKKIT-863
By: rmichela <deltahat@gmail.com>
This commit is contained in:
parent
2280c6be2b
commit
10cd1cbb5c
16 changed files with 596 additions and 35 deletions
|
@ -12,8 +12,9 @@ import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.command.ConsoleCommandSender;
|
import org.bukkit.command.ConsoleCommandSender;
|
||||||
import org.bukkit.command.PluginCommand;
|
import org.bukkit.command.PluginCommand;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
|
||||||
import org.bukkit.event.inventory.InventoryType;
|
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.Inventory;
|
||||||
import org.bukkit.inventory.InventoryHolder;
|
import org.bukkit.inventory.InventoryHolder;
|
||||||
import org.bukkit.inventory.Recipe;
|
import org.bukkit.inventory.Recipe;
|
||||||
|
@ -336,4 +337,8 @@ public final class Bukkit {
|
||||||
public static Inventory createInventory(InventoryHolder owner, int size, String title) {
|
public static Inventory createInventory(InventoryHolder owner, int size, String title) {
|
||||||
return server.createInventory(owner, size, title);
|
return server.createInventory(owner, size, title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static HelpMap getHelpMap() {
|
||||||
|
return server.getHelpMap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import org.bukkit.command.ConsoleCommandSender;
|
||||||
import org.bukkit.command.PluginCommand;
|
import org.bukkit.command.PluginCommand;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.inventory.InventoryType;
|
import org.bukkit.event.inventory.InventoryType;
|
||||||
|
import org.bukkit.help.HelpMap;
|
||||||
import org.bukkit.inventory.Inventory;
|
import org.bukkit.inventory.Inventory;
|
||||||
import org.bukkit.inventory.InventoryHolder;
|
import org.bukkit.inventory.InventoryHolder;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
@ -564,6 +565,13 @@ public interface Server extends PluginMessageRecipient {
|
||||||
*/
|
*/
|
||||||
public Messenger getMessenger();
|
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},
|
* 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
|
* the new inventory has a size of 27; otherwise the new inventory has the normal size for
|
||||||
|
|
|
@ -83,7 +83,7 @@ public abstract class Command {
|
||||||
* @return true if they can use it, otherwise false
|
* @return true if they can use it, otherwise false
|
||||||
*/
|
*/
|
||||||
public boolean testPermission(CommandSender target) {
|
public boolean testPermission(CommandSender target) {
|
||||||
if ((permission == null) || (permission.length() == 0) || (target.hasPermission(permission))) {
|
if (testPermissionSilent(target)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,6 +98,28 @@ public abstract class Command {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* Returns the current lable for this command
|
||||||
*
|
*
|
||||||
|
|
|
@ -12,6 +12,13 @@ public interface CommandSender extends Permissible {
|
||||||
*/
|
*/
|
||||||
public void sendMessage(String message);
|
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
|
* Returns the server instance that this command is running on
|
||||||
*
|
*
|
||||||
|
|
|
@ -11,6 +11,10 @@ public class MultipleCommandAlias extends Command {
|
||||||
this.commands = commands;
|
this.commands = commands;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Command[] getCommands() {
|
||||||
|
return commands;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean execute(CommandSender sender, String commandLabel, String[] args) {
|
public boolean execute(CommandSender sender, String commandLabel, String[] args) {
|
||||||
boolean result = false;
|
boolean result = false;
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
package org.bukkit.command;
|
package org.bukkit.command;
|
||||||
|
|
||||||
import org.bukkit.command.defaults.*;
|
import org.bukkit.command.defaults.*;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import org.bukkit.Server;
|
import org.bukkit.Server;
|
||||||
import static org.bukkit.util.Java15Compat.Arrays_copyOfRange;
|
import static org.bukkit.util.Java15Compat.Arrays_copyOfRange;
|
||||||
|
|
||||||
|
@ -144,6 +140,10 @@ public class SimpleCommandMap implements CommandMap {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<VanillaCommand> getFallbackCommands() {
|
||||||
|
return Collections.unmodifiableSet(fallbackCommands);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
@ -156,9 +156,7 @@ public class SimpleCommandMap implements CommandMap {
|
||||||
|
|
||||||
String sentCommandLabel = args[0].toLowerCase();
|
String sentCommandLabel = args[0].toLowerCase();
|
||||||
Command target = getCommand(sentCommandLabel);
|
Command target = getCommand(sentCommandLabel);
|
||||||
if (target == null) {
|
|
||||||
target = getFallback(commandLine.toLowerCase());
|
|
||||||
}
|
|
||||||
if (target == null) {
|
if (target == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -186,7 +184,15 @@ public class SimpleCommandMap implements CommandMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Command getCommand(String name) {
|
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() {
|
public void registerServerAliases() {
|
||||||
|
|
|
@ -1,12 +1,23 @@
|
||||||
package org.bukkit.command.defaults;
|
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.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 class HelpCommand extends VanillaCommand {
|
||||||
public HelpCommand() {
|
public HelpCommand() {
|
||||||
super("help");
|
super("help");
|
||||||
this.description = "Shows the help menu";
|
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");
|
this.setPermission("bukkit.command.help");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,25 +25,62 @@ public class HelpCommand extends VanillaCommand {
|
||||||
public boolean execute(CommandSender sender, String currentAlias, String[] args) {
|
public boolean execute(CommandSender sender, String currentAlias, String[] args) {
|
||||||
if (!testPermission(sender)) return true;
|
if (!testPermission(sender)) return true;
|
||||||
|
|
||||||
sender.sendMessage("help or ? shows this message");
|
String command;
|
||||||
sender.sendMessage("kick <player> removes a player from the server");
|
int pageNumber;
|
||||||
sender.sendMessage("ban <player> bans a player from the server");
|
int pageHeight;
|
||||||
sender.sendMessage("pardon <player> pardons a banned player so that they can connect again");
|
int pageWidth;
|
||||||
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");
|
if (args.length == 0) {
|
||||||
sender.sendMessage("op <player> turns a player into an op");
|
command = "";
|
||||||
sender.sendMessage("deop <player> removes op status from a player");
|
pageNumber = 1;
|
||||||
sender.sendMessage("tp <player1> <player2> moves one player to the same location as another player");
|
} else if (NumberUtils.isDigits(args[args.length - 1])) {
|
||||||
sender.sendMessage("give <player> <id> [num] gives a player a resource");
|
command = StringUtils.join(ArrayUtils.subarray(args, 0, args.length - 1), " ");
|
||||||
sender.sendMessage("tell <player> <message> sends a private message to a player");
|
pageNumber = NumberUtils.createInteger(args[args.length - 1]);
|
||||||
sender.sendMessage("stop gracefully stops the server");
|
} else {
|
||||||
sender.sendMessage("save-all forces a server-wide level save");
|
command = StringUtils.join(args, " ");
|
||||||
sender.sendMessage("save-off disables terrain saving (useful for backup scripts)");
|
pageNumber = 1;
|
||||||
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");
|
if (sender instanceof ConsoleCommandSender) {
|
||||||
sender.sendMessage("time <add|set> <amount> adds to or sets the world time (0-24000)");
|
pageHeight = ChatPaginator.UNBOUNDED_PAGE_HEIGHT;
|
||||||
sender.sendMessage("gamemode <player> <mode> sets player\'s game mode (0 or 1)");
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ public class TimeCommand extends VanillaCommand {
|
||||||
super("time");
|
super("time");
|
||||||
this.description = "Changes the time on each world";
|
this.description = "Changes the time on each world";
|
||||||
this.usageMessage = "/time set <value>\n/time add <value>";
|
this.usageMessage = "/time set <value>\n/time add <value>";
|
||||||
|
this.setPermission("bukkit.command.time.add;bukkit.command.time.set");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -11,6 +11,7 @@ public class WhitelistCommand extends VanillaCommand {
|
||||||
super("whitelist");
|
super("whitelist");
|
||||||
this.description = "Prevents the specified player from using this server";
|
this.description = "Prevents the specified player from using this server";
|
||||||
this.usageMessage = "/whitelist (add|remove) <player>\n/whitelist (on|off|list|reload)";
|
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
|
@Override
|
||||||
|
|
44
paper-api/src/main/java/org/bukkit/help/HelpMap.java
Normal file
44
paper-api/src/main/java/org/bukkit/help/HelpMap.java
Normal file
|
@ -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);
|
||||||
|
}
|
90
paper-api/src/main/java/org/bukkit/help/HelpTopic.java
Normal file
90
paper-api/src/main/java/org/bukkit/help/HelpTopic.java
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
137
paper-api/src/main/java/org/bukkit/util/ChatPaginator.java
Normal file
137
paper-api/src/main/java/org/bukkit/util/ChatPaginator.java
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -99,7 +99,7 @@ public final class CommandPermissions {
|
||||||
DefaultPermissions.registerPermission(PREFIX + "kick", "Allows the user to kick players", PermissionDefault.OP, commands);
|
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 + "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 + "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 + "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 + "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);
|
DefaultPermissions.registerPermission(PREFIX + "version", "Allows the user to view the version of the server", PermissionDefault.TRUE, commands);
|
||||||
|
|
157
paper-api/src/test/java/org/bukkit/ChatPaginatorTest.java
Normal file
157
paper-api/src/test/java/org/bukkit/ChatPaginatorTest.java
Normal file
|
@ -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"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -556,6 +556,10 @@ public class TestPlayer implements Player {
|
||||||
throw new UnsupportedOperationException("Not supported yet.");
|
throw new UnsupportedOperationException("Not supported yet.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void sendMessage(String[] messages) {
|
||||||
|
throw new UnsupportedOperationException("Not supported yet.");
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isOnline() {
|
public boolean isOnline() {
|
||||||
throw new UnsupportedOperationException("Not supported yet.");
|
throw new UnsupportedOperationException("Not supported yet.");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue