diff --git a/server/src/main/java/dev/plex/command/PlexBrigadierCommand.java b/server/src/main/java/dev/plex/command/PlexBrigadierCommand.java index 95e17a3..476f76f 100644 --- a/server/src/main/java/dev/plex/command/PlexBrigadierCommand.java +++ b/server/src/main/java/dev/plex/command/PlexBrigadierCommand.java @@ -4,14 +4,16 @@ import com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource; import com.google.common.collect.Maps; import com.google.gson.GsonBuilder; import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.*; import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.tree.CommandNode; +import com.mojang.brigadier.tree.LiteralCommandNode; import dev.plex.Plex; import dev.plex.cache.DataUtils; -import dev.plex.command.annotation.CommandName; -import dev.plex.command.annotation.CommandPermission; -import dev.plex.command.annotation.Default; -import dev.plex.command.annotation.SubCommand; +import dev.plex.command.annotation.*; +import dev.plex.command.source.RequiredCommandSource; import dev.plex.player.PlexPlayer; import dev.plex.util.PlexLog; import dev.plex.util.PlexUtils; @@ -25,10 +27,10 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; +import java.lang.System; import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; +import java.lang.reflect.Parameter; +import java.util.*; /** * @author Taah @@ -47,8 +49,6 @@ public abstract class PlexBrigadierCommand { final Object dedicatedServer = ReflectionsUtil.callFunction(getCraftServer(), "getServer"); final Object minecraftServer = Class.forName("net.minecraft.server.MinecraftServer").cast(dedicatedServer); - // System.out.println(Arrays.toString(Arrays.stream(minecraftServer.getClass().getDeclaredMethods()).map(Method::getName).toArray(String[]::new))); - final Object serverFunctionsManager = ReflectionsUtil.callFunction(minecraftServer, "aA"); this.commandDispatcher = ReflectionsUtil.callFunction(serverFunctionsManager, "b"); } @@ -60,7 +60,10 @@ public abstract class PlexBrigadierCommand if (!this.getClass().isAnnotationPresent(CommandName.class)) { - PlexLog.error("Cannot find command name for class " + this.getClass().getName()); + if (this.commandDispatcher != null) + { + this.commandDispatcher.register(execute()); + } return; } @@ -89,128 +92,203 @@ public abstract class PlexBrigadierCommand if (this.commandDispatcher != null) { - for (String s : commandName) + for (String name : commandName) { - PlexLog.debug("registering command " + s); - LiteralArgumentBuilder builder = LiteralArgumentBuilder.literal(s.toLowerCase()); + LiteralArgumentBuilder builder = LiteralArgumentBuilder.literal(name.toLowerCase()); + for (Map.Entry stringMethodEntry : subcommands.entrySet()) { - PlexLog.debug("registering subcommand " + stringMethodEntry.getKey()); String[] subCommandArgs = stringMethodEntry.getKey().split(" "); - LiteralArgumentBuilder parentBuilder = LiteralArgumentBuilder.literal(subCommandArgs[0]); - LiteralArgumentBuilder currSubCommand = parentBuilder; - if (subCommandArgs.length == 1) + LinkedList> builders = new LinkedList<>(); + for (int i = 0; i < subCommandArgs.length; i++) { - parentBuilder.executes(context -> - { - if (stringMethodEntry.getValue().isAnnotationPresent(CommandPermission.class)) - { - String permission = stringMethodEntry.getValue().getAnnotation(CommandPermission.class).value(); - if (!context.getSource().getBukkitSender().hasPermission(permission)) - { - send(context, PlexUtils.messageString("noPermissionNode", permission)); - return 0; - } - } - try - { - stringMethodEntry.getValue().invoke(this, context.getSource().getBukkitSender()); - } - catch (Exception e) - { - PlexLog.error(e.getMessage()); - for (StackTraceElement stackTraceElement : e.getStackTrace()) - { - PlexLog.error(stackTraceElement.toString()); - } - return 0; - } - return 1; - }); + LiteralArgumentBuilder newNode = LiteralArgumentBuilder.literal(subCommandArgs[i]); + builders.addLast(newNode); } - else + + if (builders.size() == 1) { - for (int i = 1; i < subCommandArgs.length; i++) + LiteralArgumentBuilder parent = builders.removeFirst(); + LinkedList> argumentBuilders = new LinkedList<>(); + + LinkedHashMap> arguments = getArguments(stringMethodEntry.getValue()); + for (Map.Entry> parameterArgumentBuilderEntry : arguments.entrySet()) { - LiteralArgumentBuilder curr = LiteralArgumentBuilder.literal(subCommandArgs[i]); - if (i == subCommandArgs.length - 1) - { - curr.executes(context -> - { - if (stringMethodEntry.getValue().isAnnotationPresent(CommandPermission.class)) - { - String permission = stringMethodEntry.getValue().getAnnotation(CommandPermission.class).value(); - if (!context.getSource().getBukkitSender().hasPermission(permission)) - { - send(context, PlexUtils.messageString("noPermissionNode", permission)); - return 0; - } - } - try - { - stringMethodEntry.getValue().invoke(this, context.getSource().getBukkitSender()); - } - catch (Exception e) - { - PlexLog.error(e.getMessage()); - for (StackTraceElement stackTraceElement : e.getStackTrace()) - { - PlexLog.error(stackTraceElement.toString()); - } - return 0; - } - return 1; - }); - } - currSubCommand.then(curr); - currSubCommand = curr; + argumentBuilders.addLast(parameterArgumentBuilderEntry.getValue()); } + boolean setExecution = false; + CommandNode parentArg = null; + CommandNode currArg = null; + while (!argumentBuilders.isEmpty()) + { + if (parentArg == null) + { + RequiredArgumentBuilder newParent = argumentBuilders.removeFirst(); + if (argumentBuilders.isEmpty()) + { + newParent.executes(context -> execute(stringMethodEntry.getValue(), context, arguments.keySet())); + setExecution = true; + } + parentArg = newParent.build(); + parent.then(parentArg); + currArg = parentArg; + } + else + { + RequiredArgumentBuilder newCurr = argumentBuilders.removeFirst(); + if (argumentBuilders.isEmpty()) + { + newCurr.executes(context -> execute(stringMethodEntry.getValue(), context, arguments.keySet())); + setExecution = true; + } + CommandNode newCurrNode = newCurr.build(); + currArg.addChild(newCurrNode); + currArg = newCurrNode; + } + } + if (!setExecution) + { + parent.executes(context -> execute(stringMethodEntry.getValue(), context, arguments.keySet())); + } + builder.then(parent); } - - PlexLog.debug(new GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create().toJson(parentBuilder)); - - builder = builder.then( - parentBuilder - ); + else if (builders.size() > 1) + { + LiteralCommandNode parent = builders.removeFirst().build(); + LiteralCommandNode curr = null; + while (!builders.isEmpty()) + { + LiteralArgumentBuilder newCurr = builders.removeFirst(); + PlexLog.debug("Adding subcommand " + newCurr.getLiteral()); + if (builders.isEmpty()) + { + LinkedList> argumentBuilders = new LinkedList<>(); + LinkedHashMap> arguments = getArguments(stringMethodEntry.getValue()); + for (Map.Entry> parameterArgumentBuilderEntry : arguments.entrySet()) + { + argumentBuilders.addLast(parameterArgumentBuilderEntry.getValue()); + } + boolean setExecution = false; + CommandNode parentArg = null; + CommandNode currArg = null; + while (!argumentBuilders.isEmpty()) + { + if (parentArg == null) + { + RequiredArgumentBuilder newParent = argumentBuilders.removeFirst(); + if (argumentBuilders.isEmpty()) + { + newParent.executes(context -> execute(stringMethodEntry.getValue(), context, arguments.keySet())); + setExecution = true; + } + parentArg = newParent.build(); + newCurr.then(parentArg); + currArg = parentArg; + } + else + { + RequiredArgumentBuilder newCurrArg = argumentBuilders.removeFirst(); + if (argumentBuilders.isEmpty()) + { + newCurrArg.executes(context -> execute(stringMethodEntry.getValue(), context, arguments.keySet())); + setExecution = true; + } + CommandNode newCurrNode = newCurrArg.build(); + currArg.addChild(newCurrNode); + currArg = newCurrNode; + } + } + if (!setExecution) + { + newCurr.executes(context -> execute(stringMethodEntry.getValue(), context, arguments.keySet())); + } + } + if (curr == null) + { + LiteralCommandNode temp = newCurr.build(); + parent.addChild(temp); + curr = temp; + } + else + { + LiteralCommandNode temp = newCurr.build(); + curr.addChild(temp); + curr = temp; + } + } + builder.then(parent); + } + PlexLog.debug("Overall Builder: " + new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create().toJson(builder)); } + if (defaultMethod != null) { - PlexLog.debug("registering default method"); - Method finalDefaultMethod = defaultMethod; - finalDefaultMethod.setAccessible(true); - builder = builder.executes(context -> + LinkedList> argumentBuilders = new LinkedList<>(); + LinkedHashMap> arguments = getArguments(defaultMethod); + for (Map.Entry> parameterArgumentBuilderEntry : arguments.entrySet()) { - if (finalDefaultMethod.isAnnotationPresent(CommandPermission.class)) + argumentBuilders.addLast(parameterArgumentBuilderEntry.getValue()); + } + boolean setExecution = false; + CommandNode parentArg = null; + CommandNode currArg = null; + while (!argumentBuilders.isEmpty()) + { + if (parentArg == null) { - String permission = finalDefaultMethod.getAnnotation(CommandPermission.class).value(); - if (!context.getSource().getBukkitSender().hasPermission(permission)) + RequiredArgumentBuilder newParent = argumentBuilders.removeFirst(); + if (argumentBuilders.isEmpty()) { - send(context, PlexUtils.messageString("noPermissionNode", permission)); - return 0; + Method finalDefaultMethod = defaultMethod; + newParent.executes(context -> execute(finalDefaultMethod, context, arguments.keySet())); + setExecution = true; } + parentArg = newParent.build(); + builder.then(parentArg); + currArg = parentArg; } - try + else { - finalDefaultMethod.invoke(this, context.getSource().getBukkitSender()); - } - catch (Exception e) - { - PlexLog.error(e.getMessage()); - for (StackTraceElement stackTraceElement : e.getStackTrace()) + RequiredArgumentBuilder newCurrArg = argumentBuilders.removeFirst(); + if (argumentBuilders.isEmpty()) { - PlexLog.error(stackTraceElement.toString()); + Method finalDefaultMethod1 = defaultMethod; + newCurrArg.executes(context -> execute(finalDefaultMethod1, context, arguments.keySet())); + setExecution = true; } - return 0; + CommandNode newCurrNode = newCurrArg.build(); + currArg.addChild(newCurrNode); + currArg = newCurrNode; } - return 1; - }); + } + if (!setExecution) + { + Method finalDefaultMethod2 = defaultMethod; + builder.executes(context -> execute(finalDefaultMethod2, context, arguments.keySet())); + } } - PlexLog.debug(new GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create().toJson(builder)); + this.commandDispatcher.register(builder); } + + this.commandDispatcher.register(LiteralArgumentBuilder.literal("testing") + .then(RequiredArgumentBuilder.argument("test0", IntegerArgumentType.integer()) + .then(RequiredArgumentBuilder.argument("test", StringArgumentType.word()) + .then(RequiredArgumentBuilder.argument("test1", StringArgumentType.word()) + .executes(context -> + { + send(context, context.getArgument("test", String.class)); + send(context, context.getArgument("test1", String.class)); + return 1; + }))))); } } + public LiteralArgumentBuilder execute() + { + return LiteralArgumentBuilder.literal(this.getClass().getName().toLowerCase()); + } + /** * Gets a PlexPlayer from Player object * @@ -355,6 +433,128 @@ public abstract class PlexBrigadierCommand return player.getUniqueId(); } + private LinkedHashMap> getArguments(Method method) + { + LinkedHashMap> result = new LinkedHashMap<>(); + if (!method.canAccess(this)) + { + method.setAccessible(true); + } + for (Parameter parameter : method.getParameters()) + { + if (parameter.isAnnotationPresent(Argument.class)) + { + Argument argument = parameter.getAnnotation(Argument.class); + if (String.class.isAssignableFrom(parameter.getType())) + { + result.put(parameter, RequiredArgumentBuilder.argument(argument.value(), argument.argumentType() == StringArgumentType.StringType.SINGLE_WORD ? StringArgumentType.word() : StringArgumentType.greedyString())); + } + else if (int.class.isAssignableFrom(parameter.getType())) + { + result.put(parameter, RequiredArgumentBuilder.argument(argument.value(), IntegerArgumentType.integer(argument.min() == Double.MIN_VALUE ? Integer.MIN_VALUE : (int) argument.min(), argument.max() == Double.MAX_VALUE ? Integer.MAX_VALUE : (int) argument.max()))); + } + else if (double.class.isAssignableFrom(parameter.getType())) + { + result.put(parameter, RequiredArgumentBuilder.argument(argument.value(), DoubleArgumentType.doubleArg(argument.min(), argument.max()))); + } + else if (float.class.isAssignableFrom(parameter.getType())) + { + result.put(parameter, RequiredArgumentBuilder.argument(argument.value(), FloatArgumentType.floatArg(argument.min() == Double.MIN_VALUE ? Float.MIN_VALUE : (int) argument.min(), argument.max() == Double.MAX_VALUE ? Float.MAX_VALUE : (int) argument.max()))); + } + else if (boolean.class.isAssignableFrom(parameter.getType())) + { + result.put(parameter, RequiredArgumentBuilder.argument(argument.value(), BoolArgumentType.bool())); + } + else if (long.class.isAssignableFrom(parameter.getType())) + { + result.put(parameter, RequiredArgumentBuilder.argument(argument.value(), LongArgumentType.longArg(argument.min() == Double.MIN_VALUE ? Long.MIN_VALUE : (int) argument.min(), argument.max() == Double.MAX_VALUE ? Long.MAX_VALUE : (int) argument.max()))); + } + } + } + return result; + } + + private Object getArgument(Class clazz, CommandContext context, String name) + { + if (String.class.isAssignableFrom(clazz)) + { + return StringArgumentType.getString(context, name); + } + else if (int.class.isAssignableFrom(clazz)) + { + return IntegerArgumentType.getInteger(context, name); + } + else if (double.class.isAssignableFrom(clazz)) + { + return DoubleArgumentType.getDouble(context, name); + } + else if (float.class.isAssignableFrom(clazz)) + { + return FloatArgumentType.getFloat(context, name); + } + else if (boolean.class.isAssignableFrom(clazz)) + { + return BoolArgumentType.getBool(context, name); + } + else if (long.class.isAssignableFrom(clazz)) + { + return LongArgumentType.getLong(context, name); + } + return null; + } + + private int execute(Method method, CommandContext context, Set arguments) + { + if (method.isAnnotationPresent(CommandPermission.class)) + { + String permission = method.getAnnotation(CommandPermission.class).value(); + if (!context.getSource().getBukkitSender().hasPermission(permission)) + { + send(context, PlexUtils.messageComponent("noPermissionNode", permission)); + return 1; + } + } + try + { + List params = arguments + .stream().map(bukkitBrigadierCommandSourceArgumentBuilder -> getArgument(bukkitBrigadierCommandSourceArgumentBuilder.getType(), context, bukkitBrigadierCommandSourceArgumentBuilder.getAnnotation(Argument.class).value())).toList(); + LinkedList parameters = new LinkedList<>(params); +// parameters.addFirst(context.getSource().getBukkitSender()); + if (method.isAnnotationPresent(CommandSource.class)) { + RequiredCommandSource commandSource = method.getAnnotation(CommandSource.class).value(); + if (commandSource == RequiredCommandSource.IN_GAME) { + if (!(context.getSource().getBukkitSender() instanceof Player player)) { + send(context, PlexUtils.messageComponent("noPermissionConsole")); + return 1; + } else { + parameters.addFirst(player); + } + } else if (commandSource == RequiredCommandSource.CONSOLE) { + if (context.getSource().getBukkitSender() instanceof Player) { + send(context, PlexUtils.messageComponent("noPermissionInGame")); + return 1; + } + parameters.addFirst(context.getSource().getBukkitSender()); + } else { + parameters.addFirst(context.getSource().getBukkitSender()); + } + } + System.out.println(Arrays.toString(parameters.stream().map(Object::getClass).map(Class::getName).toArray())); + System.out.println(Arrays.toString(Arrays.stream(method.getParameterTypes()).map(Class::getName).toArray())); + method.invoke(this, parameters.toArray()); + return 1; + } + catch (Exception e) + { + PlexLog.error(e.getMessage()); + for (StackTraceElement stackTraceElement : e.getStackTrace()) + { + PlexLog.error(stackTraceElement.toString()); + } + return 0; + } + } + private Object getCraftServer() { String nmsVersion = Bukkit.getServer().getClass().getPackage().getName(); diff --git a/server/src/main/java/dev/plex/command/annotation/Argument.java b/server/src/main/java/dev/plex/command/annotation/Argument.java new file mode 100644 index 0000000..aa99ef3 --- /dev/null +++ b/server/src/main/java/dev/plex/command/annotation/Argument.java @@ -0,0 +1,25 @@ +package dev.plex.command.annotation; + +import com.mojang.brigadier.arguments.StringArgumentType; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Taah + * @project Plex + * @since 4:31 AM [08-07-2023] + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface Argument +{ + String value(); + + StringArgumentType.StringType argumentType() default StringArgumentType.StringType.SINGLE_WORD; + + double min() default Double.MIN_VALUE; + double max() default Double.MAX_VALUE; +} diff --git a/server/src/main/java/dev/plex/command/annotation/CommandSource.java b/server/src/main/java/dev/plex/command/annotation/CommandSource.java new file mode 100644 index 0000000..93fdd74 --- /dev/null +++ b/server/src/main/java/dev/plex/command/annotation/CommandSource.java @@ -0,0 +1,20 @@ +package dev.plex.command.annotation; + +import dev.plex.command.source.RequiredCommandSource; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Taah + * @project Plex + * @since 7:08 AM [09-07-2023] + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface CommandSource +{ + RequiredCommandSource value() default RequiredCommandSource.ANY; +} diff --git a/server/src/main/java/dev/plex/command/impl/brigadier/AdminChatCMD.java b/server/src/main/java/dev/plex/command/impl/brigadier/AdminChatCMD.java new file mode 100644 index 0000000..d3e9cea --- /dev/null +++ b/server/src/main/java/dev/plex/command/impl/brigadier/AdminChatCMD.java @@ -0,0 +1,33 @@ +package dev.plex.command.impl.brigadier; + +import dev.plex.cache.DataUtils; +import dev.plex.command.PlexBrigadierCommand; +import dev.plex.command.annotation.CommandName; +import dev.plex.command.annotation.CommandPermission; +import dev.plex.command.annotation.CommandSource; +import dev.plex.command.annotation.Default; +import dev.plex.command.source.RequiredCommandSource; +import dev.plex.player.PlexPlayer; +import org.apache.commons.lang3.BooleanUtils; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import static dev.plex.util.PlexUtils.messageComponent; + +/** + * @author Taah + * @project Plex + * @since 6:54 AM [09-07-2023] + */ +@CommandName({"adminchat", "o", "sc", "ac", "staffchat"}) +public class AdminChatCMD extends PlexBrigadierCommand +{ + @Default + @CommandPermission("plex.adminchat") + @CommandSource(RequiredCommandSource.IN_GAME) + public void toggle(Player sender) { + PlexPlayer player = DataUtils.getPlayer(sender.getUniqueId()); + player.setStaffChat(!player.isStaffChat()); + send(sender, messageComponent("adminChatToggled", BooleanUtils.toStringOnOff(player.isStaffChat()))); + } +} diff --git a/server/src/main/java/dev/plex/command/impl/brigadier/PlexBrigadierCMD.java b/server/src/main/java/dev/plex/command/impl/brigadier/PlexBrigadierCMD.java index 3a557fb..544a718 100644 --- a/server/src/main/java/dev/plex/command/impl/brigadier/PlexBrigadierCMD.java +++ b/server/src/main/java/dev/plex/command/impl/brigadier/PlexBrigadierCMD.java @@ -1,17 +1,11 @@ package dev.plex.command.impl.brigadier; -import com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource; -import com.mojang.brigadier.builder.LiteralArgumentBuilder; import dev.plex.command.PlexBrigadierCommand; -import dev.plex.command.annotation.CommandName; -import dev.plex.command.annotation.CommandPermission; -import dev.plex.command.annotation.Default; -import dev.plex.command.annotation.SubCommand; +import dev.plex.command.annotation.*; import dev.plex.command.exception.CommandFailException; import dev.plex.module.PlexModule; import dev.plex.module.PlexModuleFile; import dev.plex.util.BuildInfo; -import dev.plex.util.PlexUtils; import dev.plex.util.TimeUtils; import org.apache.commons.lang3.StringUtils; import org.bukkit.command.CommandSender; @@ -23,7 +17,7 @@ import java.util.stream.Collectors; * @project Plex * @since 3:46 PM [07-07-2023] */ -@CommandName({"plex"}) +@CommandName({"plex", "pplexx"}) public class PlexBrigadierCMD extends PlexBrigadierCommand { @SubCommand("reload") @@ -54,6 +48,14 @@ public class PlexBrigadierCMD extends PlexBrigadierCommand send(sender, "Plex successfully reloaded."); } + /*@SubCommand("test yourmom") + @CommandPermission("plex.test") + public void testPlex(CommandSender sender, @Argument(value = "test", min = 0, max = 100) int i, @Argument(value = "test2", min = 0, max = 100) int j*//*, @Argument(value = "param", argumentType = StringArgumentType.StringType.GREEDY_PHRASE) String param*//*) { +// send(sender, String.valueOf(i)); + send(sender, String.valueOf(j)); +// send(sender, param); + }*/ + @SubCommand("redis") @CommandPermission("plex.redis") public void testRedis(CommandSender sender) @@ -83,6 +85,15 @@ public class PlexBrigadierCMD extends PlexBrigadierCommand send(sender, mmString("All modules reloaded!")); } + /*@SubCommand("modules testing more args") + @CommandPermission("plex.modules.reload") + public void viewViewModules(CommandSender sender) + { + send(sender, "leave me alone"); +// send(sender, mmString("Modules (" + plugin.getModuleManager().getModules().size() + "): " + StringUtils.join(plugin.getModuleManager().getModules().stream().map(PlexModule::getPlexModuleFile).map(PlexModuleFile::getName).collect(Collectors.toList()), ", "))); + + }*/ + @Default public void defaultCommand(CommandSender sender) {