Compare commits

...
Author SHA1 Message Date
Ludovic MarieandClaude Opus 5.5 729eaf1829 Dedicated server: read the world's data beside the game's resources
A launch that names a world preloads its worldgen bundle, decoration, loot
tables and the registries the server sends while the rest of the resources
load. A dedicated server read them only once its resources were in, one
after the other. It now preloads its own world (level-name, and level-type's
preset for a world not made yet) the same way.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-01 00:08:58 +01:00
3 changed files with 67 additions and 33 deletions

No files matched your search

+16 -2
View File
@@ -17804,8 +17804,22 @@ 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());
// A dedicated server's world, from server.properties (`dedicated::preflight`).
let launch = match launch_world {
Some(folder) => std::env::current_dir()
.ok()
.map(|dir| (dir.join("saves").join(folder), None)),
None if headless => dedicated::launch_world(),
None => None,
};
if let Some((world_root, preset)) = launch {
world_loading::preload_launch_world(
world_root,
preset,
&root,
assets.registry,
biomes.clone(),
);
}
let tables = Arc::new(MeshTables::new(&assets, &render, &facts, visibility)?);
let pack = PackResources::assemble(
+40 -19
View File
@@ -522,25 +522,14 @@ impl Game<'_> {
/// streaming reads them.
fn write_world_gen(&self, dir: &std::path::Path, props: &ServerProperties) -> Result<()> {
let level_type = &props.level_type;
let preset = parse_identifier(level_type)
.as_deref()
.and_then(horizon_client::save::WorldPreset::of_id)
.or_else(|| {
LEGACY_PRESET_NAMES
.iter()
.find(|(name, _)| *name == level_type)
.map(|(_, preset)| *preset)
})
.unwrap_or_else(|| {
log_from(
"Worker-Main-1",
"WARN",
&format!(
"Failed to parse level-type {level_type}, defaulting to minecraft:normal"
),
);
horizon_client::save::WorldPreset::Normal
});
let preset = level_type_preset(level_type).unwrap_or_else(|| {
log_from(
"Worker-Main-1",
"WARN",
&format!("Failed to parse level-type {level_type}, defaulting to minecraft:normal"),
);
horizon_client::save::WorldPreset::Normal
});
let flat = preset == horizon_client::save::WorldPreset::Flat;
let settings = if flat {
let biomes = world_biomes!(self);
@@ -565,6 +554,38 @@ impl Game<'_> {
}
}
/// `level-type`'s world preset (an identifier, or a legacy name such as
/// `DEFAULT`); none when it names neither.
fn level_type_preset(level_type: &str) -> Option<horizon_client::save::WorldPreset> {
parse_identifier(level_type)
.as_deref()
.and_then(horizon_client::save::WorldPreset::of_id)
.or_else(|| {
LEGACY_PRESET_NAMES
.iter()
.find(|(name, _)| *name == level_type)
.map(|(_, preset)| *preset)
})
}
/// The world this server opens, once `preflight` has read server.properties,
/// and, for a world not made yet, the preset `write_world_gen` will give it:
/// its data is read beside the game's resources (`preload_launch_world`).
pub(super) fn launch_world() -> Option<(PathBuf, Option<&'static str>)> {
let setup = SETUP.get()?.lock().unwrap_or_else(|e| e.into_inner());
let setup = setup.as_ref()?;
let world = std::env::current_dir()
.ok()?
.join(&setup.universe)
.join(&setup.level_name);
let preset = (!world.join("level.dat").exists()).then(|| {
level_type_preset(&setup.props.level_type)
.unwrap_or(horizon_client::save::WorldPreset::Normal)
.id()
});
Some((world, preset))
}
/// PlayerList.BAN_DATE_FORMAT ("yyyy-MM-dd 'at' HH:mm:ss z"), in UTC.
fn ban_date(millis: i64) -> String {
let s = format_date(millis);
+11 -12
View File
@@ -371,14 +371,16 @@ 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.
/// A launch that names a world (`--world`, `--quickPlaySingleplayer`, or a
/// dedicated server's `level-name`): 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 (`preset`: the one a new world will be written with, else
/// the world's own), 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,
world_root: std::path::PathBuf,
preset: Option<&'static str>,
resources: &std::path::Path,
registry: &'static horizon_world::registry::BlockRegistry,
biomes: Arc<horizon_client::biome::Biomes>,
@@ -387,10 +389,6 @@ pub(super) fn preload_launch_world(
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())
@@ -409,7 +407,8 @@ pub(super) fn preload_launch_world(
&overrides,
registry,
) {
let preset = horizon_client::save::WorldGen::preset(&world_root);
let preset =
preset.unwrap_or_else(|| horizon_client::save::WorldGen::preset(&world_root));
let _ = data.prepare_decoration(preset, "minecraft:overworld");
}
let _ = horizon_client::structure_blocks::loot_data(