Skip to content

Adding Zones

Status: Current Last reviewed: 2026-07-07

A zone is a ZoneData document plus a terrain generator. The shipped zone is starter_valley_001 (Greyfen Village).

ZoneData Anatomy

The document (schema version 2) is described in full on the Level Editor page. The parts you author are:

  • Identity: zoneId, zoneName, worldSize.
  • terrain: a generator key plus a seed.
  • environment: sky and fog colours and distances, sun and hemisphere light settings.
  • Landmarks: spawnPoint, villageCenter, campCenter, campLeashRadius.
  • objects: every placed ZoneObjectInstance.

Colliders, NPCs, mob spawns, quest triggers, target props, the safe zone, and ember relays are all derived from objects by buildZoneRuntime, so you never hand-author those lists.

Terrain Generators

TERRAIN_GENERATORS in shared/src/world.ts maps a generator name to a pure (x, z) => height function. It ships starter_valley (rolling hills with a flattened village plateau, a raised camp, and a sunken road) and flat. To add terrain, add a deterministic function and register it under a new key, then reference that key in the zone's terrain.generator. Keep it pure and deterministic so the server and client agree on height without shipping a heightmap.

Two Ways to Author a Zone

As pure JSON. Copy server/data/zones/starter_valley_001.json, change zoneId/zoneName, and edit the objects array (by hand or, more easily, in /admin via Export JSON / Import JSON). Validated on save.

As a generator function. For a procedurally built zone, write a buildXZoneData() in shared/src/world.ts that returns a ZoneData (the shipped buildDefaultZoneData is the worked example), and have ZoneStore seed it if no file exists.

Current Limitation

The server hosts a single zone, chosen by STARTER_ZONE_ID in server/src/index.ts, and every player joins it. A second zone can exist as data, but there is no zone router or cross-zone travel yet. Milestone 7 adds the router that runs multiple Zone instances and wires up portals. The per-zone simulation is already isolated in the Zone class, so the change is additive rather than a rewrite.