Merge pull request #5019 from rdbtCVS/perf/preload-world-at-launch

Preload the world data of a launch that names a world
This commit is contained in:
rdbt authored and GitHub committed 2026-10-31 07:45:52 +00:00
commit 9d979caa48
3 files changed
+87 -7

No files matched your search

+16 -1
View File
@@ -16543,6 +16543,14 @@ fn synchronous_startup(options: &Options) -> bool {
|| std::env::var_os("HORIZON_NET_REPORT").is_some()
}
/// The local world a client's launch opens at once (`--world`,
/// `--quickPlaySingleplayer`), whose generator data can load with the game's.
fn launch_world(options: &Options) -> Option<&str> {
(options.execution_role.is_client() && options.server.is_none() && !options.test_terrain)
.then_some(options.world.as_deref())
.flatten()
}
/// A step of the resource load: on its own scoped thread, or, when the
/// settings allow a single worker, run where it is joined.
enum Step<'scope, T> {
@@ -16701,9 +16709,12 @@ fn gameplay_tables(
}
/// The synchronous launch loads both phases; an ordinary launch returns the
/// core bundle while the rest worker keeps loading.
/// core bundle while the rest worker keeps loading. `launch_world`: the world
/// the launch opens, whose data is preloaded meanwhile.
#[allow(clippy::too_many_arguments)]
fn load_resources(
mut root: PathBuf,
launch_world: Option<&str>,
vanilla_root: &std::path::Path,
settings: &Settings,
started: Instant,
@@ -16793,6 +16804,9 @@ fn load_resources(
let assets = Arc::new(assets);
let render = Arc::new(render);
let biomes = Arc::new(biomes);
if let Some(folder) = launch_world {
world_loading::preload_launch_world(folder, &root, assets.registry, biomes.clone());
}
let tables = Arc::new(MeshTables::new(&assets, &render, &facts, visibility)?);
let pack = PackResources::assemble(
&root,
@@ -17898,6 +17912,7 @@ pub fn run() -> Result<()> {
);
let loaded = load_resources(
options.resources.clone(),
launch_world(&options),
&vanilla_root,
&early_settings,
started,
@@ -241,6 +241,7 @@ pub(super) fn spawn_loader(
let settings = settings.clone();
let vanilla_root = vanilla_root.to_path_buf();
let dev_mods = options.dev_mods.clone();
let world = launch_world(options).map(str::to_owned);
let progress = progress.clone();
let handle = std::thread::Builder::new()
.name("resource-loader".into())
@@ -254,6 +255,7 @@ pub(super) fn spawn_loader(
let root = mods::content_root(&root, &settings.mods.enabled, &dev_mods);
let result = load_resources(
root,
world.as_deref(),
&vanilla_root,
&settings,
started,
@@ -325,6 +325,58 @@ pub(super) fn preload_world_data(
}
}
/// A launch that names a world (`--world`, `--quickPlaySingleplayer`): the
/// same preload, started as soon as the block registry and the biomes are
/// loaded, so it overlaps the rest of the startup instead of the world's
/// opening. Read for the world's own data packs and preset, the bundle and
/// decoration `make_streaming` asks for; a world they don't fit (one its
/// upgrade rewrites) reads its own when it opens.
pub(super) fn preload_launch_world(
folder: &str,
resources: &std::path::Path,
registry: &'static horizon_world::registry::BlockRegistry,
biomes: Arc<horizon_client::biome::Biomes>,
) {
let mut preload = PRELOAD.lock().unwrap_or_else(|e| e.into_inner());
if !matches!(*preload, Preload::Idle) {
return;
}
let Ok(dir) = std::env::current_dir() else {
return;
};
let world_root = dir.join("saves").join(folder);
let resources = resources.to_owned();
let spawned = std::thread::Builder::new()
.name("World data preload".into())
.spawn(move || {
// Not behind the server: the world's opening waits for it.
let started = std::time::Instant::now();
// Errors are left for the world opening to report.
let (overrides, _) = worldgen_overrides_of(&world_root, &biomes);
if let Ok(data) = horizon_client::vanillagen::shared_data_with(
&resources.join("worldgen"),
&overrides,
registry,
) {
let preset = horizon_client::save::WorldGen::preset(&world_root);
let _ = data.prepare_decoration(preset, "minecraft:overworld");
}
let _ = horizon_client::structure_blocks::loot_data(
&resources,
&horizon_world::loot::PackOverlay::NONE,
);
let _ = super::lan::sync_data(&resources, &horizon_world::loot::PackOverlay::NONE);
log::info!(
"Horizon: world data preloaded for the launch's world in {} ms",
started.elapsed().as_millis()
);
});
match spawned {
Ok(thread) => *preload = Preload::Started { thread, left: None },
Err(e) => log::warn!("Horizon: world data preload: {e}"),
}
}
/// 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;
@@ -360,18 +412,29 @@ fn world_worldgen_overrides(
world_root: &std::path::Path,
biomes: &horizon_client::biome::Biomes,
) -> horizon_world::worldgen::overrides::WorldgenOverrides {
let (overrides, warnings) = worldgen_overrides_of(world_root, biomes);
for warning in warnings {
log::warn!("Horizon: data pack worldgen: {warning}");
}
overrides
}
/// The worldgen entries of the world's enabled data packs, and what the
/// generator leaves out of them.
fn worldgen_overrides_of(
world_root: &std::path::Path,
biomes: &horizon_client::biome::Biomes,
) -> (
horizon_world::worldgen::overrides::WorldgenOverrides,
Vec<String>,
) {
let packs = horizon_client::datapack::discover(world_root);
let enabled = horizon_client::datapack::enabled_ids(world_root, &packs);
let enabled: Vec<_> = enabled
.iter()
.filter_map(|id| packs.iter().find(|p| &p.id == id))
.collect();
let (overrides, warnings) =
horizon_client::datapack::worldgen_overrides(&enabled, |b| biomes.contains(b));
for warning in warnings {
log::warn!("Horizon: data pack worldgen: {warning}");
}
overrides
horizon_client::datapack::worldgen_overrides(&enabled, |b| biomes.contains(b))
}
/// The shapes of the world's levels: their dimension types, the world's