mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
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:
1 parent
9c62aba384
commit
bf05176692
11 files changed
+313
-83
No files matched your search
@@ -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(),
|
||||
}),
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(),
|
||||
})
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
@@ -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')
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user