Appearance
World generation: seed plus edits
You'll learn: how a lobby's terrain is made, and why lobbies are cheap. You'll need this when: your game has generated terrain, or players dig and build.
Status: the recipe, per-lobby seeds, regenerateTerrain and mods' world-gen features (ore, tree, scatter, structure) are Built, with four simple generators; onlookers' pages generate the same blocks from the recipe and the seed. biome and loot features are registered but not used by generation yet. The engine's own generators in place of the simple ones are Planned.
A lobby is seed plus edits
Your plot's terrain is built in layers:
lobby's edit layer what players changed in this lobby (a sparse diff)
over
the shared base what you placed by hand and published (structures, the lobby area)
over
generated terrain generate(recipe, this lobby's seed), inside your boundsEach lobby has its own seed, so two lobbies can be two different worlds from the same recipe. Onlookers don't download your terrain: they get the recipe and the seed, generate it themselves, then apply the edits. That's why lobbies are cheap.

Rendered with the runtime's headless renderer from makeGenerator (source: img/src/render-worldgen.mjs).
The recipe
A runtime declares it in plot.json:
json
{ "kind": "runtime", "worldgen": { "generator": "hills", "seed": 7, "features": ["ores:copper"] } }| Generator | Makes |
|---|---|
"flat" | grass over dirt over stone (or your own layers) |
"hills" | gentle value-noise hills |
"void" | air: sky islands are yours to place |
"world" (or none) | the world's own terrain under your plot |
Seeds and regenerating
ts
const id = lobbies.create({ seed: 1234 }); // runtime, plot level; default: derived from the plot and lobby ids
lobbies.regenerateTerrain(id, 5678); // drop this lobby's edits and generate again (refused unless empty)Want 20 different maps? Create 20 lobbies with 20 seeds.
Features from mods
A mod can add to every lobby's generation:
ts
import { worldgen } from "@inventery/minecraft";
worldgen.feature("copper", "ore", { block: "ores:copper_ore", veinsPerChunk: 6, maxY: 40 });The runtime names the features its recipe uses in worldgen.features, in order; each is applied over the generated terrain, deterministically from the lobby's seed, per 16×16 column. data per kind (defaults in brackets):
| Kind | data |
|---|---|
ore | block, replaces (["stone"]), veinsPerChunk (4), size (4), minY, maxY |
tree | trunk ("oak_log"), leaves ("oak_leaves"), perChunk (2), height ([4, 6]), on (["grass"]) |
scatter | block, perChunk (8), on (["grass"]) |
structure | blocks ([[dx, dy, dz, block], ...]), chance (0.1), on |
biome, loot | registered only, for now |
A feature naming an unknown block or feature id is skipped.
Hand-placed content on top
Anything you write at plot level goes into the shared base, which every lobby sees and none can change:
ts
export function init() {
if (!storage.getPlot("built")) {
world.fillBox({ min: [0, 18, 4], max: [8, 19, 12] }, world.blockId("stone")); // an island, in every lobby
storage.setPlot("built", true);
}
}In a lobby hook, the same world.fillBox writes that lobby's edit layer instead. Coordinates in host calls are world coordinates (world.bounds().min is the plot's corner).