Files
settled-reach/server/src/workers/systems.rs
T
jpmschweitzerandClaude Opus 4.6 5f678dac48 fix(simulation): address #843 review — seeding, atomics, API consistency
Review fixes from Hoshe + Tyre:
- EntityRng seeding: splitmix64(seed) ^ splitmix64(id) instead of
  splitmix64(seed + id) — eliminates collision class where adjacent
  seeds produce identical streams
- AtomicBool ordering: Relaxed → SeqCst for shutdown flag (correct
  on weakly-ordered architectures)
- Worker Drop: join handles instead of detaching threads
- Normalize stub API: remove ChunkGenWorker convenience wrappers,
  use .pool consistently across all 3 workers
- trigger_monologue: downgrade &mut to shared refs (no-op anchor
  was blocking parallel systems)
- Remove dead SimRng inserts from migrated monologue tests
- Document determinism gap on poll_worker_results
- Document bevy_tasks/rayon dep rationale in Cargo.toml

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 00:22:44 +02:00

69 lines
2.6 KiB
Rust

//! Tick-loop integration systems for background workers (#843 Part C).
//!
//! - `poll_worker_results`: runs in PreInput, drains completed results
//! - `push_worker_requests`: runs in PostSnapshot, queues new work
//!
//! Both are no-ops when no results/requests are pending. The worker pools
//! exist as Bevy Resources — systems access them via `Res<ChunkGenWorker>` etc.
use bevy_ecs::prelude::*;
use super::stubs::{ChunkGenWorker, NpcPrepWorker, OffscreenTickWorker};
/// PreInput: poll all worker pools for completed results.
///
/// Currently logs completions at debug level — real incorporation logic
/// (spawning chunks, activating NPCs) plugs in at Phase 5+.
///
/// **DETERMINISM GAP (D-010):** Results are incorporated at the tick they
/// happen to be ready, not at a deterministic tick. Two runs with the same
/// seed may incorporate a chunk at tick 500 vs tick 502 depending on CPU
/// load — diverging world state from that point.
///
/// This is acceptable while all handlers are no-ops (no simulation state
/// is modified). Before any handler produces real state changes, implement
/// a `WorkerResultLog` resource that records `(tick, worker_type, result)`.
/// Replay reads from the log instead of polling channels. See plan #843.
pub fn poll_worker_results(
chunk_worker: Option<Res<ChunkGenWorker>>,
npc_worker: Option<Res<NpcPrepWorker>>,
offscreen_worker: Option<Res<OffscreenTickWorker>>,
) {
if let Some(worker) = chunk_worker {
for (result, _strategy) in worker.pool.drain() {
tracing::debug!(
chunk_x = result.chunk_x,
chunk_y = result.chunk_y,
"ChunkGen result ready"
);
}
}
if let Some(worker) = npc_worker {
for (result, _strategy) in worker.pool.drain() {
tracing::debug!(area = %result.area_id, "NpcPrep result ready");
}
}
if let Some(worker) = offscreen_worker {
for (result, _strategy) in worker.pool.drain() {
tracing::debug!(
area = %result.area_id,
ticks = result.ticks_advanced,
"OffscreenTick result ready"
);
}
}
}
/// PostSnapshot: push new work requests based on player state.
///
/// Placeholder — real logic (predict player movement direction, queue
/// chunks ahead, snapshot departing NPCs) plugs in at Phase 5+.
pub fn push_worker_requests() {
// No-op until chunk streaming and NPC tier transitions are implemented.
// The worker pools are ready to receive requests via:
// chunk_worker.submit(req, DeliveryStrategy::GracefulDegrade);
// npc_worker.pool.submit(WorkRequest { ... });
}