Skip to content

Custom adapters & the conformance kit

A game that isn’t built on vgai’s first-party scene format can still run — and be edited — inside the editor by shipping its own adapter: a module in the game folder implementing the small RootAdapter contract (the same seam described in the adapter seam). The editor mounts, renders, and authors the game through it; your game keeps its own architecture.

The editor authoring the conformance-kit fixture game: the adapter-provided Sentry node selected in the hierarchy, the gizmo bound to it, and the inspector showing an ungrouped Aware field plus Vitals (Health/Score) and AI (Mode) groups
A custom adapter's game in the editor — hierarchy, gizmo, and a domain-shaped grouped inspector, all driven through the adapter's own providers.
  1. Your vgai.project.json declares a root with "adapter": { "module": "src/adapter.ts", "surface": "three" } (game-folder-relative path; no .., no absolute paths — and surface is required, the object is validated strictly). There is nothing to declare about capability: the editor measures what your adapter reaches.
  2. The module default-exports { id, mount(host) }. Your manifest’s adapter.surface — "three", "canvas", or "dom" — picks the host mount() receives and the mounted shape it returns (a real THREE.Scene + THREE.Camera for three, a real pixi Container stage for canvas, the same container element it was handed for dom), plus a dispose() and (what makes you editable) an optional authoring surface: hierarchy (required), transforms, inspector, persistence — a react adapter has no transforms surface by design (no 3D object to bind).
  3. Capability reach is measured against reality after mount (packages/editor/src/adapter-reach.ts): capture, transforms, inspector and persistence are each checked off the live adapter your mount() returned. A missing capability never blocks the mount — it becomes a standing warning that names the mechanism to close it and never stops firing; the conformance kit surfaces the same gaps in its report before a user’s editor does. There is no summary grade and no acceptable partial state.
  4. Prove it — the quick way, from a monorepo checkout: vgai conformance /abs/path/to/your/game-folder (add --json for a machine-readable report). Or call the kit directly from a vitest test in this repo:
import { runAdapterConformanceKit } from './adapter-conformance-kit';
const report = await runAdapterConformanceKit('/abs/path/to/your/game-folder');
// throws a named `adapter-conformance: …` error on the first violation

The full contract — provider-by-provider assertions, the inspector probe table, the three.js identity/dedupe rules, worked examples — lives in docs/BRINGING-AN-EXISTING-GAME.md in the repo. That guide is the canonical reference; this page is the map.

runAdapterConformanceKit(projectRoot) runs headless (Node, no GPU/DOM) and walks the whole contract in order: manifest loads → module path-guarded and imported → default export shape-checked → mount() against a headless host matching your root’s surface (ThreeHostContext for three, CanvasHostContext for canvas, { container, game } for dom) → the mount-contract check for that surface (three: scene/camera real instances; canvas: stage a real pixi Container; dom: container is the same element handed to mount(), plus drivesOwnLoop: false) → hierarchy roots non-empty, ids unique, id↔object3D bridge bidirectional (three/canvas — explicitly skipped, with a logged note, for dom) → transforms get/apply round-trip, rotation is a quaternion (three/canvas only; dom has no transform surface) → every non-readonly inspector field survives a set→get round-trip (every kind) → persistence is honest (destination named, save() resolves; every kind) → capability-reach warnings surfaced → dispose() doesn’t throw.

There is no sandbox: a custom adapter module runs with full editor-realm privileges, exactly like game entry code (single-realm, by design). The boundary is declaration honesty: the module path can’t escape the game folder, capabilities must be declared, and declared-vs-achieved is verified and logged. Don’t run game folders you don’t trust.