From 1a515237afb7ad09353a65f5fbc6e98a7c29ce8e Mon Sep 17 00:00:00 2001 From: Kai Stevenson Date: Sat, 22 Aug 2026 15:00:35 -0700 Subject: sim manager refactor --- src/sim/sim_manager/mod.rs | 152 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 src/sim/sim_manager/mod.rs (limited to 'src/sim/sim_manager/mod.rs') diff --git a/src/sim/sim_manager/mod.rs b/src/sim/sim_manager/mod.rs new file mode 100644 index 0000000..a98f3b2 --- /dev/null +++ b/src/sim/sim_manager/mod.rs @@ -0,0 +1,152 @@ +use std::time::Instant; + +use crate::{ + Config, + config::{PHYSICS_DELTA_TIME, PHYSICS_FPS, SIM_FPS}, + sim::{ + cell::{cell::Cell, materials::MaterialId}, + cell_manager::manager::CellManager, + particle_manager::ParticleManager, + rb_manager::RbManager, + sim_manager::utils::write_rb_entity_to_world, + }, +}; + +mod utils; + +pub struct SimManager { + // timing + pub paused: bool, + pub ignore_pause_next_tick: bool, + pub last_cell_update: Instant, + pub cell_updates_due: f32, + pub last_physics_update: Instant, + pub physics_updates_due: f32, + + // systems + pub cell_manager: CellManager, + pub rb_manager: RbManager, + pub particle_manager: ParticleManager, +} + +impl SimManager { + pub fn new() -> Self { + SimManager { + paused: false, + ignore_pause_next_tick: false, + last_cell_update: Instant::now(), + cell_updates_due: 0.0, + last_physics_update: Instant::now(), + physics_updates_due: 0.0, + cell_manager: CellManager::from_default_size(), + rb_manager: RbManager::new(), + particle_manager: ParticleManager::new(), + } + } + + fn cell_update(&mut self, config: &Config) { + // before we tick, write all the rb entities into the sim world + // TODO optimize + let entity_ids: Vec = self.rb_manager.rb_entities.keys().copied().collect(); + let mut cells_written_by_entity: Vec<(u32, Vec<(u8, u8, i32, i32)>)> = Vec::new(); + + for entity_id in entity_ids { + let cells_written = write_rb_entity_to_world(self, entity_id, self.cell_manager.seqno); + cells_written_by_entity.push((entity_id, cells_written)); + } + + self.cell_manager.tick(config.use_threading); + + // after we tick, remove the written rb cells and update the entities + // TODO optimize + for (entity_id, cells_written) in cells_written_by_entity { + let rb_entity = self.rb_manager.rb_entities.get_mut(&entity_id).unwrap(); + for (lx, ly, x, y) in cells_written { + // update the entity + // TODO we should skip cells that weren't changed? + // TODO optimize + let new_local_cell = self.cell_manager.get_cell_from_game_position(x, y).unwrap(); + if new_local_cell.material != MaterialId::Void && !new_local_cell.rb() { + panic!( + "Someone swapped into this rb's cell! ({x}, {y}, {m:#?}, {f})", + m = new_local_cell.material, + f = new_local_cell.flags + ); + } + rb_entity.set_cell_at_local_position(lx, ly, new_local_cell); + // update the world + self.cell_manager + .set_cell_from_game_position(x, y, Cell::void(), false); + } + } + } + + fn physics_update(&mut self, delta_time: f32) { + // before we move the rigidbodies, upsert the current terrain state + // TODO use the chunk sleeping, and make this range dynamic + for cx in -5..5 { + for cy in -5..5 { + if let Some(chunk) = self + .cell_manager + .chunk_position_to_chunk_idx + .get(&(cx, cy)) + .map(|&idx| &self.cell_manager.chunks[idx]) + && !chunk.sleeping + { + self.rb_manager.upsert_chunk_collider(cx, cy, chunk); + } + } + } + + // move the rbs + self.rb_manager.tick(delta_time); + + // move the particles + self.particle_manager + .tick(&mut self.cell_manager, delta_time); + } + + pub fn update(&mut self, config: &Config, _delta_time: f32) -> () { + let now = Instant::now(); + let secs_since_last_cell_update = (now - self.last_cell_update).as_secs_f32(); + let expected_secs_since_last_cell_update = 1.0 / SIM_FPS as f32; + + self.last_cell_update = now; + if self.paused && self.ignore_pause_next_tick { + self.cell_update(config); + } else if !self.paused { + self.cell_updates_due += + secs_since_last_cell_update / expected_secs_since_last_cell_update; + let mut cell_updates_done = 0; + // don't ever update more than 3 times per frame, or else we can get a pseudo deadlock + while self.cell_updates_due >= 1.0 && cell_updates_done < 3 { + self.cell_update(config); + self.cell_updates_due -= 1.0; + cell_updates_done += 1; + } + self.cell_updates_due = self.cell_updates_due.min(3.0); + } + + // PHYSICS UPDATE + let secs_since_last_physics_update = (now - self.last_physics_update).as_secs_f32(); + let expected_secs_since_last_physics_update = 1.0 / PHYSICS_FPS as f32; + + self.last_physics_update = now; + if self.paused && self.ignore_pause_next_tick { + self.physics_update(PHYSICS_DELTA_TIME); + } else if !self.paused { + self.physics_updates_due += + secs_since_last_physics_update / expected_secs_since_last_physics_update; + let mut updates_done = 0; + // don't ever update more than 3 times per frame, or else we can get a pseudo deadlock + while self.physics_updates_due >= 1.0 && updates_done < 3 { + self.physics_update(PHYSICS_DELTA_TIME); + self.physics_updates_due -= 1.0; + updates_done += 1; + } + self.physics_updates_due = self.physics_updates_due.min(3.0); + } + + self.ignore_pause_next_tick = false; + } +} -- cgit v1.3.1