Command API and Help done.

Commands now require a setup to define their permission, player/console
status, description and any parameters they have. This is also where any
subcommands are created if they exist.

Each command automatically has a help subcommand. This is used to
display help. This will also recursively go to any other sub commands
and get help from them. 

Note that getUsage() now *only* shows the command and any sub commands.
It turns out that Bukkit requires this to start with a / because it
actually uses this in its own help system and the server will not start
if it is not in the right format. Therefore I split off parameters into
their own string. This also enables them to be translatable. 

Everything should work at this point. It's just waiting on the locale
system to work to display the strings in the locale files.
This commit is contained in:
Tastybento 2017-12-27 12:09:08 -08:00
parent bdbe271a83
commit 2825f9535e
23 changed files with 186 additions and 107 deletions

View File

@ -33,6 +33,8 @@ commands:
end: "&7================================="
about: "display this help page"
admin:
help:
description: "Admin command"
version:
description: "display %bsb_plugin_name% and addons versions"
setrange:
@ -41,8 +43,10 @@ commands:
about:
description: "display info about %bsb_plugin_name%"
go:
usage: "<home number>"
parameters: "<home number>"
description: "teleport you to your island"
help:
description: "The main island command"
spawn:
description: "teleport you to the spawn"
create:
@ -58,12 +62,12 @@ commands:
must-be-on-your-island: "You must be on your island to set home!"
num-homes: "Homes can be 1 to [max]."
home-set: "Your island home has been set to your current location."
usage: "<home number>"
parameters: "<home number>"
setname:
description: "set a name for your island"
name-too-short: "Too short. Minimum size is [length] characters."
name-too-long: "Too long. Maximum size is [length] characters."
usage: "<name>"
parameters: "<name>"
resetname:
description: "reset your island name"
team:
@ -77,7 +81,7 @@ commands:
none-invited-you: "No one invited you :c."
you-already-are-in-team: "You are already on a team!"
invalid-invite: "That invite is no longer valid, sorry."
usage: "<player>"
parameters: "<player>"
you-can-invite: "You can invite [number] more players."
accept:
description: "accept an invitation"
@ -93,24 +97,24 @@ commands:
description: "leave your island"
kick:
description: "remove a member from your island"
usage: "<player>"
parameters: "<player>"
promote:
description: "promote a player on your island to another rank"
usage: "<player>"
parameters: "<player>"
setowner:
description: "transfer your island ownership to a member"
errors:
cant-transfer-to-yourself: "You can't transfer ownership to yourself! Well, in facts, you could have been able to do so... But we don't want you to do so. 'Cause it's bad."
target-is-not-member: "That player is not part of your island team!"
name-is-the-owner: "[name] is now the island owner!"
usage: "<player>"
parameters: "<player>"
you-are-the-owner: "You are now the island owner!"
ban:
description: "ban a player from your island"
usage: "<player>"
parameters: "<player>"
unban:
description: "unban a player from your island"
usage: "<player>"
parameters: "<player>"
banlist:
description: "list banned players"
lock:
@ -119,7 +123,7 @@ commands:
description: "display island settings"
language:
description: "select language"
usage: "<language>"
parameters: "<language>"
protection:
flags:

View File

