← World map and portals · all tasks

Homes connect to the world, and the game gets a way out of itself

done priority 1 estimate L

world-map-and-portals/009 · created 2026-08-16 · updated 2026-08-18

What prompted it

Kris played the deployed build and found four things (2026-08-16).

1. A home was a sealed box

Account creation made a home Area with no portal out of it. A player who reached their home could never leave. Fixed: ensureHomeArea now creates the door in the same breath as the home — from the home’s bottom-middle cell into the hub Area — and a boot backfill opens homes that predate it (it ran in production and reported opened a way out of 1 home area(s)).

2. home_area portals never worked

The Village’s village-to-home portal was drawn on the map and inert underfoot. It failed at three layers: getPortalsForArea filtered out every row with a null destination, so the cell never reached the world process’s grid; and the director had no resolution branch at all. Now the row reaches the grid carrying a destSpecial, and the director resolves it per player, at travel time, to that player’s own home.

A sentinel destWorldId: "home_area" was considered and rejected. getWorldById queries a UUID column, so a non-UUID string doesn’t return null — it throws (Postgres 22P02), which escapes into a fire-and-forget catch. Neither confirmed nor rejected would ever be sent, and the player — already erased from the source world by step 1 of the sequencing — would hang forever, with the request re-sent on every control reconnect. Hence a real protocol field.

Why the home portal is one-way

Kris asked for bidirectional, and the experience is: out through your own door, back through the Village’s home portal. The implementation cannot be a bidirectional row, and this is correctness, not preference:

A two-way portal’s return leg lives at the destination cell. Every player’s home portal points at the same Village cell, so N players means N return legs stacked on one cell, and grid.set_portal() keeps only the last. Stepping there would drop you into a stranger’s home.

test/home-portal.test.ts is the proof: two travellers walk the same Village cell and each arrives in their own home.

3. The Village must not be deletable

It carries is_home_hub (one per server, enforced by a partial unique index) and an authored entry_x/entry_y. The editor shows a hub badge and replaces the delete button with an explanation; the loader refuses a file with no hub, a hub that isn’t public, an out-of-bounds entry cell, or an entry cell sitting on a portal.

4. The in-game map was unreachable, and there was no way out of the game

The map screen existed but hung off the account screen, which is only reachable from the title — so an in-game player could not get to it at all. The top-right “Signed in as …” was a click-through Label. It is now a profile pill opening a menu: the player’s name, World Map, Account, and Main Menu (Kris’s wording) to leave the world.

And nothing was visible anyway: the deployed web build was 02456a9 from 2026-08-13, 39 commits behind, predating the world map entirely. Re-exported and redeployed.

Also fixed along the way

Deliberately not done

Players stacking on the arrival cell — world-map-and-portals/008, which waits on player collision existing at all.

View source in repository · also available as raw markdown.