Autosave counts down from max(100, tick rate * 300) ticks

MinecraftServer.computeNextAutosaveInterval asks for Math.max(100,
(int)(rate * 300.0F)) ticks, where the rate is the tick rate or, while /tick
sprint runs, the measured NANOSECONDS_PER_SECOND / (averageTickTime + 1);
tickServer counts ticksUntilAutosave down and saves when it reaches zero, and
onTickRateChanged shortens a wait already running. Horizon saved on every
six-thousandth tick whatever the rate, so /tick rate and /tick sprint changed
how long a world ran between saves and a slow rate kept the same interval.

The countdown is a field on Game, the interval is next_autosave_interval, and
/tick rate now calls on_tick_rate_changed. Twenty ticks a second is still the
six thousand ticks a world autosaved on, so nothing changes at the default.

Port map: the two rest-013 rows now name those functions.
This commit is contained in:
MSedgeMC committed 2026-11-02 09:58:19 +00:00
1 parent 3476a852df
commit 8bd75ba4dd
3 files changed
+57 -3

No files matched your search

+52 -1
View File
@@ -2610,6 +2610,9 @@ struct Game<'a> {
saving_indicator: horizon_client::hud_overlays::SavingIndicator,
/// This game's world is autosaving this tick (`MinecraftServer.isSaving`).
autosaving: bool,
/// `MinecraftServer.ticksUntilAutosave`: the ticks left before the next
/// autosave, counted down a tick at a time.
ticks_until_autosave: i64,
applied_modifiers: Vec<(String, String)>,
experience: Experience,
orbs: Vec<ExperienceOrb>,
@@ -7298,12 +7301,19 @@ impl Game<'_> {
self.world.weather.tick_levels();
self.tick_weather_effects();
}
self.autosaving = self.ticks.is_multiple_of(6000);
// MinecraftServer.tickServer's countdown, then autoSave: three
// hundred seconds of ticks at the rate the server runs at now.
self.ticks_until_autosave -= 1;
self.autosaving = self.ticks_until_autosave <= 0;
if let Some(serving) = &self.serving {
serving.set_saving(self.autosaving);
}
if self.autosaving {
self.save_game_with(false)?;
self.ticks_until_autosave = self.next_autosave_interval();
if let Some(serving) = &self.serving {
serving.set_saving(false);
}
}
if running {
self.advance_clock();
@@ -15326,6 +15336,25 @@ impl Game<'_> {
fn save_game(&mut self) -> Result<()> {
self.save_game_with(true)
}
/// `MinecraftServer.computeNextAutosaveInterval`: three hundred seconds
/// of ticks at the rate the server runs at now - its measured rate while
/// a `/tick sprint` runs - never fewer than a hundred.
fn next_autosave_interval(&self) -> i64 {
let rate = if self.clock.sprinting() {
1.0e9 / (self.clock.average_nanos() + 1) as f32
} else {
self.clock.rate()
};
autosave_interval(rate)
}
/// `MinecraftServer.onTickRateChanged`: a rate that asks for a shorter
/// wait than the one already running shortens it.
pub(super) fn on_tick_rate_changed(&mut self) {
let interval = self.next_autosave_interval();
if interval < self.ticks_until_autosave {
self.ticks_until_autosave = interval;
}
}
/// The autosave doesn't `wait` for the chunks to be on disk: every
/// generated chunk is saved, hundreds at a time, encoded and written on
/// the region writer meanwhile.
@@ -16984,6 +17013,10 @@ impl Game<'_> {
}
}
}
/// `computeNextAutosaveInterval`'s arithmetic: `Math.max(100, (int)(rate * 300.0F))`.
fn autosave_interval(rate: f32) -> i64 {
i64::from((rate * 300.0) as i32).max(100)
}
impl ApplicationHandler for Game<'_> {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
if self.window.is_some() {
@@ -18918,6 +18951,8 @@ impl<'a> Game<'a> {
tool_highlight: Default::default(),
saving_indicator: Default::default(),
autosaving: false,
// `computeNextAutosaveInterval` at the clock's 20 ticks a second.
ticks_until_autosave: 6000,
applied_modifiers: Vec::new(),
experience: Experience::default(),
orbs: Vec::new(),
@@ -21715,6 +21750,22 @@ mod tests {
use super::*;
use horizon_client::container_screen::{Button, Clicks, Release, Target};
/// `MinecraftServer.computeNextAutosaveInterval`: three hundred seconds of
/// ticks at the rate the server runs at, never fewer than a hundred.
#[test]
fn the_autosave_waits_for_three_hundred_seconds_of_ticks() {
// 20 ticks a second is the six thousand ticks a world autosaved on
// before the rate could be changed (`/tick rate`).
assert_eq!(autosave_interval(20.0), 6000);
assert_eq!(autosave_interval(10.0), 3000);
assert_eq!(autosave_interval(40.0), 12000);
assert_eq!(autosave_interval(1000.0), 300_000);
// `Math.max(100, ...)`: a very low rate still autosaves every hundred
// ticks, and a frozen clock (0) does too.
assert_eq!(autosave_interval(0.5), 150);
assert_eq!(autosave_interval(0.0), 100);
}
/// `NativeScreenView.id` (the `screen_render_post` hook) is a mod's
/// contract: every `Screen` has its own id, and it does not change.
/// `Screen::id`'s match makes a new variant give itself one; this list
@@ -81,6 +81,9 @@ impl Game<'_> {
_ => 20.0,
};
self.clock.set_rate(rate);
// MinecraftServer.setTickRate: the autosave wait already
// running is shortened when the new rate asks for less.
self.on_tick_rate_changed();
self.feedback(t(
"commands.tick.rate.success",
&[lit(format_fixed(f64::from(rate), 1))],
+2 -2
View File
@@ -78,8 +78,8 @@ MinecraftServer.loadStatusIcon inline crates/horizon-client/src/game/dedicated.r
MinecraftServer.processPacketsAndTick port crates/horizon-client/src/game/levels.rs::Game::tick_server reads the network, then ticks the levels
MinecraftServer.autoSave inline crates/horizon-client/src/game.rs::Game::tick gap: minor: the save runs every 6000 ticks; /save-off is checked in the dedicated loop, where the check does nothing, so it does not stop the save
MinecraftServer.logTickMethodTime skip:server tick timing log for the profiler
MinecraftServer.computeNextAutosaveInterval missing minor: the save runs every 6000 ticks (game.rs tick), not 300 s scaled by the tick rate
MinecraftServer.onTickRateChanged missing minor: the autosave interval does not follow /tick rate
MinecraftServer.computeNextAutosaveInterval port crates/horizon-client/src/game.rs::Game::next_autosave_interval Math.max(100, (int)(rate * 300.0F)) ticks counted down a tick at a time, at the tick rate or the measured rate while /tick sprint runs; twenty a second is still the six thousand a world autosaved on
MinecraftServer.onTickRateChanged port crates/horizon-client/src/game.rs::Game::on_tick_rate_changed /tick rate recomputes the wait and shortens the one already running when the new rate asks for less
MinecraftServer.buildPlayerStatus port crates/horizon-client/src/lan/mod.rs::Shared::status_json up to 12 sampled player names in the status JSON
MinecraftServer.updateEffectiveRespawnData inline crates/horizon-client/src/game/initial_spawn.rs::Game::spawn_finder gap: minor: no cached respawn data; the radius is clamped to the border when the spawn is found
MinecraftServer.forceGameTimeSynchronization port crates/horizon-client/src/lan/hub.rs::Hub::sync_world game time sent to guests with SetTime
1 DelegatingOps.DelegateListBuilder.build skip:java forwards to the original ListBuilder; codec plumbing
78 MinecraftServer.processPacketsAndTick port crates/horizon-client/src/game/levels.rs::Game::tick_server reads the network, then ticks the levels
79 MinecraftServer.autoSave inline crates/horizon-client/src/game.rs::Game::tick gap: minor: the save runs every 6000 ticks; /save-off is checked in the dedicated loop, where the check does nothing, so it does not stop the save
80 MinecraftServer.logTickMethodTime skip:server tick timing log for the profiler
81 MinecraftServer.computeNextAutosaveInterval missing port crates/horizon-client/src/game.rs::Game::next_autosave_interval minor: the save runs every 6000 ticks (game.rs tick), not 300 s scaled by the tick rate Math.max(100, (int)(rate * 300.0F)) ticks counted down a tick at a time, at the tick rate or the measured rate while /tick sprint runs; twenty a second is still the six thousand a world autosaved on
82 MinecraftServer.onTickRateChanged missing port crates/horizon-client/src/game.rs::Game::on_tick_rate_changed minor: the autosave interval does not follow /tick rate /tick rate recomputes the wait and shortens the one already running when the new rate asks for less
83 MinecraftServer.buildPlayerStatus port crates/horizon-client/src/lan/mod.rs::Shared::status_json up to 12 sampled player names in the status JSON
84 MinecraftServer.updateEffectiveRespawnData inline crates/horizon-client/src/game/initial_spawn.rs::Game::spawn_finder gap: minor: no cached respawn data; the radius is clamped to the border when the spawn is found
85 MinecraftServer.forceGameTimeSynchronization port crates/horizon-client/src/lan/hub.rs::Hub::sync_world game time sent to guests with SetTime