# @ferret/levels: API

Level manifests, the level registry, load, unload and travel, environment and the `.ferret` package format.

156 exports. Generated from the source (`packages/levels/src`), so it matches the code.

## Classes

### AssetLibrary

*class* · `packages/levels/src/library.ts:100`

```ts
class AssetLibrary implements Service
```

- `readonly name: "asset-library"`
- `constructor(private readonly o: AssetLibraryOptions)`
- `init(world: World): void`
- `async open(url?: string): Promise<Catalog>` — Open the catalog (once; again to refresh).
- `list(q: LibraryQuery = {}): LibraryEntry[]` — Items of the catalog, filtered; no package is opened.
- `has(id: string): boolean`
- `entry(id: string): LibraryEntry`
- `previewUrl(id: string): string | undefined` — Absolute URL of an item's preview image (from the catalog, nothing is opened).
- `async pkg(pkgId: string): Promise<FerretPackage>` — The package of the catalog, open in the mount (with its dependencies).
- `async item<T extends LibraryItem = LibraryItem>(id: string): Promise<T>` — The item file, read from its package.
- `async url(ref: string): Promise<string>` — A URL the engine's loaders can read for a `ferret://<package>/<path>` reference (a clip set's file, a map).
- `async texture(id: string, o: { scene?: BABYLON.Scene } = {}): Promise<Partial<Record<'basecolor' | 'normal' | 'orm' | 'emissive', BABYLON.Texture>>>` — The maps of a texture item as Babylon textures (colour spaces set: base colour and emissive are sRGB, the others linear).
- `async material(id: string, o: MaterialOptions = {}): Promise<BABYLON.PBRMaterial>` — A PBR material from a material item (and a variant of it): the description maps one to one to `PBRMaterial`.
- `async clipsOf(characterId: string, o: { compatible?: boolean } = {}): Promise<ClipRef[]>` — The clip sets a character names (and the clip sets of its skeleton in the catalog), with the reference of each file.
- `async bindClips(c: LibraryCharacter, o: BindOptions = {}): Promise<BoundClips>` — Put the clip sets of a character on its bones (matched by bone name; a clip set is loaded once however many bodies use it). Waits for the model. `groups` are one AnimationGroup per clip name, ready for an `Animator` or `group.start()`.
- `async character(id: string, o: CharacterOptions = {}): Promise<LibraryCharacter>` — A character in the world at once (a placeholder until the model is in).
- `async model(id: string, o: ModelPlacement = {}): Promise<LibraryModel>` — A static model of the library in the world at once (a placeholder of its bounds until the file is in).
- `async attach(character: LibraryCharacter, partId: string, o: AttachOptions = {}): Promise<LibraryCharacter | null>` — Hang a part (a weapon, a hat, a rider) on a body that is in, by a socket of the body's item (`sockets`: a bone and an offset) or by a bone and an offset of your own. The part follows the bone through every clip. Resolves with the part (dispose it to take it off), or null when the body or the bone is not found. Offsets are in the glTF's own space: metres of the bone's local frame and a quaternion.
- `async preload(id: string, tier?: LibraryTier): Promise<void>` — Bring an item's files to the device now (a model's file, a texture's maps), so a later call does not wait.
- `release(): void` — Free what the library made: materials, textures, characters. Packages stay open (they cost the mount nothing but the index).
- `dispose(): void`

### CatalogClient

*class* · `packages/levels/src/catalog.ts:31`

```ts
class CatalogClient
```

- `readonly registrations: CatalogRegistration[]` — every catalog registered through this client, in order
- `constructor(private readonly levels: LevelService, private readonly mount: PackageMount, private readonly store: AssetStore, private readonly defaults: CatalogOptions = {})`
- `async register(url: string, o: CatalogOptions = {}): Promise<CatalogRegistration>`

### CodeLoader

*class* · `packages/levels/src/code.ts:64`

```ts
class CodeLoader
```

- `constructor(readonly world: World, readonly mount: PackageMount, private o: CodeLoaderOptions = {})`
- `list(): LoadedCode[]` — The code loaded so far, in load order.
- `exports(id: string): LibraryModule | null | undefined` — The exports of a loaded package's code, or undefined.
- `require(id: string, range = '*'): Promise<LoadedCode>` — Load the code of a package (and of its dependencies first) and run its `install` once. Resolves at once when it is loaded; concurrent calls share one load. Rejects with the reason when the package may not run code or something is missing.
- `dispose(): void` — Call the `dispose` that `install` returned, newest first (the modules themselves stay: a page cannot unload them).

### CollisionChunks

*class* · `packages/levels/src/collision-chunks.ts:31`

```ts
class CollisionChunks
```

Keeps the chunks round the player resident. `update` is cheap (a few dozen boxes); fetching and instancing happen in the background.

- `constructor(private def: CollisionChunksDef, private assets: PackageAssets, private at: (file: string) => string, private name: string, private places: () => [number, number, number][], private onError: (message: string) => void = () => {})`
- `async ready(): Promise<void>` — Start the chunks near `places` and wait for them (the level is not playable before the ground round the spawn is solid).
- `update(): void`
- `get loaded(): number`
- `dispose(): void`

### LevelService

*class* · `packages/levels/src/levels.ts:108`

```ts
class LevelService implements Service
```

The level registry and travel between levels. `travel(id)` shows the loading screen (events), removes the old level with
everything it made, builds or loads the new one, moves the persistent entities (the player) to the spawn point and announces
the new level. Events: `level:loading` { id, progress }, `level:ready` { id, report }, `level:unloaded` { id }, `level:failed` { id, error }.
`state` is the game's data that survives travel and goes to `Platform.state` with `saveData`.

