Skip to content

Reach the tile route, and give the map what it was missing (#130, #131) - #132

Merged
CaYatur merged 2 commits into
mainfrom
fix/map-routing-and-markers
Jul 29, 2026
Merged

Reach the tile route, and give the map what it was missing (#130, #131)#132
CaYatur merged 2 commits into
mainfrom
fix/map-routing-and-markers

Conversation

@CaYatur

@CaYatur CaYatur commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Reach the tile route, and give the map what it was missing (#130, #131)

The world rendered in the app and in neither web surface, because the panel's
tile route was unreachable.
Server routes match

/^\/api\/servers\/([^/]+)(?:\/(\w+))?$/

and \w does not match a slash, so /api/servers/:id/map/tiles matched nothing
and fell through to the 404 at the bottom. Every tile fetch from the panel has
404'd since #119; the desktop was fine because it goes over IPC. One nested
segment is allowed now, still \w-only per segment so a sub-route cannot
express a traversal.

The coverage test could not have caught it: it compares the documented surface
to route literals found in the handler's own source, and 'map/tiles' was in
the source. So there is a second check now — every documented fixed-path GET
route must answer something other than the router's own "not-found", called with
an owner token so a scope refusal cannot be mistaken for a reachable route. It
found the bug immediately when reinstated, and it also flags routes with a
second placeholder as untestable rather than pretending to cover them.

Heatmap off by default, on all three surfaces. It is an analysis overlay,
and a red block over the one player online was the first thing anyone saw.

Click a player to go to them. The list under the map was inert; with several
people online, finding one meant panning and reading the coordinate readout. It
centres the view and keeps the zoom — jumping to a fixed zoom would throw away
the scale the operator had chosen.

Structures on the map: villages, dungeons and ruins, temples, fortresses,
mineshafts, with a filter. Read from structures.starts in the same chunk NBT
the surface already comes from, so it costs a lookup rather than a second pass
over the world, and only when asked for — a payload that carries them "in case"
is one the public feed could leak.

Off by default everywhere. An operator may switch them on for their own map
without a setting, because they can already read the world folder. The public
map cannot
: the feed sends markers only when the operator published them and
ignores what the caller asks for, so the site cannot be talked into a treasure
map of a private world. The toggle is hidden on the public page for the same
reason — a control the feed ignores is not a control.

How far ahead to load, as an operator setting. Worth being exact about what
it is not: MSMS never generates terrain. It reads region files the server has
already written, and a map that could grow a world by being panned would be a
map that can fill a disk. "Load ahead" reads a ring of chunks around the
viewport so panning is already drawn — more parsing, same world. Pregenerating
terrain deliberately is a separate feature.

**The world rendered in the app and in neither web surface, because the panel's
tile route was unreachable.** Server routes match

    /^\/api\/servers\/([^/]+)(?:\/(\w+))?$/

and `\w` does not match a slash, so `/api/servers/:id/map/tiles` matched nothing
and fell through to the 404 at the bottom. Every tile fetch from the panel has
404'd since #119; the desktop was fine because it goes over IPC. One nested
segment is allowed now, still `\w`-only per segment so a sub-route cannot
express a traversal.

The coverage test could not have caught it: it compares the documented surface
to route literals found in the handler's own source, and `'map/tiles'` was in
the source. So there is a second check now — every documented fixed-path GET
route must answer something other than the router's own "not-found", called with
an owner token so a scope refusal cannot be mistaken for a reachable route. It
found the bug immediately when reinstated, and it also flags routes with a
second placeholder as untestable rather than pretending to cover them.

**Heatmap off by default**, on all three surfaces. It is an analysis overlay,
and a red block over the one player online was the first thing anyone saw.

**Click a player to go to them.** The list under the map was inert; with several
people online, finding one meant panning and reading the coordinate readout. It
centres the view and keeps the zoom — jumping to a fixed zoom would throw away
the scale the operator had chosen.

**Structures on the map**: villages, dungeons and ruins, temples, fortresses,
mineshafts, with a filter. Read from `structures.starts` in the same chunk NBT
the surface already comes from, so it costs a lookup rather than a second pass
over the world, and only when asked for — a payload that carries them "in case"
is one the public feed could leak.

Off by default everywhere. An operator may switch them on for their own map
without a setting, because they can already read the world folder. **The public
map cannot**: the feed sends markers only when the operator published them and
ignores what the caller asks for, so the site cannot be talked into a treasure
map of a private world. The toggle is hidden on the public page for the same
reason — a control the feed ignores is not a control.

**How far ahead to load**, as an operator setting. Worth being exact about what
it is not: MSMS never generates terrain. It reads region files the server has
already written, and a map that could grow a world by being panned would be a
map that can fill a disk. "Load ahead" reads a ring of chunks around the
viewport so panning is already drawn — more parsing, same world. Pregenerating
terrain deliberately is a separate feature.
Copilot AI review requested due to automatic review settings July 29, 2026 02:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

…nsion changes

I split the three maps again, one PR after merging them. Structures went into the
shared web engine and the routes, and the desktop — which is an admin surface and
the one an operator uses most — got none of it. It has the toggle, the filter and
the same markers now, drawn from the same `structureKind` grouping so a village
is the same dot everywhere.

Worse, and older than this PR: tiles are keyed by chunk alone, and the nether
uses the same coordinates as the overworld. Switching dimension kept the tiles,
so one world's terrain was drawn under another world's players — shipped in #119
and true on every surface. Both caches are dropped when the dimension changes.

The markers were also drawn before the grid and the heatmap, so a grid line
crossed every one of them. They go after both and before the players: a marker
under a line reads as a smudge, and a player must never be behind one.
@CaYatur

CaYatur commented Jul 29, 2026

Copy link
Copy Markdown
Owner Author

Self-review: markers on the desktop too, and drop tiles when the dimension changes

I split the three maps again, one PR after merging them. Structures went into the
shared web engine and the routes, and the desktop — which is an admin surface and
the one an operator uses most — got none of it. It has the toggle, the filter and
the same markers now, drawn from the same structureKind grouping so a village
is the same dot everywhere.

Worse, and older than this PR: tiles are keyed by chunk alone, and the nether
uses the same coordinates as the overworld. Switching dimension kept the tiles,
so one world's terrain was drawn under another world's players — shipped in #119
and true on every surface. Both caches are dropped when the dimension changes.

The markers were also drawn before the grid and the heatmap, so a grid line
crossed every one of them. They go after both and before the players: a marker
under a line reads as a smudge, and a player must never be behind one.

@CaYatur
CaYatur merged commit 1cfe928 into main Jul 29, 2026
1 check passed
@CaYatur
CaYatur deleted the fix/map-routing-and-markers branch July 29, 2026 10:07
@CaYatur CaYatur self-assigned this Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants