524 lines
30 KiB
Markdown
524 lines
30 KiB
Markdown
# Worldlets design
|
|
|
|
Status: **W0-W3, J1 and J3a built**: the Cove (with its stream), the cliff
|
|
tunnel, and the journey on a server (a server per land; chat and the
|
|
player list across lands are J3b). The rest is planned.
|
|
|
|
*As built in J3a (a server per land):*
|
|
- `scripts/net/lands.gd` (autoload `Lands`): the server players join is the
|
|
hub. `Lands.ensure(land)` starts a land server (`--server --land cove
|
|
--port <hub+1..> --hub-port <hub>`, same config and world folder, output
|
|
to `land-<land>.log` beside server.json) and waits for it to say it's
|
|
listening. Hub and land servers talk over TCP on 127.0.0.1, port hub +
|
|
100, one JSON object per line: hello/ready, the player count, the clock
|
|
(the hub's, every 10 s: one clock everywhere), how far the tunnel's dug,
|
|
one-time passes, "stop", "host_left". A land server stops after 2 minutes
|
|
empty, when the hub stops, or if it loses the hub.
|
|
- A land server's saves: `Saves.land` makes it load and save its own
|
|
worldlet's file (`worldlets/cove.json`) in the hub's world folder.
|
|
Characters (`players/<id>.json`) are shared, and say which land they're
|
|
in (`land`) and, mid-crossing, where they'll come out (`arrive`).
|
|
- Crossing (`PlayerNet.cross`): the server saves the character as heading
|
|
there, makes sure the other server's up, and sends the game
|
|
`_s2c_travel {land, port, pass}`; the game drops the connection quietly
|
|
and joins that port with the pass (`Net.join(address, "", pass)`), checked
|
|
after the identity proof: a land server lets nobody in without one. The
|
|
world reloads on `Net.arrived`. Joining the hub when your character's in
|
|
another land sends you on there; a crossing that fails goes back to the
|
|
title. A hosted game: the host crossing doesn't stop the hub; the host
|
|
leaving the game from another land stops everything.
|
|
- The tunnel online: built on the server and in every game. The server
|
|
decides blows on its faces (`"hit"` finds them by uid), props
|
|
(`"tunnel_prop"`), going in and out (`"tunnel_go"`: a server teleport;
|
|
the move check allows the tunnel only from inside it) and the crossings
|
|
(`"cross"`), and tells everyone how it stands (`_s2c_tunnel`; it's in the
|
|
shared state a joining player gets, and saved with the Valley).
|
|
- `tests/test_net_lands.gd`: Alice digs and props online, lights it,
|
|
crosses to the Cove (bag and day kept) while Bob stays in the Valley,
|
|
comes back up, then leaves from the Cove and rejoining sends her there.
|
|
- Next, J3b: chat, the player list (with where each player is) and admin
|
|
commands across lands; sleeping needs everyone in every land. J3c: a land
|
|
server crashing, the Host page's extra ports.
|
|
|
|
*As built in J1:*
|
|
- `scripts/world/tunnel.gd`: the seam (an outcrop on the clifftop strip,
|
|
7 m in from the edge, its angle from the seed) and the tunnel space, out
|
|
of sight 260 m under the island: a CSG rock block with the landings and
|
|
six sloping stretches cut out (6 m on, 4 m down each, switching back on
|
|
two lanes: 24 m down), then a sea cave. Faces (`tunnel_face.gd`) answer
|
|
like boulders, so the pick works on them: tier 3 only ("Too hard: you
|
|
need a better pick"), 10 blows for the seam and 15 per stretch. Each
|
|
stretch must be propped (2 wood, [E] at its mouth) before the next face
|
|
takes a blow ("It's loose..."): 10 wood all the way down.
|
|
- Underground: no sun, moon, sky reflections or sky-tinted fog
|
|
(`day_night.underground`), no weather, no lake water, sheltered. Candles
|
|
and torch stands place anywhere on its floor and save as ordinary pieces.
|
|
- Walking out of the sea cave's mouth travels (`world.travel()`): the
|
|
worldlet you leave is saved, your character and the clock go with you,
|
|
and the other loads, arriving at its sea cave (`sea_cave.gd`, on the
|
|
Cove's beach where the headland starts). [E] at the cave goes back up the
|
|
same tunnel to its bottom, once it's dug through.
|
|
- Saves: `world.json` is the home worldlet's state, as before;
|
|
others are `worldlets/<id>.json`; meta.json's `worldlet` is where you
|
|
are, `home` where you began, `arrive` where the next load puts you.
|
|
- `tests/test_tunnel.gd`: dig through, walk every stretch down and back
|
|
up, light it, reload, out to the Cove, back up, out at the top, and the
|
|
chest at home is still there.
|
|
- Not yet: Old Tam's tip about the seam, the Guide page, and travel on a
|
|
server (J3: the tunnel isn't built online). The journey between
|
|
worldlets is §10, and the two ways to play (the journey, or one land) are in
|
|
its decisions.
|
|
|
|
*As built in W3:*
|
|
- `data/worldlets/cove.json`: low hills, mountains behind, a wide sandy
|
|
bay (a `beach` block) and a small pond. Not a start; its `next` is back
|
|
to the Valley (tunnel, J1) and on to the Highlands (sail).
|
|
- The pier (`scripts/world/pier.gd`), a world "place" (`"places":
|
|
[{"type": "pier"}]`): planks on posts from where the sand is at deck
|
|
height to 24 m past the waterline. Worked out from the terrain, so it's
|
|
the same for everyone and never saved or dismantled; scenery keeps clear
|
|
of it. ~1.9 m of sea at its end, ~2.7 m a cast beyond.
|
|
- Sea fish in `data/fish.json`: mackerel (common) and cod, which only bites
|
|
in water 2.2 m or deeper (a per-fish `min_depth`). Grilled at a campfire,
|
|
salted, or smoked, like the lake's.
|
|
- New World has no "Where": the journey always begins in the Valley (the
|
|
Host page keeps it, for One land mode). Started with
|
|
`godot --path . -- --dev-worldlets`, New World and Host offer every
|
|
worldlet, the others marked "(testing)", to try one directly.
|
|
- No sea stacks without a sea cliff.
|
|
- Tests: `test_cove` (walk onto the pier, catch a mackerel off its end, cod
|
|
only in deep water), and the Cove in `test_terrain_edges` and
|
|
`test_boundary`.
|
|
- The stream (after 0.4.0): a `stream` block, a line of points from a
|
|
spring under the mountains to the bay. Its water surface is the running
|
|
minimum of the ground along it (so it only ever runs downhill, down to
|
|
sea level), the bed is cut 0.6 m under that and the banks rise a little
|
|
above it; its water is its own ribbon mesh (`terrain._stream_mesh()`),
|
|
murkier than the lake. `water_at()` answers "stream" (trout bite there,
|
|
`data/fish.json`), the map shows it, and scenery, the den, the barrow and
|
|
the stones keep clear of it (`terrain.near_stream()`).
|
|
|
|
*As built in W2:*
|
|
- `lake` blocks can repeat: every lake and pond is its own body
|
|
(`terrain.lakes`, `world.water_bodies()`), all at one surface
|
|
(`water_level`, set by the first). The first is still "the lake" to the
|
|
older code (ice, its fish, the tutorial).
|
|
- A `beach` block (angle, width, `shore_offset`, `slope`, `current_out`,
|
|
`wall_out`): the land eases to a waterline and shelves gently into the
|
|
sea, which is then the same surface as the ponds (one ocean sheet, no
|
|
inland disc). The invisible wall stands well out in the water, and before
|
|
it a current (`terrain.current_at()`, `world._current()`) carries
|
|
swimmers back faster than they can swim, with a toast.
|
|
- `water_at()` tells the sea off a beach from a pond.
|
|
- Test-only worldlets live in `tests/worldlets/` (never listed or offered).
|
|
`testbay.json` (two ponds and a beach) runs through
|
|
`test_water_bodies`, `test_terrain_edges` (8 seeds) and `test_boundary`
|
|
(swimming out across the beach).
|
|
|
|
*As built in W1:* a "Where" picker on the New World and Host Game pages
|
|
(worldlets with `"start": true`; only the Valley, so it's greyed out), the
|
|
worldlet in the Load list, and a hosted island keeps its worldlet like its
|
|
seed.
|
|
|
|
*As built in W0:*
|
|
- `data/worldlets/valley.json`: the Valley as data, its terrain as five
|
|
building blocks in order (`hills`, `rim`, `sea_cliff`, `lake`,
|
|
`ocean_edge`) with every number that used to be hard-coded.
|
|
- `terrain.gd` runs a worldlet's blocks (`use_worldlet()`); each block is
|
|
the old code's arithmetic, unchanged. `tests/test_worldlets.gd` checks
|
|
four seeds give exactly the same height grid as before (SHA-256 of the
|
|
grid, taken from the old code).
|
|
- The worldlet is chosen like the seed: a server's (it sends its real one),
|
|
else the save's `meta.json` "worldlet", else the Valley (worlds from
|
|
before W0). New worlds and servers record it.
|
|
- The WorldInfo API on the world: `worldlet_id()`, `water_bodies()`,
|
|
`climate()`, `crossings()` (the planned ways on), beside `water_at()`.
|
|
Systems still read the terrain's numbers directly; they move over to
|
|
these as each new worldlet needs them to differ (W1-W3).
|
|
|
|
## 1. What a worldlet is
|
|
A **worldlet** is a small, complete world type: a ~320 m playable area with
|
|
its own shape, water, climate, wildlife and features, generated from a seed
|
|
like today's island. The current map becomes the first worldlet, **"Valley"**:
|
|
a mountain ring, a lake and a sea cliff. Others can differ in kind, not just
|
|
in seed:
|
|
|
|
| Worldlet | Shape | Water | Twist |
|
|
|---|---|---|---|
|
|
| **Valley** (today) | mountain ring + sea cliff | lake | wolves' den in the rim |
|
|
| **Cove** | low hills down to a sandy bay | sea + harbour, a stream | sea fishing, a pier, tides later |
|
|
| **Highlands** | plateau, cliffs, a tarn | small cold lake | snow most days, harsher nights |
|
|
| **Marsh** | flat, reedy, pools | many shallow pools | fog, insects, bog iron later |
|
|
| **Tutorial** | small (~160 m), calm, fixed seed | one pond | guided steps, a fenced rabbit meadow, named spots (docs/TUTORIAL_DESIGN.md) |
|
|
|
|
A worldlet is **data plus a few generator building blocks**, not a new
|
|
scene with its own code. That keeps the game's systems shared (building,
|
|
survival, creatures, saving) and makes worldlets addable by addons later
|
|
(§7).
|
|
|
|
## 2. The big decision: one worldlet per save, or linked worldlets?
|
|
|
|
**A. One worldlet per save.** New World asks for a name, a seed and a
|
|
worldlet type. Simple, and it fits everything already built.
|
|
|
|
**B. Linked worldlets in one save.** Several worldlets form one world, and
|
|
you travel between them (a mountain pass, a cave tunnel, a boat). Each keeps
|
|
its own state. More exciting, but much more work, especially in M2: a server
|
|
would have to simulate several places at once, or keep all players together.
|
|
|
|
**Recommendation: build A first, with a save layout that allows B later.**
|
|
Each worldlet's state goes in its own file from day one
|
|
(`worlds/<id>/worldlets/<worldlet>.json`), and the player's own state moves
|
|
to `worlds/<id>/player.json`. Then B is "add travel and a second file",
|
|
not "migrate every save".
|
|
|
|
## 3. Definition format
|
|
`data/worldlets/<id>.json`. Every number that's an `@export` on
|
|
`terrain.gd` today moves here, plus the lists that are hard-coded:
|
|
|
|
```json
|
|
{
|
|
"id": "cove",
|
|
"name": "Cove",
|
|
"description": "Low hills running down to a sheltered bay.",
|
|
"size": 320,
|
|
"terrain": {
|
|
"base": {"height_scale": 7.0, "octaves": 4},
|
|
"boundary": [
|
|
{"type": "mountains", "from_deg": 60, "to_deg": 300, "height": 50},
|
|
{"type": "beach", "from_deg": 300, "to_deg": 60, "slope": 0.08}
|
|
],
|
|
"features": [
|
|
{"type": "cove", "angle_deg": 0, "width": 60},
|
|
{"type": "stream", "from": "mountains", "to": "cove"}
|
|
]
|
|
},
|
|
"water": {"sea_level": -1.5, "lakes": []},
|
|
"climate": {"cold_day_chance": 0.1, "snowline": 45, "day_length": 1200},
|
|
"wildlife": {
|
|
"budget": {"rabbit": 5, "deer": 3, "boar": 2},
|
|
"wolves": {"den": "rim", "den_day": 3, "hunt_day": 5}
|
|
},
|
|
"places": [{"type": "pier", "at": "cove"}],
|
|
"spawn": {"near": "cove", "facing": "sea"}
|
|
}
|
|
```
|
|
|
|
Generator **building blocks** (GDScript, shared by all worldlets): base
|
|
hills, mountain rim, sea cliff, beach slope, cove/bay, lake basin, stream
|
|
bed, plateau. A worldlet only picks and parameterises them, so it can't do
|
|
anything a block can't.
|
|
|
|
## 4. What changes in the code
|
|
Most game code already asks the terrain generic questions:
|
|
`sample_height`, `sample_normal`, `inside_boundary` and `find_spawn`. The
|
|
work is in the places that know about *this* island's single lake and
|
|
cliff. Today:
|
|
|
|
| Assumption | Used in | Becomes |
|
|
|---|---|---|
|
|
| one lake at `water_level` | player (swimming), creature, spawner, den, build_system, props, lake_fish, world | `world.water_at(x, z)` (done for fishing) |
|
|
| `lake_center` / `lake_radius` | lake_fish, world (views) | the water bodies list |
|
|
| `sea_angle_deg`, `coast_point` | props (sea stacks, cliff-top strip), world (views) | the boundary segment list |
|
|
| `snow_height` | props, spawner, weather | `climate.snowline` |
|
|
| `play_radius` | props, den | boundary queries |
|
|
|
|
The steps:
|
|
1. **Terrain pipeline.** `terrain.gd` becomes a runner that stacks the
|
|
chosen building blocks into the height grid. The grid, collision,
|
|
`sample_height` and the boundary wall stay exactly as they are.
|
|
2. **A `WorldInfo` API** on the world node, for everything outside terrain:
|
|
`water_at`, `water_bodies()`, `boundary_at(angle)`, `features(type)`,
|
|
`climate`. No system reads terrain exports directly any more.
|
|
3. **Water bodies.** Each lake, pond and the sea is a body with a surface,
|
|
shape and type. The water mesh is built per body. Swimming, the "too
|
|
deep" check, animal steering and fishing all go through `water_at`.
|
|
4. **Props and places by feature.** Scenery reads densities from the
|
|
definition. The den, the sea stacks and (in Cove) the pier are "places"
|
|
positioned by features, not hard-coded angles.
|
|
5. **Saves.** `meta.json` gains `"worldlet": "valley"`. Older saves default
|
|
to Valley, so nothing breaks.
|
|
6. **New World screen.** Pick a worldlet type (with a short description and
|
|
a preview render), then name and seed as now.
|
|
|
|
## 5. The Cove and sea fishing
|
|
- **Beach boundary.** Sand slopes gently into shallow water. You can wade
|
|
and swim out until the water is deep, and then a current carries you back
|
|
(the beach equivalent of the invisible wall, with a toast).
|
|
- **Harbour.** A sheltered bay with a generated wooden pier. The pier is
|
|
built from the building pieces, so it's "placed by the world" and can't
|
|
be dismantled.
|
|
- **Sea fish** in `data/fish.json` under `"sea"`: mackerel (common),
|
|
cod (bigger, deeper water), and maybe crab pots later. Casting from the
|
|
pier reaches deep water. The fishing code needs no changes: it already
|
|
asks `water_at` what it landed in and looks up that water's fish.
|
|
- Later: tides (sea level shifting slowly), a raft or rowing boat, and
|
|
driftwood on the beach.
|
|
|
|
## 6. Done already (groundwork)
|
|
- `world.water_at(x, z)` returns `{type, surface, depth}` for the lake and
|
|
the sea, and fishing uses it instead of `terrain.water_level`.
|
|
- `data/fish.json` holds the fish per water type, bite timings and
|
|
dawn/dusk weights. `ItemDB.define_water()` registers a water's fish, so an
|
|
addon or a worldlet can add "sea" without code.
|
|
|
|
## 7. How it fits the roadmap
|
|
- **Before M2 (recommended):** steps 1-6 for Valley alone, with no visible
|
|
change, then the Cove. Doing the refactor before multiplayer means M2
|
|
replicates "worldlet id + seed + edits", not one hard-coded island.
|
|
- **M2:** the server loads one worldlet per save (option A). Clients
|
|
regenerate it from id + seed, which is the same determinism we rely on
|
|
now.
|
|
- **M3 (addons):** `define_worldlet()` takes the same JSON. Addons can only
|
|
combine building blocks, so a worldlet addon can't run arbitrary code in
|
|
the generator.
|
|
- **Option B (linked worldlets)** fits after M2, once we know how the
|
|
server handles players in different places.
|
|
|
|
## 8. Phasing and tests
|
|
| Phase | Delivers | Done when |
|
|
|---|---|---|
|
|
| **W0** | terrain pipeline + WorldInfo API; Valley defined in JSON | seed 1337 generates the **same height grid** as today (a hash test), and all 9 suites still pass |
|
|
| **W1** | worldlet picked on New World and saved in meta; old saves = Valley | load an old save and a new Valley save; both play |
|
|
| **W2** | water bodies (several per world), beach boundary block, swim-back current | a test worldlet with two ponds and a beach passes the boundary and terrain-edge tests |
|
|
| **W3** | **Cove** worldlet: bay, stream, pier place, sea fish | you catch a mackerel off the pier |
|
|
| **W4** | (optional) Highlands / Marsh | each passes the shared suites |
|
|
| **W5** | (after M2, optional) linked worldlets and travel | walk through a pass from Valley into Cove and back |
|
|
|
|
Every existing suite runs per worldlet (boundary, terrain edges, survival,
|
|
building, wildlife, fishing), so a new worldlet can't ship with a hole in
|
|
its edge or animals that spawn in the sea.
|
|
|
|
## 9. Questions for you
|
|
1. **A or B?** One worldlet per save to start, or do you want travel between
|
|
them early? (Recommendation: A, with the save layout ready for B.)
|
|
2. **Worldlet size:** keep ~320 m, or let some be bigger?
|
|
3. **Difficulty per worldlet:** should harsher worldlets (Highlands) bring
|
|
wolves sooner, or keep the same grace period everywhere?
|
|
4. **Which second worldlet first:** Cove (sea fishing, the beach you
|
|
mentioned) is my suggestion.
|
|
|
|
## 10. The journey: tunnelling out (proposal, Nov 2026)
|
|
|
|
A goal for the game, from Paul: once you have a good enough pick, you tunnel
|
|
through the mountain rim into the next, slightly harder worldlet, and now and
|
|
then go back to earlier ones for what only they have. This is option B
|
|
(linked worldlets) from §2, reached by digging rather than a path.
|
|
|
|
### The shape of it
|
|
- **A chain of worldlets**, each a little harder: Valley (today's island) →
|
|
Cove (sea, salt, mackerel) → Highlands (cold, deep snow, bigger packs) →
|
|
Marsh (bog iron, sickness, mists) → the Ember, the volcanic heart the game
|
|
is named for: the end of the journey.
|
|
- **The goal**, something to tell players on day 1: Old Tam talks of the
|
|
land beyond the mountains, where the fire in the earth still burns. Reach
|
|
the Ember and light the old beacon there. (Whether there's an ending or
|
|
it's open-ended is a question below.)
|
|
- **Gates:** each tunnel needs the best tool from the worldlet you're in.
|
|
The antler pickaxe (from the Old Stag) opens the Valley's tunnel; the
|
|
Highlands need a pick made from something found in the Cove, and so on.
|
|
Each worldlet has its own boss or trial, like the Old Stag.
|
|
|
|
### A different crossing each time (Paul, Nov 2026)
|
|
Players have to work out how to get from each worldlet to the next, and
|
|
it's a different way every time: tunnelling, climbing, burrowing, sailing.
|
|
Each crossing is that worldlet's puzzle, and what it needs comes from what
|
|
you've learned and gathered there:
|
|
|
|
| From → to | Crossing | Needs (a first idea) |
|
|
|---|---|---|
|
|
| Valley → Cove | **Tunnel** through the rim at a seam | the antler pickaxe, timber props, light |
|
|
| Cove → Highlands | **Sail** out of the bay and round the headland | a boat (planks, nails, a hide sail, rope), calm weather, food that keeps |
|
|
| Highlands → Marsh | **Climb** a frozen cliff | climbing picks and rope, warmth to last the climb (bear cloak, pemmican) |
|
|
| Marsh → Ember | **Burrow** under the old lava crust | a digging tool from bog iron, and a way through the heat and gas |
|
|
|
|
- **The hint, not the answer:** each worldlet shows its way out without
|
|
explaining it: a seam in the rock with old pick marks, a wrecked boat on
|
|
the beach, rope frayed on a cliff ledge. Old Tam (and his tips) nudge,
|
|
and the guide can keep it vague.
|
|
- **Coming back** uses the same crossing in reverse (the tunnel stays dug,
|
|
the boat stays moored, the rope stays fixed), so supply runs get easier
|
|
once you've made the way.
|
|
- **New systems this needs, one per crossing:** digging into a rim seam
|
|
(tunnel and burrow share most of it), a boat you build and steer on the
|
|
water, and climbing a marked face. Each is a phase of its own (J1 tunnel;
|
|
later the sail, the climb, the burrow).
|
|
|
|
### Tunnelling (the first crossing)
|
|
- **Where:** each worldlet's rim has one or two **seams**: cracked, darker
|
|
rock faces with a few loose stones at the foot, findable by exploring
|
|
(and Old Tam's tips). The rim stays a wall everywhere else.
|
|
- **How:** hit a seam with a good enough pick, many times. Each stretch dug
|
|
needs **timber props** (wood) or it falls back in, and it's dark in there:
|
|
candles, torch stands or a lantern. The tunnel is a short, built-for-it
|
|
cave (a few bends, maybe a side chamber with ore or a hazard), so it
|
|
stays hand-made rather than a hole through the terrain mesh, which the
|
|
heightmap can't do.
|
|
- **Through:** the far end opens onto the next worldlet. Walking out loads
|
|
it (a short fade, "The Cove"), with your bag, cloak and stats as they are.
|
|
|
|
### The first tunnel: down the sea cliff to the Cove
|
|
The Valley's sea side ends at a sheer cliff, its top about level with the
|
|
valley and the sea some 25 m below. The Cove is down there, so the first
|
|
tunnel goes **down**, inside the cliff, from the clifftop to the shore.
|
|
- **The seam:** a cracked outcrop on the clifftop strip near the edge (the
|
|
sea_cliff block marks where), with old pick marks and loose stones at its
|
|
foot. It can't be dug until you have the antler pickaxe (tier 3); a lesser
|
|
pick just chips it ("Too hard: you need a better pick").
|
|
- **Digging down:** the tunnel is about six stretches, each a few metres on
|
|
and a few down: a sloping passage that turns back on itself like a
|
|
stair, so the 25 m drop fits inside the cliff. A stretch takes many pick
|
|
blows (stamina, and wear on the pick) to open.
|
|
- **Props:** each new stretch needs a **timber prop** (2 wood) set at its
|
|
mouth before you can dig the next. Leave one out and the face won't take
|
|
another blow ("It's loose: prop it first"); nothing caves in on you, so
|
|
it's a cost, not a trap. That's 10 wood all the way down.
|
|
- **Light:** the tunnel has no daylight at all. Every stretch is pitch dark
|
|
until you bring light: a torch in your hand works, but your hands are full
|
|
with the pick, so the way to do it is to set **candles and torch stands**
|
|
as you go (tallow candles from bear fat, torch stands from wood). Placed
|
|
lights stay, so the way is lit for the trip back and for friends.
|
|
- **The bottom:** the last stretch breaks out into a sea cave, its mouth
|
|
opening on the Cove's shingle beach. Walking out of the mouth is the
|
|
crossing: a fade, then the Cove, arriving at the cave's Cove end. The way
|
|
back starts there and climbs the same tunnel.
|
|
- **What's saved:** the tunnel belongs to the Valley's save: how far it's
|
|
dug, the props, the lights. On a server it's shared: anyone can dig on
|
|
from where someone stopped, and the lights are everyone's.
|
|
- **How it's built:** the tunnel is its own small space (a "between" scene
|
|
next to the worldlet), made from a kit of rock pieces (straight, turn,
|
|
slope down, the cave mouth) laid out from the seed, with box-shaped
|
|
colliders. The heightmap can't have holes or overhangs, so the passage is
|
|
never cut into the terrain itself: you walk into the seam and you're in
|
|
the tunnel scene.
|
|
|
|
### Why go back
|
|
- **Each worldlet has things the others lack.** Clay and salt only in the
|
|
Valley and Cove; little wood in the Highlands; no bears outside the
|
|
Valley (so bear fat for candles and pemmican is a supply run); a fish or
|
|
herb only one place has. Food that keeps (pemmican, salted and smoked
|
|
meat, clay jars) becomes what you carry between them.
|
|
- **Your bases stay** where you built them: each worldlet keeps its own
|
|
save file, so your Valley hut, chests and jars are waiting.
|
|
- **Time:** there's one clock for every land (see the decisions below). A
|
|
worldlet you're not in doesn't run, but catches up on the time when you
|
|
come back, as after a night's sleep; nothing roams it meanwhile, so a base
|
|
you left isn't raided.
|
|
|
|
### Building it (phases)
|
|
| Phase | Delivers | Done when |
|
|
|---|---|---|
|
|
| **W0-W2** | the worldlet groundwork above (terrain pipeline, worldlet in the save, several water bodies) | as §8 |
|
|
| **J1** | seams in the rim, digging with props and light, a tunnel scene, travel between two worldlets in single-player, per-worldlet save files | dig through from the Valley, walk to the Cove, walk back to your hut |
|
|
| **W3** | the Cove (§5) as the second worldlet | its suites pass, and its tunnel back works |
|
|
| **J2** | gates and supply needs: tool tiers per tunnel, resources only some worldlets have | a test bot can't open the second tunnel without the Cove's pick |
|
|
| **J3** | multiplayer travel (see below) | two players in different worldlets on one server, or the rule we choose |
|
|
| **J4** | Highlands, Marsh, the Ember and its ending | the journey can be finished |
|
|
|
|
### Multiplayer: the hard part
|
|
A server today runs one island. Options:
|
|
- **Everyone together:** travel needs every player at the tunnel (like the
|
|
sleep rule). Simplest; one worldlet runs at a time.
|
|
- **Spread out:** the server runs each worldlet that has someone in it.
|
|
The most freedom, and the most work and CPU.
|
|
- **Party leader:** whoever goes through brings the others with a vote.
|
|
|
|
### Decisions (Paul, Nov 2026)
|
|
- **Extendable, not finished:** the Ember has a last boss, and after it you
|
|
keep playing. The chain is built so more worldlets (and crossings) can be
|
|
added after the Ember later: each worldlet names the next, nothing assumes
|
|
the Ember is the end.
|
|
- **The crossings:** tunnel, sail, climb, burrow, in that order.
|
|
- **Harder by biome, not just by numbers:** each worldlet has its own,
|
|
stronger animals and its own hazards. The Marsh, for one: mosquitoes whose
|
|
bites can make you sick, and bog, mist and the like. Others in the same
|
|
spirit per biome (to design with each worldlet).
|
|
- **Multiplayer: spread out.** Each player goes where they like; the server
|
|
runs every worldlet someone is in. Built in J3, after single-player travel
|
|
works.
|
|
- **A server per land (Paul, Nov 2026)**, rather than one process holding
|
|
several worldlets (which would mean making every network module, the
|
|
saves and the actions handle several worlds at once):
|
|
- the Valley's server is the **hub**: the address everyone joins. It
|
|
starts a server for another land (the next UDP port: 24243 the Cove,
|
|
and so on) when someone first goes there, and stops it a couple of
|
|
minutes after its last player leaves;
|
|
- each land server is today's server, with its own worldlet, save file,
|
|
weather and creatures; it keeps a private link to the hub on the host
|
|
machine;
|
|
- characters live in one shared folder. A crossing saves your character
|
|
first, marks you as on your way, and hands your game the land's port
|
|
and a one-time pass; your game reconnects there ("On to the Cove...").
|
|
Nothing can be lost or copied on the way: crash mid-way and you come
|
|
back where you were going. Joining the hub when your character's in
|
|
another land sends you straight on there;
|
|
- chat, the player list (with where each player is) and admin commands
|
|
go through the hub to every land;
|
|
- the tunnel belongs to the Valley's server; the Cove's asks the hub
|
|
whether it's dug through;
|
|
- from the internet, friends need those extra ports forwarded too (on a
|
|
home network, nothing to do). The Host page will list them;
|
|
- the Old Stag and other bosses scale with the players in their land.
|
|
- Phases: **J3a** the tunnel online, land servers on demand, the
|
|
crossing and the shared characters; **J3b** chat, the player list and
|
|
admin across lands; **J3c** the awkward cases (a land server crashing,
|
|
rejoining mid-crossing, the Host page's ports).
|
|
- **One clock for every land (Paul, Nov 2026).** The day and hour are the
|
|
same everywhere, so the lands never drift apart:
|
|
- sleeping until morning needs **everyone, in every land**, in bed (on a
|
|
server the hub counts them all, and every land skips to morning
|
|
together);
|
|
- a land nobody is in doesn't run, but when someone arrives it catches up
|
|
on the time since anyone was last there, as after a night's sleep:
|
|
kilns and smelters work, fires burn down, food goes off, trees and
|
|
berries grow back. (Built for single-player already: `world.catch_up()`.)
|
|
No creatures roam it meanwhile, so a base you left isn't raided.
|
|
- **Boats: one each, no passengers.** A boat belongs to whoever built it
|
|
(like a bed): only the owner, and their friends under the server's locks
|
|
rule, can sail it, and nobody else can dismantle it. It stays moored where
|
|
it was last left (the Cove end or the Highlands end), so nobody is
|
|
stranded by someone else taking the only boat. Lose or wreck it and you
|
|
build another, which is harder where wood is scarce (the Highlands), so
|
|
plan the trip back. Each player has to earn their own crossing by boat.
|
|
- **The other crossings are shared:** a dug tunnel, a fixed climbing rope
|
|
and a dug burrow are changes to the world, there for everyone once one
|
|
player has made them (like a door or a bridge).
|
|
- **Two ways to play (Paul, Nov 2026).**
|
|
- **The journey** (the standard game), single-player and multiplayer
|
|
alike: every new world starts in the Valley and the worldlets come in
|
|
their fixed order (Valley, Cove, Highlands, Marsh, Ember). Each
|
|
worldlet's shape is set by its definition; the seed only changes the
|
|
detail (hills, trees, where the den is), so the journey is the same
|
|
adventure for everyone.
|
|
- **One land** (a hosting choice): no journey, no crossings, the server
|
|
is locked to one worldlet, and the host picks which, from all of them.
|
|
Co-op or PvP as now (the rules preset). In code: a `mode` in
|
|
`server.json` ("journey" / "one_land"); in one-land mode the Host
|
|
page's "Where" lists every worldlet rather than only the starts, and
|
|
the crossings (`world.crossings()`) answer none, so a tunnel seam
|
|
can't be dug. Kept, like the seed and the worldlet, once the island
|
|
exists. Worth building once the Cove exists (until then there's only
|
|
the Valley to pick).
|
|
|
|
### Server cost, measured (0.3.1)
|
|
One worldlet, a dedicated server on the M5 Pro, never pausing: about
|
|
**12% of one CPU core and 270 MB of memory**, with nobody on. Players add
|
|
creatures, weather and pickups around them. So with *spread out*, the worst
|
|
case of 8 players in all 5 worldlets is about five of today's servers in
|
|
one process: roughly 1.4 GB and a little over half a core before players,
|
|
and worldlets with nobody in them aren't running and cost nothing (each starts its own server when someone goes there).
|
|
Comfortable on the home Mac; a small cloud server (2 GB) would be tight.
|
|
|
|
### (Answered) questions
|
|
1. **The ending:** a real ending at the Ember (light the beacon, a last
|
|
boss, the credits) or open-ended (the journey is the goal, keep living)?
|
|
2. **The crossings:** do the four above fit (tunnel, sail, climb, burrow),
|
|
and in that order? Should a worldlet have more than one way out (a hard
|
|
one and a long one)? And for the tunnel: only at marked seams, or
|
|
anywhere in the rim (more freedom, harder to make good tunnels)?
|
|
3. **Multiplayer:** everyone together, spread out, or a party vote?
|
|
4. **How much harder** each worldlet: stronger animals and weather only, or
|
|
new rules too (thirst in the Ember, sickness in the Marsh)?
|
|
5. **Order:** Cove second (sea fishing, gentle) as planned, or straight
|
|
into the Highlands for a harder step?
|