Appearance
Talking between scripts
Three ways for scripts to reach each other, and when each is right.
| Way | Reaches | Good for |
|---|---|---|
events:emit / listen | your own scripts | a switch and its door |
events:send / open | anyone who opted in | your HUD and somebody else's shop |
objects | one object you can address | painting every lamp |
Your own build talking to itself
The sender:
luau
--!strict
local self = require("self")
local events = require("events")
self:on_touch(function(player)
events:emit("alarm", { by = player.id })
end)Every listener:
luau
--!strict
local self = require("self")
local events = require("events")
events:listen("alarm", function(data)
self:set({ color = "#e35d6a", emissive = 3 })
self:after(5, function()
self:set({ color = "#3a3a3a", emissive = 0 })
end)
end)One button, twenty lamps, no list of ids anywhere. Adding a lamp is copying the listener onto it.
emit is private to you. Another creator's scripts never hear it, even if they guess the channel name.
Two scripts on the same object
They do not share variables, but they hear the same events:
luau
-- main.luau
events:emit("state", { open = true })
-- lock.luau
events:listen("state", function(data)
-- ...
end)emit reaches every script of the same owner including the one that sent it, so a script can listen to its own channel. That is occasionally what you want and occasionally a loop — see below.
Crossing owners
Your personal HUD is yours; the shop is somebody else's. emit cannot reach across, and that is deliberate.
The receiver opens a door:
luau
events:open("acme.shop", function(data, from)
if from.kind ~= "hud" then return end
serve(from.owner, data)
end)The sender knocks:
luau
events:send("acme.shop", { want = "rope" })open is the receiver's consent. Nothing can push a message into a script that never asked for one.
The from table is stamped by the region and is the only part you can trust:
| Field | |
|---|---|
kind | "object", "hud" or "attachment" |
owner | the account that owns the sending script |
object | the sending object's id, or nil from a HUD |
Channel names are global to the region. Two creators who both pick "shop" are on the same channel. Prefix yours: "acme.shop", "mira.lift".
Reaching a specific object
When you know which object, objects is more direct than a broadcast:
luau
local objects = require("objects")
local lamps = objects:tagged("streetlight")
if lamps then
for _, lamp in ipairs(lamps) do
lamp:set({ light = true })
end
endTags beat names: a name is what a person reads and may rename; a tag is what a script means.
You cannot read another object's state, only act on it. Tags are the way round it: an object's own script controls its tags, so a tag like "door.open" is the public half of an object that anyone may check with has_tag.
Choosing: event or tag?
| Event | something happened. A moment |
| Tag | something is true. A state |
A door that just opened emits; a door that is open carries a tag. Somebody arriving later missed the event but can still read the tag.
What can travel
Nothing, a boolean, a number, a string, or a table with string keys — 3 levels deep, 32 entries, 512-byte strings. Anything else is refused with a reason rather than trimmed.
Values arrive as what they were; unlike self.store, you do not convert.
Always check the shape you were given:
luau
events:listen("alarm", function(data)
if type(data) ~= "table" then return end
local who = tostring(data.by or "")
-- ...
end)Loops
If A emits to B, B emits to C, and C emits back to A, you have built a loop. The region cuts it at 64 deliveries rather than letting it run.
If a message mysteriously stops arriving, a loop is the first thing to check. The usual fix is a guard:
luau
local settling = false
events:listen("state", function(data)
if settling then return end
settling = true
-- ... change things, which may emit ...
settling = false
end)Ceilings
Sends per event (emit + send) | 16 |
| Cascade cut at | 64 deliveries |
| Channel name | 1 – 64 characters |
Registering the same channel twice replaces the handler — a script has one handler per channel, not a list.
Built-in channels
The world reports on its own channels; listen with events:listen:
| Channel | |
|---|---|
world.destroyed | something with health was destroyed |
world.collapse | a building piece lost its support and came down |
world.block | a block was placed or broken |
world.build | a building piece was raised |
