diff options
Diffstat (limited to 'dicore3/command/src/main/java/io/dico/dicore/command/registration')
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; } |