Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit61a33237e0

README.md

entity_signals

Per-entity destroy signals, surfaced on the entity proxy as entity(id).onDestroying and entity(id).onDestroyed, plus the dispatch the despawn pipeline calls to fire them.

local boss = entity.find("Boss")

boss.onDestroying:Connect(function()
    -- runs BEFORE teardown — final state is still readable
    saveBossStats(boss.component.get("Combatant").public)
end)

boss.onDestroyed:Connect(function()
    log.info("boss fully cleaned up")
end)

entity.despawn(boss.id)   -- onDestroying fires, then teardown, then onDestroyed

Order

onDestroying and onDestroyed are Signals (see signal.module), created on first access. The despawn pipeline drives the order. For a cascade rooted at one entity.despawn(root) call:

  1. onDestroying fires across the whole subtree, root first — before any component teardown, so handlers read final state.
  2. components tear down (onDestroy(self) per component), leaves first.
  3. onDestroyed fires per entity as it is removed, leaves first.

onDestroying handlers must not yield, and the entity is frozen against reparenting / re-mutation during the destroying window.

An entity nobody observes costs nothing and fires nothing.

Entity-keyed tables

A module that keeps per-entity state for something to read every frame (a registry a render feature walks, a set of marked entities) declares its table with entityKeyed. When an entity is despawned, by entity.despawn, a parent's cascade, a scene rebuild or reload, or a mode flip, its id leaves every such table, so the reader only ever meets entities in the world and its cost follows them.

local EntitySignals = require("@builtin::modules.entity_signals")

local marked = EntitySignals.entityKeyed({})

function M.mark(id: string)
    marked[id] = true
end

The table is one of the engine's own records: a module-state restore leaves it as it stands, so an id that left it stays out.