Cmdr

repository·master·Indexed 19 days ago

https://github.com/evaera/cmdr

An extensible, type-safe command console for Roblox developers. Cmdr allows for the creation of admin and game-system commands featuring intelligent autocompletion, dual client-server validation, and programmatic execution. It supports custom command implementations, type-safe arguments, and embedded commands, providing a robust framework for developers using Luau.

Tokens
17.7K
Snippets
34
Records
68
Agent score
68%

What's inside cmdr

  1. Overview of Cmdr

    master

    Cmdr is a fully extensible and type-safe command console designed for Roblox developers. It provides a robust framework for creating admin commands and integrating custom game systems via a command-line interface.

    Key features include:

    • Intelligent Autocompletion & Validation: Instant feedback and suggestions as users type.
    • Security: Dual validation on both the client and server to ensure input integrity.
    • Programmatic Execution: Ability to run commands on behalf of the local user via code.
    • Input Binding: Bind commands directly to specific user inputs.
    • Embedded Commands: Dynamically use the output of one command as an argument for another.
  2. How hooks work in Cmdr

    master

    Hooks are callback functions used to tap into the command execution process. They are ideal for implementing permission systems, logging, or overriding command output.

    Every hook receives a CommandContext, providing access to parsed arguments, execution context, and user metadata.

    Registration Locations

    • Server commands: Execute both client and server hooks.
    • Client commands: Execute client hooks only.

    Execution Logic

    Hooks execute in order of priority (lowest to highest). If a hook returns a value that halts execution (like a string in BeforeRun), subsequent hooks in that phase will not run.

    -- Example of a basic hook registration
    registry:RegisterHook("BeforeRun", function(context)
    	if context.Group == "DefaultAdmin" and context.Executor.UserId ~= game.CreatorId then
    		return "You don't have permission to run this command"
    	end
    end)
  3. How command strings and embedded commands work

    master

    Meta-commands like bind, alias, and run utilize command strings. A command string is raw text consisting of a command name and predefined arguments (e.g., $1, $2) that is executed in the background.

    Embedded commands allow you to evaluate a command and use its output directly within a command string using the ${command arg1 arg2} syntax. These are evaluated during the preprocessing step of the meta-command.

    Key behaviors:

    • Nesting: Embedded commands can be nested: run echo ${run echo ${echo hello}!}.
    • Quoting: By default, if an embedded command's output contains spaces, it is encapsulated in quotes so it is treated as a single argument.
    • Literal Syntax: To prevent automatic quoting and allow Cmdr to parse the output as multiple separate arguments, use the literal syntax: ${{"command arg1 arg2"}}.
    # Standard embedded command (quoted if spaces exist)
    run teleport ${echo first second}
    
    # Literal embedded command (parsed as separate arguments)
    run teleport ${{"echo first second"}}
  4. Implement client-side logic with Data and ClientRun

    master

    You can extend commands with client-side capabilities using two methods:

    1. Data function: Define a Data function in your command definition. This runs on the client and can return data (like the mouse position) to the command implementation. Access this data in both client and server implementations using context:GetData().
    2. ClientRun function: Add a ClientRun function to your command definition. This allows the command to run on the client.

    Execution behavior for ClientRun:

    • If ClientRun returns a string, the command runs entirely on the client and does not trigger the server implementation or server-side hooks.
    • If ClientRun returns nothing (nil), it will proceed to execute the associated Server module implementation on the server.

    Caution: If you use ClientRun but do not provide a Server module, you must return a string from ClientRun to avoid errors.

    -- Example of a command definition with Data and ClientRun
    return {
        Name = "example",
        -- ... other fields
        Data = function() 
            return game.Players.LocalPlayer:GetMouse().Hit.Position 
        end,
        ClientRun = function(context, mousePos) 
            print("Mouse position: ", mousePos) 
            return "Done"
        end
    }
  5. Cmdr security architecture: Isolation and Validation

    master

    Cmdr employs several architectural layers to maintain security:

    • Server implementation isolation: Command definitions (names/arguments) are placed in client-accessible folders, but implementation scripts (*Server.lua) are kept strictly in server storage (e.g., ServerScriptService). The server implementation is never delivered to the client.
    • Payload parsing and type coercion: When a client requests a command, the server re-parses the raw input through registered argument types to construct a CommandContext. If parsing or type validation fails on the server, execution is aborted before the command logic runs.
    • Client-side execution isolation: Commands using a ClientRun function or local utilities execute exclusively on the invoking client. Modifications made by an exploiter to these scripts only affect their local environment and cannot impact the server or other players.
  6. Use AutoExec for automated command execution

    master

    The AutoExec feature allows you to define a list of command strings that run automatically the moment a command is registered. This is useful for setting up aliases, shortcuts, or initial state configurations without manual intervention.

    Key Benefits:

    • Encapsulated Setup: Keeps initialization logic bundled with the command definition.
    • Automated Macros: Registers shortcuts as soon as a command becomes active.
    • Order Independence: Commands in an AutoExec array are deferred to the end of the current frame cycle, ensuring all dependent commands are fully registered first.

    :::warning Client-Only Execution AutoExec arrays only execute on the client. This prevents duplicate work on the server and protects performance. :::

    AutoExec = {
    	'alias "my-shortcut" my-command',
    	'var= .my_setting true',
    }
  7. Dispatch network events from server commands

    master

    Cmdr provides two methods on the context object within a server-side command implementation to communicate with clients via network events:

    • context:SendEvent(target, eventName, ...args): Dispatches a message to a specific client (e.g., a player).
    • context:BroadcastEvent(eventName, ...args): Dispatches a message to every connected client.

    When using user-generated text in these events, ensure you filter the content on the server using Roblox's TextService to comply with moderation requirements.

    -- To a specific player
    context:SendEvent(player, "ShowNotification", filteredTitle, filteredMessage, duration)
    
    -- To everyone
    context:BroadcastEvent("ShowNotification", filteredTitle, filteredMessage, duration)
  8. Use default values with the dot (.) operator

    master

    You can define a Default function within a custom type. This function must return a string, as it is processed before parsing.

    When a user executes a command, they can use a single period . to automatically substitute the argument with its defined default value. For example, if a command is kill <player> and the player type has a default of the command runner, the user can type kill ..

  9. Use prefixed union types for arguments

    master

    You can allow a single argument to accept multiple types by using prefixes. This is defined in the Type key of an argument within the Args section of a command definition. When a user provides an argument starting with a specific symbol, the command receives the value as the associated type.

    Syntax Rules:

    • Use a space between the symbol and the type (e.g., "string # number").
    • You can chain as many prefixed types as needed (e.g., "string # number @ player % team").

    Example: If Type = "string # number", providing #33 as the argument will result in the function receiving the number 33 instead of the string "#33".

    -- Example of a prefixed union type definition
    Type = "string # number"
  10. Understand the command execution order

    master

    When a command is executed, Cmdr follows this sequence (hooks are evaluated from lowest to highest priority within their respective phases):

    1. BeforeRun hooks (Client)
    2. Data function (Client)
    3. ClientRun function (Client)
    4. BeforeRun hooks (Server) *
    5. Server command implementation *
    6. AfterRun hooks (Server) *
    7. AfterRun hooks (Client)

    *Note: Server steps (4, 5, 6) only run if ClientRun is not present or returns nil.

    WARNING

    Never rely exclusively on client-side hooks (BeforeCommandRegister or client BeforeRun) for critical authorization. Always validate permissions on the server.

  11. Best practices for customizing Cmdr

    master

    When customizing Cmdr, do not modify the Cmdr source code directly. Modifying the library or placing your custom commands inside Cmdr's BuiltInCommands folder will prevent you from receiving future updates easily.

    Instead, use the provided API to customize behavior, register new commands, or implement hooks. If you find a missing feature or a bug, open an issue on GitHub rather than patching the source code.

  12. When to avoid using Cmdr

    master

    Cmdr may not be suitable if:

    • You need a pre-made admin suite: Cmdr provides core utility commands but does not include a massive library of moderation or 'fun' commands out of the box. You must implement your own or use the Cookbook.
    • You are unfamiliar with Luau: Cmdr is developer-focused and requires writing your own command implementations, permission hooks, and custom systems (like logging) in Luau.
    • Your primary audience is on Mobile or Console: Cmdr is keyboard-first. While functional on other platforms, mobile UI support is basic, and you will likely need to build custom UI toggles for touch or controller users.