Log inGet started

dataType (asset type)

A dataType is a user-authored typed-data contract: it declares a set of fields — names, types, constraints, defaults — that any bound .data instance must satisfy. A contract can carry an optional…

When to use one

  • You want a typed, validated configuration shape (a weapon's damage / fire mode / recoil profile, a quest's objectives, a loot table's entries) that many .data instances can be authored against.
  • You want field-level validation (required, numeric ranges, enums, references to other assets or data instances) enforced the same way everywhere the contract is used.
  • You want shared structure across related contracts without duplicating field declarations — extends factors common fields into a parent contract.

If you only need a snapshot of one component's properties, use a .preset instead. If you need executable shared logic rather than typed configuration, use a .module.

Where it lives

  • Source: /zero/source/.../<Name>.dataType/
  • Identity: <Name> (the .dataType suffix strips).
  • Folder shape:
    • schema.yaml — the field declaration (fields:, optional extends:). Required.
    • README.md — instance documentation: what the contract models, what each field means. Required.
    • .metadata — description + tags for search. Required.
    • behavior.luau — optional ref methods this contract's .data instances expose beyond the built-in :contract / :satisfies / :validate surface.

How to create one

asset.create("dataType", "<Name>")
-- Creates: /zero/source/<Name>.dataType/
--   schema.yaml   (empty `fields:` scaffold)
--   README.md     (instance README template)
--   .metadata     (empty description/tags — fill both to publish)

Then edit schema.yaml to declare the contract's fields.

schema.yaml field-spec vocabulary

extends: "item"           # optional: parent contract identity
fields:
  damage:
    type: number
    required: true
    min: 0
    max: 100
  fireMode:
    type: string
    enum: ["semi", "burst", "auto"]
    default: "semi"
  muzzle:
    type: vec3
    default: [0, 0, 0]
  tint:
    type: color             # "#rrggbb"/"#rrggbbaa" or {r,g,b[,a]}
  fireSound:
    type: assetRef
    assetType: soundClip     # required for assetRef fields
  recoil:
    type: dataRef
    contract: recoilProfile  # required for dataRef fields
    required: true
  falloff:
    type: array
    items:
      type: struct
      fields:
        distance: { type: number }
        multiplier: { type: number }

Field types: number, string, boolean, vec3, color, assetRef, dataRef, array, struct.

  • required — a boolean; a field with a default is never "missing" even when required is set.
  • default — must itself satisfy the field's own constraints (checked at declaration time).
  • min / max — apply only to number fields.
  • enum — apply only to scalar fields (number / string / boolean); the value must equal one of the listed entries.
  • assetRef — needs an assetType; validated against asset.resolve(identity, assetType).
  • dataRef — needs a contract; validated against whether the referenced .data instance's contract chain includes it (:satisfies).
  • array — needs an items field spec every element must satisfy.
  • struct — needs a fields map of nested field specs; struct values reject unknown keys the same way top-level values do.

extends

A contract may declare extends: "<parentContract>" to inherit the parent's fields. The child adds new fields — it never redeclares a field the parent (or any ancestor) already declared; doing so is a schema error. Behavior is optional at every level: a contract may carry behavior.luau or not regardless of whether its parent does. Chains are single-inheritance and acyclic — a cycle raises loudly the moment the chain is resolved (:schema, :validate, :defaults, :chain all resolve the chain). Editing a parent's schema does not re-warn its child contracts at edit time; validation stays loud where the chain is used — :schema / :validate calls and the publish gate.

Ref surface (AssetRef<dataType>)

  • ref:schema() — the merged field map (own fields + every field inherited through extends).
  • ref:validate(values)(ok, violations) against the merged schema. violations is an array of { path, message }.
  • ref:defaults() — the default value table the merged schema declares (a fresh, mutable table).
  • ref:parent() — the parent contract ref (extends), or nil for a root contract.
  • ref:chain() — the full extends chain as refs, root parent first, this contract last.
  • ref:instances() — every .data instance bound to this contract or to a contract that extends it, as AssetRefs. A full scan over the world's data assets — a discovery surface for authoring and tooling, not a per-frame call.

Discovery

  • asset.list("dataType") — every registered contract, as AssetRefs.
  • asset.inspect("<name>") — the contract's files, declared schema body, instance README, and this type README.
  • cat /zero/source/<Name>.dataType — same summary.

Authoring conventions

  • Name contracts for the concept they type (weapon, questObjective, lootTable), not for a specific instance.
  • Factor genuinely shared fields into a parent contract via extends; don't duplicate a field declaration across sibling contracts.
  • Document every field's meaning and units in README.md — the schema only carries types and constraints, not intent.

Common pitfalls

  • Redeclaring an inherited field. extends merges parent fields in; a child that re-declares a field name the parent already declared is a merge error, not a silent override.
  • Cyclic extends. Contract A extending B extending A raises loudly the first time the chain is resolved — fix the chain, there is no partial result to fall back to.
  • assetRef/dataRef without existence checks. assetType and contract on ref fields are declaration-time requirements; the referenced asset or data instance is checked at validation time, not declaration time.
  • .data — a configured value instance bound to a dataType contract.
  • .preset — a snapshot of one component's properties, not a typed multi-field contract.
  • .component — executable entity behavior, versus this type's typed configuration data.
  • asset-type
  • reference