Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit61a33237e0

README.md

field

Single-field component schema builder. Every public.X on a component declares its type, default value, and Sync/NoSync replication mode in one place via the Field.<kind>(default, mode) constructor that produces a FieldDesc<T> descriptor. The engine walks public at component registration, extracts each descriptor into the per-component asset-fields + sync registries, runs each asset default through the category-aware resolver, and replaces the descriptor with its unwrapped default so user code reads public.X as the value (not the descriptor).

This module is exposed as the Field, Sync, and NoSync Luau globals via --!global directives. Component authors call Field.<kind> and Sync / NoSync directly without a require. --!global-types exports SyncMode, FieldDesc<T>, AssetRef<C>, EntityRef, and ComponentRef<T> to every typed source.

Exports

  • Field.number(default, mode, marker?) -> FieldDesc<number>
  • Field.range(min, max, default, mode, marker?) -> FieldDesc<number>
  • Field.integer(min, max, default, mode, marker?) -> FieldDesc<number>
  • Field.string(default, mode, marker?) -> FieldDesc<string>
  • Field.enum(values, default, mode, marker?) -> FieldDesc<string>
  • Field.bool(default, mode, marker?) -> FieldDesc<boolean>
  • Field.vec2(default, mode, marker?) -> FieldDesc<vec2>
  • Field.vec3(default, mode, marker?) -> FieldDesc<vec3>
  • Field.quat(default, mode, marker?) -> FieldDesc<quat>
  • Field.color(default, mode, marker?) -> FieldDesc<color>
  • Field.entityRef(default, mode, marker?) -> FieldDesc<EntityRef>
  • Field.assetRef<C>(category, default, mode, marker?) -> FieldDesc<AssetRef<C>>
  • Field.dataRef<C>(contract, default, mode, marker?) -> FieldDesc<AssetRef<"data">>
  • Field.componentRef<T>(componentType, default, mode, marker?) -> FieldDesc<ComponentRef<T>>
  • Field.table<T>(default, mode, marker?) -> FieldDesc<T>
  • Field.alias(target, description?) -> FieldDesc<any>
  • Sync / NoSync — string singletons used as the replication-mode argument.

Every constructor's trailing marker argument accepts the bare Serialized marker (unchanged meaning), a plain string read as the field's description, or a FieldOptions table carrying serialized and/or description.

Types (global via --!global-types):

  • SyncMode = "Sync" | "NoSync"
  • FieldOptions = { serialized: SerializedMode?, description: string? }
  • FieldDesc<T> = { __zero_field: boolean, kind: string, default: T?, sync: boolean, serialized: boolean?, description: string?, enforced: boolean, category: string?, componentType: string? }
  • AssetRef<C> = { __ref: string, type: C, name: string, guid: string, identity: string, path: string, primary: string? } — the envelope asset.resolve / asset.ref / asset.list hand back. The category is in type; primary is the single content file the resolver found, which getSource reads, falling back to path.
  • EntityRef — the live entity proxy (entity(id) / entity.spawn() / bundle:instantiate() return it; carries id, name, position, component, …). An entityRef field accepts a proxy OR a raw id string on write and reads back a live proxy (.id for the string, nil if unset); the stored/serialised form is the plain id.
  • ComponentRef<T> = { __ref: string, entity: string, componentType: T }

Usage

-- Inside a `public` block on a component
public = {
    positionX = Field.number(0, Sync),
    speed     = Field.number(5, Sync, "Metres per second at full throttle."),
    enabled   = Field.bool(true, Sync),
    material  = Field.assetRef("material", "@my-library::materials.gold", Sync),
    source    = Field.assetRef("bundle", nil, Sync),
    target    = Field.entityRef(nil, Sync),
    aimCam    = Field.componentRef("Camera", nil, NoSync),
    idMap     = Field.table({} :: { [string]: string }, NoSync),
}

Notes

  • Sync / NoSync is REQUIRED on every constructor. Passing nil, true, false, or anything else raises a load-time error so a multiplayer-by-default engine can't silently ship single-player components.
  • Field.assetRef's category argument is inferred as a singleton string type (C). Cross-category assignment is therefore a type error: AssetRef<"mesh"> does not unify with AssetRef<"material">.
  • Field.componentRef's componentType argument works the same way — the engine validates the referent's component type at every write.
  • Field.table<T>: pin the shape by ascribing the default, Field.table({} :: MyShape, NoSync).
  • A live component proxy held in a Field.table value, at any depth, is saved in the scene record as its reference: the { __ref, entity, componentType } envelope Field.componentRef stores, so a component holding itself saves one envelope naming itself.
  • Field.range and Field.enum carry a constraint the engine checks on the field's default and on every write, through field_constraints.module. A value outside the declared interval or member set is refused with the interval or the whole set named, so what the field reads back is a value its consumer can use.
  • Field.integer carries the same range constraint with whole = true: a fraction is refused as well as a value outside the interval, for a count, a level or a group number, where a fraction would be read as some other whole number.
  • The engine resolves any string default for assetRef through asset.resolve(identity, category) at component registration, so the first read of public.<field> returns a resolved envelope, not a raw string.
  • Field.dataRef rides the same assetRef machinery (category "data") plus a contract gate: every write (and the registration-time default) must resolve to a .data instance whose dataType contract chain includes contract, checked through the generic field-constraint hook. A rejected write raises and leaves the field's previous value unchanged.
  • Constrained fields (Field.dataRef) are top-level only — declaring one inside Field.struct / Field.list aborts component registration with an error.
  • FieldDesc.enforced is true exactly for the kinds that carry a constraint (enum, range, bitmask, dataRef, taggedRef, instantiableRef) — the field the field_constraints hook checks on every write and default. Every other kind reads enforced = false.