feat(client): atlas layer-stream codec — encode request / decode response (#960, D-225)

Client half of the layer-stream protocol (codec only; transport wiring next).
- protocol.gd: encode_atlas_layer_request (bare {body_id, up_to} map so the
  server demux routes it to the proxy, not the PlayerInput array) and
  decode_atlas_layer_response (-> {body_id, status, error, layer1}; returns null
  for non-atlas frames, e.g. a snapshot, so receive_bytes can disambiguate).
- gen_fixtures.rs: cross-language fixtures (atlas_response_ready/pending/
  not_found) from real rmp_serde output, matching the test_protocol.gd pattern.
- test_protocol.gd: 5 tests decode the fixtures + verify a snapshot is not
  mistaken for a response + the request encodes to the right shape. 68/68 pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-24 18:25:41 +02:00
co-authored by Claude Opus 4.7
parent 1bece0b201
commit a9ba8ba90c
6 changed files with 154 additions and 0 deletions
+41
View File
@@ -726,6 +726,47 @@ static func encode_request_bookmark_catalog() -> PackedByteArray:
return result.value
## Encode an AtlasLayerRequest (#969, D-225) for the layer-stream proxy.
## A bare map {body_id, up_to} — NOT the Vec<PlayerInput> array — so the server's
## frame demux routes it to the atlas proxy. up_to is a CascadeLayer unit variant
## (bare string: "Heightmap" | "Topography").
static func encode_atlas_layer_request(body_id: String, up_to: String = "Topography") -> PackedByteArray:
var msg := {"body_id": body_id, "up_to": up_to}
var result = _mp().encode(msg)
if result.status != null:
push_error("Protocol: encode_atlas_layer_request failed: %s" % result.status)
return PackedByteArray()
return result.value
## Decode an AtlasLayerResponse (#969, D-225). Returns a Dictionary
## {body_id, status, error, layer1}, or null if the bytes are not an atlas
## response (no "status" key — e.g. an ObserverSnapshot). status is the variant
## name ("Ready"|"Pending"|"NotFound"|"Error"); error holds the message for the
## Error variant. layer1 is the raw decoded Layer1Output map, or null.
static func decode_atlas_layer_response(bytes: PackedByteArray) -> Variant:
var result = _mp().decode(bytes)
if result.status != null:
return null
var raw = result.value
if not raw is Dictionary or not raw.has("status"):
return null
var status_raw = raw["status"]
var status := ""
var error := ""
if status_raw is String:
status = status_raw
elif status_raw is Dictionary and status_raw.has("Error"):
status = "Error"
error = str(status_raw["Error"])
return {
"body_id": raw.get("body_id", ""),
"status": status,
"error": error,
"layer1": raw.get("layer1"),
}
## Encode a ConfirmBookmark action (#614, #680).
## Struct variant with bookmark_id and starting_location_id.
static func encode_confirm_bookmark(bookmark_id: String, starting_location_id: String) -> PackedByteArray: