[Bleeding] Added Help API. Addresses BUKKIT-863

This commit is contained in:
rmichela 2012-03-01 00:19:11 -05:00 committed by EvilSeph
parent c73adc9533
commit 818a61040e
15 changed files with 516 additions and 2 deletions

View File

@ -58,6 +58,7 @@ import org.bukkit.command.SimpleCommandMap;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.configuration.serialization.ConfigurationSerialization; import org.bukkit.configuration.serialization.ConfigurationSerialization;
import org.bukkit.craftbukkit.help.SimpleHelpMap;
import org.bukkit.craftbukkit.inventory.CraftFurnaceRecipe; import org.bukkit.craftbukkit.inventory.CraftFurnaceRecipe;
import org.bukkit.craftbukkit.inventory.CraftInventoryCustom; import org.bukkit.craftbukkit.inventory.CraftInventoryCustom;
import org.bukkit.craftbukkit.inventory.CraftRecipe; import org.bukkit.craftbukkit.inventory.CraftRecipe;
@ -81,6 +82,7 @@ import org.bukkit.event.world.WorldLoadEvent;
import org.bukkit.event.world.WorldSaveEvent; import org.bukkit.event.world.WorldSaveEvent;
import org.bukkit.event.world.WorldUnloadEvent; import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator;
import org.bukkit.help.HelpMap;
import org.bukkit.inventory.FurnaceRecipe; import org.bukkit.inventory.FurnaceRecipe;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
@ -126,6 +128,7 @@ public final class CraftServer implements Server {
private final ServicesManager servicesManager = new SimpleServicesManager(); private final ServicesManager servicesManager = new SimpleServicesManager();
private final BukkitScheduler scheduler = new CraftScheduler(); private final BukkitScheduler scheduler = new CraftScheduler();
private final SimpleCommandMap commandMap = new SimpleCommandMap(this); private final SimpleCommandMap commandMap = new SimpleCommandMap(this);
private final SimpleHelpMap helpMap = new SimpleHelpMap();
private final StandardMessenger messenger = new StandardMessenger(); private final StandardMessenger messenger = new StandardMessenger();
private final PluginManager pluginManager = new SimplePluginManager(this, commandMap); private final PluginManager pluginManager = new SimplePluginManager(this, commandMap);
protected final MinecraftServer console; protected final MinecraftServer console;
@ -215,6 +218,11 @@ public final class CraftServer implements Server {
} }
public void enablePlugins(PluginLoadOrder type) { public void enablePlugins(PluginLoadOrder type) {
if (type == PluginLoadOrder.STARTUP) {
helpMap.clear();
helpMap.initializeGeneralTopics(this);
}
Plugin[] plugins = pluginManager.getPlugins(); Plugin[] plugins = pluginManager.getPlugins();
for (Plugin plugin : plugins) { for (Plugin plugin : plugins) {
@ -227,6 +235,7 @@ public final class CraftServer implements Server {
commandMap.registerServerAliases(); commandMap.registerServerAliases();
loadCustomPermissions(); loadCustomPermissions();
DefaultPermissions.registerCorePermissions(); DefaultPermissions.registerCorePermissions();
helpMap.initializeCommands(this);
} }
} }
@ -1122,4 +1131,12 @@ public final class CraftServer implements Server {
Validate.isTrue(size % 9 == 0, "Chests must have a size that is a multiple of 9!"); Validate.isTrue(size % 9 == 0, "Chests must have a size that is a multiple of 9!");
return new CraftInventoryCustom(owner, size, title); return new CraftInventoryCustom(owner, size, title);
} }
public HelpMap getHelpMap() {
return helpMap;
}
public SimpleCommandMap getCommandMap() {
return commandMap;
}
} }

View File

@ -16,6 +16,12 @@ public class CraftConsoleCommandSender extends ServerCommandSender implements Co
System.out.println(ChatColor.stripColor(message)); System.out.println(ChatColor.stripColor(message));
} }
public void sendMessage(String[] messages) {
for (String message : messages) {
sendMessage(message);
}
}
public String getName() { public String getName() {
return "CONSOLE"; return "CONSOLE";
} }

View File

@ -12,6 +12,12 @@ public class CraftRemoteConsoleCommandSender extends ServerCommandSender impleme
RemoteControlCommandListener.instance.sendMessage(message + "\n"); // Send a newline after each message, to preserve formatting. RemoteControlCommandListener.instance.sendMessage(message + "\n"); // Send a newline after each message, to preserve formatting.
} }
public void sendMessage(String[] messages) {
for (String message : messages) {
sendMessage(message);
}
}
public String getName() { public String getName() {
return "Rcon"; return "Rcon";
} }

View File

