Skip to content

Instantly share code, notes, and snippets.

@zomb-676
Last active January 8, 2024 09:29
Show Gist options
  • Save zomb-676/8ef203ee1bb7e344fb0c82f17e9a1a0f to your computer and use it in GitHub Desktop.
Save zomb-676/8ef203ee1bb7e344fb0c82f17e9a1a0f to your computer and use it in GitHub Desktop.
CommandDSL
import com.mojang.brigadier.Command
import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.RedirectModifier
import com.mojang.brigadier.SingleRedirectModifier
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.CommandNode
import java.util.function.Predicate

@JvmInline
@Suppress("unused")
value class CommandDSL<S>(
    inline val dispatcher: CommandDispatcher<S>,
) {
    inline operator fun String.invoke(block: WithNode<S, LiteralArgumentBuilder<S>>.() -> Unit) {
        val n = LiteralArgumentBuilder.literal<S>(this)
        block(WithNode(n))
        dispatcher.register(n)
    }

    @JvmInline
    value class WithNode<S, T : ArgumentBuilder<S, T>>(
        inline val node: ArgumentBuilder<S, T>
    ) {
        inline operator fun String.invoke(block: WithNode<S, LiteralArgumentBuilder<S>>.() -> Unit) {
            val n = LiteralArgumentBuilder.literal<S>(this)
            block(WithNode(n))
            node.then(n)
        }

        inline operator fun <Arg> String.invoke(
            pType: ArgumentType<Arg>,
            block: WithNode<S, RequiredArgumentBuilder<S, Arg>>.() -> Unit
        ) {
            val n = RequiredArgumentBuilder.argument<S, Arg>(this, pType)
            block(WithNode(n))
            node.then(n)
        }

        inline fun execute(crossinline block: CommandContext<S>.() -> Unit) {
            node.executes { context ->
                try {
                    block(context)
                    return@executes Command.SINGLE_SUCCESS
                    //so no need to return Command.SINGLE_SUCCESS manually
                    //can just wirite code and just throw when error happended
                } catch (e : Exception) {
                    LOGGER.catching(e)
                    return@executes 2
                }
            }
        }

        inline fun require(predicate: Predicate<S>, block: WithNode<S, T>.() -> Unit) {
            val n = node.requires(predicate)
            block(WithNode(n))
        }

        inline fun redirect(target: CommandNode<S>, block: WithNode<S, T>.() -> Unit) {
            val n = node.redirect(target)
            block(WithNode(n))
        }

        inline fun redirect(
            target: CommandNode<S>,
            modifier: SingleRedirectModifier<S>,
            block: WithNode<S, T>.() -> Unit
        ) {
            val n = node.redirect(target, modifier)
            block(WithNode(n))
        }

        inline fun fork(target: CommandNode<S>, modifier: RedirectModifier<S>, block: WithNode<S, T>.() -> Unit) {
            val n = node.fork(target, modifier)
            block(WithNode((n)))
        }

        inline fun forward(
            target: CommandNode<S>,
            modifier: RedirectModifier<S>,
            fork: Boolean,
            block: WithNode<S, T>.() -> Unit
        ) {
            val n = node.forward(target, modifier, fork)
            block(WithNode(n))
        }

        inline val arguments get() = node.arguments
        inline val command get() = node.command
        inline val requirement get() = node.requirement
        inline val redirect get() = node.redirect
        inline val redirectModifier get() = node.redirectModifier
        inline val isFork get() = node.isFork
    }
}

for example

    private fun registerClientCommand(event: RegisterClientCommandsEvent) {
        CommandDSL(event.dispatcher).apply {
            MOD_ID { //literal argumnet
                "resend" { //literal argumnet
                    "search_half_range"(IntegerArgumentType.integer(0, 8)) { //IntegerArgument
                        execute { //execute
                            val player = this.source.player ?: return@execute
                            val range = getArgument("search_half_range", Int::class.java)
                                ?: throw RuntimeException("can't get argument")
                                //you can throw then the exception will be logged and will not return Command.SINGLE_SUCCESS
                            trigSearch(player, range)
                        }
                    }
                }
                "clear_cache" { //literal argumnet
                    execute { //execute
                        ClientSearchCache.clearCache()
                    }
                }
            }
        }
    }

can also fully work with functions like

inline fun runOnDevelopment(codeBlock: () -> Unit)

so that you can easily reigster command controlled by condition

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment