diff options
Diffstat (limited to 'src/main')
-rw-r--r-- | src/main/java/io/github/moulberry/notenoughupdates/mixins/AccessorCommandHandler.java | 33 | ||||
-rw-r--r-- | src/main/java/io/github/moulberry/notenoughupdates/util/brigadier/NEUBrigadierHook.kt | 92 | ||||
-rw-r--r-- | src/main/kotlin/io/github/moulberry/notenoughupdates/events/RegisterBrigadierCommandEvent.kt | 45 | ||||
-rw-r--r-- | src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/BrigadierRoot.kt (renamed from src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/Rome.kt) | 73 | ||||
-rw-r--r-- | src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/dsl.kt | 21 | ||||
-rw-r--r-- | src/main/resources/mixins.notenoughupdates.json | 1 |
6 files changed, 205 insertions, 60 deletions
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/mixins/AccessorCommandHandler.java b/src/main/java/io/github/moulberry/notenoughupdates/mixins/AccessorCommandHandler.java new file mode 100644 index 00000000..73d31196 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/mixins/AccessorCommandHandler.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.mixins; + +import net.minecraft.command.CommandHandler; +import net.minecraft.command.ICommand; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Set; + +@Mixin(CommandHandler.class) +public interface AccessorCommandHandler { + @Accessor("commandSet") + Set<ICommand> neuGetClientCommands(); +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/brigadier/NEUBrigadierHook.kt b/src/main/java/io/github/moulberry/notenoughupdates/util/brigadier/NEUBrigadierHook.kt new file mode 100644 index 00000000..b8ba7b48 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/brigadier/NEUBrigadierHook.kt @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2023 Linnea Gräf + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.util.brigadier + +import com.mojang.brigadier.ParseResults +import com.mojang.brigadier.exceptions.CommandSyntaxException +import com.mojang.brigadier.suggestion.Suggestions +import com.mojang.brigadier.tree.CommandNode +import net.minecraft.command.CommandBase +import net.minecraft.command.ICommandSender +import net.minecraft.util.BlockPos +import net.minecraft.util.ChatComponentText +import net.minecraft.util.EnumChatFormatting +import java.util.concurrent.CompletableFuture +import java.util.function.Predicate + +/** + * Hook for converting brigadier commands to normal legacy Minecraft commands (string array style). + */ +class NEUBrigadierHook(val brigadierRoot: BrigadierRoot, val commandNode: CommandNode<DefaultSource>) : CommandBase() { + /** + * Runs before the command gets executed. Return false to prevent execution. + */ + var beforeCommand: Predicate<ParseResults<DefaultSource>>? = null + + override fun getCommandName(): String { + return commandNode.name + } + + override fun getCommandUsage(sender: ICommandSender): String { + return brigadierRoot.dispatcher.getAllUsage(commandNode, sender, true).joinToString("\n") + } + + private fun getText(args: Array<out String>) = "${commandNode.name} ${args.joinToString(" ")}" + + override fun processCommand(sender: ICommandSender, args: Array<out String>) { + val results = brigadierRoot.parseText.apply(sender to getText(args).trim()) + if (beforeCommand?.test(results) == false) + return + try { + brigadierRoot.dispatcher.execute(results) + } catch (syntax: CommandSyntaxException) { + sender.addChatMessage(ChatComponentText("${EnumChatFormatting.RED}${syntax.message}")) + } + } + + // We love async tab completion (may end up requiring pressing tab multiple times, but uhhhhh .get() bad) + private var lastCompletionText: String? = null + private var lastCompletion: CompletableFuture<Suggestions>? = null + override fun addTabCompletionOptions( + sender: ICommandSender, + args: Array<out String>, + pos: BlockPos + ): List<String> { + val originalText = getText(args) + var lc: CompletableFuture<Suggestions>? = null + if (lastCompletionText == originalText) { + lc = lastCompletion + } + if (lc == null) { + lastCompletion?.cancel(true) + val results = brigadierRoot.parseText.apply(sender to originalText) + lc = brigadierRoot.dispatcher.getCompletionSuggestions(results) + } + lastCompletion = lc + lastCompletionText = originalText + val suggestions = lastCompletion?.getNow(null) ?: return emptyList() + return suggestions.list.map { it.text } + } + + override fun canCommandSenderUseCommand(sender: ICommandSender): Boolean { + return true // Permissions are checked by brigadier instead (or by the beforeCommand hook) + } + +} diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/events/RegisterBrigadierCommandEvent.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/events/RegisterBrigadierCommandEvent.kt new file mode 100644 index 00000000..1f79d2cd --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/events/RegisterBrigadierCommandEvent.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023 Linnea Gräf + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.events + +import com.mojang.brigadier.builder.LiteralArgumentBuilder +import io.github.moulberry.notenoughupdates.util.brigadier.BrigadierRoot +import io.github.moulberry.notenoughupdates.util.brigadier.NEUBrigadierHook +import io.github.moulberry.notenoughupdates.util.brigadier.literal +import net.minecraft.command.ICommandSender +import java.util.function.Consumer + +data class RegisterBrigadierCommandEvent(val brigadierRoot: BrigadierRoot) : NEUEvent() { + val dispatcher = brigadierRoot.dispatcher + val hooks = mutableListOf<NEUBrigadierHook>() + fun command(name: String, block: Consumer<LiteralArgumentBuilder<ICommandSender>>): NEUBrigadierHook { + return command(name) { + block.accept(this) + } + } + + fun command(name: String, block: LiteralArgumentBuilder<ICommandSender>.() -> Unit): NEUBrigadierHook { + val node = dispatcher.register(literal(name, block)) + val hook = NEUBrigadierHook(brigadierRoot, node) + hooks.add(hook) + return hook + } + +} diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/Rome.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/BrigadierRoot.kt index 7f8f3f74..8af23293 100644 --- a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/Rome.kt +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/BrigadierRoot.kt @@ -30,6 +30,7 @@ import io.github.moulberry.notenoughupdates.autosubscribe.NEUAutoSubscribe import io.github.moulberry.notenoughupdates.commands.dev.DevTestCommand.DEV_TESTERS import io.github.moulberry.notenoughupdates.core.config.GuiPositionEditor import io.github.moulberry.notenoughupdates.core.util.MiscUtils +import io.github.moulberry.notenoughupdates.events.RegisterBrigadierCommandEvent import io.github.moulberry.notenoughupdates.miscfeatures.FishingHelper import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.CustomBiomes import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.LocationChangeEvent @@ -55,11 +56,13 @@ import net.minecraft.util.EnumParticleTypes import net.minecraftforge.client.ClientCommandHandler import net.minecraftforge.common.MinecraftForge import java.util.concurrent.CompletableFuture +import kotlin.math.floor @NEUAutoSubscribe -object Rome { - val dispatcher = CommandDispatcher<DefaultSource>() - private val parseText = +object BrigadierRoot { + var dispatcher = CommandDispatcher<DefaultSource>() + private set + val parseText = LRUCache.memoize<Pair<ICommandSender, String>, ParseResults<DefaultSource>>({ (sender, text) -> dispatcher.parse(text, sender) }, 1) @@ -162,8 +165,8 @@ object Rome { } } thenLiteralExecute("center") { - val x = Math.floor(Minecraft.getMinecraft().thePlayer.posX) + 0.5f - val z = Math.floor(Minecraft.getMinecraft().thePlayer.posZ) + 0.5f + val x = floor(Minecraft.getMinecraft().thePlayer.posX) + 0.5f + val z = floor(Minecraft.getMinecraft().thePlayer.posZ) + 0.5f Minecraft.getMinecraft().thePlayer.setPosition(x, Minecraft.getMinecraft().thePlayer.posY, z) reply("Literal hacks") } @@ -186,56 +189,16 @@ object Rome { fun updateHooks() = registerHooks(ClientCommandHandler.instance) fun registerHooks(handler: ClientCommandHandler) { - dispatcher.root.children.forEach { commandNode -> - if (commandNode.name in handler.commands) return@forEach - handler.registerCommand(object : CommandBase() { - override fun getCommandName(): String { - return commandNode.name - } - - override fun getCommandUsage(sender: ICommandSender): String { - return dispatcher.getAllUsage(commandNode, sender, true).joinToString("\n") - } - - fun getText(args: Array<out String>) = "${commandNode.name} ${args.joinToString(" ")}" - - override fun processCommand(sender: ICommandSender, args: Array<out String>) { - val results = parseText.apply(sender to getText(args).trim()) - try { - dispatcher.execute(results) - } catch (syntax: CommandSyntaxException) { - sender.addChatMessage(ChatComponentText("${RED}${syntax.message}")) - } - } - - var lastCompletionText: String? = null - var lastCompletion: CompletableFuture<Suggestions>? = null - - override fun addTabCompletionOptions( - sender: ICommandSender, - args: Array<out String>, - pos: BlockPos - ): List<String> { - val originalText = getText(args) - var lc: CompletableFuture<Suggestions>? = null - if (lastCompletionText == originalText) { - lc = lastCompletion - } - if (lc == null) { - lastCompletion?.cancel(true) - val results = parseText.apply(sender to originalText) - lc = dispatcher.getCompletionSuggestions(results) - } - lastCompletion = lc - lastCompletionText = originalText - val suggestions = lastCompletion?.getNow(null) ?: return emptyList() - return suggestions.list.map { it.text } - } - - override fun canCommandSenderUseCommand(sender: ICommandSender): Boolean { - return true - } - }) + val iterator = handler.commands.entries.iterator() + while (iterator.hasNext()) { + if (iterator.next().value is NEUBrigadierHook) + iterator.remove() + } + dispatcher = CommandDispatcher() + val event = RegisterBrigadierCommandEvent(this) + event.post() + event.hooks.forEach { + handler.registerCommand(it) } } } diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/dsl.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/dsl.kt index b054f6d9..841322b5 100644 --- a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/dsl.kt +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/dsl.kt @@ -35,11 +35,7 @@ import java.lang.reflect.TypeVariable typealias DefaultSource = ICommandSender -fun literal( - name: String, - block: LiteralArgumentBuilder<DefaultSource>.() -> Unit -): LiteralArgumentBuilder<DefaultSource> = - LiteralArgumentBuilder.literal<DefaultSource>(name).also(block) + private fun normalizeGeneric(argument: Type): Class<*> { return if (argument is Class<*>) { @@ -105,6 +101,21 @@ fun <T : ArgumentBuilder<DefaultSource, T>, AT : Any> T.thenArgument( block: RequiredArgumentBuilder<DefaultSource, AT>.(TypeSafeArg<AT>) -> Unit ): T = then(argument(name, argument, block)) +fun <T : ArgumentBuilder<DefaultSource, T>, AT : Any> T.thenArgumentExecute( + name: String, + argument: ArgumentType<AT>, + block: CommandContext<DefaultSource>.(TypeSafeArg<AT>) -> Unit +): T = thenArgument(name, argument) { + thenExecute { + block(it) + } +} + +fun literal( + name: String, + block: LiteralArgumentBuilder<DefaultSource>.() -> Unit +): LiteralArgumentBuilder<DefaultSource> = + LiteralArgumentBuilder.literal<DefaultSource>(name).also(block) fun <T : ArgumentBuilder<DefaultSource, T>> T.thenLiteral( name: String, diff --git a/src/main/resources/mixins.notenoughupdates.json b/src/main/resources/mixins.notenoughupdates.json index 8877aed8..8a8b10bc 100644 --- a/src/main/resources/mixins.notenoughupdates.json +++ b/src/main/resources/mixins.notenoughupdates.json @@ -5,6 +5,7 @@ "plugin": "io.github.moulberry.notenoughupdates.envcheck.NEUMixinConfigPlugin", "compatibilityLevel": "JAVA_8", "mixins": [ + "AccessorCommandHandler", "AccessorEntityAgeable", "AccessorEntityArmorStand", "AccessorGuiPlayerTabOverlay", |