Decree system

This commit is contained in:
cyberpwn 2021-08-13 10:39:40 -04:00
parent f69c244080
commit 6ba9dc74d8
15 changed files with 648 additions and 216 deletions

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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!");
}
}

View File

@ -696,4 +696,9 @@ public class KList<T> extends ArrayList<T> implements List<T> {
t.shuffle(rng);
return t;
}
public KList<T> qdrop() {
pop();
return this;
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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<DecreeCommand> getCommands() {
KList<DecreeCommand> 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<String> getAliases() {
KList<String> 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;
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util.decree;
import com.volmit.iris.util.plugin.VolmitSender;
public interface DecreeExecutor {
default VolmitSender sender()
{
return DecreeContext.get();
}
default <T> T get(T v, T ifUndefined)
{
return v == null ? ifUndefined : v;
}
}

View File

@ -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<String> getAliases() {
public KList<String> getNames() {
KList<String> 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();
}
}

View File

@ -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<String> getAliases() {
public KList<String> getNames() {
KList<String> 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;
}
}

View File

@ -48,19 +48,10 @@ public interface DecreeParameterHandler<T> {
/**
* Returns whether a certain type is supported by this handler<br>
* 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)

View File

@ -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<DecreeParameterHandler<?>> handlers = Iris.initialize("com.volmit.iris.util.decree.handlers", null).convert((i) -> (DecreeParameterHandler<?>) i);
/**
* Should return the root command<br>
* Root must extend {@link DecreeCommand}
*
* @return The root command class (this#getClass)
*/
Class<? extends DecreeCommand> getRoot();
VirtualDecreeCommand getRoot();
default boolean call(VolmitSender sender, String[] args)
{
DecreeContext.touch(sender);
return getRoot().invoke(sender, enhanceArgs(args));
}
@Nullable
@Override
default List<String> 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<String> enhanceArgs(String[] args)
{
KList<String> 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<String> command, Class<? extends DecreeCommand> decree){
return null;
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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());
}
}

View File

@ -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.<br>
@ -58,5 +56,5 @@ public @interface Decree {
* Can be initialized as just a string (ex. "alias") or as an array (ex. {"alias1", "alias2"})<br>
* 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 "";
}

View File

@ -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";

View File

@ -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);
}
}

View File

@ -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<br>
* 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();
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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<VirtualDecreeCommand> nodes;
private final DecreeNode node;
private VirtualDecreeCommand(Class<?> type, VirtualDecreeCommand parent, KList<VirtualDecreeCommand> 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<String> 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<String> getNames()
{
if(isNode())
{
return getNode().getNames();
}
KList<String> 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<String, Object> map(VolmitSender sender, KList<String> in)
{
KMap<String, Object> 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<String> realArgs)
{
return invoke(sender, realArgs, new KList<>());
}
public boolean invoke(VolmitSender sender, KList<String> args, KList<Integer> 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<String, Object> 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 <INSERT REAL NODE HERE>"); // TODO:
}
};
if(getNode().isSync())
{
J.s(rx);
}
else
{
rx.run();
}
return true;
}
public VirtualDecreeCommand matchNode(String in, KList<Integer> 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<String> 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<String> a = getNames();
for(String i : a)
{
if(i.equalsIgnoreCase(in))
{
return true;
}
}
return false;
}
}

View File

@ -83,6 +83,10 @@ public class J {
});
}
public static void aBukkit(Runnable a) {
Bukkit.getScheduler().scheduleAsyncDelayedTask(Iris.instance, a);
}
public static <T> Future<T> a(Callable<T> a) {
return MultiBurst.burst.lazySubmit(a);
}