feat(simulation): run Layer-3 settlement placement in the runtime cascade (#955)
Wire the existing D-211 attractor-matching engine into the live generation cascade so settlements are placed in-game, not just in tests. - CityContextReader::read_body_settlements reads a body's settlements from atlas_city_names (ordered by id for determinism). NULL settlement_class defaults to PopulationBudget, not NameLocked: the class is NULL until placement runs, and NameLocked would force every settlement Tier-A in match_cities and collapse population tiering (D-211). NULL economic_role falls back to residential. - The AnalyzeBody work item carries the body's Vec<CityRecord>, and run_work_item now runs up_to Settlement (was Topography). A body with no settlements yields empty placements at negligible cost. - The atlas layer proxy reads settlements on a cache miss and pins them onto the work item, keeping the Rayon task DB-free (D-225). A read failure is non-fatal: log and place no cities (Layer 1 still runs). Threaded through a new CityContextReaderResource Bevy resource opened in main.rs, mirroring BodySourceResolverResource. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ use bevy_ecs::prelude::*;
|
||||
use bevy_ecs::schedule::IntoScheduleConfigs;
|
||||
|
||||
use crate::atlas::body_world_state::{BodyWorldStateCache, CACHE_CAPACITY};
|
||||
use crate::atlas::city_context_reader::CityContextReaderResource;
|
||||
use crate::atlas::gen_queue::{GenCompletion, GenerationQueue};
|
||||
use crate::atlas::layer_proxy::{handle_atlas_request, AtlasLayerResponse, AtlasLayerStatus};
|
||||
use crate::atlas::source_resolver::BodySourceResolverResource;
|
||||
@@ -44,6 +45,7 @@ fn serve_atlas_requests(
|
||||
mut cache: ResMut<BodyWorldStateCache>,
|
||||
queue: Res<GenerationQueue>,
|
||||
resolver: Option<Res<BodySourceResolverResource>>,
|
||||
city_reader: Option<Res<CityContextReaderResource>>,
|
||||
rng: Option<Res<SimRng>>,
|
||||
time: Option<Res<SimulationTime>>,
|
||||
) {
|
||||
@@ -52,10 +54,13 @@ fn serve_atlas_requests(
|
||||
}
|
||||
let world_seed = rng.as_ref().map(|r| r.seed()).unwrap_or(0);
|
||||
let tick = time.as_ref().map(|t| t.tick).unwrap_or(0);
|
||||
let reader = city_reader.as_ref().map(|r| &r.0);
|
||||
let pending: Vec<_> = requests.0.drain(..).collect();
|
||||
for req in pending {
|
||||
let resp = match resolver.as_ref() {
|
||||
Some(r) => handle_atlas_request(&req, &mut cache, &queue, &r.0, world_seed, tick),
|
||||
Some(r) => {
|
||||
handle_atlas_request(&req, &mut cache, &queue, &r.0, reader, world_seed, tick)
|
||||
}
|
||||
None => AtlasLayerResponse {
|
||||
body_id: req.body_id.clone(),
|
||||
status: AtlasLayerStatus::Error("no body source resolver".to_string()),
|
||||
@@ -155,6 +160,7 @@ mod tests {
|
||||
heightmap_path: test_heightmap_path(),
|
||||
sea_level: 0.3,
|
||||
body_seed: SeedChain::for_body(42, "PlanetX"),
|
||||
cities: vec![],
|
||||
},
|
||||
GenPriority::Immediate,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user