diff --git a/Cargo.lock b/Cargo.lock index 5cff08740c..8133a96275 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1643,6 +1643,15 @@ dependencies = [ "miniz_oxide 0.4.3", ] +[[package]] +name = "float-cmp" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4" +dependencies = [ + "num-traits", +] + [[package]] name = "fnv" version = "1.0.7" @@ -5598,6 +5607,7 @@ dependencies = [ "directories-next", "dot_vox", "enum-iterator", + "float-cmp", "hashbrown 0.9.1", "image", "indexmap", @@ -6681,4 +6691,4 @@ dependencies = [ name = "xml-rs" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a" +checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a" \ No newline at end of file diff --git a/common/Cargo.toml b/common/Cargo.toml index 7c5cb4c482..2d614c40a3 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -68,6 +68,7 @@ specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", rev = "9fab7b3 [dev-dependencies] #bench criterion = "0.3" +float-cmp = "0.8.0" [[bench]] name = "chonk_benchmark" diff --git a/common/src/lib.rs b/common/src/lib.rs index 472cd38b64..c3e8501488 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -71,6 +71,7 @@ pub mod uid; #[cfg(not(target_arch = "wasm32"))] pub mod vol; #[cfg(not(target_arch = "wasm32"))] pub mod volumes; +pub mod vsystem; pub use combat::DamageSource; #[cfg(not(target_arch = "wasm32"))] diff --git a/common/src/metrics.rs b/common/src/metrics.rs index cc7435bd41..58ebf31627 100644 --- a/common/src/metrics.rs +++ b/common/src/metrics.rs @@ -1,7 +1,12 @@ -use std::sync::atomic::AtomicU64; +use crate::vsystem::CpuTimeline; +use std::{ + collections::HashMap, + sync::{atomic::AtomicU64, Mutex}, +}; #[derive(Default)] pub struct SysMetrics { + pub stats: Mutex>, pub agent_ns: AtomicU64, pub mount_ns: AtomicU64, pub controller_ns: AtomicU64, diff --git a/common/src/vsystem.rs b/common/src/vsystem.rs new file mode 100644 index 0000000000..b1af1ee844 --- /dev/null +++ b/common/src/vsystem.rs @@ -0,0 +1,458 @@ +use crate::metrics::SysMetrics; +use specs::{ReadExpect, RunNow, System}; +use std::{collections::HashMap, time::Instant}; + +/// measuring the level of threads a unit of code ran on. Use Rayon when it ran +/// on their threadpool. Use Exact when you know on how many threads your code +/// ran on exactly. +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum ParMode { + None, /* Job is not running at all */ + Single, + Rayon, + Exact(u16), +} + +//TODO: make use of the phase of a system for advanced scheduling and logging +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum Phase { + Create, + Review, + Apply, +} + +//TODO: make use of the origin of the system for better logging +#[derive(Clone, PartialEq, Debug)] +pub enum Origin { + Common, + Client, + Server, + Frontend(&'static str), +} + +#[derive(Default, Debug, Clone)] +pub struct CpuTimeline { + /// + /// - The first entry will always be ParMode::Single, as when the + /// System::run is executed, we run + /// single threaded until we start a Rayon::ParIter or similar + /// - The last entry will contain the end time of the System. To mark the + /// End it will always contain + /// ParMode::None, which means from that point on 0 CPU threads work in this + /// system + measures: Vec<(Instant, ParMode)>, +} + +#[derive(Default)] +pub struct CpuTimeStats { + /// the first entry will always be 0, the last entry will always be `dt` + /// `usage` starting from `ns` + measures: Vec<(/* ns */ u64, /* usage */ f32)>, +} + +/// Parallel Mode tells us how much you are scaling. `None` means your code +/// isn't running. `Single` means you are running single threaded. +/// `Rayon` means you are running on the rayon threadpool. +impl ParMode { + fn threads(&self, rayon_threads: u16) -> u16 { + match self { + ParMode::None => 0, + ParMode::Single => 1, + ParMode::Rayon => rayon_threads, + ParMode::Exact(u) => *u, + } + } +} + +impl CpuTimeline { + fn reset(&mut self) { + self.measures.clear(); + self.measures.push((Instant::now(), ParMode::Single)); + } + + /// Start a new measurement. par will be covering the parallelisation AFTER + /// this statement, till the next / end of the VSystem. + pub fn measure(&mut self, par: ParMode) { self.measures.push((Instant::now(), par)); } + + fn end(&mut self) { self.measures.push((Instant::now(), ParMode::None)); } + + fn get(&self, time: Instant) -> ParMode { + match self.measures.binary_search_by_key(&time, |&(a, _)| a) { + Ok(id) => self.measures[id].1, + Err(0) => ParMode::None, /* not yet started */ + Err(id) => self.measures[id - 1].1, + } + } +} + +impl CpuTimeStats { + pub fn length_ns(&self) -> u64 { self.end_ns() - self.start_ns() } + + pub fn start_ns(&self) -> u64 { + self.measures + .iter() + .find(|e| e.1 > 0.001) + .unwrap_or(&(0, 0.0)) + .0 + } + + pub fn end_ns(&self) -> u64 { self.measures.last().unwrap_or(&(0, 0.0)).0 } + + pub fn avg_threads(&self) -> f32 { + let mut sum = 0.0; + for w in self.measures.windows(2) { + let len = w[1].0 - w[0].0; + let h = w[0].1; + sum += len as f32 * h; + } + sum / (self.length_ns() as f32) + } +} + +/// The Idea is to transform individual timelines per system to a map of all +/// cores and what they (prob) are working on. +/// +/// # Example +/// +/// - Input: 3 services, 0 and 1 are 100% parallel and 2 is single threaded. `-` +/// means no work for *0.5s*. `#` means full work for *0.5s*. We see the first +/// service starts after 1s and runs for 3s The second one starts a sec later +/// and runs for 4s. The last service runs 2.5s after the tick start and runs +/// for 1s. Read left to right. +/// ```ignore +/// [--######------] +/// [----########--] +/// [-----##-------] +/// ``` +/// +/// - Output: a Map that calculates where our 6 cores are spending their time. +/// Here each number means 50% of a core is working on it. A '-' represents an +/// idling core. We start with all 6 cores idling. Then all cores start to +/// work on task 0. 2s in, task1 starts and we have to split cores. 2.5s in +/// task2 starts. We have 6 physical threads but work to fill 13. Later task 2 +/// and task 0 will finish their work and give more threads for task 1 to work +/// on. Read top to bottom +/// ```ignore +/// 0-1s [------------] +/// 1-2s [000000000000] +/// 2-2.5s [000000111111] +/// 2.5-3.5s [000001111122] +/// 3.5-4s [000000111111] +/// 4-6s [111111111111] +/// 6s.. [------------] +/// ``` +pub fn gen_stats( + timelines: &HashMap, + tick_work_start: Instant, + rayon_threads: u16, + physical_threads: u16, +) -> HashMap { + let mut result = HashMap::new(); + let mut all = timelines + .iter() + .map(|(s, t)| { + let mut stat = CpuTimeStats::default(); + stat.measures.push((0, 0.0)); + result.insert(s.clone(), stat); + t.measures.iter().map(|e| &e.0) + }) + .flatten() + .collect::>(); + + all.sort(); + all.dedup(); + for time in all { + let relative_time = time.duration_since(tick_work_start).as_nanos() as u64; + // get all parallelisation at this particular time + let individual_cores_wanted = timelines + .iter() + .map(|(k, t)| (k, t.get(*time).threads(rayon_threads))) + .collect::>(); + let total = individual_cores_wanted + .iter() + .map(|(_, a)| a) + .sum::() + .max(1) as f32; + let total_or_max = total.max(physical_threads as f32); + // update ALL states + for individual in individual_cores_wanted.iter() { + let actual = (individual.1 as f32 / total_or_max) * physical_threads as f32; + let p = result.get_mut(individual.0).unwrap(); + if (p.measures.last().unwrap().1 - actual).abs() > 0.0001 { + p.measures.push((relative_time, actual)); + } + } + } + result +} + +/// This trait wraps around specs::System and does additional veloren tasks like +/// metrics collection +/// +/// ``` +/// use specs::Read; +/// pub use veloren_common::vsystem::{Origin, ParMode, Phase, VJob, VSystem}; +/// # use std::time::Duration; +/// pub struct Sys; +/// impl<'a> VSystem<'a> for Sys { +/// type SystemData = (Read<'a, ()>, Read<'a, ()>); +/// +/// const NAME: &'static str = "example"; +/// const ORIGIN: Origin = Origin::Frontend("voxygen"); +/// const PHASE: Phase = Phase::Create; +/// +/// fn run(job: &mut VJob, (_read, _read2): Self::SystemData) { +/// std::thread::sleep(Duration::from_millis(100)); +/// job.cpu_stats.measure(ParMode::Rayon); +/// std::thread::sleep(Duration::from_millis(500)); +/// job.cpu_stats.measure(ParMode::Single); +/// std::thread::sleep(Duration::from_millis(40)); +/// } +/// } +/// ``` +pub trait VSystem<'a> { + const NAME: &'static str; + const PHASE: Phase; + const ORIGIN: Origin; + + type SystemData: specs::SystemData<'a>; + fn run(job: &mut VJob, data: Self::SystemData); + fn sys_name() -> String { format!("{}_sys", Self::NAME) } +} + +pub fn dispatch<'a, 'b, T>(builder: &mut specs::DispatcherBuilder<'a, 'b>, dep: &[&str]) +where + T: for<'c> VSystem<'c> + Send + 'a + Default, +{ + builder.add(VJob::::default(), &T::sys_name(), dep); +} + +pub fn run_now<'a, 'b, T>(world: &'a specs::World) +where + T: for<'c> VSystem<'c> + Send + 'a + Default, +{ + VJob::::default().run_now(world); +} + +/// This Struct will wrap the System in order to avoid the can only impl trait +/// for local defined structs error It also contains the cpu measurements +pub struct VJob +where + T: ?Sized, +{ + pub own: Box, + pub cpu_stats: CpuTimeline, +} + +impl<'a, T> System<'a> for VJob +where + T: VSystem<'a>, +{ + type SystemData = (T::SystemData, ReadExpect<'a, SysMetrics>); + + fn run(&mut self, data: Self::SystemData) { + crate::span!(_guard, "run", &format!("{}::Sys::run", T::NAME)); + self.cpu_stats.reset(); + T::run(self, data.0); + self.cpu_stats.end(); + data.1 + .stats + .lock() + .unwrap() + .insert(T::NAME.to_string(), self.cpu_stats.clone()); + } +} + +impl<'a, T> Default for VJob +where + T: VSystem<'a> + Default, +{ + fn default() -> Self { + Self { + own: Box::new(T::default()), + cpu_stats: CpuTimeline::default(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use float_cmp::approx_eq; + use std::time::Duration; + + fn mock_timelines( + tick_start: Instant, + durations: Vec<(u64, u64, ParMode)>, + ) -> HashMap { + let job = durations + .iter() + .enumerate() + .map(|(i, (s, e, p))| { + ( + i, + tick_start + Duration::from_millis(*s), + tick_start + Duration::from_millis(*e), + *p, + ) + }) + .collect::>(); + + job.iter() + .map(|(i, f, s, p)| { + (i.to_string(), CpuTimeline { + measures: vec![(*f, *p), (*s, ParMode::None)], + }) + }) + .collect() + } + + #[test] + fn single() { + const RAYON_THREADS: u16 = 4; + const PHYSICAL_THREADS: u16 = RAYON_THREADS; + let tick_start = Instant::now(); + let job_d = vec![(500, 1500, ParMode::Rayon)]; + let timelines = mock_timelines(tick_start, job_d); + + let stats = gen_stats(&timelines, tick_start, RAYON_THREADS, PHYSICAL_THREADS); + + const THREADS: f32 = PHYSICAL_THREADS as f32; + + let s = &stats["0"]; + let measures = &s.measures; + assert_eq!(measures.len(), 3); + assert_eq!(measures[0].0, 0); + assert!(approx_eq!(f32, measures[0].1, 0.0)); + assert_eq!(measures[1].0, 500000000); + assert!(approx_eq!(f32, measures[1].1, THREADS)); + assert_eq!(measures[2].0, 1500000000); + assert!(approx_eq!(f32, measures[2].1, 0.0)); + assert_eq!(s.start_ns(), 500000000); + assert_eq!(s.end_ns(), 1500000000); + assert_eq!(s.length_ns(), 1000000000); + assert!(approx_eq!(f32, s.avg_threads(), THREADS)); + } + + #[test] + fn two_jobs() { + const RAYON_THREADS: u16 = 8; + const PHYSICAL_THREADS: u16 = RAYON_THREADS; + let tick_start = Instant::now(); + let job_d = vec![(2000, 3000, ParMode::Single), (5000, 6500, ParMode::Single)]; + let timelines = mock_timelines(tick_start, job_d); + + let stats = gen_stats(&timelines, tick_start, RAYON_THREADS, PHYSICAL_THREADS); + + let s = &stats["0"]; + let measures = &s.measures; + assert_eq!(measures.len(), 3); + assert_eq!(measures[0].0, 0); + assert!(approx_eq!(f32, measures[0].1, 0.0)); + assert_eq!(measures[1].0, 2000000000); + assert!(approx_eq!(f32, measures[1].1, 1.0)); + assert_eq!(measures[2].0, 3000000000); + assert!(approx_eq!(f32, measures[2].1, 0.0)); + assert_eq!(s.start_ns(), 2000000000); + assert_eq!(s.end_ns(), 3000000000); + assert_eq!(s.length_ns(), 1000000000); + assert!(approx_eq!(f32, s.avg_threads(), 1.0)); + + let s = &stats["1"]; + let measures = &s.measures; + assert_eq!(measures.len(), 3); + assert_eq!(measures[0].0, 0); + assert!(approx_eq!(f32, measures[0].1, 0.0)); + assert_eq!(measures[1].0, 5000000000); + assert!(approx_eq!(f32, measures[1].1, 1.0)); + assert_eq!(measures[2].0, 6500000000); + assert!(approx_eq!(f32, measures[2].1, 0.0)); + assert_eq!(s.start_ns(), 5000000000); + assert_eq!(s.end_ns(), 6500000000); + assert_eq!(s.length_ns(), 1500000000); + assert!(approx_eq!(f32, s.avg_threads(), 1.0)); + } + + #[test] + fn generate_stats() { + const RAYON_THREADS: u16 = 6; + const PHYSICAL_THREADS: u16 = RAYON_THREADS; + let tick_start = Instant::now(); + let job_d = vec![ + (2000, 5000, ParMode::Rayon), + (3000, 7000, ParMode::Rayon), + (3500, 4500, ParMode::Single), + ]; + let timelines = mock_timelines(tick_start, job_d); + + let stats = gen_stats(&timelines, tick_start, RAYON_THREADS, PHYSICAL_THREADS); + + const THREADS: f32 = PHYSICAL_THREADS as f32; + + let s = &stats["0"]; + let measures = &s.measures; + assert_eq!(measures.len(), 6); + assert_eq!(measures[0].0, 0); + assert!(approx_eq!(f32, measures[0].1, 0.0)); + assert_eq!(measures[1].0, 2000000000); + assert!(approx_eq!(f32, measures[1].1, THREADS)); + assert_eq!(measures[2].0, 3000000000); + assert!(approx_eq!(f32, measures[2].1, THREADS / 2.0)); + assert_eq!(measures[3].0, 3500000000); + assert!(approx_eq!( + f32, + measures[3].1, + THREADS * THREADS / (THREADS * 2.0 + 1.0) + )); + assert_eq!(measures[4].0, 4500000000); + assert!(approx_eq!(f32, measures[4].1, THREADS / 2.0)); + assert_eq!(measures[5].0, 5000000000); + assert!(approx_eq!(f32, measures[5].1, 0.0)); + assert_eq!(s.start_ns(), 2000000000); + assert_eq!(s.end_ns(), 5000000000); + assert_eq!(s.length_ns(), 3000000000); + assert!(approx_eq!(f32, s.avg_threads(), 3.923077)); + + let s = &stats["1"]; + let measures = &s.measures; + assert_eq!(measures.len(), 6); + assert_eq!(measures[0].0, 0); + assert!(approx_eq!(f32, measures[0].1, 0.0)); + assert_eq!(measures[1].0, 3000000000); + assert!(approx_eq!(f32, measures[1].1, THREADS / 2.0)); + assert_eq!(measures[2].0, 3500000000); + assert!(approx_eq!( + f32, + measures[2].1, + THREADS * THREADS / (THREADS * 2.0 + 1.0) + )); + assert_eq!(measures[3].0, 4500000000); + assert!(approx_eq!(f32, measures[3].1, THREADS / 2.0)); + assert_eq!(measures[4].0, 5000000000); + assert!(approx_eq!(f32, measures[4].1, THREADS)); + assert_eq!(measures[5].0, 7000000000); + assert!(approx_eq!(f32, measures[5].1, 0.0)); + assert_eq!(s.start_ns(), 3000000000); + assert_eq!(s.end_ns(), 7000000000); + assert_eq!(s.length_ns(), 4000000000); + assert!(approx_eq!(f32, s.avg_threads(), 4.4423075)); + + let s = &stats["2"]; + let measures = &s.measures; + assert_eq!(measures.len(), 3); + assert_eq!(measures[0].0, 0); + assert!(approx_eq!(f32, measures[0].1, 0.0)); + assert_eq!(measures[1].0, 3500000000); + assert!(approx_eq!( + f32, + measures[1].1, + THREADS / (THREADS * 2.0 + 1.0) + )); + assert_eq!(measures[2].0, 4500000000); + assert!(approx_eq!(f32, measures[2].1, 0.0)); + assert_eq!(s.start_ns(), 3500000000); + assert_eq!(s.end_ns(), 4500000000); + assert_eq!(s.length_ns(), 1000000000); + assert!(approx_eq!(f32, s.avg_threads(), 0.4615385)); + } +} diff --git a/common/sys/src/aura.rs b/common/sys/src/aura.rs index 3a7dda3f6b..e1bd0628b6 100644 --- a/common/sys/src/aura.rs +++ b/common/sys/src/aura.rs @@ -8,10 +8,11 @@ use common::{ event::{EventBus, ServerEvent}, resources::DeltaTime, uid::UidAllocator, + vsystem::{Origin, Phase, VJob, VSystem}, }; use specs::{ - saveload::MarkerAllocator, shred::ResourceId, Entities, Join, Read, ReadStorage, System, - SystemData, World, WriteStorage, + saveload::MarkerAllocator, shred::ResourceId, Entities, Join, Read, ReadStorage, SystemData, + World, WriteStorage, }; use std::time::Duration; @@ -27,15 +28,20 @@ pub struct ReadData<'a> { groups: ReadStorage<'a, Group>, } +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { type SystemData = ( ReadData<'a>, WriteStorage<'a, Auras>, WriteStorage<'a, Buffs>, ); - fn run(&mut self, (read_data, mut auras, mut buffs): Self::SystemData) { + const NAME: &'static str = "aura"; + const ORIGIN: Origin = Origin::Common; + const PHASE: Phase = Phase::Create; + + fn run(_job: &mut VJob, (read_data, mut auras, mut buffs): Self::SystemData) { let mut server_emitter = read_data.server_bus.emitter(); let dt = read_data.dt.0; diff --git a/common/sys/src/beam.rs b/common/sys/src/beam.rs index 0f4cdf130d..99564ecc84 100644 --- a/common/sys/src/beam.rs +++ b/common/sys/src/beam.rs @@ -7,11 +7,12 @@ use common::{ event::{EventBus, ServerEvent}, resources::{DeltaTime, Time}, uid::{Uid, UidAllocator}, + vsystem::{Origin, Phase, VJob, VSystem}, GroupTarget, }; use specs::{ - saveload::MarkerAllocator, shred::ResourceId, Entities, Join, Read, ReadStorage, System, - SystemData, World, WriteStorage, + saveload::MarkerAllocator, shred::ResourceId, Entities, Join, Read, ReadStorage, SystemData, + World, WriteStorage, }; use std::time::Duration; use vek::*; @@ -37,15 +38,20 @@ pub struct ReadData<'a> { } /// This system is responsible for handling beams that heal or do damage +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { type SystemData = ( ReadData<'a>, WriteStorage<'a, BeamSegment>, WriteStorage<'a, Beam>, ); - fn run(&mut self, (read_data, mut beam_segments, mut beams): Self::SystemData) { + const NAME: &'static str = "beam"; + const ORIGIN: Origin = Origin::Common; + const PHASE: Phase = Phase::Create; + + fn run(_job: &mut VJob, (read_data, mut beam_segments, mut beams): Self::SystemData) { let mut server_emitter = read_data.server_bus.emitter(); let time = read_data.time.0; diff --git a/common/sys/src/buff.rs b/common/sys/src/buff.rs index 2a2aaa8c05..3f2d24e0aa 100644 --- a/common/sys/src/buff.rs +++ b/common/sys/src/buff.rs @@ -5,10 +5,11 @@ use common::{ }, event::{EventBus, ServerEvent}, resources::DeltaTime, + vsystem::{Origin, Phase, VJob, VSystem}, Damage, DamageSource, }; use specs::{ - shred::ResourceId, Entities, Join, Read, ReadStorage, System, SystemData, World, WriteStorage, + shred::ResourceId, Entities, Join, Read, ReadStorage, SystemData, World, WriteStorage, }; use std::time::Duration; @@ -20,8 +21,9 @@ pub struct ReadData<'a> { inventories: ReadStorage<'a, Inventory>, } +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { type SystemData = ( ReadData<'a>, WriteStorage<'a, Health>, @@ -30,8 +32,12 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Stats>, ); + const NAME: &'static str = "buff"; + const ORIGIN: Origin = Origin::Common; + const PHASE: Phase = Phase::Create; + fn run( - &mut self, + _job: &mut VJob, (read_data, mut healths, mut energies, mut buffs, mut stats): Self::SystemData, ) { let mut server_emitter = read_data.server_bus.emitter(); diff --git a/common/sys/src/character_behavior.rs b/common/sys/src/character_behavior.rs index fc83ab21ec..b03b1b1c72 100644 --- a/common/sys/src/character_behavior.rs +++ b/common/sys/src/character_behavior.rs @@ -1,6 +1,6 @@ use specs::{ - shred::ResourceId, Entities, Join, LazyUpdate, Read, ReadExpect, ReadStorage, System, - SystemData, World, WriteStorage, + shred::ResourceId, Entities, Join, LazyUpdate, Read, ReadExpect, ReadStorage, SystemData, + World, WriteStorage, }; use common::{ @@ -15,12 +15,12 @@ use common::{ event::{EventBus, LocalEvent, ServerEvent}, metrics::SysMetrics, resources::DeltaTime, - span, states::{ self, behavior::{CharacterBehavior, JoinData, JoinStruct}, }, uid::Uid, + vsystem::{Origin, Phase, VJob, VSystem}, }; use std::time::Duration; @@ -72,9 +72,10 @@ pub struct ReadData<'a> { /// ## Character Behavior System /// Passes `JoinData` to `CharacterState`'s `behavior` handler fn's. Receives a /// `StateUpdate` in return and performs updates to ECS Components from that. +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { #[allow(clippy::type_complexity)] type SystemData = ( ReadData<'a>, @@ -88,9 +89,13 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Poise>, ); + const NAME: &'static str = "character_behavior"; + const ORIGIN: Origin = Origin::Common; + const PHASE: Phase = Phase::Create; + #[allow(clippy::while_let_on_iterator)] // TODO: Pending review in #587 fn run( - &mut self, + _job: &mut VJob, ( read_data, mut character_states, @@ -104,7 +109,6 @@ impl<'a> System<'a> for Sys { ): Self::SystemData, ) { let start_time = std::time::Instant::now(); - span!(_guard, "run", "character_behavior::Sys::run"); let mut server_emitter = read_data.server_bus.emitter(); let mut local_emitter = read_data.local_bus.emitter(); diff --git a/common/sys/src/controller.rs b/common/sys/src/controller.rs index 8618c55e97..f83e806516 100644 --- a/common/sys/src/controller.rs +++ b/common/sys/src/controller.rs @@ -2,13 +2,13 @@ use common::{ comp::{BuffChange, ControlEvent, Controller}, event::{EventBus, ServerEvent}, metrics::SysMetrics, - span, uid::UidAllocator, + vsystem::{Origin, Phase, VJob, VSystem}, }; use specs::{ saveload::{Marker, MarkerAllocator}, shred::ResourceId, - Entities, Join, Read, ReadExpect, System, SystemData, World, WriteStorage, + Entities, Join, Read, ReadExpect, SystemData, World, WriteStorage, }; use vek::*; @@ -20,14 +20,18 @@ pub struct ReadData<'a> { metrics: ReadExpect<'a, SysMetrics>, } +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { type SystemData = (ReadData<'a>, WriteStorage<'a, Controller>); - fn run(&mut self, (read_data, mut controllers): Self::SystemData) { + const NAME: &'static str = "controller"; + const ORIGIN: Origin = Origin::Common; + const PHASE: Phase = Phase::Create; + + fn run(_job: &mut VJob, (read_data, mut controllers): Self::SystemData) { let start_time = std::time::Instant::now(); - span!(_guard, "run", "controller::Sys::run"); let mut server_emitter = read_data.server_bus.emitter(); for (entity, controller) in (&read_data.entities, &mut controllers).join() { diff --git a/common/sys/src/lib.rs b/common/sys/src/lib.rs index 560e56af65..72da8cf129 100644 --- a/common/sys/src/lib.rs +++ b/common/sys/src/lib.rs @@ -15,33 +15,23 @@ pub mod state; mod stats; // External +use common::vsystem::{dispatch, VSystem}; use specs::DispatcherBuilder; -// System names -pub const CHARACTER_BEHAVIOR_SYS: &str = "character_behavior_sys"; -pub const MELEE_SYS: &str = "melee_sys"; -pub const BEAM_SYS: &str = "beam_sys"; -pub const CONTROLLER_SYS: &str = "controller_sys"; -pub const MOUNT_SYS: &str = "mount_sys"; -pub const PHYS_SYS: &str = "phys_sys"; -pub const PROJECTILE_SYS: &str = "projectile_sys"; -pub const SHOCKWAVE_SYS: &str = "shockwave_sys"; -pub const STATS_SYS: &str = "stats_sys"; -pub const BUFFS_SYS: &str = "buffs_sys"; -pub const AURAS_SYS: &str = "auras_sys"; - pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) { - dispatch_builder.add(mount::Sys, MOUNT_SYS, &[]); - dispatch_builder.add(controller::Sys, CONTROLLER_SYS, &[MOUNT_SYS]); - dispatch_builder.add(character_behavior::Sys, CHARACTER_BEHAVIOR_SYS, &[ - CONTROLLER_SYS, + dispatch::(dispatch_builder, &[]); + dispatch::(dispatch_builder, &[&mount::Sys::sys_name()]); + dispatch::(dispatch_builder, &[&controller::Sys::sys_name()]); + dispatch::(dispatch_builder, &[]); + dispatch::(dispatch_builder, &[]); + dispatch::(dispatch_builder, &[ + &controller::Sys::sys_name(), + &mount::Sys::sys_name(), + &stats::Sys::sys_name(), ]); - dispatch_builder.add(stats::Sys, STATS_SYS, &[]); - dispatch_builder.add(buff::Sys, BUFFS_SYS, &[]); - dispatch_builder.add(phys::Sys, PHYS_SYS, &[CONTROLLER_SYS, MOUNT_SYS, STATS_SYS]); - dispatch_builder.add(projectile::Sys, PROJECTILE_SYS, &[PHYS_SYS]); - dispatch_builder.add(shockwave::Sys, SHOCKWAVE_SYS, &[PHYS_SYS]); - dispatch_builder.add(beam::Sys, BEAM_SYS, &[PHYS_SYS]); - dispatch_builder.add(melee::Sys, MELEE_SYS, &[PROJECTILE_SYS]); - dispatch_builder.add(aura::Sys, AURAS_SYS, &[]); + dispatch::(dispatch_builder, &[&phys::Sys::sys_name()]); + dispatch::(dispatch_builder, &[&phys::Sys::sys_name()]); + dispatch::(dispatch_builder, &[&phys::Sys::sys_name()]); + dispatch::(dispatch_builder, &[&projectile::Sys::sys_name()]); + dispatch::(dispatch_builder, &[]); } diff --git a/common/sys/src/melee.rs b/common/sys/src/melee.rs index 548b76f143..72dd435cb5 100644 --- a/common/sys/src/melee.rs +++ b/common/sys/src/melee.rs @@ -3,13 +3,13 @@ use common::{ comp::{Body, CharacterState, Energy, Group, Health, Inventory, Melee, Ori, Pos, Scale, Stats}, event::{EventBus, ServerEvent}, metrics::SysMetrics, - span, uid::Uid, util::Dir, + vsystem::{Origin, Phase, VJob, VSystem}, GroupTarget, }; use specs::{ - shred::ResourceId, Entities, Join, Read, ReadExpect, ReadStorage, System, SystemData, World, + shred::ResourceId, Entities, Join, Read, ReadExpect, ReadStorage, SystemData, World, WriteStorage, }; use vek::*; @@ -34,14 +34,18 @@ pub struct ReadData<'a> { /// This system is responsible for handling accepted inputs like moving or /// attacking +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { type SystemData = (ReadData<'a>, WriteStorage<'a, Melee>); - fn run(&mut self, (read_data, mut melee_attacks): Self::SystemData) { + const NAME: &'static str = "melee"; + const ORIGIN: Origin = Origin::Common; + const PHASE: Phase = Phase::Create; + + fn run(_job: &mut VJob, (read_data, mut melee_attacks): Self::SystemData) { let start_time = std::time::Instant::now(); - span!(_guard, "run", "melee::Sys::run"); let mut server_emitter = read_data.server_bus.emitter(); // Attacks for (attacker, uid, pos, ori, melee_attack, body) in ( diff --git a/common/sys/src/mount.rs b/common/sys/src/mount.rs index 80b71cfed5..44991b211a 100644 --- a/common/sys/src/mount.rs +++ b/common/sys/src/mount.rs @@ -1,18 +1,19 @@ use common::{ comp::{Controller, MountState, Mounting, Ori, Pos, Vel}, metrics::SysMetrics, - span, uid::UidAllocator, + vsystem::{Origin, Phase, VJob, VSystem}, }; use specs::{ saveload::{Marker, MarkerAllocator}, - Entities, Join, Read, ReadExpect, System, WriteStorage, + Entities, Join, Read, ReadExpect, WriteStorage, }; use vek::*; /// This system is responsible for controlling mounts +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { #[allow(clippy::type_complexity)] type SystemData = ( Read<'a, UidAllocator>, @@ -26,8 +27,12 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Ori>, ); + const NAME: &'static str = "mount"; + const ORIGIN: Origin = Origin::Common; + const PHASE: Phase = Phase::Create; + fn run( - &mut self, + _job: &mut VJob, ( uid_allocator, sys_metrics, @@ -41,7 +46,6 @@ impl<'a> System<'a> for Sys { ): Self::SystemData, ) { let start_time = std::time::Instant::now(); - span!(_guard, "run", "mount::Sys::run"); // Mounted entities. for (entity, mut mount_states) in (&entities, &mut mount_state.restrict_mut()).join() { match mount_states.get_unchecked() { diff --git a/common/sys/src/phys.rs b/common/sys/src/phys.rs index f576447082..3f05858fd1 100644 --- a/common/sys/src/phys.rs +++ b/common/sys/src/phys.rs @@ -11,11 +11,10 @@ use common::{ terrain::{Block, TerrainGrid}, uid::Uid, vol::ReadVol, + vsystem::{Origin, ParMode, Phase, VJob, VSystem}, }; use rayon::iter::ParallelIterator; -use specs::{ - Entities, Join, ParJoin, Read, ReadExpect, ReadStorage, System, WriteExpect, WriteStorage, -}; +use specs::{Entities, Join, ParJoin, Read, ReadExpect, ReadStorage, WriteExpect, WriteStorage}; use std::ops::Range; use vek::*; @@ -61,9 +60,10 @@ fn calc_z_limit( } /// This system applies forces and calculates new positions and velocities. +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { #[allow(clippy::type_complexity)] type SystemData = ( Entities<'a>, @@ -90,10 +90,14 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, CharacterState>, ); + const NAME: &'static str = "phys"; + const ORIGIN: Origin = Origin::Common; + const PHASE: Phase = Phase::Create; + #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 #[allow(clippy::blocks_in_if_conditions)] // TODO: Pending review in #587 fn run( - &mut self, + job: &mut VJob, ( entities, uids, @@ -120,7 +124,6 @@ impl<'a> System<'a> for Sys { ): Self::SystemData, ) { let start_time = std::time::Instant::now(); - span!(_guard, "run", "phys::Sys::run"); let mut event_emitter = event_bus.emitter(); // Add/reset physics state components @@ -212,6 +215,7 @@ impl<'a> System<'a> for Sys { drop(guard); span!(guard, "Apply pushback"); + job.cpu_stats.measure(ParMode::Rayon); let metrics = ( &entities, &positions, @@ -776,6 +780,7 @@ impl<'a> System<'a> for Sys { land_on_grounds_a }); drop(guard); + job.cpu_stats.measure(ParMode::Single); land_on_grounds.into_iter().for_each(|(entity, vel)| { event_emitter.emit(ServerEvent::LandOnGround { entity, vel: vel.0 }); diff --git a/common/sys/src/projectile.rs b/common/sys/src/projectile.rs index 45701c3cb3..ed9cd79abe 100644 --- a/common/sys/src/projectile.rs +++ b/common/sys/src/projectile.rs @@ -7,14 +7,14 @@ use common::{ event::{EventBus, ServerEvent}, metrics::SysMetrics, resources::DeltaTime, - span, uid::UidAllocator, util::Dir, + vsystem::{Origin, Phase, VJob, VSystem}, GroupTarget, }; use specs::{ saveload::MarkerAllocator, shred::ResourceId, Entities, Join, Read, ReadExpect, ReadStorage, - System, SystemData, World, WriteStorage, + SystemData, World, WriteStorage, }; use std::time::Duration; @@ -35,17 +35,24 @@ pub struct ReadData<'a> { } /// This system is responsible for handling projectile effect triggers +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { type SystemData = ( ReadData<'a>, WriteStorage<'a, Ori>, WriteStorage<'a, Projectile>, ); - fn run(&mut self, (read_data, mut orientations, mut projectiles): Self::SystemData) { + const NAME: &'static str = "projectile"; + const ORIGIN: Origin = Origin::Common; + const PHASE: Phase = Phase::Create; + + fn run( + _job: &mut VJob, + (read_data, mut orientations, mut projectiles): Self::SystemData, + ) { let start_time = std::time::Instant::now(); - span!(_guard, "run", "projectile::Sys::run"); let mut server_emitter = read_data.server_bus.emitter(); // Attacks diff --git a/common/sys/src/shockwave.rs b/common/sys/src/shockwave.rs index 80b5a12efa..5699b1b22f 100644 --- a/common/sys/src/shockwave.rs +++ b/common/sys/src/shockwave.rs @@ -8,11 +8,12 @@ use common::{ resources::{DeltaTime, Time}, uid::{Uid, UidAllocator}, util::Dir, + vsystem::{Origin, Phase, VJob, VSystem}, GroupTarget, }; use specs::{ - saveload::MarkerAllocator, shred::ResourceId, Entities, Join, Read, ReadStorage, System, - SystemData, World, WriteStorage, + saveload::MarkerAllocator, shred::ResourceId, Entities, Join, Read, ReadStorage, SystemData, + World, WriteStorage, }; use vek::*; @@ -39,15 +40,23 @@ pub struct ReadData<'a> { /// This system is responsible for handling accepted inputs like moving or /// attacking +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { type SystemData = ( ReadData<'a>, WriteStorage<'a, Shockwave>, WriteStorage<'a, ShockwaveHitEntities>, ); - fn run(&mut self, (read_data, mut shockwaves, mut shockwave_hit_lists): Self::SystemData) { + const NAME: &'static str = "shockwave"; + const ORIGIN: Origin = Origin::Common; + const PHASE: Phase = Phase::Create; + + fn run( + _job: &mut VJob, + (read_data, mut shockwaves, mut shockwave_hit_lists): Self::SystemData, + ) { let mut server_emitter = read_data.server_bus.emitter(); let time = read_data.time.0; diff --git a/common/sys/src/stats.rs b/common/sys/src/stats.rs index fa9fdb8e21..1f900819b0 100644 --- a/common/sys/src/stats.rs +++ b/common/sys/src/stats.rs @@ -8,13 +8,13 @@ use common::{ metrics::SysMetrics, outcome::Outcome, resources::{DeltaTime, Time}, - span, uid::Uid, + vsystem::{Origin, Phase, VJob, VSystem}, }; use hashbrown::HashSet; use specs::{ - shred::ResourceId, Entities, Join, Read, ReadExpect, ReadStorage, System, SystemData, World, - Write, WriteStorage, + shred::ResourceId, Entities, Join, Read, ReadExpect, ReadStorage, SystemData, World, Write, + WriteStorage, }; use vek::Vec3; @@ -36,8 +36,9 @@ pub struct ReadData<'a> { } /// This system kills players, levels them up, and regenerates energy. +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { #[allow(clippy::type_complexity)] type SystemData = ( ReadData<'a>, @@ -49,8 +50,12 @@ impl<'a> System<'a> for Sys { Write<'a, Vec>, ); + const NAME: &'static str = "stats"; + const ORIGIN: Origin = Origin::Common; + const PHASE: Phase = Phase::Create; + fn run( - &mut self, + _job: &mut VJob, ( read_data, mut stats, @@ -62,7 +67,6 @@ impl<'a> System<'a> for Sys { ): Self::SystemData, ) { let start_time = std::time::Instant::now(); - span!(_guard, "run", "stats::Sys::run"); let mut server_event_emitter = read_data.server_bus.emitter(); let dt = read_data.dt.0; diff --git a/server/src/lib.rs b/server/src/lib.rs index 8215c01e48..8e41ff8b89 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -66,6 +66,7 @@ use common::{ rtsim::RtSimEntity, terrain::TerrainChunkSize, vol::{ReadVol, RectVolSize}, + vsystem::run_now, }; use common_net::{ msg::{ @@ -87,11 +88,11 @@ use persistence::{ use plugin_api::Uid; use prometheus::Registry; use prometheus_hyper::Server as PrometheusServer; -use specs::{join::Join, Builder, Entity as EcsEntity, RunNow, SystemData, WorldExt}; +use specs::{join::Join, Builder, Entity as EcsEntity, SystemData, WorldExt}; use std::{ i32, ops::{Deref, DerefMut}, - sync::{atomic::Ordering, Arc}, + sync::Arc, time::{Duration, Instant}, }; #[cfg(not(feature = "worldgen"))] @@ -510,12 +511,12 @@ impl Server { // Run message receiving sys before the systems in common for decreased latency // (e.g. run before controller system) //TODO: run in parallel - sys::msg::general::Sys.run_now(&self.state.ecs()); + run_now::(&self.state.ecs()); self.register_run(); - sys::msg::character_screen::Sys.run_now(&self.state.ecs()); - sys::msg::in_game::Sys.run_now(&self.state.ecs()); - sys::msg::ping::Sys.run_now(&self.state.ecs()); - sys::agent::Sys.run_now(&self.state.ecs()); + run_now::(&self.state.ecs()); + run_now::(&self.state.ecs()); + run_now::(&self.state.ecs()); + run_now::(&self.state.ecs()); let before_state_tick = Instant::now(); @@ -705,6 +706,29 @@ impl Server { // 8) Update Metrics // Get system timing info + + { + let lock = self + .state + .ecs() + .read_resource::(); + let state = lock.stats.lock().unwrap(); + for (name, stat) in common::vsystem::gen_stats(&state, before_new_connections, 8, 8) { + self.tick_metrics + .system_start_time + .with_label_values(&[&name]) + .set(stat.start_ns() as i64); + self.tick_metrics + .system_length_time + .with_label_values(&[&name]) + .set(stat.length_ns() as i64); + self.tick_metrics + .system_thread_avg + .with_label_values(&[&name]) + .set(stat.avg_threads() as f64); + } + } + let agent_nanos = self.state.ecs().read_resource::().nanos as i64; let entity_sync_nanos = self .state @@ -819,6 +843,7 @@ impl Server { //detailed state metrics { + /* let res = self .state .ecs() @@ -834,7 +859,7 @@ impl Server { let melee_ns = res.melee_ns.load(Ordering::Relaxed); c.with_label_values(&[sys::AGENT_SYS]).inc_by(agent_ns); - c.with_label_values(&[common_sys::MOUNT_SYS]) + c.with_label_values(&[common_sys::]) .inc_by(mount_ns); c.with_label_values(&[common_sys::CONTROLLER_SYS]) .inc_by(controller_ns); @@ -866,6 +891,7 @@ impl Server { .observe(projectile_ns as f64 / NANOSEC_PER_SEC); h.with_label_values(&[common_sys::MELEE_SYS]) .observe(melee_ns as f64 / NANOSEC_PER_SEC); + */ } //detailed physics metrics diff --git a/server/src/metrics.rs b/server/src/metrics.rs index ac57121437..622cd4006c 100644 --- a/server/src/metrics.rs +++ b/server/src/metrics.rs @@ -1,6 +1,6 @@ use prometheus::{ - Gauge, HistogramOpts, HistogramVec, IntCounter, IntCounterVec, IntGauge, IntGaugeVec, Opts, - Registry, + Gauge, GaugeVec, HistogramOpts, HistogramVec, IntCounter, IntCounterVec, IntGauge, IntGaugeVec, + Opts, Registry, }; use std::{ convert::TryInto, @@ -54,6 +54,9 @@ pub struct TickMetrics { pub start_time: IntGauge, pub time_of_day: Gauge, pub light_count: IntGauge, + pub system_start_time: IntGaugeVec, + pub system_length_time: IntGaugeVec, + pub system_thread_avg: GaugeVec, tick: Arc, } @@ -290,6 +293,24 @@ impl TickMetrics { Opts::new("tick_time", "time in ns required for a tick of the server"), &["period"], )?; + let system_start_time = IntGaugeVec::new( + Opts::new( + "system_start_time", + "start relative to tick start in ns required per ECS system", + ), + &["system"], + )?; + let system_length_time = IntGaugeVec::new( + Opts::new("system_length_time", "time in ns required per ECS system"), + &["system"], + )?; + let system_thread_avg = GaugeVec::new( + Opts::new( + "system_thread_avg", + "average threads used by the ECS system", + ), + &["system"], + )?; let since_the_epoch = SystemTime::now() .duration_since(UNIX_EPOCH) @@ -306,6 +327,9 @@ impl TickMetrics { let light_count_clone = light_count.clone(); let tick_time_clone = tick_time.clone(); let tick = Arc::new(AtomicU64::new(0)); + let system_start_time_clone = system_start_time.clone(); + let system_length_time_clone = system_length_time.clone(); + let system_thread_avg_clone = system_thread_avg.clone(); let f = |registry: &Registry| { registry.register(Box::new(chonks_count_clone))?; @@ -317,6 +341,9 @@ impl TickMetrics { registry.register(Box::new(time_of_day_clone))?; registry.register(Box::new(light_count_clone))?; registry.register(Box::new(tick_time_clone))?; + registry.register(Box::new(system_start_time_clone))?; + registry.register(Box::new(system_length_time_clone))?; + registry.register(Box::new(system_thread_avg_clone))?; Ok(()) }; @@ -331,6 +358,9 @@ impl TickMetrics { start_time, time_of_day, light_count, + system_start_time, + system_length_time, + system_thread_avg, tick, }, Box::new(f), diff --git a/server/src/rtsim/load_chunks.rs b/server/src/rtsim/load_chunks.rs index 756b4c3f79..1dde223a96 100644 --- a/server/src/rtsim/load_chunks.rs +++ b/server/src/rtsim/load_chunks.rs @@ -1,13 +1,21 @@ use super::*; -use common::event::{EventBus, ServerEvent}; -use specs::{Read, System, WriteExpect}; +use common::{ + event::{EventBus, ServerEvent}, + vsystem::{Origin, Phase, VJob, VSystem}, +}; +use specs::{Read, WriteExpect}; +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { #[allow(clippy::type_complexity)] type SystemData = (Read<'a, EventBus>, WriteExpect<'a, RtSim>); - fn run(&mut self, (_server_event_bus, mut rtsim): Self::SystemData) { + const NAME: &'static str = "rtsim::load_chunks"; + const ORIGIN: Origin = Origin::Server; + const PHASE: Phase = Phase::Create; + + fn run(_job: &mut VJob, (_server_event_bus, mut rtsim): Self::SystemData) { for _chunk in std::mem::take(&mut rtsim.chunks.chunks_to_load) { // TODO } diff --git a/server/src/rtsim/mod.rs b/server/src/rtsim/mod.rs index f2352c9168..6bdeb93f8f 100644 --- a/server/src/rtsim/mod.rs +++ b/server/src/rtsim/mod.rs @@ -12,6 +12,7 @@ use common::{ rtsim::{RtSimController, RtSimEntity, RtSimId}, terrain::TerrainChunk, vol::RectRasterableVol, + vsystem::{dispatch, VSystem}, }; use common_sys::state::State; use rand::prelude::*; @@ -72,14 +73,13 @@ impl RtSim { } } -const LOAD_CHUNK_SYS: &str = "rtsim_load_chunk_sys"; -const UNLOAD_CHUNK_SYS: &str = "rtsim_unload_chunk_sys"; -const TICK_SYS: &str = "rtsim_tick_sys"; - pub fn add_server_systems(dispatch_builder: &mut DispatcherBuilder) { - dispatch_builder.add(unload_chunks::Sys, UNLOAD_CHUNK_SYS, &[]); - dispatch_builder.add(load_chunks::Sys, LOAD_CHUNK_SYS, &[UNLOAD_CHUNK_SYS]); - dispatch_builder.add(tick::Sys, TICK_SYS, &[LOAD_CHUNK_SYS, UNLOAD_CHUNK_SYS]); + dispatch::(dispatch_builder, &[]); + dispatch::(dispatch_builder, &[&unload_chunks::Sys::sys_name()]); + dispatch::(dispatch_builder, &[ + &load_chunks::Sys::sys_name(), + &unload_chunks::Sys::sys_name(), + ]); } pub fn init(state: &mut State, #[cfg(feature = "worldgen")] world: &world::World) { diff --git a/server/src/rtsim/tick.rs b/server/src/rtsim/tick.rs index 002c414f23..afb8847e9a 100644 --- a/server/src/rtsim/tick.rs +++ b/server/src/rtsim/tick.rs @@ -7,14 +7,16 @@ use common::{ event::{EventBus, ServerEvent}, resources::DeltaTime, terrain::TerrainGrid, + vsystem::{Origin, Phase, VJob, VSystem}, }; -use specs::{Join, Read, ReadExpect, ReadStorage, System, WriteExpect, WriteStorage}; +use specs::{Join, Read, ReadExpect, ReadStorage, WriteExpect, WriteStorage}; use std::sync::Arc; const ENTITY_TICK_PERIOD: u64 = 30; +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { #[allow(clippy::type_complexity)] type SystemData = ( Read<'a, DeltaTime>, @@ -28,8 +30,12 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, comp::Agent>, ); + const NAME: &'static str = "rtsim::tick"; + const ORIGIN: Origin = Origin::Server; + const PHASE: Phase = Phase::Create; + fn run( - &mut self, + _job: &mut VJob, ( dt, server_event_bus, diff --git a/server/src/rtsim/unload_chunks.rs b/server/src/rtsim/unload_chunks.rs index 1de4eb144b..f05800fb87 100644 --- a/server/src/rtsim/unload_chunks.rs +++ b/server/src/rtsim/unload_chunks.rs @@ -3,11 +3,13 @@ use common::{ comp::Pos, event::{EventBus, ServerEvent}, terrain::TerrainGrid, + vsystem::{Origin, Phase, VJob, VSystem}, }; -use specs::{Entities, Read, ReadExpect, ReadStorage, System, WriteExpect}; +use specs::{Entities, Read, ReadExpect, ReadStorage, WriteExpect}; +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { #[allow(clippy::type_complexity)] type SystemData = ( Read<'a, EventBus>, @@ -18,8 +20,12 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Pos>, ); + const NAME: &'static str = "rtsim::unload_chunks"; + const ORIGIN: Origin = Origin::Server; + const PHASE: Phase = Phase::Create; + fn run( - &mut self, + _job: &mut VJob, ( _server_event_bus, mut rtsim, diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index ebdac75da4..b883765eba 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -19,20 +19,20 @@ use common::{ metrics::SysMetrics, path::TraversalConfig, resources::{DeltaTime, TimeOfDay}, - span, terrain::{Block, TerrainGrid}, time::DayPeriod, uid::{Uid, UidAllocator}, util::Dir, vol::ReadVol, + vsystem::{Origin, Phase, VJob, VSystem}, }; use rand::{thread_rng, Rng}; use rayon::iter::ParallelIterator; use specs::{ saveload::{Marker, MarkerAllocator}, shred::ResourceId, - Entities, Entity as EcsEntity, Join, ParJoin, Read, ReadExpect, ReadStorage, System, - SystemData, World, Write, WriteStorage, + Entities, Entity as EcsEntity, Join, ParJoin, Read, ReadExpect, ReadStorage, SystemData, World, + Write, WriteStorage, }; use std::f32::consts::PI; use vek::*; @@ -99,8 +99,9 @@ const SNEAK_COEFFICIENT: f32 = 0.25; const AVG_FOLLOW_DIST: f32 = 6.0; /// This system will allow NPCs to modify their controller +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { #[allow(clippy::type_complexity)] type SystemData = ( ReadData<'a>, @@ -110,13 +111,16 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Controller>, ); + const NAME: &'static str = "agent"; + const ORIGIN: Origin = Origin::Server; + const PHASE: Phase = Phase::Create; + #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 fn run( - &mut self, + _job: &mut VJob, (read_data, mut sys_timer, event_bus, mut agents, mut controllers): Self::SystemData, ) { let start_time = std::time::Instant::now(); - span!(_guard, "run", "agent::Sys::run"); sys_timer.start(); ( diff --git a/server/src/sys/entity_sync.rs b/server/src/sys/entity_sync.rs index 46315841d4..f48fa6ce42 100644 --- a/server/src/sys/entity_sync.rs +++ b/server/src/sys/entity_sync.rs @@ -12,20 +12,21 @@ use common::{ outcome::Outcome, region::{Event as RegionEvent, RegionMap}, resources::TimeOfDay, - span, terrain::TerrainChunkSize, uid::Uid, vol::RectVolSize, + vsystem::{Origin, Phase, VJob, VSystem}, }; use common_net::{msg::ServerGeneral, sync::CompSyncPackage}; use specs::{ - Entities, Entity as EcsEntity, Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage, + Entities, Entity as EcsEntity, Join, Read, ReadExpect, ReadStorage, Write, WriteStorage, }; use vek::*; /// This system will send physics updates to the client +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { #[allow(clippy::type_complexity)] // TODO: Pending review in #587 type SystemData = ( Entities<'a>, @@ -52,8 +53,12 @@ impl<'a> System<'a> for Sys { ReadTrackers<'a>, ); + const NAME: &'static str = "entity_sync"; + const ORIGIN: Origin = Origin::Server; + const PHASE: Phase = Phase::Create; + fn run( - &mut self, + _job: &mut VJob, ( entities, tick, @@ -79,7 +84,6 @@ impl<'a> System<'a> for Sys { trackers, ): Self::SystemData, ) { - span!(_guard, "run", "entity_sync::Sys::run"); timer.start(); let tick = tick.0; diff --git a/server/src/sys/invite_timeout.rs b/server/src/sys/invite_timeout.rs index 1bc1979cb1..37fd4bbc0f 100644 --- a/server/src/sys/invite_timeout.rs +++ b/server/src/sys/invite_timeout.rs @@ -2,15 +2,16 @@ use super::SysTimer; use crate::client::Client; use common::{ comp::invite::{Invite, PendingInvites}, - span, uid::Uid, + vsystem::{Origin, Phase, VJob, VSystem}, }; use common_net::msg::{InviteAnswer, ServerGeneral}; -use specs::{Entities, Join, ReadStorage, System, Write, WriteStorage}; +use specs::{Entities, Join, ReadStorage, Write, WriteStorage}; /// This system removes timed out invites +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { #[allow(clippy::type_complexity)] // TODO: Pending review in #587 type SystemData = ( Entities<'a>, @@ -21,11 +22,14 @@ impl<'a> System<'a> for Sys { Write<'a, SysTimer>, ); + const NAME: &'static str = "invite_timeout"; + const ORIGIN: Origin = Origin::Server; + const PHASE: Phase = Phase::Create; + fn run( - &mut self, + _job: &mut VJob, (entities, mut invites, mut pending_invites, clients, uids, mut timer): Self::SystemData, ) { - span!(_guard, "run", "invite_timeout::Sys::run"); timer.start(); let now = std::time::Instant::now(); diff --git a/server/src/sys/mod.rs b/server/src/sys/mod.rs index 649c52114b..bbd6a258e3 100644 --- a/server/src/sys/mod.rs +++ b/server/src/sys/mod.rs @@ -10,6 +10,7 @@ pub mod terrain; pub mod terrain_sync; pub mod waypoint; +use common::vsystem::{dispatch, run_now}; use specs::DispatcherBuilder; use std::{ marker::PhantomData, @@ -31,38 +32,23 @@ pub type PersistenceTimer = SysTimer; pub type PersistenceScheduler = SysScheduler; pub type AgentTimer = SysTimer; -// System names -// Note: commented names may be useful in the future -//const ENTITY_SYNC_SYS: &str = "server_entity_sync_sys"; -//const SENTINEL_SYS: &str = "sentinel_sys"; -//const SUBSCRIPTION_SYS: &str = "server_subscription_sys"; -//const TERRAIN_SYNC_SYS: &str = "server_terrain_sync_sys"; -const TERRAIN_SYS: &str = "server_terrain_sys"; -const WAYPOINT_SYS: &str = "server_waypoint_sys"; -const INVITE_TIMEOUT_SYS: &str = "server_invite_timeout_sys"; -const PERSISTENCE_SYS: &str = "server_persistence_sys"; -const OBJECT_SYS: &str = "server_object_sys"; -pub const AGENT_SYS: &str = "agent_sys"; - pub fn add_server_systems(dispatch_builder: &mut DispatcherBuilder) { - dispatch_builder.add(terrain::Sys, TERRAIN_SYS, &[]); - dispatch_builder.add(waypoint::Sys, WAYPOINT_SYS, &[]); - dispatch_builder.add(invite_timeout::Sys, INVITE_TIMEOUT_SYS, &[]); - dispatch_builder.add(persistence::Sys, PERSISTENCE_SYS, &[]); - dispatch_builder.add(object::Sys, OBJECT_SYS, &[]); + dispatch::(dispatch_builder, &[]); + dispatch::(dispatch_builder, &[]); + dispatch::(dispatch_builder, &[]); + dispatch::(dispatch_builder, &[]); + dispatch::(dispatch_builder, &[]); } pub fn run_sync_systems(ecs: &mut specs::World) { - use specs::RunNow; - // Setup for entity sync // If I'm not mistaken, these two could be ran in parallel - sentinel::Sys.run_now(ecs); - subscription::Sys.run_now(ecs); + run_now::(ecs); + run_now::(ecs); // Sync - terrain_sync::Sys.run_now(ecs); - entity_sync::Sys.run_now(ecs); + run_now::(ecs); + run_now::(ecs); } /// Used to schedule systems to run at an interval diff --git a/server/src/sys/msg/character_screen.rs b/server/src/sys/msg/character_screen.rs index b3dd3ca57a..e3b8b8e1b2 100644 --- a/server/src/sys/msg/character_screen.rs +++ b/server/src/sys/msg/character_screen.rs @@ -6,11 +6,11 @@ use crate::{ use common::{ comp::{ChatType, Player, UnresolvedChatMsg}, event::{EventBus, ServerEvent}, - span, uid::Uid, + vsystem::{Origin, Phase, VJob, VSystem}, }; use common_net::msg::{ClientGeneral, ServerGeneral}; -use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, Write}; +use specs::{Entities, Join, Read, ReadExpect, ReadStorage, Write}; use std::sync::atomic::Ordering; use tracing::{debug, warn}; @@ -121,8 +121,9 @@ impl Sys { } /// This system will handle new messages from clients +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { #[allow(clippy::type_complexity)] type SystemData = ( Entities<'a>, @@ -137,8 +138,12 @@ impl<'a> System<'a> for Sys { ReadExpect<'a, AliasValidator>, ); + const NAME: &'static str = "msg::character_screen"; + const ORIGIN: Origin = Origin::Server; + const PHASE: Phase = Phase::Create; + fn run( - &mut self, + _job: &mut VJob, ( entities, server_event_bus, @@ -152,7 +157,6 @@ impl<'a> System<'a> for Sys { alias_validator, ): Self::SystemData, ) { - span!(_guard, "run", "msg::character_screen::Sys::run"); timer.start(); let mut server_emitter = server_event_bus.emitter(); diff --git a/server/src/sys/msg/general.rs b/server/src/sys/msg/general.rs index 1200a74d0e..d928fea9b9 100644 --- a/server/src/sys/msg/general.rs +++ b/server/src/sys/msg/general.rs @@ -4,13 +4,13 @@ use common::{ comp::{ChatMode, Player, UnresolvedChatMsg}, event::{EventBus, ServerEvent}, resources::Time, - span, uid::Uid, + vsystem::{Origin, Phase, VJob, VSystem}, }; use common_net::msg::{ validate_chat_msg, ChatMsgValidationError, ClientGeneral, MAX_BYTES_CHAT_MSG, }; -use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, Write}; +use specs::{Entities, Join, Read, ReadExpect, ReadStorage, Write}; use std::sync::atomic::Ordering; use tracing::{debug, error, warn}; @@ -65,8 +65,9 @@ impl Sys { } /// This system will handle new messages from clients +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { #[allow(clippy::type_complexity)] type SystemData = ( Entities<'a>, @@ -80,8 +81,12 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Client>, ); + const NAME: &'static str = "msg::general"; + const ORIGIN: Origin = Origin::Server; + const PHASE: Phase = Phase::Create; + fn run( - &mut self, + _job: &mut VJob, ( entities, server_event_bus, @@ -94,7 +99,6 @@ impl<'a> System<'a> for Sys { clients, ): Self::SystemData, ) { - span!(_guard, "run", "msg::general::Sys::run"); timer.start(); let mut server_emitter = server_event_bus.emitter(); diff --git a/server/src/sys/msg/in_game.rs b/server/src/sys/msg/in_game.rs index c8076c9bc6..454436a9e3 100644 --- a/server/src/sys/msg/in_game.rs +++ b/server/src/sys/msg/in_game.rs @@ -3,13 +3,13 @@ use crate::{client::Client, metrics::NetworkRequestMetrics, presence::Presence, use common::{ comp::{CanBuild, ControlEvent, Controller, ForceUpdate, Health, Ori, Pos, Stats, Vel}, event::{EventBus, ServerEvent}, - span, terrain::{TerrainChunkSize, TerrainGrid}, vol::{ReadVol, RectVolSize}, + vsystem::{Origin, Phase, VJob, VSystem}, }; use common_net::msg::{ClientGeneral, PresenceKind, ServerGeneral}; use common_sys::state::BlockChange; -use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage}; +use specs::{Entities, Join, Read, ReadExpect, ReadStorage, Write, WriteStorage}; use tracing::{debug, trace}; impl Sys { @@ -165,8 +165,9 @@ impl Sys { } /// This system will handle new messages from clients +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { #[allow(clippy::type_complexity)] type SystemData = ( Entities<'a>, @@ -188,8 +189,12 @@ impl<'a> System<'a> for Sys { Read<'a, Settings>, ); + const NAME: &'static str = "msg::in_game"; + const ORIGIN: Origin = Origin::Server; + const PHASE: Phase = Phase::Create; + fn run( - &mut self, + _job: &mut VJob, ( entities, server_event_bus, @@ -210,7 +215,6 @@ impl<'a> System<'a> for Sys { settings, ): Self::SystemData, ) { - span!(_guard, "run", "msg::in_game::Sys::run"); timer.start(); let mut server_emitter = server_event_bus.emitter(); diff --git a/server/src/sys/msg/ping.rs b/server/src/sys/msg/ping.rs index ea12514b1b..a80ee2799a 100644 --- a/server/src/sys/msg/ping.rs +++ b/server/src/sys/msg/ping.rs @@ -3,10 +3,10 @@ use crate::{client::Client, metrics::PlayerMetrics, Settings}; use common::{ event::{EventBus, ServerEvent}, resources::Time, - span, + vsystem::{Origin, Phase, VJob, VSystem}, }; use common_net::msg::PingMsg; -use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, Write}; +use specs::{Entities, Join, Read, ReadExpect, ReadStorage, Write}; use std::sync::atomic::Ordering; use tracing::{debug, info}; @@ -21,8 +21,9 @@ impl Sys { } /// This system will handle new messages from clients +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { #[allow(clippy::type_complexity)] type SystemData = ( Entities<'a>, @@ -34,8 +35,12 @@ impl<'a> System<'a> for Sys { Read<'a, Settings>, ); + const NAME: &'static str = "msg::ping"; + const ORIGIN: Origin = Origin::Server; + const PHASE: Phase = Phase::Create; + fn run( - &mut self, + _job: &mut VJob, ( entities, server_event_bus, @@ -46,7 +51,6 @@ impl<'a> System<'a> for Sys { settings, ): Self::SystemData, ) { - span!(_guard, "run", "msg::ping::Sys::run"); timer.start(); let mut server_emitter = server_event_bus.emitter(); diff --git a/server/src/sys/object.rs b/server/src/sys/object.rs index 0810017d93..a5aee2f43f 100644 --- a/server/src/sys/object.rs +++ b/server/src/sys/object.rs @@ -3,13 +3,15 @@ use common::{ effect::Effect, event::{EventBus, ServerEvent}, resources::DeltaTime, - span, Damage, DamageSource, Explosion, RadiusEffect, + vsystem::{Origin, Phase, VJob, VSystem}, + Damage, DamageSource, Explosion, RadiusEffect, }; -use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage}; +use specs::{Entities, Join, Read, ReadStorage, WriteStorage}; /// This system is responsible for handling misc object behaviours +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { #[allow(clippy::type_complexity)] type SystemData = ( Entities<'a>, @@ -21,11 +23,14 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Object>, ); + const NAME: &'static str = "object"; + const ORIGIN: Origin = Origin::Server; + const PHASE: Phase = Phase::Create; + fn run( - &mut self, + _job: &mut VJob, (entities, _dt, server_bus, positions, velocities, physics_states, mut objects): Self::SystemData, ) { - span!(_guard, "run", "object::Sys::run"); let mut server_emitter = server_bus.emitter(); // Objects diff --git a/server/src/sys/persistence.rs b/server/src/sys/persistence.rs index 2aecd80f75..858b8a5cf1 100644 --- a/server/src/sys/persistence.rs +++ b/server/src/sys/persistence.rs @@ -5,14 +5,15 @@ use crate::{ }; use common::{ comp::{Inventory, Stats, Waypoint}, - span, + vsystem::{Origin, Phase, VJob, VSystem}, }; use common_net::msg::PresenceKind; -use specs::{Join, ReadExpect, ReadStorage, System, Write}; +use specs::{Join, ReadExpect, ReadStorage, Write}; +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { #[allow(clippy::type_complexity)] // TODO: Pending review in #587 type SystemData = ( ReadStorage<'a, Presence>, @@ -24,8 +25,12 @@ impl<'a> System<'a> for Sys { Write<'a, SysTimer>, ); + const NAME: &'static str = "persistence"; + const ORIGIN: Origin = Origin::Server; + const PHASE: Phase = Phase::Create; + fn run( - &mut self, + _job: &mut VJob, ( presences, player_stats, @@ -36,7 +41,6 @@ impl<'a> System<'a> for Sys { mut timer, ): Self::SystemData, ) { - span!(_guard, "run", "persistence::Sys::run"); if scheduler.should_run() { timer.start(); updater.batch_update( diff --git a/server/src/sys/sentinel.rs b/server/src/sys/sentinel.rs index 517743f9e7..f6c9c38aaf 100644 --- a/server/src/sys/sentinel.rs +++ b/server/src/sys/sentinel.rs @@ -5,8 +5,8 @@ use common::{ Gravity, Group, Health, Inventory, Item, LightEmitter, Mass, MountState, Mounting, Ori, Player, Poise, Pos, Scale, Shockwave, Stats, Sticky, Vel, }, - span, uid::Uid, + vsystem::{Origin, Phase, VJob, VSystem}, }; use common_net::{ msg::EcsCompPacket, @@ -14,24 +14,28 @@ use common_net::{ }; use hashbrown::HashMap; use specs::{ - shred::ResourceId, Entity as EcsEntity, Join, ReadExpect, ReadStorage, System, SystemData, - World, Write, WriteExpect, + shred::ResourceId, Entity as EcsEntity, Join, ReadExpect, ReadStorage, SystemData, World, + Write, WriteExpect, }; use vek::*; /// Always watching /// This system will monitor specific components for insertion, removal, and /// modification +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { type SystemData = ( Write<'a, SysTimer>, TrackedComps<'a>, WriteTrackers<'a>, ); - fn run(&mut self, (mut timer, comps, mut trackers): Self::SystemData) { - span!(_guard, "run", "sentinel::Sys::run"); + const NAME: &'static str = "sentinel"; + const ORIGIN: Origin = Origin::Server; + const PHASE: Phase = Phase::Create; + + fn run(_job: &mut VJob, (mut timer, comps, mut trackers): Self::SystemData) { timer.start(); record_changes(&comps, &mut trackers); diff --git a/server/src/sys/subscription.rs b/server/src/sys/subscription.rs index 66ac09ff48..e8ad98aec4 100644 --- a/server/src/sys/subscription.rs +++ b/server/src/sys/subscription.rs @@ -9,22 +9,22 @@ use crate::{ use common::{ comp::{Ori, Pos, Vel}, region::{region_in_vd, regions_in_vd, Event as RegionEvent, RegionMap}, - span, terrain::TerrainChunkSize, uid::Uid, vol::RectVolSize, + vsystem::{Origin, Phase, VJob, VSystem}, }; use common_net::msg::ServerGeneral; use specs::{ - Entities, Join, ReadExpect, ReadStorage, System, SystemData, World, WorldExt, Write, - WriteStorage, + Entities, Join, ReadExpect, ReadStorage, SystemData, World, WorldExt, Write, WriteStorage, }; use tracing::{debug, error}; use vek::*; /// This system will update region subscriptions based on client positions +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { #[allow(clippy::type_complexity)] // TODO: Pending review in #587 type SystemData = ( Entities<'a>, @@ -41,9 +41,13 @@ impl<'a> System<'a> for Sys { TrackedComps<'a>, ); + const NAME: &'static str = "subscription"; + const ORIGIN: Origin = Origin::Server; + const PHASE: Phase = Phase::Create; + #[allow(clippy::blocks_in_if_conditions)] // TODO: Pending review in #587 fn run( - &mut self, + _job: &mut VJob, ( entities, region_map, @@ -59,7 +63,6 @@ impl<'a> System<'a> for Sys { tracked_comps, ): Self::SystemData, ) { - span!(_guard, "run", "subscription::Sys::run"); timer.start(); // To update subscriptions diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 6c493baf7b..e99e4a4ca7 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -7,13 +7,13 @@ use common::{ event::{EventBus, ServerEvent}, generation::get_npc_name, npc::NPC_NAMES, - span, terrain::TerrainGrid, + vsystem::{Origin, Phase, VJob, VSystem}, LoadoutBuilder, SkillSetBuilder, }; use common_net::msg::ServerGeneral; use common_sys::state::TerrainChanges; -use specs::{Join, Read, ReadStorage, System, Write, WriteExpect}; +use specs::{Join, Read, ReadStorage, Write, WriteExpect}; use std::sync::Arc; use vek::*; @@ -23,8 +23,9 @@ use vek::*; /// 2. Sends new chunks to nearby clients /// 3. Handles the chunk's supplement (e.g. npcs) /// 4. Removes chunks outside the range of players +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { #[allow(clippy::type_complexity)] // TODO: Pending review in #587 type SystemData = ( Read<'a, EventBus>, @@ -39,8 +40,12 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Client>, ); + const NAME: &'static str = "terrain"; + const ORIGIN: Origin = Origin::Server; + const PHASE: Phase = Phase::Create; + fn run( - &mut self, + _job: &mut VJob, ( server_event_bus, tick, @@ -54,7 +59,6 @@ impl<'a> System<'a> for Sys { clients, ): Self::SystemData, ) { - span!(_guard, "run", "terrain::Sys::run"); timer.start(); let mut server_emitter = server_event_bus.emitter(); diff --git a/server/src/sys/terrain_sync.rs b/server/src/sys/terrain_sync.rs index ffbfcfab60..2e52332036 100644 --- a/server/src/sys/terrain_sync.rs +++ b/server/src/sys/terrain_sync.rs @@ -1,14 +1,19 @@ use super::SysTimer; use crate::{client::Client, presence::Presence}; -use common::{comp::Pos, span, terrain::TerrainGrid}; +use common::{ + comp::Pos, + terrain::TerrainGrid, + vsystem::{Origin, Phase, VJob, VSystem}, +}; use common_net::msg::ServerGeneral; use common_sys::state::TerrainChanges; -use specs::{Join, Read, ReadExpect, ReadStorage, System, Write}; +use specs::{Join, Read, ReadExpect, ReadStorage, Write}; /// This systems sends new chunks to clients as well as changes to existing /// chunks +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { #[allow(clippy::type_complexity)] // TODO: Pending review in #587 type SystemData = ( ReadExpect<'a, TerrainGrid>, @@ -19,11 +24,14 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Client>, ); + const NAME: &'static str = "terrain_sync"; + const ORIGIN: Origin = Origin::Server; + const PHASE: Phase = Phase::Create; + fn run( - &mut self, + _job: &mut VJob, (terrain, terrain_changes, mut timer, positions, presences, clients): Self::SystemData, ) { - span!(_guard, "run", "terrain_sync::Sys::run"); timer.start(); // Sync changed chunks diff --git a/server/src/sys/waypoint.rs b/server/src/sys/waypoint.rs index de81c557bf..7d261cc3be 100644 --- a/server/src/sys/waypoint.rs +++ b/server/src/sys/waypoint.rs @@ -3,18 +3,19 @@ use crate::client::Client; use common::{ comp::{Player, Pos, Waypoint, WaypointArea}, resources::Time, - span, + vsystem::{Origin, Phase, VJob, VSystem}, }; use common_net::msg::{Notification, ServerGeneral}; -use specs::{Entities, Join, Read, ReadStorage, System, Write, WriteStorage}; +use specs::{Entities, Join, Read, ReadStorage, Write, WriteStorage}; /// Cooldown time (in seconds) for "Waypoint Saved" notifications const NOTIFY_TIME: f64 = 10.0; /// This system updates player waypoints /// TODO: Make this faster by only considering local waypoints +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { #[allow(clippy::type_complexity)] // TODO: Pending review in #587 type SystemData = ( Entities<'a>, @@ -27,8 +28,12 @@ impl<'a> System<'a> for Sys { Write<'a, SysTimer>, ); + const NAME: &'static str = "waypoint"; + const ORIGIN: Origin = Origin::Server; + const PHASE: Phase = Phase::Create; + fn run( - &mut self, + _job: &mut VJob, ( entities, positions, @@ -40,7 +45,6 @@ impl<'a> System<'a> for Sys { mut timer, ): Self::SystemData, ) { - span!(_guard, "run", "waypoint::Sys::run"); timer.start(); for (entity, player_pos, _, client) in (&entities, &positions, &players, &clients).join() { diff --git a/voxygen/src/ecs/sys.rs b/voxygen/src/ecs/sys.rs index a3b184effb..7405e0de8f 100644 --- a/voxygen/src/ecs/sys.rs +++ b/voxygen/src/ecs/sys.rs @@ -1,15 +1,10 @@ pub mod floater; mod interpolation; +use common::vsystem::{dispatch, VSystem}; use specs::DispatcherBuilder; -// System names -const FLOATER_SYS: &str = "floater_voxygen_sys"; -const INTERPOLATION_SYS: &str = "interpolation_voxygen_sys"; - pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) { - dispatch_builder.add(interpolation::Sys, INTERPOLATION_SYS, &[ - common_sys::PHYS_SYS, - ]); - dispatch_builder.add(floater::Sys, FLOATER_SYS, &[INTERPOLATION_SYS]); + dispatch::(dispatch_builder, &[&common_sys::phys::Sys::sys_name()]); + dispatch::(dispatch_builder, &[&interpolation::Sys::sys_name()]); } diff --git a/voxygen/src/ecs/sys/floater.rs b/voxygen/src/ecs/sys/floater.rs index b19acf22e8..8e293ec2db 100644 --- a/voxygen/src/ecs/sys/floater.rs +++ b/voxygen/src/ecs/sys/floater.rs @@ -6,16 +6,18 @@ use common::{ comp::{Health, HealthSource, Pos}, resources::DeltaTime, uid::Uid, + vsystem::{Origin, Phase, VJob, VSystem}, }; -use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage}; +use specs::{Entities, Join, Read, ReadExpect, ReadStorage, WriteStorage}; // How long floaters last (in seconds) pub const HP_SHOWTIME: f32 = 3.0; pub const MY_HP_SHOWTIME: f32 = 2.5; pub const HP_ACCUMULATETIME: f32 = 1.0; +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { #[allow(clippy::type_complexity)] // TODO: Pending review in #587 type SystemData = ( Entities<'a>, @@ -27,9 +29,13 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, HpFloaterList>, ); + const NAME: &'static str = "floater"; + const ORIGIN: Origin = Origin::Frontend("voxygen"); + const PHASE: Phase = Phase::Create; + #[allow(clippy::blocks_in_if_conditions)] // TODO: Pending review in #587 fn run( - &mut self, + _job: &mut VJob, (entities, my_entity, dt, uids, pos, healths, mut hp_floater_lists): Self::SystemData, ) { // Add hp floater lists to all entities with health and a position diff --git a/voxygen/src/ecs/sys/interpolation.rs b/voxygen/src/ecs/sys/interpolation.rs index 9a09f852ee..4bd1489230 100644 --- a/voxygen/src/ecs/sys/interpolation.rs +++ b/voxygen/src/ecs/sys/interpolation.rs @@ -2,14 +2,16 @@ use crate::ecs::comp::Interpolated; use common::{ comp::{object, Body, Ori, Pos, Vel}, resources::DeltaTime, + vsystem::{Origin, Phase, VJob, VSystem}, }; -use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage}; +use specs::{Entities, Join, Read, ReadStorage, WriteStorage}; use tracing::warn; use vek::*; /// This system will allow NPCs to modify their controller +#[derive(Default)] pub struct Sys; -impl<'a> System<'a> for Sys { +impl<'a> VSystem<'a> for Sys { #[allow(clippy::type_complexity)] // TODO: Pending review in #587 type SystemData = ( Entities<'a>, @@ -21,8 +23,12 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Interpolated>, ); + const NAME: &'static str = "interpolation"; + const ORIGIN: Origin = Origin::Frontend("voxygen"); + const PHASE: Phase = Phase::Create; + fn run( - &mut self, + _job: &mut VJob, (entities, dt, positions, orientations, velocities, bodies, mut interpolated): Self::SystemData, ) { // Update interpolated positions and orientations