util.event.emit_callback

Shared

Fires an event and returns a promise that resolves with the handler's return values


Syntax

local result = util.event.emit_callback(
    name,
    options = nil,
    ...
)

options is positional — pass nil or omit it entirely when no routing is needed.

  • When routing options are needed — pass the options table as the second argument.
  • When no routing options are needed — omit options and pass arguments directly after name.

Only serializable values survive remote transmission. Non-serializable values are silently dropped.

  • Safe to sendnil, bool, number, string, table (including nested tables).
  • Silently dropped — functions, userdata, threads, and any other non-serializable Lua values.
  • Local-only emit (remote = false) — no serialization occurs; all Lua values are forwarded as-is.
  • Nested tables — fully supported, but deeply nested structures increase payload size and serialization cost.

Parameters

TypeNameDescription
stringnameName of the event to fire
tableoptionsRouting options table
Refer Options section
any...Arguments forwarded to every handler

Returns

Always returns a single promise regardless of how many handlers are registered or whether the call is local or remote.
For remote calls, the remote side aggregates all of its handlers using the same rules before sending a single reply — so the caller's promise resolves with the same structure described below.

How that promise resolves depends on the number of registered handlers and the collect_all option:

collect_allHandlersResolved value
falseNoneResolves immediately with no values
falseOne or moreResolves directly with the first handler's return value(s), unpacked
trueNoneResolves immediately with no values
trueOne or moreResolves with {[1]={...}, [2]={...}, ...}
Each slot is always a table of that handler's return values, even if only one value was returned

Options

Server to client util.event.emit_callback requires a valid peer.

  • peer is mandatory when calling with { remote = true } from the server — omitting it throws an error.
  • peer must refer to a currently connected peer — passing a disconnected or invalid peer-id throws an error.
  • Broadcasting a callback to multiple clients is not supported — use util.event.emit instead if you need to notify all peers without expecting a reply.
TypeKeyDefaultSideDescription
boolremotefalseShared• When true - transmits the event to the remote side; client sends to server, server broadcasts to all peers
• When false - fires handlers on the current side only
intpeer0ServerTarget peer-id for directed server-side remote dispatch (ignored on client)
• When 0 - broadcasts to all connected peers
• When > 0 - sends to that specific peer only
boolcollect_allfalseShared• When false - only the first registered handler is fired and its return values are resolved directly, unpacked
• When true - all registered handlers are fired and results are resolved as a table of tables, one entry per handler

Examples

Await a return value from the first local handler
util.event.on("on_get_score", function()
    return 42
end)

util.thread.create(function(self)
    local ok, score = self:await(util.event.emit_callback("on_get_score"))
    core.engine.print("info", "Score:", score) -- 42
end):resume()
Collect return values from all local handlers using collect_all
util.event.on("on_query", function() return "handler_a" end)
util.event.on("on_query", function() return "handler_b" end)

util.thread.create(function(self)
    local ok, results = self:await(util.event.emit_callback("on_query", { collect_all = true }))
    core.engine.print("info", "Handler 1 returned:", results[1][1]) -- 'handler_a'
    core.engine.print("info", "Handler 2 returned:", results[2][1]) -- 'handler_b'
end):resume()
Multiple handlers each returning multiple values and being collected using collect_all
util.event.on("on_status", function() return "ok", 100 end)
util.event.on("on_status", function() return "ok", 200 end)

util.thread.create(function(self)
    local ok, results = self:await(util.event.emit_callback("on_status", { collect_all = true }))
    core.engine.print("info", results[1][1], results[1][2]) -- 'ok'  100
    core.engine.print("info", results[2][1], results[2][2]) -- 'ok'  200
end):resume()
Send a callback from client to server and await the reply
if core.engine.get_platform() == "client" then
    util.thread.create(function(self)
        local ok, result, data = self:await(util.event.emit_callback("on_fetch_inventory", { remote = true }, "weapons"))
        core.engine.print("info", "Server replied:", result) -- 'ok'
        core.engine.iprint(data)
    end):resume()
else
    util.event.on("on_fetch_inventory", function(category)
        local thread = util.thread.current()
        thread:sleep(2000)
        return "ok", { category = category, items = { "sword", "bow" } }
    end, { async = true })
end
Send a callback from server to a specific client and await the reply
if core.engine.get_platform() == "client" then
    util.event.on("on_request_position", function()
        local thread = util.thread.current()
        thread:sleep(500)
        return 12.5, 0.0, 87.3
    end, { async = true })
else
    util.thread.create(function(self)
        local ok, x, y, z = self:await(util.event.emit_callback("on_request_position", { remote = true, peer = 1963414407 }))
        core.engine.print("info", "Client position:", x, y, z)
    end):resume()
end
Collect aggregated results from multiple handlers on the remote client
if core.engine.get_platform() == "client" then
    util.event.on("on_status", function() return "ok" end)
    util.event.on("on_status", function() return "ok", { detail = "extra" } end)
else
    util.thread.create(function(self)
        local ok, results = self:await(util.event.emit_callback("on_status", { remote = true, peer = 1963414407, collect_all = true }))
        core.engine.print("info", "Handler 1:", results[1][1]) -- 'ok'
        core.engine.print("info", "Handler 2:", results[2][1], tostring(results[2][2])) -- 'ok'  'table: 0x...'
    end):resume()
end

On this page