Skip to content

1. Your first command

Create config/ignis/mycommands.lua and run /ignis reload after each change to see your commands in-game.

register("ping", function(ctx)
ctx.player:sendMessage("Pong!")
end)

The handler receives a ctx table with ctx.player. Call methods on it to interact with the player.

Add a <target:player> argument — the value arrives as a positional parameter:

register("heal <target:player>", function(ctx, target)
target:heal(20)
ctx.player:sendMessage("Healed " .. target.name)
end)

Wrap an argument in [...] to make it optional. Missing optional args are nil:

register("heal [<target:player>]", function(ctx, target)
local t = target or ctx.player
t:heal(20)
end)

See Registering Commands for all argument types.

A choice argument limits input to predefined options and provides tab completions:

register("gamemode <mode:choice=survival,creative,adventure,spectator> [<target:player>]", function(ctx, mode, target)
local t = target or ctx.player
t:sendMessage("Set " .. t.name .. " to " .. mode)
end)

Schedule a one-shot action with a delay in ticks (20 ticks = 1 second):

register("callback", function(ctx)
ctx.player:sendMessage("You'll be healed in 5 seconds...")
mc.schedule(100, function()
ctx.player:heal(20)
ctx.player:sendMessage("Healed!")
end)
end)

Repeating timers with mc.scheduleRepeating(delay, interval, callback):

register("countdown <seconds:int>", function(ctx, seconds)
local remaining = seconds
local id -- declare first so the callback can see it
id = mc.scheduleRepeating(0, 20, function()
remaining = remaining - 1
if remaining <= 0 then
mc.broadcast("Go!")
mc.cancelTask(id)
else
mc.broadcast(remaining .. "...")
end
end)
end)

See the mc.* API for scheduler options.

Command handlers and mc.schedule callbacks support mc.sleep directly.

mc.sleep(ticks) pauses execution for the given number of ticks. See Async API for details:

register("delayedheal", function(ctx)
ctx.player:sendMessage("Healing in 3...")
mc.sleep(20)
ctx.player:sendMessage("2...")
mc.sleep(20)
ctx.player:sendMessage("1...")
mc.sleep(20)
ctx.player:heal(20)
ctx.player:sendMessage("Healed!")
end)

mc.fetch(url) sends an HTTP GET request and waits for the response. See Async API for POST config and response fields:

register("playerinfo", function(ctx)
local res = mc.fetch("https://api.github.com/users/" .. ctx.player.name)
if res.ok then
local data = res.json
ctx.player:sendMessage("GitHub account: " .. (data.login or "not found"))
else
ctx.player:sendMessage("API error: " .. (res.error or res.status))
end
end)

Pass a permission node as the third argument to register. Players without it can’t use the command:

register("kick <target:player> [<reason:text>]", function(ctx, target, reason)
target:kick(reason or "Kicked by an operator")
end, "admin.kick")

See World API and Vector API for particle, sound, and vec options:

register("fart", function(ctx)
local p = ctx.player
local pos = p.pos
p.world:playSound("minecraft:entity.player.burp", pos, 1.0, 1.0)
p.world:particle("minecraft:heart", vec(pos.x, pos.y + 1, pos.z), {
count = 5,
delta = { 0.5, 0.5, 0.5 }
})
p:sendMessage("You farted!")
end)
  • Events and storage — part 2 of this guide: reacting to events and saving data
  • Events — react to player joins, block breaks, chat, and more
  • Async API — detailed mc.fetch and mc.sleep reference
  • Storage — persist data across reloads with mc.data and player.data
  • Libraries — formatting templates, simple registrations, chest GUIs
  • Language extensions\{ ... } lambda syntax