fix(simulation): address PR #125 review — tick truncation, ordering, perf, protocol test

- Widen EventPort tick methods from u32 to u64 (prevents overflow)
- Add is_identity() guard on hot-path String allocation in modifiers
- Replace Vec::remove(0) with VecDeque::pop_front() in price history
- Add .after(tick_economy_simulation) ordering for debug commands
- Fix stale PROTOCOL_VERSION assertion (20 → 21) in serialization test
- Add D-181 Phase 2 visibility scope comment on serve_econ_state_query
- Eliminate double lookup in rebuild_signals via single-pass extraction
- Track economy seed TODO with backlog ticket reference

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-10 14:06:54 +02:00
co-authored by Claude Opus 4.6
parent 77232434f4
commit 1498115aba
8 changed files with 37 additions and 32 deletions
+15 -3
View File
@@ -130,7 +130,7 @@ struct ActiveEvent {
struct ScheduledEvent {
/// The simulation tick at which to activate this event.
inject_at_tick: u32,
inject_at_tick: u64,
event: EconEvent,
}
@@ -165,7 +165,7 @@ impl EventPort {
///
/// The event becomes active at the START of `inject_at_tick`, before
/// `compute_modifiers` is called for that tick.
pub fn push_at(&mut self, inject_at_tick: u32, event: EconEvent) {
pub fn push_at(&mut self, inject_at_tick: u64, event: EconEvent) {
self.scheduled.push(ScheduledEvent {
inject_at_tick,
event,
@@ -175,7 +175,7 @@ impl EventPort {
/// Activate any events scheduled for `current_tick`.
///
/// Call at the START of each tick, before `compute_modifiers`.
pub fn activate_scheduled(&mut self, current_tick: u32) {
pub fn activate_scheduled(&mut self, current_tick: u64) {
// Stable Rust: partition scheduled list manually (no drain_filter).
let mut still_pending = Vec::new();
let mut to_activate = Vec::new();
@@ -289,7 +289,11 @@ pub struct EventModifiers {
impl EventModifiers {
/// Combined demand multiplier for `(system_id, commodity_id)`.
/// Returns `1.0` if no active demand shock targets this pair.
/// Guards String allocation: fast-path returns 1.0 when no demand events are active.
pub fn demand_for(&self, system_id: &str, commodity_id: &str) -> f64 {
if self.demand.is_empty() {
return 1.0;
}
*self
.demand
.get(&(system_id.to_string(), commodity_id.to_string()))
@@ -297,7 +301,11 @@ impl EventModifiers {
}
/// Combined productivity multiplier for `(system_id, commodity_id)`.
/// Guards String allocation: fast-path returns 1.0 when no productivity events are active.
pub fn productivity_for(&self, system_id: &str, commodity_id: &str) -> f64 {
if self.productivity.is_empty() {
return 1.0;
}
*self
.productivity
.get(&(system_id.to_string(), commodity_id.to_string()))
@@ -305,7 +313,11 @@ impl EventModifiers {
}
/// Combined capacity multiplier for `(system_id, commodity_id)`.
/// Guards String allocation: fast-path returns 1.0 when no capacity events are active.
pub fn capacity_for(&self, system_id: &str, commodity_id: &str) -> f64 {
if self.capacity.is_empty() {
return 1.0;
}
*self
.capacity
.get(&(system_id.to_string(), commodity_id.to_string()))
+1 -1
View File
@@ -376,7 +376,7 @@ fn run_shock_response_test(
// Schedule: inject 90% capacity disruption at tick WARMUP_TICKS
let mut port = events::EventPort::new();
port.push_at(
WARMUP_TICKS,
WARMUP_TICKS as u64,
events::EconEvent {
target: events::EconEventTarget::Node(shock_node.clone()),
effect: events::EconEventEffect::CapacityMultiplier(0.1),
+1 -1
View File
@@ -135,7 +135,7 @@ pub fn run_with_events(
for tick in 0..ticks {
// Activate any events scheduled for this tick (D-180)
events.activate_scheduled(tick);
events.activate_scheduled(tick as u64);
let mods = events.compute_modifiers(economy);
step_inner(economy, productivity, shadow, &archetypes, &mut nodes, &mods, ALPHA);
+1 -2
View File
@@ -103,8 +103,7 @@ impl Simulation {
/// event port. Call once per economy tick (every ECON_TICK_RATE game ticks).
pub fn step(&mut self) {
// Activate any events scheduled for this tick (D-180)
let tick32 = self.tick as u32;
self.events.activate_scheduled(tick32);
self.events.activate_scheduled(self.tick);
let mods = self.events.compute_modifiers(&self.economy);