/* * 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 com.mojang.brigadier.tree.ArgumentCommandNode import com.mojang.brigadier.tree.CommandNode import com.mojang.brigadier.tree.LiteralCommandNode import io.github.moulberry.notenoughupdates.commands.dev.DevTestCommand 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 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(ChatComponentText("§e[NEU] ").appendSibling(component)) } @JvmOverloads fun > C.reply(text: String, block: ChatComponentText.() -> Unit = {}) { source.addChatMessage(ChatComponentText(text.split("\n").joinToString("\n") { "§e[NEU] $it" }).also(block)) } 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 ): ArgumentCommandNode = argument(name, argument, block).build().also(::then) fun , AT : Any> T.thenArgumentExecute( name: String, argument: ArgumentType, block: CommandContext.(TypeSafeArg) -> Unit ): ArgumentCommandNode = thenArgument(name, argument) { thenExecute { block(it) } } fun literal( name: String, block: LiteralArgumentBuilder.() -> Unit = {} ): LiteralArgumentBuilder = LiteralArgumentBuilder.literal(name).also(block) fun > T.thenLiteral( name: String, block: LiteralArgumentBuilder.() -> Unit ): LiteralCommandNode = then(literal(name), block) as LiteralCommandNode fun > T.thenLiteralExecute( name: String, block: CommandContext.() -> Unit ): LiteralCommandNode = thenLiteral(name) { thenExecute(block) } fun > T.thenRedirect(node: CommandNode): T { node.children.forEach { this.then(it) } forward(node.redirect, node.redirectModifier, node.isFork) executes(node.command) return this } fun , U : ArgumentBuilder> T.then( node: U, block: U.() -> Unit ): CommandNode = node.also(block).build().also(::then) fun > T.thenExecute(block: CommandContext.() -> Unit): T = executes { block(it) 1 } fun > T.requiresDev(): T { requires { DevTestCommand.isDeveloper(it) } return this } fun NEUBrigadierHook.withHelp(helpText: String): NEUBrigadierHook { commandNode.withHelp(helpText) return this } fun > T.withHelp(helpText: String): T { BrigadierRoot.setHelpForNode(this, helpText) return this } fun > T.suggestsList(list: List) { suggestsList { list } } fun > T.suggestsList(list: () -> List) { suggests { context, builder -> list().filter { it.startsWith(builder.remaining, ignoreCase = true) } .forEach { builder.suggest(it) } builder.buildFuture() } }