feat(ui): step-canvas map component — RTT terrain + stepped zoom (D-255, T-1182)
The two-layer client rebuild per D-255(a)(b)(e), replacing the _canvas.scale continuous-zoom model with one viewer, one path, all six rungs: - step_canvas_protocol.gd: StepCanvasRequest/Response codec against the T-1181 wire contract — incl. the discovered png_bytes subtlety (rmp_serde without serde_bytes emits a msgpack int-array, not bin; decode repacks via PackedByteArray before load_png_from_buffer) and the extent-echo rule (read the server-clamped extent, never assume the requested one). - step_canvas/ component: transport (six-rung ladder, cursor-anchored scroll steps, edge-scroll/WASD pan with re-request on edge crossing, hard reset-to-Global), RTT terrain layer (Image.set_pixel colorize per the c1 measured ruling, texture.update reuse on step-cross, NEAREST coarse / LINEAR fine per rung), unscaled screen-space annotation sibling (courses + settlement markers at literal px), in-memory LRU cache (Tier 1; T-1183 layers the disk tiers beneath), request lifecycle (pending retry, staleness gate, extent echo). - Full _canvas.scale retirement in the same change: the zoom-scaled canvas model, the _zs compensation family, select_rung / MAX_COVERAGE_M / compute_tile_grid, the orbital-mosaic-vs-window two-path split, _view_zoom/_canonical_fit_zoom — 10 source files deleted; their 14 test suites deleted with them (T-1157 dead-goldens rule; replacement visual-capture coverage is re-scoped T-1157). - Surviving surfaces kept per the ticket: atlas_window_cache.gd's LRU shape (the ticket's named file atlas_window_tile_set.gd was the retiring orchestrator; the real LRU shape lives in atlas_window_cache.gd — cited in step_canvas_cache.gd), overlay colors, legend/overlay-bar chrome, AtlasViewer descend geometry. Determinism boundary per D-255(e): the client interpolates only within the closed server-supplied input set. 7 new gdUnit suites (164 cases) incl. a real extent-echo bug caught by its own test during implementation. Full client suite green (exit 0) with the live-gated suites running against a worktree server build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,12 @@ static func _amp():
|
||||
return load("res://scripts/protocol/atlas_map_protocol.gd")
|
||||
|
||||
|
||||
## StepCanvasRequest/StepCanvasResponse codec (T-1182, D-255(c)) — same
|
||||
## load()-by-path rationale as _bp()/_amp() above.
|
||||
static func _scp():
|
||||
return load("res://scripts/protocol/step_canvas_protocol.gd")
|
||||
|
||||
|
||||
# -- Decode: bytes from server → GDScript types --------------------------------
|
||||
|
||||
|
||||
@@ -874,15 +880,41 @@ static func decode_browse_response(bytes: PackedByteArray) -> Variant:
|
||||
return browse_response_from_raw(decode_raw(bytes))
|
||||
|
||||
|
||||
## Encode a StepCanvasRequest (T-1182, D-255(c)) — the stepped Atlas ladder's
|
||||
## data-canvas request. Delegates to step_canvas_protocol.gd — see that file
|
||||
## for the full wire-shape rationale (the sixth Inbound discriminator,
|
||||
## PNG-per-field decode note).
|
||||
static func encode_step_canvas_request(
|
||||
body_id: String, rung: String, center: Vector2i, extent: Vector2i, min_wl_m: int = 0
|
||||
) -> PackedByteArray:
|
||||
return _scp().encode_step_canvas_request(_mp(), body_id, rung, center, extent, min_wl_m)
|
||||
|
||||
|
||||
## Build a StepCanvasResponse from an already-decoded raw value. See
|
||||
## step_canvas_protocol.gd for the full field-by-field wire-shape rationale.
|
||||
static func step_canvas_response_from_raw(raw: Variant) -> Variant:
|
||||
return _scp().step_canvas_response_from_raw(raw)
|
||||
|
||||
|
||||
## Decode a StepCanvasResponse from MessagePack bytes. See
|
||||
## step_canvas_response_from_raw.
|
||||
static func decode_step_canvas_response(bytes: PackedByteArray) -> Variant:
|
||||
return step_canvas_response_from_raw(decode_raw(bytes))
|
||||
|
||||
|
||||
## Decode + classify one inbound frame (#960, D-225; T-949 adds starmap/
|
||||
## citynames; T-1131/T-1133 adds browse). Returns {kind, value}, kind one of
|
||||
## "snapshot"|"atlas"|"starmap"|"citynames"|"browse"|"unknown" — all msgpack
|
||||
## maps, told apart by field, most-specific-first: StarMapResponse is the
|
||||
## only kind with "data" and no "body_id"; CityNamesResponse the only one
|
||||
## with "cities"; BrowseResponse the only one with its OWN "kind" field
|
||||
## alongside "status"; anything else carrying "status" is AtlasLayerResponse.
|
||||
## Lets receive_bytes decode the frame ONCE instead of double-decoding the
|
||||
## 20 Hz snapshot path.
|
||||
## citynames; T-1131/T-1133 adds browse; T-1182 adds step_canvas). Returns
|
||||
## {kind, value}, kind one of
|
||||
## "snapshot"|"atlas"|"starmap"|"citynames"|"browse"|"step_canvas"|"unknown" —
|
||||
## all msgpack maps, told apart by field, most-specific-first: StarMapResponse
|
||||
## is the only kind with "data" and no "body_id"; CityNamesResponse the only
|
||||
## one with "cities"; BrowseResponse the only one with its OWN "kind" field
|
||||
## alongside "status"; StepCanvasResponse the only one with "rung" (checked
|
||||
## BEFORE the generic "status"-only AtlasLayerResponse fallback, since a
|
||||
## step-canvas response also carries "status" and would otherwise be
|
||||
## misrouted to "atlas"); anything else carrying "status" is
|
||||
## AtlasLayerResponse. Lets receive_bytes decode the frame ONCE instead of
|
||||
## double-decoding the 20 Hz snapshot path.
|
||||
static func decode_inbound(bytes: PackedByteArray) -> Dictionary:
|
||||
var raw = decode_raw(bytes)
|
||||
if not raw is Dictionary:
|
||||
@@ -895,6 +927,8 @@ static func decode_inbound(bytes: PackedByteArray) -> Dictionary:
|
||||
return {"kind": "citynames", "value": city_names_response_from_raw(raw)}
|
||||
if raw.has("kind") and raw.has("status"):
|
||||
return {"kind": "browse", "value": browse_response_from_raw(raw)}
|
||||
if raw.has("rung") and raw.has("status"):
|
||||
return {"kind": "step_canvas", "value": step_canvas_response_from_raw(raw)}
|
||||
if raw.has("status"):
|
||||
return {"kind": "atlas", "value": atlas_response_from_raw(raw)}
|
||||
return {"kind": "snapshot", "value": _decode_snapshot_from_raw(raw)}
|
||||
|
||||
Reference in New Issue
Block a user