diff options
author | Linnea Gräf <nea@nea.moe> | 2024-08-28 19:04:24 +0200 |
---|---|---|
committer | Linnea Gräf <nea@nea.moe> | 2024-08-28 19:04:24 +0200 |
commit | d2f240ff0ca0d27f417f837e706c781a98c31311 (patch) | |
tree | 0db7aff6cc14deaf36eed83889d59fd6b3a6f599 /src/main/kotlin/commands | |
parent | a6906308163aa3b2d18fa1dc1aa71ac9bbcc83ab (diff) | |
download | Firmament-d2f240ff0ca0d27f417f837e706c781a98c31311.tar.gz Firmament-d2f240ff0ca0d27f417f837e706c781a98c31311.tar.bz2 Firmament-d2f240ff0ca0d27f417f837e706c781a98c31311.zip |
Refactor source layout
Introduce compat source sets and move all kotlin sources to the main directory
[no changelog]
Diffstat (limited to 'src/main/kotlin/commands')
-rw-r--r-- | src/main/kotlin/commands/CaseInsensitiveLiteralCommandNode.kt | 75 | ||||
-rw-r--r-- | src/main/kotlin/commands/RestArgumentType.kt | 15 | ||||
-rw-r--r-- | src/main/kotlin/commands/dsl.kt | 118 | ||||
-rw-r--r-- | src/main/kotlin/commands/rome.kt | 230 |
4 files changed, 438 insertions, 0 deletions
diff --git a/src/main/kotlin/commands/CaseInsensitiveLiteralCommandNode.kt b/src/main/kotlin/commands/CaseInsensitiveLiteralCommandNode.kt new file mode 100644 index 0000000..10772b0 --- /dev/null +++ b/src/main/kotlin/commands/CaseInsensitiveLiteralCommandNode.kt @@ -0,0 +1,75 @@ + + +package moe.nea.firmament.commands + +import com.mojang.brigadier.Command +import com.mojang.brigadier.RedirectModifier +import com.mojang.brigadier.StringReader +import com.mojang.brigadier.builder.LiteralArgumentBuilder +import com.mojang.brigadier.context.CommandContextBuilder +import com.mojang.brigadier.context.StringRange +import com.mojang.brigadier.exceptions.CommandSyntaxException +import com.mojang.brigadier.tree.CommandNode +import com.mojang.brigadier.tree.LiteralCommandNode +import java.util.function.Predicate + +class CaseInsensitiveLiteralCommandNode<S>( + literal: String, command: Command<S>?, requirement: Predicate<S>?, + redirect: CommandNode<S>?, modifier: RedirectModifier<S>?, forks: Boolean +) : LiteralCommandNode<S>( + literal.lowercase(), command, requirement, redirect, modifier, forks +) { + class Builder<S>(literal: String) : LiteralArgumentBuilder<S>(literal) { + override fun build(): LiteralCommandNode<S> { + val result = CaseInsensitiveLiteralCommandNode( + literal, + command, requirement, redirect, redirectModifier, isFork + ) + for (argument in arguments) { + result.addChild(argument) + } + return result + } + } + + override fun createBuilder(): LiteralArgumentBuilder<S> { + return Builder<S>(literal).also { + it.requires(requirement) + it.forward(redirect, redirectModifier, isFork) + if (command != null) + it.executes(command) + } + } + + override fun parse(reader: StringReader, contextBuilder: CommandContextBuilder<S>) { + val start = reader.cursor + val end = parse0(reader) + if (end > -1) { + contextBuilder.withNode(this, StringRange.between(start, end)) + return + } + + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.literalIncorrect().createWithContext(reader, literal) + } + + override fun toString(): String { + return "<iliteral $literal>" + } + + private fun parse0(reader: StringReader): Int { + val start = reader.cursor + if (reader.canRead(literal.length)) { + val end = start + literal.length + if (reader.string.substring(start, end).equals(literal, true)) { + reader.cursor = end + if (!reader.canRead() || reader.peek() == ' ') { + return end + } else { + reader.cursor = start + } + } + } + return -1 + } + +} diff --git a/src/main/kotlin/commands/RestArgumentType.kt b/src/main/kotlin/commands/RestArgumentType.kt new file mode 100644 index 0000000..361907f --- /dev/null +++ b/src/main/kotlin/commands/RestArgumentType.kt @@ -0,0 +1,15 @@ + + +package moe.nea.firmament.commands + +import com.mojang.brigadier.StringReader +import com.mojang.brigadier.arguments.ArgumentType + +object RestArgumentType : ArgumentType<String> { + override fun parse(reader: StringReader): String { + val remaining = reader.remaining + reader.cursor += remaining.length + return remaining + } + +} diff --git a/src/main/kotlin/commands/dsl.kt b/src/main/kotlin/commands/dsl.kt new file mode 100644 index 0000000..d1f0d8c --- /dev/null +++ b/src/main/kotlin/commands/dsl.kt @@ -0,0 +1,118 @@ + + +package moe.nea.firmament.commands + +import com.mojang.brigadier.arguments.ArgumentType +import com.mojang.brigadier.builder.ArgumentBuilder +import com.mojang.brigadier.builder.RequiredArgumentBuilder +import com.mojang.brigadier.context.CommandContext +import com.mojang.brigadier.suggestion.SuggestionProvider +import kotlinx.coroutines.launch +import moe.nea.firmament.Firmament +import moe.nea.firmament.util.MinecraftDispatcher +import moe.nea.firmament.util.iterate +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource +import java.lang.reflect.ParameterizedType +import java.lang.reflect.Type +import java.lang.reflect.TypeVariable + + +typealias DefaultSource = FabricClientCommandSource + + +inline val <T : CommandContext<*>> T.context get() = this +operator fun <T : Any, C : CommandContext<*>> C.get(arg: TypeSafeArg<T>): T { + return arg.get(this) +} + +fun literal( + name: String, + block: CaseInsensitiveLiteralCommandNode.Builder<DefaultSource>.() -> Unit +): CaseInsensitiveLiteralCommandNode.Builder<DefaultSource> = + CaseInsensitiveLiteralCommandNode.Builder<DefaultSource>(name).also(block) + + +private fun normalizeGeneric(argument: Type): Class<*> { + return when (argument) { + is Class<*> -> argument + is TypeVariable<*> -> normalizeGeneric(argument.bounds[0]) + is ParameterizedType -> normalizeGeneric(argument.rawType) + else -> Any::class.java + } +} + +data class TypeSafeArg<T : Any>(val name: String, val argument: ArgumentType<T>) { + val argClass by lazy { + argument.javaClass + .iterate<Class<in ArgumentType<T>>> { + it.superclass + } + .flatMap { + it.genericInterfaces.toList() + } + .filterIsInstance<ParameterizedType>() + .find { it.rawType == ArgumentType::class.java }!! + .let { normalizeGeneric(it.actualTypeArguments[0]) } + } + + @JvmName("getWithThis") + fun <S> CommandContext<S>.get(): T = + get(this) + + + fun <S> get(ctx: CommandContext<S>): T { + try { + return ctx.getArgument(name, argClass) as T + } catch (e: Exception) { + if (ctx.child != null) { + return get(ctx.child) + } + throw e + } + } +} + + +fun <T : Any> argument( + name: String, + argument: ArgumentType<T>, + block: RequiredArgumentBuilder<DefaultSource, T>.(TypeSafeArg<T>) -> Unit +): RequiredArgumentBuilder<DefaultSource, T> = + RequiredArgumentBuilder.argument<DefaultSource, T>(name, argument).also { block(it, TypeSafeArg(name, argument)) } + +fun <T : ArgumentBuilder<DefaultSource, T>, AT : Any> T.thenArgument( + name: String, + argument: ArgumentType<AT>, + block: RequiredArgumentBuilder<DefaultSource, AT>.(TypeSafeArg<AT>) -> Unit +): T = then(argument(name, argument, block)) + +fun <T : RequiredArgumentBuilder<DefaultSource, String>> T.suggestsList(provider: CommandContext<DefaultSource>.() -> Iterable<String>) { + suggests(SuggestionProvider<DefaultSource> { context, builder -> + provider(context) + .asSequence() + .filter { it.startsWith(builder.remaining, ignoreCase = true) } + .forEach { + builder.suggest(it) + } + builder.buildFuture() + }) +} + +fun <T : ArgumentBuilder<DefaultSource, T>> T.thenLiteral( + name: String, + block: CaseInsensitiveLiteralCommandNode.Builder<DefaultSource>.() -> Unit +): T = + then(literal(name, block)) + +fun <T : ArgumentBuilder<DefaultSource, T>> T.then(node: ArgumentBuilder<DefaultSource, *>, block: T.() -> Unit): T = + then(node).also(block) + +fun <T : ArgumentBuilder<DefaultSource, T>> T.thenExecute(block: suspend CommandContext<DefaultSource>.() -> Unit): T = + executes { + Firmament.coroutineScope.launch(MinecraftDispatcher) { + block(it) + } + 1 + } + + diff --git a/src/main/kotlin/commands/rome.kt b/src/main/kotlin/commands/rome.kt new file mode 100644 index 0000000..015512d --- /dev/null +++ b/src/main/kotlin/commands/rome.kt @@ -0,0 +1,230 @@ + + +package moe.nea.firmament.commands + +import com.mojang.brigadier.CommandDispatcher +import com.mojang.brigadier.arguments.StringArgumentType.string +import io.ktor.client.statement.bodyAsText +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource +import net.minecraft.text.Text +import moe.nea.firmament.apis.UrsaManager +import moe.nea.firmament.events.CommandEvent +import moe.nea.firmament.features.debug.PowerUserTools +import moe.nea.firmament.features.inventory.buttons.InventoryButtons +import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlayScreen +import moe.nea.firmament.features.inventory.storageoverlay.StorageOverviewScreen +import moe.nea.firmament.gui.config.AllConfigsGui +import moe.nea.firmament.gui.config.BooleanHandler +import moe.nea.firmament.gui.config.ManagedOption +import moe.nea.firmament.repo.HypixelStaticData +import moe.nea.firmament.repo.RepoManager +import moe.nea.firmament.util.FirmFormatters +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.SBData +import moe.nea.firmament.util.ScreenUtil +import moe.nea.firmament.util.SkyblockId + + +fun firmamentCommand() = literal("firmament") { + thenLiteral("config") { + thenExecute { + AllConfigsGui.showAllGuis() + } + thenLiteral("toggle") { + thenArgument("config", string()) { config -> + suggestsList { + AllConfigsGui.allConfigs.asSequence().map { it.name }.asIterable() + } + thenArgument("property", string()) { property -> + suggestsList { + (AllConfigsGui.allConfigs.find { it.name == this[config] } ?: return@suggestsList listOf()) + .allOptions.entries.asSequence().filter { it.value.handler is BooleanHandler } + .map { it.key } + .asIterable() + } + thenExecute { + val config = this[config] + val property = this[property] + + val configObj = AllConfigsGui.allConfigs.find { it.name == config } + if (configObj == null) { + source.sendFeedback( + Text.stringifiedTranslatable( + "firmament.command.toggle.no-config-found", + config + ) + ) + return@thenExecute + } + val propertyObj = configObj.allOptions[property] + if (propertyObj == null) { + source.sendFeedback( + Text.stringifiedTranslatable("firmament.command.toggle.no-property-found", property) + ) + return@thenExecute + } + if (propertyObj.handler !is BooleanHandler) { + source.sendFeedback( + Text.stringifiedTranslatable("firmament.command.toggle.not-a-toggle", property) + ) + return@thenExecute + } + propertyObj as ManagedOption<Boolean> + propertyObj.value = !propertyObj.value + configObj.save() + source.sendFeedback( + Text.stringifiedTranslatable( + "firmament.command.toggle.toggled", configObj.labelText, + propertyObj.labelText, + Text.translatable("firmament.toggle.${propertyObj.value}") + ) + ) + } + } + } + } + } + thenLiteral("buttons") { + thenExecute { + InventoryButtons.openEditor() + } + } + thenLiteral("sendcoords") { + thenExecute { + val p = MC.player ?: return@thenExecute + MC.sendServerChat("x: ${p.blockX}, y: ${p.blockY}, z: ${p.blockZ}") + } + thenArgument("rest", RestArgumentType) { rest -> + thenExecute { + val p = MC.player ?: return@thenExecute + MC.sendServerChat("x: ${p.blockX}, y: ${p.blockY}, z: ${p.blockZ} ${this[rest]}") + } + } + } + thenLiteral("storageoverview") { + thenExecute { + ScreenUtil.setScreenLater(StorageOverviewScreen()) + MC.player?.networkHandler?.sendChatCommand("storage") + } + } + thenLiteral("storage") { + thenExecute { + ScreenUtil.setScreenLater(StorageOverlayScreen()) + MC.player?.networkHandler?.sendChatCommand("storage") + } + } + thenLiteral("repo") { + thenLiteral("reload") { + thenLiteral("fetch") { + thenExecute { + source.sendFeedback(Text.translatable("firmament.repo.reload.network")) // TODO better reporting + RepoManager.launchAsyncUpdate() + } + } + thenExecute { + source.sendFeedback(Text.translatable("firmament.repo.reload.disk")) + RepoManager.reload() + } + } + } + thenLiteral("price") { + thenArgument("item", string()) { item -> + suggestsList { RepoManager.neuRepo.items.items.keys } + thenExecute { + val itemName = SkyblockId(get(item)) + source.sendFeedback(Text.stringifiedTranslatable("firmament.price", itemName.neuItem)) + val bazaarData = HypixelStaticData.bazaarData[itemName] + if (bazaarData != null) { + source.sendFeedback(Text.translatable("firmament.price.bazaar")) + source.sendFeedback( + Text.stringifiedTranslatable("firmament.price.bazaar.productid", bazaarData.productId.bazaarId) + ) + source.sendFeedback( + Text.stringifiedTranslatable( + "firmament.price.bazaar.buy.price", + FirmFormatters.formatCommas(bazaarData.quickStatus.buyPrice, 1) + ) + ) + source.sendFeedback( + Text.stringifiedTranslatable( + "firmament.price.bazaar.buy.order", + bazaarData.quickStatus.buyOrders + ) + ) + source.sendFeedback( + Text.stringifiedTranslatable( + "firmament.price.bazaar.sell.price", + FirmFormatters.formatCommas(bazaarData.quickStatus.sellPrice, 1) + ) + ) + source.sendFeedback( + Text.stringifiedTranslatable( + "firmament.price.bazaar.sell.order", + bazaarData.quickStatus.sellOrders + ) + ) + } + val lowestBin = HypixelStaticData.lowestBin[itemName] + if (lowestBin != null) { + source.sendFeedback( + Text.stringifiedTranslatable( + "firmament.price.lowestbin", + FirmFormatters.formatCommas(lowestBin, 1) + ) + ) + } + } + } + } + thenLiteral("dev") { + thenLiteral("simulate") { + thenArgument("message", RestArgumentType) { message -> + thenExecute { + MC.instance.messageHandler.onGameMessage(Text.literal(get(message)), false) + } + } + } + thenLiteral("sbdata") { + thenExecute { + source.sendFeedback(Text.stringifiedTranslatable("firmament.sbinfo.profile", SBData.profileId)) + val locrawInfo = SBData.locraw + if (locrawInfo == null) { + source.sendFeedback(Text.translatable("firmament.sbinfo.nolocraw")) + } else { + source.sendFeedback(Text.stringifiedTranslatable("firmament.sbinfo.server", locrawInfo.server)) + source.sendFeedback(Text.stringifiedTranslatable("firmament.sbinfo.gametype", locrawInfo.gametype)) + source.sendFeedback(Text.stringifiedTranslatable("firmament.sbinfo.mode", locrawInfo.mode)) + source.sendFeedback(Text.stringifiedTranslatable("firmament.sbinfo.map", locrawInfo.map)) + } + } + } + thenLiteral("copyEntities") { + thenExecute { + val player = MC.player ?: return@thenExecute + player.world.getOtherEntities(player, player.boundingBox.expand(12.0)).forEach(PowerUserTools::showEntity) + } + } + thenLiteral("callUrsa") { + thenArgument("path", string()) { path -> + thenExecute { + source.sendFeedback(Text.translatable("firmament.ursa.debugrequest.start")) + val text = UrsaManager.request(this[path].split("/")).bodyAsText() + source.sendFeedback(Text.stringifiedTranslatable("firmament.ursa.debugrequest.result", text)) + } + } + } + } + CommandEvent.SubCommand.publish(CommandEvent.SubCommand(this@literal)) +} + + +fun registerFirmamentCommand(dispatcher: CommandDispatcher<FabricClientCommandSource>) { + val firmament = dispatcher.register(firmamentCommand()) + dispatcher.register(literal("firm") { + redirect(firmament) + }) +} + + + + |