feat(client): protocol v6 bridge — stance, inventory, input mapping
Upgrade client protocol bridge from v5 to v6 to match server. Adds player_stance (4 variants) and player_inventory decode to ObserverSnapshot. Adds TOGGLE_STANCE_UP/DOWN to InputMapper. Includes 25 gdUnit4 tests for v6 decode + server serialization test gap fix. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,10 @@ var nearby_interactions: Array = [] # [{entity_id, entity_type, distance, verbs
|
||||
# v5 fields (#414)
|
||||
var current_monologue: Variant = null # {id, text, duration_seconds} or null
|
||||
|
||||
# v6 fields (#449, D-053, D-065)
|
||||
var player_stance: String = "Walk" # Sprint/Walk/Careful/Crouch
|
||||
var player_inventory: Array = [] # [{item_id, name, slot}]
|
||||
|
||||
func apply_snapshot(snapshot: Dictionary) -> void:
|
||||
current_snapshot = snapshot
|
||||
|
||||
@@ -82,6 +86,16 @@ func apply_snapshot(snapshot: Dictionary) -> void:
|
||||
else:
|
||||
current_monologue = null
|
||||
|
||||
# v6: player_stance (#449, D-053)
|
||||
if snapshot.has("player_stance") and snapshot.player_stance is String:
|
||||
player_stance = snapshot.player_stance
|
||||
|
||||
# v6: player_inventory (#449, D-065)
|
||||
if snapshot.has("player_inventory") and snapshot.player_inventory is Array:
|
||||
player_inventory = snapshot.player_inventory
|
||||
else:
|
||||
player_inventory = []
|
||||
|
||||
# v2: visible_tiles with visibility sectors
|
||||
# Derives visible_positions when not explicitly provided (real server mode)
|
||||
if snapshot.has("visible_tiles") and snapshot.visible_tiles is Array and snapshot.visible_tiles.size() > 0:
|
||||
|
||||
@@ -6,7 +6,8 @@ extends Node
|
||||
enum Action {
|
||||
MOVE_NORTH, MOVE_NORTHEAST, MOVE_EAST, MOVE_SOUTHEAST,
|
||||
MOVE_SOUTH, MOVE_SOUTHWEST, MOVE_WEST, MOVE_NORTHWEST,
|
||||
INTERACT, USE_PERCEPTION_MODE, OPEN_MENU, PAUSE
|
||||
INTERACT, USE_PERCEPTION_MODE, OPEN_MENU, PAUSE,
|
||||
TOGGLE_STANCE_UP, TOGGLE_STANCE_DOWN,
|
||||
}
|
||||
|
||||
var input_queue: Array[Dictionary] = []
|
||||
@@ -40,6 +41,10 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||
action = Action.OPEN_MENU
|
||||
elif event.is_action_pressed("pause"):
|
||||
action = Action.PAUSE
|
||||
elif event.is_action_pressed("stance_up"):
|
||||
action = Action.TOGGLE_STANCE_UP
|
||||
elif event.is_action_pressed("stance_down"):
|
||||
action = Action.TOGGLE_STANCE_DOWN
|
||||
|
||||
# Queue the action if valid
|
||||
if action != -1:
|
||||
|
||||
@@ -242,6 +242,8 @@ static func _action_enum_to_wire(action: int) -> String:
|
||||
InputMapper.Action.INTERACT: return "Interact"
|
||||
InputMapper.Action.USE_PERCEPTION_MODE: return "UsePerceptionMode"
|
||||
InputMapper.Action.PAUSE: return "Pause"
|
||||
InputMapper.Action.TOGGLE_STANCE_UP: return "ToggleStanceUp"
|
||||
InputMapper.Action.TOGGLE_STANCE_DOWN: return "ToggleStanceDown"
|
||||
InputMapper.Action.OPEN_MENU:
|
||||
# Client-only action, not part of wire protocol
|
||||
push_warning("SimBridge: OPEN_MENU is client-only, not sent to server")
|
||||
@@ -328,6 +330,8 @@ func _test_snapshot() -> Dictionary:
|
||||
"tick_rate": "Full",
|
||||
},
|
||||
"player_facing": _test_facing,
|
||||
"player_stance": "Walk",
|
||||
"player_inventory": [],
|
||||
"entities": entities,
|
||||
"tiles": _test_tiles(),
|
||||
"visible_tiles": _test_visible_tiles(),
|
||||
|
||||
@@ -11,7 +11,7 @@ class_name Protocol
|
||||
|
||||
## Protocol version — must match server PROTOCOL_VERSION in bridge/types.rs.
|
||||
## Reject snapshots where version != this value.
|
||||
const PROTOCOL_VERSION: int = 5
|
||||
const PROTOCOL_VERSION: int = 6
|
||||
|
||||
|
||||
# -- Decode: bytes from server → GDScript types --------------------------------
|
||||
@@ -104,6 +104,24 @@ static func decode_snapshot(bytes: PackedByteArray) -> Variant:
|
||||
"duration_seconds": float(raw_monologue.get("duration_seconds", 5.0)),
|
||||
}
|
||||
|
||||
# v6: player_stance (#449, D-053) — unit enum → bare string
|
||||
var player_stance: String = "Walk"
|
||||
var raw_stance: Variant = raw.get("player_stance")
|
||||
if raw_stance is String:
|
||||
player_stance = raw_stance
|
||||
|
||||
# v6: player_inventory (#449, D-065) — array of {item_id, name, slot}
|
||||
var player_inventory: Array = []
|
||||
var raw_inventory: Variant = raw.get("player_inventory")
|
||||
if raw_inventory is Array:
|
||||
for raw_item in raw_inventory:
|
||||
if raw_item is Dictionary and raw_item.has("item_id") and raw_item.has("name"):
|
||||
player_inventory.append({
|
||||
"item_id": int(raw_item["item_id"]),
|
||||
"name": str(raw_item["name"]),
|
||||
"slot": int(raw_item.get("slot", 0)),
|
||||
})
|
||||
|
||||
return {
|
||||
"tick": tick,
|
||||
"entities": entities,
|
||||
@@ -111,6 +129,8 @@ static func decode_snapshot(bytes: PackedByteArray) -> Variant:
|
||||
"version": version,
|
||||
"game_time": game_time,
|
||||
"player_facing": player_facing,
|
||||
"player_stance": player_stance,
|
||||
"player_inventory": player_inventory,
|
||||
"visible_tiles": visible_tiles,
|
||||
"nearby_interactions": nearby_interactions,
|
||||
"current_monologue": current_monologue,
|
||||
|
||||
Reference in New Issue
Block a user