lloyAlloy on GitHub
message
Docs

message

Updated
--- One tick of the simulation.
export message Step(dt: number, gravity: number)

--- A job, and the levels a worker answers with.
export message Light(job: number, input: buffer) reply(job: number, levels: buffer)

--- The parts that fell this tick.
export message Fallen(parts: { BasePart }) as parallel
Step.fire(worker, 0.016, 196.2)            -- worker:SendMessage("Step", ...)
Step.on(function(dt, gravity) end)         -- script:GetActor():BindToMessage("Step", ...)
Fallen.once(function(parts) end)           -- the first message, then the binding goes
Light.replied(function(job, levels) end)   -- any script: the answers come back here
Light.on(function(job, input, respond)     -- the worker: `respond` sends one
    respond(job, input)
end)

A typed channel between actors. The name is the topic, so a typo in a topic cannot compile. The parameters type both ends: fire takes them after the Actor, and the handler of on and once gets them.

fire sends to the Actor it takes first. on and once bind on the Actor of the script that calls them. That script needs the .actor. infix, and the compiler reports the call in any other file. as parallel binds with BindToMessageParallel. The handler then runs in the parallel phase and takes the rules of a parallel block.

reply(params) gives the message an answer that any script can take, an actor script or not. replied binds a handler of the reply, and it needs no .actor. infix. The handler of on and once takes respond after the parameters, and respond(...) sends the reply to the script that fired. The header reads parameters, then reply(...), then as parallel.

The answer rides a BindableEvent. Each Luau VM makes its own on the first replied, and fire sends it after the arguments, where the source does not show it. A fire before any replied of its VM sends no event, and respond then does nothing. A BindableEvent fires only in the serial phase, so respond in an as parallel handler calls task.synchronize() first, and the statements after it run serial. Nothing matches an answer to its request: pass a job number both ways, as job above. A message whose parameters end in ... takes no reply.

A message carries what the engine carries. The arguments cross as they are, with no serializer, because an actor boundary is not a network boundary. A function, a coroutine, and a value that keeps its methods on a metatable, such as a struct, a payload enum, or a HashMap, do not arrive whole. Each one is a WireType report with the rule named for actors. A reply parameter takes the same rules. A SharedTable crosses by reference. A parameter takes no default and no pattern.

The name holds a table with topic and a fire, and replied under a reply, so Attributes.get(Step, tag) reads an attribute of the declaration. on and once bind only through the name, because the compiler writes them on the Actor of the script where they stand.

An attribute goes on a message when its declaration names message in its targets. A contract reads the parameters as fields: requires field job: number.

The word is free as a name. message declares only before a name and ( on the same line, so local message = "hi" and message(x) read the Luau way.