@ -13,9 +13,9 @@ public interface BSBCommand {
/**
* Anything that needs to be set up for this command.
* This is where you register subcommands. This will be run if it exists.
* This is where you register subcommands and other settings
*/
default void setup() {};
public void setup();
/**
* What will be executed when this command is run

View File

@ -41,7 +41,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* The parent command to this one. If this is a top-level command it will be empty.
*/
private final Optional<CompositeCommand> parent;
protected final CompositeCommand parent;
/**
* The permission required to execute this command
*/
@ -51,9 +51,16 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
*/
private Map<String, CompositeCommand> subCommands;
/**
* The usage string for this command. It is the commands followed by a locale reference.
* The parameters string for this command. It is the commands followed by a locale reference.
*/
private String parameters = "";
/**
* The command chain from the very top, e.g., /island team promote
*/
private String usage;
/**
* BSkyBlock plugin
*/
private BSkyBlock bsb;
/**
@ -64,14 +71,13 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
*/
public CompositeCommand(CompositeCommand parent, String label, String... aliases) {
super(label);
this.parent = Optional.of(parent);
this.parent = parent;
this.subCommandLevel = parent.getLevel() + 1;
// Add this sub-command to the parent
parent.getSubCommands().put(label, this);
this.setAliases(new ArrayList<>(Arrays.asList(aliases)));
this.subCommands = new LinkedHashMap<>();
setUsage("");
setDescription("");
this.setup();
// If this command does not define its own help class, then use the default help command
if (!this.getSubCommand("help").isPresent() && !label.equals("help"))
@ -90,13 +96,14 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
public CompositeCommand(String label, String... string) {
super(label);
this.setAliases(new ArrayList<>(Arrays.asList(string)));
this.parent = Optional.empty();
this.parent = null;
setUsage("");
this.subCommandLevel = 0; // Top level
this.subCommands = new LinkedHashMap<>();
if (!label.equals("help"))
new DefaultHelpCommand(this);
this.setup();
if (!this.getSubCommand("help").isPresent() && !label.equals("help"))
new DefaultHelpCommand(this);
}
@ -204,7 +211,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* @return the parent command object
*/
public Optional<CompositeCommand> getParent() {
public CompositeCommand getParent() {
return parent;
}
@ -256,34 +263,41 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
protected UUID getTeamLeader(User user) {
return bsb.getIslands().getTeamLeader(user.getUniqueId());
}
public void setparameters(String parameters) {
this.setParameters(parameters);
}
@Override
public String getUsage() {
return usage;
}
/**
* This creates the full linking chain of commands
*/
@Override
public Command setUsage(String usage) {
// Go up the chain
Optional<CompositeCommand> parent = this.getParent();
CompositeCommand parent = this.getParent();
this.usage = this.getLabel() + " " + usage;
while (parent.isPresent()) {
this.usage = parent.get().getLabel() + " " + this.usage;
parent = parent.get().getParent();
while (parent != null) {
this.usage = parent.getLabel() + " " + this.usage;
parent = parent.getParent();
}
this.usage = "/" + this.usage;
this.usage = this.usage.trim();
return this;
}
/**
* Get usage for sub commands
* @param subCommands
* @return
*/
public String getUsage(String... subCommands) {
CompositeCommand subCommand = this.getCommandFromArgs(subCommands);
return subCommand.getUsage();
public String getParameters() {
return parameters;
}
public void setParameters(String parameters) {
this.parameters = parameters;
}
/**

View File

@ -1,6 +1,5 @@
package us.tastybento.bskyblock.api.commands;
import java.util.ArrayList;
import java.util.List;
/**
@ -11,45 +10,47 @@ import java.util.List;
*/
public class DefaultHelpCommand extends CompositeCommand {
private CompositeCommand parent;
public DefaultHelpCommand(CompositeCommand parent) {
super(parent, "help");
this.parent = parent;
}
@Override
public void setup() {
// Set the usage to what the parent's command is
this.setParameters(parent.getParameters());
this.setDescription(parent.getDescription());
}
@Override
public boolean execute(User user, List<String> args) {
if (args.isEmpty()) {
// Show the top level help
if (user.isPlayer()) {
// Player. Check perms
if (user.hasPermission(parent.getPermission())) {
user.sendMessage((parent.getUsage().isEmpty() ? "" : parent.getUsage() + " ") + parent.getDescription());
} else {
user.sendMessage("errors.no-permission");
return true;
if (args.isEmpty()) {
if (!parent.getLabel().equals("help")) {
// Get elements
String params = getParameters().isEmpty() ? "" : user.getTranslation(getParameters()) + " ";
String desc = getDescription().isEmpty() ? "" : user.getTranslation(getDescription());
// Show the help
if (user.isPlayer()) {
// Player. Check perms
if (user.hasPermission(parent.getPermission())) {
user.sendLegacyMessage(parent.getUsage() + " " + params + desc);
} else {
// No permission, nothing to see here. If you don't have permission, you cannot see any sub commands
return true;
}
} else if (!parent.isOnlyPlayer()) {
// Console. Only show if it is a console command
user.sendLegacyMessage(parent.getUsage() + " " + params + desc);
}
} else if (!parent.isOnlyPlayer()) {
// Console. Only show if it is a console command
user.sendMessage((parent.getUsage().isEmpty() ? "" : parent.getUsage() + " ") + parent.getDescription());
}
// Run through any subcommands
// Run through any subcommands and get their help
for (CompositeCommand subCommand : parent.getSubCommands().values()) {
// Ignore the help command
if (!subCommand.getLabel().equals("help")) {
String usage = subCommand.getUsage();
String desc = subCommand.getDescription();
if (user.isPlayer()) {
// Player. Check perms
if (user.hasPermission(parent.getPermission()) && user.hasPermission(subCommand.getPermission())) {
user.sendMessage((usage.isEmpty() ? "" : usage + " ") + desc);
}
} else if (!subCommand.isOnlyPlayer()) {
user.sendMessage((usage.isEmpty() ? "" : usage + " ") + desc);
}
// Every command should have help because every command has a default help
if (subCommand.getSubCommand("help").isPresent()) {
// This sub-sub command has a help, so use it
subCommand.getSubCommand("help").get().execute(user, args);
}
}
}
}

View File

@ -147,6 +147,12 @@ public class User {
return sender.isOp();
}
/**
* Gets a translation for this user
* @param reference
* @param variables
* @return
*/
public String getTranslation(String reference, String... variables) {
String translation = plugin.getLocalesManager().get(sender, reference);
if (variables.length > 1) {

View File

@ -18,8 +18,6 @@ public class IslandCommand extends CompositeCommand {
public IslandCommand() {
super(Settings.ISLANDCOMMAND, "is");
this.setUsage("island.usage");
this.setOnlyPlayer(true);
}
/* (non-Javadoc)
@ -27,6 +25,8 @@ public class IslandCommand extends CompositeCommand {
*/
@Override
public void setup() {
this.setDescription("island.help.description");
this.setOnlyPlayer(true);
// Permission
this.setPermission(Settings.PERMPREFIX + "island");
// Set up subcommands

View File

@ -10,6 +10,10 @@ public class AdminVersionCommand extends CompositeCommand {
public AdminVersionCommand(CompositeCommand adminCommand) {
super(adminCommand, "version");
}
@Override
public void setup() {
// Permission
this.setPermission(Settings.PERMPREFIX + "admin.version");
}

View File

@ -14,9 +14,14 @@ public class IslandAboutCommand extends CompositeCommand {
*/
public IslandAboutCommand(CompositeCommand islandCommand) {
super(islandCommand, "about", "ab");
}
@Override
public void setup() {
this.setDescription("commands.island.about.description");
}
@Override
public boolean execute(User user, List<String> args) {
user.sendLegacyMessage("About " + BSkyBlock.getPlugin().getDescription().getName() + " v" + BSkyBlock.getPlugin().getDescription().getVersion() + ":");

View File

@ -21,6 +21,10 @@ public class IslandCreateCommand extends CompositeCommand {
public IslandCreateCommand(IslandCommand islandCommand) {
super(islandCommand, "create", "auto");
}
@Override
public void setup() {
this.setPermission(Settings.PERMPREFIX + "island.create");
this.setOnlyPlayer(true);
this.setDescription("commands.island.create.description");

View File

@ -22,14 +22,13 @@ public class IslandGoCommand extends CompositeCommand {
public IslandGoCommand(IslandCommand islandCommand) {
super(islandCommand, "go", "home", "h");
this.setPermission(Settings.PERMPREFIX + "island.home");
this.setOnlyPlayer(true);
this.setUsage("commands.island.go.usage");
this.setDescription("commands.island.go.description");
}
@Override
public void setup() {
this.setPermission(Settings.PERMPREFIX + "island.home");
this.setOnlyPlayer(true);
this.setDescription("commands.island.go.description");
new IslandMultiHomeHelp(this);
}

View File

@ -15,29 +15,35 @@ import us.tastybento.bskyblock.util.Util;
*/
public class IslandMultiHomeHelp extends CompositeCommand {
private CompositeCommand parent;
public IslandMultiHomeHelp(CompositeCommand parent) {
super(parent, "help");
this.parent = parent;
this.setOnlyPlayer(true);
super(parent, "help");
}
@Override
public void setup() {
this.setOnlyPlayer(true);
this.setParameters(parent.getParameters());
this.setDescription(parent.getDescription());
this.setPermission(parent.getPermission());
}
@Override
public boolean execute(User user, List<String> args) {
if (user.isPlayer()) {
user.sendLegacyMessage("DEBUG: Custom help");
// Get elements
String params = getParameters().isEmpty() ? "" : user.getTranslation(getParameters()) + " ";
String desc = getDescription().isEmpty() ? "" : user.getTranslation(getDescription());
// Player. Check perms
if (user.hasPermission(parent.getPermission())) {
if (user.hasPermission(getPermission())) {
int maxHomes = Util.getPermValue(user.getPlayer(), Settings.PERMPREFIX + "island.maxhomes", Settings.maxHomes);
if (maxHomes > 1) {
user.sendMessage((parent.getUsage().isEmpty() ? "" : parent.getUsage() + " ") + parent.getDescription());
user.sendLegacyMessage(parent.getUsage() + " " + params + desc);
} else {
user.sendMessage(parent.getDescription());
// No params
user.sendLegacyMessage(parent.getUsage() + " " + desc);
}
return true;
} else {
user.sendMessage("errors.no-permission");
return true;
}

View File

@ -19,6 +19,10 @@ public class IslandResetCommand extends CompositeCommand {
public IslandResetCommand(CompositeCommand islandCommand) {
super(islandCommand, "reset", "restart");
}
@Override
public void setup() {
this.setPermission(Settings.PERMPREFIX + "island.create");
this.setOnlyPlayer(true);
this.setDescription("commands.island.reset.description");

View File

@ -18,9 +18,12 @@ public class IslandResetnameCommand extends CompositeCommand {
public IslandResetnameCommand(CompositeCommand islandCommand) {
super(islandCommand, "resetname");
}
@Override
public void setup() {
this.setPermission(Settings.PERMPREFIX + "island.name");
this.setOnlyPlayer(true);
this.setUsage("commands.island.resetname.usage");
this.setDescription("commands.island.resetname.description");
}

View File

@ -12,14 +12,13 @@ public class IslandSethomeCommand extends CompositeCommand {
public IslandSethomeCommand(CompositeCommand islandCommand) {
super(islandCommand, "sethome");
this.setPermission(Settings.PERMPREFIX + "island.sethome");
this.setOnlyPlayer(true);
this.setUsage("commands.island.sethome.usage");
this.setDescription("commands.island.sethome.description");
}
@Override
public void setup() {
this.setPermission(Settings.PERMPREFIX + "island.sethome");
this.setOnlyPlayer(true);
this.setDescription("commands.island.sethome.description");
new IslandMultiHomeHelp(this);
}

View File

@ -22,9 +22,13 @@ public class IslandSetnameCommand extends CompositeCommand {
public IslandSetnameCommand(CompositeCommand islandCommand) {
super(islandCommand, "setname");
}
@Override
public void setup() {
this.setPermission(Settings.PERMPREFIX + "island.name");
this.setOnlyPlayer(true);
this.setUsage("commands.island.setname.usage");
this.setParameters("commands.island.setname.parameters");
this.setDescription("commands.island.setname.description");
}

View File

@ -20,13 +20,14 @@ public class IslandTeamCommand extends AbstractIslandTeamCommand {
public IslandTeamCommand(CompositeCommand islandCommand) {
super(islandCommand, "team");
this.setPermission(Settings.PERMPREFIX + "island.team");
this.setOnlyPlayer(true);
this.setDescription("commands.island.team.description");
}
@Override
public void setup() {
this.setPermission(Settings.PERMPREFIX + "island.team");
this.setOnlyPlayer(true);
this.setDescription("commands.island.team.description");
new IslandTeamInviteCommand(this);
new IslandTeamLeaveCommand(this);
new IslandTeamPromoteCommand(this);

View File

@ -18,9 +18,12 @@ public class IslandTeamInviteAcceptCommand extends AbstractIslandTeamCommand {
public IslandTeamInviteAcceptCommand(IslandTeamInviteCommand islandTeamInviteCommand) {
super(islandTeamInviteCommand, "accept");
}
@Override
public void setup() {
this.setPermission(Settings.PERMPREFIX + "island.team");
this.setOnlyPlayer(true);
this.setUsage("commands.island.team.invite.accept.usage");
this.setDescription("commands.island.team.invite.accept.description");
}

View File

@ -22,14 +22,14 @@ public class IslandTeamInviteCommand extends AbstractIslandTeamCommand {
public IslandTeamInviteCommand(IslandTeamCommand islandTeamCommand) {
super(islandTeamCommand, "invite");
this.setPermission(Settings.PERMPREFIX + "island.team");
this.setOnlyPlayer(true);
this.setUsage("commands.island.team.invite.usage");
this.setDescription("commands.island.team.invite.description");
}
@Override
public void setup() {
this.setPermission(Settings.PERMPREFIX + "island.team");
this.setOnlyPlayer(true);
this.setDescription("commands.island.team.invite.description");
new IslandTeamInviteAcceptCommand(this);
new IslandTeamInviteRejectCommand(this);
}

View File

@ -13,9 +13,12 @@ public class IslandTeamInviteRejectCommand extends AbstractIslandTeamCommand {
public IslandTeamInviteRejectCommand(IslandTeamInviteCommand islandTeamInviteCommand) {
super(islandTeamInviteCommand, "reject");
}
@Override
public void setup() {
this.setPermission(Settings.PERMPREFIX + "island.team");
this.setOnlyPlayer(true);
this.setUsage("commands.island.team.invite.reject.usage");
this.setDescription("commands.island.team.invite.reject.description");
}

View File

@ -9,9 +9,13 @@ public class IslandTeamLeaveCommand extends AbstractIslandTeamCommand {
public IslandTeamLeaveCommand(IslandTeamCommand islandTeamCommand) {
super(islandTeamCommand, "leave");
}
@Override
public void setup() {
this.setPermission(Settings.PERMPREFIX + "island.team");
this.setOnlyPlayer(true);
this.setUsage("command.island.team.leave.usage");
this.setParameters("command.island.team.leave.parameters");
this.setDescription("commands.island.team.leave.description");
}

View File

@ -9,9 +9,13 @@ public class IslandTeamPromoteCommand extends AbstractIslandTeamCommand {
public IslandTeamPromoteCommand(IslandTeamCommand islandTeamCommand) {
super(islandTeamCommand, "promote");
}
@Override
public void setup() {
this.setPermission(Settings.PERMPREFIX + "island.team");
this.setOnlyPlayer(true);
this.setUsage("island.team.promote.usage");
this.setParameters("island.team.promote.parameters");
this.setDescription("commands.island.team.promote.description");
}

View File

@ -21,9 +21,13 @@ public class IslandTeamSetownerCommand extends AbstractIslandTeamCommand {
public IslandTeamSetownerCommand(IslandTeamCommand islandTeamCommand) {
super(islandTeamCommand, "setleader");
}
@Override
public void setup() {
this.setPermission(Settings.PERMPREFIX + "island.team");
this.setOnlyPlayer(true);
this.setUsage("commands.island.team.setowner.usage");
this.setParameters("commands.island.team.setowner.parameters");
this.setDescription("commands.island.team.setowner.description");
}

View File

@ -3,6 +3,7 @@ package bskyblock;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
@ -87,7 +88,7 @@ public class TestBSkyBlock {
assertEquals(2, testCommand.getAliases().size());
assertEquals("t", testCommand.getAliases().get(0));
assertTrue(testCommand.isOnlyPlayer());
assertEquals(testCommand.getParent(), Optional.empty());
assertNull(testCommand.getParent());
assertEquals(Settings.PERMPREFIX + "default.permission", testCommand.getPermission());
// Check commands and aliases match to correct class
for (Entry<String, CompositeCommand> command : testCommand.getSubCommands().entrySet()) {
@ -98,8 +99,8 @@ public class TestBSkyBlock {
}
}
String[] args = {""};
assertEquals(Arrays.asList("help", "sub1","sub2"), testCommand.tabComplete(player, "test", args));
assertNotSame(Arrays.asList("help", "sub1","sub2"), testCommand.tabComplete(sender, "test", args));
assertEquals(Arrays.asList("sub1","sub2", "help"), testCommand.tabComplete(player, "test", args));
assertNotSame(Arrays.asList("sub1","sub2", "help"), testCommand.tabComplete(sender, "test", args));
args[0] = "su";
assertEquals(Arrays.asList("sub1","sub2"), testCommand.tabComplete(player, "test", args));
args[0] = "d";
@ -130,11 +131,8 @@ public class TestBSkyBlock {
assertTrue(testCommand.execute(player, "test", new String[] {"sub2", "subsub", "subsubsub", "ben", "100", "today"}));
// Usage tests
assertEquals("/test test.usage", testCommand.getUsage());
assertEquals("/test test.usage", testCommand.getUsage("sdfsd"));
assertEquals("/test sub1 sub.usage", testCommand.getUsage("sub1"));
// If the usage has not been defined, then it just shows the command
assertEquals("/test sub2 subsub", testCommand.getUsage("sub2", "subsub"));
assertEquals("/test", testCommand.getUsage());
assertEquals("test.params", testCommand.getParameters());
// Test help
//assertTrue(testCommand.execute(player, "test", new String[] {"help"}));
@ -144,7 +142,7 @@ public class TestBSkyBlock {
public TestCommand() {
super("test", "t", "tt");
this.setUsage("test.usage");
this.setParameters("test.params");
}
@Override
@ -165,7 +163,11 @@ public class TestBSkyBlock {
public TestSubCommand(CompositeCommand parent) {
super(parent, "sub1", "subone");
this.setUsage("sub.usage");
}
@Override
public void setup() {
this.setParameters("sub.params");
}
@Override
@ -218,8 +220,10 @@ public class TestBSkyBlock {
public TestSubSubSubCommand(CompositeCommand parent) {
super(parent, "subsubsub", "level3", "subsubsubby");
}
@Override
public void setup() {}
@Override
public boolean execute(User user, List<String> args) {
@ -242,6 +246,9 @@ public class TestBSkyBlock {
public Test3ArgsCommand() {
super("args", "");
}
@Override
public void setup() {}
@Override
public boolean execute(User user, List<String> args) {