Skip to content

Commit 8ad7e3c

Browse files
committed
core: allow for rtps and wsr to be real numbers
1 parent f8b7b03 commit 8ad7e3c

8 files changed

Lines changed: 155 additions & 58 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ server_context = "global"
102102
| `/speed <speed>` | None | Sets your flyspeed. |
103103
| `/gamemode <mode>` | `/gmc`, `/gmsp` | Sets your gamemode. |
104104
| `/container <type> <power>` | None | Gives you a container (e.g. barrel) which outputs a specified amount of power when used with a comparator. |
105-
| `/worldsendrate [hertz]` | `/wsr` | Sets the world send rate to `[hertz]` (frequency of world updates sent to clients). Range: 1-1000. Default: 60. |
105+
| `/worldsendrate [hertz]` | `/wsr` | Sets the world send rate to `[hertz]` (frequency of world updates sent to clients). Range: 0-1000. Default: 60. |
106106
| `/toggleautorp` | None | Toggles automatic redpiler compilation. |
107107
| `/stop` | None | Stops the server. |
108108

‎crates/core/src/plot/commands.rs‎

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,11 @@ impl Plot {
293293
return false;
294294
}
295295

296-
let tps = if let Ok(tps) = args[0].parse::<u32>() {
296+
let tps = if let Ok(tps) = args[0].parse::<f32>() {
297+
if tps < 0.0 {
298+
self.players[player].send_error_message("RTPS must be cannot be negative!");
299+
return false;
300+
}
297301
Tps::Limited(tps)
298302
} else if !args[0].is_empty() && "unlimited".starts_with(args[0]) {
299303
Tps::Unlimited
@@ -490,7 +494,7 @@ impl Plot {
490494
"worldsendrate" | "wsr" => {
491495
if args.is_empty() {
492496
self.players[player].send_system_message(&format!(
493-
"Current world send rate: {} Hz",
497+
"Current world send rate: {:.2} Hz",
494498
self.world_send_rate.0
495499
));
496500
return false;
@@ -501,17 +505,18 @@ impl Plot {
501505
return false;
502506
}
503507

504-
let Ok(hertz) = args[0].parse::<u32>() else {
508+
let Ok(hertz) = args[0].parse::<f32>() else {
505509
self.players[player].send_error_message("Unable to parse send rate!");
506510
return false;
507511
};
508-
if hertz == 0 {
509-
self.players[player].send_error_message("The world send rate cannot be 0!");
512+
if hertz < 0.0 {
513+
self.players[player]
514+
.send_error_message("The world send rate cannot be negative!");
510515
return false;
511516
}
512-
if hertz > 1000 {
517+
if hertz > 1000.0 {
513518
self.players[player]
514-
.send_error_message("The world send rate cannot go higher than 1000!");
519+
.send_error_message("The world send rate cannot be higher than 1000!");
515520
return false;
516521
}
517522

@@ -615,7 +620,7 @@ pub static DECLARE_COMMANDS: Lazy<PacketEncoder> = Lazy::new(|| {
615620
children: vec![],
616621
redirect_node: None,
617622
name: Some("rtps"),
618-
parser: Some(Parser::Integer(0, i32::MAX)),
623+
parser: Some(Parser::Float(0.0, f32::MAX)),
619624
suggestions_type: None,
620625
},
621626
// 8: /radvance
@@ -999,13 +1004,13 @@ pub static DECLARE_COMMANDS: Lazy<PacketEncoder> = Lazy::new(|| {
9991004
parser: None,
10001005
suggestions_type: None,
10011006
},
1002-
// 50: /worldsendrate [rticks]
1007+
// 50: /worldsendrate [hertz]
10031008
Node {
10041009
flags: (CommandFlags::ARGUMENT | CommandFlags::EXECUTABLE).bits() as i8,
10051010
children: vec![],
10061011
redirect_node: None,
10071012
name: Some("hertz"),
1008-
parser: Some(Parser::Integer(0, 1000)),
1013+
parser: Some(Parser::Float(0.0, 1000.0)),
10091014
suggestions_type: None,
10101015
},
10111016
// 51: /wsr

‎crates/core/src/plot/data.rs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use std::time::Duration;
99
pub fn sleep_time_for_tps(tps: Tps) -> Duration {
1010
match tps {
1111
Tps::Limited(tps) => {
12-
if tps > 10 {
13-
Duration::from_micros(1_000_000 / tps as u64)
12+
if tps > 10.0 {
13+
Duration::from_secs_f32(1.0 / tps)
1414
} else {
1515
Duration::from_millis(50)
1616
}
@@ -53,7 +53,7 @@ static EMPTY_PLOT: Lazy<PlotData> = Lazy::new(|| {
5353
};
5454
let chunk_data: Vec<ChunkData> = world.chunks.iter_mut().map(ChunkData::new).collect();
5555
PlotData {
56-
tps: Tps::Limited(10),
56+
tps: Tps::Limited(10.0),
5757
world_send_rate: WorldSendRate::default(),
5858
chunk_data,
5959
pending_ticks: Vec::new(),

‎crates/core/src/plot/mod.rs‎

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -987,25 +987,31 @@ impl Plot {
987987
let now = Instant::now();
988988
self.last_player_time = now;
989989

990-
let world_send_rate =
991-
Duration::from_nanos(1_000_000_000 / self.world_send_rate.0 as u64);
990+
let world_send_rate = if self.world_send_rate.0 == 0.0 {
991+
Duration::MAX
992+
} else {
993+
Duration::from_secs_f32(1.0 / self.world_send_rate.0)
994+
};
992995

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

10031009
let batch_size = match self.tps {
1004-
Tps::Limited(tps) if tps != 0 => {
1005-
let dur_per_tick = Duration::from_nanos(1_000_000_000 / tps as u64);
1010+
Tps::Limited(tps) if tps != 0.0 => {
1011+
let dur_per_tick = Duration::from_secs_f32(1.0 / tps);
10061012
self.lag_time += now - self.last_update_time;
1007-
let batch_size = (self.lag_time.as_nanos() / dur_per_tick.as_nanos()) as u64;
1008-
self.lag_time -= dur_per_tick * batch_size as u32;
1013+
let batch_size = (self.lag_time.as_nanos() / dur_per_tick.as_nanos()) as u32;
1014+
self.lag_time -= dur_per_tick * batch_size;
10091015
batch_size.min(max_batch_size)
10101016
}
10111017
Tps::Unlimited => max_batch_size,
@@ -1014,24 +1020,24 @@ impl Plot {
10141020

10151021
self.last_update_time = now;
10161022
if batch_size != 0 {
1017-
// 50_000 (= 3.33 MHz) here is arbitrary.
1018-
// We just need a number that's not too high so we actually get around to sending
1019-
// block updates.
1020-
let batch_size = batch_size.min(50_000) as u32;
1021-
let mut ticks_completed = batch_size;
1023+
let mut ticks_completed = 0;
10221024
if self.redpiler.is_active() {
10231025
self.tickn(batch_size as u64);
10241026
self.redpiler.flush(&mut self.world);
1027+
ticks_completed += batch_size;
10251028
} else {
1026-
for i in 0..batch_size {
1029+
for _ in 0..batch_size {
10271030
self.tick();
1031+
ticks_completed += 1;
10281032
if now.elapsed() > Duration::from_millis(200) {
1029-
ticks_completed = i + 1;
10301033
break;
10311034
}
10321035
}
10331036
}
1034-
self.last_nspt = Some(self.last_update_time.elapsed() / ticks_completed);
1037+
1038+
if ticks_completed != 0 {
1039+
self.last_nspt = Some(self.last_update_time.elapsed() / ticks_completed);
1040+
}
10351041
}
10361042

10371043
if self.auto_redpiler

‎crates/core/src/plot/monitor.rs‎

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,41 @@ use std::thread::JoinHandle;
77
use std::time::Duration;
88
use tracing::warn;
99

10+
const MONITOR_SCHEDULE: Duration = Duration::from_millis(500);
11+
1012
#[derive(Default)]
1113
struct AtomicTps {
12-
tps: AtomicU32,
13-
unlimited: AtomicBool,
14+
tps_bits: AtomicU32,
1415
}
1516

1617
impl AtomicTps {
1718
fn from_tps(tps: Tps) -> Self {
18-
match tps {
19-
Tps::Limited(tps) => AtomicTps {
20-
tps: AtomicU32::new(tps),
21-
unlimited: AtomicBool::new(false),
22-
},
23-
Tps::Unlimited => AtomicTps {
24-
tps: AtomicU32::new(0),
25-
unlimited: AtomicBool::new(true),
26-
},
19+
AtomicTps {
20+
tps_bits: AtomicU32::new(Self::tps_to_bits(tps)),
2721
}
2822
}
2923

3024
fn update(&self, tps: Tps) {
25+
self.tps_bits
26+
.store(Self::tps_to_bits(tps), Ordering::Relaxed);
27+
}
28+
29+
fn tps_to_bits(tps: Tps) -> u32 {
3130
match tps {
32-
Tps::Limited(tps) => {
33-
self.tps.store(tps, Ordering::Relaxed);
34-
self.unlimited.store(false, Ordering::Relaxed);
31+
Tps::Limited(tps) if tps.is_nan() => {
32+
panic!("Tps should never be NaN under any circumstance")
3533
}
36-
Tps::Unlimited => self.unlimited.store(true, Ordering::Relaxed),
34+
Tps::Limited(tps) => tps.to_bits(),
35+
Tps::Unlimited => f32::NAN.to_bits(),
36+
}
37+
}
38+
39+
fn get(&self) -> Tps {
40+
let tps = f32::from_bits(self.tps_bits.load(Ordering::Relaxed));
41+
if tps.is_nan() {
42+
Tps::Unlimited
43+
} else {
44+
Tps::Limited(tps)
3745
}
3846
}
3947
}
@@ -148,13 +156,13 @@ impl TimingsMonitor {
148156

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

155163
let mut behind_for = 0;
156164
loop {
157-
thread::sleep(Duration::from_millis(500));
165+
thread::sleep(MONITOR_SCHEDULE);
158166
if !data.running.load(Ordering::Relaxed) {
159167
return;
160168
}
@@ -166,7 +174,7 @@ impl TimingsMonitor {
166174
let ticks_passed = (ticks_count - last_ticks_count) as u32;
167175
last_ticks_count = ticks_count;
168176

169-
let tps = data.tps.tps.load(Ordering::Relaxed);
177+
let tps = data.tps.get();
170178
let ticking = data.ticking.load(Ordering::Relaxed);
171179
if !(ticking && was_ticking_before)
172180
|| tps != last_tps
@@ -179,8 +187,14 @@ impl TimingsMonitor {
179187
}
180188

181189
// 5% threshold
182-
if data.tps.unlimited.load(Ordering::Relaxed) || ticks_passed < (tps / 2) * 95 / 100
183-
{
190+
let is_behind = match tps {
191+
Tps::Unlimited => false,
192+
Tps::Limited(tps_val) => {
193+
(ticks_passed as f32) < tps_val * MONITOR_SCHEDULE.as_secs_f32() * 0.95
194+
}
195+
};
196+
197+
if is_behind {
184198
behind_for += 1;
185199
} else {
186200
behind_for = 0;
@@ -190,8 +204,8 @@ impl TimingsMonitor {
190204
if behind_for >= 3 {
191205
data.too_slow.store(true, Ordering::Relaxed);
192206
// warn!(
193-
// "running behind by {} ticks",
194-
// ((tps / 2) * 95 / 100) - ticks_passed
207+
// "running behind by {:.1} ticks",
208+
// (tps * MONITOR_SCHEDULE.as_secs_f32() * 0.95) - (ticks_passed as f32)
195209
// );
196210
}
197211

‎crates/save_data/src/plot_data.rs‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod fixer;
2+
mod v2_to_v3;
23

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

2325
#[derive(Error, Debug)]
2426
pub enum PlotLoadError {
@@ -132,18 +134,18 @@ impl ChunkData {
132134
}
133135
}
134136

135-
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
137+
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
136138
pub enum Tps {
137-
Limited(u32),
139+
Limited(f32),
138140
Unlimited,
139141
}
140142

141-
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
142-
pub struct WorldSendRate(pub u32);
143+
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
144+
pub struct WorldSendRate(pub f32);
143145

144146
impl Default for WorldSendRate {
145147
fn default() -> Self {
146-
Self(60)
148+
Self(60.0)
147149
}
148150
}
149151

‎crates/save_data/src/plot_data/fixer.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//! seperate download. As our save format changes in the future, the fixer
88
//! module may become quite big.
99
10+
use super::v2_to_v3;
1011
use super::{PlotData, PlotLoadError};
1112
use crate::plot_data::VERSION;
1213
use std::fs;
@@ -41,6 +42,7 @@ pub fn try_fix(path: impl AsRef<Path>, info: FixInfo) -> Result<Option<PlotData>
4142
FixInfo::OldVersion {
4243
version: version @ 0..=1,
4344
} => return Err(PlotLoadError::ConversionUnavailable(version)),
45+
FixInfo::OldVersion { version: 2 } => v2_to_v3::convert_v2_to_v3(&path)?,
4446
_ => None,
4547
};
4648

0 commit comments

Comments
 (0)