# The host, capabilities and the step

# The host, capabilities and the step

This page is the working manual for `PhysicsHost`. The [overview](overview.md) says what physics in Ferret is; here is how to use it, in the order you need it.

## What it is for

The host runs one fixed step of physics for the whole world: it asks every *switched-on capability* to add forces, lets the kernel move the bodies, then lets capabilities react to what the contacts did. It is for games that want rigid bodies, buoyancy, breakage, fire and the rest *on demand*. It is not a renderer and it does not move characters: a `CharacterMovement` is moved by its own code and only *reads* fields (gravity, medium) from the host.

## The model

Four things, and how they relate:

- the **kernel** moves bodies (a rigid-body engine; see bodies and kernels (skill `ferret-physics-bodies`));
- a **capability** is one realistic behaviour with a name, a phase, defaults and hooks (`gravity`, `friction`, `strength`...);
- the **host** owns the kernel and the capabilities, and decides the order;
- the **scoped config** holds each capability's numbers at four scopes: world, level, zone, entity.

Registering a capability makes it *available*. Nothing runs until it is **enabled**. Enabling and disabling are cheap and can happen at any time: when a level loads, when a player enters a room, from a command.

## First use

```ts
import { PhysicsHost } from '@ferret/physics';
import { gravity } from '@ferret/physics-gravity';

const host = world.addService(new PhysicsHost({ capabilities: [gravity()] }));
host.enable('gravity');

const ball = world.spawn('ball', { position: new BABYLON.Vector3(0, 10, 0) });
ball.add('RigidBody', { shape: 'sphere', radius: 0.25, mass: 1 });

world.run(1);                                  // one simulated second: 60 fixed steps
```

`world.run(seconds)` and `world.step()` drive the world without a renderer. In a running game the loop does it: `World.advance` takes the frame time and runs as many fixed steps as fit.

## The step, in order

Every fixed step the host does this, and nothing else:

1. Kinematic bodies take the velocity that carries them to where their node is now.
2. **Phase `field`**: capabilities add forces (gravity, drag, buoyancy, explosions, springs). Within a phase they run by `order`, ties by name. Never by registration order, so two games that register the same capabilities in a different order get the same physics.
3. The kernel integrates and resolves contacts. Contacts are collected.
4. **Phase `strength`**, then **phase `stretch`**: capabilities that act on what the contacts did (damage, breakage), then those that integrate soft things.
5. Every dynamic body's node is set to its body's pose.
6. Contact events leave as `physics:contact` on the world's event bus, in one batch.
7. The governor, if you set one, is told how long it took (budgets and tiers (skill `ferret-physics-fx-tuning`)).

What follows from the order: a force added in `field` is in the same step's integration; damage from a hit shows in the same step it happened; a capability never sees a half-finished step of another.

## Recipes

### Switch a capability on for one place only

Capabilities have scoped values, and the narrowest scope that has a value wins: **entity, zone, level, world, default**.

```ts
import { PhysicsHost } from '@ferret/physics';
import { gravity } from '@ferret/physics-gravity';

const host = world.addService(new PhysicsHost({ capabilities: [gravity()] }));
host.enable('gravity');
host.config('gravity').set('world', 'scale', 1);
host.config('gravity').set('entity', 'scale', 0.16, 'moon-rock');       // one thing on the moon
const e = world.spawn('moon-rock');
```

A **zone** value needs the world's zone service (see zones (skill `ferret`)): `config.set('zone', key, value, zoneName)` applies to every body whose position is inside that zone. A **level** value is cleared when the level goes (`endLevel`); a **world** value stays.

### Apply a whole `physics` block

A level manifest, a game or an agent can hand over everything at once: materials, pair rules, combine rules, gravity, the list of capabilities and each one's values. `configure` validates first and throws with every problem listed, so a typo in a level never half-applies:

```ts
import { PhysicsHost } from '@ferret/physics';
import { gravity } from '@ferret/physics-gravity';
import { friction } from '@ferret/physics-friction';

const host = world.addService(new PhysicsHost({ capabilities: [gravity(), friction()] }));
host.configure({
  capabilities: ['gravity', 'friction'],
  materials: { ice: { friction: 0.03, restitution: 0.1 } },
  gravity: { vector: [0, -3.7, 0] },
  friction: { scale: 1.2 },
}, 'world');
```

`validatePhysicsConfig(block, knownNames)` is the same check without applying: use it in tools and in the editor.

### Load and unload a level's physics

`beginLevel(block)` applies a block, **links the constraints and fractures by node name** and returns a list of warnings for what could not be linked. `endLevel()` undoes it: its constraints go, the capabilities it switched on go off (unless something else still needs them) and its level and zone values are cleared. The level package does this for you; do it yourself only for a game that manages its own scenes. The block's `constraints` and `fractured` are covered in the level physics block (skill `ferret`).

