mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-07-18 18:23:06 +00:00
Decree system
This commit is contained in:
parent
f69c244080
commit
6ba9dc74d8
32
src/main/java/com/volmit/iris/core/decrees/DecreeIris.java
Normal file
32
src/main/java/com/volmit/iris/core/decrees/DecreeIris.java
Normal 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!");
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
@ -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 "";
|
||||
}
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user