Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit3bb1da2647

README.md

asset_events

Per-asset runtime for declared asset events. An assetType's behavior.luau declares an events schema the same way a component declares one; this module turns that schema into the live objects an asset's ref fires and listens on.

Three faces, one Signal — keyed by the asset

The construction is component_events': one Signal per declared event, reachable through a private fire-capable table, an owner-side emitter the type's own behavior fires with, and a subscribe-only facade every other holder of the ref sees. The facade exposes connect / once / wait and has no fire at any key, so firing authority stays with the type.

What differs is the owner. A component event belongs to one instance on one entity; an asset event belongs to the asset, so the runtime is keyed by the asset's stable guid. Every resolver of that guid subscribes to the same Signals, and a ref that is reclaimed and re-resolved re-attaches to the subscriptions already there — the same reason asset_ref's runtime table is guid-keyed rather than stored on the envelope.

Both tables reject an undeclared event name: the private table's metatable raises on a bad key and the facade raises on a bad key, so a typo surfaces at the subscribe call instead of returning nil.

Declaring events on a type

-- <name>.assetType/behavior.luau
local M = {}

M.events = {
    changed = { payload = { value = Field.number(0, NoSync) } },
}

M.ref = {
    poke = function(self)
        local emitter = require("@builtin::assetTypes.assetType.shared.events").emitter(self.guid)
        if emitter ~= nil then emitter.changed:fire({ value = 1 }) end
    end,
}

return M
-- any holder of the ref
local ref = asset.resolve("@builtin::…")
ref.events.changed:connect(function(p) print(p.value) end)

Exports

  • M.forAsset(guid, eventSchema) — the runtime for one guid, built on first use and reused after. A later call with a different schema reconciles: surviving events keep their Signal and their subscribers, new events get a fresh one, removed events are disconnected and dropped.
  • M.facade(guid) — the subscribe-only view, or nil before a runtime exists.
  • M.emitter(guid) — the owner-side :fire view, or nil before a runtime exists.
  • M.has(guid) — whether a guid holds a live runtime.
  • M.teardown(guid) — drop one asset's runtime, disconnecting subscribers.
  • M.teardownAll() — drop every runtime. Called on an engine mode flip so a play-mode subscription does not survive into edit.
  • M.liveGuids() — every guid holding a runtime, sorted.

Notes

  • Subscriptions made through the facade are ordinary signal.module connections and disconnect the same way any other does.
  • The runtimes table is held strongly, and its lifetime is bounded by the mode flip that clears it.