@ -136,6 +136,12 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
this.sendRawMessage(message); this.sendRawMessage(message);
} }
public void sendMessage(String[] messages) {
for (String message : messages) {
sendMessage(message);
}
}
public String getDisplayName() { public String getDisplayName() {
return getHandle().displayName; return getHandle().displayName;
} }

View File

@ -0,0 +1,32 @@
package org.bukkit.craftbukkit.help;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.help.HelpTopic;
/**
* This is a help topic implementation for general topics registered in the help.yml file.
*/
public class CustomHelpTopic extends HelpTopic {
private String permissionNode;
public CustomHelpTopic(String name, String shortText, String fullText, String permissionNode) {
this.permissionNode = permissionNode;
this.name = name;
this.shortText = shortText;
this.fullText = shortText + "\n" + fullText;
}
public boolean canSee(CommandSender sender) {
if (sender instanceof ConsoleCommandSender) {
return true;
}
if (!permissionNode.equals("")) {
return sender.hasPermission(permissionNode);
} else {
return true;
}
}
}

View File

@ -0,0 +1,57 @@
package org.bukkit.craftbukkit.help;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.help.HelpTopic;
import org.bukkit.util.ChatPaginator;
import java.util.Collection;
/**
* This help topic generates the list of all other help topics.
*/
public class DefaultHelpTopic extends HelpTopic {
private Collection<HelpTopic> allTopics;
public DefaultHelpTopic(Collection<HelpTopic> allTopics) {
this.allTopics = allTopics;
}
public boolean canSee(CommandSender sender) {
return true;
}
public String getName() {
return "Overall";
}
public String getShortText() {
return "";
}
public String getFullText(CommandSender sender) {
StringBuilder sb = new StringBuilder();
for (HelpTopic topic : allTopics) {
if (topic.canSee(sender)) {
StringBuilder line = new StringBuilder();
line.append(ChatColor.GOLD);
line.append(topic.getName());
line.append(": ");
line.append(ChatColor.WHITE);
line.append(topic.getShortText());
String lineStr = line.toString().replace("\n", ". ");
if (sender instanceof Player && lineStr.length() > ChatPaginator.AVERAGE_CHAT_PAGE_WIDTH) {
sb.append(lineStr.substring(0, ChatPaginator.AVERAGE_CHAT_PAGE_WIDTH - 3));
sb.append("...");
} else {
sb.append(lineStr);
}
sb.append("\n");
}
}
return sb.toString();
}
}

View File

@ -0,0 +1,70 @@
package org.bukkit.craftbukkit.help;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.apache.commons.lang.StringUtils;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.command.defaults.VanillaCommand;
import org.bukkit.help.HelpTopic;
public class GenericCommandHelpTopic extends HelpTopic {
private Command command;
public GenericCommandHelpTopic(Command command) {
this.command = command;
if (command.getLabel().startsWith("/")) {
name = command.getLabel();
} else {
name = "/" + command.getLabel();
}
// The short text is the first line of the description
int i = command.getDescription().indexOf("\n");
if (i > 1) {
shortText = command.getDescription().substring(0, i - 1);
} else {
shortText = command.getDescription();
}
// Build full text
StringBuffer sb = new StringBuffer();
sb.append(ChatColor.GOLD);
sb.append("Description: ");
sb.append(ChatColor.WHITE);
sb.append(command.getDescription());
sb.append("\n");
sb.append(ChatColor.GOLD);
sb.append("Usage: ");
sb.append(ChatColor.WHITE);
sb.append(command.getUsage().replace("<command>", name.substring(1)));
if (command.getAliases().size() > 0) {
sb.append("\n");
sb.append(ChatColor.GOLD);
sb.append("Aliases: ");
sb.append(ChatColor.WHITE);
sb.append(ChatColor.WHITE + StringUtils.join(command.getAliases(), ", "));
}
fullText = sb.toString();
}
public boolean canSee(CommandSender sender) {
if (!command.isRegistered() && !(command instanceof VanillaCommand)) {
// Unregistered commands should not show up in the help (ignore VanillaCommands)
return false;
}
if (sender instanceof ConsoleCommandSender) {
return true;
}
return command.testPermissionSilent(sender);
}
}

View File

@ -0,0 +1,40 @@
package org.bukkit.craftbukkit.help;
/**
* A HelpTopicAmendment represents the contents of a topic amendment from the help.yml
*/
public class HelpTopicAmendment {
private String topicName;
private String shortText;
private String fullText;
public HelpTopicAmendment(String topicName, String shortText, String fullText) {
this.fullText = fullText;
this.shortText = shortText;
this.topicName = topicName;
}
/**
* Gets the amended full text
* @return the full text
*/
public String getFullText() {
return fullText;
}
/**
* Gets the amended short text
* @return the short text
*/
public String getShortText() {
return shortText;
}
/**
* Gets the name of the topic being amended
* @return the topic name
*/
public String getTopicName() {
return topicName;
}
}