### Ask and command it from outside

The host registers debug commands, the same ones an agent or the inspector uses: `physics.info` (kernel, capabilities on and off, cost, last step time), `physics.enable` / `physics.disable`, `physics.configure`, `physics.materials`, `body.state` and `body.impulse`. Capabilities add their own (`gravity.at`, `gravity.set`...). From a test or a console: `await world.commands.execute('physics.info', {})`.

### Write a capability of your own

A capability is a plain object: it works alone and meets the others only through the kernel, the materials and the order of the step.

```ts
import { PhysicsHost, type Capability, type Vec3 } from '@ferret/physics';

/** a steady wind pushing everything that is dynamic */
const wind = (): Capability => {
  let off: (() => void) | null = null;
  return {
    name: 'wind', phase: 'field', order: 10,
    defaults: { force: [4, 0, 0] },
    cost: 'one vector add per body per step',
    enable(host) {
      off = host.kernel.addForceField(b => b.kind === 'dynamic' ? host.config('wind').get<Vec3>('force', { position: b.position }) : null);
    },
    disable() { off?.(); off = null; },
  };
};

const host = world.addService(new PhysicsHost({ capabilities: [wind()] }));
host.kernel.gravity = [0, 0, 0];
host.enable('wind');
const e = world.spawn('leaf', { position: new BABYLON.Vector3(0, 1, 0) });
e.add('RigidBody', { shape: 'sphere', radius: 0.1, mass: 1 });
world.run(1);
```

The rules a capability keeps: put the work in a **phase** (`field` for forces, `strength` and `stretch` for reactions); use `addForceField` for anything that depends on position so fast bodies feel the force where they are; give `defaults` for every number so the scopes work; declare `requires` for capabilities that must be on first and `needsBodies` if it needs a rigid-body kernel; clean up in `disable`.

## Parameters

| Piece | Setting | Meaning |
|---|---|---|
| `PhysicsHost` options | `kernel` | the rigid-body engine; the light kernel when missing |
| | `materials` | the material library; the kernel's own when it has one |
| | `capabilities` | registered at once; none is on until enabled |
| `Capability` | `name`, `phase`, `order` | identity and place in the step; lower `order` first |
| | `defaults` | the value at every scope unless overridden |
| | `needsBodies` | refuses to switch on if the kernel has no rigid bodies |
| | `requires` | names that must be on first (`disable` of them is refused while this one is on) |
| | `cost` | words for the inspector |
| `host.enable(name, values?)` | `values` | written to the **world** scope, then switched on |
| `host.governor` | | see budgets and tiers (skill `ferret-physics-fx-tuning`) |

## Interplay

- Capabilities that provide a **field** others read set `host.fields.*` while they are on: `fields.gravity(p)` (used by characters), `fields.medium(p)` (water, mud, air with its flow), `fields.column(p, h)` (how much of a vertical segment stands in a dense medium). If the capability is off the field is absent and the reader falls back to its default.
- Capabilities exchange *data through materials*, not code: a `strength` reads the material's strengths, a `heat` its heat capacity. A material without the number means "does not apply".
- `physics-links` (skill `ferret-physics-links`) closes loops between capabilities (a fire heats a pot, steam turns a turbine) without either knowing the other.
- Events leave on the world's bus: `physics:enabled`, `physics:disabled`, `physics:contact`, `physics:degrade`; each capability adds its own (`terrain:flooded`...).

## Budget

The host's own overhead is a sort of the enabled capabilities per step and a position sync per body. The cost of the step is the sum of what each capability declares in `cost`. `host.lastStepMs` is the last step's wall time; `physics.info` shows it per frame.

## Debugging

| Symptom | Cause |
|---|---|
| `capability "x" is already registered` | two `register` calls with one name; register once |
| `no capability "x" is registered` | you enabled before registering, or the name has a typo (the message lists what is registered) |
| `needs rigid bodies, and the "light" kernel has none` | a capability that needs real rigid bodies: give the host a Havok kernel (bodies and kernels (skill `ferret-physics-bodies`)) |
| `needs "y" switched on first` | enable in dependency order, or let `configure` do it: its list is enabled in the order given |
| a body does not fall | gravity is not on, or the body is `static`, or its `gravityScale` is 0, or it is `exempt` from gravity (a vessel does its own) |
| a value seems ignored | a narrower scope overrides it: `config.get(key, ctx)` with the entity or position shows what wins |
| the node does not follow its body | the `RigidBody` has `rotate: false` (position only) or the node has a parent whose transform is not what you expect |

## Reference

`PhysicsHost`, `Capability`, `Phase`, `ScopedConfig`, `PhysicsConfig`, `validatePhysicsConfig`, `PhysicsFields`, `RigidBody`, `@ferret/physics`.
