diff --git a/build.gradle b/build.gradle
index b3f35803..62b509c4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -7,7 +7,7 @@ apply plugin: 'maven-publish'
allprojects {
group = 'com.wizardlybump17.wlib'
- version = '1.6.7'
+ version = '1.7.0'
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'java'
diff --git a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandExecutor.java b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandExecutor.java
index 57ed1875..743da1c2 100644
--- a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandExecutor.java
+++ b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandExecutor.java
@@ -1,13 +1,13 @@
package com.wizardlybump17.wlib.bungee.command;
-import com.wizardlybump17.wlib.bungee.command.sender.GenericSender;
-import com.wizardlybump17.wlib.bungee.command.sender.ProxiedPlayerSender;
+import com.wizardlybump17.wlib.bungee.command.sender.CommandSender;
import com.wizardlybump17.wlib.command.CommandManager;
-import com.wizardlybump17.wlib.command.CommandSender;
+import com.wizardlybump17.wlib.command.exception.CommandException;
import com.wizardlybump17.wlib.command.holder.CommandExecutor;
-import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Command;
+import java.util.logging.Level;
+
public class BungeeCommandExecutor extends Command implements CommandExecutor {
private final CommandManager manager;
@@ -18,17 +18,16 @@ public BungeeCommandExecutor(CommandManager manager, String name) {
}
@Override
- public void execute(CommandSender> sender, String commandName, String[] args) {
+ public void execute(com.wizardlybump17.wlib.command.CommandSender> sender, String commandName, String[] args) throws CommandException {
manager.execute(sender, commandName + " " + String.join(" ", args));
}
@Override
public void execute(net.md_5.bungee.api.CommandSender sender, String[] args) {
- CommandSender> realSender;
- if (sender instanceof ProxiedPlayer)
- realSender = new ProxiedPlayerSender((ProxiedPlayer) sender);
- else
- realSender = new GenericSender(sender);
- execute(realSender, getName(), args);
+ try {
+ execute(new CommandSender(sender), getName(), args);
+ } catch (CommandException e) {
+ manager.getHolder().getLogger().log(Level.SEVERE, "Error while executing a command", e);
+ }
}
}
diff --git a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandManager.java b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandManager.java
index 48c0b047..416de0ef 100644
--- a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandManager.java
+++ b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandManager.java
@@ -1,7 +1,7 @@
package com.wizardlybump17.wlib.bungee.command;
import com.wizardlybump17.wlib.command.CommandManager;
-import com.wizardlybump17.wlib.command.RegisteredCommand;
+import com.wizardlybump17.wlib.command.registered.RegisteredCommand;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.Plugin;
diff --git a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/sender/GenericSender.java b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/sender/CommandSender.java
similarity index 64%
rename from bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/sender/GenericSender.java
rename to bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/sender/CommandSender.java
index 86698b06..ef0ddda4 100644
--- a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/sender/GenericSender.java
+++ b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/sender/CommandSender.java
@@ -1,11 +1,13 @@
package com.wizardlybump17.wlib.bungee.command.sender;
-import com.wizardlybump17.wlib.command.CommandSender;
import lombok.RequiredArgsConstructor;
import net.md_5.bungee.api.chat.TextComponent;
+import net.md_5.bungee.api.connection.ConnectedPlayer;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import org.jetbrains.annotations.NotNull;
@RequiredArgsConstructor
-public class GenericSender implements CommandSender {
+public class CommandSender implements com.wizardlybump17.wlib.command.CommandSender {
private final net.md_5.bungee.api.CommandSender handle;
@@ -34,12 +36,11 @@ public boolean hasPermission(String permission) {
return handle.hasPermission(permission);
}
- @Override
- public GenericSender toGeneric() {
- return this;
+ public @NotNull ProxiedPlayer asProxiedPlayer() {
+ return (ProxiedPlayer) handle;
}
- public static boolean isGeneric() {
- return true;
+ public @NotNull ConnectedPlayer asConnectedPlayer() {
+ return (ConnectedPlayer) handle;
}
}
diff --git a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/sender/ProxiedPlayerSender.java b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/sender/ProxiedPlayerSender.java
deleted file mode 100644
index d2fca3f7..00000000
--- a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/sender/ProxiedPlayerSender.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.wizardlybump17.wlib.bungee.command.sender;
-
-import com.wizardlybump17.wlib.command.CommandSender;
-import lombok.RequiredArgsConstructor;
-import net.md_5.bungee.api.chat.TextComponent;
-import net.md_5.bungee.api.connection.ProxiedPlayer;
-
-@RequiredArgsConstructor
-public class ProxiedPlayerSender implements CommandSender {
-
- private final ProxiedPlayer handle;
-
- @Override
- public ProxiedPlayer getHandle() {
- return handle;
- }
-
- @Override
- public void sendMessage(String message) {
- handle.sendMessage(TextComponent.fromLegacyText(message));
- }
-
- @Override
- public void sendMessage(String... messages) {
- handle.sendMessage(TextComponent.fromLegacyText(String.join("\n", messages)));
- }
-
- @Override
- public String getName() {
- return handle.getName();
- }
-
- @Override
- public boolean hasPermission(String permission) {
- return handle.hasPermission(permission);
- }
-
- @Override
- public GenericSender toGeneric() {
- return new GenericSender(handle);
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/CommandManager.java b/commands/src/main/java/com/wizardlybump17/wlib/command/CommandManager.java
index 371354f6..6f8509a9 100644
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/CommandManager.java
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/CommandManager.java
@@ -1,59 +1,37 @@
package com.wizardlybump17.wlib.command;
+import com.wizardlybump17.wlib.command.data.CommandData;
+import com.wizardlybump17.wlib.command.exception.CommandException;
+import com.wizardlybump17.wlib.command.extractor.CommandExtractor;
+import com.wizardlybump17.wlib.command.extractor.DirectCommandExtractor;
+import com.wizardlybump17.wlib.command.extractor.MethodCommandExtractor;
import com.wizardlybump17.wlib.command.holder.CommandHolder;
-import com.wizardlybump17.wlib.util.ReflectionUtil;
+import com.wizardlybump17.wlib.command.registered.RegisteredCommand;
import lombok.Getter;
import lombok.NonNull;
-import lombok.RequiredArgsConstructor;
-import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.NotNull;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Level;
+import java.util.*;
@Getter
-@RequiredArgsConstructor
public class CommandManager {
private final List commands = new ArrayList<>();
protected final CommandHolder> holder;
private final @NonNull Map, Map> fieldCache = new HashMap<>();
+ private final @NotNull Set commandExtractors = new HashSet<>();
- public void registerCommands(Object... objects) {
- for (Object object : objects) {
- for (Method method : object.getClass().getDeclaredMethods()) {
- if (!method.isAnnotationPresent(Command.class) || method.getParameterCount() == 0 || !CommandSender.class.isAssignableFrom(method.getParameterTypes()[0]))
- continue;
-
- MethodHandle handle;
- try {
- handle = MethodHandles.publicLookup().findVirtual(object.getClass(), method.getName(), MethodType.methodType(method.getReturnType(), method.getParameterTypes()));
- } catch (NoSuchMethodException | IllegalAccessException e) {
- holder.getLogger().log(Level.SEVERE, "Error while trying to get the MethodHandle for " + method.getName() + " at " + object.getClass().getName(), e);
- continue;
- }
-
- RegisteredCommand command = new RegisteredCommand(
- method.getAnnotation(Command.class),
- object,
- method,
- handle
- );
-
- commands.add(command);
- com.wizardlybump17.wlib.command.holder.Command holderCommand = holder.getCommand(command.getName());
- if (holderCommand != null)
- holderCommand.setExecutor(holderCommand.getDefaultExecutor(this, command.getName()));
- }
- }
+ public CommandManager(@NotNull CommandHolder> holder) {
+ this.holder = holder;
+ commandExtractors.add(new MethodCommandExtractor());
+ commandExtractors.add(new DirectCommandExtractor());
+ }
+ public void registerCommands(Object... objects) {
+ for (Object object : objects)
+ for (CommandExtractor extractor : commandExtractors)
+ commands.addAll(extractor.extract(this, holder, object));
commands.sort(null);
}
@@ -61,22 +39,26 @@ public void unregisterCommands() {
commands.clear();
}
- public void execute(CommandSender> sender, String string) {
+ public void execute(CommandSender> sender, String string) throws CommandException {
if (commands.isEmpty())
return;
for (RegisteredCommand registeredCommand : commands) {
- Command command = registeredCommand.getCommand();
+ CommandData command = registeredCommand.getCommand();
CommandResult result = registeredCommand.execute(sender, string);
switch (result) {
case PERMISSION_FAIL -> {
- handleMessage(registeredCommand, sender, command.permissionMessage(), command.permissionMessageIsField());
+ String message = command.getPermissionMessage();
+ if (message != null)
+ sender.sendMessage(message);
return;
}
case INVALID_SENDER -> {
- handleMessage(registeredCommand, sender, command.invalidSenderMessage(), command.invalidSenderMessageIsField());
+ String message = command.getInvalidSenderMessage();
+ if (message != null)
+ sender.sendMessage(message);
return;
}
@@ -87,40 +69,9 @@ public void execute(CommandSender> sender, String string) {
}
}
- protected void handleMessage(@NonNull RegisteredCommand registeredCommand, @NonNull CommandSender> sender, @NonNull String message, boolean isField) {
- if (!isField) {
- if (!message.isEmpty())
- sender.sendMessage(message);
- return;
- }
-
- String fieldMessage = getFieldMessage(registeredCommand, message);
- if (fieldMessage != null)
- sender.sendMessage(fieldMessage);
- }
-
- protected @Nullable String getFieldMessage(@NonNull RegisteredCommand registeredCommand, @NonNull String fieldName) {
- Map fields = fieldCache.computeIfAbsent(registeredCommand.getObject().getClass(), clazz -> {
- Map map = new HashMap<>();
- for (Field field : clazz.getDeclaredFields())
- map.put(field.getName(), field);
- return map;
- });
-
- Field field = fields.get(fieldName);
- if (field == null)
- return null;
-
- Object fieldValue = ReflectionUtil.getFieldValue(field, registeredCommand.getObject());
- return fieldValue == null ? null : fieldValue.toString();
- }
-
@NonNull
public List<@NonNull String> autoComplete(@NonNull CommandSender> sender, @NonNull String string) {
- List result = new ArrayList<>();
- for (RegisteredCommand command : commands)
- result.addAll(command.autoComplete(sender, string));
- return result;
+ return List.of();
}
public List getCommand(String name) {
@@ -131,10 +82,10 @@ public List getCommand(String name) {
return commands;
}
- public List getCommands(Object object) {
+ public List getCommands(@NotNull Object object) {
List commands = new ArrayList<>(this.commands.size());
for (RegisteredCommand command : this.commands)
- if (command.getObject() == object)
+ if (command.isOwnedBy(object))
commands.add(command);
return commands;
}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/CommandSender.java b/commands/src/main/java/com/wizardlybump17/wlib/command/CommandSender.java
index c4b89aa3..127a3037 100644
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/CommandSender.java
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/CommandSender.java
@@ -1,7 +1,5 @@
package com.wizardlybump17.wlib.command;
-import org.jetbrains.annotations.Nullable;
-
/**
* Represents a command sender, someone that can trigger commands.
*
@@ -21,14 +19,4 @@ public interface CommandSender {
String getName();
boolean hasPermission(String permission);
-
- /**
- * Used to convert this CommandSender to a generic sender, a command sender that can be anything
- *
- * @return the generic sender
- */
- @Nullable
- default CommandSender> toGeneric() {
- return null;
- }
}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/RegisteredCommand.java b/commands/src/main/java/com/wizardlybump17/wlib/command/RegisteredCommand.java
deleted file mode 100644
index 5ae2885e..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/RegisteredCommand.java
+++ /dev/null
@@ -1,275 +0,0 @@
-package com.wizardlybump17.wlib.command;
-
-import com.wizardlybump17.wlib.command.args.ArgsNode;
-import com.wizardlybump17.wlib.command.args.ArgsReaderRegistry;
-import com.wizardlybump17.wlib.command.args.ArgsReaderType;
-import com.wizardlybump17.wlib.command.args.reader.ArgsReader;
-import com.wizardlybump17.wlib.command.args.reader.ArgsReaderException;
-import com.wizardlybump17.wlib.object.Pair;
-import lombok.Getter;
-import lombok.NonNull;
-import org.jetbrains.annotations.NotNull;
-
-import java.lang.invoke.MethodHandle;
-import java.lang.reflect.Method;
-import java.lang.reflect.Parameter;
-import java.util.*;
-
-@Getter
-public class RegisteredCommand implements Comparable {
-
- private final Command command;
- private final Object object;
- private final Method method;
- private final List nodes = new ArrayList<>();
- private final @NonNull MethodHandle methodHandle;
-
- public RegisteredCommand(Command command, Object object, Method method, @NonNull MethodHandle methodHandle) {
- this.command = command;
- this.object = object;
- this.method = method;
- this.methodHandle = methodHandle;
- prepareNodes();
- }
-
- private void prepareNodes() {
- String[] commandArgs = command.execution().split(" ");
-
- Class>[] types = method.getParameterTypes();
- Parameter[] parameters = method.getParameters();
- int index = 1; //skipping the first type because of the CommandSender
- for (String commandArg : commandArgs) {
- if (!requiredArgs(commandArg)) {
- nodes.add(new ArgsNode(
- commandArg,
- false,
- null,
- null,
- false
- ));
- continue;
- }
-
- Description description = parameters[index].getAnnotation(Description.class);
-
- ArgsReaderType argsReaderType = parameters[index].getAnnotation(ArgsReaderType.class);
- if (argsReaderType == null && Argument.class.isAssignableFrom(types[index]))
- throw new IllegalArgumentException("the \"" + commandArg + "\" argument requires the " + ArgsReaderType.class.getName() + " annotation");
-
- ArgsReader> reader;
- if (argsReaderType == null)
- reader = ArgsReaderRegistry.INSTANCE.getReader(types[index]);
- else
- reader = ArgsReaderRegistry.INSTANCE.get(argsReaderType.value());
- if (reader == null)
- throw new IllegalArgumentException("no reader found for " + types[index].getName());
-
- nodes.add(new ArgsNode(
- trim(commandArg),
- true,
- reader,
- description == null ? null : description.value(),
- Argument.class.isAssignableFrom(types[index])
- ));
-
- index++;
- }
- }
-
- public String getName() {
- return command.execution().split(" ")[0];
- }
-
- @Override
- public int compareTo(@NotNull RegisteredCommand o) {
- if (o.command.priority() == -1 && command.priority() == -1) {
- int args = compareArgs(o);
-
- if (args == 0)
- return compareSize(o);
-
- return args;
- }
-
- return Integer.compare(o.command.priority(), command.priority());
- }
-
- private int compareArgs(RegisteredCommand other) {
- return Integer.compare(other.command.execution().split(" ").length, command.execution().split(" ").length);
- }
-
- private int compareSize(RegisteredCommand other) {
- return Integer.compare(other.command.execution().length(), command.execution().length());
- }
-
- public Optional>> parse(String input, boolean autoComplete) throws ArgsReaderException {
- List toParse = new ArrayList<>();
- checkArrays(input, toParse, autoComplete);
-
- List> result = new ArrayList<>(nodes.size());
-
- if (parseRequiredOnly(result, toParse, autoComplete))
- return Optional.of(result);
-
- return Optional.empty();
- }
-
- private boolean parseRequiredOnly(List> target, List strings, boolean autoComplete) throws ArgsReaderException {
- if (autoComplete && nodes.size() > strings.size())
- return false;
-
- for (int i = 0; i < nodes.size() && i < strings.size(); i++) {
- ArgsNode node = nodes.get(i);
- if (!node.isUserInput()) {
- if (!node.getName().equalsIgnoreCase(strings.get(i)))
- return false;
-
- continue;
- }
-
- Object parse = node.parse(strings.get(i));
- if (parse == ArgsNode.EMPTY)
- continue;
-
- target.add(new Pair<>(node, parse));
- }
-
- return true;
- }
-
- private void checkArrays(String input, List target, boolean autoComplete) throws ArgsReaderException {
- StringBuilder builder = new StringBuilder();
- boolean inArray = false;
- for (String s : input.split(" ")) {
- if (s.startsWith("\"") && s.endsWith("\"") && !s.endsWith("\\\"") && s.length() != 1) { //"string" | single word
- target.add(s.substring(1, s.length() - 1));
- continue;
- }
-
- if (s.startsWith("\"")) { //"string | it is starting the array
- builder.append(s.substring(1).replace("\\\"", "\"")).append(" ");
- inArray = true;
- continue;
- }
-
- if (s.endsWith("\"") && !s.endsWith("\\\"")) { //string" | it is ending the array
- builder.append(s.replace("\\\"", "\""), 0, s.length() - 1);
- target.add(builder.toString());
- builder = new StringBuilder();
- inArray = false;
- continue;
- }
-
- if (!builder.isEmpty()) { //string | it is in the array
- builder.append(s.replace("\\\"", "\"")).append(" ");
- continue;
- }
-
- target.add(s.replace("\\\"", "\"")); //string | it is not in the array
- }
-
- if (inArray && !autoComplete)
- throw new ArgsReaderException("Invalid array");
- }
-
- public CommandResult execute(CommandSender> sender, String string) {
- try {
- Optional>> parse = parse(string, true);
- if (parse.isEmpty())
- return CommandResult.ARGS_FAIL;
-
- if (!getSenderType().isInstance(sender) && !isSenderGeneric())
- return CommandResult.INVALID_SENDER;
-
- Object[] objects = parse.get().stream().map(Pair::getSecond).toArray();
- return executeInternal(sender, objects);
- } catch (ArgsReaderException e) {
- return CommandResult.EXCEPTION;
- }
- }
-
- private CommandResult executeInternal(CommandSender> sender, Object[] objects) {
- try {
- if (!command.permission().isEmpty() && !sender.hasPermission(command.permission()))
- return CommandResult.PERMISSION_FAIL;
-
- List
+ *
The type of each parameter is defined in the method that have this annotation.
+ * An example for the command above:
+ *
+ * @Command(execution = "command <hello> world")
+ * public void commandHelloWorld(GenericSender sender, String hello) {
+ * System.out.println(sender.getName() + " executed the hello world command with the argument " + hello);
+ * }
+ *
+ *
+ * @return how the command must be sent to be triggered
+ * @see com.wizardlybump17.wlib.command.args.reader.ArgsReader
+ */
+ @NotNull String getExecution();
+
+ /**
+ * @return which permission the sender must have to trigger this command
+ */
+ default @Nullable String getPermission() {
+ return null;
+ }
+
+ /**
+ *
Used when the {@link CommandSender} does not have the required {@link #getPermission()}.
+ * @return the message to be sent when the {@link CommandSender} does not have the required {@link #getPermission()}
+ */
+ default @Nullable String getPermissionMessage() {
+ return null;
+ }
+
+ /**
+ * @return the priority of this command
+ */
+ default int getPriority() {
+ return getExecution().split(" ").length;
+ }
+
+ /**
+ * @return the description of this command
+ */
+ default @Nullable String getDescription() {
+ return null;
+ }
+
+ /**
+ *
Used when the {@link CommandSender} is not valid for this command.