Skip to content

Async API

Coroutine-based async operations — sequential code without callback nesting. Under the hood, mc.fetch and mc.sleep yield the execution and resume on the server thread when the operation completes. No need to worry about syncing (if you know what it is).

Yields the current coroutine and resumes after the specified number of ticks (20 ticks = 1 second).

mc.broadcast("Wait for 2 seconds...")
mc.sleep(40)
mc.broadcast("Done!")

Simple GET request. Returns a response table.

local res = mc.fetch("https://api.example.com/data")
if res.ok then
mc.broadcast(res.text)
else
mc.broadcast("Error: " .. res.error)
end

Full request with options:

OptionTypeDefaultDescription
urlstringRequest URL (required)
methodstring"GET"HTTP method
headerstable{}Custom headers
bodystringnilRaw request body
jsontablenilAuto-encodes to JSON and sets Content-Type: application/json
timeoutnumber30Timeout in seconds

body & json are mutually exclusive.

local res = mc.fetch({
url = "https://api.example.com/data",
method = "POST",
json = { key = "value" },
headers = { Authorization = "Bearer token" },
timeout = 10
})
FieldTypeDescription
res.okbooleantrue if status is 2xx
res.statusnumberHTTP status code
res.textstringResponse body as string
res.headerstableResponse headers
res.jsontable or nilLazy-parsed JSON (parsed on first access)
res.errorstring or nilError message if the request failed
local res = mc.fetch("https://api.github.com/repos/user/repo")
if res.ok then
local data = res.json
mc.broadcast("Stars: " .. data.stargazers_count)
end

No callbacks, no nesting — just sequential code:

register("fetch", function(ctx)
-- Step 1: fetch a post
local post = mc.fetch("https://jsonplaceholder.typicode.com/posts/1")
local postData = post.json
-- Step 2: wait a tick
mc.sleep(1)
-- Step 3: fetch comments
local comments = mc.fetch("https://jsonplaceholder.typicode.com/posts/" .. postData.id .. "/comments")
ctx.player:sendMessage("Fetched " .. #comments.json .. " comments")
end)

mc.sleep and mc.fetch only work inside coroutines, i.e. command handlers (register(...)) and scheduled callbacks (mc.schedule, mc.scheduleRepeating). They do not work inside event handlers (mc.on(...)), which run on the main thread and cannot be yielded.

If you need async behaviour in an event, use these:

mc.schedule(0, function()
p:sendMessage("Called in the next tick")
mc.sleep(20) -- yay
end)
-- OR
coroutine.wrap(function()
p:sendMessage("Called immediately")
mc.sleep(20)
end)()

Beware, all pending coroutines (sleeps, in-flight HTTP requests) are discarded on /ignis reload. The Lua state is completely torn down and rebuilt. This is intended, and you should consider it when reloading/writing scripts.

So don’t use it extremely long mc.sleep (e.g. for an hour or a day) for critical mechanics (e.g. ban durations or the wait time for daily bonuses). For long pauses, use the persistent storage saving the time when player can perform an action again.

mc.sleep is ideal for short delays: animations, spell casting & etc.