From 9fabe241cfe613b836335aa918b1a2423bfde31b Mon Sep 17 00:00:00 2001 From: nea Date: Fri, 20 Jan 2023 09:49:36 +0100 Subject: brigadier wip --- .../commands/dev/DevTestCommand.java | 7 +- .../util/brigadier/EnumArgumentType.kt | 64 ++++++ .../util/brigadier/RestArgumentType.kt | 31 +++ .../notenoughupdates/util/brigadier/Rome.kt | 241 +++++++++++++++++++++ .../notenoughupdates/util/brigadier/dsl.kt | 133 ++++++++++++ .../moulberry/notenoughupdates/util/iterate.kt | 28 +++ 6 files changed, 503 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/EnumArgumentType.kt create mode 100644 src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/RestArgumentType.kt create mode 100644 src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/Rome.kt create mode 100644 src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/dsl.kt create mode 100644 src/main/kotlin/io/github/moulberry/notenoughupdates/util/iterate.kt (limited to 'src') diff --git a/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java b/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java index cf266dca..3a2c39ca 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java @@ -19,6 +19,8 @@ package io.github.moulberry.notenoughupdates.commands.dev; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; import io.github.moulberry.notenoughupdates.BuildFlags; import io.github.moulberry.notenoughupdates.NotEnoughUpdates; import io.github.moulberry.notenoughupdates.commands.ClientCommandBase; @@ -49,9 +51,12 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; +import static com.mojang.brigadier.arguments.IntegerArgumentType.integer; +import static com.mojang.brigadier.builder.RequiredArgumentBuilder.argument; + public class DevTestCommand extends ClientCommandBase { - private static final List DEV_TESTERS = + public static final List DEV_TESTERS = Arrays.asList( "d0e05de7-6067-454d-beae-c6d19d886191", // moulberry "66502b40-6ac1-4d33-950d-3df110297aab", // lucycoconut diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/EnumArgumentType.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/EnumArgumentType.kt new file mode 100644 index 00000000..14b6ed6e --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/EnumArgumentType.kt @@ -0,0 +1,64 @@ +/* + * 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 . + */ + +package io.github.moulberry.notenoughupdates.util.brigadier + +import com.mojang.brigadier.LiteralMessage +import com.mojang.brigadier.StringReader +import com.mojang.brigadier.arguments.ArgumentType +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 java.util.concurrent.CompletableFuture + +class EnumArgumentType>( + val values: List +) : ArgumentType { + companion object { + @JvmStatic + fun > enum(values: Array) = EnumArgumentType(values.toList()) + + inline fun > enum() = enum(enumValues()) + } + + override fun getExamples(): Collection { + return values.map { it.name } + } + + override fun listSuggestions( + context: CommandContext, + builder: SuggestionsBuilder + ): CompletableFuture { + + examples + .filter {builder.remaining.isBlank() || it.startsWith(builder.remaining, ignoreCase = true) } + .forEach { builder.suggest(it) } + return builder.buildFuture() + } + + private val invalidEnum = + SimpleCommandExceptionType(LiteralMessage("Expected one of: ${values.joinToString(", ")}")) + + override fun parse(reader: StringReader): T { + val enumName = reader.readString() + return values.find { enumName == it.name } + ?: throw invalidEnum.createWithContext(reader) + } +} diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/RestArgumentType.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/RestArgumentType.kt new file mode 100644 index 00000000..adfdae6a --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/RestArgumentType.kt @@ -0,0 +1,31 @@ +/* + * 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 . + */ + +package io.github.moulberry.notenoughupdates.util.brigadier + +import com.mojang.brigadier.StringReader +import com.mojang.brigadier.arguments.ArgumentType + +object RestArgumentType : ArgumentType { + override fun parse(reader: StringReader): String { + val remaining = reader.remaining + reader.cursor += remaining.length + return remaining + } +} diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/Rome.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/Rome.kt new file mode 100644 index 00000000..7f8f3f74 --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/Rome.kt @@ -0,0 +1,241 @@ +/* + * 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 . + */ + +package io.github.moulberry.notenoughupdates.util.brigadier + +import com.mojang.brigadier.CommandDispatcher +import com.mojang.brigadier.ParseResults +import com.mojang.brigadier.arguments.StringArgumentType.string +import com.mojang.brigadier.exceptions.CommandSyntaxException +import com.mojang.brigadier.suggestion.Suggestions +import io.github.moulberry.notenoughupdates.BuildFlags +import io.github.moulberry.notenoughupdates.NotEnoughUpdates +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.miscfeatures.FishingHelper +import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.CustomBiomes +import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.LocationChangeEvent +import io.github.moulberry.notenoughupdates.miscgui.GuiPriceGraph +import io.github.moulberry.notenoughupdates.miscgui.minionhelper.MinionHelperManager +import io.github.moulberry.notenoughupdates.util.LRUCache +import io.github.moulberry.notenoughupdates.util.PronounDB +import io.github.moulberry.notenoughupdates.util.SBInfo +import io.github.moulberry.notenoughupdates.util.TabListUtils +import io.github.moulberry.notenoughupdates.util.brigadier.EnumArgumentType.Companion.enum +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.GuiScreen +import net.minecraft.command.CommandBase +import net.minecraft.command.ICommandSender +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.launchwrapper.Launch +import net.minecraft.util.BlockPos +import net.minecraft.util.ChatComponentText +import net.minecraft.util.EnumChatFormatting +import net.minecraft.util.EnumChatFormatting.GOLD +import net.minecraft.util.EnumChatFormatting.RED +import net.minecraft.util.EnumParticleTypes +import net.minecraftforge.client.ClientCommandHandler +import net.minecraftforge.common.MinecraftForge +import java.util.concurrent.CompletableFuture + +@NEUAutoSubscribe +object Rome { + val dispatcher = CommandDispatcher() + private val parseText = + LRUCache.memoize, ParseResults>({ (sender, text) -> + dispatcher.parse(text, sender) + }, 1) + + init { + dispatcher.register(literal("neudevtest2") { + requires { + DEV_TESTERS.contains((it as? EntityPlayer)?.uniqueID?.toString()) + || Launch.blackboard.get("fml.deobfuscatedEnvironment") as Boolean + } + thenLiteralExecute("profileinfo") { + val currentProfile = SBInfo.getInstance().currentProfile + val gamemode = SBInfo.getInstance().getGamemodeForProfile(currentProfile) + reply("${GOLD}You are on Profile $currentProfile with the mode $gamemode") + } + thenLiteralExecute("buildflags") { + reply("BuildFlags: \n" + + BuildFlags.getAllFlags().entries + .joinToString(("\n")) { (key, value) -> " + $key - $value" }) + } + thenLiteral("exteditor") { + thenExecute { + reply("Your external editor is: §Z${NotEnoughUpdates.INSTANCE.config.hidden.externalEditor}") + } + thenArgument("editor", string()) { newEditor -> + thenExecute { + NotEnoughUpdates.INSTANCE.config.hidden.externalEditor = this[newEditor] + reply("You changed your external editor to: §Z${this[newEditor]}") + } + } + } + thenLiteral("pricetest") { + thenExecute { + NotEnoughUpdates.INSTANCE.manager.auctionManager.updateBazaar() + } + thenArgument("item", string()) { item -> + thenExecute { + NotEnoughUpdates.INSTANCE.openGui = GuiPriceGraph(this[item]) + } + } + } + thenLiteralExecute("zone") { + val target = Minecraft.getMinecraft().objectMouseOver.blockPos + ?: Minecraft.getMinecraft().thePlayer.position + val zone = CustomBiomes.INSTANCE.getSpecialZone(target) + listOf( + ChatComponentText("Showing Zone Info for: $target"), + ChatComponentText("Zone: " + (zone?.name ?: "null")), + ChatComponentText("Location: " + SBInfo.getInstance().getLocation()), + ChatComponentText("Biome: " + CustomBiomes.INSTANCE.getCustomBiome(target)) + ).forEach { component -> + reply(component) + } + MinecraftForge.EVENT_BUS.post( + LocationChangeEvent( + SBInfo.getInstance().getLocation(), SBInfo + .getInstance() + .getLocation() + ) + ) + } + thenLiteralExecute("positiontest") { + NotEnoughUpdates.INSTANCE.openGui = GuiPositionEditor() + } + thenLiteral("pt") { + thenArgument("particle", enum()) { particle -> + thenExecute { + FishingHelper.type = this[particle] + reply("Fishing particles set to ${FishingHelper.type}") + } + } + } + thenLiteralExecute("dev") { + NotEnoughUpdates.INSTANCE.config.hidden.dev = !NotEnoughUpdates.INSTANCE.config.hidden.dev + reply("§e[NEU] Dev mode " + if (NotEnoughUpdates.INSTANCE.config.hidden.dev) "§aenabled" else "§cdisabled") + } + thenLiteralExecute("saveconfig") { + NotEnoughUpdates.INSTANCE.saveConfig() + reply("Config saved") + } + thenLiteralExecute("searchmode") { + NotEnoughUpdates.INSTANCE.config.hidden.firstTimeSearchFocus = true + reply(EnumChatFormatting.AQUA.toString() + "I would never search") + } + thenLiteralExecute("bluehair") { + PronounDB.test() + } + thenLiteral("opengui") { + thenArgument("class", string()) { className -> + thenExecute { + try { + NotEnoughUpdates.INSTANCE.openGui = + Class.forName(this[className]).newInstance() as GuiScreen + reply("Opening gui: " + NotEnoughUpdates.INSTANCE.openGui) + } catch (e: Exception) { + e.printStackTrace() + reply("Failed to open this GUI.") + } + } + } + } + thenLiteralExecute("center") { + val x = Math.floor(Minecraft.getMinecraft().thePlayer.posX) + 0.5f + val z = Math.floor(Minecraft.getMinecraft().thePlayer.posZ) + 0.5f + Minecraft.getMinecraft().thePlayer.setPosition(x, Minecraft.getMinecraft().thePlayer.posY, z) + reply("Literal hacks") + } + thenLiteral("minion") { + thenArgument("args", RestArgumentType) { arg -> + thenExecute { + MinionHelperManager.getInstance().handleCommand(arrayOf("minion") + this[arg].split(" ")) + } + } + } + thenLiteralExecute("copytablist") { + val tabList = TabListUtils.getTabList().joinToString("\n", postfix = "\n") + MiscUtils.copyToClipboard(tabList) + reply("Copied tablist to clipboard!") + } + }) + updateHooks() + } + + 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) = "${commandNode.name} ${args.joinToString(" ")}" + + override fun processCommand(sender: ICommandSender, args: Array) { + 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? = null + + override fun addTabCompletionOptions( + sender: ICommandSender, + args: Array, + pos: BlockPos + ): List { + val originalText = getText(args) + var lc: CompletableFuture? = 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 + } + }) + } + } +} 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 new file mode 100644 index 00000000..b054f6d9 --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/brigadier/dsl.kt @@ -0,0 +1,133 @@ +/* + * 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 . + */ + +package io.github.moulberry.notenoughupdates.util.brigadier + +import com.mojang.brigadier.arguments.ArgumentType +import com.mojang.brigadier.builder.ArgumentBuilder +import com.mojang.brigadier.builder.LiteralArgumentBuilder +import com.mojang.brigadier.builder.RequiredArgumentBuilder +import com.mojang.brigadier.context.CommandContext +import io.github.moulberry.notenoughupdates.util.iterate +import net.minecraft.command.ICommandSender +import net.minecraft.util.ChatComponentText +import net.minecraft.util.IChatComponent +import java.lang.reflect.ParameterizedType +import java.lang.reflect.Type +import java.lang.reflect.TypeVariable + + +typealias DefaultSource = ICommandSender + +fun literal( + name: String, + block: LiteralArgumentBuilder.() -> Unit +): LiteralArgumentBuilder = + LiteralArgumentBuilder.literal(name).also(block) + +private fun normalizeGeneric(argument: Type): Class<*> { + return if (argument is Class<*>) { + argument + } else if (argument is TypeVariable<*>) { + normalizeGeneric(argument.bounds[0]) + } else if (argument is ParameterizedType) { + normalizeGeneric(argument.rawType) + } else { + Any::class.java + } +} + +data class TypeSafeArg(val name: String, val argument: ArgumentType) { + val argClass by lazy { + argument.javaClass + .iterate>> { + it.superclass + } + .flatMap { + it.genericInterfaces.toList() + } + .filterIsInstance() + .find { it.rawType == ArgumentType::class.java }!! + .let { + normalizeGeneric(it.actualTypeArguments[0]) + } + } + + @JvmName("getWithThis") + fun CommandContext.get(): T = + get(this) + + + fun get(ctx: CommandContext): T { + return ctx.getArgument(name, argClass) as T + } +} + +fun > C.reply(component: IChatComponent) { + source.addChatMessage(component) +} + +fun > C.reply(text: String) { + source.addChatMessage(ChatComponentText(text)) +} + +operator fun > C.get(arg: TypeSafeArg): T { + return arg.get(this) +} + + +fun argument( + name: String, + argument: ArgumentType, + block: RequiredArgumentBuilder.(TypeSafeArg) -> Unit +): RequiredArgumentBuilder = + RequiredArgumentBuilder.argument(name, argument).also { block(it, TypeSafeArg(name, argument)) } + +fun , AT : Any> T.thenArgument( + name: String, + argument: ArgumentType, + block: RequiredArgumentBuilder.(TypeSafeArg) -> Unit +): T = then(argument(name, argument, block)) + + +fun > T.thenLiteral( + name: String, + block: LiteralArgumentBuilder.() -> Unit +): T = + then(literal(name, block)) + + +fun > T.thenLiteralExecute( + name: String, + block: CommandContext.() -> Unit +): T = + thenLiteral(name) { + thenExecute(block) + } + +fun > T.then(node: ArgumentBuilder, block: T.() -> Unit): T = + then(node).also(block) + +fun > T.thenExecute(block: CommandContext.() -> Unit): T = + executes { + block(it) + 1 + } + + diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/iterate.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/iterate.kt new file mode 100644 index 00000000..bcfe11aa --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/iterate.kt @@ -0,0 +1,28 @@ +/* + * 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 . + */ + +package io.github.moulberry.notenoughupdates.util + +fun T.iterate(evolve: (T) -> T?): Sequence = sequence { + var pointer: T? = this@iterate + while (pointer != null) { + yield(pointer) + pointer = evolve(pointer) + } +} -- cgit