feat(engine): T-1131 browse data proxy — six entity kinds, index+detail, one wire envelope (D-254 SS4)
browse_reader.rs: BrowseReader on the CityContextReader::open() pattern
— six index + six detail reads against systems.db (bodies filterable by
containing system; system/corporation/commodity details fold their join
partners). browse_proxy.rs: BrowseRequest{browse, kind, query} /
BrowseResponse{kind, status, index, detail} wire types + dispatcher;
BrowseIndexRow{id, primary, secondary} generic across kinds;
BrowseDetail a per-kind enum of field-exhaustive structs.
Demux: Inbound::BrowseRequest is the FIFTH map shape — deliberately the
last; the doc's four-shape ceiling is re-pinned at five with rationale
(six kinds x two forms folded into ONE envelope whose internal enums
pick sub-behavior, the AtlasLayerRequest.up_to precedent, instead of
twelve top-level shapes) and a hard rule that a sixth shape must
migrate to the D-225 tagged-envelope framing. Served for BOTH roles,
connection-tagged 1:1 in-order like atlas/starmap/citynames.
serve_browse_requests pub so integration tests drive the true
end-to-end pipeline. Wire-only per D-254 (T-949 precedent); v1
exclusions (cascade geometry, event logs) respected.
30 unit tests + 7 bridge_tcp integration tests (six-kind round-trip
over real TCP, reader-can-browse, no crossed responses between two
readers, unknown-id NotFound, empty-table Ready); 2 pre-existing tests
updated for the new receive_bridge_inputs parameter.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,8 @@ use crate::atlas::atlas_data_proxy::{
|
||||
use crate::atlas::attractor_matching::CityPlacement;
|
||||
use crate::atlas::body_params_reader::BodyParamsReaderResource;
|
||||
use crate::atlas::body_world_state::{BodyWorldStateCache, CACHE_CAPACITY};
|
||||
use crate::atlas::browse_proxy::handle_browse_request;
|
||||
use crate::atlas::browse_reader::BrowseReaderResource;
|
||||
use crate::atlas::city_context_reader::{
|
||||
context_from_read_set, CityContextReaderResource, CityEconomicReadSet,
|
||||
};
|
||||
@@ -41,8 +43,8 @@ use crate::atlas::trait_swerve::{
|
||||
build_swerve_pools, compute_swerve_rates, SwerveDrivers, SwervePools,
|
||||
};
|
||||
use crate::bridge::{
|
||||
AtlasRequestBuffer, AtlasResponseBuffer, CityNamesRequestBuffer, CityNamesResponseBuffer,
|
||||
StarMapRequestBuffer, StarMapResponseBuffer,
|
||||
AtlasRequestBuffer, AtlasResponseBuffer, BrowseRequestBuffer, BrowseResponseBuffer,
|
||||
CityNamesRequestBuffer, CityNamesResponseBuffer, StarMapRequestBuffer, StarMapResponseBuffer,
|
||||
};
|
||||
use crate::seed::{SeedChain, SeedDomain};
|
||||
use crate::simulation::generator::{
|
||||
@@ -68,7 +70,8 @@ impl Plugin for GenerationPlugin {
|
||||
.add_systems(
|
||||
Update,
|
||||
serve_city_names_requests.in_set(TickPhase::PreInput),
|
||||
);
|
||||
)
|
||||
.add_systems(Update, serve_browse_requests.in_set(TickPhase::PreInput));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,6 +173,34 @@ fn serve_city_names_requests(
|
||||
}
|
||||
}
|
||||
|
||||
/// Drain inbound data-browser requests and serve each through the proxy
|
||||
/// (D-254 §4, T-1131): one of the six v1 registry-tier entity kinds, dispatched
|
||||
/// to `BrowseReader` by `(kind, query)`.
|
||||
///
|
||||
/// `pub` (unlike its atlas/star-map/city-names siblings, which stay private)
|
||||
/// so `server/tests/bridge_tcp.rs`'s browse integration tests can drive the
|
||||
/// TRUE full pipeline (demux -> receive_bridge_inputs -> BrowseRequestBuffer
|
||||
/// -> serve_browse_requests -> BrowseResponseBuffer -> send_browse_responses)
|
||||
/// end-to-end via `RunSystemOnce`, rather than bypassing this system the way
|
||||
/// `reader_receives_tagged_star_map_response` bypasses `serve_star_map_requests`
|
||||
/// (see that test's own doc comment) because it has no way to call it.
|
||||
pub fn serve_browse_requests(
|
||||
mut requests: ResMut<BrowseRequestBuffer>,
|
||||
mut responses: ResMut<BrowseResponseBuffer>,
|
||||
browse_reader: Option<Res<BrowseReaderResource>>,
|
||||
) {
|
||||
if requests.0.is_empty() {
|
||||
return;
|
||||
}
|
||||
let reader = browse_reader.as_ref().map(|r| &r.0);
|
||||
let pending: Vec<_> = requests.0.drain(..).collect();
|
||||
for (conn_id, req) in pending {
|
||||
responses
|
||||
.0
|
||||
.push((conn_id, handle_browse_request(&req, reader)));
|
||||
}
|
||||
}
|
||||
|
||||
/// Drain finished background work each tick and apply it to the cache (D-206).
|
||||
///
|
||||
/// Runs in `PreInput` (off the Rayon workers, on the main thread): a cheap
|
||||
|
||||
Reference in New Issue
Block a user