- `readonly name: "levels"`
- `state: Record<string, unknown>` — persistent state across levels: inventory, flags, health ... plain JSON
- `readonly spawns: Map<string, SpawnPoint>`
- `readonly transitions: TransitionRegistry` — the game's transitions: register them by name and choose which one a travel uses
- `current: LevelDef | null`
- `travelling: boolean`
- `report: LevelReport`
- `animGraphs: Record<string, any>` — the animation graphs of the current level package (its manifest's `animGraphs`), by name
- `movement: Record<string, number>` — the player character values of the current level package (its manifest's `movement`), over the engine's defaults
- `libraries?: (manifest: import('./manifest').LevelManifest, levelId: string) => Promise<void>` — plugged in by `installLevelLibraries` (./libraries): require the libraries a level's manifest needs before the level is built
- `packageLoader?: (ctx: LevelContext, level: LevelDef, report: LevelReport) => Promise<void>`
- `readonly persistent: Set<Entity>` — entities that always travel with the player (the player itself): they are placed at the spawn, never removed
- `constructor(readonly opts: LevelOptions = {})`
- `init(world: World)`
- `register(def: LevelDef): this`
- `unregister(id: string): boolean` — Remove a level from the registry (a catalog that no longer lists it). The level being played is not touched.
- `replace(def: LevelDef): this` — Register, or replace a level of the same id that is not the one being travelled to (a catalog update).
- `catalogs?: { register(url: string, o?: unknown): Promise<unknown> }` — Plugged in by `installCatalogs` (./catalog): the remote levels of a catalog.
- `registerCatalog(url: string, o?: unknown): Promise<unknown>` — Fetch a catalog and register its levels; needs `installCatalogs`.
- `has(id: string): boolean`
- `get(id: string): LevelDef`
- `list(): LevelDef[]`
- `exempt(e: Entity): this` — An entity that is not part of any level though it appears while one loads (another player's avatar): it is not removed with the level.
- `keep(e: Entity): this` — Entities that go with the player to every level.
- `async travel(id: string, opts: TravelOptions = {}): Promise<LevelReport>`
- `spawn(id?: string): SpawnPoint | null` — The spawn point with that id, or the level's default one.
- `place(e: Entity, s: SpawnPoint)` — Put an entity at a spawn point (any entity with a CharacterMovement is teleported properly).
- `hold<T>(work: Promise<T>, label = 'artefact'): Promise<T>` — An artefact the level is not ready without. An entity of the level calls this while the level is built (a behaviour's `onAttach`) with the promise of what it still loads (a character's model, its clips). `level:ready` is announced when the last of them has settled, or after `holdMs`; a rejected one is a warning of the report. Outside a travel the hold is ignored. Returns the promise.
- `get holding(): number` — how many holds of the level being built are not taken up yet or still open
- `unload()`
- `onUnload(fn: () => void)` — Run `fn` when the current level is removed (release assets, clear a service).
- `saveData(): { level: string | null; state: Record<string, unknown> }` — What to hand to `Platform.saveState`: the game's state and where the player is.
- `loadData(data: { level?: string | null; state?: Record<string, unknown> } | null | undefined): string | null` — Restore what `saveData` made; returns the level to travel to (or null).
- `dispose()`

### LightBudget

*class* · `packages/levels/src/light-budget.ts:130`

```ts
class LightBudget
```

- `readonly def: LightBudgetDef`
- `onChange?: (entered: number[], left: number[]) => void` — called after a re-ranking when real lights changed hands: indices that entered and left the pool
- `onSounds?: (start: number[], stop: number[]) => void` — called when the nearest lamps with a sound change: indices to start and to stop
- `constructor(readonly scene: BABYLON.Scene, def: LightBudgetDef, readonly opts: LightBudgetOptions = {})`
- `get name()`
- `get size()`
- `get real(): number[]` — the lights that are real now (fading ones included)
- `get glowMesh()`
- `glowSize(i: number): number` — the size in metres of lamp `i`'s glow dot now (0: hidden, because it is a real light or out of reach)
- `get lights(): (BABYLON.PointLight | BABYLON.SpotLight)[]`
- `stats(): LightBudgetStats`
- `setBudget(n: number)` — change the budget while running (a quality switch): the pool grows or shrinks, and the next re-ranking fills it
- `update(dt: number)` — one step of the budget: `dt` seconds since the last. It runs by itself every frame; tests call it with a clock of their own
- `dispose()`

### PackageMount

*class* · `packages/levels/src/ferret.ts:35`

```ts
class PackageMount
```

Connects packages to a LevelService. `add(pkg)` registers the package's levels (as `<id>` for the first package, `<pkgid>/<id>`
for the others, or with the `prefix` given); `url(pkg, path)` gives a blob URL for any file of a package, for sounds, textures
and video. Nothing is unpacked: a file is read from the package when a level asks for it, and a model's blob URL goes when the
level that held it is gone.

- `readonly packages: PackageSet`
- `readonly assets: MountAssets` — give this to the level loader instead of the asset cache (`installPackages` does)
- `readonly loader: PackageLoaderOptions` — the options the level loader runs with: hand this to `installEditor` as its `loader` so the editor's draft shows in reloads
- `constructor(readonly levels: LevelService, private inner: MountAssets, readonly opts: MountOptions = {})`
- `add(pkg: FerretPackage, o: { prefix?: string } = {}): LevelDef[]` — Open a package into the mount and register its levels. Returns what was registered.
- `startLevel(pkg: FerretPackage): string | null` — The level a package starts in (`entry.level` of a game, else its first), as registered.
- `async url(pkg: FerretPackage, path: string): Promise<string>` — A blob URL for a file of a package (`path` inside `pkg`, or `ferret://other/path`). Stays valid until `dispose`.
- `async fetch(url: string): Promise<FetchResult>` — The loader's `fetch` for package urls; anything else goes to the real one.
- `forget(pkgId: string): void` — Drop everything cached for a package (its blob URLs): it was replaced by another version.
- `dispose(): void`

### PrefetchService

*class* · `packages/levels/src/prefetch.ts:30`

```ts
class PrefetchService implements Service
```

- `readonly name: "prefetch"`
- `constructor(private readonly levels: LevelService, o: PrefetchOptions = {})`
- `init(world: World): void`
- `near(zone: string, levelId: string, o: { inSeconds?: number } = {}): () => void` — Doors and zones: entering `zone` makes `levelId` wanted (the player is probably about to go there).
- `hint(id: string, o: { inSeconds?: number; from?: 'level' | 'game' } = {}): void` — `id` is wanted. With `inSeconds` (the player will need it in about that long) the start is put off as long as the estimate allows it to still finish in time; without it the download starts at once (when no travel is running).
- `focus(ids: string[]): void` — Only these are wanted from now on: every other background download stops at its next file.
- `cancel(id: string): void`
- `get bandwidth(): number` — The smoothed link speed in bytes per second, 0 when nothing was measured.
- `state(id: string): JobState | 'none'`
- `async idle(id: string): Promise<void>` — Resolves when the level's background download has finished (done, cancelled or failed).
- `status(): { rate: number; jobs: Record<string, { state: JobState; wanted: boolean; bytes: number }> }`
- `dispose(): void`

### ProbeVolume

*class* · `packages/levels/src/probe-volume.ts:88`

```ts
class ProbeVolume
```

The baked grid on the GPU and the plugin on every PBR material of the scene (also those added later).

- `readonly textures: BABYLON.RawTexture3D[]`
- `enabled: boolean`
- `constructor(readonly scene: BABYLON.Scene, readonly def: ProbeVolumeDef, data: ArrayBuffer)`
- `setEnabled(on: boolean)` — Switch the baked light on or off (the materials recompile).
- `dispose()`

### RuntimeAssets

*class* · `packages/levels/src/runtime.ts:54`

```ts
class RuntimeAssets implements Service
```

- `readonly name: "runtime-assets"`
- `constructor(private readonly o: RuntimeAssetsOptions)`
- `init(world: World): void`
- `async bytes(ref: string, o: Pick<RequireOptions, 'priority' | 'signal' | 'sha256' | 'size' | 'pkg'> = {}): Promise<Uint8Array>` — The verified bytes of any reference.
- `async url(ref: string, o: Pick<RequireOptions, 'priority' | 'signal' | 'sha256' | 'size' | 'pkg'> = {}): Promise<string>` — A URL the engine's loaders (Audio, Texture, video) can read, for any reference.
- `status(ref: string): string`
- `model(ref: string, o: ModelOptions = {}): ModelHandle` — Ask for a model. Returns at once with the placeholder in place.
- `get scene(): BABYLON.Scene`
- `get events()`
- `get models()`
- `forget(h: ModelHandleImpl): void`
- `async modelUrl(ref: string, o: ModelOptions): Promise<string>`
- `dispose(): void`

### StreamingDebugView

*class* · `packages/levels/src/streaming-debug.ts:12`

```ts
class StreamingDebugView
```

- `constructor(private readonly world: World, private readonly sv: StreamingService, private readonly block: () => { cells: Record<string, { bounds: number[] }> } | null)`
- `get visible(): boolean`
- `show(): void`
- `hide(): void`

### StreamingService

*class* · `packages/levels/src/streaming.ts:54`

```ts
class StreamingService implements Service
```

- `readonly name: "streaming"`
- `tuning: { radiusScale: number; budgetScale: number; memoryScale: number; }` — Runtime scales on top of the options
- `init(world: World): void`
- `get active(): boolean`
- `blockDef(): StreamingBlock | null` — the block being streamed (for the debug view and tools)
- `begin(block: StreamingBlock, source: CellSource, o: StreamingOptions): void` — Start streaming a level. The cells within `loadRadius` of the focus are what `ready()` waits for.
- `end(): void` — Stop streaming and release everything this service added. Called when the level is unloaded.
- `memory(): { resident: number; cap: number | null; pinned: number }` — Bytes the resident cells take against the cap (pinned ones are counted and never evicted).
- `gate(): string[]` — The cells that must be in before the level is playable: those within `loadRadius` of the focus.
- `ready(): Promise<void>` — Resolves when every cell of the gate is loaded (or has failed: a missing far cell must not hold the player).
- `update(_dt = 0): void` — Called once per rendered frame.
- `async loadBundle(id: string): Promise<void>` — Load a bundle (the files an interior needs) now; resolves when it is in. Loading it twice is one load.
- `bundleState(id: string): State | undefined`
- `bundleRoots(id: string): BABYLON.TransformNode[]`
- `hasCell(id: string): boolean`
- `cellRoot(id: string): BABYLON.TransformNode | null` — the root of a resident cell's instance (what an entity that `needs` the cell looks its node up in), else null
- `cellState(id: string): State | undefined`
- `snapshot(): { cells: Record<string, State>; bundles: Record<string, State> }`
- `dispose(): void`

### TransitionRegistry

*class* · `packages/levels/src/transitions.ts:54`

```ts
class TransitionRegistry
```

- `register(name: string, t: Transition | (() => Transition)): this` — Register a transition by name: one object, or a function that makes a fresh one per travel. Registering `default` sets what is used when nothing else is chosen.
- `has(name: string): boolean`
- `names(): string[]`
- `choose(fn: TransitionChooser): this` — `(from, to, plan) => name`: decides per travel (a theme in `to.tags`, a short wait in `plan.cached`).
- `pick(from: LevelDef | null, to: LevelDef, plan: LevelPlan, named?: string): string` — The name to use: the one the call named, else the chooser's, else `default`, else the bar. A name that is not registered falls back to the bar.
- `create(name: string): Transition`

### TransitionRun

*class* · `packages/levels/src/transitions.ts:90`

```ts
class TransitionRun
```

A transition with the guarantees of 5.1 around it: `enter` has 2 s to be on screen; a throw anywhere is logged and the bar takes
over; a broken transition never blocks the travel. After a fallback every further call is a no-op and `name` reads `bar`.

- `name: string`
- `fellBack: string | null` — set when the game's transition failed and the engine's bar replaced it
- `constructor(readonly wanted: string, private readonly t: Transition, private readonly ctx: TransitionContext, private readonly o: RunOptions = {})`
- `async enter(): Promise<void>`
- `update(p: TransitionProgress): void`
- `async ready(): Promise<void>` — Resolves when the game lets the player in. A cancel does not hold the player in a transition that waits for a button.
- `async fail(err: StreamError): Promise<boolean>` — True when the transition showed the failure itself.
- `dispose(): void`

### TravelAborted

*class* · `packages/levels/src/levels.ts:60`

```ts
class TravelAborted extends Error
```

The player (or the menu) cancelled the travel.

- `constructor(readonly id: string)`

## Functions

### applyEnvironment

*function* · `packages/levels/src/environment.ts:203`

```ts
function applyEnvironment(scene: BABYLON.Scene, def: EnvironmentDef, name = 'env', o: { /** the game's shared clock in seconds (the world's time): waves are drawn from it, so every peer sees the same sea at the same moment */ clock?: () => number } = {}): Environment
```

Build the environment in a scene. Dispose it when the level unloads.

### applyLandscape

*function* · `packages/levels/src/landscape.ts:349`

```ts
async function applyLandscape(scene: BABYLON.Scene, def: LandscapeDef, o: ApplyLandscapeOptions): Promise<LandscapeView>
```

Put a landscape's layer blend on its mesh's material, mark a far plane, and bake the virtual texture and hand it to the props.
A problem (a texture that does not load, a mesh that is not there) is a warning in the view, never a thrown error: the level still loads with the baked look.

### atmosphereColours

*function* · `packages/levels/src/atmosphere.ts:189`

```ts
function atmosphereColours(t: AtmosphereTable, sunScale = 1, sunColor: Vec3 = [1, 1, 1]): { top: Vec3; horizon: Vec3; ground: Vec3 }
```

colours of the sky that the rest of the environment uses (water, reflection, fog): the zenith, the horizon away from the sun, the ground

### blendWeights

*function* · `packages/levels/src/landscape.ts:107`

```ts
function blendWeights(weights: number[], heights: number[] = [], heightBlend: number[] = []): number[]
```

Weights of the layers at a point -> the weights the material uses: normalised to sum 1 (no paint at all = the first layer), then height blended.

### bloomWeight

*function* · `packages/levels/src/environment.ts:104`

```ts
function bloomWeight(intensity: number): number
```

### buildAtmosphereTable

*function* · `packages/levels/src/atmosphere.ts:117`

```ts
function buildAtmosphereTable(def: AtmosphereDef | undefined, sunElevation: number, n = LUT_SIZE): AtmosphereTable
```

Fill the table for the sun at `sunElevation` radians above the observer's horizon.

### buildWater

*function* · `packages/levels/src/water.ts:145`

```ts
function buildWater(scene: BABYLON.Scene, def: WaterDef, o: { name: string; skyTop: Vec3; skyHorizon: Vec3; sunDir: Vec3; sunColor: Vec3; /** the game's shared clock in seconds: with it every peer draws the same waves at the same moment (default: this surface's own time since it was built) */ clock?: () => number }): WaterSurfaceView
```

Build one water surface in a scene. `sky` and `sun` come from the rest of the environment.

### cellDistance

*function* · `packages/levels/src/streaming.ts:49`

```ts
function cellDistance(b: StreamCellDef['bounds'], x: number, z: number): number
```

Distance on the ground plane from a point to a cell's box (0 inside it).

### deepColour

*function* · `packages/levels/src/water.ts:62`

```ts
function deepColour(absorption: Vec3 = [110, 158, 350], scattering: Vec3 = [0.05, 0.05, 0.05]): Vec3
```

The deep colour of the water from Unreal's absorption distances and scattering (linear RGB).

### defaultLibraryUrl

*function* · `packages/levels/src/library.ts:94`

```ts
function defaultLibraryUrl(): string
```

### distanceToBounds

*function* · `packages/levels/src/collision-chunks.ts:13`

```ts
function distanceToBounds(b: CollisionChunkDef['bounds'], x: number, y: number, z: number): number
```

Distance from a point to a box (0 inside).

### effectiveFogDensity

*function* · `packages/levels/src/height-fog.ts:39`

```ts
function effectiveFogDensity(f: HeightFogDef, h: number): number
```

the flat density Babylon's exponential fog takes for a view at height `h`: the density of the air there (a ray along the ground is what a walker sees most)

### environmentBabylonParts

*function* · `packages/levels/src/environment.ts:97`

```ts
function environmentBabylonParts(env?: EnvironmentDef): string[]
```

The Babylon parts a level's environment needs beyond the core: the post-processing pipeline for bloom, particles for fire.

### flickerFactor

*function* · `packages/levels/src/light-budget.ts:104`

```ts
function flickerFactor(l: Pick<BudgetLight, 'flicker'>, phase: number, t: number): number
```

the multiplier of a light's intensity at time `t` seconds

### flickerValue

*function* · `packages/levels/src/light-budget.ts:99`

```ts
function flickerValue(phase: number, t: number): number
```

a smooth repeatable noise in 0..1: two sines of unrelated rate, per light phase (no random state, so tests and replays agree)

### fogDensityAt

*function* · `packages/levels/src/height-fog.ts:21`

```ts
function fogDensityAt(f: HeightFogDef, h: number): number
```

extinction per metre at height `h`

### fogOpticalDepth

*function* · `packages/levels/src/height-fog.ts:29`

```ts
function fogOpticalDepth(f: HeightFogDef, h0: number, dirY: number, dist: number): number
```

Optical depth of a straight ray from a point at height `h0` going `dist` metres along a direction with vertical component `dirY` (-1..1):
the integral of the density, exact for each layer. The transmittance is exp(-depth).

### gradientFaces

*function* · `packages/levels/src/environment.ts:154`

```ts
function gradientFaces(top: Vec3, horizon: Vec3, ground: Vec3, n = 8): Uint8Array[]
```

A cube map of the sky's gradient (ground below the horizon, top above): every face a few texels, smooth at every mip level, so any roughness reads it.

### heightRaster

*function* · `packages/levels/src/landscape.ts:127`

```ts
function heightRaster(positions: ArrayLike<number>, rect: [number, number, number, number], w: number, h: number): Float32Array
```

Spread vertices (x, y, z; z is the plane's second axis) over a w x h raster of the rectangle: the height of the nearest vertex per texel, holes filled.

### installCatalogCommands

*function* · `packages/levels/src/catalog.ts:161`

```ts
function installCatalogCommands(world: import('@ferret/core').World, levels: LevelService, client: CatalogClient): void
```

The catalog commands: the viewer and an agent open a catalog, list its levels with their state on the device,
refresh it and travel into a level. `catalog.levels {check: true}` opens each level's package (a few ranged reads) to say whether
its core set is `cached` or `missing` and how many bytes a visit would download; without `check` the state is `unknown`.

### installCatalogs

*function* · `packages/levels/src/catalog.ts:150`

```ts
function installCatalogs(levels: LevelService, mount: PackageMount, store: AssetStore, defaults: CatalogOptions = {}): CatalogClient
```

Plug catalog support into a LevelService: afterwards `levels.registerCatalog(url)` works.

### installCode

*function* · `packages/levels/src/code.ts:171`

```ts
function installCode(world: World, mount: PackageMount, o: CodeLoaderOptions = {}): CodeLoader
```

Let a mount run the code of its packages.

### installCodeCommands

*function* · `packages/levels/src/commands.ts:120`

```ts
function installCodeCommands(world: World, code: CodeLoader): void
```

`pkg.require` and `pkg.loaded`: run the code of an open package (and of its dependencies) through a CodeLoader, and list what runs.

### installLevelLibraries

*function* · `packages/levels/src/libraries.ts:62`

```ts
function installLevelLibraries(levels: LevelService, code: CodeLoader): void
```

Make `travel` require the libraries of every package level before it is built. The ones the manifest names are found through the
loader's `open` hook (the catalog) when they are not open yet; the ones only the open libraries' `provides` imply are already open.
Emits `level:libraries` { level, libraries } with what was required.

### installPackageCommands

*function* · `packages/levels/src/commands.ts:32`

```ts
function installPackageCommands(world: World, mount: PackageMount, hooks: PackageCommandHooks = {}): void
```

The `pkg.*` commands (PLAN.md 4.9), by hand and through MCP alike, over a PackageMount:
pkg.open, pkg.unlock, pkg.level, pkg.list, pkg.info, pkg.validate, pkg.deps, pkg.verify, pkg.code, pkg.pack, pkg.save, pkg.keys,
pkg.catalog and pkg.publish.
`installCodeCommands` adds pkg.require and pkg.loaded (running a package's code).

### installPackageLoader

*function* · `packages/levels/src/package.ts:37`

```ts
function installPackageLoader(levels: LevelService, o: PackageLoaderOptions): void
```

Teach a LevelService to load level packages: a folder with `manifest.json`, `scene.glb` and optionally `navmesh.bin`.
Problems with the wiring (a node the manifest names is missing, a behaviour is not registered) do not stop the level:
they are collected in the report so the game still runs and the artist sees what to fix.

### installPackages

*function* · `packages/levels/src/ferret.ts:154`

```ts
function installPackages(levels: LevelService, assets: MountAssets, o: MountOptions = {}): PackageMount
```

Set a LevelService up to play packages.

### installStreamCommands

*function* · `packages/levels/src/runtime.ts:204`

```ts
function installStreamCommands(world: World, store: AssetStore): void
```

`stream.status` and `stream.cache`: what the store is doing and what it holds on the device.

### installStreamingDebug

*function* · `packages/levels/src/streaming-debug.ts:56`

```ts
function installStreamingDebug(world: World, sv: StreamingService): StreamingDebugView
```

`stream.debug {on?}`: show or hide the cell boxes of the streamed level; returns whether they are shown.

### installTravelCommands

*function* · `packages/levels/src/transitions.ts:195`

```ts
function installTravelCommands(world: World, registry: TransitionRegistry): void
```

The `travel.*` commands: `travel.preview {name, seconds?, bytes?, fail?, cached?}` and `travel.transitions`.

### layerBlendGlsl

*function* · `packages/levels/src/landscape.ts:154`

```ts
function layerBlendGlsl(layers: LandscapeLayerDef[], pos: string, tex = 'texture', linear = 'toLinearSpace'): string
```

The layer blend as GLSL statements. Needs in scope: `vec2 <pos>` (metres in the landscape's coordinates), uniform `vec4 ferretLsRect` (minX, minZ, 1/sizeX, 1/sizeZ),
samplers ferretLsW<i>, ferretLsA<k>, ferretLsN<k>. Leaves `vec3 lsColour` (linear) and, with normal maps, `vec3 lsNormal` (tangent space) set.
`tex` is the sampling function (texture in GLSL ES 3, texture2D in the procedural texture's shader), `linear` converts sRGB to linear.

### levelLibraries

*function* · `packages/levels/src/libraries.ts:41`

```ts
function levelLibraries(manifest: LevelManifest, providers: Iterable<Provider>): Record<string, string>
```

Which libraries a level needs: those its manifest names, and every one of `providers` whose `provides` the level uses (a level key
with content, a capability it switches on, an actuator or rotor kind, a flow source). Id -> version range, sorted by id.

### lutElevation

*function* · `packages/levels/src/atmosphere.ts:79`

```ts
function lutElevation(i: number, n = LUT_SIZE): number
```

the view elevation (radians above the horizon) of table entry `i`: dense near the horizon, where the sky changes fastest

### miePhase

*function* · `packages/levels/src/atmosphere.ts:82`

```ts
function miePhase(c: number, g: number): number
```

### pickTier

*function* · `packages/levels/src/library.ts:88`

```ts
function pickTier<T>(have: Partial<Record<LibraryTier, T>>, want: LibraryTier = 'desktop'): { tier: LibraryTier; value: T } | null
```

The first tier at or below `want` that the item has (a low-end device asks for `low` and gets `low`, never `desktop`).

### planCollisionChunks

*function* · `packages/levels/src/collision-chunks.ts:19`

```ts
function planCollisionChunks(def: CollisionChunksDef, at: [number, number, number][], resident: ReadonlySet<number>): { load: number[]; drop: number[] }
```

Which chunks to load and which to drop for the places in `at` (metres), given the ones resident. A chunk stays while it is within `unloadRadius` of any place; it comes when within `loadRadius`.

### previewTransition

*function* · `packages/levels/src/transitions.ts:162`

```ts
async function previewTransition(world: World, registry: TransitionRegistry, name: string, o: PreviewOptions = {}): Promise<{ name: string; fellBack: string | null; updates: number; ended: 'ready' | 'failed' }>
```

Run a registered transition against a fake travel, without travelling: the author sees it in the viewer and tests it in
Playwright. Returns what the transition was told.

### rankNearest

*function* · `packages/levels/src/light-budget.ts:85`

```ts
function rankNearest(items: readonly { position: Vec3; priority?: number }[], at: Vec3, n: number, opts: { held?: ReadonlySet<number>; hysteresis?: number; maxDistance?: number; filter?: (i: number) => boolean } = {}): number[]
```

The indices of the `n` lights that matter most to a viewer at `at`: the smallest distance / priority, within `maxDistance`. Lights in `held`
(the ones that are real now) count as nearer by `hysteresis`, so a newcomer has to be clearly nearer to take over.

### rayleighPhase

*function* · `packages/levels/src/atmosphere.ts:81`

```ts
function rayleighPhase(c: number): number
```

### refKind

*function* · `packages/levels/src/runtime.ts:16`

```ts
function refKind(ref: string): RefKind
```

Which scheme a reference uses.

### resolveAtmosphere

*function* · `packages/levels/src/atmosphere.ts:59`

```ts
function resolveAtmosphere(a: AtmosphereDef = {}): ResolvedAtmosphere
```

### skyRadiance

*function* · `packages/levels/src/atmosphere.ts:174`

```ts
function skyRadiance(t: AtmosphereTable, elevation: number, cosSun: number, sunScale = 1, sunColor: Vec3 = [1, 1, 1]): Vec3
```

Radiance of the sky in a direction, in the picture's units for a sun of unit colour and strength: `elevation` of the view above the horizon,
`cosSun` the cosine of the angle between the view and the sun. The dome's shader does the same on the GPU.

### spawnFromManifest

*function* · `packages/levels/src/levels.ts:429`

```ts
function spawnFromManifest(s: ManifestSpawn): { id: string; position: [number, number, number]; yaw?: number }
```

### validateAtmosphere

*function* · `packages/levels/src/atmosphere.ts:63`

```ts
function validateAtmosphere(a: unknown, at = 'environment.skyAtmosphere'): { path: string; message: string }[]
```

### validateEnvironment

*function* · `packages/levels/src/environment.ts:65`

```ts
function validateEnvironment(e: unknown, at = 'environment'): ManifestIssue[]
```

### validateLandscapes

*function* · `packages/levels/src/landscape.ts:67`

```ts
function validateLandscapes(l: unknown, at: string): { path: string; message: string }[]
```

### validateLightBudget

*function* · `packages/levels/src/light-budget.ts:57`

```ts
function validateLightBudget(d: unknown, at = 'environment.lights'): ManifestIssue[]
```

### validateManifest

*function* · `packages/levels/src/manifest.ts:111`

```ts
function validateManifest(m: unknown): ManifestIssue[]
```

Check a manifest against the schema. Returns the problems; an empty list means the manifest is usable.

### validatePackage

*function* · `packages/levels/src/ferret.ts:185`

```ts
async function validatePackage(pkg: FerretPackage, o: ValidateOptions = {}): Promise<PackageIssue[]>
```

`pkg.validate`: the package's own checks (manifest, files, sha256, no code in kinds without code) and then every level: its
manifest against the schema, and that the scene, collision mesh and navmesh it names exist — in the package or, through
`ferret://`, in a declared dependency.

### validateProbes

*function* · `packages/levels/src/probe-volume.ts:23`

```ts
function validateProbes(p: unknown, at = 'environment.probes'): { path: string; message: string }[]
```

### validateWater

*function* · `packages/levels/src/water.ts:46`

```ts
function validateWater(w: unknown, at: string): { path: string; message: string }[]
```

### virtualTextureShare

*function* · `packages/levels/src/landscape.ts:121`

```ts
function virtualTextureShare(above: number, blendHeight: number, strength = 1): number
```

The colour a prop takes at height `above` metres over the ground: the share of the ground colour (virtual texture) it keeps.

### waveHeight

*function* · `packages/levels/src/water.ts:69`

```ts
function waveHeight(waves: WaterWave[], x: number, z: number, t: number): number
```

Height of the waves at (x, z) and time t: the sum of Gerstner waves' vertical part, metres above the level.

## Interfaces

### ApplyLandscapeOptions

*interface* · `packages/levels/src/landscape.ts:326`

```ts
interface ApplyLandscapeOptions
```

- `at: (file: string) => string` — turn a file name of the def into a URL
- `texture?: (url: string, scene: BABYLON.Scene, o: { srgb: boolean; repeat: boolean }) => BABYLON.BaseTexture | Promise<BABYLON.BaseTexture>` — create a texture from a URL (default: a Babylon Texture, resolved once loaded); tests pass their own
- `scope?: BABYLON.AbstractMesh[]` — the name pattern of the meshes of the virtual texture's props is searched among these (default: all meshes of the scene)

### AssetLibraryOptions

*interface* · `packages/levels/src/library.ts:23`

```ts
interface AssetLibraryOptions
```

- `store: AssetStore`
- `mount: PackageMount`
- `assets?: RuntimeAssets` — needed for characters
- `tier?: LibraryTier` — the package tier to open (default `desktop`); a tier a package does not have falls back to its plain file
- `policy?: CatalogPolicy` — the catalog policy (trusted keys, `allowUnsigned`)

### AtmosphereDef

*interface* · `packages/levels/src/atmosphere.ts:17`

```ts
interface AtmosphereDef
```

Unreal's `SkyAtmosphere` component. Every field is optional and defaults to the component's own default. Lengths km, coefficients per km.

- `groundRadius?: number` — planet radius, km (6360)
- `atmosphereHeight?: number` — thickness of the air, km (60)
- `rayleighScattering?: Vec3` — Rayleigh scattering per km (0.005802, 0.013558, 0.0331)
- `rayleighScaleHeight?: number` — Rayleigh scale height, km (8)
- `mieScattering?: Vec3` — Mie scattering per km (0.003996 each)
- `mieAbsorption?: Vec3` — Mie absorption per km (0.00444 each)
- `mieScaleHeight?: number` — Mie scale height, km (1.2)
- `mieAnisotropy?: number` — Mie anisotropy g, -1..1 (0.8)
- `absorption?: Vec3` — ozone absorption per km at the middle of its layer (0.00065, 0.001881, 0.000085)
- `groundAlbedo?: Vec3` — colour of the ground the air sits on (0.402 each)
- `multiScatteringFactor?: number` — how much of the single scattering comes back as multiple scattering (1)
- `skyLuminanceFactor?: Vec3` — colour factor on the sky's luminance (1 each)
- `observerAltitude?: number` — how far the observer is above the ground, km (0)

### AtmosphereTable

*interface* · `packages/levels/src/atmosphere.ts:87`

```ts
interface AtmosphereTable
```

- `n: number`
- `rayleigh: Vec3[]` — Rayleigh in-scatter per view elevation, phase left out, per channel, in units of the sun's irradiance
- `mie: Vec3[]` — the same for Mie
- `sunTransmittance: Vec3` — share of the sun's light that reaches the observer: the sun disc's and the lit ground's colour (zero with the sun under the horizon)
- `ground: Vec3` — colour of the ground below the horizon, in the same units (albedo x sun and sky light)
- `g: number`
- `skyLuminanceFactor: Vec3`
- `multiScatter: number`
- `sunElevation: number`

### AttachOptions

*interface* · `packages/levels/src/library.ts:56`

```ts
interface AttachOptions
```

- `socket?: string` — a socket of the body's item (`grip_r`, `axe_r`...)
- `bone?: string` — a bone by name, with `position` and `rotation` of your own (they win over the socket's)
- `position?: [number, number, number]`
- `rotation?: [number, number, number, number]`
- `compatibleClips?: boolean` — also bind the clip sets of other skeletons that fit the part by bone names, and the other sets of its own skeleton (as for `CharacterOptions`): a rider's riding clips

### BindOptions

*interface* · `packages/levels/src/library.ts:76`

```ts
interface BindOptions
```

- `map?: Record<string, string>` — graph clip name -> clip name in the clip sets (default: every clip under its own name)
- `oneShot?: string[]` — graph clip names that do not loop

### BudgetLight

*interface* · `packages/levels/src/light-budget.ts:12`

```ts
interface BudgetLight
```

A level with hundreds of lamps, candles, torches and fires (Unreal draws them all as movable lights) cannot give each a real light: every
real light is paid for in every pixel of every lit surface. The light budget keeps a small pool of real lights and moves it to the lights
nearest the camera, from time to time; every other lamp is a glowing dot (an emissive instance of one shared mesh: one draw call for all).
The pool never changes size while it runs, so no shader is rebuilt when a light changes hands. A light that changes hands fades out and in
instead of popping. This is a performance limit, not a design: Unreal lights all of them.

- `id?: string` — a name for events and tests (the source actor's)
- `group?: string` — a group of lamps of one kind ('torch', 'candle'), for statistics and the sound budget
- `position: Vec3` — the level's metres
- `color: Vec3` — linear colour 0..1
- `intensity: number` — Babylon intensity at 1 m (already converted from the source engine's units, see `tools/ue-export/lights.mjs`)
- `range: number` — the metres where the light reaches zero (the source engine's attenuation radius)
- `kind?: 'point' | 'spot'`
- `direction?: Vec3` — a spot's axis (the way the light travels) and full cone angle in radians
- `angle?: number`
- `flicker?: { min: number; max: number; rate?: number }` — a flame's flicker: the intensity moves between `min` and `max` times its value, `rate` times a second
- `priority?: number` — nearer than a light of priority 1 by this factor when ranking (a chandelier over a candle); default 1
- `size?: number` — the size of the glow dot in metres (default from the range)
- `sound?: string` — a looping sound this lamp makes (a torch's crackle); it is budgeted by `sounds`, the caller plays it

### CatalogOptions

*interface* · `packages/levels/src/catalog.ts:11`

```ts
interface CatalogOptions extends CatalogPolicy
```

- `tier?: string` — device tier: packages with a `tiers[tier]` file use it
- `prefix?: string` — put in front of every level id of this catalog (two catalogs may share ids)

### CatalogRegistration

*interface* · `packages/levels/src/catalog.ts:18`

```ts
interface CatalogRegistration
```

- `readonly url: string`
- `readonly catalog: Catalog`
- `readonly stale: boolean` — the catalog came from the copy kept by an earlier session (no network)
- `readonly unsigned: boolean` — development only: the catalog was not signed
- `readonly levels: string[]` — ids registered in the LevelService
- `refresh(): Promise<{ changed: boolean; stale: boolean }>` — Ask the server again. `changed` when a new version replaced the registered levels; a failure leaves everything as it was.

### CellSource

*interface* · `packages/levels/src/streaming.ts:17`

```ts
interface CellSource
```

What the service needs from the asset layer (an `AssetCache` fits): fetch and parse a file, then instantiate it.

- `load(url: string): Promise<void>`
- `instantiate(url: string, name?: string): Promise<{ root: BABYLON.TransformNode; dispose(): void }>`
- `release(url: string): void`

### CharacterOptions

*interface* · `packages/levels/src/library.ts:44`

```ts
interface CharacterOptions extends Omit<ModelOptions, 'pkg'>
```

- `compatibleClips?: boolean` — also bind the clip sets of other skeletons that list this body's skeleton as `compatible`, and the other sets of its own skeleton (as `<set id>/<clip>`)
- `tier?: LibraryTier`
- `yaw?: number`

### ClipRef

*interface* · `packages/levels/src/library.ts:38`

```ts
interface ClipRef
```

A clip set a character can use: where its file is, and the clips in it. Bind them with `bindClips` of `@ferret/animation`.

- `id: string`
- `ref: string`
- `skeleton: string`
- `clips: ClipsItem['clips']`
- `prefix?: string` — set for a clip set that only fits by bone names (`compatible`): its clips are bound as `<prefix><clip>`, so they never replace the body's own

### CodeLoaderOptions

*interface* · `packages/levels/src/code.ts:47`

```ts
interface CodeLoaderOptions
```

- `origin?: (pkg: FerretPackage) => boolean` — does this package come from where the game does (shipped with it, its own site)? Then its code is the game's (CodePolicy.origin)
- `trusted?: () => Iterable<string> | undefined` — key ids whose signatures are trusted; without them any valid signature is enough
- `ferret?: unknown` — the engine's API handed to the code (default `globalThis.Ferret`, the shared engine)
- `esm?: boolean` — the engine is ES modules: the code imports `@ferret/*` itself, so no engine API object is needed and `ctx.ferret` may be undefined
- `extendContext?: (ctx: LibraryContext, pkg: FerretPackage) => void` — add to the context handed to a package's `install` (the player adds `game`, `levels`, `root`, `args`, `tier`)
- `open?: (id: string, range: string) => Promise<FerretPackage | null>` — a package that is not open yet (a dependency, a `require` by id): open it, e.g. through the catalog; null when there is none
- `importModule?: (bytes: Uint8Array, path: string, pkg: FerretPackage) => Promise<LibraryModule>` — import the module from its checked bytes (default: a blob URL and `import()`; tests use a data URL)

### CollisionChunkDef

*interface* · `packages/levels/src/collision-chunks.ts:9`

```ts
interface CollisionChunkDef
```

- `file: string`
- `bounds: [number, number, number, number, number, number]`

