World open: read the server's sync data beside the worldgen preload

The world data preload read the registries, tags, recipes and advancements
the server sends (lan::sync_data) after the worldgen bundle and the loot
tables. A launch into a world reached the server thread's own preload
before that, so the server started a second read of the same data and
then waited on SYNC's lock while the preload read it again: ~100 ms on
the server thread before settle_spawn (server.sync_adopt). The preload
now reads it on a thread of its own from the start.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
VaspyyyandClaude Opus 5.5 committed 2026-11-02 03:58:00 +01:00
1 parent 435730a405
commit 189e128d00
1 file changed
+37 -3
@@ -379,6 +379,7 @@ pub(super) fn preload_world_data(
.name("World data preload".into())
.spawn(move || {
horizon_threads::behind_server();
let sync = spawn_sync_preload(&resources, true);
// Errors are left for the world opening to report.
if let Ok(data) = horizon_client::vanillagen::shared_data(
&resources.join("worldgen"),
@@ -394,8 +395,7 @@ pub(super) fn preload_world_data(
&resources,
&horizon_world::loot::PackOverlay::NONE,
);
let _ =
super::lan::sync_data(&resources, &horizon_world::loot::PackOverlay::NONE);
join_sync_preload(sync, &resources);
});
match spawned {
Ok(thread) => *preload = Preload::Started { thread, left: None },
@@ -444,6 +444,7 @@ pub(super) fn preload_launch_world(
.spawn(move || {
// Not behind the server: the world's opening waits for it.
let started = std::time::Instant::now();
let sync = spawn_sync_preload(&resources, false);
// Errors are left for the world opening to report. The world's
// flags from its level.dat: the feature packs a flag forces on.
let enabled_features = horizon_client::save::Level::read(&world_root)
@@ -463,7 +464,7 @@ pub(super) fn preload_launch_world(
&resources,
&horizon_world::loot::PackOverlay::NONE,
);
let _ = super::lan::sync_data(&resources, &horizon_world::loot::PackOverlay::NONE);
join_sync_preload(sync, &resources);
log::info!(
"Horizon: world data preloaded for the launch's world in {} ms",
started.elapsed().as_millis()
@@ -475,6 +476,39 @@ pub(super) fn preload_launch_world(
}
}
/// The registries, tags, recipes and advancements the server sends
/// (`lan::sync_data`), read on a thread of their own beside the worldgen and
/// loot reads: they share nothing, and read after them they came too late
/// for the server thread, which then read them a second time.
fn spawn_sync_preload(
resources: &std::path::Path,
behind_server: bool,
) -> Option<std::thread::JoinHandle<()>> {
let resources = resources.to_owned();
std::thread::Builder::new()
.name("Sync data preload".into())
.spawn(move || {
if behind_server {
horizon_threads::behind_server();
}
let _ = super::lan::sync_data(&resources, &horizon_world::loot::PackOverlay::NONE);
})
.ok()
}
/// Waits for [`spawn_sync_preload`]'s thread, so the preload is over when
/// its own thread is; reads the data here if that thread did not start.
fn join_sync_preload(sync: Option<std::thread::JoinHandle<()>>, resources: &std::path::Path) {
match sync {
Some(thread) => {
let _ = thread.join();
}
None => {
let _ = super::lan::sync_data(resources, &horizon_world::loot::PackOverlay::NONE);
}
}
}
/// A world opens: what `preload_world_data` read is the world's from now on.
pub(super) fn world_data_used() {
*PRELOAD.lock().unwrap_or_else(|e| e.into_inner()) = Preload::Used;