Skip to content

Wizards of Lua — Dynamic Commands

With dynamic commands you can register custom commands at runtime and bind them to Lua code. You can add, remove, and inspect commands via the WizardsOfLua module API.

1. Registering a Dynamic Command

A command definition is a Lua table with these attributes:

  • id (string): unique identifier and permission node (e.g. coins.add).
  • level (number): minimum operator level (0 - 4) required for invoking this command.
  • pattern (string): Brigadier-style syntax with literals and %-placeholders.
  • code (string): Lua code; receives an args table mapping placeholders to parsed values.

Example: define a new command

-- Register a command that adds coins to a player
WizardsOfLua.setCommand( {
  id      = "coins.add",
  level   = 2,
  pattern = "coins add amount:%i to p:%player",
  code    = [[
    local player = args.p
    local c = player.extra.coins or 0
    c = c + args.amount
    player:putExtra({ coins = c })
    print(player.name .. " now has " .. c .. " coins.")
  ]]
})

In‐game, invoke it like this:

/coins add 15 to @p

Note: Calling WizardsOfLua.setCommand(…) queues the new command for registration at the end of the current game tick. The command becomes usable on the next tick. So if your script wants to invoke it immediately, you need to yield for one tick (e.g. via sleep(1)) before calling it. Wizards of Lua will not replace other mods’ or vanilla commands. If the requested top-level literal is already taken by another provider, setCommand(...) throws an error.

2. Command ID syntax

  • Use a dot-separated sequence of lowercase words (e.g. my.command.action).
  • If you integrate with a permissions plugin (LuckPerms, etc.), this ID becomes the permission node.

3. Pattern Syntax

A pattern is one or more tokens separated by spaces. Each token can be:

  • Literal word (e.g. home, spawn, coins)
  • Unnamed placeholder (e.g. %s, %i, %d, …)
  • Named placeholder (e.g. name:%s, amount:%i, …)

Naming rule: All placeholders in a single pattern must be either all unnamed or all named.
Greedy %s: If %s appears as the last token, it captures the rest of the input, including spaces.

3.1 Enum-String Placeholders

If you need a string argument that’s restricted to a fixed set of values (and you want built-in tab-completion), use:

  • Unnamed enum-string:
%s[easy|normal|hard]
  • Named enum-string:
mode:%s[easy|normal|hard]

At runtime the system will:

  1. Suggest only easy, normal, hard when you press Tab.
  2. Reject any other value with a “Unknown value” error.

4. Accessing Placeholder Values

When your command runs, the parsed values are provided in the args table:

  • Unnamed placeholders → passed by index
  -- Pattern: "calc %d + %d"
  -- Invocation: /calc 3.5 + 4.1
  args = { [1] = 99, [2] = 7 }
  • Named placeholders → passed by key
  -- Pattern: "coins add amount:%i to p:%player"
  -- Invocation: /coins add amount:50 to Alice
  args = { amount = 50, p = <Player Alice> }

5. Placeholder Reference

Placeholder Description Returns Lua Type
%s String (single word or greedy if last) raw text string
%s[o1…oN] Enum-string (only accepts the listed options) one of o1…oN string
%d Floating-point number e.g. 3.14, -0.5 number
%i Integer e.g. 42, -7 number
%b Boolean true or false boolean
%v 3D vector (x, y, z) a Vec3d Vec3
%entity Single entity (selector or name) one Entity Entity
%entities Multiple entities (selector or names) list of Entity { Entity, … }
%player Single online player (selector or name) one Player Player
%players Multiple online players (selector/list) list of Player { Player, … }
%item Item-stack argument an ItemStack Item

Options: Options are separated by | characters, e.g. red|green|blue.
Selectors: Most selectors (@e, @a, etc.) require operator level ≥ 2 to use.

6. Defining the Lua Callback

The code field is a string containing your Lua logic. It is compiled and run at invocation time.

  • Syntax errors only surface when the command is executed.
  • Inside your code, args is a predefined variable that contains the placeholder values.

7. Managing Dynamic Commands

7.1 Listing Commands

local all = WizardsOfLua.listCommands()
for i, cmd in ipairs(all) do
  print(i, cmd.id, "→", cmd.pattern, "(level " .. cmd.level .. ")", cmd.code)  
end

7.2 Removing a Command

WizardsOfLua.removeCommand("coins.add")

8. Best Practices

  • Unique IDs: Avoid collisions by choosing clear, namespaced IDs.
  • Permission Levels: Assign higher levels (2–4) to potentially destructive commands.
  • Code Size: Aim for inline Lua snippets under ~20 lines. For complex logic, factor code into separate .lua files under config/wizards-of-lua/ and require them from your code block.

What’s next?