@ferret/core
World, fixed-step loop, entities and components, services, event bus, logging and the debug command channel.
Classes
CommandChannel class
class CommandChannelregister(name: string, description: string, fn: CommandFn): void | |
has(name: string): boolean | |
list(): CommandInfo[] | |
async execute(name: string, args: any = {}): Promise<unknown> |
packages/core/src/commands.ts:9
Component class
abstract class Component<P extends object = Record<string, any>>A component is a plain class attached to an entity. Parameters come from the manifest (params). Hooks: onAttach (attached), onStart (first fixed step after attach), onFixedUpdate(dt) at the fixed rate, onUpdate(dt) once per rendered frame, onDetach (removed or entity destroyed).
entity: Entity | |
started: boolean | |
enabled: boolean | |
constructor(public params: P = {} as P) | |
get world(): World | |
get scene(): BABYLON.Scene | |
get node(): BABYLON.TransformNode | |
onAttach?(): void | |
onStart?(): void | |
onFixedUpdate?(dt: number): void | |
onUpdate?(dt: number): void | |
onDetach?(): void |
packages/core/src/component.ts:9
Entity class
class EntityAn entity wraps a Babylon TransformNode and carries components.
readonly id: string | |
readonly components: Component[] | |
readonly tags: Set<string> | |
timeDilation: number | Per-entity time dilation (a game may slow its NPCs). 1 = normal. |
destroyed: boolean | |
data: Record<string, any> | |
constructor(readonly world: World, readonly node: BABYLON.TransformNode, id?: string) | |
get name(): string | |
get position(): BABYLON.Vector3 | |
add<C extends Component>(cls: ComponentClass<C> | string, params?: any): C | |
get<C extends Component>(cls: ComponentClass<C> | string): C | undefined | |
require<C extends Component>(cls: ComponentClass<C> | string): C | |
remove(c: Component): void | |
destroy(): void |
packages/core/src/entity.ts:7
EventBus class
class EventBusSmall event bus. emit calls handlers synchronously, in subscription order.
on<T = any>(name: string, fn: Handler<T>): () => void | |
once<T = any>(name: string, fn: Handler<T>): () => void | |
off(name: string, fn: Handler): void | |
emit<T = any>(name: string, payload?: T): void | |
clear(): void |
packages/core/src/events.ts:4
Logger class
class Loggerlevel: LogLevel | |
sinks: Array<(level: LogLevel, scope: string, msg: string) => void> | Sinks receive every line at or above level; the OS Beroun platform adds one that posts to the shell's log. |
constructor(readonly scope: string) | |
debug(m: string) | |
info(m: string) | |
warn(m: string) | |
error(m: string) |
packages/core/src/log.ts:4
Registry class
class Registry<T>A name -> constructor registry. A manifest names components and behaviours; games register the classes.
constructor(readonly kind: string) | |
register(name: string, item: T): this | |
get(name: string): T | |
has(name: string): boolean | |
names(): string[] |
packages/core/src/registry.ts:2
SceneHost class
class SceneHost implements ServiceThe scene host: the place for a second World over a second Scene on the same Engine, so a game's interlude can be an ordinary scene with its own lights, camera and behaviours while the target level loads in the main one. Both scenes share the GL context, shaders and texture caches. Only the active world is rendered (rendering both cost 15x in WebKit, S0). The main world always advances (its services - ui, net, input - keep running, and its paused flag still decides whether the simulation steps); another world advances only while it is the active one.
readonly name: "host" | |
constructor(readonly engine: BABYLON.AbstractEngine) | |
init(world: World): void | |
get main(): World | The world the level service builds into and the game normally plays in. |
get active(): World | The world that is advanced and rendered now. |
worlds(): World[] | |
get(name: string): World | undefined | |
nameOf(w: World): string | undefined | |
create(name: string, opts: HostWorldOptions = {}): World | A new world over a new scene on the shared engine. It is not shown until show. The game owns what is in it. |
show(world: World): void | Show a world: from the next frame it is the one advanced and rendered. The one that was shown is not advanced any more. |
showMain(): void | Show the main world again (a travel does this when the player is let in, and when a transition fails). |
dispose(world?: World): void | Dispose a world the game created, with its scene (all of them with no argument: the service's own dispose). If it is shown, the main world is shown first. |
advance(realDt: number): void | One real frame: advance the main world, and the active one if it is another. |
render(): void | Render the active world's scene. |
packages/core/src/host.ts:15
World class
class WorldThe World owns the Babylon scene, the fixed-step simulation loop (decoupled from rendering), the clock with time dilation, the event bus, the services and the entities.
readonly events: EventBus | |
readonly components: Registry<ComponentClass> | |
readonly behaviours: Registry<ComponentClass> | |
readonly commands: CommandChannel | |
readonly entities: Entity[] | |
readonly fixedStep: number | |
readonly maxFrame: number | |
timeScale: number | Global time scale (slow motion < 1). |
time: number | Simulated seconds since the world was created (affected by timeScale, not by entity dilation). |
tickCount: number | Number of fixed steps run. |
paused: boolean | |
constructor(readonly scene: BABYLON.Scene, opts: WorldOptions = {}) | |
addService<S extends Service>(s: S): S | |
service<S extends Service = Service>(name: string): S | |
hasService(name: string): boolean | |
get alpha(): number | How far the render frame is between the last two fixed steps (0..1), for interpolating what the camera follows. |
spawn(name: string, opts: { node?: BABYLON.TransformNode; position?: BABYLON.Vector3; id?: string; tags?: string[] } = {}): Entity | Make an entity over a new TransformNode (or over an existing node of the scene). |
removeEntity(e: Entity): void | |
entity(id: string): Entity | undefined | |
findByName(name: string): Entity | undefined | |
findByTag(tag: string): Entity[] | |
entitiesWith<C extends Component>(cls: ComponentClass<C>): Entity[] | |
advance(realDt: number): number | Advance by a real frame time: runs as many fixed steps as fit, then the per-frame updates. |
step(): void | Exactly one fixed step (also the way tests drive the world). |
run(seconds: number): void | Run the world headless for seconds of simulated time. |
dispose(): void |
packages/core/src/world.ts:29
Functions
ensureBabylon function
function ensureBabylon(...parts: string[]): Promise<void>Make sure the named parts of Babylon are loaded (core, gltf, pbr, shadows, particles, pipeline, video, debug): fetched from globalThis.FERRET_BABYLON (the folder of the parts, set by the player or the page) and imported once. Resolves at once when they are there.
packages/core/src/babylon.ts:22
hasBabylonPart function
function hasBabylonPart(part: string): booleanIs part available now: the full build, or a part already loaded?
packages/core/src/babylon.ts:13
Interfaces
CommandInfo interface
interface CommandInfoname: string | |
description: string |
packages/core/src/commands.ts:7
HostWorldOptions interface
interface HostWorldOptions extends WorldOptionsclearColor?: [number, number, number] |
packages/core/src/host.ts:4
Service interface
interface ServiceA world-wide singleton (the rough equivalent of a UE subsystem): input, levels, zones, physics, ui ...
readonly name: string | |
init?(world: World): void | |
fixedUpdate?(dt: number): void | Called before the components' fixed update, at the fixed rate. |
update?(dt: number): void | Called once per rendered frame. |
dispose?(): void |
packages/core/src/world.ts:8
WorldOptions interface
interface WorldOptionsfixedStep?: number | Fixed simulation step in seconds (default 1/60). |
maxFrame?: number | Longest real frame time that is simulated, in seconds; a stall is not a jump (default 0.1). |
packages/core/src/world.ts:18
Types
CommandFn type
type CommandFn = (args: any) => unknown | Promise<unknown>The debug command channel: named commands that a tool (the inspector, the model viewer, an AI agent over MCP) calls to look at and change a running world. Commands are plain JSON in, plain JSON out.
packages/core/src/commands.ts:5
ComponentClass type
type ComponentClass<C extends Component = Component> = new (params?: any) => Cpackages/core/src/component.ts:26
Handler type
type Handler<T = any> = (payload: T) => voidpackages/core/src/events.ts:1
LogLevel type
type LogLevel = 'debug' | 'info' | 'warn' | 'error'packages/core/src/log.ts:1
Constants
BABYLON_VERSION const
const BABYLON_VERSION: "9.33.1"the Babylon version this engine was built and tested with
packages/core/src/version.ts:10
FERRET_BUILD const
const FERRET_BUILD: stringthe build stamp yyyymmddHHMM in the LOCAL time of the build machine, never UTC; dev when the code runs unbuilt (tests, the repository)
packages/core/src/version.ts:12
FERRET_VERSION const
const FERRET_VERSION: "0.1.0"the engine's version (semver); every module of a release shares it
packages/core/src/version.ts:8
FERRET_VERSION_FULL const
const FERRET_VERSION_FULL: stringwhat to show a person: 0.1.0+202611051430
packages/core/src/version.ts:14