fix(client): the deep rungs were flat because relief_q fell off the wire (T-1213)
`relief_q` is the one field with signal below District — elev_q's 80 m steps
quantise sub-district detail away, which is precisely why relief_q was invented.
The server has encoded it since 5eb394b36 and the terrain layer has asked for it
by name ever since. step_canvas_protocol.gd's decode dictionary never listed the
key, so `canvas.get("relief_q")` was always null and the plane arrived nowhere.
The server half of that change landed; the protocol half did not.
That is the whole reason Region and below rendered as a flat wash. Measured plane
variety at District before the fix:
{morphology: 1, elev_q: 11, relief_q: 0, moisture_q: 25, vegetation: 3}
A 0 there means ABSENT, not constant — a distinction the capture could not make
until this commit adds it, and the reason two earlier sessions read the flatness
as a missing generator rather than a missing key.
Also spends the field properly. It drove a stipple PROBABILITY only, so a ridge
and a plain differed in dot density, which at one pixel per cell reads as noise;
and `_ruggedness()` took absf(relief_q - 50), discarding the sign the server
deliberately preserved ("a hollow and a rise are different ground... the reverse
is not recoverable"). Relief now shades continuously and signed — rises lighten,
hollows darken — UNDER the stipple rather than instead of it. Ruggedness
(unsigned) and elevation (signed) are different questions and both are worth
asking.
Ladder, before -> after (tooling/atlas-flatness, lum p1-p99):
Global 145.69 -> 145.69 unchanged, correct: relief_q is flat 50 at
orbital rungs by construction
Region 33.59 -> 71.01 2.1x
District 13.72 -> 77.01 5.6x
Quarter 11.01 -> 42.56 3.9x
Structure retention Global->Quarter: 7.6% -> 29%.
NOT finished, and the ticket says so: Region now reads as heavy speckle, because
ruggedness is real data instead of an elev_q-gradient fallback and far more cells
earn a mark than the T-1194 tuning assumed; District reads as soft blobby relief,
form without directionality. Both are grammar/tuning follow-ups on a channel that
finally carries signal.
0.4.9 is a REQUIRED bump. The disk cache stores the DECODED canvas, so every
earlier entry physically lacks the field and would keep rendering flat against a
build that reads it — the first bump in this series where a warm cache is wrong
about CONTENT, not merely stale. tooling/canvas_sources.py gains
step_canvas_protocol.gd for the same reason: it decides which planes exist, the
cache stores its output, and the T-1242 gate would not have flagged this fix
while the registry stopped at ui/.../step_canvas/.
Regression cover: every protocol test passed throughout the weeks the plane was
missing, because each asserted a field it already knew about and none asserted
the SET. There is now a test walking all eight dense planes of EncodedStepCanvas,
verified by disabling the fix and watching it fail by name.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -34,16 +34,16 @@ class_name StepCanvasProtocol
|
||||
## (duplicated here per browse_protocol.gd's own "genuinely standalone"
|
||||
## precedent, not shared via a Callable).
|
||||
## EncodedStepCanvas — a map: {width, height, morphology, elev_q, temp_dc,
|
||||
## moisture_q, vegetation, settlement_id, lake_margin_q, glaciation,
|
||||
## flooded_q, courses, cliffs}. `lake_margin_q` (T-1188) is a MessagePack
|
||||
## moisture_q, vegetation, settlement_id, lake_margin_q, relief_q,
|
||||
## glaciation, flooded_q, courses, cliffs}. `lake_margin_q` (T-1188) is a MessagePack
|
||||
## map key that did not exist before this codec version — an older server
|
||||
## build's payload simply omits it (`d.get("lake_margin_q")` below returns
|
||||
## null, decode_png_field() then returns an empty PackedByteArray, the
|
||||
## same "field absent -> draws as the colorize fallback" posture every
|
||||
## other optional plane on this wire already has); a client this new
|
||||
## talking to that old a server is not a supported combination anyway
|
||||
## (D-192 co-ship). The seven PNG-per-field dense planes (morphology/
|
||||
## elev_q/moisture_q/vegetation/lake_margin_q/glaciation/flooded_q) are
|
||||
## (D-192 co-ship). The eight PNG-per-field dense planes (morphology/
|
||||
## elev_q/moisture_q/vegetation/lake_margin_q/relief_q/glaciation/flooded_q) are
|
||||
## each a map {"png_bytes": [...]} — png_bytes is a Rust `Vec<u8>` with NO
|
||||
## serde_bytes annotation anywhere in this codebase (confirmed: grep for
|
||||
## serde_bytes across server/src returns nothing), so serde's blanket
|
||||
@@ -181,6 +181,16 @@ static func _decode_encoded_canvas(raw: Variant) -> Variant:
|
||||
else []
|
||||
),
|
||||
"lake_margin_q": decode_png_field(d.get("lake_margin_q")),
|
||||
# T-1213: the server has encoded this since relief_q was added, and the
|
||||
# terrain layer has asked for it ever since — but this decode never
|
||||
# listed the key, so `canvas.get("relief_q")` was always null and the
|
||||
# plane arrived nowhere. The whole point of relief_q is that it is the
|
||||
# ONE field with signal below District (elev_q's 80 m steps quantise the
|
||||
# sub-district detail away), so its absence is exactly why those rungs
|
||||
# render as a flat wash. Measured before the fix: plane variety at
|
||||
# District was {morphology: 1, elev_q: 11, relief_q: 0, moisture_q: 25,
|
||||
# vegetation: 3} — a 0 meaning ABSENT, not merely constant.
|
||||
"relief_q": decode_png_field(d.get("relief_q")),
|
||||
"glaciation": decode_png_field(d.get("glaciation")),
|
||||
"flooded_q": decode_png_field(d.get("flooded_q")),
|
||||
"courses": d.get("courses", []),
|
||||
|
||||
Reference in New Issue
Block a user