Add the amplified, large-biome and single-biome world types

The create screen only offered Default and Superflat, so the amplified
world type of #1873 could not be chosen at all, and the generator
always built the overworld's noise settings.

The world-type button now cycles through the presets vanilla's
WorldPresetTags.NORMAL tag holds (normal, flat, large_biomes, amplified,
single_biome_surface), the chosen preset is written into the new world's
world_gen_settings.dat with the generator
worldgen/world_preset/<preset>.json has, and the generator is built from
that preset, so an amplified world gets the amplified noise settings.
A world opened again reads its preset back from its generator.

Co-Authored-By: minimax-m3.1-flash (Claude Code) <[email protected]>
This commit is contained in:
Claude Codeandminimax-m3.1-flash authored and levzzz committed 2026-10-27 16:11:14 +05:00
1 parent 9c62aba384
commit bf05176692
11 files changed
+313 -83

No files matched your search

+1 -1
View File
@@ -564,7 +564,7 @@ impl Game<'_> {
"hardcore": options.hardcore,
"difficulty": horizon_client::title::DIFFICULTIES[options.difficulty()],
"allow_commands": options.commands(*creative),
"superflat": options.flat,
"world_type": options.world_type.id(),
"generate_structures": options.structures,
"bonus_chest": options.bonus_chest(),
}),
+20 -22
View File
@@ -298,14 +298,14 @@ impl Lists {
}
}
/// The world presets of 26.3's `worldgen/world_preset`.
const WORLD_PRESETS: [&str; 6] = [
"minecraft:normal",
"minecraft:flat",
"minecraft:large_biomes",
"minecraft:amplified",
"minecraft:single_biome_surface",
"minecraft:debug_all_block_states",
/// DedicatedServerProperties.LEGACY_PRESET_NAMES: the old `level-type`
/// names, before the presets had their own ids.
const LEGACY_PRESET_NAMES: [(&str, horizon_client::save::WorldPreset); 2] = [
("default", horizon_client::save::WorldPreset::Normal),
(
"largebiomes",
horizon_client::save::WorldPreset::LargeBiomes,
),
];
/// Identifier.tryParse: `namespace:path`, or a path in `minecraft`.
@@ -322,11 +322,18 @@ impl Game<'_> {
/// DedicatedServerProperties.createDimensions for a world that doesn't
/// exist yet: `level-type`'s preset, a flat world's `generator-settings`
/// and `generate-structures`, written before the world opens so that
/// streaming reads them. Horizon generates the normal and flat presets.
/// 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)
.filter(|id| WORLD_PRESETS.contains(&id.as_str()))
.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",
@@ -335,18 +342,9 @@ impl Game<'_> {
"Failed to parse level-type {level_type}, defaulting to minecraft:normal"
),
);
"minecraft:normal".to_owned()
horizon_client::save::WorldPreset::Normal
});
let flat = preset == "minecraft:flat";
if !flat && preset != "minecraft:normal" {
log_from(
"Worker-Main-1",
"WARN",
&format!(
"Horizon doesn't generate the {preset} world preset yet: using minecraft:normal"
),
);
}
let flat = preset == horizon_client::save::WorldPreset::Flat;
let settings = if flat {
let biomes = self.res.biomes();
let (settings, errors) = horizon_client::flatgen::settings_from_json(
@@ -362,7 +360,7 @@ impl Game<'_> {
None
};
let options = horizon_client::save::WorldGen {
flat,
preset,
structures: props.generate_structures,
bonus_chest: false,
};
@@ -322,11 +322,13 @@ pub(super) fn make_streaming(
res.threads(),
)
} else {
// WorldOptions.generateStructures, from the world's settings.
let structures =
horizon_client::save::WorldGen::read(world_root).is_none_or(|w| w.structures);
let inner = Arc::new(VanillaTerrain::for_world(&options.resources.join("worldgen"), pack.assets.registry, res.biomes(), seed, dim.id(), structures).map_err(|e| {
format!("{e}\nPrepare the worldgen bundle with the launcher, or: horizon-prepare --prepare")
// WorldOptions.generateStructures and the world preset, from the
// world's settings.
let world_gen = horizon_client::save::WorldGen::read(world_root);
let structures = world_gen.is_none_or(|w| w.structures);
let preset = world_gen.map_or(horizon_client::save::WorldPreset::Normal, |w| w.preset);
let inner = Arc::new(VanillaTerrain::for_world(&options.resources.join("worldgen"), pack.assets.registry, res.biomes(), seed, dim.id(), preset.id(), structures).map_err(|e| {
format!("{e}\nPrepare the worldgen bundle with the launcher, or: horizon-prepare --prepare)")
})?);
if dim == Dim::Overworld && structures {
inner.search_rings_behind();
+102 -17
View File
@@ -2506,14 +2506,83 @@ const INT_RULES: [&str; 11] = [
"respawn_radius",
];
/// The world presets of 26.3's `worldgen/world_preset` that the create
/// screen offers, in the order of `WorldPresetTags.NORMAL`.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum WorldPreset {
Normal,
Flat,
LargeBiomes,
Amplified,
SingleBiomeSurface,
}
impl WorldPreset {
/// The presets, in the order the world-type button cycles through them.
pub const ALL: [Self; 5] = [
Self::Normal,
Self::Flat,
Self::LargeBiomes,
Self::Amplified,
Self::SingleBiomeSurface,
];
/// The preset's registry id (`minecraft:normal`).
pub const fn id(self) -> &'static str {
match self {
Self::Normal => "minecraft:normal",
Self::Flat => "minecraft:flat",
Self::LargeBiomes => "minecraft:large_biomes",
Self::Amplified => "minecraft:amplified",
Self::SingleBiomeSurface => "minecraft:single_biome_surface",
}
}
/// The preset an id names, if it is one of them.
pub fn of_id(id: &str) -> Option<Self> {
Self::ALL.into_iter().find(|preset| preset.id() == id)
}
/// The language key the world-type button shows
/// (`Identifier.toLanguageKey("generator")`).
pub const fn label(self) -> &'static str {
match self {
Self::Normal => "generator.minecraft.normal",
Self::Flat => "generator.minecraft.flat",
Self::LargeBiomes => "generator.minecraft.large_biomes",
Self::Amplified => "generator.minecraft.amplified",
Self::SingleBiomeSurface => "generator.minecraft.single_biome_surface",
}
}
/// The preset the world-type button cycles to next.
pub fn next(self) -> Self {
let index = Self::ALL
.iter()
.position(|preset| *preset == self)
.unwrap_or(0);
Self::ALL[(index + 1) % Self::ALL.len()]
}
/// The preset a saved overworld generator is: the noise settings name
/// the preset, and the single-biome preset shares the overworld's noise
/// settings, so its fixed biome source tells the two apart.
pub fn of_generator(generator: &Tag) -> Self {
if text(generator.get("type")).as_deref() == Some("minecraft:flat") {
return Self::Flat;
}
let source = generator.get("biome_source");
let fixed = text(source.and_then(|s| s.get("type"))).as_deref() == Some("minecraft:fixed");
match text(generator.get("settings")).as_deref() {
Some("minecraft:large_biomes") => Self::LargeBiomes,
Some("minecraft:amplified") => Self::Amplified,
Some("minecraft:overworld") if fixed => Self::SingleBiomeSurface,
_ => Self::Normal,
}
}
}
/// World and player state stored in `level.dat`, the saved-data files and
/// the player file.
/// The world options CreateWorldScreen sets (`WorldOptions` and the world
/// preset) that `world_gen_settings` keeps.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct WorldGen {
/// The `minecraft:flat` preset (Superflat) instead of `normal`.
pub flat: bool,
/// The world preset the world is created with.
pub preset: WorldPreset,
pub structures: bool,
pub bonus_chest: bool,
}
@@ -2527,8 +2596,7 @@ impl WorldGen {
.and_then(|d| d.get("minecraft:overworld"))
.and_then(|o| o.get("generator"));
Some(Self {
flat: text(generator.and_then(|g| g.get("type")))
.is_some_and(|t| t.trim_start_matches("minecraft:") == "flat"),
preset: generator.map_or(WorldPreset::Normal, WorldPreset::of_generator),
structures: flag("generate_structures", true),
bonus_chest: flag("bonus_chest", false),
})
@@ -2537,7 +2605,7 @@ impl WorldGen {
impl Default for WorldGen {
fn default() -> Self {
Self {
flat: false,
preset: WorldPreset::Normal,
structures: true,
bonus_chest: false,
}
@@ -2612,6 +2680,34 @@ pub fn write_world_gen_settings_with(
("preset", string(p)),
])
};
// The overworld generator of worldgen/world_preset/<preset>.json: the
// noise settings and the biome source each preset uses.
let overworld = match options.preset {
WorldPreset::Flat => flat,
WorldPreset::LargeBiomes => dimension(
"minecraft:overworld",
"minecraft:large_biomes",
preset("minecraft:overworld"),
),
WorldPreset::Amplified => dimension(
"minecraft:overworld",
"minecraft:amplified",
preset("minecraft:overworld"),
),
WorldPreset::SingleBiomeSurface => dimension(
"minecraft:overworld",
"minecraft:overworld",
Tag::compound([
("type", string("minecraft:fixed")),
("biome", string("minecraft:plains")),
]),
),
WorldPreset::Normal => dimension(
"minecraft:overworld",
"minecraft:overworld",
preset("minecraft:overworld"),
),
};
write_saved_data(
dir,
"world_gen_settings",
@@ -2625,18 +2721,7 @@ pub fn write_world_gen_settings_with(
(
"dimensions",
Tag::compound([
(
"minecraft:overworld",
if options.flat {
flat
} else {
dimension(
"minecraft:overworld",
"minecraft:overworld",
preset("minecraft:overworld"),
)
},
),
("minecraft:overworld", overworld),
(
"minecraft:the_nether",
dimension(
@@ -30,8 +30,8 @@ pub struct WorldOptions {
pub difficulty: usize,
/// Allow Cheats as toggled; unset follows the game mode.
pub commands: Option<bool>,
/// The Superflat world preset instead of Default.
pub flat: bool,
/// The world preset the world is created with.
pub world_type: crate::save::WorldPreset,
pub structures: bool,
pub bonus_chest: bool,
/// `getGameRules`: the Game Rules screen's values, saved with the world.
@@ -44,7 +44,7 @@ impl Default for WorldOptions {
hardcore: false,
difficulty: 2,
commands: None,
flat: false,
world_type: crate::save::WorldPreset::Normal,
structures: true,
bonus_chest: false,
rules: crate::gamerules::GameRules::default(),
@@ -66,7 +66,7 @@ impl WorldOptions {
.position(|d| *d == level.difficulty)
.unwrap_or(2),
commands: Some(commands),
flat: generation.flat,
world_type: generation.preset,
structures: generation.structures,
bonus_chest: generation.bonus_chest,
rules: crate::gamerules::GameRules::load(dir),
@@ -87,7 +87,7 @@ impl WorldOptions {
}
pub fn world_gen(&self) -> crate::save::WorldGen {
crate::save::WorldGen {
flat: self.flat,
preset: self.world_type,
structures: self.structures,
bonus_chest: self.bonus_chest(),
}
@@ -105,7 +105,7 @@ impl WorldOptions {
}
23 => self.difficulty = (self.difficulty + 1) % DIFFICULTIES.len(),
24 => self.commands = Some(!self.commands(*creative)),
25 => self.flat = !self.flat,
25 => self.world_type = self.world_type.next(),
27 => self.structures = !self.structures,
28 => self.bonus_chest = !self.bonus_chest,
_ => {}
@@ -236,14 +236,7 @@ pub(super) fn buttons(w: f32, h: f32, creative: bool, o: &WorldOptions) -> Vec<B
gx,
gy,
150.0,
labelled(
"selectWorld.mapType",
tr(if o.flat {
"generator.minecraft.flat"
} else {
"generator.minecraft.normal"
}),
),
labelled("selectWorld.mapType", tr(o.world_type.label())),
true,
25,
),
+95 -13
View File
@@ -745,27 +745,49 @@ fn create_world_tabs_follow_vanilla_and_their_choices_reach_the_action() {
};
assert!(*creative && !options.hardcore && options.commands(true));
// The World tab (second of three 124-wide tabs from x 54): its seed box
// takes the focus, Superflat and the switches toggle.
// takes the focus, the world type cycles and the switches toggle.
let (action, sound) = f.title.click(54.0 + 124.0 + 10.0, 10.0, size);
assert!(sound && action == Action::None);
let world = labels(&f);
assert_eq!((world[0].1, world[0].2), (85.0, 38.0));
assert_eq!((world[2].1, world[2].2), (85.0 + 266.0, 38.0 + 73.0));
f.title.click(world[0].1 + 10.0, world[0].2 + 10.0, size);
// The world-type button cycles through vanilla's world presets.
let world_type = |f: &Fixture| -> crate::save::WorldPreset {
let Mode::Create { options, .. } = &f.title.mode else {
panic!()
};
options.world_type
};
assert_eq!(world_type(&f), crate::save::WorldPreset::Normal);
for expected in [
crate::save::WorldPreset::Flat,
crate::save::WorldPreset::LargeBiomes,
crate::save::WorldPreset::Amplified,
crate::save::WorldPreset::SingleBiomeSurface,
crate::save::WorldPreset::Normal,
] {
f.title.click(world[0].1 + 10.0, world[0].2 + 10.0, size);
assert_eq!(world_type(&f), expected);
}
// The amplified preset is the third one.
for _ in 0..3 {
f.title.click(world[0].1 + 10.0, world[0].2 + 10.0, size);
}
f.title.click(world[3].1 + 10.0, world[3].2 + 10.0, size);
f.title.click(100.0, 38.0 + 41.0 + 10.0, size);
let Mode::Create { focus, options, .. } = &f.title.mode else {
panic!()
};
assert_eq!(*focus, 1);
assert!(options.flat && options.bonus_chest());
assert_eq!(options.world_type, crate::save::WorldPreset::Amplified);
assert!(options.bonus_chest());
let Action::Create { options, .. } = f.title.create_action() else {
panic!()
};
assert_eq!(
options.world_gen(),
crate::save::WorldGen {
flat: true,
preset: crate::save::WorldPreset::Amplified,
structures: true,
bonus_chest: true
}
@@ -783,26 +805,86 @@ fn new_world_generation_settings_round_trip() {
let f = Fixture::new();
let dir = f.root.join("flat");
let options = crate::save::WorldGen {
flat: true,
preset: crate::save::WorldPreset::Flat,
structures: false,
bonus_chest: true,
};
crate::save::write_world_gen_settings(&dir, 42, options).unwrap();
assert_eq!(crate::save::WorldGen::read(&dir), Some(options));
let settings = crate::save::world_gen_settings(&dir).unwrap();
let generator = settings
.get("dimensions")
.and_then(|d| d.get("minecraft:overworld"))
.and_then(|o| o.get("generator"));
assert_eq!(
crate::save::text(
settings
.get("dimensions")
.and_then(|d| d.get("minecraft:overworld"))
.and_then(|o| o.get("generator"))
.and_then(|g| g.get("type"))
)
.as_deref(),
crate::save::text(generator.and_then(|g| g.get("type"))).as_deref(),
Some("minecraft:flat")
);
}
#[test]
fn each_world_preset_writes_its_own_generator() {
let f = Fixture::new();
let generator = |preset: crate::save::WorldPreset| {
let dir = f.root.join(preset.id());
let options = crate::save::WorldGen {
preset,
structures: true,
bonus_chest: false,
};
crate::save::write_world_gen_settings(&dir, 42, options).unwrap();
assert_eq!(crate::save::WorldGen::read(&dir), Some(options));
let settings = crate::save::world_gen_settings(&dir).unwrap();
let generator = settings
.get("dimensions")
.and_then(|d| d.get("minecraft:overworld"))
.and_then(|o| o.get("generator"))
.cloned()
.unwrap();
(
crate::save::text(generator.get("type")),
crate::save::text(generator.get("settings")),
crate::save::text(generator.get("biome_source").and_then(|s| s.get("type"))),
)
};
assert_eq!(
generator(crate::save::WorldPreset::Normal),
(
Some("minecraft:noise".to_owned()),
Some("minecraft:overworld".to_owned()),
Some("minecraft:multi_noise".to_owned()),
)
);
assert_eq!(
generator(crate::save::WorldPreset::Amplified),
(
Some("minecraft:noise".to_owned()),
Some("minecraft:amplified".to_owned()),
Some("minecraft:multi_noise".to_owned()),
)
);
assert_eq!(
generator(crate::save::WorldPreset::LargeBiomes),
(
Some("minecraft:noise".to_owned()),
Some("minecraft:large_biomes".to_owned()),
Some("minecraft:multi_noise".to_owned()),
)
);
assert_eq!(
generator(crate::save::WorldPreset::SingleBiomeSurface),
(
Some("minecraft:noise".to_owned()),
Some("minecraft:overworld".to_owned()),
Some("minecraft:fixed".to_owned()),
)
);
assert_eq!(
generator(crate::save::WorldPreset::Flat).0,
Some("minecraft:flat".to_owned())
);
}
#[test]
fn world_fields_edit_at_their_cursor() {
use winit::keyboard::KeyCode;
+17 -5
View File
@@ -46,6 +46,8 @@ pub struct VanillaTerrain {
block_entity_types: Vec<Option<&'static str>>,
seed: i64,
dimension: String,
/// The world preset the generator was built from.
preset: String,
/// When the last proto chunk was generated, in milliseconds since
/// `created`: the ring search waits for a pause in generation.
last_proto: std::sync::atomic::AtomicU64,
@@ -114,13 +116,21 @@ impl VanillaTerrain {
.unwrap_or(0),
}
}
/// The overworld of the `minecraft:normal` world preset.
pub fn new(
dir: &Path,
registry: &'static BlockRegistry,
biomes: &Biomes,
seed: i64,
) -> crate::Result<Self> {
Self::for_dimension(dir, registry, biomes, seed, "minecraft:overworld")
Self::for_dimension(
dir,
registry,
biomes,
seed,
"minecraft:overworld",
"minecraft:normal",
)
}
pub fn for_dimension(
dir: &Path,
@@ -128,18 +138,20 @@ impl VanillaTerrain {
biomes: &Biomes,
seed: i64,
dimension: &str,
preset: &str,
) -> crate::Result<Self> {
Self::for_world(dir, registry, biomes, seed, dimension, true)
Self::for_world(dir, registry, biomes, seed, dimension, preset, true)
}
/// A dimension of a world whose `generate_structures` is `structures`
/// (`ChunkGenerator.createStructures` and `findNearestMapStructure` do
/// nothing without).
/// nothing without) and whose world preset is `preset`.
pub fn for_world(
dir: &Path,
registry: &'static BlockRegistry,
biomes: &Biomes,
seed: i64,
dimension: &str,
preset: &str,
structures: bool,
) -> crate::Result<Self> {
let data: Arc<GeneratorData> = shared_data(dir, registry)?;
@@ -148,8 +160,7 @@ impl VanillaTerrain {
.iter()
.map(|name| biomes.id(name))
.collect();
let generator =
OverworldGenerator::for_world(data, seed, "minecraft:normal", dimension, structures)?;
let generator = OverworldGenerator::for_world(data, seed, preset, dimension, structures)?;
let unsupported = generator.unsupported_features();
if !unsupported.is_empty() {
log::warn!(
@@ -200,6 +211,7 @@ impl VanillaTerrain {
block_entity_types,
seed,
dimension: dimension.to_owned(),
preset: preset.to_owned(),
last_proto: std::sync::atomic::AtomicU64::new(0),
created: std::time::Instant::now(),
})
+1 -1
View File
@@ -67,7 +67,7 @@ impl Builder {
let generator = match OverworldGenerator::for_dimension(
terrain.generator.data().clone(),
terrain.seed,
"minecraft:normal",
&terrain.preset,
&terrain.dimension,
) {
Ok(generator) => generator,
@@ -257,6 +257,7 @@ fn saved_proto_chunks_restore_exactly() {
&biomes,
-1860902098141295193,
"minecraft:the_nether",
"minecraft:normal",
)
.unwrap();
let dir = std::env::temp_dir().join(format!("horizon-proto-saves-{}", std::process::id()));
@@ -337,6 +338,7 @@ fn saved_proto_block_entities_keep_their_id() {
&biomes,
12345,
"minecraft:overworld",
"minecraft:normal",
)
.unwrap();
let dir = std::env::temp_dir().join(format!("horizon-proto-ids-{}", std::process::id()));
@@ -3225,4 +3225,56 @@ mod tests {
assert!(Arc::ptr_eq(first, hit));
}
}
/// The resources the worldgen tests read.
fn test_root() -> std::path::PathBuf {
std::env::var_os("HORIZON_TEST_RESOURCES")
.or_else(|| std::env::var_os("HORIZON_RESOURCES"))
.map(std::path::PathBuf::from)
.unwrap_or_else(|| {
Path::new(env!("CARGO_MANIFEST_DIR")).join("../../.local/horizon-client/26.3")
})
}
/// The block registry the worldgen bundle needs. `GeneratorData` keeps it
/// for as long as the generator lives, so it is shared, not freed.
static TEST_REGISTRY: std::sync::LazyLock<BlockRegistry> = std::sync::LazyLock::new(|| {
crate::level::data::load_registry(&test_root()).expect("registry")
});
/// The world presets with their own noise settings generate the terrain
/// those settings describe: the amplified and large-biome presets are not
/// the overworld's terrain under another name.
#[test]
fn world_presets_use_their_own_noise_settings() {
let root = test_root();
if !root.join("worldgen/world_presets.json").exists() {
eprintln!("skipping: no worldgen bundle at {}", root.display());
return;
}
let data =
GeneratorData::load(&root.join("worldgen"), &*TEST_REGISTRY).expect("worldgen data");
let surface = |preset: &str| {
let generator = OverworldGenerator::for_world(
data.clone(),
1234,
preset,
"minecraft:overworld",
false,
)
.expect("generator");
let mut worker = generator.worker();
(0..32)
.map(|i| {
worker
.surface_sample(i * 137, i * 91)
.expect("surface")
.height
})
.collect::<Vec<_>>()
};
let normal = surface("minecraft:normal");
assert!(normal.iter().all(|height| (0..384).contains(height)));
assert_ne!(normal, surface("minecraft:amplified"));
assert_ne!(normal, surface("minecraft:large_biomes"));
}
}
+9 -5
View File
@@ -15,8 +15,8 @@ the worker's number is left out). In a world without structures (`generate-struc
finds none) and how many of the saved chunks hold structure starts or references (none:
ChunkStatusTasks.generateStructureStarts does nothing).
Horizon generates the normal and flat presets only: `large_biomes`, `amplified`, `single_biome_surface` and
`debug_all_block_states` aren't tested.
Horizon generates the normal, flat, amplified, large-biome and single-biome presets:
`debug_all_block_states` isn't tested.
"""
import json
import os
@@ -61,6 +61,9 @@ CASES = {
'generator-settings': '{"layers":[{"block":"minecraft:glass","height":5}],"biome":"nowhere"}',
},
'normal': {'level-type': 'minecraft:normal', 'level-seed': 'abc', 'generate-structures': 'false'},
'amplified': {'level-type': 'minecraft:amplified', 'level-seed': '1'},
'large-biomes': {'level-type': 'minecraft:large_biomes', 'level-seed': '2'},
'single-biome': {'level-type': 'minecraft:single_biome_surface', 'level-seed': '3'},
'unknown': {'level-type': 'Some Type', 'level-seed': '42'},
}
WORKER = re.compile(r'\[Worker-Main-\d+/(WARN|ERROR)\]: (.*)')
@@ -155,9 +158,10 @@ def main():
schema=1, minecraft=lock['minecraft']['version'], platform=platform.platform(),
java=subprocess.check_output(['java', '--version'], text=True).splitlines()[0],
cases=list(CASES), lines=len(vanilla), output_sha256=lab.digest(build / 'horizon.txt', 'sha256'),
scope='a new dedicated-server world: level-type (flat, normal, unknown), a flat world\'s '
'generator-settings, generate-structures, as world_gen_settings.dat and the lines logged; without '
'structures, locate structure\'s replies and the saved chunks\' structure starts and references.'))
scope='a new dedicated-server world: level-type (flat, normal, amplified, large_biomes, '
'single_biome_surface, unknown), a flat world\'s generator-settings, generate-structures, as '
'world_gen_settings.dat and the lines logged; without structures, locate structure\'s replies and '
'the saved chunks\' structure starts and references.'))
print(f'server world gen: {len(CASES)} cases, {len(vanilla)} lines identical')