on(created, ...) looks like a web page's onload: "when I am loaded, do this". But what runs when a world loads is two different things, and ConeScript keeps them apart:
- Build is pure construction: sculpt the lantern, bake its glass's pattern, lay the land and carve the brook into it. It is also called hydration, the step that turns a description into running state. Its products (meshes, materials, textures, solids) become the world's state, and the building code can then be dropped. Because building is pure (the same inputs always give the same products), it may be skipped when its products are already cached.
- Start is the acts that must happen on every load: start the lanterns' nearness chase, light the lamps if the world arrives after dusk, start a villager's round. If start were mixed into build, a warm start (one that loads cached products instead of building) would silently skip them.
Nothing new is written to say which is which. What a part declares is build; what on(created, ...) does is start.
How you write it
Pieces of a world, sorted into build and start.
// BUILD: pure construction; skipped when its products are cached
part land // the terrain, a part (terrain.html)
part walkway: spine.through(points: [(-22, 0, 498), (-22, 0, 476), ..., (-16, 0, 333.6)], kind: uniform, ends: doubled)
part brook : stream(course: ..., level: ..., depth: ...)
field heights: heightGrid = terrain.bake(size: 1025, // the terrain's own order
steps: [grid, press(walkway), carve(brook)])
part pathLanterns
each p in 0 ..< 6 {
each (k, side) in [(0, -1), (1, 1)] {
let way = land.walkway;
let spot = way.spotAt(way.fromEnd(1.8 m + 2.8 m * p), across: side * 1.5 m); // reads the land
part lantern[2 * p + k]: pathLantern, at: spot
}
}
part materials
part granite: [physical(...), paintedBy(surfacepatterns.RockParams.granite(512, seed: 6101))] // the rocks'
// START: acts that happen on every load, warm or cold
on(created, {
let h = hour(now(world));
set(lampLevel, if h >= sky.duskHour || h < sky.dawnHour { 1 } else { 0 });
})
part pathLantern: lanternHead // as on designs.html
...
on(created, start(chase(gain, toward: if near && lampLevel > 0 { nearGain } else { 1 },
speed: rate(nearGain - 1, per: sim(0.4 s)))))
part village
part round: villager(who: people.averageMan, home: cottageFront)
on(created, start(npcbehave.patrol(body, places: [cottageFront, playground, bridgeCrest],
dwell: sim(8 s), on: walkable)))
terrain.bake(size, steps) is a placeholder name. The world's own on(created, ...), the lamp level's, is the world part's start.
The rule
on(created, ...)holds acts only:set,start,spawnand the other actions (actions.html). It never constructs.- A declaration (a part, a shape, a material, a place, a field's default) never acts. It is a value, and the same discipline as value blocks applies: building may not set, start, spawn, send, say or play (subscriptions.html).
What it means
Build: pure construction
Everything a part declares is built once: the land's grid of heights, 1025 by 1025, dressed and carved; each lantern's meshes and materials; the granite's painted pattern. Building reads only its inputs (fields, seeds, other parts' built products) and writes only its products. Construction that is pure can be cached, shared between copies, moved to another machine, or done again at a different resolution without anything else in the world noticing.
The cache
A product already built from the same inputs is loaded instead of built. The key is everything the product was made from: a different input, a different key. A typical cached product is a baked texture set (a color map, a normal map, which gives a flat surface the shading of small bumps, and an occlusion-roughness-metal map), keyed by the bake's name, the tile's size and seed, and a format number; a warm start loads it in a fraction of the time a cold build takes. The cache is the proof that build can be skipped, and the warning: it is safe only because nothing in a bake acts.
Start: on(created, ...)
Start is short and cheap and must never be skipped. created is the trigger "this part has just been made". It fires for a part made at load and for one spawned later, since a spawn is the same copy made later (designs.html). An animator started from on(created, ...) and never stopped is "a value always following another": the lanterns' gain is always chasing.
Start comes before every message. A part's on(created, ...) runs, and its acts take effect, before any message to that part is handled, whether the message was sent before the part was made or is waiting in the same tick. So the world's start act sets the lamps once, from the hour it arrives at, before its first dusk or dawn is read, and a dusk that arrives in the first tick fades from the level the start act set, never the other way round (Events: when it runs).
The lamps on arrival are a start act: a world that arrives after dusk or before dawn (the hours the sky gives, sky.duskHour and sky.dawnHour: 17:28 and 6:31 on 15 October at 45 degrees north, Sky and weather) starts with the lamps fully on, with no fade; otherwise off. The dusk and dawn rules handle every change after that. Had it been folded into building the lanterns, a warm start would have left the lamps dark at night. The lanterns' nearness chase is started the same way, once, as each lantern is added, and runs from then on.
Order follows dependencies, not the tree
A lantern's place reads the land's height at its spot, so the land must be built before the lanterns, though the lanterns are not inside the land. Inside the land, the terrain's own steps come in a fixed order: the grid, the walkway pressed in, the brook carved. (The dressed area round the cottage is no step: it changes no height.) The order of building follows what reads what, which the language can see because construction names what it reads. An order worked out from dependencies cannot be got wrong by moving a line, and tells the engine what can be built in parallel and what must be rebuilt when something changes.
The building code is needed again
The code that built a part is dropped only when nothing can call for it again. Three things can:
- a part spawned later (a copy of a design, made while the world runs);
- a product wanted at another resolution (a sign rebuilt with sharper text as you walk up);
- a product whose construction inputs changed (a design edited while the world runs).
"Dropped after loading" is a memory saving the engine may make, not a rule.
Each machine builds only the products it needs
One description has several products, one per consumer: render meshes and materials for the view, collision solids for physics, a navigation mesh, picking triangles, sound bindings. The server needs solids and the land's heights; it draws nothing. Each visitor's machine needs meshes, materials and textures to draw with, and solids too, to predict its own movement. This is a proposal.
Preparing, waking and caching parts of a world
A world is not loaded whole. A visitor's machine holds one scene, and of that scene only the regions that might be seen (Worlds, scenes and regions). Build and start apply to each piece as it arrives:
- A scene beyond a portal is prepared before it is needed. As the visitor nears a portal, the browser fetches, builds and caches the scene beyond, so going through is immediate. How ready it must be is the portal's requirement (
arrival: instantorarrival: mayFade, proposal: Portals: being ready); when to start is the browser's. - A region wakes when it might be seen. First its stand-in is drawn, then its parts are built, deeper as the visitor goes deeper (Worlds: regions). Under the proposal, a region states where it can be seen from as an area, and an eye entering that area wakes it (Worlds: waking a region).
- The cache makes a return warm. Products are keyed by what they were made from, so a region or scene left and come back to is loaded, not built again, and a scene prepared for a portal not taken costs nothing to keep.
- Order still follows dependencies. A region whose parts read another region's products (the land under a district) needs those first.
The content states the partition and the requirements; when to fetch, how much to hold and what to let go are the browser's, by distance, approach and budget.
How it connects
- Construction pipelines: what build runs; products.
- Designs: one build for many copies; a build again for each spawn.
- Events:
createdamong the triggers. - Subscriptions: a build-time subscription is one whose inputs stop changing once the world is built.
- Distribution: which machine builds what.
- Worlds, scenes and regions and Portals: what is prepared, woken and cached.
Open questions
- The cache's key in the model. For content it would be the description's own hash plus the vocabulary's version; which vocabulary changes invalidate which products is not stated.
- Whether a start act may read built products. The lantern's chase reads
near, which needs the area, a built product, so start runs after build. Whether it may run before all of a large world is built (a world arriving piece by piece, region by region) is open. - Is waking a creation? When a region wakes, or a visitor enters a scene, whether its parts'
on(created, ...)runs, runs once per machine, or runs only the first time the world makes them; and what the server does for a region no visitor's machine holds (Worlds, scenes and regions). - Products not yet named. Solids, picking triangles, the navigation mesh: the content does not say which consumer needs which, so the engine cannot yet decide per machine.
- A start act that depends on time since load would make warm and cold starts differ. The lamps on arrival read the clock, not the load.
- A loop of build-time dependencies: the brook's level reads the land's height, and the land is carved by the brook.