Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ server_context = "global"
| `/speed <speed>` | None | Sets your flyspeed. |
| `/gamemode <mode>` | `/gmc`, `/gmsp` | Sets your gamemode. |
| `/container <type> <power>` | None | Gives you a container (e.g. barrel) which outputs a specified amount of power when used with a comparator. |
| `/worldsendrate [hertz]` | `/wsr` | Sets the world send rate to `[hertz]` (frequency of world updates sent to clients). Range: 1-1000. Default: 60. |
| `/worldsendrate [hertz]` | `/wsr` | Sets the world send rate to `[hertz]` (frequency of world updates sent to clients). Range: 0-1000. Default: 60. |
| `/toggleautorp` | None | Toggles automatic redpiler compilation. |
| `/stop` | None | Stops the server. |

Expand Down
25 changes: 15 additions & 10 deletions crates/core/src/plot/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,11 @@ impl Plot {
return false;
}

let tps = if let Ok(tps) = args[0].parse::<u32>() {
let tps = if let Ok(tps) = args[0].parse::<f32>() {
if tps < 0.0 {
self.players[player].send_error_message("RTPS must be cannot be negative!");
return false;
}
Tps::Limited(tps)
} else if !args[0].is_empty() && "unlimited".starts_with(args[0]) {
Tps::Unlimited
Expand Down Expand Up @@ -490,7 +494,7 @@ impl Plot {
"worldsendrate" | "wsr" => {
if args.is_empty() {
self.players[player].send_system_message(&format!(
"Current world send rate: {} Hz",
"Current world send rate: {:.2} Hz",
self.world_send_rate.0
));
return false;
Expand All @@ -501,17 +505,18 @@ impl Plot {
return false;
}

let Ok(hertz) = args[0].parse::<u32>() else {
let Ok(hertz) = args[0].parse::<f32>() else {
self.players[player].send_error_message("Unable to parse send rate!");
return false;
};
if hertz == 0 {
self.players[player].send_error_message("The world send rate cannot be 0!");
if hertz < 0.0 {
self.players[player]
.send_error_message("The world send rate cannot be negative!");
return false;
}
if hertz > 1000 {
if hertz > 1000.0 {
self.players[player]
.send_error_message("The world send rate cannot go higher than 1000!");
.send_error_message("The world send rate cannot be higher than 1000!");
return false;
}

Expand Down Expand Up @@ -615,7 +620,7 @@ pub static DECLARE_COMMANDS: Lazy<PacketEncoder> = Lazy::new(|| {
children: vec![],
redirect_node: None,
name: Some("rtps"),
parser: Some(Parser::Integer(0, i32::MAX)),
parser: Some(Parser::Float(0.0, f32::MAX)),
suggestions_type: None,
},
// 8: /radvance
Expand Down Expand Up @@ -999,13 +1004,13 @@ pub static DECLARE_COMMANDS: Lazy<PacketEncoder> = Lazy::new(|| {
parser: None,
suggestions_type: None,
},
// 50: /worldsendrate [rticks]
// 50: /worldsendrate [hertz]
Node {
flags: (CommandFlags::ARGUMENT | CommandFlags::EXECUTABLE).bits() as i8,
children: vec![],
redirect_node: None,
name: Some("hertz"),
parser: Some(Parser::Integer(0, 1000)),
parser: Some(Parser::Float(0.0, 1000.0)),
suggestions_type: None,
},
// 51: /wsr
Expand Down
6 changes: 3 additions & 3 deletions crates/core/src/plot/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use std::time::Duration;
pub fn sleep_time_for_tps(tps: Tps) -> Duration {
match tps {
Tps::Limited(tps) => {
if tps > 10 {
Duration::from_micros(1_000_000 / tps as u64)
if tps > 10.0 {
Duration::from_secs_f32(1.0 / tps)
} else {
Duration::from_millis(50)
}
Expand Down Expand Up @@ -53,7 +53,7 @@ static EMPTY_PLOT: Lazy<PlotData> = Lazy::new(|| {
};
let chunk_data: Vec<ChunkData> = world.chunks.iter_mut().map(ChunkData::new).collect();
PlotData {
tps: Tps::Limited(10),
tps: Tps::Limited(10.0),
world_send_rate: WorldSendRate::default(),
chunk_data,
pending_ticks: Vec::new(),
Expand Down
38 changes: 22 additions & 16 deletions crates/core/src/plot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -987,25 +987,31 @@ impl Plot {
let now = Instant::now();
self.last_player_time = now;

let world_send_rate =
Duration::from_nanos(1_000_000_000 / self.world_send_rate.0 as u64);
let world_send_rate = if self.world_send_rate.0 == 0.0 {
Duration::MAX
} else {
Duration::from_secs_f32(1.0 / self.world_send_rate.0)
};

// 50_000 (= 3.33 MHz) here is arbitrary.
// We just need a number that's not too high so we actually get around to sending block updates.
let max_batch_size = match self.last_nspt {
Some(Duration::ZERO) | None => 1,
Some(last_nspt) => {
let ticks_fit = (world_send_rate.as_nanos() / last_nspt.as_nanos()) as u64;
let ticks_fit = (world_send_rate.as_nanos() / last_nspt.as_nanos()) as u32;
// A tick previously took longer than the world send rate.
// Run at least one just so we're not stuck doing nothing
ticks_fit.max(1)
}
};
}
.min(50_000);

let batch_size = match self.tps {
Tps::Limited(tps) if tps != 0 => {
let dur_per_tick = Duration::from_nanos(1_000_000_000 / tps as u64);
Tps::Limited(tps) if tps != 0.0 => {
let dur_per_tick = Duration::from_secs_f32(1.0 / tps);
self.lag_time += now - self.last_update_time;
let batch_size = (self.lag_time.as_nanos() / dur_per_tick.as_nanos()) as u64;
self.lag_time -= dur_per_tick * batch_size as u32;
let batch_size = (self.lag_time.as_nanos() / dur_per_tick.as_nanos()) as u32;
self.lag_time -= dur_per_tick * batch_size;
batch_size.min(max_batch_size)
}
Tps::Unlimited => max_batch_size,
Expand All @@ -1014,24 +1020,24 @@ impl Plot {

self.last_update_time = now;
if batch_size != 0 {
// 50_000 (= 3.33 MHz) here is arbitrary.
// We just need a number that's not too high so we actually get around to sending
// block updates.
let batch_size = batch_size.min(50_000) as u32;
let mut ticks_completed = batch_size;
let mut ticks_completed = 0;
if self.redpiler.is_active() {
self.tickn(batch_size as u64);
self.redpiler.flush(&mut self.world);
ticks_completed += batch_size;
} else {
for i in 0..batch_size {
for _ in 0..batch_size {
self.tick();
ticks_completed += 1;
if now.elapsed() > Duration::from_millis(200) {
ticks_completed = i + 1;
break;
}
}
}
self.last_nspt = Some(self.last_update_time.elapsed() / ticks_completed);

if ticks_completed != 0 {
self.last_nspt = Some(self.last_update_time.elapsed() / ticks_completed);
}
}

if self.auto_redpiler
Expand Down
58 changes: 36 additions & 22 deletions crates/core/src/plot/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,41 @@ use std::thread::JoinHandle;
use std::time::Duration;
use tracing::warn;

const MONITOR_SCHEDULE: Duration = Duration::from_millis(500);

#[derive(Default)]
struct AtomicTps {
tps: AtomicU32,
unlimited: AtomicBool,
tps_bits: AtomicU32,
}

impl AtomicTps {
fn from_tps(tps: Tps) -> Self {
match tps {
Tps::Limited(tps) => AtomicTps {
tps: AtomicU32::new(tps),
unlimited: AtomicBool::new(false),
},
Tps::Unlimited => AtomicTps {
tps: AtomicU32::new(0),
unlimited: AtomicBool::new(true),
},
AtomicTps {
tps_bits: AtomicU32::new(Self::tps_to_bits(tps)),
}
}

fn update(&self, tps: Tps) {
self.tps_bits
.store(Self::tps_to_bits(tps), Ordering::Relaxed);
}

fn tps_to_bits(tps: Tps) -> u32 {
match tps {
Tps::Limited(tps) => {
self.tps.store(tps, Ordering::Relaxed);
self.unlimited.store(false, Ordering::Relaxed);
Tps::Limited(tps) if tps.is_nan() => {
panic!("Tps should never be NaN under any circumstance")
}
Tps::Unlimited => self.unlimited.store(true, Ordering::Relaxed),
Tps::Limited(tps) => tps.to_bits(),
Tps::Unlimited => f32::NAN.to_bits(),
}
}

fn get(&self) -> Tps {
let tps = f32::from_bits(self.tps_bits.load(Ordering::Relaxed));
if tps.is_nan() {
Tps::Unlimited
} else {
Tps::Limited(tps)
}
}
}
Expand Down Expand Up @@ -148,13 +156,13 @@ impl TimingsMonitor {

fn run_thread(data: Arc<MonitorData>) -> JoinHandle<()> {
thread::spawn(move || {
let mut last_tps = data.tps.tps.load(Ordering::Relaxed);
let mut last_tps = data.tps.get();
let mut last_ticks_count = data.ticks_passed.load(Ordering::Relaxed);
let mut was_ticking_before = data.ticking.load(Ordering::Relaxed);

let mut behind_for = 0;
loop {
thread::sleep(Duration::from_millis(500));
thread::sleep(MONITOR_SCHEDULE);
if !data.running.load(Ordering::Relaxed) {
return;
}
Expand All @@ -166,7 +174,7 @@ impl TimingsMonitor {
let ticks_passed = (ticks_count - last_ticks_count) as u32;
last_ticks_count = ticks_count;

let tps = data.tps.tps.load(Ordering::Relaxed);
let tps = data.tps.get();
let ticking = data.ticking.load(Ordering::Relaxed);
if !(ticking && was_ticking_before)
|| tps != last_tps
Expand All @@ -179,8 +187,14 @@ impl TimingsMonitor {
}

// 5% threshold
if data.tps.unlimited.load(Ordering::Relaxed) || ticks_passed < (tps / 2) * 95 / 100
{
let is_behind = match tps {
Tps::Unlimited => false,
Tps::Limited(tps_val) => {
(ticks_passed as f32) < tps_val * MONITOR_SCHEDULE.as_secs_f32() * 0.95
}
};

if is_behind {
behind_for += 1;
} else {
behind_for = 0;
Expand All @@ -190,8 +204,8 @@ impl TimingsMonitor {
if behind_for >= 3 {
data.too_slow.store(true, Ordering::Relaxed);
// warn!(
// "running behind by {} ticks",
// ((tps / 2) * 95 / 100) - ticks_passed
// "running behind by {:.1} ticks",
// (tps * MONITOR_SCHEDULE.as_secs_f32() * 0.95) - (ticks_passed as f32)
// );
}

Expand Down
14 changes: 8 additions & 6 deletions crates/save_data/src/plot_data.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod fixer;
mod v2_to_v3;

use self::fixer::FixInfo;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
Expand All @@ -18,7 +19,8 @@ use thiserror::Error;
/// 0: Initial plot data file with header (MC 1.18.2)
/// 1: Add world send rate
/// 2: Update to MC 1.20.4
pub const VERSION: u32 = 2;
/// 3: Change Tps and WorldSendRate to support real values (f32)
pub const VERSION: u32 = 3;

#[derive(Error, Debug)]
pub enum PlotLoadError {
Expand Down Expand Up @@ -132,18 +134,18 @@ impl ChunkData {
}
}

#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
pub enum Tps {
Limited(u32),
Limited(f32),
Unlimited,
}

#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub struct WorldSendRate(pub u32);
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
pub struct WorldSendRate(pub f32);

impl Default for WorldSendRate {
fn default() -> Self {
Self(60)
Self(60.0)
}
}

Expand Down
2 changes: 2 additions & 0 deletions crates/save_data/src/plot_data/fixer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//! seperate download. As our save format changes in the future, the fixer
//! module may become quite big.

use super::v2_to_v3;
use super::{PlotData, PlotLoadError};
use crate::plot_data::VERSION;
use std::fs;
Expand Down Expand Up @@ -41,6 +42,7 @@ pub fn try_fix(path: impl AsRef<Path>, info: FixInfo) -> Result<Option<PlotData>
FixInfo::OldVersion {
version: version @ 0..=1,
} => return Err(PlotLoadError::ConversionUnavailable(version)),
FixInfo::OldVersion { version: 2 } => v2_to_v3::convert_v2_to_v3(&path)?,
_ => None,
};

Expand Down
Loading