/*
* Firmament is a Hypixel Skyblock mod for modern Minecraft versions
* Copyright (C) 2023 Linnea Gräf
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package moe.nea.firmament.commands
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 com.mojang.brigadier.suggestion.SuggestionProvider
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.lang.reflect.TypeVariable
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
import moe.nea.firmament.util.iterate
typealias DefaultSource = FabricClientCommandSource
inline val > T.context get() = this
operator fun > C.get(arg: TypeSafeArg): T {
return arg.get(this)
}
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 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.suggestsList(provider: () -> Iterable) {
suggests(SuggestionProvider { context, builder ->
provider()
.asSequence()
.filter { it.startsWith(builder.remaining, ignoreCase = true) }
.forEach {
builder.suggest(it)
}
builder.buildFuture()
})
}
fun > T.thenLiteral(
name: String,
block: LiteralArgumentBuilder.() -> Unit
): T =
then(literal(name, 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
}