feat(simulation): demux inbound bridge stream — receive() + atlas routing (#969, D-225)
Replaces the fixed-type SimBridge::receive_inputs() with a tagged receive() -> Option<Inbound>, where Inbound is Inputs(Vec<PlayerInput>) or AtlasRequest(AtlasLayerRequest). A shared decode_inbound() demuxes a frame by shape (msgpack array = inputs, map = atlas request) — additive, no wire change to existing input/snapshot frames. Adds send_atlas_response() to the trait (both TcpBridge + LocalBridge impls). receive_bridge_inputs routes inputs to the InputQueue as before; atlas requests to a new AtlasRequestBuffer (drained by the serve system next). Integration tests (bridge_tcp/bridge_ipc) updated to the tagged receive(); a demux unit test covers all three branches. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
use settled_reach_server::bridge::framing::{read_framed, write_framed};
|
||||
use settled_reach_server::bridge::local::LocalBridge;
|
||||
use settled_reach_server::bridge::types::*;
|
||||
use settled_reach_server::bridge::SimBridge;
|
||||
use settled_reach_server::bridge::{Inbound, SimBridge};
|
||||
use settled_reach_server::simulation::time::{DayPhase, TickRate};
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::path::PathBuf;
|
||||
@@ -116,7 +116,10 @@ fn input_roundtrip_over_unix_socket() {
|
||||
let server_handle = thread::spawn(move || {
|
||||
let bridge = LocalBridge::accept(&server_path).expect("failed to accept");
|
||||
|
||||
let inputs = bridge.receive_inputs().expect("failed to receive inputs");
|
||||
let inputs = match bridge.receive().expect("failed to receive") {
|
||||
Some(Inbound::Inputs(inputs)) => inputs,
|
||||
other => panic!("expected inputs, got {other:?}"),
|
||||
};
|
||||
|
||||
assert_eq!(inputs.len(), 2);
|
||||
assert_eq!(inputs[0].tick, 10);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use settled_reach_server::bridge::framing::{read_framed, write_framed};
|
||||
use settled_reach_server::bridge::tcp::TcpBridge;
|
||||
use settled_reach_server::bridge::types::*;
|
||||
use settled_reach_server::bridge::SimBridge;
|
||||
use settled_reach_server::bridge::{Inbound, SimBridge};
|
||||
use settled_reach_server::simulation::time::{DayPhase, TickRate};
|
||||
use std::net::{TcpListener, TcpStream};
|
||||
use std::thread;
|
||||
@@ -101,8 +101,8 @@ fn input_roundtrip_over_tcp() {
|
||||
// Non-blocking socket: retry until data arrives or timeout.
|
||||
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(5);
|
||||
let inputs = loop {
|
||||
match bridge.receive_inputs() {
|
||||
Ok(inputs) if !inputs.is_empty() => break inputs,
|
||||
match bridge.receive() {
|
||||
Ok(Some(Inbound::Inputs(inputs))) if !inputs.is_empty() => break inputs,
|
||||
Ok(_) => {
|
||||
assert!(
|
||||
std::time::Instant::now() < deadline,
|
||||
@@ -168,16 +168,16 @@ fn tcp_bridge_eof_returns_error() {
|
||||
// Non-blocking socket: retry until we get Disconnected or timeout.
|
||||
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(5);
|
||||
loop {
|
||||
match bridge.receive_inputs() {
|
||||
Ok(inputs) if inputs.is_empty() => {
|
||||
// WouldBlock — client hasn't disconnected yet, retry
|
||||
match bridge.receive() {
|
||||
Ok(None) => {
|
||||
// No data yet — client hasn't disconnected, retry
|
||||
assert!(
|
||||
std::time::Instant::now() < deadline,
|
||||
"timed out waiting for EOF"
|
||||
);
|
||||
thread::sleep(std::time::Duration::from_millis(1));
|
||||
}
|
||||
Ok(inputs) => panic!("expected Disconnected error, got {} inputs", inputs.len()),
|
||||
Ok(Some(_)) => panic!("expected Disconnected error, got a message"),
|
||||
Err(settled_reach_server::bridge::BridgeError::Disconnected) => break,
|
||||
Err(e) => panic!("expected Disconnected error, got: {}", e),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user