Skip to main content

Overview

The Command Builder provides a Kotlin DSL (Domain-Specific Language) for creating commands with a clean, declarative syntax. It simplifies command creation with support for parameters, subcommands, auto-completion, and validation. Package: net.ccbluex.liquidbounce.features.command.builder

Core Classes

Command

class Command(
    val name: String,
    val aliases: List<String> = emptyList(),
    val executable: Boolean = true,
    val parameters: List<Parameter<*>> = emptyList(),
    val subcommandMap: Map<String, Command> = emptyMap(),
    val handler: Handler? = null
)
name
String
required
Command name (primary identifier)
aliases
List<String>
default:"emptyList()"
Alternative names for the command
executable
Boolean
default:"true"
Whether the command can be executed directly
parameters
List<Parameter<*>>
default:"emptyList()"
List of command parameters
subcommandMap
Map<String, Command>
default:"emptyMap()"
Map of subcommands
handler
Handler?
default:"null"
Command execution handler

Building Commands

Basic Command

val greetCommand = Command("greet") {
    handler { context ->
        chat("Hello, world!")
    }
}

Command with Aliases

val toggleCommand = Command(
    name = "toggle",
    aliases = listOf("t", "enable", "disable")
) {
    // Command implementation
}

Command with Parameters

val friendCommand = Command("friend") {
    val action = parameter<String>("action", required = true)
    val name = parameter<String>("name", required = true)
    
    handler { context ->
        val actionValue = context[action]
        val nameValue = context[name]
        
        when (actionValue) {
            "add" -> addFriend(nameValue)
            "remove" -> removeFriend(nameValue)
            else -> chat("Unknown action: $actionValue")
        }
    }
}

Command with Subcommands

val configCommand = Command("config") {
    subcommand("load") {
        val name = parameter<String>("name", required = true)
        
        handler { context ->
            loadConfig(context[name])
        }
    }
    
    subcommand("save") {
        val name = parameter<String>("name", required = false, default = "current")
        
        handler { context ->
            saveConfig(context[name])
        }
    }
    
    subcommand("list") {
        handler {
            listConfigs()
        }
    }
}

// Usage:
// .config load HypixelBedwars
// .config save MyConfig
// .config list

Parameters

Required Parameters

val param = parameter<String>(
    name = "username",
    required = true
)

Optional Parameters

val param = parameter<Int>(
    name = "count",
    required = false,
    default = 10
)

Vararg Parameters

val message = parameter<String>(
    name = "message",
    vararg = true
)

// Usage: .say Hello world this is a message
// message will contain all arguments as array

Parameter with Validation

val count = parameter(
    name = "count",
    verifier = Parameter.Verificator { text ->
        val value = text.toIntOrNull()
        if (value == null || value < 1 || value > 100) {
            Parameter.Verificator.Result.Error("Count must be 1-100")
        } else {
            Parameter.Verificator.Result.Ok(value)
        }
    }
)

Parameter with Auto-completion

val module = parameter(
    name = "module",
    autocompletionHandler = AutoCompletionProvider { begin, args ->
        ModuleManager
            .filter { it.name.startsWith(begin, ignoreCase = true) }
            .map { it.name }
    }
)

Handler Context

Accessing Parameters

handler { context ->
    val paramValue = context[parameter]
    // Use paramValue
}

Getting Command Info

handler { context ->
    val command = context.command
    println("Executing: ${command.name}")
}

All Parameters

handler { context ->
    val allParams = context.parameters
    // Array of all parameter values
}

Advanced Examples

Module Toggle Command

val toggleCommand = Command("toggle", aliases = listOf("t")) {
    val moduleName = parameter(
        name = "module",
        required = true,
        verifier = Parameter.Verificator { text ->
            val module = ModuleManager[text]
            if (module == null) {
                Parameter.Verificator.Result.Error("Module not found")
            } else {
                Parameter.Verificator.Result.Ok(module)
            }
        },
        autocompletionHandler = AutoCompletionProvider { begin, _ ->
            ModuleManager
                .filter { it.name.startsWith(begin, ignoreCase = true) }
                .map { it.name }
        }
    )
    
    handler { context ->
        val module = context[moduleName] as ClientModule
        module.enabled = !module.enabled
        
        val state = if (module.enabled) "enabled" else "disabled"
        chat("${module.name} $state")
    }
}

Config Management Command

val configCommand = Command("config") {
    subcommand("load") {
        val configName = parameter(
            name = "name",
            required = true,
            autocompletionHandler = AutoCompletionProvider { begin, _ ->
                ConfigSystem.userConfigsFolder
                    .listFiles { file -> file.extension == "json" }
                    ?.map { it.nameWithoutExtension }
                    ?.filter { it.startsWith(begin, ignoreCase = true) }
                    ?: emptyList()
            }
        )
        
        handler { context ->
            val name = context[configName]
            try {
                loadUserConfig(name)
                chat("Config loaded: $name")
            } catch (e: Exception) {
                chat("Failed to load config: ${e.message}")
            }
        }
    }
    
    subcommand("save") {
        val configName = parameter<String>("name", required = true)
        
        handler { context ->
            val name = context[configName]
            try {
                saveUserConfig(name)
                chat("Config saved: $name")
            } catch (e: Exception) {
                chat("Failed to save config: ${e.message}")
            }
        }
    }
    
    subcommand("delete") {
        val configName = parameter(
            name = "name",
            required = true,
            autocompletionHandler = AutoCompletionProvider { begin, _ ->
                ConfigSystem.userConfigsFolder
                    .listFiles { file -> file.extension == "json" }
                    ?.map { it.nameWithoutExtension }
                    ?.filter { it.startsWith(begin, ignoreCase = true) }
                    ?: emptyList()
            }
        )
        
        handler { context ->
            val name = context[configName]
            val file = File(ConfigSystem.userConfigsFolder, "$name.json")
            if (file.delete()) {
                chat("Config deleted: $name")
            } else {
                chat("Failed to delete config: $name")
            }
        }
    }
    
    subcommand("list") {
        handler {
            val configs = ConfigSystem.userConfigsFolder
                .listFiles { file -> file.extension == "json" }
                ?.map { it.nameWithoutExtension }
                ?: emptyList()
            
            if (configs.isEmpty()) {
                chat("No configs found")
            } else {
                chat("Available configs:")
                configs.forEach { chat("  - $it") }
            }
        }
    }
}

Best Practices

  1. Use meaningful parameter names - Makes commands self-documenting
  2. Provide auto-completion - Improves user experience
  3. Validate parameters - Catch errors before execution
  4. Add aliases for common commands - Speed up command entry
  5. Group related commands with subcommands - Better organization
  6. Provide helpful error messages - Guide users to correct usage
  7. Use varargs for message-like parameters - Natural syntax

See Also

Build docs developers (and LLMs) love