2020-07-10 16:37:18 +02:00
|
|
|
package net.minestom.server.command.builder;
|
|
|
|
|
2020-07-14 13:35:07 +02:00
|
|
|
import net.minestom.server.command.CommandSender;
|
2020-07-10 16:37:18 +02:00
|
|
|
import net.minestom.server.command.builder.arguments.Argument;
|
|
|
|
import net.minestom.server.command.builder.condition.CommandCondition;
|
|
|
|
|
|
|
|
import java.util.*;
|
|
|
|
import java.util.regex.Pattern;
|
|
|
|
|
2020-07-14 13:35:07 +02:00
|
|
|
public class CommandDispatcher {
|
2020-07-10 16:37:18 +02:00
|
|
|
|
2020-07-14 13:35:07 +02:00
|
|
|
private Map<String, Command> commandMap = new HashMap<>();
|
|
|
|
private Set<Command> commands = new HashSet<>();
|
2020-07-10 16:37:18 +02:00
|
|
|
|
2020-07-14 13:35:07 +02:00
|
|
|
public void register(Command command) {
|
2020-07-10 16:37:18 +02:00
|
|
|
this.commandMap.put(command.getName(), command);
|
|
|
|
for (String alias : command.getAliases()) {
|
|
|
|
this.commandMap.put(alias, command);
|
|
|
|
}
|
|
|
|
this.commands.add(command);
|
|
|
|
}
|
|
|
|
|
2020-08-03 06:36:42 +02:00
|
|
|
/**
|
|
|
|
* Parse the given command
|
|
|
|
*
|
|
|
|
* @param commandString the command (containing the command name and the args if any)
|
|
|
|
* @return the result of the parsing, null if the command doesn't exist
|
|
|
|
*/
|
2020-07-10 16:37:18 +02:00
|
|
|
public CommandResult parse(String commandString) {
|
|
|
|
commandString = commandString.trim();
|
|
|
|
|
|
|
|
// Split space
|
2020-08-03 06:36:42 +02:00
|
|
|
final String spaceRegex = " ";
|
|
|
|
final String[] splitted = commandString.split(spaceRegex);
|
|
|
|
final String commandName = splitted[0];
|
2020-07-10 16:37:18 +02:00
|
|
|
|
2020-08-03 06:36:42 +02:00
|
|
|
final String[] args = commandString.replaceFirst(Pattern.quote(commandName), "").trim().split(spaceRegex);
|
2020-07-10 16:37:18 +02:00
|
|
|
|
2020-08-03 06:36:42 +02:00
|
|
|
final Command command = findCommand(commandName);
|
|
|
|
// Check if the command exists
|
2020-07-10 16:37:18 +02:00
|
|
|
if (command == null)
|
|
|
|
return null;
|
|
|
|
|
2020-08-03 06:36:42 +02:00
|
|
|
// Find the used syntax, or check which argument is wrong
|
2020-07-10 16:37:18 +02:00
|
|
|
return findCommandResult(command, args);
|
|
|
|
}
|
|
|
|
|
2020-07-14 13:35:07 +02:00
|
|
|
public void execute(CommandSender source, String commandString) {
|
2020-07-10 16:37:18 +02:00
|
|
|
CommandResult result = parse(commandString);
|
|
|
|
result.execute(source);
|
|
|
|
}
|
|
|
|
|
2020-07-14 13:35:07 +02:00
|
|
|
public Set<Command> getCommands() {
|
2020-07-10 16:37:18 +02:00
|
|
|
return Collections.unmodifiableSet(commands);
|
|
|
|
}
|
|
|
|
|
2020-07-14 13:35:07 +02:00
|
|
|
private Command findCommand(String commandName) {
|
2020-07-10 16:37:18 +02:00
|
|
|
return commandMap.containsKey(commandName) ? commandMap.get(commandName) : null;
|
|
|
|
}
|
|
|
|
|
2020-07-14 13:35:07 +02:00
|
|
|
private CommandResult findCommandResult(Command command, String[] args) {
|
2020-07-10 16:37:18 +02:00
|
|
|
CommandResult result = new CommandResult();
|
|
|
|
result.command = command;
|
|
|
|
|
|
|
|
Arguments executorArgs = new Arguments();
|
|
|
|
|
|
|
|
// Default executor
|
|
|
|
// Check if args array is empty
|
|
|
|
if (args[0].length() == 0) {
|
|
|
|
result.executor = command.getDefaultExecutor();
|
|
|
|
result.arguments = executorArgs;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Find syntax
|
2020-08-03 06:36:42 +02:00
|
|
|
final Collection<CommandSyntax> syntaxes = command.getSyntaxes();
|
2020-07-10 16:37:18 +02:00
|
|
|
List<CommandSyntax> validSyntaxes = new ArrayList<>();
|
|
|
|
Map<CommandSyntax, String[]> syntaxesValues = new HashMap<>();
|
|
|
|
|
|
|
|
TreeMap<Integer, CommandSuggestionHolder> syntaxesSuggestions = new TreeMap<>(Collections.reverseOrder());
|
|
|
|
|
|
|
|
for (CommandSyntax syntax : syntaxes) {
|
2020-08-03 06:36:42 +02:00
|
|
|
final Argument[] arguments = syntax.getArguments();
|
|
|
|
final String[] argsValues = new String[arguments.length];
|
2020-07-10 16:37:18 +02:00
|
|
|
|
|
|
|
boolean syntaxCorrect = true;
|
|
|
|
int argIndex = 0;
|
|
|
|
|
|
|
|
boolean useRemaining = false;
|
|
|
|
for (int argCount = 0; argCount < syntax.getArguments().length; argCount++) {
|
|
|
|
Argument argument = syntax.getArguments()[argCount];
|
|
|
|
useRemaining = argument.useRemaining();
|
|
|
|
|
|
|
|
int correctionResult = Argument.SUCCESS;
|
|
|
|
boolean correct = false;
|
|
|
|
String argValue = "";
|
|
|
|
|
|
|
|
if (useRemaining) {
|
|
|
|
for (int i = argIndex; i < args.length; i++) {
|
2020-08-03 06:36:42 +02:00
|
|
|
final String arg = args[i];
|
2020-07-10 16:37:18 +02:00
|
|
|
if (argValue.length() > 0)
|
|
|
|
argValue += " ";
|
|
|
|
argValue += arg;
|
|
|
|
}
|
|
|
|
|
|
|
|
correctionResult = argument.getCorrectionResult(argValue);
|
|
|
|
if (correctionResult == Argument.SUCCESS) {
|
|
|
|
correct = true;
|
|
|
|
argsValues[argIndex] = argValue;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (int i = argIndex; i < args.length; i++) {
|
2020-08-03 06:36:42 +02:00
|
|
|
final String arg = args[i];
|
2020-07-10 16:37:18 +02:00
|
|
|
|
|
|
|
argValue += arg;
|
|
|
|
|
|
|
|
correctionResult = argument.getCorrectionResult(argValue);
|
|
|
|
if (correctionResult == Argument.SUCCESS) {
|
|
|
|
correct = true;
|
|
|
|
argsValues[argIndex] = argValue;
|
|
|
|
argIndex = i + 1;
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
if (!argument.allowSpace())
|
|
|
|
break;
|
|
|
|
argValue += " ";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (correct) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
syntaxCorrect = false;
|
|
|
|
CommandSuggestionHolder suggestionHolder = new CommandSuggestionHolder();
|
|
|
|
suggestionHolder.syntax = syntax;
|
|
|
|
suggestionHolder.argValue = argValue;
|
|
|
|
suggestionHolder.correctionResult = correctionResult;
|
|
|
|
suggestionHolder.argIndex = argCount;
|
|
|
|
syntaxesSuggestions.put(argCount, suggestionHolder);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (syntaxCorrect) {
|
|
|
|
if (args.length == argIndex || useRemaining) {
|
|
|
|
validSyntaxes.add(syntax);
|
|
|
|
syntaxesValues.put(syntax, argsValues);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find the valid syntax with the most of args
|
|
|
|
CommandSyntax finalSyntax = null;
|
|
|
|
for (CommandSyntax syntax : validSyntaxes) {
|
|
|
|
if (finalSyntax == null || finalSyntax.getArguments().length < syntax.getArguments().length) {
|
|
|
|
finalSyntax = syntax;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify args conditions of finalSyntax
|
|
|
|
if (finalSyntax != null) {
|
2020-08-03 06:36:42 +02:00
|
|
|
final Argument[] arguments = finalSyntax.getArguments();
|
|
|
|
final String[] argsValues = syntaxesValues.get(finalSyntax);
|
2020-07-10 16:37:18 +02:00
|
|
|
for (int i = 0; i < arguments.length; i++) {
|
2020-08-03 06:36:42 +02:00
|
|
|
final Argument argument = arguments[i];
|
|
|
|
final String argValue = argsValues[i];
|
2020-07-10 16:37:18 +02:00
|
|
|
// Finally parse it
|
2020-08-03 06:36:42 +02:00
|
|
|
final Object parsedValue = argument.parse(argValue);
|
|
|
|
final int conditionResult = argument.getConditionResult(parsedValue);
|
2020-07-10 16:37:18 +02:00
|
|
|
if (conditionResult == Argument.SUCCESS) {
|
|
|
|
executorArgs.setArg(argument.getId(), parsedValue);
|
|
|
|
} else {
|
|
|
|
result.callback = argument.getCallback();
|
|
|
|
result.value = argValue;
|
|
|
|
result.error = conditionResult;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If command isn't correct, find the closest
|
|
|
|
if (finalSyntax == null) {
|
|
|
|
// Get closest valid syntax
|
|
|
|
if (!syntaxesSuggestions.isEmpty()) {
|
2020-08-03 06:36:42 +02:00
|
|
|
final int max = syntaxesSuggestions.firstKey();
|
|
|
|
final CommandSuggestionHolder suggestionHolder = syntaxesSuggestions.get(max);
|
|
|
|
final CommandSyntax syntax = suggestionHolder.syntax;
|
|
|
|
final String argValue = suggestionHolder.argValue;
|
|
|
|
final int correctionResult = suggestionHolder.correctionResult;
|
|
|
|
final int argIndex = suggestionHolder.argIndex;
|
2020-07-10 16:37:18 +02:00
|
|
|
|
|
|
|
if (argValue.length() > 0) {
|
|
|
|
Argument argument = syntax.getArguments()[argIndex];
|
|
|
|
result.callback = argument.getCallback();
|
|
|
|
result.value = argValue;
|
|
|
|
result.error = correctionResult;
|
|
|
|
} else {
|
|
|
|
result.executor = command.getDefaultExecutor();
|
|
|
|
result.arguments = executorArgs;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use finalSyntax, or default executor if no syntax has been found
|
|
|
|
result.executor = finalSyntax == null ? command.getDefaultExecutor() : finalSyntax.getExecutor();
|
|
|
|
result.arguments = executorArgs;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
private class CommandSuggestionHolder {
|
|
|
|
private CommandSyntax syntax;
|
|
|
|
private String argValue;
|
|
|
|
private int correctionResult;
|
|
|
|
private int argIndex;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private class CommandResult {
|
|
|
|
|
|
|
|
// Command
|
2020-07-14 13:35:07 +02:00
|
|
|
private Command command;
|
2020-07-10 16:37:18 +02:00
|
|
|
|
|
|
|
// Command Executor
|
|
|
|
private CommandExecutor executor;
|
|
|
|
private Arguments arguments;
|
|
|
|
|
|
|
|
// Argument Callback
|
|
|
|
private ArgumentCallback callback;
|
|
|
|
private String value;
|
|
|
|
private int error;
|
|
|
|
|
2020-08-03 06:36:42 +02:00
|
|
|
/**
|
|
|
|
* Execute the command for the given source
|
|
|
|
* <p>
|
|
|
|
* The command will not be executed if the {@link CommandCondition} of the command
|
|
|
|
* is not validated
|
|
|
|
*
|
|
|
|
* @param source the command source
|
|
|
|
*/
|
2020-07-14 13:35:07 +02:00
|
|
|
public void execute(CommandSender source) {
|
2020-07-10 16:37:18 +02:00
|
|
|
// Condition check
|
2020-08-03 06:36:42 +02:00
|
|
|
final CommandCondition condition = command.getCondition();
|
2020-07-10 16:37:18 +02:00
|
|
|
if (condition != null) {
|
2020-08-03 06:36:42 +02:00
|
|
|
final boolean result = condition.apply(source);
|
2020-07-10 16:37:18 +02:00
|
|
|
if (!result)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Condition is respected
|
|
|
|
if (executor != null) {
|
2020-08-03 06:36:42 +02:00
|
|
|
// An executor has been found
|
2020-07-10 16:37:18 +02:00
|
|
|
executor.apply(source, arguments);
|
|
|
|
} else if (callback != null) {
|
2020-08-03 06:36:42 +02:00
|
|
|
// No syntax has been validated but the faulty argument has been found
|
|
|
|
// Execute the faulty argument callback
|
2020-07-10 16:37:18 +02:00
|
|
|
callback.apply(source, value, error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|