diff --git a/mod/src/main/kotlin/gg/skytils/skytilsmod/core/Config.kt b/mod/src/main/kotlin/gg/skytils/skytilsmod/core/Config.kt index da25ffe0f..6869c9573 100644 --- a/mod/src/main/kotlin/gg/skytils/skytilsmod/core/Config.kt +++ b/mod/src/main/kotlin/gg/skytils/skytilsmod/core/Config.kt @@ -101,7 +101,7 @@ object Config : Vigilant( @Property( type = PropertyType.SWITCH, name = "Command Alias Subcommands", - description = "§b[BETA] Allows the use of spaces in command aliases to target subcommands.\nThis should only be turned on if used.", + description = "§b[BETA] Allows the use of spaces in command alias names.\nThis should only be turned on if used.\n§6You may need to switch servers to apply changes.", category = "General", subcategory = "Command Aliases", i18nName = "skytils.config.general.command_aliases.command_alias_subcommands", i18nCategory = "skytils.config.general", @@ -4440,7 +4440,7 @@ object Config : Vigilant( init { registerListener("commandAliasesSpaces") { prop: Boolean -> - CommandAliases.recreateMap(prop) + CommandAliases.refresh(allowSpaces = prop) } addDependency("showEtherwarpTeleportPosColor", "showEtherwarpTeleportPos") diff --git a/mod/src/main/kotlin/gg/skytils/skytilsmod/features/impl/handlers/CommandAliases.kt b/mod/src/main/kotlin/gg/skytils/skytilsmod/features/impl/handlers/CommandAliases.kt index cafc5cb9d..4e136c48e 100644 --- a/mod/src/main/kotlin/gg/skytils/skytilsmod/features/impl/handlers/CommandAliases.kt +++ b/mod/src/main/kotlin/gg/skytils/skytilsmod/features/impl/handlers/CommandAliases.kt @@ -17,6 +17,8 @@ */ package gg.skytils.skytilsmod.features.impl.handlers +import com.mojang.brigadier.CommandDispatcher +import com.mojang.brigadier.arguments.StringArgumentType import gg.essential.universal.UChat import gg.skytils.event.EventSubscriber import gg.skytils.event.impl.play.ChatMessageSentEvent @@ -26,22 +28,32 @@ import gg.skytils.skytilsmod.Skytils.failPrefix import gg.skytils.skytilsmod.core.PersistentSave import gg.skytils.skytilsmod.utils.runClientCommand import kotlinx.serialization.encodeToString +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.ClientCommandRegistrationCallback +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource import java.io.File import java.io.Reader import java.io.Writer -import java.util.* -import kotlin.collections.sortedMapOf +import java.util.Comparator +import java.util.HashMap +import java.util.IllegalFormatException +import java.util.SortedMap object CommandAliases : PersistentSave(File(Skytils.modDir, "commandaliases.json")), EventSubscriber { val aliases get() = _aliases + private var chatMessageRegister: (() -> Boolean)? = null + + private var commandDispatcher: CommandDispatcher? = null + private var _aliases: MutableMap = hashMapOf() private val comparator = Comparator.comparingInt(String::length) .reversed() .thenComparing(Comparator.naturalOrder()) - fun recreateMap(commandAliasSpaces: Boolean) { + fun recreateMap(commandAliasSpaces: Boolean = Skytils.config.commandAliasesSpaces) { _aliases = if (commandAliasSpaces) sortedMapOf( comparator @@ -60,54 +72,38 @@ object CommandAliases : PersistentSave(File(Skytils.modDir, "commandaliases.json } init { - recreateMap(Skytils.config.commandAliasesSpaces) + recreateMap() } fun onSendChatMessage(event: ChatMessageSentEvent) { - if (event.message.startsWith("/")) { - if (!Skytils.config.commandAliasesSpaces) { - val args = event.message.substring(1).trim().split(" ").toMutableList() - val command = args.removeAt(0) - val replacement = aliases[command] ?: return - event.cancelled = true - try { - val msg = - if (Skytils.config.commandAliasMode == 0) "/${replacement} ${args.joinToString(" ")}" else "/${ - replacement.format( - *args.toTypedArray() - ) - }" - if (event.addToHistory) { - mc.inGameHud.chatHud.addToMessageHistory(msg) - } - if (runClientCommand(msg) != 0) return - Skytils.sendMessageQueue.add(msg) - } catch (ignored: IllegalFormatException) { - if (event.addToHistory) mc.inGameHud.chatHud.addToMessageHistory(event.message) - UChat.chat("$failPrefix §cYou did not specify the correct amount of arguments for this alias!") - } - } else { - val candidate = event.message.substring(1).trim() - val replacement = aliases.entries.find { candidate == it.key || candidate.startsWith(it.key + ' ') } ?: return - val args = candidate.removePrefix(replacement.key).trim() - event.cancelled = true - try { - val msg = - if (Skytils.config.commandAliasMode == 0) "/${replacement.value} $args" else "/${ - replacement.value.format( - *args.split(" ").toTypedArray() - ) - }" - if (event.addToHistory) { - mc.inGameHud.chatHud.addToMessageHistory(msg) - } - if (runClientCommand(msg) != 0) return - Skytils.sendMessageQueue.add(msg) - } catch (ignored: IllegalFormatException) { - if (event.addToHistory) mc.inGameHud.chatHud.addToMessageHistory(event.message) - UChat.chat("$failPrefix §cYou did not specify the correct amount of arguments for this alias!") - } + if (!event.message.startsWith("/")) return + + val candidate = event.message.substring(1).trim() + + val entry = aliases.entries + .find { (key, _) -> + ' ' in key && (candidate == key || candidate.startsWith("$key ")) + } ?: return + + val (key, template) = entry + val args = candidate.removePrefix(key).trim() + + event.cancelled = true + try { + val msg = + if (Skytils.config.commandAliasMode == 0) "/$template $args" else "/${ + template.format( + *args.split(" ").toTypedArray() + ) + }" + if (event.addToHistory) { + mc.inGameHud.chatHud.addToMessageHistory(msg) } + if (runClientCommand(msg) != 0) return + Skytils.sendMessageQueue.add(msg) + } catch (_: IllegalFormatException) { + if (event.addToHistory) mc.inGameHud.chatHud.addToMessageHistory(event.message) + UChat.chat("$failPrefix §cYou did not specify the correct amount of arguments for this alias!") } } @@ -125,6 +121,78 @@ object CommandAliases : PersistentSave(File(Skytils.modDir, "commandaliases.json } override fun setup() { - register(::onSendChatMessage) + ClientCommandRegistrationCallback.EVENT.register { dispatcher, _ -> + commandDispatcher = dispatcher + refresh(dispatcher) + } + } + + fun refresh(dispatcher: CommandDispatcher? = commandDispatcher, allowSpaces: Boolean = Skytils.config.commandAliasesSpaces, customRemovalKeys: Set? = null) { + if (dispatcher == null) return + + recreateMap(allowSpaces) + + if (allowSpaces) { + if (chatMessageRegister == null) { + chatMessageRegister = register(::onSendChatMessage) + } + } else { + chatMessageRegister?.invoke() + chatMessageRegister = null + } + + dispatcher.root.children + .removeIf { node -> aliases.containsKey(node.name) || (customRemovalKeys?.contains(node.name) == true) } // Due to how minecraft works, even after removal, the command will only be recognized as removed after switching servers. + + aliases.forEach { (alias, template) -> + val hasSpace = ' ' in alias + + if (!allowSpaces && hasSpace) return@forEach + + val literalNode = literal(alias) + .then( + argument("args", StringArgumentType.greedyString()) + .apply { + if (!hasSpace) { + this.executes { ctx -> + val raw = StringArgumentType.getString(ctx, "args") + runAlias(alias, template, raw) + 1 + } + } + } + ) + + if (!hasSpace) { + literalNode.executes { ctx -> + runAlias(alias, template, "") + 1 + } + } + + dispatcher.register(literalNode) + } + } + + private fun runAlias( + alias: String, + template: String, + rawArgs: String + ) { + val args = if (rawArgs.isBlank()) emptyList() else rawArgs.split(" ") + val msg = try { + when (Skytils.config.commandAliasMode) { + 0 -> "/" + listOf(template, *args.toTypedArray()).joinToString(" ") + else -> "/" + template.format(*args.toTypedArray()) + } + } catch (_: IllegalFormatException) { + mc.inGameHud.chatHud.addToMessageHistory("/$alias $rawArgs") + UChat.chat("$failPrefix §cWrong number of arguments for alias '$alias'!") + return + } + + mc.inGameHud.chatHud.addToMessageHistory(msg) + if (runClientCommand(msg) != 0) return + Skytils.sendMessageQueue.add(msg) } } \ No newline at end of file diff --git a/mod/src/main/kotlin/gg/skytils/skytilsmod/gui/features/CommandAliasesGui.kt b/mod/src/main/kotlin/gg/skytils/skytilsmod/gui/features/CommandAliasesGui.kt index ccb7f09e4..70c27d4da 100644 --- a/mod/src/main/kotlin/gg/skytils/skytilsmod/gui/features/CommandAliasesGui.kt +++ b/mod/src/main/kotlin/gg/skytils/skytilsmod/gui/features/CommandAliasesGui.kt @@ -30,8 +30,10 @@ import gg.essential.elementa.constraints.RelativeConstraint import gg.essential.elementa.constraints.SiblingConstraint import gg.essential.elementa.dsl.* import gg.essential.elementa.effects.OutlineEffect +import gg.essential.universal.UChat import gg.essential.universal.UKeyboard import gg.essential.vigilance.utils.onLeftClick +import gg.skytils.skytilsmod.Skytils.prefix import gg.skytils.skytilsmod.core.PersistentSave import gg.skytils.skytilsmod.features.impl.handlers.CommandAliases import gg.skytils.skytilsmod.gui.ReopenableGUI @@ -145,6 +147,7 @@ class CommandAliasesGui : WindowScreen(ElementaVersion.V2, newGuiScale = 2), Reo } override fun onScreenClose() { + val aliasKeys = CommandAliases.aliases.keys.toSet() // Make sure to copy with toSet() super.onScreenClose() CommandAliases.aliases.clear() @@ -160,5 +163,7 @@ class CommandAliasesGui : WindowScreen(ElementaVersion.V2, newGuiScale = 2), Reo } PersistentSave.markDirty() + CommandAliases.refresh(customRemovalKeys = aliasKeys) + UChat.chat("$prefix §6You may need to switch servers to apply changes to aliases.") } } \ No newline at end of file