feat(simulation): feature-name pipeline wired + legacy window_granularity u32 retired (T-1169, T-1159)
One commit for two tickets whose changes share the bridge/plugin plumbing files. T-1169 connects the three dormant feature-name pieces: atlas_feature_names populated at regen (17,891 rows — 15,190 mountain, 2,701 river — via populate_atlas_feature_names mirroring the city-names importer; systems.db regenerated, stamp fresh), attach_feature_names wired into the cascade's Topography block with name pools threaded DB-free through AnalyzeBody (D-225 pattern) and assignments stored on Layer1Output/BodyWorldState for future consumers, and a FeatureNamesRequest/Response read proxy as the bridge's 7th tagged envelope (D-236 pattern, both SimBridge impls). Client label DRAW is deliberately NOT here — implementation proved both river and mountain labels need a wire-carried position (the pool is position-free; course polylines aren't correlated with the named attractors by construction) — deferred to T-1195's single design pass. cascade_layer1 golden re-pinned (additive feature_names field). T-1159 retires the legacy u32 granularity field fully shadowed by window_granularity_v2: AtlasLayerRequest.window_granularity, DistrictWindowLayer.granularity echo, the u32::MAX sentinel, and resolve_window_granularity are gone server-side; client encode paths and the caller-less atlas_window_cache legacy key component dropped; msgpack fixtures regenerated; the T-1150 aliasing regression test now drives through the surviving enum field. The district_window carrier itself survives byte-compatible per D-255(c). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+30
-12
@@ -342,21 +342,23 @@ fn single_tick_drains_all_ready_inbound_frames() {
|
||||
use settled_reach_server::atlas::layer_proxy::AtlasLayerRequest;
|
||||
use settled_reach_server::bridge::{
|
||||
receive_bridge_inputs, AtlasRequestBuffer, BridgeResource, BrowseRequestBuffer,
|
||||
CityNamesRequestBuffer, HandshakeState, ServerRunning, StarMapRequestBuffer,
|
||||
StepCanvasRequestBuffer,
|
||||
CityNamesRequestBuffer, FeatureNamesRequestBuffer, HandshakeState, ServerRunning,
|
||||
StarMapRequestBuffer, StepCanvasRequestBuffer,
|
||||
};
|
||||
use settled_reach_server::simulation::input::InputQueue;
|
||||
|
||||
let listener = TcpListener::bind("127.0.0.1:0").expect("failed to bind");
|
||||
let server_addr = listener.local_addr().expect("failed to get local address");
|
||||
|
||||
// Client: five frames back-to-back in one tick window — two input
|
||||
// batches plus one of EACH request shape (atlas, star-map, city-names:
|
||||
// the full D-225/T-949 demux surface over the real framing/poll path —
|
||||
// PR #176 review H6). Returns the stream so it stays open until
|
||||
// assertions complete (no EOF race).
|
||||
// Client: six frames back-to-back in one tick window — two input
|
||||
// batches plus one of EACH request shape (atlas, star-map, city-names,
|
||||
// feature-names: the full D-225/T-949/T-1169 demux surface over the real
|
||||
// framing/poll path — PR #176 review H6). Returns the stream so it stays
|
||||
// open until assertions complete (no EOF race).
|
||||
let client_handle = thread::spawn(move || {
|
||||
use settled_reach_server::atlas::atlas_data_proxy::{CityNamesRequest, StarMapRequest};
|
||||
use settled_reach_server::atlas::atlas_data_proxy::{
|
||||
CityNamesRequest, FeatureNamesRequest, StarMapRequest,
|
||||
};
|
||||
|
||||
let mut stream = TcpStream::connect(server_addr).expect("failed to connect");
|
||||
for tick in [20u64, 21] {
|
||||
@@ -372,7 +374,6 @@ fn single_tick_drains_all_ready_inbound_frames() {
|
||||
up_to: CascadeLayer::Topography,
|
||||
window_center: None,
|
||||
window_n: 0,
|
||||
window_granularity: 0,
|
||||
window_granularity_v2: None,
|
||||
window_min_wl_m: 0,
|
||||
};
|
||||
@@ -387,6 +388,13 @@ fn single_tick_drains_all_ready_inbound_frames() {
|
||||
};
|
||||
let payload = rmp_serde::to_vec_named(&cn).expect("failed to serialize city names");
|
||||
write_framed(&mut stream, &payload).expect("write city names frame");
|
||||
let fn_req = FeatureNamesRequest {
|
||||
feature_names: true,
|
||||
body_id: "GJ1c".into(),
|
||||
};
|
||||
let payload =
|
||||
rmp_serde::to_vec_named(&fn_req).expect("failed to serialize feature names");
|
||||
write_framed(&mut stream, &payload).expect("write feature names frame");
|
||||
stream
|
||||
});
|
||||
|
||||
@@ -406,6 +414,7 @@ fn single_tick_drains_all_ready_inbound_frames() {
|
||||
world.init_resource::<CityNamesRequestBuffer>();
|
||||
world.init_resource::<BrowseRequestBuffer>();
|
||||
world.init_resource::<StepCanvasRequestBuffer>();
|
||||
world.init_resource::<FeatureNamesRequestBuffer>();
|
||||
|
||||
world
|
||||
.run_system_once(receive_bridge_inputs)
|
||||
@@ -433,6 +442,13 @@ fn single_tick_drains_all_ready_inbound_frames() {
|
||||
"the city-names request must drain in the same tick (H6: real wire path)"
|
||||
);
|
||||
assert_eq!(city_names[0].1.body_id, "GJ1c");
|
||||
let feature_names = &world.resource::<FeatureNamesRequestBuffer>().0;
|
||||
assert_eq!(
|
||||
feature_names.len(),
|
||||
1,
|
||||
"the feature-names request must drain in the same tick (T-1169: real wire path)"
|
||||
);
|
||||
assert_eq!(feature_names[0].1.body_id, "GJ1c");
|
||||
assert!(
|
||||
world.resource::<ServerRunning>().0,
|
||||
"draining must not shut the server down"
|
||||
@@ -502,8 +518,9 @@ fn new_multi_connection_world() -> (bevy_ecs::world::World, std::net::SocketAddr
|
||||
use settled_reach_server::bridge::{
|
||||
AtlasRequestBuffer, AtlasResponseBuffer, BridgeResource, BrowseRequestBuffer,
|
||||
BrowseResponseBuffer, CityNamesRequestBuffer, CityNamesResponseBuffer, ConnectionListener,
|
||||
HandshakeState, PendingConnections, ServerRunning, SnapshotBuffer, StarMapRequestBuffer,
|
||||
StarMapResponseBuffer, StepCanvasRequestBuffer, StepCanvasResponseBuffer,
|
||||
FeatureNamesRequestBuffer, FeatureNamesResponseBuffer, HandshakeState, PendingConnections,
|
||||
ServerRunning, SnapshotBuffer, StarMapRequestBuffer, StarMapResponseBuffer,
|
||||
StepCanvasRequestBuffer, StepCanvasResponseBuffer,
|
||||
};
|
||||
use settled_reach_server::simulation::input::InputQueue;
|
||||
|
||||
@@ -531,6 +548,8 @@ fn new_multi_connection_world() -> (bevy_ecs::world::World, std::net::SocketAddr
|
||||
world.init_resource::<BrowseResponseBuffer>();
|
||||
world.init_resource::<StepCanvasRequestBuffer>();
|
||||
world.init_resource::<StepCanvasResponseBuffer>();
|
||||
world.init_resource::<FeatureNamesRequestBuffer>();
|
||||
world.init_resource::<FeatureNamesResponseBuffer>();
|
||||
world.init_resource::<SnapshotBuffer>();
|
||||
(world, addr)
|
||||
}
|
||||
@@ -1515,7 +1534,6 @@ mod connection_zero_window_delivery {
|
||||
up_to: CascadeLayer::Topography,
|
||||
window_center: Some(center),
|
||||
window_n: 4,
|
||||
window_granularity: 0,
|
||||
window_granularity_v2: Some(WindowGranularity::Region),
|
||||
window_min_wl_m: 0,
|
||||
}
|
||||
|
||||
@@ -112,6 +112,14 @@
|
||||
//! additive at the position level, exactly as designed. No duplicate
|
||||
//! `(row, col)` positions exist in the final array (verified — the
|
||||
//! `edge_ids_are_unique` invariant `build_edges` depends on holds).
|
||||
//!
|
||||
//! **Fourth deliberate re-pin (T-1169, D-223):** `Layer1Output` gained an
|
||||
//! additive `feature_names: Vec<FeatureNameAssignment>` field (river-mouth/
|
||||
//! alpine-peak name assignments from the new `attach_feature_names` cascade
|
||||
//! wiring). This fixture calls `run_cascade_from_heightmap` with empty
|
||||
//! `river_names`/`mountain_names` pools (no DB access from this test), so
|
||||
//! the field serializes as `[]` — a pure additive-shape change, zero effect
|
||||
//! on every pre-existing field.
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -149,8 +157,16 @@ fn cascade_layer0_to_1_matches_golden() {
|
||||
// ── Layer 1 — downsample, then run the cascade to topography. ───────────
|
||||
let small = heightmap.downsample(DOWNSAMPLE.0, DOWNSAMPLE.1);
|
||||
let body_seed = SeedChain::for_body(WORLD_SEED, "GJ1c");
|
||||
let snapshot =
|
||||
run_cascade_from_heightmap(body_seed, small, &[], None, None, CascadeLayer::Topography);
|
||||
let snapshot = run_cascade_from_heightmap(
|
||||
body_seed,
|
||||
small,
|
||||
&[],
|
||||
None,
|
||||
None,
|
||||
&[],
|
||||
&[],
|
||||
CascadeLayer::Topography,
|
||||
);
|
||||
let layer1 = snapshot.layer1.expect("Layer 1 ran");
|
||||
|
||||
let actual = json!({
|
||||
|
||||
@@ -7,7 +7,6 @@ use settled_reach_server::atlas::layer_proxy::{
|
||||
AtlasLayerResponse, AtlasLayerStatus, DistrictWindowLayer, QuarterFootprintEntry,
|
||||
QuarterFootprintLayer, RegionGridLayer, RoadGraphEdge, RoadGraphLayer, RoadGraphNode,
|
||||
SettlementEntry, SettlementLayer, SettlementSizeClass, WindowGranularity, REGION_TEMP_NONE_DC,
|
||||
WINDOW_GRANULARITY_DISTRICT,
|
||||
};
|
||||
use settled_reach_server::atlas::region_profile::{SeasonPhase, WeatherState};
|
||||
use settled_reach_server::atlas::road_graph::RoadNodeKind;
|
||||
@@ -579,6 +578,7 @@ fn generate_atlas_layer_response_fixtures() {
|
||||
grid_w: 512,
|
||||
grid_h: 256,
|
||||
survey_basin_dirs: std::collections::BTreeMap::new(),
|
||||
feature_names: vec![],
|
||||
};
|
||||
// T-960 §1/§2: a small populated RoadGraphLayer + SettlementLayer, one
|
||||
// settlement (a capital) connected to one waypoint-free short edge.
|
||||
@@ -729,7 +729,6 @@ fn generate_atlas_layer_response_fixtures() {
|
||||
let window = DistrictWindowLayer {
|
||||
center: (10, -5),
|
||||
n: 2,
|
||||
granularity: WINDOW_GRANULARITY_DISTRICT,
|
||||
granularity_v2: WindowGranularity::District,
|
||||
min_wl_m: 0,
|
||||
morphology: vec![0, 8, 14, 16], // OpenOcean, AlluvialPlain, Alpine, Wetland
|
||||
|
||||
@@ -7761,6 +7761,7 @@
|
||||
"territorial_status": "FrontierUnclaimed"
|
||||
}
|
||||
],
|
||||
"feature_names": [],
|
||||
"grid_h": 128,
|
||||
"grid_w": 256,
|
||||
"river_network": {
|
||||
|
||||
Reference in New Issue
Block a user