Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit44fd9e59bd

README.md

Built-in Asset Types

This directory holds the built-in asset types. Each subfolder named <typename>.assetType/ defines a type called <typename>, and every <name>.<typename>/ folder anywhere in a world is an asset of it.

A type registers the moment its folder is written, whether it lives here or anywhere under /zero/source/, so a built-in type and one you author work the same way. asset.list("assetType") lists the registered types and asset.categories() their names. To make one, run asset.create("assetType", "<typename>"); the scaffold passes validation.

The complete authoring reference is assetType.assetType/README.md (guides { path: "types/assetType" }): every type.yaml key, the files an asset holds, settings, the Inspector, about.yaml, descriptions, behavior.luau and its hooks, previews, references, indexing, and what blocks a push. This page is the map around it.

What a type folder holds

A type folder typically holds a type.yaml (the contract for what an asset of the type holds), a behavior.luau when the type has code, a README.md, an about.yaml, an inspector.luau, and a template/ folder of the files asset.create starts a new asset from.

What the folder must contain is set by the meta-type's own type.yaml (assetType.assetType/type.yaml), and asset.validate on a type reports anything missing. What an asset of the type must contain is set by the type's own type.yaml, so two types can differ in every file they require. Open any built-in type's type.yaml to see a complete one.

How template/ is used

asset.create(typename, name) copies the files of template/ into /zero/source/<name>.<typename>/ and replaces each literal [name] with the new asset's name. No other marker is substituted. A type with an onCreate hook lays the files the hook returns over the template, so a type needs a template, an onCreate, or both.

Template files are real and parse-clean, so a new asset opens ready to edit. Where a type asks its assets to say what they are, the template's text starts with [[describe]] and holds nothing else; see "Ask your assets to say what they are" in the reference.

.metadata

.metadata is a file inside the asset folder that holds agent-editable tags and free-form fields. Because it is inside the folder, it travels with the asset on every move, pull and install. Whether an asset of a type has one is that type's choice: its type.yaml lists .metadata under files.required, under files.optional, or not at all. A loose file (a .glb, a .png, a script.luau with no asset folder around it) is a resource, not an asset, and has no .metadata; to give something tags, make it an asset of a type.

The link from an asset to its type

Every asset records which type it was written against, pinned by the type's guid in the asset's engine-managed .refs. Pulling an asset into another world carries it to the same type definition, so two worlds that each ship a type of the same name never disagree about which one an asset means.

  • A folder whose dotted suffix has no registered type is not an asset. Creating one is rejected; author the type first.
  • A push is refused when an asset's type is not among what it carries.

Type-owned shared code

A type can ship code that every asset of it uses, without each asset requiring a path to wherever that code lives. Put a <name>.module/ folder inside the type (conventionally shared.module/), and name it in the type's behavior.luau with the modules key:

-- inside <typename>.assetType/behavior.luau
M.modules = {
    shared = require(".shared"),   -- the sibling shared.module/
}

An asset reaches it with one line:

-- inside any <name>.<typename>/init.luau
local api = asset.containing(__FILE__).modules.shared

__FILE__ is the chunk's own VFS path. asset.containing(__FILE__) is the asset the code sits in, and .modules.<name> is the code its type ships. The lookup follows the asset's pinned type, so an imported asset reaches the type version it was authored against, and an asset of one type never reaches another type's modules.

ExpressionReturns
ref.modules.<name>The exports of the module the asset's type declares under that name.
ref.typeRefThe pinned type's guid, or nil for an asset with no type.
asset.typeRef(target)The same guid for a handle, identity, guid or path.
asset.resolve(ref.typeRef)The type itself as an asset reference.

A .toolbox also ships a shared.module/, but its tools sit in the same folder and reach it with a relative require(".shared"). Assets of a type live anywhere, so they use asset.containing(__FILE__).modules.<name> instead.

testSuite.assetType and service.assetType are worked examples: each carries its runtime in a shared.module/ that its behavior.luau declares.

Worked examples to read

  • dynamicAsset.assetType: ref methods and an onChange that regenerates only when its input changed, the pattern every onChange hook needs because its own writes arrive again.
  • material.assetType: a type whose picture is its identity (preview:, and indexing: with a content entry), with rich ref methods and onCreate.
  • component.assetType: settings read from the component's own public fields, and an Inspector card an asset can add to.
  • texture.assetType/inspector.luau: a full Inspector declaration.