Log inGet started

dataType (asset type)

Updated October 8, 2026

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 named set of values for settings something already declares, 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   (`fields: {}`, a valid contract with no fields yet)
--   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.
  • ls /zero/source/<Name>.dataType lists the files the folder holds.

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 named set of values for one declarer's settings schema (an assetType, an importer, a component), not a contract of its own.
  • .component — executable entity behavior, versus this type's typed configuration data.

In the Inspector

Contract: the contract it extends (a link), every field across the extends chain with its type, whether it is required, its default and its bounds or allowed values, and Count instances, which scans every data asset for the ones that satisfy it (a count taken before a data asset was made or removed says so).

  • asset-type
  • reference