Skip to content

Instantly share code, notes, and snippets.

@zeroeightysix
Created September 9, 2020 13:48
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zeroeightysix/cc982cc0526e9a367d7f42a1dd8c354e to your computer and use it in GitHub Desktop.
Save zeroeightysix/cc982cc0526e9a367d7f42a1dd8c354e to your computer and use it in GitHub Desktop.
A small kotlin DSL for brigadier

Use like so:

// Assuming `dispatcher` is a `CommandDispatcher`
dispatcher register rootLiteral("my_command") {
    // A literal argument: this matches no other value than 'echo'
    literal("echo") {
        // A required argument: its name is 'str_to_echo', but it matches any (greedy) string.
        greedyString("str_to_echo") {
            // Specify what this (sub)command does when executed.
            does { context ->
                // Get the value of a required argument
                // Specifying the type is necessary so `from` can infer what type to get from the context.
                val str: String = "str_to_echo" from context

                // Repeat to the user what they provided for the str_to_echo argument.
                println("echo: $str")
            }
        }
    }
    literal("exit") {
        does {
            System.exit(0)
        }
    }
}

This will produce a command with the following syntax: my_command <echo|exit> [str_to_echo]

What it might output:

$ my_command echo Hello world!
echo: Hello world!
$ my_command echo
// ERROR: required argument 'str_to_echo' not specified
$ my_command
// ERROR
$ my_command exit
Process exited with exit code 0.
import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.arguments.*
import com.mojang.brigadier.builder.ArgumentBuilder
import com.mojang.brigadier.builder.LiteralArgumentBuilder
import com.mojang.brigadier.builder.RequiredArgumentBuilder
import com.mojang.brigadier.context.CommandContext
@DslMarker
@Target(AnnotationTarget.TYPE)
annotation class BrigadierDsl
infix fun <S> CommandDispatcher<S>.register(builder: LiteralArgumentBuilder<S>) = this.register(builder)
/**
* Creates a new [LiteralArgumentBuilder] without a parent.
*/
fun <T> rootLiteral(name: String, block: (@BrigadierDsl LiteralArgumentBuilder<T>).() -> Unit) =
LiteralArgumentBuilder.literal<T>(name).also(block)
/**
* Appends a new [literal](LiteralArgumentBuilder) to `this` [ArgumentBuilder].
*
* @param name the name of the literal argument
* @param block the receiver function for further construction of the literal argument
*/
fun <T> ArgumentBuilder<T, *>.literal(name: String, block: (@BrigadierDsl LiteralArgumentBuilder<T>).() -> Unit) =
then(rootLiteral(name, block))
/**
* Appends a new required argument to `this` [ArgumentBuilder].
*
* @param name the name of the required argument
* @param argument the type of required argument, for example [IntegerArgumentType]
* @param block the receiver function for further construction of the required argument
*/
fun <S, T : ArgumentBuilder<S, T>, R> ArgumentBuilder<S, T>.argument(
name: String,
argument: ArgumentType<R>,
block: (@BrigadierDsl RequiredArgumentBuilder<S, R>).() -> Unit
) =
then(RequiredArgumentBuilder.argument<S, R>(name, argument).also(block))
/**
* A shorthand for appending a boolean required argument to `this` [ArgumentBuilder]
*
* @see argument
*/
fun <S, T : ArgumentBuilder<S, T>> ArgumentBuilder<S, T>.bool(
name: String,
block: (@BrigadierDsl RequiredArgumentBuilder<S, Boolean>).() -> Unit
) =
argument(name, BoolArgumentType.bool(), block)
/**
* A shorthand for appending a double required argument to `this` [ArgumentBuilder]
*
* @see argument
*/
fun <S, T : ArgumentBuilder<S, T>> ArgumentBuilder<S, T>.double(
name: String,
block: RequiredArgumentBuilder<S, Double>.() -> Unit
) =
argument(name, DoubleArgumentType.doubleArg(), block)
/**
* A shorthand for appending a float required argument to `this` [ArgumentBuilder]
*
* @see argument
*/
fun <S, T : ArgumentBuilder<S, T>> ArgumentBuilder<S, T>.float(
name: String,
block: (@BrigadierDsl RequiredArgumentBuilder<S, Float>).() -> Unit
) =
argument(name, FloatArgumentType.floatArg(), block)
/**
* A shorthand for appending a integer required argument to `this` [ArgumentBuilder]
*
* @see argument
*/
fun <S, T : ArgumentBuilder<S, T>> ArgumentBuilder<S, T>.integer(
name: String,
block: (@BrigadierDsl RequiredArgumentBuilder<S, Int>).() -> Unit
) =
argument(name, IntegerArgumentType.integer(), block)
/**
* A shorthand for appending a `long` required argument to `this` [ArgumentBuilder]
*
* @see argument
*/
fun <S, T : ArgumentBuilder<S, T>> ArgumentBuilder<S, T>.long(
name: String,
block: (@BrigadierDsl RequiredArgumentBuilder<S, Long>).() -> Unit
) =
argument(name, LongArgumentType.longArg(), block)
/**
* A shorthand for appending a string required argument to `this` [ArgumentBuilder]
*
* @see argument
*/
fun <S, T : ArgumentBuilder<S, T>> ArgumentBuilder<S, T>.string(
name: String,
block: (@BrigadierDsl RequiredArgumentBuilder<S, String>).() -> Unit
) =
argument(name, StringArgumentType.string(), block)
/**
* A shorthand for appending a greedy string required argument to `this` [ArgumentBuilder]
*
* @see argument
*/
fun <S, T : ArgumentBuilder<S, T>> ArgumentBuilder<S, T>.greedyString(
name: String,
block: (@BrigadierDsl RequiredArgumentBuilder<S, String>).() -> Unit
) =
argument(name, StringArgumentType.greedyString(), block)
/**
* Sets the executes callback for `this` [ArgumentBuilder]
*
* @param command the callback
*/
infix fun <S> ArgumentBuilder<S, *>.does(command: (@BrigadierDsl CommandContext<S>) -> Int) = executes(command)
/**
* Gets the value of a (required) argument in the command hierarchy
*
* @see CommandContext.getArgument
*/
inline infix fun <reified R, S> String.from(ctx: CommandContext<S>) = ctx.getArgument(this, R::class.java)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment