feat(client): wire SimBridge to Protocol codec
SimBridge now encodes player inputs via Protocol.encode_player_input() and exposes receive_bytes()/drain_outbound() for the transport layer. Test mode still works unchanged. Transport (ticket #79) will call these methods to complete the IPC pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,8 @@ enum ConnectionState { DISCONNECTED, CONNECTING, CONNECTED, ERROR }
|
||||
var state: ConnectionState = ConnectionState.DISCONNECTED
|
||||
var test_mode: bool = true # Enable test mode for development without Rust server
|
||||
var _test_tick: int = 0
|
||||
var _last_snapshot: Variant = null # Most recent decoded snapshot (consumed by poll_snapshot)
|
||||
var _outbound_buffer: Array[PackedByteArray] = [] # Encoded inputs awaiting transport
|
||||
|
||||
# Signals
|
||||
signal connection_state_changed(old_state: ConnectionState, new_state: ConnectionState)
|
||||
@@ -35,14 +37,22 @@ func connect_to_sim() -> void:
|
||||
func disconnect_from_sim() -> void:
|
||||
_set_state(ConnectionState.DISCONNECTED)
|
||||
|
||||
# Send input to simulation (real implementation comes later)
|
||||
# Send input to simulation server.
|
||||
# player_input: Dictionary with "action" (int from InputMapper.Action enum) and "timestamp_msec".
|
||||
# In test mode, inputs are silently dropped. Transport (ticket #79) will send the bytes.
|
||||
func send_input(player_input: Dictionary) -> void:
|
||||
if state != ConnectionState.CONNECTED:
|
||||
return
|
||||
# TODO: Serialize and send via MessagePack when IPC is implemented
|
||||
pass
|
||||
if test_mode:
|
||||
return
|
||||
var action_name := _action_enum_to_wire(player_input.get("action", -1))
|
||||
if action_name.is_empty():
|
||||
return
|
||||
var tick: int = player_input.get("timestamp_msec", 0)
|
||||
_outbound_buffer.append(Protocol.encode_player_input(tick, action_name))
|
||||
|
||||
# Poll for snapshot from simulation
|
||||
# Poll for snapshot from simulation.
|
||||
# In test mode returns hardcoded data. In live mode, returns the last decoded snapshot (if any).
|
||||
func poll_snapshot() -> Variant:
|
||||
if state != ConnectionState.CONNECTED:
|
||||
return null
|
||||
@@ -52,9 +62,40 @@ func poll_snapshot() -> Variant:
|
||||
snapshot_received.emit(snapshot)
|
||||
return snapshot
|
||||
|
||||
# TODO: Actual polling logic when IPC/MessagePack is implemented
|
||||
if _last_snapshot != null:
|
||||
var snapshot = _last_snapshot
|
||||
_last_snapshot = null
|
||||
snapshot_received.emit(snapshot)
|
||||
return snapshot
|
||||
|
||||
return null
|
||||
|
||||
# Called by transport layer (ticket #79) when raw bytes arrive from the server.
|
||||
func receive_bytes(bytes: PackedByteArray) -> void:
|
||||
var snapshot = Protocol.decode_snapshot(bytes)
|
||||
if snapshot != null:
|
||||
_last_snapshot = snapshot
|
||||
|
||||
# Drain the outbound buffer. Called by transport layer (ticket #79) to get encoded messages.
|
||||
func drain_outbound() -> Array[PackedByteArray]:
|
||||
var messages = _outbound_buffer.duplicate()
|
||||
_outbound_buffer.clear()
|
||||
return messages
|
||||
|
||||
# Map InputMapper.Action enum values to wire-format action names (matching Rust PlayerAction).
|
||||
static func _action_enum_to_wire(action: int) -> String:
|
||||
match action:
|
||||
0: return "MoveNorth" # InputMapper.Action.MOVE_NORTH
|
||||
1: return "MoveSouth" # InputMapper.Action.MOVE_SOUTH
|
||||
2: return "MoveEast" # InputMapper.Action.MOVE_EAST
|
||||
3: return "MoveWest" # InputMapper.Action.MOVE_WEST
|
||||
4: return "Interact" # InputMapper.Action.INTERACT
|
||||
5: return "UsePerceptionMode" # InputMapper.Action.USE_PERCEPTION_MODE
|
||||
7: return "Pause" # InputMapper.Action.PAUSE
|
||||
_:
|
||||
push_warning("SimBridge: unknown action enum %s" % action)
|
||||
return ""
|
||||
|
||||
# Hardcoded test snapshot for development (deterministic per D-010 principle 4)
|
||||
func _test_snapshot() -> Dictionary:
|
||||
_test_tick += 1
|
||||
|
||||
Reference in New Issue
Block a user