value_type
Converts a **handle-backed value type** — `ColorSequence`, `NumberSequence` — between the live object a session holds and the durable payload its `serialize()` produces.
value_type
Converts a handle-backed value type — ColorSequence, NumberSequence —
between the live object a session holds and the durable payload its
serialize() produces.
A sequence has two forms:
| Form | Shape | Meaning |
|---|---|---|
| live | { kind = "ColorSequence", __h = 13 } | __h names a curve in this process's curve registry |
| durable | { kind = "ColorSequence", keypoints = { … } } | the curve itself, which any session rebuilds |
__h is minted when the curve is created and is meaningful for exactly as long
as that registry lives, so a record carrying it names nothing in the session
that reads it back. The keypoints are the authored data, and they cross every
boundary intact — which is why a record carries the durable form and a live
component field carries the live one.
Both forms carry kind, so a value read out of a component field says which
converter owns it whether it came from memory or from a record. The two are told
apart by which of __h / keypoints the table itself holds.
Field.table copies the table it is handed and drops its metatable, so a live
value read back off a component field answers no methods at all. The field gives
that same stored table back on every read, so bind puts the methods on it once
and the field answers them from then on.
API
local ValueType = require("modules.value_type")
local color = ColorSequence.new({ 1, 0.3, 0.1 }, { 0.1, 0.1, 1 })
ValueType.kindOf(color) -- "ColorSequence"
ValueType.isLive(color) -- true
ValueType.isPayload(color:serialize()) -- true
-- The keypoints out of either form. The second argument is the kind to read a
-- bare `{ __h = n }` as, for a caller that knows its field's type.
ValueType.keypoints(component.color, "ColorSequence")
-- The durable payload for a live value; nil for anything else.
ValueType.serialize(color) -- { kind = "ColorSequence", keypoints = { … } }
-- The live value a payload names, rebuilt in this session; nil for anything else.
ValueType.revive(record.color)
-- A whole component field map, with every payload rebuilt.
local applyData, rebuilt = ValueType.withRevived(record.data)
-- Put the methods back on a value read out of a component field.
ValueType.bind(component.color, "ColorSequence"):evaluate(0)
withRevived returns the same table when the map carries no payload, so the
common case allocates nothing; the second return says how many fields were
rebuilt.
Consumers
scene_saver asks per field as it serialises an entity, and writes the payload
in place of the handle. scene_loader rebuilds the payloads in a component's
record before applying it, so the component takes the field holding the same
curve it held when the record was written. dirty_hot_reload does the same as
it applies a body, and reduces the live side of its record comparison to the
durable form so both sides are compared in one shape. ParticleEmitter reads
the keypoints out of whichever form its color / size / transparency /
squash fields arrive in.
Scoped to this part · feeds back into the world's score.