Package
All Ambient packages must have an ambient.toml
manifest that describes their functionality. This format is in flux, but is inspired by Rust’s Cargo.toml
.
Next to the ambient.toml
, other files may be present. A screenshot.png
can be used to provide a thumbnail for the package on the Ambient website. A README.md
can be used to provide a description of the package on the Ambient website.
To view documentation for a package, add --open-docs
to a command that builds packages (i.e. ambient build/run/serve/...
). This documentation is autogenerated and contains all items available to that package.
Package definitions are “projected” to guest code, so that they can use them. For Rust, this is done through the use of a build script that generates a src/packages.rs
, creating a packages
module that contains all the packages known to the package, including itself. Your own package can be accessed through packages::this
.
Reference
SnakeCaseIdentifier
s are snake-case ASCII identifiers (as a string)PascalCaseIdentifier
s are PascalCase ASCII identifiers (as a string)Identifiers
are either aSnakeCaseIdentifier
or aPascalCaseIdentifier
based on contextItemPath
s are a double-colon-separated list ofSnakeCaseIdentifier
s followed by a singleIdentifier
. For example,my_package
is anIdentifier
, andmy_package::my_component
is anItemPath
.- See
ValueType
for a description of the types that can be used in Ambient.
Package / [package]
The package
section contains metadata about the package itself, such as its name and version.
Property | Type | Required | Description |
---|---|---|---|
id | SnakeCaseIdentifier | ✅ | The package’s ID. Autogenerated by Ambient. Do not attempt to specify your own ID. |
name | String | ✅ | A human-readable name for the package. |
version | String | ✅ | The package’s version, in (major, minor, patch) format. Semantically versioned. |
content | PackageContent | ✅ | A description of the content of this Package. See below. |
ambient_version | String | ✅ | The version of Ambient this package is intended to be used with. |
authors | String[] | The authors of the package. | |
description | String | A human-readable description of the package. | |
repository | String | Where the source code for this package can be found. | |
public | Bool | Indicates if this package will be publicly available when deployed. Defaults to true. |
PackageContent
These are the valid configurations for package content:
# A Playable is anything that can be run as an application; i.e. games, examples, applications etc.
content = { type = "Playable" }
content = { type = "Playable", example = true } # example defaults to false
# Assets are things you can use as a dependency in your package
content = { type = "Asset", models = true, textures = true } # Contains models and textures
# These are the valid asset types:
#
# models
# animations
# textures
# materials
# audio
# fonts
# code
# schema
#
# You can use any combination of them
# Tools are things you can use to develop your package
content = { type = "Tool" }
# Mods are extension to Playables
content = { type = "Mod", for_playables = ["i3terk32jw"] }
Example
#
# The package section describes all package metadata.
#
[package]
id = "d563xtcr72ovuuhfkvsgag6z3wiy5jwr"
name = "My Cool Package"
version = "0.0.1"
content = { type = "Asset", code = true }
ambient_version = "0.3.0"
# The following are optional:
# authors = ["Cool Cat"]
# description = "A sample package that's the coolest thing ever."
# repository = "https://my-cool-forge.io/my-cool-package"
# public = true
Build / [build]
The build section contains settings related to building the package.
Rust Settings / [build.rust]
Property | Type | Required | Description |
---|---|---|---|
feature-multibuild | String[] | An array of strings defining the Rust features to be used when building the package. This is used to build the same code for both client and server.cargo build will be run with each of these features to produce a separate WASM binary, which is then componentized and copied into a folder of the corresponding name in build/ .Client and server are built by default (e.g. ["client", "server"] ); this is exposed so that you can disable building one side entirely if required. |
Example
[build.rust]
feature-multibuild = ["client", "server"]
Components / [components]
The components
section contains custom components defined by the package. Components are used to store data on entities.
This is a TOML table, where the keys are the component IDs (SnakeCaseIdentifier
), and the values are the component definitions.
Property | Type | Required | Description |
---|---|---|---|
type | ValueType | ✅ | The type of the component. |
name | String | A human-readable name for the component. | |
description | String | A human-readable description of the component. | |
attributes | ComponentAttribute[] | An array of attributes for the component. |
A ComponentAttribute
is a string that can be one of the following:
Debuggable
: this component can have its debug value printed, especially in ECS dumpsNetworked
: this component is networkedResource
: this component will only ever be used as a resource; will error if attached to an entityMaybeResource
: this component can be used as a resource or as a component; necessary if treating this component as a resourceStore
: this component’s value should be persisted when the world is saved
Example
[components]
# Inline tables can be used.
cool_component = { type = "I32", name = "Cool Component", description = "A cool component", attributes = ["Debuggable"] }
# Explicit tables can also be used.
[components.cool_component2]
type = "I32"
name = "Cool Component 2"
description = "A cool component 2"
attributes = ["Debuggable"]
Concepts / [concepts]
The concepts
section contains custom concepts defined by the package. Concepts are used to define a set of components that can be attached to an entity. For more information on how to use concepts, see the ECS documentation.
This is a TOML table, where the keys are the concept IDs (CamelCaseIdentifier
), and the values are the concept definitions.
Property | Type | Required | Description |
---|---|---|---|
name | String | A human-readable name for the concept. | |
description | String | A human-readable description of the concept. | |
extends | String[] | An array of concepts to extend. Must be defined in this package manifest. | |
components.required | Map<ItemPath, ConceptValue> | ✅ | An object containing the required components for this concept, and any associated information about the use of the component in this concept (see below). |
components.optional | Map<ItemPath, ConceptValue> | An object containing the optional components for this concept, and any associated information about the use of the component in this concept (see below). These components do not need to be specified to satisfy a concept, but may provide additional control or information if available. |
The components
is an object where the keys are ItemPath
s of components defined in the package manifest, and the values are ConceptValue
s.
ConceptValue
s are a TOML table with the following properties:
Property | Type | Required | Description |
---|---|---|---|
description | String | A human-readable description of the component in the context of the concept, which may be different to the component’s description. | |
suggested | toml::Value | If specified, the suggested value for this component in this concept. This is merely a suggestion, but must match the type of the component.Mat4 and Quat support Identity as a string, which will use the relevant identity value for that type.F32 and F64 support PI , FRAC_PI_2 , -PI , and -FRAC_PI_2 as string values, which correspond to pi (~3.14), half-pi (~1.57), and negative versions respectively. |
Example
[concepts.Concept1]
name = "Concept 1"
description = "The best"
[concepts.Concept1.components.required]
cool_component = {}
# A concept that extends `concept1` and has both `cool_component` and `cool_component2`.
[concepts.Concept2]
extends = ["Concept1"]
[concepts.Concept2.components.required]
cool_component2 = { suggested = 42 }
[concepts.Concept2.components.optional]
cool_component3 = { suggested = 42 }
Messages / [messages]
The messages
section contains custom messages defined by the package. Messages are used to communicate between client and server, or between packages/modules on the same side.
For an example of how to use messages, see the messaging example.
This is a TOML table, where the keys are the message IDs (PascalCaseIdentifier
), and the values are the message definitions.
Property | Type | Required | Description |
---|---|---|---|
description | String | A human-readable description of the message. | |
fields | Map<SnakeCaseIdentifier, ValueType> | ✅ | An object containing the fields and their types. Must be one of the types supported for components. |
Example
[messages.Input]
description = "Describes the input state of the player."
[messages.Input.fields]
# Each field in the message must have a type.
direction = "Vec2"
mouse_delta_x = "F32"
Enums / [enums]
The enums
section contains custom enums defined by the package. Enums are used to define a closed set of values.
This is a TOML table, where the keys are the package IDs (PascalCaseIdentifier
), and the values are the package definitions.
Property | Type | Required | Description |
---|---|---|---|
description | String | A human-readable description of the enum. | |
members | Map<PascalCaseIdentifier, String> | ✅ | An object containing the members and their descriptions. The description can be empty. |
Example
[enums.CakeBakeState]
description = "Describes the state of a cake bake."
[enums.CakeBakeState.members]
GatheringIngredients = "Gathering ingredients"
MixingIngredients = "Mixing ingredients"
Baking = "Baking"
Cooling = "Cooling"
Decorating = "Decorating"
Done = "Done"
Includes / [includes]
The includes
section contains a list of manifests to pull in under a given name. This is useful for splitting up a package into multiple files.
This is a TOML table, where the keys are the name that you want to access this include by (SnakeCaseIdentifier
), and the location of the package manifest is the value.
Example
[includes]
graphics = "graphics/ambient.toml"
Dependencies / [dependencies]
The dependencies
section contains a list of package IDs that this package depends on.
Depending on another package gives you access to its items, including its components, concepts, messages, and enums. It can also provide access to any assets that the package has.
This is a TOML table, where the keys are the name that you want to access this package by (SnakeCaseIdentifier
), and the location of the package is the value.
To access an item from a package, use the following syntax: import_name::item_id
. For example, if you have a package imported with the name the_basics
and an enum with ID BasicEnum
, you can access it with the_basics::BasicEnum
.
At least one of path
or (id
and version
) must be specified.
Property | Type | Description |
---|---|---|
path | String | A relative path to the package to depend on. |
id | String | The ID of a package to depend on. Must be combined with version . |
version | String | The version of a package to depend on. Only exact versions are currently supported. Must be combined with id . |
enabled | bool | Control whether or not logic associated with this package should be enabled on load. Enabled by default. |
For an example of how to use dependencies, see the dependencies example.
Example
[dependencies]
the_basics = { path = "../basics" }
[components]
my_component = { type = "the_basics::BasicEnum" }
Runtime access to packages
Packages are represented as entities within the ECS, with their metadata being stored as components. This means that you can access the metadata of a package at runtime. To do so, you can use the entity()
function inside the generated Rust code for the package:
use ambient_api::prelude::*; #[main] fn main() { dbg!(entity::get_all_components(packages::this::entity())); }
Or by querying for entities that have the is_package
component:
use ambient_api::{ core::package::components::{is_package, name}, prelude::*, }; #[main] fn main() { let q = query((is_package(), name())).build(); // List all packages and their names. dbg!(q.evaluate()); }
ValueType
In Ambient, all typed values must have a type that belongs to ValueType
. This includes component types and message fields.
A ValueType
is either:
-
a string that can be one of the following primitive types:
Bool
: a boolean value, true or falseEmpty
: a component that has no value; most often used for tagging an entityEntityId
: an entity IDF32
: a 32-bit floating point valueF64
: a 64-bit floating point valueMat4
: a 4x4 32-bit floating point matrixQuat
: a 32-bit floating point quaternionString
: a UTF-8 stringU8
: an 8-bit unsigned integer valueU16
: an 16-bit unsigned integer valueU32
: a 32-bit unsigned integer valueU64
: a 64-bit unsigned integer valueI8
: an 8-bit signed integer valueI16
: an 16-bit signed integer valueI32
: a 32-bit signed integer valueI64
: a 64-bit signed integer valueUvec2
: a 2-element 32-bit unsigned integer vectorUvec3
: a 3-element 32-bit unsigned integer vectorUvec4
: a 4-element 32-bit unsigned integer vectorIvec2
: a 2-element 32-bit signed integer vectorIvec3
: a 3-element 32-bit signed integer vectorIvec4
: a 4-element 32-bit signed integer vectorVec2
: a 2-element 32-bit floating point vectorVec3
: a 3-element 32-bit floating point vectorVec4
: a 4-element 32-bit floating point vectorDuration
: A time span. Often used as a timestamp, in which case it designates the duration since Jan 1, 1970.
-
a contained type of the form
{ type = "Vec", element_type = ValueType }
or{ type = "Option", element_type = ValueType }
- Note that
Vec
andOption
are the only supported container types, andelement_type
must be a primitiveValueType
(that is, you cannot have nested contained types).
- Note that
-
a string that refers to an
enum
defined by a package; see Enums.
Note that ValueType
s are not themselves values, but rather types of values. For example, Vec2
is a ValueType
, but Vec2(1.0, 2.0)
is a value of type Vec2
. Additionally, ValueType
s from other packages can be referred to using ItemPath
s: my_package::my_component::MyType
.
WebAssembly
All .wasm
components in the build/{client, server}
directory will be loaded for the given network side. The .wasm
filenames must be snake-case ASCII identifiers, like the id
in the manifest.
This means any .wasm
which implements the Ambient WIT interface and targets WASI snapshot 2 (or uses an adapter that targets WASI snapshot 2) should run within Ambient.
As a convenience for Rust users, Ambient will automatically build a Cargo.toml
if present at the root of your package, as wasm32-wasi
for the features specified in build.rust.feature-multibuild
in ambient.toml
(defaults to client
and server
).
The default new package template will create client.rs
and server.rs
files, with a Cargo.toml
preconfigured with targets for both. The resulting WASM bytecode files are then converted to a component and placed in build/{client, server}
.
The process it takes is equivalent to these commands:
cd your_package
cargo build --target wasm32-wasi --features client
wasm-tools component new target/wasm32-wasi/debug/your_package_client.wasm -o build/client/your_package.wasm --adapt wasi_snapshot_preview1.command.wasm
cargo build --target wasm32-wasi --features server
wasm-tools component new target/wasm32-wasi/debug/your_package_server.wasm -o build/server/your_package.wasm --adapt wasi_snapshot_preview1.command.wasm
using wasm-tools and a bundled version of the preview1-to-preview2 WASI adapter.
Rust
Rust is a first-class language for Ambient packages. The default new package template will create client.rs
and server.rs
files, with a Cargo.toml
preconfigured with targets for both.
The API provides a #[main]
attribute macro that generates code to allow you to access the data and functionality of the packages known to your package. All packages, including your own, will be in the packages
module.
Restrictions
When running locally, guest code can:
- use WASI filesystem APIs (e.g.
std::fs
in Rust) to read and write files in thedata
directory of the built package - use the Ambient HTTP APIs (e.g.
http
in Rust) to make HTTP GET/POST requests to arbitrary servers
This functionality is disabled when the server is running on a hosted environment (i.e. Ambient deployments) for security reasons. To test if your logic still works in a hosted environment, run Ambient with the AMBIENT_HOSTED
environment variable set to anything (e.g. AMBIENT_HOSTED=1 ambient run
).