From f4abb20ed2b1edb8d988d9e733ae32b97a5664f9 Mon Sep 17 00:00:00 2001 From: Matthew Olsson Date: Thu, 30 Nov 2023 18:33:59 -0700 Subject: [PATCH] DynamicCommands: Allow redirecting to MC CommandNodes --- api/ctjs.api | 1 + .../ctjs/api/commands/DynamicCommands.kt | 10 ++++++ .../ctjs/internal/commands/Command.kt | 14 ++++++-- .../internal/commands/CommandCollection.kt | 11 ++++--- .../ctjs/internal/commands/DynamicCommand.kt | 32 ++++++++++++++----- .../ctjs/internal/commands/StaticCommand.kt | 8 ++--- 6 files changed, 55 insertions(+), 21 deletions(-) diff --git a/api/ctjs.api b/api/ctjs.api index 9f160261..e7665603 100644 --- a/api/ctjs.api +++ b/api/ctjs.api @@ -666,6 +666,7 @@ public final class com/chattriggers/ctjs/api/commands/DynamicCommands : com/chat public static final fun player ()Lcom/mojang/brigadier/arguments/ArgumentType; public static final fun players ()Lcom/mojang/brigadier/arguments/ArgumentType; public static final fun redirect (Lcom/chattriggers/ctjs/api/commands/RootCommand;)V + public static final fun redirect (Lcom/mojang/brigadier/tree/CommandNode;)V public static final fun registerCommand (Ljava/lang/String;)Lcom/chattriggers/ctjs/api/commands/RootCommand; public static final fun registerCommand (Ljava/lang/String;Lorg/mozilla/javascript/Function;)Lcom/chattriggers/ctjs/api/commands/RootCommand; public static synthetic fun registerCommand$default (Ljava/lang/String;Lorg/mozilla/javascript/Function;ILjava/lang/Object;)Lcom/chattriggers/ctjs/api/commands/RootCommand; diff --git a/src/main/kotlin/com/chattriggers/ctjs/api/commands/DynamicCommands.kt b/src/main/kotlin/com/chattriggers/ctjs/api/commands/DynamicCommands.kt index 81633a64..584cf7ce 100644 --- a/src/main/kotlin/com/chattriggers/ctjs/api/commands/DynamicCommands.kt +++ b/src/main/kotlin/com/chattriggers/ctjs/api/commands/DynamicCommands.kt @@ -23,6 +23,7 @@ import com.chattriggers.ctjs.internal.engine.JSLoader import com.chattriggers.ctjs.internal.mixins.commands.EntitySelectorAccessor import com.chattriggers.ctjs.MCEntity import com.chattriggers.ctjs.MCNbtCompound +import com.chattriggers.ctjs.api.client.Client import com.chattriggers.ctjs.internal.utils.asMixin import com.mojang.brigadier.CommandDispatcher import com.mojang.brigadier.ImmutableStringReader @@ -32,7 +33,9 @@ import com.mojang.brigadier.context.CommandContext import com.mojang.brigadier.exceptions.SimpleCommandExceptionType import com.mojang.brigadier.suggestion.Suggestions import com.mojang.brigadier.suggestion.SuggestionsBuilder +import com.mojang.brigadier.tree.CommandNode import net.minecraft.block.pattern.CachedBlockPosition +import net.minecraft.command.CommandSource import net.minecraft.command.EntitySelector import net.minecraft.command.argument.* import net.minecraft.command.argument.AngleArgumentType.Angle @@ -206,6 +209,13 @@ object DynamicCommands : CommandCollection() { currentNode!!.hasRedirect = true } + @JvmStatic + fun redirect(node: CommandNode) { + requireNotNull(currentNode) { "Call to Commands.redirect() outside of Commands.buildCommand()" } + require(!currentNode!!.hasRedirect) { "Duplicate call to Commands.redirect()" } + currentNode!!.children.add(DynamicCommand.Node.RedirectToCommandNode(currentNode, node)) + } + @JvmStatic fun exec(method: Function) { requireNotNull(currentNode) { "Call to Commands.argument() outside of Commands.buildCommand()" } diff --git a/src/main/kotlin/com/chattriggers/ctjs/internal/commands/Command.kt b/src/main/kotlin/com/chattriggers/ctjs/internal/commands/Command.kt index 915fe3c8..35b89494 100644 --- a/src/main/kotlin/com/chattriggers/ctjs/internal/commands/Command.kt +++ b/src/main/kotlin/com/chattriggers/ctjs/internal/commands/Command.kt @@ -3,18 +3,26 @@ package com.chattriggers.ctjs.internal.commands import com.chattriggers.ctjs.internal.mixins.commands.CommandNodeAccessor import com.chattriggers.ctjs.internal.utils.asMixin import com.mojang.brigadier.CommandDispatcher -import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource +import com.mojang.brigadier.arguments.ArgumentType +import com.mojang.brigadier.builder.LiteralArgumentBuilder +import com.mojang.brigadier.builder.RequiredArgumentBuilder +import net.minecraft.command.CommandSource interface Command { val overrideExisting: Boolean val name: String - fun registerImpl(dispatcher: CommandDispatcher) + fun registerImpl(dispatcher: CommandDispatcher) - fun unregisterImpl(dispatcher: CommandDispatcher) { + fun unregisterImpl(dispatcher: CommandDispatcher) { dispatcher.root.asMixin().apply { childNodes.remove(name) literals.remove(name) } } } + +fun literal(name: String) = LiteralArgumentBuilder.literal(name) + +fun argument(name: String, argument: ArgumentType) = + RequiredArgumentBuilder.argument(name, argument) diff --git a/src/main/kotlin/com/chattriggers/ctjs/internal/commands/CommandCollection.kt b/src/main/kotlin/com/chattriggers/ctjs/internal/commands/CommandCollection.kt index 6ace09dc..18ed464c 100644 --- a/src/main/kotlin/com/chattriggers/ctjs/internal/commands/CommandCollection.kt +++ b/src/main/kotlin/com/chattriggers/ctjs/internal/commands/CommandCollection.kt @@ -8,23 +8,24 @@ import com.mojang.brigadier.CommandDispatcher import com.mojang.brigadier.builder.ArgumentBuilder import com.mojang.brigadier.context.CommandContext import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback -import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents +import net.minecraft.command.CommandSource abstract class CommandCollection : Initializer { private val allCommands = mutableSetOf() - private var clientDispatcher: CommandDispatcher? = null - private var networkDispatcher: CommandDispatcher? = null + private var clientDispatcher: CommandDispatcher? = null + private var networkDispatcher: CommandDispatcher? = null + @Suppress("UNCHECKED_CAST") override fun init() { ClientCommandRegistrationCallback.EVENT.register { dispatcher, _ -> - clientDispatcher = dispatcher + clientDispatcher = dispatcher as CommandDispatcher allCommands.forEach { it.registerImpl(dispatcher) } } CTEvents.NETWORK_COMMAND_DISPATCHER_REGISTER.register { dispatcher -> - networkDispatcher = dispatcher + networkDispatcher = dispatcher as CommandDispatcher allCommands.forEach { it.registerImpl(dispatcher) } } diff --git a/src/main/kotlin/com/chattriggers/ctjs/internal/commands/DynamicCommand.kt b/src/main/kotlin/com/chattriggers/ctjs/internal/commands/DynamicCommand.kt index a7e073cd..52a41072 100644 --- a/src/main/kotlin/com/chattriggers/ctjs/internal/commands/DynamicCommand.kt +++ b/src/main/kotlin/com/chattriggers/ctjs/internal/commands/DynamicCommand.kt @@ -10,9 +10,10 @@ import com.mojang.brigadier.CommandDispatcher import com.mojang.brigadier.arguments.ArgumentType import com.mojang.brigadier.builder.ArgumentBuilder import com.mojang.brigadier.builder.LiteralArgumentBuilder +import com.mojang.brigadier.tree.CommandNode import com.mojang.brigadier.tree.LiteralCommandNode import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager -import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource +import net.minecraft.command.CommandSource import org.mozilla.javascript.Function import org.mozilla.javascript.NativeObject import org.mozilla.javascript.ScriptableObject @@ -22,12 +23,12 @@ object DynamicCommand { var method: Function? = null var hasRedirect = false val children = mutableListOf() - var builder: ArgumentBuilder? = null + var builder: ArgumentBuilder? = null open class Literal(parent: Node?, val name: String) : Node(parent) class Root(name: String) : Literal(null, name), RootCommand { - var commandNode: LiteralCommandNode? = null + var commandNode: LiteralCommandNode? = null override fun register() { DynamicCommands.register(CommandImpl(this)) @@ -38,7 +39,9 @@ object DynamicCommand { class Redirect(parent: Node?, val target: Root) : Node(parent) - fun initialize(dispatcher: CommandDispatcher) { + class RedirectToCommandNode(parent: Node?, val target: CommandNode) : Node(parent) + + fun initialize(dispatcher: CommandDispatcher) { if (this is Redirect) { check(method == null) check(children.isEmpty()) @@ -53,9 +56,22 @@ object DynamicCommand { return } + if (this is RedirectToCommandNode) { + check(method == null) + check(children.isEmpty()) + + parent!!.builder!!.redirect(target) { + for ((name, arg) in it.asMixin().arguments) + it.source.asMixin().setContextValue(name, arg.result) + it.source + } + + return + } + builder = when (this) { - is Literal -> ClientCommandManager.literal(name) - is Argument -> ClientCommandManager.argument(name, type) + is Literal -> literal(name) + is Argument -> argument(name, type) else -> throw IllegalStateException("unreachable") } @@ -88,9 +104,9 @@ object DynamicCommand { override val overrideExisting = true override val name = node.name - override fun registerImpl(dispatcher: CommandDispatcher) { + override fun registerImpl(dispatcher: CommandDispatcher) { node.initialize(dispatcher) - val builder = node.builder!! as LiteralArgumentBuilder + val builder = node.builder!! as LiteralArgumentBuilder node.commandNode = dispatcher.register(builder) } } diff --git a/src/main/kotlin/com/chattriggers/ctjs/internal/commands/StaticCommand.kt b/src/main/kotlin/com/chattriggers/ctjs/internal/commands/StaticCommand.kt index 3ee2388a..1e332712 100644 --- a/src/main/kotlin/com/chattriggers/ctjs/internal/commands/StaticCommand.kt +++ b/src/main/kotlin/com/chattriggers/ctjs/internal/commands/StaticCommand.kt @@ -5,9 +5,7 @@ import com.chattriggers.ctjs.internal.mixins.commands.CommandNodeAccessor import com.chattriggers.ctjs.internal.utils.asMixin import com.mojang.brigadier.CommandDispatcher import com.mojang.brigadier.arguments.StringArgumentType -import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument -import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal -import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource +import net.minecraft.command.CommandSource internal class StaticCommand( val trigger: CommandTrigger, @@ -17,7 +15,7 @@ internal class StaticCommand( private val staticSuggestions: List, private val dynamicSuggestions: ((List) -> List)?, ) : Command { - override fun registerImpl(dispatcher: CommandDispatcher) { + override fun registerImpl(dispatcher: CommandDispatcher) { val builder = literal(name) .then(argument("args", StringArgumentType.greedyString()) .suggests { ctx, builder -> @@ -58,7 +56,7 @@ internal class StaticCommand( } } - override fun unregisterImpl(dispatcher: CommandDispatcher) { + override fun unregisterImpl(dispatcher: CommandDispatcher) { super.unregisterImpl(dispatcher) dispatcher.root.asMixin().apply { for (alias in aliases) {