diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b321e64f8..15797bbd8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Switched to a Whittaker map for better tree spawning patterns - Switched to procedural snow cover on trees - Significantly improved terrain generation performance +- Significantly stabilized the game clock, to produce more "constant" TPS ### Removed diff --git a/Cargo.lock b/Cargo.lock index 068d50c164..ecddda7a45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4593,16 +4593,7 @@ checksum = "6f70411ba0d11c4aee042b1c73e68086322a293f8038c53ac95d36319c91ec19" dependencies = [ "tracing-core", "tracing-subscriber", - "tracy-client 0.9.0", -] - -[[package]] -name = "tracy-client" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650c280019cd1a841752d50f6a834216a2c8a810aeb18bdd48054be23cacd8a6" -dependencies = [ - "tracy-client-sys 0.9.0", + "tracy-client", ] [[package]] @@ -4611,16 +4602,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb214203bffe8cefe9bc0dfc12140f85e8d59b42e1f52f696071fdf8595e066a" dependencies = [ - "tracy-client-sys 0.10.0", -] - -[[package]] -name = "tracy-client-sys" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07e4636dad390858d247cf2633eaea90e67d7e21151cd476bbfd070d321279f" -dependencies = [ - "cc", + "tracy-client-sys", ] [[package]] @@ -4907,7 +4889,7 @@ dependencies = [ "specs-idvs", "sum_type", "tracing", - "tracy-client 0.8.0", + "tracy-client", "vek 0.12.0", ] diff --git a/chat-cli/src/main.rs b/chat-cli/src/main.rs index 4306d18d65..d537c42e75 100644 --- a/chat-cli/src/main.rs +++ b/chat-cli/src/main.rs @@ -26,7 +26,7 @@ fn main() { info!("Starting chat-cli..."); // Set up an fps clock. - let mut clock = Clock::start(); + let mut clock = Clock::new(Duration::from_millis(1000 / TPS)); println!("Enter your username"); let username = read_input(); @@ -71,11 +71,7 @@ fn main() { client.send_chat(msg) } - let events = match client.tick( - comp::ControllerInputs::default(), - clock.get_last_delta(), - |_| {}, - ) { + let events = match client.tick(comp::ControllerInputs::default(), clock.dt(), |_| {}) { Ok(events) => events, Err(err) => { error!("Error: {:?}", err); @@ -104,6 +100,6 @@ fn main() { client.cleanup(); // Wait for the next tick. - clock.tick(Duration::from_millis(1000 / TPS)); + clock.tick(); } } diff --git a/common/Cargo.toml b/common/Cargo.toml index e2767bcb4a..ee17816cd4 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -42,7 +42,7 @@ num-traits = "0.2" num-derive = "0.3" # Tracy -tracy-client = { version = "0.8.0", optional = true } +tracy-client = { version = "0.9.0", optional = true } [dev-dependencies] criterion = "0.3" diff --git a/common/src/clock.rs b/common/src/clock.rs index 5fc354622f..39e0acefec 100644 --- a/common/src/clock.rs +++ b/common/src/clock.rs @@ -1,63 +1,170 @@ use crate::span; use std::{ + collections::VecDeque, thread, time::{Duration, Instant}, }; -const CLOCK_SMOOTHING: f64 = 0.9; - +/// This Clock tries to make this tick a constant time by sleeping the rest of +/// the tick +/// - if we actually took less time than we planned: sleep and return planned +/// time +/// - if we ran behind: don't sleep and return actual time +/// We DON'T do any fancy averaging of the deltas for tick for 2 reasons: +/// - all Systems have to work based on `dt` and we cannot assume that this is +/// const through all ticks +/// - when we have a slow tick, a lag, it doesn't help that we have 10 fast +/// ticks directly afterwards +/// We return a smoothed version for display only! pub struct Clock { + /// This is the dt that the Clock tries to archive with each call of tick. + target_dt: Duration, + /// last time `tick` was called last_sys_time: Instant, - last_delta: Option, - running_average_delta: f64, - compensation: f64, + /// will be calculated in `tick` returns the dt used by the next iteration + /// of the main loop + last_dt: Duration, + /// summed up `last_dt` + total_tick_time: Duration, + // Stats only + // stored as millis in u16 to save space. if it's more than u16::MAX (16s) we have other + // problems + last_dts_millis: VecDeque, + last_dts_millis_sorted: Vec, + stats: ClockStats, } +pub struct ClockStats { + /// busy_dt is the part of the last tick that we didn't sleep. + /// e.g. the total tick is 33ms, including 25ms sleeping. then this returns + /// 8ms + pub last_busy_dt: Duration, + /// avg over the last NUMBER_OF_OLD_DELTAS_KEPT ticks + pub average_tps: f64, + /// = 50% percentile + pub median_tps: f64, + /// lowest 10% of the frames + pub percentile_90_tps: f64, + /// lowest 5% of the frames + pub percentile_95_tps: f64, + /// lowest 1% of the frames + pub percentile_99_tps: f64, +} + +const NUMBER_OF_OLD_DELTAS_KEPT: usize = 100; + impl Clock { - pub fn start() -> Self { + pub fn new(target_dt: Duration) -> Self { Self { + target_dt, last_sys_time: Instant::now(), - last_delta: None, - running_average_delta: 0.0, - compensation: 1.0, + last_dt: target_dt, + total_tick_time: Duration::default(), + last_dts_millis: VecDeque::with_capacity(NUMBER_OF_OLD_DELTAS_KEPT), + last_dts_millis_sorted: Vec::with_capacity(NUMBER_OF_OLD_DELTAS_KEPT), + stats: ClockStats::new(&Vec::new(), target_dt), } } - pub fn get_tps(&self) -> f64 { 1.0 / self.running_average_delta } + pub fn set_target_dt(&mut self, target_dt: Duration) { self.target_dt = target_dt; } - pub fn get_last_delta(&self) -> Duration { - self.last_delta.unwrap_or_else(|| Duration::new(0, 0)) - } + pub fn stats(&self) -> &ClockStats { &self.stats } - pub fn get_avg_delta(&self) -> Duration { Duration::from_secs_f64(self.running_average_delta) } + pub fn dt(&self) -> Duration { self.last_dt } - pub fn tick(&mut self, tgt: Duration) { + /// Do not modify without asking @xMAC94x first! + pub fn tick(&mut self) { span!(_guard, "tick", "Clock::tick"); - let delta = Instant::now().duration_since(self.last_sys_time); + let current_sys_time = Instant::now(); + let busy_delta = current_sys_time.duration_since(self.last_sys_time); + // Maintain TPS + self.last_dts_millis_sorted = self.last_dts_millis.iter().copied().collect(); + self.last_dts_millis_sorted.sort_unstable(); + self.stats = ClockStats::new(&self.last_dts_millis_sorted, busy_delta); // Attempt to sleep to fill the gap. - if let Some(sleep_dur) = tgt.checked_sub(delta) { - if self.running_average_delta != 0.0 { - self.compensation = - (self.compensation + (tgt.as_secs_f64() / self.running_average_delta) - 1.0) - .max(0.0) - } - - let sleep_secs = sleep_dur.as_secs_f64() * self.compensation; - if sleep_secs > 0.0 { - thread::sleep(Duration::from_secs_f64(sleep_secs)); - } + if let Some(sleep_dur) = self.target_dt.checked_sub(busy_delta) { + Clock::sleep(current_sys_time, sleep_dur) } - let delta = Instant::now().duration_since(self.last_sys_time); + let after_sleep_sys_time = Instant::now(); + self.last_dt = after_sleep_sys_time.duration_since(self.last_sys_time); + if self.last_dts_millis.len() >= NUMBER_OF_OLD_DELTAS_KEPT { + self.last_dts_millis.pop_front(); + } + self.last_dts_millis + .push_back((self.last_dt.as_millis() as u16).min(std::u16::MAX)); + self.total_tick_time += self.last_dt; + self.last_sys_time = after_sleep_sys_time; + } - self.last_sys_time = Instant::now(); - self.last_delta = Some(delta); - self.running_average_delta = if self.running_average_delta == 0.0 { - delta.as_secs_f64() + /// try to do a high precision sleep. + /// When this `fn` returns it SHOULD be before+dur as accurate is possible. + /// Returns the after sleep Instant. + fn sleep(before: Instant, dur: Duration) { + const BUSY_WAITING_TRIGGER_DUR: Duration = Duration::from_millis(2); + #[cfg(not(windows))] + const BUSY_WAITING_TIME: Duration = Duration::from_nanos(125_000); + #[cfg(windows)] + const BUSY_WAITING_TIME: Duration = Duration::from_nanos(500_000); + + // If we have more than BUSY_TRIGGER_DUR on our sleep bucket, do sleep for + // (1-BUSY_WAITING_TIME) time and do some busy waiting. If we are on high load, + // don't try such fancy precision increasing + if dur > BUSY_WAITING_TRIGGER_DUR { + let first_sleep_dur = dur - BUSY_WAITING_TIME; + thread::sleep(first_sleep_dur); + let target_time = before + dur; + span!(_guard, "tick", "Clock::busy_wait"); + while target_time > Instant::now() { + //busy waiting + std::sync::atomic::spin_loop_hint(); + } } else { - CLOCK_SMOOTHING * self.running_average_delta - + (1.0 - CLOCK_SMOOTHING) * delta.as_secs_f64() - }; + thread::sleep(dur); + } + } +} + +impl ClockStats { + fn new(sorted: &[u16], last_busy_dt: Duration) -> Self { + const NANOS_PER_SEC: f64 = Duration::from_secs(1).as_nanos() as f64; + const NANOS_PER_MILLI: f64 = Duration::from_millis(1).as_nanos() as f64; + + let len = sorted.len(); + let average_millis = sorted.iter().fold(0, |a, x| a + *x as u32) / len.max(1) as u32; + + let average_tps = NANOS_PER_SEC / (average_millis as f64 * NANOS_PER_MILLI); + let (median_tps, percentile_90_tps, percentile_95_tps, percentile_99_tps) = if len + >= NUMBER_OF_OLD_DELTAS_KEPT + { + let median_millis = sorted[len / 2]; + let percentile_90_millis = sorted[(NUMBER_OF_OLD_DELTAS_KEPT as f32 * 0.1) as usize]; + let percentile_95_millis = sorted[(NUMBER_OF_OLD_DELTAS_KEPT as f32 * 0.05) as usize]; + let percentile_99_millis = sorted[(NUMBER_OF_OLD_DELTAS_KEPT as f32 * 0.01) as usize]; + + let median_tps = NANOS_PER_SEC / (median_millis as f64 * NANOS_PER_MILLI); + let percentile_90_tps = NANOS_PER_SEC / (percentile_90_millis as f64 * NANOS_PER_MILLI); + let percentile_95_tps = NANOS_PER_SEC / (percentile_95_millis as f64 * NANOS_PER_MILLI); + let percentile_99_tps = NANOS_PER_SEC / (percentile_99_millis as f64 * NANOS_PER_MILLI); + ( + median_tps, + percentile_90_tps, + percentile_95_tps, + percentile_99_tps, + ) + } else { + let avg_tps = NANOS_PER_SEC / last_busy_dt.as_nanos() as f64; + (avg_tps, avg_tps, avg_tps, avg_tps) + }; + + Self { + last_busy_dt, + average_tps, + median_tps, + percentile_90_tps, + percentile_95_tps, + percentile_99_tps, + } } } diff --git a/server-cli/src/main.rs b/server-cli/src/main.rs index 1707656261..e5a52a8648 100644 --- a/server-cli/src/main.rs +++ b/server-cli/src/main.rs @@ -140,19 +140,22 @@ fn main() -> io::Result<()> { let mut shutdown_coordinator = ShutdownCoordinator::new(Arc::clone(&sigusr1_signal)); // Set up an fps clock - let mut clock = Clock::start(); + let mut clock = Clock::new(Duration::from_millis(1000 / TPS)); // Wait for a tick so we don't start with a zero dt - // TODO: consider integrating this into Clock::start? - clock.tick(Duration::from_millis(1000 / TPS)); loop { + #[cfg(feature = "tracy")] + common::util::tracy_client::finish_continuous_frame!(); + #[cfg(feature = "tracy")] + let frame = common::util::tracy_client::start_noncontinuous_frame!("work"); + // Terminate the server if instructed to do so by the shutdown coordinator if shutdown_coordinator.check(&mut server, &settings) { break; } let events = server - .tick(Input::default(), clock.get_last_delta()) + .tick(Input::default(), clock.dt()) .expect("Failed to tick server"); for event in events { @@ -165,8 +168,6 @@ fn main() -> io::Result<()> { // Clean up the server after a tick. server.cleanup(); - #[cfg(feature = "tracy")] - common::util::tracy_client::finish_continuous_frame!(); if let Some(tui) = tui.as_ref() { match tui.msg_r.try_recv() { @@ -194,8 +195,10 @@ fn main() -> io::Result<()> { } } + #[cfg(feature = "tracy")] + drop(frame); // Wait for the next tick. - clock.tick(Duration::from_millis(1000 / TPS)); + clock.tick(); } Ok(()) diff --git a/voxygen/src/audio/channel.rs b/voxygen/src/audio/channel.rs index 5f19e42532..3bbdd70672 100644 --- a/voxygen/src/audio/channel.rs +++ b/voxygen/src/audio/channel.rs @@ -114,7 +114,7 @@ impl MusicChannel { /// Maintain the fader attached to this channel. If the channel is not /// fading, no action is taken. - pub fn maintain(&mut self, dt: f32) { + pub fn maintain(&mut self, dt: std::time::Duration) { if self.state == ChannelState::Fading { self.fader.update(dt); self.sink.set_volume(self.fader.get_volume()); diff --git a/voxygen/src/audio/fader.rs b/voxygen/src/audio/fader.rs index b2c6c8ca16..18211595e2 100644 --- a/voxygen/src/audio/fader.rs +++ b/voxygen/src/audio/fader.rs @@ -1,11 +1,13 @@ //! Controls volume transitions for Audio Channels +use std::time::Duration; + /// Faders are attached to channels with initial and target volumes as well as a /// transition time. #[derive(PartialEq, Clone, Copy)] pub struct Fader { - length: f32, - running_time: f32, + length: Duration, + running_time: Duration, volume_from: f32, volume_to: f32, is_running: bool, @@ -21,19 +23,19 @@ pub enum FadeDirection { fn lerp(t: f32, a: f32, b: f32) -> f32 { (1.0 - t) * a + t * b } impl Fader { - pub fn fade(length: f32, volume_from: f32, volume_to: f32) -> Self { + pub fn fade(length: Duration, volume_from: f32, volume_to: f32) -> Self { Self { length, - running_time: 0.0, + running_time: Duration::default(), volume_from, volume_to, is_running: true, } } - pub fn fade_in(time: f32, volume_to: f32) -> Self { Self::fade(time, 0.0, volume_to) } + pub fn fade_in(time: Duration, volume_to: f32) -> Self { Self::fade(time, 0.0, volume_to) } - pub fn fade_out(time: f32, volume_from: f32) -> Self { Self::fade(time, volume_from, 0.0) } + pub fn fade_out(time: Duration, volume_from: f32) -> Self { Self::fade(time, volume_from, 0.0) } /// Used to update the `target` volume of the fader when the max or min /// volume changes. This occurs when the player changes their in-game @@ -62,7 +64,7 @@ impl Fader { } /// Called each tick to update the volume and state - pub fn update(&mut self, dt: f32) { + pub fn update(&mut self, dt: std::time::Duration) { if self.is_running { self.running_time += dt; if self.running_time >= self.length { @@ -74,7 +76,7 @@ impl Fader { pub fn get_volume(&self) -> f32 { lerp( - self.running_time / self.length, + self.running_time.as_nanos() as f32 / self.length.as_nanos() as f32, self.volume_from, self.volume_to, ) @@ -87,8 +89,8 @@ impl Fader { impl Default for Fader { fn default() -> Self { Self { - length: 0.0, - running_time: 0.0, + length: Duration::default(), + running_time: Duration::default(), volume_from: 0.0, volume_to: 1.0, is_running: false, @@ -102,14 +104,14 @@ mod tests { #[test] fn fade_direction_in() { - let fader = Fader::fade_in(10.0, 0.0); + let fader = Fader::fade_in(Duration::from_secs(10), 0.0); assert_eq!(fader.direction(), FadeDirection::In); } #[test] fn fade_direction_out() { - let fader = Fader::fade_out(10.0, 1.0); + let fader = Fader::fade_out(Duration::from_secs(10), 1.0); assert_eq!(fader.direction(), FadeDirection::Out); } @@ -117,10 +119,10 @@ mod tests { #[test] #[allow(clippy::float_cmp)] // TODO: Pending review in #587 fn fade_out_completes() { - let mut fader = Fader::fade_out(10.0, 1.0); + let mut fader = Fader::fade_out(Duration::from_secs(10), 1.0); // Run for the full duration - fader.update(10.0); + fader.update(Duration::from_secs(10)); assert_eq!(fader.get_volume(), 0.0); assert!(fader.is_finished()); @@ -128,32 +130,32 @@ mod tests { #[test] fn update_target_volume_fading_out_when_currently_above() { - let mut fader = Fader::fade_out(20.0, 1.0); + let mut fader = Fader::fade_out(Duration::from_secs(20), 1.0); // After 0.1s, the fader should still be close to 1.0 - fader.update(0.1); + fader.update(Duration::from_millis(100)); // Reduce volume to 0.4. We are currently above that. fader.update_target_volume(0.4); // The volume should immediately reduce to < 0.4 on the next update - fader.update(0.1); + fader.update(Duration::from_millis(100)); assert!(fader.get_volume() < 0.4) } #[test] fn update_target_volume_fading_out_when_currently_below() { - let mut fader = Fader::fade_out(10.0, 0.8); + let mut fader = Fader::fade_out(Duration::from_secs(10), 0.8); // After 9s, the fader should be close to 0 - fader.update(9.0); + fader.update(Duration::from_secs(9)); // Notify of a volume increase to 1.0. We are already far below that. fader.update_target_volume(1.0); // The fader should be unaffected by the new value, and continue dropping - fader.update(0.1); + fader.update(Duration::from_millis(100)); assert!(fader.get_volume() < 0.2); } @@ -161,16 +163,16 @@ mod tests { #[test] #[allow(clippy::float_cmp)] // TODO: Pending review in #587 fn update_target_volume_fading_in_when_currently_above() { - let mut fader = Fader::fade_in(10.0, 1.0); + let mut fader = Fader::fade_in(Duration::from_secs(10), 1.0); // After 9s, the fader should be close to 1.0 - fader.update(9.0); + fader.update(Duration::from_secs(9)); // Reduce volume to 0.4. We are currently above that. fader.update_target_volume(0.4); // Run out the fader. It's volume should be 0.4 - fader.update(1.0); + fader.update(Duration::from_secs(1)); assert_eq!(fader.get_volume(), 0.4); } @@ -178,10 +180,10 @@ mod tests { #[test] #[allow(clippy::float_cmp)] // TODO: Pending review in #587 fn update_target_volume_fading_in_when_currently_below() { - let mut fader = Fader::fade_in(20.0, 1.0); + let mut fader = Fader::fade_in(Duration::from_secs(20), 1.0); // After 0.1s, the fader should still be close to 0.0 - fader.update(0.1); + fader.update(Duration::from_millis(100)); // Reduce volume to 0.4. The volume_to should be reduced accordingly. fader.update_target_volume(0.4); diff --git a/voxygen/src/audio/mod.rs b/voxygen/src/audio/mod.rs index 5dab15fd4f..6e38549b4d 100644 --- a/voxygen/src/audio/mod.rs +++ b/voxygen/src/audio/mod.rs @@ -9,6 +9,7 @@ pub mod soundcache; use channel::{MusicChannel, MusicChannelTag, SfxChannel}; use fader::Fader; use soundcache::SoundCache; +use std::time::Duration; use tracing::warn; use common::assets; @@ -84,7 +85,7 @@ impl AudioFrontend { } /// Drop any unused music channels, and update their faders - pub fn maintain(&mut self, dt: f32) { + pub fn maintain(&mut self, dt: Duration) { self.music_channels.retain(|c| !c.is_done()); for channel in self.music_channels.iter_mut() { @@ -124,11 +125,13 @@ impl AudioFrontend { if existing_channel.get_tag() != next_channel_tag { // Fade the existing channel out. It will be removed when the fade completes. - existing_channel.set_fader(Fader::fade_out(2.0, self.music_volume)); + existing_channel + .set_fader(Fader::fade_out(Duration::from_secs(2), self.music_volume)); let mut next_music_channel = MusicChannel::new(&audio_device); - next_music_channel.set_fader(Fader::fade_in(12.0, self.music_volume)); + next_music_channel + .set_fader(Fader::fade_in(Duration::from_secs(12), self.music_volume)); self.music_channels.push(next_music_channel); } diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 38f26f98f0..845a15c2ea 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -746,7 +746,7 @@ impl Hud { // pulse time for pulsating elements self.pulse = self.pulse + dt.as_secs_f32(); // FPS - let fps = global_state.clock.get_tps(); + let fps = global_state.clock.stats().average_tps; let version = common::util::DISPLAY_VERSION_LONG.clone(); if self.show.ingame { diff --git a/voxygen/src/lib.rs b/voxygen/src/lib.rs index bb9acf6d99..12a585f647 100644 --- a/voxygen/src/lib.rs +++ b/voxygen/src/lib.rs @@ -63,7 +63,7 @@ impl GlobalState { self.window.needs_refresh_resize(); } - pub fn maintain(&mut self, dt: f32) { self.audio.maintain(dt); } + pub fn maintain(&mut self, dt: std::time::Duration) { self.audio.maintain(dt); } #[cfg(feature = "singleplayer")] pub fn paused(&self) -> bool { diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index 80794f9134..ae4be0453c 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -185,7 +185,7 @@ fn main() { profile, window, settings, - clock: Clock::start(), + clock: Clock::new(std::time::Duration::from_millis(30)), info_message: None, #[cfg(feature = "singleplayer")] singleplayer: None, diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index cd3adc0229..a50dc0c604 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -155,7 +155,7 @@ impl PlayState for CharSelectionState { match self.client.borrow_mut().tick( comp::ControllerInputs::default(), - global_state.clock.get_last_delta(), + global_state.clock.dt(), |_| {}, ) { Ok(events) => { diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index a9a0d88718..7f4bb43bd0 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -211,7 +211,7 @@ impl PlayState for MainMenuState { // Maintain the UI. for event in self .main_menu_ui - .maintain(global_state, global_state.clock.get_last_delta()) + .maintain(global_state, global_state.clock.dt()) { match event { MainMenuEvent::LoginAttempt { diff --git a/voxygen/src/run.rs b/voxygen/src/run.rs index ee9004fa1d..a8eac50d09 100644 --- a/voxygen/src/run.rs +++ b/voxygen/src/run.rs @@ -168,12 +168,13 @@ fn handle_main_events_cleared( if !exit { // Wait for the next tick. span!(_guard, "Main thread sleep"); - global_state.clock.tick(Duration::from_millis( + global_state.clock.set_target_dt(Duration::from_millis( 1000 / global_state.settings.graphics.max_fps as u64, )); + global_state.clock.tick(); span!(_guard, "Maintain global state"); // Maintain global state. - global_state.maintain(global_state.clock.get_last_delta().as_secs_f32()); + global_state.maintain(global_state.clock.dt()); } } diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index cd46775585..c94d93071c 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -631,7 +631,7 @@ impl PlayState for SessionState { let right = self.scene.camera().right(); let dir = right * axis_right + forward * axis_up; - let dt = global_state.clock.get_last_delta().as_secs_f32(); + let dt = global_state.clock.dt().as_secs_f32(); if self.freefly_vel.magnitude_squared() > 0.01 { let new_vel = self.freefly_vel - self.freefly_vel.normalized() * (FREEFLY_DAMPING * dt); @@ -668,11 +668,7 @@ impl PlayState for SessionState { // Runs if either in a multiplayer server or the singleplayer server is unpaused if !global_state.paused() { // Perform an in-game tick. - match self.tick( - global_state.clock.get_avg_delta(), - global_state, - &mut outcomes, - ) { + match self.tick(global_state.clock.dt(), global_state, &mut outcomes) { Ok(TickAction::Continue) => {}, // Do nothing Ok(TickAction::Disconnect) => return PlayStateResult::Pop, // Go to main menu Err(err) => { @@ -697,8 +693,8 @@ impl PlayState for SessionState { .gameplay .toggle_debug .then(|| DebugInfo { - tps: global_state.clock.get_tps(), - frame_time: global_state.clock.get_avg_delta(), + tps: global_state.clock.stats().average_tps, + frame_time: global_state.clock.stats().last_busy_dt, ping_ms: self.client.borrow().get_ping_ms_rolling_avg(), coordinates: self .client @@ -741,7 +737,7 @@ impl PlayState for SessionState { global_state, &debug_info, &self.scene.camera(), - global_state.clock.get_last_delta(), + global_state.clock.dt(), HudInfo { is_aiming, is_first_person: matches!( diff --git a/voxygen/src/singleplayer.rs b/voxygen/src/singleplayer.rs index b9bd860142..0bc68f50d9 100644 --- a/voxygen/src/singleplayer.rs +++ b/voxygen/src/singleplayer.rs @@ -152,7 +152,7 @@ fn run_server(mut server: Server, rec: Receiver, paused: Arc) { info!("Starting server-cli..."); // Set up an fps clock - let mut clock = Clock::start(); + let mut clock = Clock::new(Duration::from_millis(1000 / TPS)); loop { // Check any event such as stopping and pausing @@ -167,7 +167,7 @@ fn run_server(mut server: Server, rec: Receiver, paused: Arc) { } // Wait for the next tick. - clock.tick(Duration::from_millis(1000 / TPS)); + clock.tick(); // Skip updating the server if it's paused if paused.load(Ordering::SeqCst) && server.number_of_players() < 2 { @@ -177,7 +177,7 @@ fn run_server(mut server: Server, rec: Receiver, paused: Arc) { } let events = server - .tick(Input::default(), clock.get_last_delta()) + .tick(Input::default(), clock.dt()) .expect("Failed to tick server!"); for event in events {