aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/commands
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2024-08-28 19:04:24 +0200
committerLinnea Gräf <nea@nea.moe>2024-08-28 19:04:24 +0200
commitd2f240ff0ca0d27f417f837e706c781a98c31311 (patch)
tree0db7aff6cc14deaf36eed83889d59fd6b3a6f599 /src/main/kotlin/commands
parenta6906308163aa3b2d18fa1dc1aa71ac9bbcc83ab (diff)
downloadFirmament-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.kt75
-rw-r--r--src/main/kotlin/commands/RestArgumentType.kt15
-rw-r--r--src/main/kotlin/commands/dsl.kt118
-rw-r--r--src/main/kotlin/commands/rome.kt230
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)
+ })
+}
+
+
+
+