summaryrefslogtreecommitdiff
path: root/dicore3/command/src/main/java/io/dico/dicore/command/registration
diff options
context:
space:
mode:
Diffstat (limited to 'dicore3/command/src/main/java/io/dico/dicore/command/registration')
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/registration/BukkitCommand.java244
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/registration/CommandMap.java118
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/CommandParseException.java54
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandInterceptor.java72
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandReceiver.java10
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCallFlags.java186
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCommand.java74
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java59
8 files changed, 488 insertions, 329 deletions
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/BukkitCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/BukkitCommand.java
index b5346d0..40de6ae 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/BukkitCommand.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/BukkitCommand.java
@@ -1,122 +1,122 @@
-package io.dico.dicore.command.registration;
-
-import io.dico.dicore.command.ICommandAddress;
-import io.dico.dicore.command.ICommandDispatcher;
-import org.bukkit.Location;
-import org.bukkit.command.Command;
-import org.bukkit.command.CommandSender;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.util.List;
-
-/**
- * This class extends the bukkit's command class.
- * Instances are injected into the command map.
- */
-public class BukkitCommand extends Command {
- private ICommandDispatcher dispatcher;
- private ICommandAddress origin;
-
- public BukkitCommand(ICommandAddress address) {
- super(validateTree(address).getNames().get(0), "", "", address.getNames().subList(1, address.getNames().size()));
- this.dispatcher = address.getDispatcherForTree();
- this.origin = address;
-
- setTimingsIfNecessary(this);
- }
-
- private static ICommandAddress validateTree(ICommandAddress tree) {
- if (!tree.hasParent()) {
- throw new IllegalArgumentException();
- }
- if (tree.getNames().isEmpty()) {
- throw new IllegalArgumentException();
- }
- return tree;
- }
-
- public ICommandAddress getOrigin() {
- return origin;
- }
-
- @Override
- public boolean execute(CommandSender sender, String label, String[] args) {
- if (!dispatcher.dispatchCommand(sender, label, args)) {
- //System.out.println("failed to dispatch command");
- // target command not found, send a message in the future TODO
- }
- return true;
- }
-
- @Override
- public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException {
- return this.tabComplete(sender, alias, args, null);
- }
-
- //@Override
- public List<String> tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException {
- return dispatcher.getTabCompletions(sender, alias, location, args);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- BukkitCommand that = (BukkitCommand) o;
-
- return getName().equals(that.getName()) && dispatcher == that.dispatcher;
- }
-
- @Override
- public int hashCode() {
- return dispatcher.hashCode() | getName().hashCode();
- }
-
- private static void setTimingsIfNecessary(Command object) {
- // with paper spigot, the timings are not set by super constructor but by CommandMap.register(), which is not invoked for this system
- // I use reflection so that the project does not require paper spigot to build
- try {
- // public field
- Field field = Command.class.getDeclaredField("timings");
- if (field.get(object) != null) return;
- Class<?> clazz = Class.forName("co.aikar.timings.TimingsManager");
- // public method
- Method method = clazz.getDeclaredMethod("getCommandTiming", String.class, Command.class);
- Object timings = method.invoke(null, "", object);
- field.set(object, timings);
- } catch (Throwable ignored) {
- }
- }
-
- /*
- public static void registerToMap(ICommandAddress tree, Map<String, Command> map) {
- BukkitCommand command = new BukkitCommand(tree);
- Iterator<String> iterator = tree.getNames().iterator();
- map.put(iterator.next(), command);
- while (iterator.hasNext()) {
- map.putIfAbsent(iterator.next(), command);
- }
- }
-
- public static void unregisterFromMap(ICommandAddress tree, Map<String, Command> map) {
- map.values().remove(new BukkitCommand(tree));
- }
-
- public static void registerChildrenToMap(ICommandAddress tree, Map<String, Command> map) {
- for (Map.Entry<String, ? extends ICommandAddress> entry : tree.getChildren().entrySet()) {
- ICommandAddress child = entry.getValue();
- registerToMap(child, map);
- }
- }
-
- public static void unregisterChildenFromMap(ICommandAddress tree, Map<String, Command> map) {
- for (Map.Entry<String, ? extends ICommandAddress> entry : tree.getChildren().entrySet()) {
- ICommandAddress child = entry.getValue();
- unregisterFromMap(child, map);
- }
- }
- */
-
-}
+package io.dico.dicore.command.registration;
+
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.command.ICommandDispatcher;
+import org.bukkit.Location;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * This class extends the bukkit's command class.
+ * Instances are injected into the command map.
+ */
+public class BukkitCommand extends Command {
+ private ICommandDispatcher dispatcher;
+ private ICommandAddress origin;
+
+ public BukkitCommand(ICommandAddress address) {
+ super(validateTree(address).getNames().get(0), "", "", address.getNames().subList(1, address.getNames().size()));
+ this.dispatcher = address.getDispatcherForTree();
+ this.origin = address;
+
+ setTimingsIfNecessary(this);
+ }
+
+ private static ICommandAddress validateTree(ICommandAddress tree) {
+ if (!tree.hasParent()) {
+ throw new IllegalArgumentException();
+ }
+ if (tree.getNames().isEmpty()) {
+ throw new IllegalArgumentException();
+ }
+ return tree;
+ }
+
+ public ICommandAddress getOrigin() {
+ return origin;
+ }
+
+ @Override
+ public boolean execute(CommandSender sender, String label, String[] args) {
+ if (!dispatcher.dispatchCommand(sender, label, args)) {
+ //System.out.println("failed to dispatch command");
+ // target command not found, send a message in the future TODO
+ }
+ return true;
+ }
+
+ @Override
+ public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException {
+ return this.tabComplete(sender, alias, args, null);
+ }
+
+ //@Override
+ public List<String> tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException {
+ return dispatcher.getTabCompletions(sender, alias, location, args);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ BukkitCommand that = (BukkitCommand) o;
+
+ return getName().equals(that.getName()) && dispatcher == that.dispatcher;
+ }
+
+ @Override
+ public int hashCode() {
+ return dispatcher.hashCode() | getName().hashCode();
+ }
+
+ private static void setTimingsIfNecessary(Command object) {
+ // with paper spigot, the timings are not set by super constructor but by CommandMap.register(), which is not invoked for this system
+ // I use reflection so that the project does not require paper spigot to build
+ try {
+ // public field
+ Field field = Command.class.getDeclaredField("timings");
+ if (field.get(object) != null) return;
+ Class<?> clazz = Class.forName("co.aikar.timings.TimingsManager");
+ // public method
+ Method method = clazz.getDeclaredMethod("getCommandTiming", String.class, Command.class);
+ Object timings = method.invoke(null, "", object);
+ field.set(object, timings);
+ } catch (Throwable ignored) {
+ }
+ }
+
+ /*
+ public static void registerToMap(ICommandAddress tree, Map<String, Command> map) {
+ BukkitCommand command = new BukkitCommand(tree);
+ Iterator<String> iterator = tree.getNames().iterator();
+ map.put(iterator.next(), command);
+ while (iterator.hasNext()) {
+ map.putIfAbsent(iterator.next(), command);
+ }
+ }
+
+ public static void unregisterFromMap(ICommandAddress tree, Map<String, Command> map) {
+ map.values().remove(new BukkitCommand(tree));
+ }
+
+ public static void registerChildrenToMap(ICommandAddress tree, Map<String, Command> map) {
+ for (Map.Entry<String, ? extends ICommandAddress> entry : tree.getChildren().entrySet()) {
+ ICommandAddress child = entry.getValue();
+ registerToMap(child, map);
+ }
+ }
+
+ public static void unregisterChildenFromMap(ICommandAddress tree, Map<String, Command> map) {
+ for (Map.Entry<String, ? extends ICommandAddress> entry : tree.getChildren().entrySet()) {
+ ICommandAddress child = entry.getValue();
+ unregisterFromMap(child, map);
+ }
+ }
+ */
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/CommandMap.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/CommandMap.java
index 780ec66..2008b29 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/CommandMap.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/CommandMap.java
@@ -1,59 +1,59 @@
-package io.dico.dicore.command.registration;
-
-import io.dico.dicore.Reflection;
-import org.bukkit.Bukkit;
-import org.bukkit.command.Command;
-import org.bukkit.command.SimpleCommandMap;
-import org.bukkit.plugin.SimplePluginManager;
-
-import java.util.*;
-
-/**
- * Provides access to bukkit's {@code Map<String, org.bukkit.command.Command>} command map.
- */
-@SuppressWarnings("ConstantConditions")
-public class CommandMap {
- private static final Map<String, Command> commandMap = findCommandMap();
-
- private CommandMap() {
-
- }
-
- public static Map<String, Command> getCommandMap() {
- return Objects.requireNonNull(commandMap);
- }
-
- public static boolean isAvailable() {
- return commandMap != null;
- }
-
- public static Command get(String key) {
- return commandMap.get(key);
- }
-
- public static void put(String key, Command command) {
- commandMap.put(key, command);
- }
-
- public static Collection<String> replace(Command command, Command replacement) {
- List<String> result = new ArrayList<>();
- for (Map.Entry<String, Command> entry : commandMap.entrySet()) {
- if (entry.getValue() == command) {
- entry.setValue(replacement);
- result.add(entry.getKey());
- }
- }
- return result;
- }
-
- private static Map<String, Command> findCommandMap() {
- try {
- return Reflection.getFieldValue(SimpleCommandMap.class, "knownCommands",
- Reflection.getFieldValue(SimplePluginManager.class, "commandMap", Bukkit.getPluginManager()));
- } catch (Exception ex) {
- ex.printStackTrace();
- return null;
- }
- }
-
-}
+package io.dico.dicore.command.registration;
+
+import io.dico.dicore.Reflection;
+import org.bukkit.Bukkit;
+import org.bukkit.command.Command;
+import org.bukkit.command.SimpleCommandMap;
+import org.bukkit.plugin.SimplePluginManager;
+
+import java.util.*;
+
+/**
+ * Provides access to bukkit's {@code Map<String, org.bukkit.command.Command>} command map.
+ */
+@SuppressWarnings("ConstantConditions")
+public class CommandMap {
+ private static final Map<String, Command> commandMap = findCommandMap();
+
+ private CommandMap() {
+
+ }
+
+ public static Map<String, Command> getCommandMap() {
+ return Objects.requireNonNull(commandMap);
+ }
+
+ public static boolean isAvailable() {
+ return commandMap != null;
+ }
+
+ public static Command get(String key) {
+ return commandMap.get(key);
+ }
+
+ public static void put(String key, Command command) {
+ commandMap.put(key, command);
+ }
+
+ public static Collection<String> replace(Command command, Command replacement) {
+ List<String> result = new ArrayList<>();
+ for (Map.Entry<String, Command> entry : commandMap.entrySet()) {
+ if (entry.getValue() == command) {
+ entry.setValue(replacement);
+ result.add(entry.getKey());
+ }
+ }
+ return result;
+ }
+
+ private static Map<String, Command> findCommandMap() {
+ try {
+ return Reflection.getFieldValue(SimpleCommandMap.class, "knownCommands",
+ Reflection.getFieldValue(SimplePluginManager.class, "commandMap", Bukkit.getPluginManager()));
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ return null;
+ }
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/CommandParseException.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/CommandParseException.java
index 478c70c..0b7ce3c 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/CommandParseException.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/CommandParseException.java
@@ -1,27 +1,27 @@
-package io.dico.dicore.command.registration.reflect;
-
-/**
- * Thrown if an error occurs while 'parsing' a reflection command method
- * Other errors can be thrown too in there that may not be directly relevant to a parsing error.
- */
-public class CommandParseException extends Exception {
-
- public CommandParseException() {
- }
-
- public CommandParseException(String message) {
- super(message);
- }
-
- public CommandParseException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public CommandParseException(Throwable cause) {
- super(cause);
- }
-
- public CommandParseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
- super(message, cause, enableSuppression, writableStackTrace);
- }
-}
+package io.dico.dicore.command.registration.reflect;
+
+/**
+ * Thrown if an error occurs while 'parsing' a reflection command method
+ * Other errors can be thrown too in there that may not be directly relevant to a parsing error.
+ */
+public class CommandParseException extends Exception {
+
+ public CommandParseException() {
+ }
+
+ public CommandParseException(String message) {
+ super(message);
+ }
+
+ public CommandParseException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public CommandParseException(Throwable cause) {
+ super(cause);
+ }
+
+ public CommandParseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandInterceptor.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandInterceptor.java
index 67b65e4..1e56b8a 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandInterceptor.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandInterceptor.java
@@ -1,36 +1,36 @@
-package io.dico.dicore.command.registration.reflect;
-
-import io.dico.dicore.command.ExecutionContext;
-
-import java.lang.reflect.Method;
-
-public interface ICommandInterceptor {
-
- /**
- * Get the receiver of the command, if applicable.
- * A command has a receiver if its first parameter implements {@link ICommandReceiver}
- * and its instance object implements this interface.
- *
- * @param context the context of execution
- * @param target the method of the command
- * @param cmdName the name of the command
- * @return the receiver
- */
- default ICommandReceiver getReceiver(ExecutionContext context, Method target, String cmdName) {
- return null;
- }
-
- /**
- * If applicable, get the coroutine context to use in suspend functions (Kotlin only).
- * The return type is object to avoid depending on the kotlin runtime.
- *
- * @param context the context of execution
- * @param target the method of the command
- * @param cmdName the name of the command
- * @return the coroutine context
- */
- default Object getCoroutineContext(ExecutionContext context, Method target, String cmdName) {
- return null;
- }
-
-}
+package io.dico.dicore.command.registration.reflect;
+
+import io.dico.dicore.command.ExecutionContext;
+
+import java.lang.reflect.Method;
+
+public interface ICommandInterceptor {
+
+ /**
+ * Get the receiver of the command, if applicable.
+ * A command has a receiver if its first parameter implements {@link ICommandReceiver}
+ * and its instance object implements this interface.
+ *
+ * @param context the context of execution
+ * @param target the method of the command
+ * @param cmdName the name of the command
+ * @return the receiver
+ */
+ default ICommandReceiver getReceiver(ExecutionContext context, Method target, String cmdName) {
+ return null;
+ }
+
+ /**
+ * If applicable, get the coroutine context to use in suspend functions (Kotlin only).
+ * The return type is object to avoid depending on the kotlin runtime.
+ *
+ * @param context the context of execution
+ * @param target the method of the command
+ * @param cmdName the name of the command
+ * @return the coroutine context
+ */
+ default Object getCoroutineContext(ExecutionContext context, Method target, String cmdName) {
+ return null;
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandReceiver.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandReceiver.java
index bdcb568..d0681b1 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandReceiver.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandReceiver.java
@@ -1,5 +1,5 @@
-package io.dico.dicore.command.registration.reflect;
-
-public interface ICommandReceiver {
-
-}
+package io.dico.dicore.command.registration.reflect;
+
+public interface ICommandReceiver {
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCallFlags.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCallFlags.java
new file mode 100644
index 0000000..fa198c2
--- /dev/null
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCallFlags.java
@@ -0,0 +1,186 @@
+package io.dico.dicore.command.registration.reflect;
+
+import io.dico.dicore.command.CommandException;
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.exceptions.checkedfunctions.CheckedSupplier;
+
+/**
+ * Call flags store which extra parameters the target function expects on top of command parameters.
+ * All 4 possible extra parameters are listed below.
+ * <p>
+ * Extra parameters are ordered by the bit that represents them in the call flags.
+ * They can either be leading or trailing the command's parameters.
+ */
+public class ReflectiveCallFlags {
+
+ /**
+ * Receiver ({@code this} in some kotlin functions - always first parameter)
+ *
+ * @see ICommandInterceptor#getReceiver(io.dico.dicore.command.ExecutionContext, java.lang.reflect.Method, String)
+ */
+ public static final int RECEIVER_BIT = 1 << 0;
+
+ /**
+ * CommandSender
+ *
+ * @see org.bukkit.command.CommandSender
+ */
+ public static final int SENDER_BIT = 1 << 1;
+
+ /**
+ * ExecutionContext
+ *
+ * @see io.dico.dicore.command.ExecutionContext
+ */
+ public static final int CONTEXT_BIT = 1 << 2;
+
+ /**
+ * Continuation (trailing parameters of kotlin suspended functions)
+ *
+ * @see kotlin.coroutines.Continuation
+ */
+ public static final int CONTINUATION_BIT = 1 << 3;
+
+ /**
+ * Mask of extra parameters that trail the command's parameters, instead of leading.
+ */
+ public static final int TRAILING_MASK = CONTINUATION_BIT;
+
+ /**
+ * Check if the call arg is trailing the command's parameters.
+ *
+ * @param bit the bit used for the call flag
+ * @return true if the call arg is trailing the command's parameters
+ */
+ public static boolean isTrailingCallArg(int bit) {
+ return (bit & TRAILING_MASK) != 0;
+ }
+
+ /**
+ * Number of call arguments leading the command parameters.
+ *
+ * @param flags the call flags
+ * @return the number of call arguments leading the command parameters
+ */
+ public static int getLeadingCallArgNum(int flags) {
+ return Integer.bitCount(flags & ~TRAILING_MASK);
+ }
+
+ /**
+ * Number of call arguments trailing the command parameters.
+ *
+ * @param flags the call flags
+ * @return the number of call arguments trailing the command parameters
+ */
+ public static int getTrailingCallArgNum(int flags) {
+ return Integer.bitCount(flags & TRAILING_MASK);
+ }
+
+ /**
+ * Check if the flags contain the call arg.
+ *
+ * @param flags the call flags
+ * @param bit the bit used for the call flag
+ * @return true if the flags contain the call arg
+ */
+ public static boolean hasCallArg(int flags, int bit) {
+ return (flags & bit) != 0;
+ }
+
+ /**
+ * Get the index used for the call arg when calling the reflective function
+ *
+ * @param flags the call flags
+ * @param bit the bit used for the call flag
+ * @param cmdParameterNum the number of parameters of the command
+ * @return the index used for the call arg
+ */
+ public static int getCallArgIndex(int flags, int bit, int cmdParameterNum) {
+ if ((bit & TRAILING_MASK) == 0) {
+ // Leading.
+
+ int preceding = precedingMaskFrom(bit);
+ int mask = flags & precedingMaskFrom(bit) & ~TRAILING_MASK;
+
+ // Count the number of present call args that are leading and precede the given bit
+ return Integer.bitCount(flags & precedingMaskFrom(bit) & ~TRAILING_MASK);
+ } else {
+ // Trailing.
+
+ // Count the number of present call args that are leading
+ // plus the number of present call args that are trailing and precede the given bit
+ // plus the command's parameters
+
+ return Integer.bitCount(flags & ~TRAILING_MASK)
+ + Integer.bitCount(flags & precedingMaskFrom(bit) & TRAILING_MASK)
+ + cmdParameterNum;
+ }
+ }
+
+ /**
+ * Get the mask for all bits trailing the given fromBit
+ *
+ * <p>
+ * For example, if the bit is 00010000
+ * This function returns 00001111
+ * <p>
+ *
+ * @param fromBit number with the bit set there the ones should stop.
+ * @return the mask for all bits trailing the given fromBit
+ */
+ private static int precedingMaskFrom(int fromBit) {
+ int trailingZeros = Integer.numberOfTrailingZeros(fromBit);
+ if (trailingZeros == 0) return 0;
+ return -1 >>> -trailingZeros;
+ }
+
+ /**
+ * Get the object array used to call the function.
+ *
+ * @param callFlags the call flags
+ * @param context the context
+ * @param parameterOrder the order of parameters in the function
+ * @param receiverFunction the function that will create the receiver for this call, if applicable
+ * @return the call args
+ */
+ public static Object[] getCallArgs(
+ int callFlags,
+ ExecutionContext context,
+ String[] parameterOrder,
+ CheckedSupplier<Object, CommandException> receiverFunction
+ ) throws CommandException {
+ int leadingParameterNum = getLeadingCallArgNum(callFlags);
+ int cmdParameterNum = parameterOrder.length;
+ int trailingParameterNum = getTrailingCallArgNum(callFlags);
+
+ Object[] result = new Object[leadingParameterNum + cmdParameterNum + trailingParameterNum];
+
+ if (hasCallArg(callFlags, RECEIVER_BIT)) {
+ int index = getCallArgIndex(callFlags, RECEIVER_BIT, cmdParameterNum);
+ result[index] = receiverFunction.get();
+ }
+
+ if (hasCallArg(callFlags, SENDER_BIT)) {
+ int index = getCallArgIndex(callFlags, SENDER_BIT, cmdParameterNum);
+ result[index] = context.getSender();
+ }
+
+ if (hasCallArg(callFlags, CONTEXT_BIT)) {
+ int index = getCallArgIndex(callFlags, CONTEXT_BIT, cmdParameterNum);
+ result[index] = context;
+ }
+
+ if (hasCallArg(callFlags, CONTINUATION_BIT)) {
+ int index = getCallArgIndex(callFlags, CONTINUATION_BIT, cmdParameterNum);
+ result[index] = null; // filled in later.
+ }
+
+ for (int i = 0; i < parameterOrder.length; i++) {
+ String parameterName = parameterOrder[i];
+ result[leadingParameterNum + i] = context.get(parameterName);
+ }
+
+ return result;
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCommand.java
index 2d7c333..f4ddc70 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCommand.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCommand.java
@@ -4,6 +4,8 @@ import io.dico.dicore.command.*;
import io.dico.dicore.command.annotation.Cmd;
import io.dico.dicore.command.annotation.GenerateCommands;
import io.dico.dicore.command.parameter.type.IParameterTypeSelector;
+import io.dico.dicore.exceptions.checkedfunctions.CheckedSupplier;
+import kotlin.coroutines.CoroutineContext;
import org.bukkit.command.CommandSender;
import java.lang.reflect.InvocationTargetException;
@@ -11,14 +13,11 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public final class ReflectiveCommand extends Command {
- private static final int continuationMask = 1 << 3;
private final Cmd cmdAnnotation;
private final Method method;
private final Object instance;
private String[] parameterOrder;
-
- // hasContinuation | hasContext | hasSender | hasReceiver
- private final int flags;
+ private final int callFlags;
ReflectiveCommand(IParameterTypeSelector selector, Method method, Object instance) throws CommandParseException {
if (!method.isAnnotationPresent(Cmd.class)) {
@@ -48,7 +47,7 @@ public final class ReflectiveCommand extends Command {
this.method = method;
this.instance = instance;
- this.flags = ReflectiveRegistration.parseCommandAttributes(selector, method, this, parameters);
+ this.callFlags = ReflectiveRegistration.parseCommandAttributes(selector, method, this, parameters);
}
public Method getMethod() {
@@ -59,12 +58,22 @@ public final class ReflectiveCommand extends Command {
return instance;
}
- public String getCmdName() { return cmdAnnotation.value(); }
+ public String getCmdName() {
+ return cmdAnnotation.value();
+ }
+
+ public int getCallFlags() {
+ return callFlags;
+ }
void setParameterOrder(String[] parameterOrder) {
this.parameterOrder = parameterOrder;
}
+ public int getParameterNum() {
+ return parameterOrder.length;
+ }
+
ICommandAddress getAddress() {
ChildCommandAddress result = new ChildCommandAddress();
result.setCommand(this);
@@ -86,54 +95,24 @@ public final class ReflectiveCommand extends Command {
@Override
public String execute(CommandSender sender, ExecutionContext context) throws CommandException {
- String[] parameterOrder = this.parameterOrder;
- int extraArgumentCount = Integer.bitCount(flags);
- int parameterStartIndex = Integer.bitCount(flags & ~continuationMask);
-
- Object[] args = new Object[parameterOrder.length + extraArgumentCount];
- int i = 0;
-
- int mask = 1;
- if ((flags & mask) != 0) {
- // Has receiver
+ CheckedSupplier<Object, CommandException> receiverFunction = () -> {
try {
- args[i++] = ((ICommandInterceptor) instance).getReceiver(context, method, getCmdName());
+ return ((ICommandInterceptor) instance).getReceiver(context, method, getCmdName());
} catch (Exception ex) {
handleException(ex);
return null; // unreachable
}
- }
+ };
- mask <<= 1;
- if ((flags & mask) != 0) {
- // Has sender
- args[i++] = sender;
- }
-
- mask <<= 1;
- if ((flags & mask) != 0) {
- // Has context
- args[i++] = context;
- }
-
- mask <<= 1;
- if ((flags & mask) != 0) {
- // Has continuation
-
- extraArgumentCount--;
- }
-
- for (int n = args.length; i < n; i++) {
- args[i] = context.get(parameterOrder[i - extraArgumentCount]);
- }
+ Object[] callArgs = ReflectiveCallFlags.getCallArgs(callFlags, context, parameterOrder, receiverFunction);
- if ((flags & mask) != 0) {
- // Since it has continuation, call as coroutine
- return callAsCoroutine(context, args);
+ if (ReflectiveCallFlags.hasCallArg(callFlags, ReflectiveCallFlags.CONTINUATION_BIT)) {
+ // If it has a continuation, call as coroutine
+ return callAsCoroutine(context, callArgs);
}
- return callSynchronously(args);
+ return callSynchronously(callArgs);
}
private boolean isSuspendFunction() {
@@ -180,8 +159,11 @@ public final class ReflectiveCommand extends Command {
throw new CommandException("An internal error occurred while executing this command.", ex);
}
- private String callAsCoroutine(ExecutionContext context, Object[] args) {
- return KotlinReflectiveRegistrationKt.callAsCoroutine(this, (ICommandInterceptor) instance, context, args);
+ private String callAsCoroutine(ExecutionContext executionContext, Object[] args) throws CommandException {
+ ICommandInterceptor factory = (ICommandInterceptor) instance;
+ CoroutineContext coroutineContext = (CoroutineContext) factory.getCoroutineContext(executionContext, method, getCmdName());
+ int continuationIndex = ReflectiveCallFlags.getCallArgIndex(callFlags, ReflectiveCallFlags.CONTINUATION_BIT, parameterOrder.length);
+ return KotlinReflectiveRegistrationKt.callCommandAsCoroutine(executionContext, coroutineContext, continuationIndex, method, instance, args);
}
}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java
index 6b1965d..ddd5420 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java
@@ -22,6 +22,8 @@ import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
+import static io.dico.dicore.command.registration.reflect.ReflectiveCallFlags.*;
+
/**
* Takes care of turning a reflection {@link Method} into a command and more.
*/
@@ -196,47 +198,43 @@ public class ReflectiveRegistration {
return new ReflectiveCommand(selector, method, instance).getAddress();
}
- static int parseCommandAttributes(IParameterTypeSelector selector, Method method, ReflectiveCommand command, java.lang.reflect.Parameter[] parameters) throws CommandParseException {
+ static int parseCommandAttributes(IParameterTypeSelector selector, Method method, ReflectiveCommand command, java.lang.reflect.Parameter[] callParameters) throws CommandParseException {
ParameterList list = command.getParameterList();
- boolean hasReceiverParameter = false;
- boolean hasSenderParameter = false;
- boolean hasContextParameter = false;
- boolean hasContinuationParameter = false;
-
- int start = 0;
- int end = parameters.length;
-
Class<?> senderParameterType = null;
+ int flags = 0;
+ int start = 0;
+ int end = callParameters.length;
- if (parameters.length > start
+ if (callParameters.length > start
&& command.getInstance() instanceof ICommandInterceptor
- && ICommandReceiver.class.isAssignableFrom(parameters[start].getType())) {
- hasReceiverParameter = true;
- start++;
+ && ICommandReceiver.class.isAssignableFrom(callParameters[start].getType())) {
+ flags |= RECEIVER_BIT;
+ ++start;
}
- if (parameters.length > start && CommandSender.class.isAssignableFrom(senderParameterType = parameters[start].getType())) {
- hasSenderParameter = true;
- start++;
+ if (callParameters.length > start && CommandSender.class.isAssignableFrom(senderParameterType = callParameters[start].getType())) {
+ flags |= SENDER_BIT;
+ ++start;
}
- if (parameters.length > start && parameters[start].getType() == ExecutionContext.class) {
- hasContextParameter = true;
- start++;
+ if (callParameters.length > start && callParameters[start].getType() == ExecutionContext.class) {
+ flags |= CONTEXT_BIT;
+ ++start;
}
- if (parameters.length > start && parameters[end - 1].getType().getName().equals("kotlin.coroutines.Continuation")) {
- hasContinuationParameter = true;
- end--;
+ if (callParameters.length > start && callParameters[end - 1].getType().getName().equals("kotlin.coroutines.Continuation")) {
+ flags |= CONTINUATION_BIT;
+ --end;
}
- String[] parameterNames = lookupParameterNames(method, parameters, start);
+ String[] parameterNames = lookupParameterNames(method, callParameters, start);
for (int i = start, n = end; i < n; i++) {
- Parameter<?, ?> parameter = parseParameter(selector, method, parameters[i], parameterNames[i - start]);
+ Parameter<?, ?> parameter = parseParameter(selector, method, callParameters[i], parameterNames[i - start]);
list.addParameter(parameter);
}
- command.setParameterOrder(hasContinuationParameter ? Arrays.copyOfRange(parameterNames, 0, parameterNames.length - 1) : parameterNames);
+
+ command.setParameterOrder(hasCallArg(flags, CONTINUATION_BIT) ? Arrays.copyOfRange(parameterNames, 0, parameterNames.length - 1) : parameterNames);
RequirePermissions cmdPermissions = method.getAnnotation(RequirePermissions.class);
if (cmdPermissions != null) {
@@ -277,6 +275,7 @@ public class ReflectiveRegistration {
command.setDescription();
}
+ boolean hasSenderParameter = hasCallArg(flags, SENDER_BIT);
if (hasSenderParameter && Player.class.isAssignableFrom(senderParameterType)) {
command.addContextFilter(IContextFilter.PLAYER_ONLY);
} else if (hasSenderParameter && ConsoleCommandSender.class.isAssignableFrom(senderParameterType)) {
@@ -287,17 +286,9 @@ public class ReflectiveRegistration {
command.addContextFilter(IContextFilter.CONSOLE_ONLY);
}
- list.setRepeatFinalParameter(parameters.length > start && parameters[parameters.length - 1].isVarArgs());
+ list.setRepeatFinalParameter(callParameters.length > start && callParameters[callParameters.length - 1].isVarArgs());
list.setFinalParameterMayBeFlag(true);
- int flags = 0;
- if (hasContinuationParameter) flags |= 1;
- flags <<= 1;
- if (hasContextParameter) flags |= 1;
- flags <<= 1;
- if (hasSenderParameter) flags |= 1;
- flags <<= 1;
- if (hasReceiverParameter) flags |= 1;
return flags;
}