From 6ba9dc74d8cb839c9cbb4f5d35495e81ca1f1fc6 Mon Sep 17 00:00:00 2001 From: cyberpwn Date: Fri, 13 Aug 2021 10:39:40 -0400 Subject: [PATCH] Decree system --- .../volmit/iris/core/decrees/DecreeIris.java | 32 ++ .../volmit/iris/util/collection/KList.java | 5 + .../iris/util/decree/DecreeCategory.java | 94 ---- .../iris/util/decree/DecreeExecutor.java | 33 ++ .../volmit/iris/util/decree/DecreeNode.java | 30 +- .../iris/util/decree/DecreeParameter.java | 10 +- .../util/decree/DecreeParameterHandler.java | 11 +- .../volmit/iris/util/decree/DecreeSystem.java | 142 ++++-- .../com/volmit/iris/util/decree/Example.java | 48 -- .../iris/util/decree/annotations/Decree.java | 10 +- .../iris/util/decree/annotations/Param.java | 2 +- .../exceptions/DecreeInstanceException.java | 10 - .../exceptions/DecreeWhichException.java | 4 +- .../decree/virtual/VirtualDecreeCommand.java | 429 ++++++++++++++++++ .../com/volmit/iris/util/scheduling/J.java | 4 + 15 files changed, 648 insertions(+), 216 deletions(-) create mode 100644 src/main/java/com/volmit/iris/core/decrees/DecreeIris.java delete mode 100644 src/main/java/com/volmit/iris/util/decree/DecreeCategory.java create mode 100644 src/main/java/com/volmit/iris/util/decree/DecreeExecutor.java delete mode 100644 src/main/java/com/volmit/iris/util/decree/Example.java delete mode 100644 src/main/java/com/volmit/iris/util/decree/exceptions/DecreeInstanceException.java create mode 100644 src/main/java/com/volmit/iris/util/decree/virtual/VirtualDecreeCommand.java diff --git a/src/main/java/com/volmit/iris/core/decrees/DecreeIris.java b/src/main/java/com/volmit/iris/core/decrees/DecreeIris.java new file mode 100644 index 000000000..f7ec71182 --- /dev/null +++ b/src/main/java/com/volmit/iris/core/decrees/DecreeIris.java @@ -0,0 +1,32 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2021 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.core.decrees; + +import com.volmit.iris.util.decree.DecreeExecutor; +import com.volmit.iris.util.decree.annotations.Decree; + +@Decree(name = "irisd", aliases = {"ird"}, description = "Basic Command") +public class DecreeIris implements DecreeExecutor +{ + @Decree(description = "Ping self", aliases = "p") + public void ping() + { + sender().sendMessage("Pong!"); + } +} diff --git a/src/main/java/com/volmit/iris/util/collection/KList.java b/src/main/java/com/volmit/iris/util/collection/KList.java index 40bab0cd2..cf6baac33 100644 --- a/src/main/java/com/volmit/iris/util/collection/KList.java +++ b/src/main/java/com/volmit/iris/util/collection/KList.java @@ -696,4 +696,9 @@ public class KList extends ArrayList implements List { t.shuffle(rng); return t; } + + public KList qdrop() { + pop(); + return this; + } } diff --git a/src/main/java/com/volmit/iris/util/decree/DecreeCategory.java b/src/main/java/com/volmit/iris/util/decree/DecreeCategory.java deleted file mode 100644 index 3971aa86e..000000000 --- a/src/main/java/com/volmit/iris/util/decree/DecreeCategory.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Bukkit Servers - * Copyright (c) 2021 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.volmit.iris.util.decree; - -import com.volmit.iris.util.collection.KList; -import com.volmit.iris.util.decree.annotations.Decree; -import com.volmit.iris.util.decree.annotations.Param; -import com.volmit.iris.util.decree.exceptions.DecreeInstanceException; - -import java.lang.reflect.*; -import java.util.Arrays; - -public class DecreeCategory { - private final Class clazz; - private final Decree decree; - - public DecreeCategory(Class clazz) throws DecreeInstanceException { - this.clazz = clazz; - this.decree = clazz.getDeclaredAnnotation(Decree.class); - if (decree == null){ - throw new DecreeInstanceException("Cannot instantiate DecreeCategory on class not annotated by @Decree"); - } - } - - /** - * Get the subcommands of this decree category - * @return The list of subcommands if ALL are only classes implementing DecreeCommand, else null - */ - public KList getCommands() { - KList c = new KList<>(); - - for(Field i : clazz.getFields()) - { - try { - i.setAccessible(true); - if (DecreeCommand.class.isAssignableFrom(i.getType())) { - c.add((DecreeCommand) i.getType().getConstructor().newInstance()); - } - } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) { - e.printStackTrace(); - } - } - - return c; - } - - public String getName() { - return decree.name().equals(Decree.METHOD_NAME) ? clazz.getName() : decree.name(); - } - - public DecreeOrigin getOrigin() { - return decree.origin(); - } - - public String getDescription() { - return decree.description().isEmpty() ? Decree.DEFAULT_DESCRIPTION : decree.description(); - } - - public KList getAliases() { - KList d = new KList<>(); - - if (Arrays.equals(decree.aliases(), new String[]{Decree.NO_ALIASES})){ - return d; - } - - for(String i : decree.aliases()) - { - if(i.isEmpty()) - { - continue; - } - - d.add(i); - } - - return d; - } -} diff --git a/src/main/java/com/volmit/iris/util/decree/DecreeExecutor.java b/src/main/java/com/volmit/iris/util/decree/DecreeExecutor.java new file mode 100644 index 000000000..57c73514d --- /dev/null +++ b/src/main/java/com/volmit/iris/util/decree/DecreeExecutor.java @@ -0,0 +1,33 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2021 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.util.decree; + +import com.volmit.iris.util.plugin.VolmitSender; + +public interface DecreeExecutor { + default VolmitSender sender() + { + return DecreeContext.get(); + } + + default T get(T v, T ifUndefined) + { + return v == null ? ifUndefined : v; + } +} diff --git a/src/main/java/com/volmit/iris/util/decree/DecreeNode.java b/src/main/java/com/volmit/iris/util/decree/DecreeNode.java index 6e11a33c2..e1d02713e 100644 --- a/src/main/java/com/volmit/iris/util/decree/DecreeNode.java +++ b/src/main/java/com/volmit/iris/util/decree/DecreeNode.java @@ -21,21 +21,24 @@ package com.volmit.iris.util.decree; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.decree.annotations.Decree; import com.volmit.iris.util.decree.annotations.Param; -import com.volmit.iris.util.decree.exceptions.DecreeInstanceException; +import lombok.Data; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.Arrays; +@Data public class DecreeNode { private final Method method; + private final Object instance; private final Decree decree; - public DecreeNode(Method method) throws DecreeInstanceException { + public DecreeNode(Object instance, Method method) { + this.instance = instance; this.method = method; this.decree = method.getDeclaredAnnotation(Decree.class); if (decree == null){ - throw new DecreeInstanceException("Cannot instantiate DecreeNode on method not annotated by @Decree"); + throw new RuntimeException("Cannot instantiate DecreeNode on method " + method.getName() + " in " + method.getDeclaringClass().getCanonicalName() + " not annotated by @Decree"); } } @@ -48,18 +51,14 @@ public class DecreeNode { for(Parameter i : method.getParameters()) { - try { - p.add(new DecreeParameter(i)); - } catch (DecreeInstanceException ignored) { - return null; - } + p.add(new DecreeParameter(i)); } return p; } public String getName() { - return decree.name().equals(Decree.METHOD_NAME) ? method.getName() : decree.name(); + return decree.name().isEmpty() ? method.getName() : decree.name(); } public DecreeOrigin getOrigin() { @@ -70,13 +69,9 @@ public class DecreeNode { return decree.description().isEmpty() ? Decree.DEFAULT_DESCRIPTION : decree.description(); } - public KList getAliases() { + public KList getNames() { KList d = new KList<>(); - if (Arrays.equals(decree.aliases(), new String[]{Decree.NO_ALIASES})){ - return d; - } - for(String i : decree.aliases()) { if(i.isEmpty()) @@ -87,6 +82,13 @@ public class DecreeNode { d.add(i); } + + d.add(getName()); + d.removeDuplicates(); return d; } + + public boolean isSync() { + return decree.sync(); + } } diff --git a/src/main/java/com/volmit/iris/util/decree/DecreeParameter.java b/src/main/java/com/volmit/iris/util/decree/DecreeParameter.java index de55ba881..4993e5ab1 100644 --- a/src/main/java/com/volmit/iris/util/decree/DecreeParameter.java +++ b/src/main/java/com/volmit/iris/util/decree/DecreeParameter.java @@ -20,7 +20,6 @@ package com.volmit.iris.util.decree; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.decree.annotations.Param; -import com.volmit.iris.util.decree.exceptions.DecreeInstanceException; import java.lang.reflect.Parameter; import java.util.Arrays; @@ -29,11 +28,11 @@ public class DecreeParameter { private final Parameter parameter; private final Param param; - public DecreeParameter(Parameter parameter) throws DecreeInstanceException { + public DecreeParameter(Parameter parameter) { this.parameter = parameter; this.param = parameter.getDeclaredAnnotation(Param.class); if (param == null){ - throw new DecreeInstanceException("Cannot instantiate DecreeParameter on parameter not annotated by @Param"); + throw new RuntimeException("Cannot instantiate DecreeParameter on " + parameter.getName() + " in method " + parameter.getDeclaringExecutable().getName() + "(...) in class " + parameter.getDeclaringExecutable().getDeclaringClass().getCanonicalName() + " not annotated by @Param"); } } @@ -57,7 +56,7 @@ public class DecreeParameter { return param.value().equals(Param.REQUIRED); } - public KList getAliases() { + public KList getNames() { KList d = new KList<>(); if (Arrays.equals(param.aliases(), new String[]{Param.NO_ALIAS})){ @@ -74,6 +73,9 @@ public class DecreeParameter { d.add(i); } + d.add(getName()); + d.removeDuplicates(); + return d; } } diff --git a/src/main/java/com/volmit/iris/util/decree/DecreeParameterHandler.java b/src/main/java/com/volmit/iris/util/decree/DecreeParameterHandler.java index 7929f0163..7aae1b91d 100644 --- a/src/main/java/com/volmit/iris/util/decree/DecreeParameterHandler.java +++ b/src/main/java/com/volmit/iris/util/decree/DecreeParameterHandler.java @@ -48,19 +48,10 @@ public interface DecreeParameterHandler { /** * Returns whether a certain type is supported by this handler
- * By default, this checks if the {@link #parse(String) parse} method returns the corresponding type. - * Hence, this should only be overwritten if multiple types, outside the designated one, are supported. * @param type The type to check * @return True if supported, false if not */ - default boolean supports(Class type){ - try { - if (this.getClass().getMethod("parse", String.class).getReturnType().equals(type)){ - return true; - } - } catch (NoSuchMethodException ignored){} - return false; - } + boolean supports(Class type); /** * The possible entries for the inputted string (support for autocomplete on partial entries) diff --git a/src/main/java/com/volmit/iris/util/decree/DecreeSystem.java b/src/main/java/com/volmit/iris/util/decree/DecreeSystem.java index 55f6d09a2..7cf9e79fd 100644 --- a/src/main/java/com/volmit/iris/util/decree/DecreeSystem.java +++ b/src/main/java/com/volmit/iris/util/decree/DecreeSystem.java @@ -20,24 +20,124 @@ package com.volmit.iris.util.decree; import com.volmit.iris.Iris; import com.volmit.iris.util.collection.KList; -import com.volmit.iris.util.decree.annotations.Decree; -import com.volmit.iris.util.math.M; -import org.checkerframework.checker.units.qual.K; +import com.volmit.iris.util.decree.virtual.VirtualDecreeCommand; +import com.volmit.iris.util.plugin.VolmitSender; +import com.volmit.iris.util.scheduling.J; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; +import java.util.List; -public interface DecreeSystem { +public interface DecreeSystem extends CommandExecutor, TabCompleter { KList> handlers = Iris.initialize("com.volmit.iris.util.decree.handlers", null).convert((i) -> (DecreeParameterHandler) i); - /** - * Should return the root command
- * Root must extend {@link DecreeCommand} - * - * @return The root command class (this#getClass) - */ - Class getRoot(); + VirtualDecreeCommand getRoot(); + + default boolean call(VolmitSender sender, String[] args) + { + DecreeContext.touch(sender); + return getRoot().invoke(sender, enhanceArgs(args)); + } + + @Nullable + @Override + default List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { + return new KList<>(); + } + + @Override + default boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + J.aBukkit(() -> call(new VolmitSender(sender), args)); + return true; + } + + static KList enhanceArgs(String[] args) + { + KList a = new KList<>(); + + if(args.length == 0) + { + return a; + } + + StringBuilder flat = new StringBuilder(); + for(String i : args) + { + if(i.trim().isEmpty()) + { + continue; + } + + flat.append(" ").append(i.trim()); + } + + flat = new StringBuilder(flat.substring(1).trim()); + StringBuilder arg = new StringBuilder(); + boolean quoting = false; + + for(int x = 0; x < flat.length(); x++) + { + char i = flat.charAt(x); + char j = x < flat.length() - 1 ? flat.charAt(x + 1) : i; + boolean hasNext = x < flat.length(); + + if(i == ' ' && !quoting) + { + if(!arg.toString().trim().isEmpty()) + { + a.add(arg.toString().trim()); + arg = new StringBuilder(); + } + } + + else if(i == '"') + { + if(!quoting && (arg.length() == 0)) + { + quoting = true; + } + + else if(quoting) + { + quoting = false; + + if(hasNext && j == ' ') + { + if(!arg.toString().trim().isEmpty()) + { + a.add(arg.toString().trim()); + arg = new StringBuilder(); + } + } + + else if(!hasNext) + { + if(!arg.toString().trim().isEmpty()) + { + a.add(arg.toString().trim()); + arg = new StringBuilder(); + } + } + } + } + + else + { + arg.append(i); + } + } + + if(!arg.toString().trim().isEmpty()) + { + a.add(arg.toString().trim()); + } + + return a; + } /** * Get the handler for the specified type @@ -55,18 +155,4 @@ public interface DecreeSystem { } return null; } - - /** - * Gets the method command to from the raw command parameters - * @param command The raw command parameters - * @return The @{@link Decree} method - */ - default Method getRootCommandFrom(String[] command){ - return getCommandFrom(new KList<>(command), getRoot()); - } - - - default Method getCommandFrom(KList command, Class decree){ - return null; - } } diff --git a/src/main/java/com/volmit/iris/util/decree/Example.java b/src/main/java/com/volmit/iris/util/decree/Example.java deleted file mode 100644 index a888050cf..000000000 --- a/src/main/java/com/volmit/iris/util/decree/Example.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Bukkit Servers - * Copyright (c) 2021 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.volmit.iris.util.decree; - -import com.volmit.iris.util.decree.annotations.Decree; -import com.volmit.iris.util.decree.annotations.Param; -import org.bukkit.entity.Player; - -@Decree(description = "Description goes here!", aliases = {"ex", "e"}) -// The description here is shown when hovering over elements in the chat -// The parameter `name` is undefined, which means it defaults to the name of the class, lowercase, so "example" -// The aliases defined give alternate options for calling this category -// You can also define "origin" which gives who can send the command. -// By default, if omitted, this is DecreeOrigin.BOTH, but it can be .PLAYER & .CONSOLE -public class Example implements DecreeCommand { - - // This subcommand, given that it implements DecreeCommand, is automatically indexed and recognised by Decree. - // The way this command is called isn't defined from here. - // Since subCommand can have another name than in /iris example subCommand. - SubExample subCommand; - - @Decree(description = "Kick a player", aliases = "k", origin = DecreeOrigin.CONSOLE) - public void kick( - @Param(name = "player", description = "The Player to kick from the server", aliases = "p") - Player player, - @Param(name = "reason", description = "A reason to kick the player for", value = "No reason!", aliases = "k") - String reason) - { - player.kickPlayer(reason); - DecreeContext.get().sendMessage("Kicked " + player.getName()); - } -} diff --git a/src/main/java/com/volmit/iris/util/decree/annotations/Decree.java b/src/main/java/com/volmit/iris/util/decree/annotations/Decree.java index 4d447d66d..02987adec 100644 --- a/src/main/java/com/volmit/iris/util/decree/annotations/Decree.java +++ b/src/main/java/com/volmit/iris/util/decree/annotations/Decree.java @@ -29,16 +29,14 @@ import java.lang.annotation.Target; @Target({ElementType.METHOD, ElementType.TYPE}) public @interface Decree { - String METHOD_NAME = "Default Method Name"; - String DEFAULT_DESCRIPTION = "No Description Provided"; - String NO_ALIASES = "No Aliases"; - /** * The name of this command, which is the Method's name by default */ - String name() default METHOD_NAME; + String name() default ""; + + boolean sync() default false; /** * The description of this command.
@@ -58,5 +56,5 @@ public @interface Decree { * Can be initialized as just a string (ex. "alias") or as an array (ex. {"alias1", "alias2"})
* If someone uses /plugin foo and you specify alias="f" here, /plugin f will do the exact same. */ - String[] aliases() default {NO_ALIASES}; + String[] aliases() default ""; } diff --git a/src/main/java/com/volmit/iris/util/decree/annotations/Param.java b/src/main/java/com/volmit/iris/util/decree/annotations/Param.java index 79b682a7c..022934464 100644 --- a/src/main/java/com/volmit/iris/util/decree/annotations/Param.java +++ b/src/main/java/com/volmit/iris/util/decree/annotations/Param.java @@ -28,7 +28,7 @@ import java.lang.annotation.Target; public @interface Param { String REQUIRED = "REQUIRED"; - String NO_ALIAS = "No Aliases Provided"; + String NO_ALIAS = ""; String DEFAULT_DESCRIPTION = "No Description Provided"; diff --git a/src/main/java/com/volmit/iris/util/decree/exceptions/DecreeInstanceException.java b/src/main/java/com/volmit/iris/util/decree/exceptions/DecreeInstanceException.java deleted file mode 100644 index f15bd3720..000000000 --- a/src/main/java/com/volmit/iris/util/decree/exceptions/DecreeInstanceException.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.volmit.iris.util.decree.exceptions; - -/** - * Thrown when classes are instantiated that fail because of a missing or faulty decree component - */ -public class DecreeInstanceException extends Exception { - public DecreeInstanceException(String message){ - super(message); - } -} diff --git a/src/main/java/com/volmit/iris/util/decree/exceptions/DecreeWhichException.java b/src/main/java/com/volmit/iris/util/decree/exceptions/DecreeWhichException.java index 3401fdd9a..1a34ec754 100644 --- a/src/main/java/com/volmit/iris/util/decree/exceptions/DecreeWhichException.java +++ b/src/main/java/com/volmit/iris/util/decree/exceptions/DecreeWhichException.java @@ -18,12 +18,14 @@ package com.volmit.iris.util.decree.exceptions; +import com.volmit.iris.util.collection.KList; + /** * Thrown when more than one option is available for a singular mapping
* Like having a hashmap where one input maps to two outputs. */ public class DecreeWhichException extends Exception{ public DecreeWhichException() { - super("More than one option for the entered input"); + super(); } } diff --git a/src/main/java/com/volmit/iris/util/decree/virtual/VirtualDecreeCommand.java b/src/main/java/com/volmit/iris/util/decree/virtual/VirtualDecreeCommand.java new file mode 100644 index 000000000..2640fa832 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/decree/virtual/VirtualDecreeCommand.java @@ -0,0 +1,429 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2021 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.util.decree.virtual; + +import com.volmit.iris.Iris; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.decree.DecreeContext; +import com.volmit.iris.util.decree.DecreeNode; +import com.volmit.iris.util.decree.DecreeParameter; +import com.volmit.iris.util.decree.DecreeSystem; +import com.volmit.iris.util.decree.annotations.Decree; +import com.volmit.iris.util.decree.exceptions.DecreeParsingException; +import com.volmit.iris.util.decree.exceptions.DecreeWhichException; +import com.volmit.iris.util.format.C; +import com.volmit.iris.util.plugin.VolmitSender; +import com.volmit.iris.util.scheduling.J; +import lombok.Data; +import lombok.Getter; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Locale; +import java.util.Objects; + +@Data +public class VirtualDecreeCommand { + private final Class type; + private final VirtualDecreeCommand parent; + private final KList nodes; + private final DecreeNode node; + + private VirtualDecreeCommand(Class type, VirtualDecreeCommand parent, KList nodes, DecreeNode node) + { + this.parent = parent; + this.type = type; + this.nodes = nodes; + this.node = node; + } + + public static VirtualDecreeCommand createRoot(Object v) throws Throwable { + return createRoot(null, v); + } + + public static VirtualDecreeCommand createRoot(VirtualDecreeCommand parent, Object v) throws Throwable { + VirtualDecreeCommand c = new VirtualDecreeCommand(v.getClass(), parent, new KList<>(), null); + + for(Field i : v.getClass().getDeclaredFields()) + { + if(Modifier.isStatic(i.getModifiers()) || Modifier.isFinal(i.getModifiers())|| Modifier.isTransient(i.getModifiers())|| Modifier.isVolatile(i.getModifiers())) + { + continue; + } + + if(!i.getType().isAnnotationPresent(Decree.class)) + { + continue; + } + + i.setAccessible(true); + Object childRoot = i.get(v); + + if(childRoot == null) + { + i.set(v, i.getType().getConstructor().newInstance()); + } + + c.getNodes().add(createRoot(c, v)); + } + + for(Method i : v.getClass().getDeclaredMethods()) + { + if(Modifier.isStatic(i.getModifiers()) || Modifier.isFinal(i.getModifiers()) || Modifier.isPrivate(i.getModifiers())) + { + continue; + } + + if(!i.isAnnotationPresent(Decree.class)) + { + continue; + } + + c.getNodes().add(new VirtualDecreeCommand(v.getClass(), c, new KList<>(), new DecreeNode(v, i))); + } + + return c; + } + + public String getPath() + { + KList n = new KList<>(); + VirtualDecreeCommand cursor = this; + + while(cursor.getParent() != null) + { + cursor = cursor.getParent(); + n.add(cursor.getName()); + } + + return "/" + n.reverse().qadd(getName()).toString(" "); + } + + public String getName() + { + return isNode() ? getNode().getName() : getType().getDeclaredAnnotation(Decree.class).name(); + } + + public String getDescription() + { + return isNode() ? getNode().getDescription() : getType().getDeclaredAnnotation(Decree.class).description(); + } + + public KList getNames() + { + if(isNode()) + { + return getNode().getNames(); + } + + KList d = new KList<>(); + Decree dc = getType().getDeclaredAnnotation(Decree.class); + for(String i : dc.aliases()) + { + if(i.isEmpty()) + { + continue; + } + + d.add(i); + } + + d.add(dc.name()); + d.removeDuplicates(); + + return d; + } + + public boolean isNode() + { + return node != null; + } + + private KMap map(VolmitSender sender, KList in) + { + KMap data = new KMap<>(); + + for(int ix = 0; ix < in.size(); ix++) + { + String i = in.get(ix); + if(i.contains("=")) + { + String[] v = i.split("\\Q=\\E"); + String key = v[0]; + String value = v[1]; + DecreeParameter param = null; + + for(DecreeParameter j : getNode().getParameters()) + { + for(String k : j.getNames()) + { + if(k.equalsIgnoreCase(key)) + { + param = j; + break; + } + } + } + + if(param == null) + { + for(DecreeParameter j : getNode().getParameters()) + { + for(String k : j.getNames()) + { + if(k.toLowerCase().contains(key.toLowerCase()) || key.toLowerCase().contains(k.toLowerCase())) + { + param = j; + break; + } + } + } + } + + if(param == null) + { + Iris.debug("Can't find parameter key for " + key + "=" + value + " in " + getPath()); + // TODO: WARN UNKNOWN PARAMETER + continue; + } + + key = param.getName(); + + try { + data.put(key, param.getHandler().parse(value)); + } catch (DecreeParsingException e) { + Iris.debug("Can't parse parameter value for " + key + "=" + value + " in " + getPath() + " using handler " + param.getHandler().getClass().getSimpleName()); + // TODO: WARN BAD PARAM + return null; + } catch (DecreeWhichException e) { + KList validOptions = param.getHandler().getPossibilities(value); + Iris.debug("Found multiple results for " + key + "=" + value + " in " + getPath() + " using the handler " + param.getHandler().getClass().getSimpleName() + " with potential matches [" + validOptions.toString(",") + "]. Asking client to define one"); + String update = null; // TODO: PICK ONE + Iris.debug("Client chose " + update + " for " + key + "=" + value + " (old) in " + getPath()); + in.set(ix--, update); + } + } + + else + { + try + { + DecreeParameter par = getNode().getParameters().get(ix); + try { + data.put(par.getName(), par.getHandler().parse(i)); + } catch (DecreeParsingException e) { + Iris.debug("Can't parse parameter value for " + par.getNames() + "=" + i + " in " + getPath() + " using handler " + par.getHandler().getClass().getSimpleName()); + // TODO: WARN BAD PARAM + return null; + } catch (DecreeWhichException e) { + Iris.debug("Can't parse parameter value for " + par.getNames() + "=" + i + " in " + getPath() + " using handler " + par.getHandler().getClass().getSimpleName()); + KList validOptions = par.getHandler().getPossibilities(i); + String update = null; + Iris.debug("Client chose " + update + " for " + par.getNames() + "=" + i + " (old) in " + getPath()); + in.set(ix--, update); + } + } + + catch(ArrayIndexOutOfBoundsException e) + { + // TODO: Ignoring parameter (not in method anywhere + } + } + } + + return data; + } + + public boolean invoke(VolmitSender sender, KList realArgs) + { + return invoke(sender, realArgs, new KList<>()); + } + + public boolean invoke(VolmitSender sender, KList args, KList skip) + { + Iris.debug("@ " + getPath() + " with " + args.toString(", ")); + if(isNode()) + { + Iris.debug("Invoke " +getPath() + "(" + args.toString(",") + ") at "); + if(invokeNode(sender, map(sender, args))) + { + return true; + } + + skip.add(hashCode()); + return false; + } + + if(args.isEmpty()) + { + int m = getNodes().size(); + + if(getNodes().isNotEmpty()) + { + for(VirtualDecreeCommand i : getNodes()) + { + sender.sendMessage(i.getPath() + " - " + i.getDescription()); + } + } + + else + { + sender.sendMessage(C.RED + "There are no subcommands in this group! Contact support, this is a command design issue!"); + } + + return true; + } + + String head = args.get(0); + VirtualDecreeCommand match = matchNode(head, skip); + + if(match != null) + { + args.pop(); + return match.invoke(sender, args, skip); + } + + skip.add(hashCode()); + + return false; + } + + private boolean invokeNode(VolmitSender sender, KMap map) { + if(map == null) + { + return false; + } + + Object[] params = new Object[getNode().getMethod().getParameterCount()]; + int vm = 0; + for(DecreeParameter i : getNode().getParameters()) + { + Object value = map.get(i.getName()); + + if(value == null) + { + if(i.isRequired()) + { + // TODO: REQUIRED... UNDEFINED + return false; + } + } + + params[vm] = value; + vm++; + } + + Runnable rx = () -> { + try + { + DecreeContext.touch(sender); + getNode().getMethod().setAccessible(true); + getNode().getMethod().invoke(getNode().getInstance(), params); + } + + catch(Throwable e) + { + e.printStackTrace(); + throw new RuntimeException("Failed to execute "); // TODO: + } + }; + + if(getNode().isSync()) + { + J.s(rx); + } + + else + { + rx.run(); + } + + return true; + } + + public VirtualDecreeCommand matchNode(String in, KList skip) + { + for(VirtualDecreeCommand i : nodes) + { + if(skip.contains(i.hashCode())) + { + continue; + } + + if(i.matches(in)) + { + return i; + } + } + + for(VirtualDecreeCommand i : nodes) + { + if(skip.contains(i.hashCode())) + { + continue; + } + + if(i.deepMatches(in)) + { + return i; + } + } + + return null; + } + + public boolean deepMatches(String in) + { + KList a = getNames(); + + for(String i : a) + { + if(i.toLowerCase().contains(in.toLowerCase()) || in.toLowerCase().contains(i.toLowerCase())) + { + return true; + } + } + + return false; + } + + @Override + public int hashCode(){ + return Objects.hash(getName(), getDescription(), getType(), getPath()); + } + + public boolean matches(String in) + { + KList a = getNames(); + + for(String i : a) + { + if(i.equalsIgnoreCase(in)) + { + return true; + } + } + + return false; + } +} diff --git a/src/main/java/com/volmit/iris/util/scheduling/J.java b/src/main/java/com/volmit/iris/util/scheduling/J.java index 1f3c10c33..3ecda344d 100644 --- a/src/main/java/com/volmit/iris/util/scheduling/J.java +++ b/src/main/java/com/volmit/iris/util/scheduling/J.java @@ -83,6 +83,10 @@ public class J { }); } + public static void aBukkit(Runnable a) { + Bukkit.getScheduler().scheduleAsyncDelayedTask(Iris.instance, a); + } + public static Future a(Callable a) { return MultiBurst.burst.lazySubmit(a); }