Skip to content

Commit

Permalink
Added "assert" command tag, created ArgType as an alias for CommandAr…
Browse files Browse the repository at this point in the history
…gumentType, made CommandCollection merge multiple base commands with the same names into a single command that will attempt to run all of the commands until one works. This behavior is now consistent with child commands.
  • Loading branch information
boxbeam committed May 28, 2020
1 parent 1536676 commit 0f854a4
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 14 deletions.
4 changes: 3 additions & 1 deletion res/messages.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ firstLocationSet: &aFirst location set!
secondLocationSet: &aSecond location set!
cancelPromptMessage: &cType '%canceltext%' to cancel.
cancelText: cancel
mustHoldItem: &cYou must be holding an item to do this!
mustHoldItem: &cYou must be holding an item to do this!
playerOnly: &cThis command can only be executed as a player!
consoleOnly: &cThis command can only be executed as console!
34 changes: 34 additions & 0 deletions src/redempt/redlib/commandmanager/ArgType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package redempt.redlib.commandmanager;

import org.bukkit.command.CommandSender;
import redempt.redlib.commandmanager.Command.CommandArgumentType;

import java.util.function.BiFunction;
import java.util.function.Function;

/**
* The name for the CommandArgumentType class is rather long. This class exists only
* as an alias for CommandArgumentType to save you a little bit of time.
* @param <T> The type this ArgType represents
*/
public class ArgType<T> extends CommandArgumentType<T> {

/**
* Create a CommandArgumentType from a name and converter
* @param name The name of this command argument type, to be used in the command file
* @param convert The {@link Function} to convert from a String to whatever type this converts to
*/
public ArgType(String name, Function<String, T> convert) {
super(name, convert);
}

/**
* Create a CommandArgumentType from a name and converter
* @param name The name of this command argument type, to be used in the command file
* @param convert The {@link BiFunction} to convert from a String to whatever type this converts to
*/
public ArgType(String name, BiFunction<CommandSender, String, T> convert) {
super(name, convert);
}

}
53 changes: 42 additions & 11 deletions src/redempt/redlib/commandmanager/Command.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,21 +53,25 @@ public class Command {

private CommandArgument[] args;
private ContextProvider<?>[] contextProviders;
private String[] names;
private ContextProvider<?>[] asserters;
protected String[] names;
private String permission;
private SenderType type;
protected String hook;
private Method methodHook;
private String help;
protected String help;
private Object listener;
protected boolean topLevel = false;
protected Command parent = null;
private boolean hideSub = false;

protected Command(String[] names, CommandArgument[] args, ContextProvider<?>[] providers, String help, String permission, SenderType type, String hook, List<Command> children, boolean hideSub) {
protected Command() {}

protected Command(String[] names, CommandArgument[] args, ContextProvider<?>[] providers, ContextProvider<?>[] asserters, String help, String permission, SenderType type, String hook, List<Command> children, boolean hideSub) {
this.names = names;
this.args = args;
this.contextProviders = providers;
this.asserters = asserters;
this.permission = permission;
this.type = type;
this.hook = hook;
Expand All @@ -89,7 +93,7 @@ public void showHelp(CommandSender sender) {
sender.sendMessage(getHelpRecursive(sender, 0).trim());
}

private String getHelpRecursive(CommandSender sender, int level) {
protected String getHelpRecursive(CommandSender sender, int level) {
if (permission != null && !sender.hasPermission(permission)) {
return "";
}
Expand Down Expand Up @@ -253,6 +257,10 @@ private Object[] processArgs(String[] sargs, CommandSender sender) {
}

private Object[] getContext(CommandSender sender) {
if (!(sender instanceof Player)) {
sender.sendMessage(Messages.msg("playerOnly"));
return null;
}
Object[] output = new Object[contextProviders.length];
for (int i = 0; i < output.length; i++) {
Object obj = null;
Expand All @@ -273,6 +281,26 @@ private Object[] getContext(CommandSender sender) {
return output;
}

private boolean assertAll(CommandSender sender) {
if (!(sender instanceof Player)) {
sender.sendMessage(Messages.msg("playerOnly"));
return false;
}
for (ContextProvider<?> provider : asserters) {
Object o = provider.provide((Player) sender);
if (o == null) {
String error = provider.getErrorMessage();
if (error != null) {
sender.sendMessage(error);
} else {
showHelp(sender);
}
return false;
}
}
return true;
}

/**
* Registers this command and its children
* @param prefix The fallback prefix
Expand Down Expand Up @@ -329,7 +357,7 @@ public List<String> tabComplete(CommandSender sender, String alias, String[] arg
}
}

private void registerHook(Object... listeners) {
protected void registerHook(Object... listeners) {
loop:
for (Object listener : listeners) {
for (Method method : listener.getClass().getDeclaredMethods()) {
Expand Down Expand Up @@ -360,7 +388,7 @@ private void registerHook(Object... listeners) {
}
}

private List<String> tab(CommandSender sender, String[] args) {
protected List<String> tab(CommandSender sender, String[] args) {
List<String> completions = new ArrayList<>();
if (args.length > 0) {
for (Command child : children) {
Expand Down Expand Up @@ -401,7 +429,7 @@ private List<String> tab(CommandSender sender, String[] args) {
return completions;
}

private boolean execute(CommandSender sender, String[] args) {
protected boolean execute(CommandSender sender, String[] args) {
if (permission != null && !sender.hasPermission(permission)) {
sender.sendMessage(msg("noPermission").replace("%permission%", permission));
return true;
Expand All @@ -417,19 +445,22 @@ private boolean execute(CommandSender sender, String[] args) {
break;
case CONSOLE:
if (sender instanceof Player) {
sender.sendMessage(ChatColor.RED + "This command can only be executed from console!");
sender.sendMessage(Messages.msg("consoleOnly"));
return true;
}
break;
case PLAYER:
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "This command can only be executed as a player!");
sender.sendMessage(Messages.msg("playerOnly"));
return true;
}
break;
}
Object[] objArgs = processArgs(parseArgs(String.join(" ", args)), sender);
if (objArgs != null) {
if (asserters.length > 0 && !assertAll(sender)) {
return true;
}
int size = objArgs.length + contextProviders.length;
Object[] arr = new Object[size];
System.arraycopy(objArgs, 0, arr, 0, objArgs.length);
Expand Down Expand Up @@ -596,7 +627,7 @@ public static CommandArgumentType<String> of(String name, String... values) {
private Function<CommandSender, List<String>> tab = null;

/**
* Had to make this into a single constructor that takes an Object for Maven reasons
* Create a CommandArgumentType from a name and converter
* @param name The name of this command argument type, to be used in the command file
* @param convert The {@link Function} to convert from a String to whatever type this converts to
*/
Expand All @@ -609,7 +640,7 @@ public CommandArgumentType(String name, Function<String, T> convert) {
}

/**
* Had to make this into a single constructor that takes an Object for Maven reasons
* Create a CommandArgumentType from a name and converter
* @param name The name of this command argument type, to be used in the command file
* @param convert The {@link BiFunction} to convert from a String to whatever type this converts to
*/
Expand Down
66 changes: 66 additions & 0 deletions src/redempt/redlib/commandmanager/CommandCollection.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package redempt.redlib.commandmanager;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.bukkit.command.CommandSender;

Expand All @@ -23,9 +27,26 @@ public CommandCollection(List<Command> commands) {
* @param listeners The list of listener objects which contain hooks for the commands in this collection
*/
public void register(String prefix, Object... listeners) {
mergeBaseCommands();
commands.stream().forEach(c -> c.register(prefix, listeners));
}

private void mergeBaseCommands() {
Map<String, List<Command>> names = new HashMap<>();
for (Command command : commands) {
String name = String.join(", ", command.getAliases());
List<Command> cmds = names.getOrDefault(name, new ArrayList<>());
cmds.add(command);
names.put(name, cmds);
}
names.forEach((k, v) -> {
if (v.size() > 1) {
commands.removeAll(v);
commands.add(new MergedBaseCommand(v));
}
});
}

/**
*
* @return The commands in this CommandCollection
Expand Down Expand Up @@ -77,4 +98,49 @@ private Command getByHookName(String hookName, Command base) {
return null;
}

private static class MergedBaseCommand extends Command {


public MergedBaseCommand(List<Command> commands) {
this.children = commands;
this.help = commands.get(0).help;
this.names = commands.get(0).names;
for (Command command : children) {
command.topLevel = false;
command.parent = this;
}
}

@Override
public boolean execute(CommandSender sender, String[] args) {
if (children.stream().anyMatch(c -> c.execute(sender, args))) {
return true;
}
sender.sendMessage(Messages.msg("helpTitle").replace("%cmdname%", children.get(0).getName()));
sender.sendMessage(getHelpRecursive(sender, 0));
return true;
}

@Override
public String getHelpRecursive(CommandSender sender, int level) {
return children.stream().map(c -> c.getHelpRecursive(sender, 1)).collect(Collectors.joining("\n"));
}

@Override
public List<String> tab(CommandSender sender, String[] args) {
List<String> completions = new ArrayList<>();
children.forEach(c -> completions.addAll(c.tab(sender, args)));
return completions;
}

@Override
public void register(String prefix, Object... listeners) {
super.register(prefix, listeners);
for (Command command : children) {
command.registerHook(listeners);
}
}

}

}
19 changes: 17 additions & 2 deletions src/redempt/redlib/commandmanager/CommandParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.regex.Pattern;

import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
Expand Down Expand Up @@ -88,6 +89,7 @@ private CommandCollection fromLines(List<String> lines, int lineNumber) {
String[] names = null;
List<CommandArgument> args = new ArrayList<>();
List<ContextProvider<?>> contextProviders = new ArrayList<>();
List<ContextProvider<?>> asserters = new ArrayList<>();
String permission = null;
String hook = null;
SenderType type = SenderType.EVERYONE;
Expand Down Expand Up @@ -209,15 +211,26 @@ private CommandCollection fromLines(List<String> lines, int lineNumber) {
}
if (line.startsWith("context ")) {
contextProviders.clear();
String rest = line.replaceFirst("^context ", "");
String rest = line.substring(8);
String[] split = rest.split(" ");
int fpos = pos;
for (String name : split) {
int fpos = pos;
ContextProvider<?> provider = Arrays.stream(this.contextProviders).filter(c -> c.getName().equals(name)).findFirst()
.orElseThrow(() -> new CommandParseException("Missing context provider " + name + ", line " + fpos));
contextProviders.add(provider);
}
}
if (line.startsWith("assert ")) {
asserters.clear();
String rest = line.substring(7);
String[] split = rest.split(" ");
int fpos = pos;
for (String name : split) {
ContextProvider<?> provider = Arrays.stream(this.contextProviders).filter(c -> c.getName().equals(name)).findFirst()
.orElseThrow(() -> new CommandParseException("Missing context provider " + name + ", line " + fpos));
asserters.add(provider);
}
}
if (line.equalsIgnoreCase("hidesub")) {
hideSub = true;
}
Expand All @@ -230,11 +243,13 @@ private CommandCollection fromLines(List<String> lines, int lineNumber) {
if (depth == 0) {
commands.add(new Command(names, args.toArray(new CommandArgument[args.size()]),
contextProviders.toArray(new ContextProvider<?>[contextProviders.size()]),
asserters.toArray(new ContextProvider<?>[asserters.size()]),
help, permission, type, hook, children, hideSub));
children = new ArrayList<>();
names = null;
args = new ArrayList<>();
contextProviders = new ArrayList<>();
asserters = new ArrayList<>();
help = null;
permission = null;
type = null;
Expand Down

0 comments on commit 0f854a4

Please sign in to comment.