Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 187 additions & 0 deletions core/src/main/java/fr/traqueur/commands/api/CommandInvoker.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
package fr.traqueur.commands.api;

import fr.traqueur.commands.api.arguments.TabCompleter;
import fr.traqueur.commands.api.exceptions.ArgumentIncorrectException;
import fr.traqueur.commands.api.exceptions.TypeArgumentNotExistException;
import fr.traqueur.commands.api.requirements.Requirement;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
* CommandInvoker is responsible for invoking commands based on the provided label and arguments.
* It checks permissions, requirements, and executes the command if all conditions are met.
*
* @param <T> The type of the command sender (e.g., Player, Console).
* @param <S> The type of the source (e.g., Player, CommandSender).
*/
public class CommandInvoker<T, S> {

/**
* The command manager that holds the commands and their configurations.
*/
private final CommandManager<T, S> commandManager;

/**
* Constructor for CommandInvoker.
* @param manager The command manager that holds the commands and their configurations.
*/
public CommandInvoker(CommandManager<T,S> manager) {
this.commandManager = manager;
}

/**
* Get the command label from the label and the arguments.
* @param label The label of the command.
* @param args The arguments of the command.
* @param commandLabelSize The size of the command label.
* @return The command label.
*/
private String getCommandLabel(String label, String[] args, int commandLabelSize) {
StringBuilder buffer = new StringBuilder();
String labelLower = label.toLowerCase();
buffer.append(labelLower);
for (int x = 0; x < commandLabelSize; x++) {
buffer.append(".").append(args[x].toLowerCase());
}
return buffer.toString();
}

/**
* Invokes a command based on the provided source, base label, and raw arguments.
* It checks for command existence, permissions, requirements, and executes the command if valid.
*
* @param source The source of the command (e.g., Player, Console).
* @param baseLabel The base label of the command.
* @param rawArgs The raw arguments passed to the command.
* @return true if the command was successfully invoked, false otherwise.
*/
public boolean invoke(S source,
String baseLabel,
String[] rawArgs) {

Map<String, Command<T, S>> commands = this.commandManager.getCommands();

String cmdLabel = "";
Command<T, S> commandFramework = null;
for (int i = rawArgs.length; i >= 0; i--) {
cmdLabel = getCommandLabel(baseLabel, rawArgs, i);
commandFramework = commands.getOrDefault(cmdLabel, null);
if(commandFramework != null) {
break;
}
}

if (commandFramework == null) {
return false;
}

if(commandFramework.inGameOnly() && ! this.commandManager.getPlatform().isPlayer(source)) {
this.commandManager.getPlatform().sendMessage(source, this.commandManager.getMessageHandler().getOnlyInGameMessage());
return true;
}

if (!commandFramework.getPermission().isEmpty() && !this.commandManager.getPlatform().hasPermission(source, commandFramework.getPermission())) {
this.commandManager.getPlatform().sendMessage(source, this.commandManager.getMessageHandler().getNoPermissionMessage());
return true;
}

List<Requirement<S>> requirements = commandFramework.getRequirements();
for (Requirement<S> requirement : requirements) {
if (!requirement.check(source)) {
String error = requirement.errorMessage().isEmpty()
? this.commandManager.getMessageHandler().getRequirementMessage()
: requirement.errorMessage();
error = error.replace("%requirement%", requirement.getClass().getSimpleName());
this.commandManager.getPlatform().sendMessage(source, error);
return true;
}
}


int subCommand = cmdLabel.split("\\.").length - 1;
String[] modArgs = Arrays.copyOfRange(rawArgs, subCommand, rawArgs.length);

if ((modArgs.length < commandFramework.getArgs().size()) || (!commandFramework.isInfiniteArgs() && (modArgs.length > commandFramework.getArgs().size() + commandFramework.getOptinalArgs().size()))) {
String usage = commandFramework.getUsage().equalsIgnoreCase("")
? commandFramework.generateDefaultUsage(this.commandManager.getPlatform(), source, cmdLabel)
: commandFramework.getUsage();
this.commandManager.getPlatform().sendMessage(source, usage);
return true;
}

try {
Arguments arguments = this.commandManager.parse(commandFramework, modArgs);
commandFramework.execute(source, arguments);
} catch (TypeArgumentNotExistException e) {
this.commandManager.getPlatform().sendMessage(source, "&cPlease contact the developer of the commands library, its should not happen.");
return false;
} catch (ArgumentIncorrectException e) {
String message = this.commandManager.getMessageHandler().getArgNotRecognized();
message = message.replace("%arg%", e.getInput());
this.commandManager.getPlatform().sendMessage(source, message);
}
return true;
}

/**
* Suggests completions for the command based on the provided source, label, and arguments.
* It filters the suggestions based on the current argument and checks permissions for each suggestion.
*
* @param source The source of the command (e.g., Player, Console).
* @param label The label of the command.
* @param args The arguments passed to the command.
* @return A list of suggested completions.
*/
public List<String> suggest(S source, String label, String[] args) {
String arg = args[args.length-1];


Map<String, Map<Integer, TabCompleter<S>>> completers = commandManager.getCompleters();
Map<String, Command<T, S>> commands = this.commandManager.getCommands();

String cmdLabel = "";
Map<Integer, TabCompleter<S>> map = null;
for (int i = args.length; i >= 0; i--) {
cmdLabel = getCommandLabel(label, args, i);
map = completers.getOrDefault(cmdLabel, null);
if(map != null) {
break;
}
}

if (map == null || !map.containsKey(args.length)) {
return Collections.emptyList();
}

TabCompleter<S> converter = map.get(args.length);
String argsBeforeString = (label +
"." +
String.join(".", Arrays.copyOf(args, args.length - 1)))
.replaceFirst("^" + cmdLabel + "\\.", "");

List<String> completer = converter.onCompletion(source, Arrays.asList(argsBeforeString.split("\\.")))
.stream()
.filter(str -> str.toLowerCase().startsWith(arg.toLowerCase()) || str.equalsIgnoreCase(arg))
.collect(Collectors.toList());

String finalCmdLabel = cmdLabel;
return completer.stream().filter(str -> {
String cmdLabelInner = finalCmdLabel + "." + str.toLowerCase();
if(commands.containsKey(cmdLabelInner)) {
Command<T, S> frameworkCommand = commands.get(cmdLabelInner);
List<Requirement<S>> requirements = frameworkCommand.getRequirements();
for (Requirement<S> requirement : requirements) {
if (!requirement.check(source)) {
return false;
}
}
return frameworkCommand.getPermission().isEmpty() || this.commandManager.getPlatform().hasPermission(source, frameworkCommand.getPermission());
}
return true;
}).collect(Collectors.toList());
}
}
11 changes: 11 additions & 0 deletions core/src/main/java/fr/traqueur/commands/api/CommandManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ public abstract class CommandManager<T, S> {
private final Map<String, Map<Integer, TabCompleter<S>>> completers;


private final CommandInvoker<T,S> invoker;

/**
* The message handler of the command manager.
*/
Expand Down Expand Up @@ -87,6 +89,7 @@ public CommandManager(CommandPlatform<T,S> platform) {
this.commands = new HashMap<>();
this.typeConverters = new HashMap<>();
this.completers = new HashMap<>();
this.invoker = new CommandInvoker<>(this);
this.registerInternalConverters();
}

Expand Down Expand Up @@ -500,6 +503,14 @@ private boolean applyParsing(String[] args, Arguments arguments, List<Argument<S
return false;
}

/**
* Get the command invoker of the command manager.
* @return The command invoker of the command manager.
*/
public CommandInvoker<T, S> getInvoker() {
return invoker;
}

/**
* Register the internal converters of the command manager.
*/
Expand Down
16 changes: 16 additions & 0 deletions core/src/main/java/fr/traqueur/commands/api/CommandPlatform.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,22 @@ public interface CommandPlatform<T, S> {
*/
boolean hasPermission(S sender, String permission);

/**
* Checks if the sender is a player.
*
* @param sender The sender to check.
* @return true if the sender is a player, false otherwise.
*/
boolean isPlayer(S sender);

/**
* Sends a message to the sender.
*
* @param sender The sender to send the message to.
* @param message The message to send.
*/
void sendMessage(S sender, String message);

/**
* Adds a command to the platform.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,18 @@ public class Updater {
}
}

/**
* Set the URL to use to check for the latest release
* @param URL_LATEST_RELEASE The URL to use
*/
public static void setUrlLatestRelease(URL URL_LATEST_RELEASE) {
Updater.URL_LATEST_RELEASE = URL_LATEST_RELEASE;
}

/**
* Set the logger to use for logging messages
* @param LOGGER The logger to use
*/
public static void setLogger(Logger LOGGER) {
Updater.LOGGER = LOGGER;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,45 @@
import java.util.List;
import java.util.stream.Collectors;

/**
* An argument converter for enum types, allowing conversion from string to enum and providing tab completion.
*
* @param <T> The type of the enum.
* @param <S> The type of the sender (e.g., player, console).
*/
public class EnumArgument<T extends Enum<T>, S> implements ArgumentConverter<Enum<T>>, TabCompleter<S> {

/**
* Creates a new EnumArgument instance for the specified enum class.
*
* @param enumClass The class of the enum
* @param <E> The type of the enum
* @param <S> The type of the sender (e.g., player, console)
* @return A new instance of EnumArgument
*/
public static <E extends Enum<E>, S> EnumArgument<E, S> of(Class<E> enumClass) {
return new EnumArgument<>(enumClass);
}

/**
* The class of the enum type this argument converter handles.
*/
private final Class<T> clazz;

/**
* Constructs a new EnumArgument for the specified enum class.
*
* @param clazz The class of the enum type
*/
public EnumArgument(Class<T> clazz) {
this.clazz = clazz;
}

/**
* Gets the class of the enum type this argument converter handles.
*
* @return The enum class
*/
@Override
public Enum<T> apply(String s) {
if (s == null || s.isEmpty()) {
Expand All @@ -31,6 +58,11 @@ public Enum<T> apply(String s) {
}
}

/**
* Gets the class of the enum type this argument converter handles.
*
* @return The enum class
*/
@Override
public List<String> onCompletion(S sender, List<String> args) {
return Arrays.stream(clazz.getEnumConstants())
Expand Down
Loading