View File

@ -0,0 +1,19 @@
package org.bukkit.craftbukkit.help;
import java.util.Comparator;
/**
* Used to impose a custom total ordering on help topics. All topics are listed in alphabetic order, but topics
* that start with a slash come after topics that don't.
*/
public class HelpTopicComparator implements Comparator<String> {
public int compare(String lhs, String rhs) {
if (lhs.startsWith("/") && !rhs.startsWith("/")) {
return 1;
} else if (!lhs.startsWith("/") && rhs.startsWith("/")) {
return -1;
} else {
return lhs.compareToIgnoreCase(rhs);
}
}
}

View File

@ -0,0 +1,72 @@
package org.bukkit.craftbukkit.help;
import org.bukkit.Server;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.help.HelpTopic;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
/**
* HelpYamlReader is responsible for processing the contents of the help.yml file.
*/
public class HelpYamlReader {
private YamlConfiguration helpYaml;
public HelpYamlReader(Server server) {
File helpYamlFile = new File("help.yml");
helpYaml = YamlConfiguration.loadConfiguration(helpYamlFile);
helpYaml.options().copyDefaults(true);
helpYaml.setDefaults(YamlConfiguration.loadConfiguration(getClass().getClassLoader().getResourceAsStream("configurations/help.yml")));
try {
if (!helpYamlFile.exists()) {
helpYaml.save(helpYamlFile);
}
} catch (IOException ex) {
server.getLogger().log(Level.SEVERE, "Could not save " + helpYamlFile, ex);
}
}
/**
* Extracts a list of all general help topics from help.yml
* @return A list of general topics.
*/
public List<HelpTopic> getGeneralTopics() {
List<HelpTopic> topics = new LinkedList<HelpTopic>();
ConfigurationSection generalTopics = helpYaml.getConfigurationSection("general-topics");
if (generalTopics != null) {
for (String topicName : generalTopics.getKeys(false)) {
ConfigurationSection section = generalTopics.getConfigurationSection(topicName);
String shortText = section.getString("shortText");
String fullText = section.getString("fullText");
String permission = section.getString("permission");
topics.add(new CustomHelpTopic(topicName, shortText, fullText, permission));
}
}
return topics;
}
/**
* Extracts a list of topic amendments from help.yml
* @return A list of amendments
*/
public List<HelpTopicAmendment> getTopicAmendments() {
List<HelpTopicAmendment> amendments = new LinkedList<HelpTopicAmendment>();
ConfigurationSection commandTopics = helpYaml.getConfigurationSection("amended-topics");
if (commandTopics != null) {
for (String topicName : commandTopics.getKeys(false)) {
ConfigurationSection section = commandTopics.getConfigurationSection(topicName);
String description = section.getString("shortText");
String usage = section.getString("fullText");
amendments.add(new HelpTopicAmendment(topicName, description, usage));
}
}
return amendments;
}
}

View File

