component_events
Per-instance runtime for declared component events. A component's `events` block (built with `Event(...)`, see `event.module`) is a schema: a map of event name to descriptor. This module turns that schema into the live objects an instance actually fires and listens on.
component_events
Per-instance runtime for declared component events. A component's events
block (built with Event(...), see event.module) is a schema: a map of
event name to descriptor. This module turns that schema into the live
objects an instance actually fires and listens on.
Three faces, one Signal
Each declared event resolves to one signal.module Signal, reachable
through three tables that share it:
buildSignals(schema)returns the private, fire-capable table (:Fire, the raw Signal method). Engine-internal — never handed to user code directly.buildEmitter(signals, schema)returns the owner-side emitter, bound into the declaring component's own env asevents. Each entry has:fire(payload)only — lowercase, symmetric with the facade — and validates the payload against the event's declared members before dispatch. The component's own code fires withevents.onHit:fire({ dmg = 10 }).buildFacade(signals)returns a subscribe-only view over the same table, exposed to outside callers through the ComponentProxy's.eventskey. Each entry has:connect(fn)/:once(fn)/:wait()— no:fire.
setup(schema) builds all three in one call and returns
{ signals, emitter, facade }; the engine calls it once per instance.
local schema = { onHit = { payload = { dmg = Field.number(0, NoSync) }, sync = false } }
local runtime = component_events.setup(schema)
-- inside the component's own code (env global `events` is runtime.emitter):
events.onHit:fire({ dmg = 10 })
-- outside code, through the proxy (proxy.events is runtime.facade):
proxy.events.onHit:connect(function(p) print("hit for", p.dmg) end)
Fire authority by construction
buildFacade never returns the underlying Signal object, only a fresh
table exposing connect / once / wait. There is no fire method to
find on the facade at any key, so outside code cannot fire a component's
events no matter what it holds a reference to. Firing authority lives only
with the owner, through the emitter's env events global.
Both tables reject access to an undeclared event name: the private table's metatable raises on a bad key, and the facade raises on a bad key too, so a typo surfaces immediately instead of silently returning nil.
Cleanup
Connections made through the facade are ordinary signal.module
connections, tracked and torn down the same way as any other signal
connection: entity_signals.module disconnects everything sourced from an
entity when that entity is destroyed. Component events carry no separate
cleanup path.
Scoped to this part · feeds back into the world's score.