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
optionsand pass arguments directly aftername.
Only serializable values survive remote transmission. Non-serializable values are silently dropped.
- Safe to send —
nil,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
| Type | Name | Description |
|---|---|---|
string | name | Name of the event to fire |
table | options | Routing 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_all | Handlers | Resolved value |
|---|---|---|
false | None | Resolves immediately with no values |
false | One or more | Resolves directly with the first handler's return value(s), unpacked |
true | None | Resolves immediately with no values |
true | One or more | Resolves 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.
peeris mandatory when calling with{ remote = true }from the server — omitting it throws an error.peermust 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.emitinstead if you need to notify all peers without expecting a reply.
| Type | Key | Default | Side | Description |
|---|---|---|---|---|
bool | remote | false | Shared | • 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 |
int | peer | 0 | Server | Target 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 |
bool | collect_all | false | Shared | • 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
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()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()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()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 })
endif 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()
endif 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