@ -0,0 +1,50 @@
package org.bukkit.craftbukkit.help;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.command.MultipleCommandAlias;
import org.bukkit.help.HelpTopic;
/**
* This is a help topic implementation for {@link MultipleCommandAlias} commands.
*/
public class MultipleCommandAliasHelpTopic extends HelpTopic {
private MultipleCommandAlias alias;
public MultipleCommandAliasHelpTopic(MultipleCommandAlias alias) {
this.alias = alias;
name = "/" + alias.getLabel();
// Build short text
StringBuilder sb = new StringBuilder();
for (int i = 0; i < alias.getCommands().length; i++) {
if (i != 0) {
sb.append(ChatColor.GOLD + " > " + ChatColor.WHITE);
}
sb.append("/");
sb.append(alias.getCommands()[i].getLabel());
}
shortText = sb.toString();
// Build full text
fullText = ChatColor.GOLD + "Alias for: " + ChatColor.WHITE + getShortText();
}
public boolean canSee(CommandSender sender) {
if (sender instanceof ConsoleCommandSender) {
return true;
}
for (Command command : alias.getCommands()) {
if (!command.testPermissionSilent(sender)) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,15 @@
package org.bukkit.craftbukkit.help;
import org.bukkit.command.MultipleCommandAlias;
import org.bukkit.help.HelpTopic;
import org.bukkit.help.HelpTopicFactory;
/**
* This class creates {@link MultipleCommandAliasHelpTopic} help topics from {@link MultipleCommandAlias} commands.
*/
public class MultipleCommandAliasHelpTopicFactory implements HelpTopicFactory<MultipleCommandAlias> {
public HelpTopic createTopic(MultipleCommandAlias multipleCommandAlias) {
return new MultipleCommandAliasHelpTopic(multipleCommandAlias);
}
}

View File

@ -0,0 +1,102 @@
package org.bukkit.craftbukkit.help;
import org.bukkit.command.Command;
import org.bukkit.command.MultipleCommandAlias;
import org.bukkit.command.defaults.VanillaCommand;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.help.HelpMap;
import org.bukkit.help.HelpTopic;
import org.bukkit.help.HelpTopicFactory;
import java.util.*;
/**
* Standard implementation of {@link HelpMap} for CraftBukkit servers.
*/
public class SimpleHelpMap implements HelpMap {
private HelpTopic defaultTopic;
private Map<String, HelpTopic> helpTopics;
private Map<Class, HelpTopicFactory> topicFactoryMap;
public SimpleHelpMap() {
helpTopics = new TreeMap<String, HelpTopic>(new HelpTopicComparator()); // Using a TreeMap for its explicit sorting on key
defaultTopic = new DefaultHelpTopic(helpTopics.values());
topicFactoryMap = new HashMap<Class, HelpTopicFactory>();
registerHelpTopicFactory(MultipleCommandAlias.class, new MultipleCommandAliasHelpTopicFactory());
}
public synchronized HelpTopic getHelpTopic(String topicName) {
if (topicName.equals("")) {
return defaultTopic;
}
if (helpTopics.containsKey(topicName)) {
return helpTopics.get(topicName);
}
return null;
}
public synchronized void addTopic(HelpTopic topic) {
// Existing topics take priority
if (!helpTopics.containsKey(topic.getName())) {
helpTopics.put(topic.getName(), topic);
}
}
public synchronized void clear() {
helpTopics.clear();
}
/**
* Reads the general topics from help.yml and adds them to the help index.
* @param server A reference to the server.
*/
public synchronized void initializeGeneralTopics(CraftServer server) {
HelpYamlReader reader = new HelpYamlReader(server);
// Initialize general help topics from the help.yml file
for (HelpTopic topic : reader.getGeneralTopics()) {
addTopic(topic);
}
}
/**
* Processes all the commands registered in the server and creates help topics for them.
* @param server A reference to the server.
*/
public synchronized void initializeCommands(CraftServer server) {
// ** Load topics from highest to lowest priority order **
// Initialize help topics from the server's command map
for (Command command : server.getCommandMap().getCommands()) {
if (topicFactoryMap.containsKey(command.getClass())) {
addTopic(topicFactoryMap.get(command.getClass()).createTopic(command));
} else {
addTopic(new GenericCommandHelpTopic(command));
}
}
// Initialize help topics from the server's fallback commands
for (VanillaCommand command : server.getCommandMap().getFallbackCommands()) {
addTopic(new GenericCommandHelpTopic(command));
}
// Amend help topics from the help.yml file
HelpYamlReader reader = new HelpYamlReader(server);
for (HelpTopicAmendment amendment : reader.getTopicAmendments()) {
if (helpTopics.containsKey(amendment.getTopicName())) {
helpTopics.get(amendment.getTopicName()).amendTopic(amendment.getShortText(), amendment.getFullText());
}
}
}
public void registerHelpTopicFactory(Class commandClass, HelpTopicFactory factory) {
if (!Command.class.isAssignableFrom(commandClass)) {
throw new IllegalArgumentException("commandClass must implement Command");
}
topicFactoryMap.put(commandClass, factory);
}
}

View File

@ -32,8 +32,8 @@ auto-updater:
preferred-channel: rb preferred-channel: rb
host: dl.bukkit.org host: dl.bukkit.org
aliases: aliases:
icanhasbukkit: # icanhasbukkit:
- version # - version
database: database:
username: bukkit username: bukkit
isolation: SERIALIZABLE isolation: SERIALIZABLE

View File

@ -0,0 +1,22 @@
# This is the help configuration file for Bukkit.
# By default you do not need to modify this file. Help topics for all plugin commands are automatically provided by
# or extracted from your installed plugins. You only need to modify this file if you wish to add new help pages to
# your server or override the help pages of existing plugin commands.
# --
# This file is divided up into two parts: general-topics and command-topics respectively. Examples are given below.
# Color codes are allowed. When amending command topic, the string <text> will be replaced wit the existing value
# in the help topic.
# --
# general-topics:
# rules:
# shortText: Rules of the server
# fullText: |
# 1. Be kind to your fellow players.
# 2. No griefing.
# 3. No swearing.
# permission: topics.rules
# --
# amended-topics:
# /stop:
# shortText: Stops the server cold....in its tracks!
# fullText: <text> - This kills the server.