feat(client): atlas request/response transport on the bridge (#960, D-225)
Client transport half of the layer-stream protocol.
- SimBridge.request_atlas_layers(body_id) sends an AtlasLayerRequest frame
(live mode only; no-op in test mode); responses arrive via a new
atlas_layers_received signal.
- receive_bytes now decodes each frame ONCE via Protocol.decode_inbound and
branches by shape (snapshot vs atlas response) — avoids double-decoding the
20 Hz snapshot path. decode_snapshot is split into decode_raw +
_decode_snapshot_from_raw (public decode_snapshot unchanged, so the 70 protocol
tests stay the regression guard); decode_inbound returns {kind, value}.
70/70 protocol tests pass, including the new decode_inbound classifier test.
(Pre-existing client-suite failures in server-dependent e2e/roundtrip + unrelated
audio/fog/dialogue suites are unchanged — verified identical at baseline.)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,17 +15,31 @@ static func _mp():
|
||||
# -- Decode: bytes from server → GDScript types --------------------------------
|
||||
|
||||
|
||||
## Decode a raw MessagePack frame to its top-level value (or null on error).
|
||||
## #960: lets receive_bytes decode a frame once and branch by shape before
|
||||
## committing to the heavier snapshot decode.
|
||||
static func decode_raw(bytes: PackedByteArray) -> Variant:
|
||||
var result = _mp().decode(bytes)
|
||||
if result.status != null:
|
||||
push_error("Protocol: msgpack decode failed: %s" % result.status)
|
||||
return null
|
||||
return result.value
|
||||
|
||||
|
||||
## Decode an ObserverSnapshot from MessagePack bytes.
|
||||
## Returns decoded snapshot Dictionary or null on error.
|
||||
## v2 fields (version, game_time, player_facing, visible_tiles) default to null/empty
|
||||
## when decoding v1 snapshots for backward compatibility.
|
||||
static func decode_snapshot(bytes: PackedByteArray) -> Variant:
|
||||
var result = _mp().decode(bytes)
|
||||
if result.status != null:
|
||||
push_error("Protocol: msgpack decode failed: %s" % result.status)
|
||||
var raw = decode_raw(bytes)
|
||||
if raw == null:
|
||||
return null
|
||||
return _decode_snapshot_from_raw(raw)
|
||||
|
||||
var raw = result.value
|
||||
|
||||
## Build an ObserverSnapshot from an already-decoded raw value (the snapshot
|
||||
## body, shared by decode_snapshot and the receive-side classifier).
|
||||
static func _decode_snapshot_from_raw(raw: Variant) -> Variant:
|
||||
if not raw is Dictionary or not raw.has("tick") or not raw.has("entities"):
|
||||
push_error("Protocol: snapshot missing required fields")
|
||||
return null
|
||||
@@ -745,10 +759,12 @@ static func encode_atlas_layer_request(body_id: String, up_to: String = "Topogra
|
||||
## 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
|
||||
return atlas_response_from_raw(decode_raw(bytes))
|
||||
|
||||
|
||||
## Build an AtlasLayerResponse from an already-decoded raw value. Returns null
|
||||
## if it is not an atlas response (no "status" key).
|
||||
static func atlas_response_from_raw(raw: Variant) -> Variant:
|
||||
if not raw is Dictionary or not raw.has("status"):
|
||||
return null
|
||||
var status_raw = raw["status"]
|
||||
@@ -767,6 +783,20 @@ static func decode_atlas_layer_response(bytes: PackedByteArray) -> Variant:
|
||||
}
|
||||
|
||||
|
||||
## Decode + classify one inbound frame (#960, D-225). Returns {kind, value} with
|
||||
## kind "snapshot" | "atlas" | "unknown" — both are msgpack maps, so they are
|
||||
## told apart by field (a response has "status"; a snapshot has "entities").
|
||||
## Lets receive_bytes decode the frame ONCE and branch, 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:
|
||||
return {"kind": "unknown", "value": null}
|
||||
if raw.has("status") and not raw.has("entities"):
|
||||
return {"kind": "atlas", "value": atlas_response_from_raw(raw)}
|
||||
return {"kind": "snapshot", "value": _decode_snapshot_from_raw(raw)}
|
||||
|
||||
|
||||
## 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:
|
||||
|
||||
Reference in New Issue
Block a user