|
Toast Engine
|
Authors: Xein
Created on: 18 Jun 2026
The World is the singleton that owns, ticks, and manages every Node that lives in the engine. It is the only object that holds nodes in memory and the only one allowed to create or destroy them.
Every entity in the engine is a Node. Nodes form a tree: each node has one parent and any number of children. The root of the active tree is called the world root. Nodes outside the tree but still resident in memory are either cached (sleeping) or global (always awake, outside the main tree).
| State | Meaning |
|---|---|
| null | default; node has been allocated but not placed anywhere |
| loading | asset is being loaded asynchronously |
| cached | resident in memory, disabled, not in any active tree |
| root | part of the active world root tree |
| global | active but outside the main tree (HUD, singletons, etc.) |
| destroy | queued for destruction; drained at the start of the next tick |
| Type | Meaning |
|---|---|
| null | not yet assigned |
| child | regular node inside a tree |
| root | root of a cached or global tree |
| world_root | the one root node currently active in the world |
setRoot swaps the world root in one atomic step so there is no frame where the world is empty (unless you cache the root yourself).
find and search accept a /-separated path or a bare name, optionally behind a node:// prefix. Bare names match the first descendant with that name. Traversal stops at nodes that are inside a prefab instance (they are opaque); search crosses those boundaries instead. The first segment may be a scope keyword:
String queries match names only. To look up a node by UID use the typed overload:
It resolves within the caller's local root (the nearest instance-root ancestor), so UIDs only need to be unique inside one prefab instance. There is no UID form of search.
By default nodes tick in an unspecified order. If you need node A to always tick before node B, register a dependency:
The World rebuilds the tick schedule whenever the dependency graph changes. Cycles are legal; the nodes in a cycle are bundled into a NodeCluster and ticked sequentially in declaration order within the cluster.
Every frame the world runs four tick phases in order:
Within each phase the scheduler groups nodes into waves. All nodes in one wave run in parallel on the thread pool. The next wave starts only after the previous one finishes. Wave indices are baked into Node::m_wave at schedule-build time so dispatch is O(1).
The wave assignment algorithm:
The thread that runs the frame hands each wave to the pool and waits for it; it only works on the wave itself when the pool cannot take it. Nodes with scripts are grouped by the Lua interpreter they run on, one job per interpreter, so no worker waits for another, see scripting.
init, begin and onEnable run on a whole tree when a PlayWorkspace or a World brings it up. A node always comes after its parent. A tree with many scripts runs a level at a time: the C++ functions of the level one after the other on the calling thread, then the scripts of the level in parallel on the pool. Nothing else is promised about the order of two nodes that are not above one another. Smaller trees keep the order where each node is followed by everything below it. TickScheduler::runLifecycle() does this, and TOAST_SERIAL_LIFECYCLE=1 turns the parallel version off.
Workspace is a lightweight version of World used by the editor viewport. It owns nodes the same way but never ticks them and never builds a dependency graph. Use it whenever you need to display or edit a node tree without running game logic.