# Levels and packages

# Levels and packages

A level is a folder (or a `.ferret` package) with a glTF scene and a **manifest** that says which nodes are gameplay entities, where the player may start and how the level looks and behaves. Artists move things in Unreal or in the editor; because the manifest refers to nodes **by name**, the wiring survives a re-export.

## The manifest

`manifest.json` (`LevelManifest`) sits next to `scene.glb`:

```json
{
  "version": 1,
  "name": "Yard",
  "scene": "scene.glb",
  "navmesh": "navmesh.bin",
  "defaultSpawn": "gate",
  "spawns": [{ "id": "gate", "position": [0, 0, 8], "yaw": 3.14 }],
  "entities": [
    { "id": "door1", "node": "Door_Main", "behaviour": "Door", "params": { "slide": [0, 2.4, 0] } },
    { "id": "key",   "node": "Key",       "behaviour": "Pickup", "params": { "item": "brass-key" } }
  ],
  "colliders": [{ "node": "Fence_Decor", "collide": false }],
  "environment": { "sun": { "intensity": 3 }, "fog": { "density": 0.01 } },
  "physics": { "capabilities": ["gravity", "friction"], "gravity": { "vector": [0, -9.81, 0] } }
}
```

| Field | Meaning |
|---|---|
| `scene`, `navmesh`, `collision` | file names; a `collision` mesh of simple shapes replaces the visible meshes as solids |
| `collisionChunks` | the solid shapes outside the play area in chunks (`loadRadius`, `unloadRadius`, `chunks`: a file and its bounds each); a chunk is fetched when the player is within `loadRadius` of its bounds and released beyond `unloadRadius`; needs `collision` |
| `navBuild` | how to build the NavMesh when `navmesh.bin` is missing: agent size, climb, slope, cell size; `bounds` (one `{min, max}` box in world metres, or a list: the NavMesh is built inside the union, like several Unreal NavMeshBoundsVolumes); `exclude` (boxes where nothing is walkable, like NavModifierVolumes with the null area; `yaw`, radians about +y, turns a box about its centre) |
| `entities` | `ManifestEntity`: a node to wrap (or a new empty one), a behaviour or several components, params, tags, a transform |
| `spawns`, `defaultSpawn` | where the player may start; the level is entered at `defaultSpawn` when none is named |
| `zones` | named zones for the `ZoneService` |
| `movement` | the player's movement values for this level, over Ferret's defaults |
| `animGraphs` | animation graphs by name |
| `colliders` | per-node overrides: make a node solid or walk-through whatever the export says |
| `environment` | sun, ambient light, fog, sky, fixed effects (`EnvironmentDef`) |
| `physics` | realism for this level: materials, pair rules, capabilities and their values (Physics overview (skill `ferret`)) |

`validateManifest` returns a list of problems (path and message); an empty list means the manifest is usable. The same check runs at load time and in the editor.

## The level service

`LevelService` is the level registry and travel. A game registers `LevelDef`s, each with a builder that gets a `LevelContext`. `travel(id)` shows the loading screen, removes the old level, builds the new one, places the player at a spawn and hides the loading screen again; it reports with events (`level:loading`, `level:loaded`, ...). **Everything a builder creates** (nodes, lights, materials, entities) is removed again when the level goes, so levels do not leak into each other.

A `LevelDoor` in the level is the usual way into another one: using it calls `travel`.

## Packages

`installPackages` teaches a `LevelService` to play packages: a folder with a manifest, or a single `.ferret` file. `PackageMount` registers the package's levels (the first package's levels as `<id>`, later ones as `<pkgid>/<id>`). Packages are loaded from a URL, from a `File`, or from bytes; the `pkg.*` commands add and remove them at run time ([Commands](commands.md)).

A package can be **signed** and its files **encrypted**; the loader checks signatures, hashes and paths before anything is used, and a package that carries code runs under a `CodePolicy`. `validatePackage` runs the package's own checks. See `@ferret/assets` for the format and its security checks.

### Code in a package

A package of kind `library` (or a `game`) may carry code: **one bundled ES module** under `code/`, named in `ferret.json` as `"code": { "entry": "code/index.js" }` (a game may name it as `entry.code`), that exports `install(ctx)`. `installCode` gives a mount a `CodeLoader`; `require(id)` (or the command `pkg.require { id }`) runs a package's code:

1. the code of its dependencies first (each must be open, or opened through the loader's `open` hook, e.g. from the catalog, and fit its version range);
2. the package must pass its `CodePolicy`: it comes from where the game does (`origin`), or it carries a valid signature (from a trusted key when the game keeps a list);
3. the module is read once and checked against its sha256 in the manifest, and **those bytes** are imported (a blob URL), so what runs is what was checked;
4. `install(ctx)` is called once. `ctx` has the `world`, the engine's API as `ctx.ferret`, the package (`ctx.pkg`), `ctx.url(path)` for its files and `ctx.require(dep)` for a dependency's exports. It may return `{ dispose }`.

The module does not import the engine: it is built with `@ferret/*` and Babylon as externals and reaches them through `ctx.ferret` and the global `BABYLON`, so it runs against the **shared engine** (`lib/ferret/ferret.js`, `globalThis.Ferret`). A game that bundles its own copy of the engine passes its API as the loader's `ferret`, or library code is refused with that reason. A page runs one version of a package's code: a module cannot be unloaded, so another version needs a reload. `pkg.loaded` lists what runs; `code:loaded` and `code:failed` are the events. `provides` names what a library's code registers (`capabilities`, `kinds`, `flowSources`, `forms`, `commands`, `levelKeys`), so a level can be resolved to the packages it needs without running them.

## Environment

`applyEnvironment` builds a level's look in the scene (sun, ambient light, fog, sky, effects) and returns an `Environment` to dispose when the level goes. Quality tiers decide which effects are on.
