fix: many generation fixes
This commit is contained in:
parent
8d4639e25a
commit
df983f8477
@ -109,7 +109,7 @@ villager = ["item", "sound"]
|
|||||||
slot_ranges = []
|
slot_ranges = []
|
||||||
|
|
||||||
noise_parameter = []
|
noise_parameter = []
|
||||||
biome = []
|
biome = ["carver"]
|
||||||
chunk_status = []
|
chunk_status = []
|
||||||
entity_pose = []
|
entity_pose = []
|
||||||
entity_status = []
|
entity_status = []
|
||||||
|
|||||||
@ -15,6 +15,7 @@ pub struct Biome {
|
|||||||
pub registry_id: &'static str,
|
pub registry_id: &'static str,
|
||||||
pub be_network_id: u8,
|
pub be_network_id: u8,
|
||||||
pub weather: Weather,
|
pub weather: Weather,
|
||||||
|
pub carvers: &'static [&'static crate::carver::CarverConfig],
|
||||||
pub features: &'static [&'static [crate::placed_feature::PlacedFeature]],
|
pub features: &'static [&'static [crate::placed_feature::PlacedFeature]],
|
||||||
pub creature_spawn_probability: f32,
|
pub creature_spawn_probability: f32,
|
||||||
pub spawners: SpawnGroups,
|
pub spawners: SpawnGroups,
|
||||||
@ -152,6 +153,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.03f32,
|
creature_spawn_probability: 0.03f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -310,6 +316,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -448,6 +459,7 @@ impl Biome {
|
|||||||
crate::placed_feature::PlacedFeature::OreDebrisSmall,
|
crate::placed_feature::PlacedFeature::OreDebrisSmall,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
carvers: &[&crate::carver::NETHER_CAVE],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -542,6 +554,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -678,6 +695,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -825,6 +847,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -972,6 +999,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -1098,6 +1130,7 @@ impl Biome {
|
|||||||
crate::placed_feature::PlacedFeature::CrimsonForestVegetation,
|
crate::placed_feature::PlacedFeature::CrimsonForestVegetation,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
carvers: &[&crate::carver::NETHER_CAVE],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -1200,6 +1233,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -1352,6 +1390,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -1504,6 +1547,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[],
|
monster: &[],
|
||||||
@ -1588,6 +1636,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -1743,6 +1796,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -1911,6 +1969,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -2068,6 +2131,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -2223,6 +2291,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -2296,6 +2369,7 @@ impl Biome {
|
|||||||
be_network_id: 9u8,
|
be_network_id: 9u8,
|
||||||
weather: Weather::new(false, 0.5f32, TemperatureModifier::None, 0.5f32),
|
weather: Weather::new(false, 0.5f32, TemperatureModifier::None, 0.5f32),
|
||||||
features: &[],
|
features: &[],
|
||||||
|
carvers: &[],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[Spawner {
|
monster: &[Spawner {
|
||||||
@ -2330,6 +2404,7 @@ impl Biome {
|
|||||||
&[],
|
&[],
|
||||||
&[crate::placed_feature::PlacedFeature::ChorusPlant],
|
&[crate::placed_feature::PlacedFeature::ChorusPlant],
|
||||||
],
|
],
|
||||||
|
carvers: &[],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[Spawner {
|
monster: &[Spawner {
|
||||||
@ -2353,6 +2428,7 @@ impl Biome {
|
|||||||
be_network_id: 9u8,
|
be_network_id: 9u8,
|
||||||
weather: Weather::new(false, 0.5f32, TemperatureModifier::None, 0.5f32),
|
weather: Weather::new(false, 0.5f32, TemperatureModifier::None, 0.5f32),
|
||||||
features: &[],
|
features: &[],
|
||||||
|
carvers: &[],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[Spawner {
|
monster: &[Spawner {
|
||||||
@ -2439,6 +2515,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.03f32,
|
creature_spawn_probability: 0.03f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -2595,6 +2676,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -2752,6 +2838,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -2911,6 +3002,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -3056,6 +3152,11 @@ impl Biome {
|
|||||||
&[crate::placed_feature::PlacedFeature::GlowLichen],
|
&[crate::placed_feature::PlacedFeature::GlowLichen],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -3190,6 +3291,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -3328,6 +3434,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -3476,6 +3587,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.07f32,
|
creature_spawn_probability: 0.07f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -3613,6 +3729,11 @@ impl Biome {
|
|||||||
&[crate::placed_feature::PlacedFeature::GlowLichen],
|
&[crate::placed_feature::PlacedFeature::GlowLichen],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -3749,6 +3870,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -3921,6 +4047,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -4088,6 +4219,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -4224,6 +4360,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -4369,6 +4510,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -4511,6 +4657,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[],
|
monster: &[],
|
||||||
@ -4573,6 +4724,7 @@ impl Biome {
|
|||||||
crate::placed_feature::PlacedFeature::RedMushroomNormal,
|
crate::placed_feature::PlacedFeature::RedMushroomNormal,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
carvers: &[&crate::carver::NETHER_CAVE],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -4685,6 +4837,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -4842,6 +4999,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -5000,6 +5162,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -5173,6 +5340,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -5338,6 +5510,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -5469,6 +5646,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -5636,6 +5818,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -5779,6 +5966,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -5950,6 +6142,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -6069,6 +6266,7 @@ impl Biome {
|
|||||||
be_network_id: 9u8,
|
be_network_id: 9u8,
|
||||||
weather: Weather::new(false, 0.5f32, TemperatureModifier::None, 0.5f32),
|
weather: Weather::new(false, 0.5f32, TemperatureModifier::None, 0.5f32),
|
||||||
features: &[&[crate::placed_feature::PlacedFeature::EndIslandDecorated]],
|
features: &[&[crate::placed_feature::PlacedFeature::EndIslandDecorated]],
|
||||||
|
carvers: &[],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[Spawner {
|
monster: &[Spawner {
|
||||||
@ -6152,6 +6350,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -6281,6 +6484,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.07f32,
|
creature_spawn_probability: 0.07f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -6426,6 +6634,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -6568,6 +6781,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -6699,6 +6917,7 @@ impl Biome {
|
|||||||
&[],
|
&[],
|
||||||
&[crate::placed_feature::PlacedFeature::SpringLava],
|
&[crate::placed_feature::PlacedFeature::SpringLava],
|
||||||
],
|
],
|
||||||
|
carvers: &[&crate::carver::NETHER_CAVE],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -6801,6 +7020,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -6952,6 +7176,11 @@ impl Biome {
|
|||||||
&[crate::placed_feature::PlacedFeature::GlowLichen],
|
&[crate::placed_feature::PlacedFeature::GlowLichen],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -7080,6 +7309,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -7207,6 +7441,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -7339,6 +7578,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -7510,6 +7754,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -7677,6 +7926,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -7793,6 +8047,7 @@ impl Biome {
|
|||||||
&[],
|
&[],
|
||||||
&[crate::placed_feature::PlacedFeature::EndPlatform],
|
&[crate::placed_feature::PlacedFeature::EndPlatform],
|
||||||
],
|
],
|
||||||
|
carvers: &[],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[Spawner {
|
monster: &[Spawner {
|
||||||
@ -7828,6 +8083,7 @@ impl Biome {
|
|||||||
&[],
|
&[],
|
||||||
&[crate::placed_feature::PlacedFeature::VoidStartPlatform],
|
&[crate::placed_feature::PlacedFeature::VoidStartPlatform],
|
||||||
],
|
],
|
||||||
|
carvers: &[],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[],
|
monster: &[],
|
||||||
@ -7911,6 +8167,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -8044,6 +8305,7 @@ impl Biome {
|
|||||||
crate::placed_feature::PlacedFeature::TwistingVines,
|
crate::placed_feature::PlacedFeature::TwistingVines,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
carvers: &[&crate::carver::NETHER_CAVE],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[Spawner {
|
monster: &[Spawner {
|
||||||
@ -8134,6 +8396,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -8291,6 +8558,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -8448,6 +8720,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -8603,6 +8880,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.1f32,
|
creature_spawn_probability: 0.1f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
@ -8776,6 +9058,11 @@ impl Biome {
|
|||||||
],
|
],
|
||||||
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
&[crate::placed_feature::PlacedFeature::FreezeTopLayer],
|
||||||
],
|
],
|
||||||
|
carvers: &[
|
||||||
|
&crate::carver::CAVE,
|
||||||
|
&crate::carver::CAVE_EXTRA_UNDERGROUND,
|
||||||
|
&crate::carver::CANYON,
|
||||||
|
],
|
||||||
creature_spawn_probability: 0.04f32,
|
creature_spawn_probability: 0.04f32,
|
||||||
spawners: SpawnGroups {
|
spawners: SpawnGroups {
|
||||||
monster: &[
|
monster: &[
|
||||||
|
|||||||
@ -8,25 +8,30 @@ use pumpkin_util::math::int_provider::{
|
|||||||
UniformIntProvider, VeryBiasedToBottomIntProvider,
|
UniformIntProvider, VeryBiasedToBottomIntProvider,
|
||||||
};
|
};
|
||||||
use pumpkin_util::y_offset::{AboveBottom, Absolute, BelowTop, YOffset};
|
use pumpkin_util::y_offset::{AboveBottom, Absolute, BelowTop, YOffset};
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub enum HeightProvider {
|
pub enum HeightProvider {
|
||||||
Uniform(UniformHeightProvider),
|
Uniform(UniformHeightProvider),
|
||||||
Trapezoid(TrapezoidHeightProvider),
|
Trapezoid(TrapezoidHeightProvider),
|
||||||
VeryBiasedToBottom(VeryBiasedToBottomHeightProvider),
|
VeryBiasedToBottom(VeryBiasedToBottomHeightProvider),
|
||||||
}
|
}
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct UniformHeightProvider {
|
pub struct UniformHeightProvider {
|
||||||
pub min_inclusive: YOffset,
|
pub min_inclusive: YOffset,
|
||||||
pub max_inclusive: YOffset,
|
pub max_inclusive: YOffset,
|
||||||
}
|
}
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct TrapezoidHeightProvider {
|
pub struct TrapezoidHeightProvider {
|
||||||
pub min_inclusive: YOffset,
|
pub min_inclusive: YOffset,
|
||||||
pub max_inclusive: YOffset,
|
pub max_inclusive: YOffset,
|
||||||
pub plateau: Option<i32>,
|
pub plateau: Option<i32>,
|
||||||
}
|
}
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct VeryBiasedToBottomHeightProvider {
|
pub struct VeryBiasedToBottomHeightProvider {
|
||||||
pub min_inclusive: YOffset,
|
pub min_inclusive: YOffset,
|
||||||
pub max_inclusive: YOffset,
|
pub max_inclusive: YOffset,
|
||||||
pub inner: Option<std::num::NonZero<u32>>,
|
pub inner: Option<std::num::NonZero<u32>>,
|
||||||
}
|
}
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct CaveCarverConfig {
|
pub struct CaveCarverConfig {
|
||||||
pub count: IntProvider,
|
pub count: IntProvider,
|
||||||
pub horizontal_radius_multiplier: FloatProvider,
|
pub horizontal_radius_multiplier: FloatProvider,
|
||||||
@ -52,6 +57,7 @@ impl CaveCarverConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct CanyonShapeConfig {
|
pub struct CanyonShapeConfig {
|
||||||
pub distance_factor: FloatProvider,
|
pub distance_factor: FloatProvider,
|
||||||
pub thickness: FloatProvider,
|
pub thickness: FloatProvider,
|
||||||
@ -61,14 +67,17 @@ pub struct CanyonShapeConfig {
|
|||||||
pub vertical_radius_center_factor: f32,
|
pub vertical_radius_center_factor: f32,
|
||||||
pub y_scale: f32,
|
pub y_scale: f32,
|
||||||
}
|
}
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct CanyonCarverConfig {
|
pub struct CanyonCarverConfig {
|
||||||
pub vertical_rotation: FloatProvider,
|
pub vertical_rotation: FloatProvider,
|
||||||
pub shape: CanyonShapeConfig,
|
pub shape: CanyonShapeConfig,
|
||||||
}
|
}
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub enum CarverAdditionalConfig {
|
pub enum CarverAdditionalConfig {
|
||||||
Cave(CaveCarverConfig),
|
Cave(CaveCarverConfig),
|
||||||
Canyon(CanyonCarverConfig),
|
Canyon(CanyonCarverConfig),
|
||||||
}
|
}
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct CarverConfig {
|
pub struct CarverConfig {
|
||||||
pub probability: f32,
|
pub probability: f32,
|
||||||
pub y: HeightProvider,
|
pub y: HeightProvider,
|
||||||
|
|||||||
@ -23,6 +23,20 @@ pub enum MaterialRule {
|
|||||||
Badlands(BadLandsMaterialRule),
|
Badlands(BadLandsMaterialRule),
|
||||||
}
|
}
|
||||||
impl MaterialRule {
|
impl MaterialRule {
|
||||||
|
#[must_use]
|
||||||
|
pub fn from_name(name: &str) -> Option<&'static Self> {
|
||||||
|
let name = name.strip_prefix("minecraft:").unwrap_or(name);
|
||||||
|
match name {
|
||||||
|
"overworld" | "amplified" | "large_biomes" => Some(&OVERWORLD),
|
||||||
|
"nether" => Some(&NETHER),
|
||||||
|
"end" => Some(&END),
|
||||||
|
"bedrock_floor" => Some(&BEDROCK_FLOOR),
|
||||||
|
"bedrock_roof" => Some(&BEDROCK_ROOF),
|
||||||
|
"overworld_caves" => Some(&OVERWORLD_CAVES),
|
||||||
|
"overworld_floating_islands" => Some(&OVERWORLD_FLOATING_ISLANDS),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn from_dimension(dimension: &Dimension) -> &'static Self {
|
pub fn from_dimension(dimension: &Dimension) -> &'static Self {
|
||||||
if dimension == &Dimension::OVERWORLD {
|
if dimension == &Dimension::OVERWORLD {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -2,6 +2,7 @@
|
|||||||
use crate::BlockState;
|
use crate::BlockState;
|
||||||
use crate::biome::ParameterPoint;
|
use crate::biome::ParameterPoint;
|
||||||
use crate::dimension::Dimension;
|
use crate::dimension::Dimension;
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub struct NoiseSettings {
|
pub struct NoiseSettings {
|
||||||
pub aquifers_enabled: bool,
|
pub aquifers_enabled: bool,
|
||||||
pub ore_veins_enabled: bool,
|
pub ore_veins_enabled: bool,
|
||||||
@ -13,6 +14,7 @@ pub struct NoiseSettings {
|
|||||||
pub spawn_target: &'static [ParameterPoint],
|
pub spawn_target: &'static [ParameterPoint],
|
||||||
}
|
}
|
||||||
pub type GenerationSettings = NoiseSettings;
|
pub type GenerationSettings = NoiseSettings;
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub struct GenerationShapeConfig {
|
pub struct GenerationShapeConfig {
|
||||||
pub min_y: i8,
|
pub min_y: i8,
|
||||||
pub height: u16,
|
pub height: u16,
|
||||||
@ -224,6 +226,20 @@ impl NoiseSettings {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
pub fn from_name(name: &str) -> Option<&'static Self> {
|
||||||
|
let name = name.strip_prefix("minecraft:").unwrap_or(name);
|
||||||
|
match name {
|
||||||
|
"overworld" => Some(&Self::OVERWORLD),
|
||||||
|
"amplified" => Some(&Self::AMPLIFIED),
|
||||||
|
"large_biomes" => Some(&Self::LARGE_BIOMES),
|
||||||
|
"nether" => Some(&Self::NETHER),
|
||||||
|
"end" => Some(&Self::END),
|
||||||
|
"caves" => Some(&Self::CAVES),
|
||||||
|
"floating_islands" => Some(&Self::FLOATING_ISLANDS),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[must_use]
|
||||||
pub fn from_dimension(dimension: &Dimension) -> &'static Self {
|
pub fn from_dimension(dimension: &Dimension) -> &'static Self {
|
||||||
if dimension == &Dimension::OVERWORLD {
|
if dimension == &Dimension::OVERWORLD {
|
||||||
&Self::OVERWORLD
|
&Self::OVERWORLD
|
||||||
|
|||||||
@ -170,6 +170,28 @@ pub fn get_carver_seed(world_seed: u64, chunk_x: i32, chunk_z: i32) -> u64 {
|
|||||||
^ world_seed
|
^ world_seed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates a large feature seed for structure selection and placement.
|
||||||
|
///
|
||||||
|
/// Matches vanilla Minecraft's `WorldgenRandom.setLargeFeatureSeed`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// - `world_seed` – The base world seed.
|
||||||
|
/// - `chunk_x` – The X chunk coordinate.
|
||||||
|
/// - `chunk_z` – The Z chunk coordinate.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A large feature seed for the given chunk.
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_large_feature_seed(world_seed: u64, chunk_x: i32, chunk_z: i32) -> u64 {
|
||||||
|
let mut random = LegacyRand::from_seed(world_seed);
|
||||||
|
let x_scale = random.next_i64();
|
||||||
|
let z_scale = random.next_i64();
|
||||||
|
((chunk_x as i64).wrapping_mul(x_scale)
|
||||||
|
^ (chunk_z as i64).wrapping_mul(z_scale)
|
||||||
|
^ (world_seed as i64)) as u64
|
||||||
|
}
|
||||||
|
|
||||||
#[expect(clippy::return_self_not_must_use)]
|
#[expect(clippy::return_self_not_must_use)]
|
||||||
pub trait RandomImpl {
|
pub trait RandomImpl {
|
||||||
fn split(&mut self) -> Self;
|
fn split(&mut self) -> Self;
|
||||||
|
|||||||
@ -7,6 +7,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct TheEndBiomeSupplier;
|
pub struct TheEndBiomeSupplier;
|
||||||
|
|
||||||
impl TheEndBiomeSupplier {
|
impl TheEndBiomeSupplier {
|
||||||
|
|||||||
@ -25,6 +25,7 @@ pub trait BiomeSupplier {
|
|||||||
fn biome(&self, x: i32, y: i32, z: i32, noise: &mut MultiNoiseSampler<'_>) -> &'static Biome;
|
fn biome(&self, x: i32, y: i32, z: i32, noise: &mut MultiNoiseSampler<'_>) -> &'static Biome;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
pub struct MultiNoiseBiomeSupplier {
|
pub struct MultiNoiseBiomeSupplier {
|
||||||
source: &'static BiomeTree,
|
source: &'static BiomeTree,
|
||||||
}
|
}
|
||||||
@ -33,6 +34,16 @@ impl MultiNoiseBiomeSupplier {
|
|||||||
pub const OVERWORLD: Self = Self::new(&OVERWORLD_BIOME_SOURCE);
|
pub const OVERWORLD: Self = Self::new(&OVERWORLD_BIOME_SOURCE);
|
||||||
pub const NETHER: Self = Self::new(&NETHER_BIOME_SOURCE);
|
pub const NETHER: Self = Self::new(&NETHER_BIOME_SOURCE);
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn from_preset(preset: &str) -> Option<Self> {
|
||||||
|
let preset = preset.strip_prefix("minecraft:").unwrap_or(preset);
|
||||||
|
match preset {
|
||||||
|
"overworld" | "large_biomes" | "amplified" => Some(Self::OVERWORLD),
|
||||||
|
"nether" => Some(Self::NETHER),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const fn new(source: &'static BiomeTree) -> Self {
|
const fn new(source: &'static BiomeTree) -> Self {
|
||||||
Self { source }
|
Self { source }
|
||||||
}
|
}
|
||||||
@ -46,6 +57,94 @@ impl BiomeSupplier for MultiNoiseBiomeSupplier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct FixedBiomeSupplier {
|
||||||
|
pub biome: &'static Biome,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FixedBiomeSupplier {
|
||||||
|
#[must_use]
|
||||||
|
pub const fn new(biome: &'static Biome) -> Self {
|
||||||
|
Self { biome }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BiomeSupplier for FixedBiomeSupplier {
|
||||||
|
fn biome(
|
||||||
|
&self,
|
||||||
|
_x: i32,
|
||||||
|
_y: i32,
|
||||||
|
_z: i32,
|
||||||
|
_noise: &mut MultiNoiseSampler<'_>,
|
||||||
|
) -> &'static Biome {
|
||||||
|
self.biome
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum ActiveBiomeSupplier {
|
||||||
|
MultiNoise(MultiNoiseBiomeSupplier),
|
||||||
|
End(end::TheEndBiomeSupplier),
|
||||||
|
Fixed(FixedBiomeSupplier),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BiomeSupplier for ActiveBiomeSupplier {
|
||||||
|
fn biome(&self, x: i32, y: i32, z: i32, noise: &mut MultiNoiseSampler<'_>) -> &'static Biome {
|
||||||
|
match self {
|
||||||
|
Self::MultiNoise(supplier) => supplier.biome(x, y, z, noise),
|
||||||
|
Self::End(supplier) => supplier.biome(x, y, z, noise),
|
||||||
|
Self::Fixed(supplier) => supplier.biome(x, y, z, noise),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveBiomeSupplier {
|
||||||
|
#[must_use]
|
||||||
|
pub fn from_biome_source(
|
||||||
|
source: Option<&crate::world_info::BiomeSource>,
|
||||||
|
dimension: &pumpkin_data::dimension::Dimension,
|
||||||
|
) -> Self {
|
||||||
|
match source {
|
||||||
|
Some(crate::world_info::BiomeSource::Fixed { biome, .. }) => {
|
||||||
|
let clean = biome.strip_prefix("minecraft:").unwrap_or(biome);
|
||||||
|
let resolved = pumpkin_data::biome::Biome::from_name(clean)
|
||||||
|
.unwrap_or(&pumpkin_data::biome::Biome::PLAINS);
|
||||||
|
Self::Fixed(FixedBiomeSupplier::new(resolved))
|
||||||
|
}
|
||||||
|
Some(crate::world_info::BiomeSource::WithPreset { preset, .. }) => {
|
||||||
|
let clean = preset.strip_prefix("minecraft:").unwrap_or(preset);
|
||||||
|
if clean == "nether" {
|
||||||
|
Self::MultiNoise(MultiNoiseBiomeSupplier::NETHER)
|
||||||
|
} else {
|
||||||
|
Self::MultiNoise(MultiNoiseBiomeSupplier::OVERWORLD)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(crate::world_info::BiomeSource::Simple { biome_type }) => {
|
||||||
|
let clean = biome_type.strip_prefix("minecraft:").unwrap_or(biome_type);
|
||||||
|
if clean == "the_end" {
|
||||||
|
Self::End(end::TheEndBiomeSupplier)
|
||||||
|
} else if clean == "the_nether" {
|
||||||
|
Self::MultiNoise(MultiNoiseBiomeSupplier::NETHER)
|
||||||
|
} else {
|
||||||
|
Self::from_dimension(dimension)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => Self::from_dimension(dimension),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn from_dimension(dimension: &pumpkin_data::dimension::Dimension) -> Self {
|
||||||
|
if dimension == &pumpkin_data::dimension::Dimension::THE_END {
|
||||||
|
Self::End(end::TheEndBiomeSupplier)
|
||||||
|
} else if dimension == &pumpkin_data::dimension::Dimension::THE_NETHER {
|
||||||
|
Self::MultiNoise(MultiNoiseBiomeSupplier::NETHER)
|
||||||
|
} else {
|
||||||
|
Self::MultiNoise(MultiNoiseBiomeSupplier::OVERWORLD)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn hash_seed(seed: u64) -> i64 {
|
pub fn hash_seed(seed: u64) -> i64 {
|
||||||
let mut hasher = Sha256::new();
|
let mut hasher = Sha256::new();
|
||||||
|
|||||||
@ -3,19 +3,22 @@ pub mod cave;
|
|||||||
pub mod mask;
|
pub mod mask;
|
||||||
|
|
||||||
use crate::ProtoChunk;
|
use crate::ProtoChunk;
|
||||||
|
use crate::biome::BiomeSupplier;
|
||||||
use crate::generation::GlobalRandomConfig;
|
use crate::generation::GlobalRandomConfig;
|
||||||
use crate::generation::generator::VanillaGenerator;
|
use crate::generation::generator::VanillaGenerator;
|
||||||
use crate::generation::noise::aquifer_sampler::CarverAquiferSampler;
|
use crate::generation::noise::aquifer_sampler::CarverAquiferSampler;
|
||||||
use crate::generation::noise::perlin::DoublePerlinNoiseSampler;
|
use crate::generation::noise::perlin::DoublePerlinNoiseSampler;
|
||||||
|
use crate::generation::noise::router::multi_noise_sampler::MultiNoiseSampler;
|
||||||
use crate::generation::noise::router::surface_height_sampler::{
|
use crate::generation::noise::router::surface_height_sampler::{
|
||||||
SurfaceHeightEstimateSampler, SurfaceHeightSamplerBuilderOptions,
|
SurfaceHeightEstimateSampler, SurfaceHeightSamplerBuilderOptions,
|
||||||
};
|
};
|
||||||
use crate::generation::surface::rule::try_apply_material_rule;
|
use crate::generation::surface::rule::try_apply_material_rule;
|
||||||
use crate::generation::surface::terrain::SurfaceTerrainBuilder;
|
use crate::generation::surface::terrain::SurfaceTerrainBuilder;
|
||||||
use crate::generation::surface::{MaterialRuleContext, steep_material_condition};
|
use crate::generation::surface::{MaterialRuleContext, steep_material_condition};
|
||||||
|
use crate::generation::{biome_coords, section_coords};
|
||||||
use pumpkin_data::block_state::BlockState;
|
use pumpkin_data::block_state::BlockState;
|
||||||
use pumpkin_data::carver::{CANYON, CAVE, CAVE_EXTRA_UNDERGROUND, NETHER_CAVE};
|
|
||||||
use pumpkin_data::carver::{CarverAdditionalConfig, CarverConfig};
|
use pumpkin_data::carver::{CarverAdditionalConfig, CarverConfig};
|
||||||
|
#[cfg(test)]
|
||||||
use pumpkin_data::dimension::Dimension;
|
use pumpkin_data::dimension::Dimension;
|
||||||
use pumpkin_data::fluid::Fluid;
|
use pumpkin_data::fluid::Fluid;
|
||||||
use pumpkin_data::material_rule::MaterialRule;
|
use pumpkin_data::material_rule::MaterialRule;
|
||||||
@ -23,9 +26,6 @@ use pumpkin_util::math::vector2::Vector2;
|
|||||||
use pumpkin_util::math::vector3::Vector3;
|
use pumpkin_util::math::vector3::Vector3;
|
||||||
use pumpkin_util::random::{RandomGenerator, RandomImpl};
|
use pumpkin_util::random::{RandomGenerator, RandomImpl};
|
||||||
|
|
||||||
const OVERWORLD_CARVERS: [&CarverConfig; 3] = [&CAVE, &CAVE_EXTRA_UNDERGROUND, &CANYON];
|
|
||||||
const NETHER_CARVERS: [&CarverConfig; 1] = [&NETHER_CAVE];
|
|
||||||
|
|
||||||
pub struct CarverBlockIds {
|
pub struct CarverBlockIds {
|
||||||
pub air: &'static BlockState,
|
pub air: &'static BlockState,
|
||||||
pub cave_air: &'static BlockState,
|
pub cave_air: &'static BlockState,
|
||||||
@ -63,6 +63,8 @@ pub struct CarvingContext<'a> {
|
|||||||
pub secondary_noise: &'a DoublePerlinNoiseSampler,
|
pub secondary_noise: &'a DoublePerlinNoiseSampler,
|
||||||
pub terrain_builder: &'a SurfaceTerrainBuilder,
|
pub terrain_builder: &'a SurfaceTerrainBuilder,
|
||||||
pub sea_level: i32,
|
pub sea_level: i32,
|
||||||
|
pub default_block: &'static BlockState,
|
||||||
|
pub default_fluid: &'static BlockState,
|
||||||
pub surface_rule: &'a MaterialRule,
|
pub surface_rule: &'a MaterialRule,
|
||||||
pub surface_height_sampler: SurfaceHeightEstimateSampler<'a>,
|
pub surface_height_sampler: SurfaceHeightEstimateSampler<'a>,
|
||||||
pub carver_aquifer: Option<CarverAquiferSampler<'a>>,
|
pub carver_aquifer: Option<CarverAquiferSampler<'a>>,
|
||||||
@ -119,6 +121,7 @@ pub trait Carver {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
pub fn carve(chunk: &mut ProtoChunk, generator: &VanillaGenerator) {
|
pub fn carve(chunk: &mut ProtoChunk, generator: &VanillaGenerator) {
|
||||||
// Vanilla applyCarvers uses a range of 8 chunks (17x17 area)
|
// Vanilla applyCarvers uses a range of 8 chunks (17x17 area)
|
||||||
let radius = 8;
|
let radius = 8;
|
||||||
@ -126,7 +129,8 @@ pub fn carve(chunk: &mut ProtoChunk, generator: &VanillaGenerator) {
|
|||||||
let chunk_z = chunk.z;
|
let chunk_z = chunk.z;
|
||||||
let chunk_pos = Vector2::new(chunk_x, chunk_z);
|
let chunk_pos = Vector2::new(chunk_x, chunk_z);
|
||||||
|
|
||||||
let carvers_to_use = carvers_for_dimension(&generator.dimension);
|
let supplier: &dyn BiomeSupplier = &generator.biome_supplier;
|
||||||
|
let mut multi_noise_sampler = MultiNoiseSampler::generate(&generator.base_router.multi_noise);
|
||||||
|
|
||||||
let generation_shape = &generator.settings.shape;
|
let generation_shape = &generator.settings.shape;
|
||||||
let surface_config = SurfaceHeightSamplerBuilderOptions::new(
|
let surface_config = SurfaceHeightSamplerBuilderOptions::new(
|
||||||
@ -156,11 +160,15 @@ pub fn carve(chunk: &mut ProtoChunk, generator: &VanillaGenerator) {
|
|||||||
secondary_noise: &generator.terrain_cache.secondary_noise,
|
secondary_noise: &generator.terrain_cache.secondary_noise,
|
||||||
terrain_builder: &generator.terrain_cache.terrain_builder,
|
terrain_builder: &generator.terrain_cache.terrain_builder,
|
||||||
sea_level: generator.settings.sea_level,
|
sea_level: generator.settings.sea_level,
|
||||||
|
default_block: generator.settings.default_block,
|
||||||
|
default_fluid: generator.settings.default_fluid,
|
||||||
surface_rule: generator.surface_rule,
|
surface_rule: generator.surface_rule,
|
||||||
surface_height_sampler,
|
surface_height_sampler,
|
||||||
carver_aquifer,
|
carver_aquifer,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let center_biome = chunk.get_biome(0, 0, 0);
|
||||||
|
|
||||||
let mut run = CarveRun {
|
let mut run = CarveRun {
|
||||||
ctx: &mut context,
|
ctx: &mut context,
|
||||||
chunk,
|
chunk,
|
||||||
@ -176,9 +184,18 @@ pub fn carve(chunk: &mut ProtoChunk, generator: &VanillaGenerator) {
|
|||||||
let carver_z = chunk_z + dz;
|
let carver_z = chunk_z + dz;
|
||||||
let carver_chunk_pos = Vector2::new(carver_x, carver_z);
|
let carver_chunk_pos = Vector2::new(carver_x, carver_z);
|
||||||
|
|
||||||
// In vanilla, carvers are per-biome. Here we use the hardcoded list but
|
let carver_biome = if dx == 0 && dz == 0 {
|
||||||
// maintain the random seed logic.
|
center_biome
|
||||||
for (index, &config) in carvers_to_use.iter().enumerate() {
|
} else {
|
||||||
|
supplier.biome(
|
||||||
|
biome_coords::from_block(section_coords::section_to_block(carver_x)),
|
||||||
|
0,
|
||||||
|
biome_coords::from_block(section_coords::section_to_block(carver_z)),
|
||||||
|
&mut multi_noise_sampler,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
for (index, &config) in carver_biome.carvers.iter().enumerate() {
|
||||||
let seed = get_large_feature_seed(
|
let seed = get_large_feature_seed(
|
||||||
generator.random_config.seed + index as u64,
|
generator.random_config.seed + index as u64,
|
||||||
carver_x,
|
carver_x,
|
||||||
@ -242,16 +259,6 @@ const fn new_carver_random(seed: u64, non_vanilla_random: bool) -> RandomGenerat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn carvers_for_dimension(dimension: &Dimension) -> &'static [&'static CarverConfig] {
|
|
||||||
if dimension == &Dimension::OVERWORLD {
|
|
||||||
&OVERWORLD_CARVERS
|
|
||||||
} else if dimension == &Dimension::THE_NETHER {
|
|
||||||
&NETHER_CARVERS
|
|
||||||
} else {
|
|
||||||
&[]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn carve_top_material(
|
fn carve_top_material(
|
||||||
run: &mut CarveRun,
|
run: &mut CarveRun,
|
||||||
x: i32,
|
x: i32,
|
||||||
@ -294,7 +301,7 @@ fn overworld_carve_state(
|
|||||||
.state
|
.state
|
||||||
.map(|state| (state, result.should_schedule_fluid_update))
|
.map(|state| (state, result.should_schedule_fluid_update))
|
||||||
} else if y < run.ctx.sea_level {
|
} else if y < run.ctx.sea_level {
|
||||||
Some((run.ids.lava, false))
|
Some((run.ctx.default_fluid, false))
|
||||||
} else {
|
} else {
|
||||||
Some((run.ids.air, false))
|
Some((run.ids.air, false))
|
||||||
}
|
}
|
||||||
@ -384,6 +391,8 @@ fn with_carve_run_options<F>(
|
|||||||
secondary_noise: &generator.terrain_cache.secondary_noise,
|
secondary_noise: &generator.terrain_cache.secondary_noise,
|
||||||
terrain_builder: &generator.terrain_cache.terrain_builder,
|
terrain_builder: &generator.terrain_cache.terrain_builder,
|
||||||
sea_level: generator.settings.sea_level,
|
sea_level: generator.settings.sea_level,
|
||||||
|
default_block: generator.settings.default_block,
|
||||||
|
default_fluid: generator.settings.default_fluid,
|
||||||
surface_rule: surface_rule.unwrap_or(generator.surface_rule),
|
surface_rule: surface_rule.unwrap_or(generator.surface_rule),
|
||||||
surface_height_sampler,
|
surface_height_sampler,
|
||||||
carver_aquifer,
|
carver_aquifer,
|
||||||
@ -545,4 +554,23 @@ mod tests {
|
|||||||
let index = (x & 15) as usize * 16 + (z & 15) as usize;
|
let index = (x & 15) as usize * 16 + (z & 15) as usize;
|
||||||
chunk.flat_surface_height_map[index] = height;
|
chunk.flat_surface_height_map[index] = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn biome_carvers_driven_by_data() {
|
||||||
|
use pumpkin_data::biome::Biome;
|
||||||
|
use pumpkin_data::carver::{CANYON, CAVE, CAVE_EXTRA_UNDERGROUND, NETHER_CAVE};
|
||||||
|
|
||||||
|
assert_eq!(Biome::PLAINS.carvers.len(), 3);
|
||||||
|
assert!(std::ptr::eq(Biome::PLAINS.carvers[0], &CAVE));
|
||||||
|
assert!(std::ptr::eq(
|
||||||
|
Biome::PLAINS.carvers[1],
|
||||||
|
&CAVE_EXTRA_UNDERGROUND
|
||||||
|
));
|
||||||
|
assert!(std::ptr::eq(Biome::PLAINS.carvers[2], &CANYON));
|
||||||
|
|
||||||
|
assert_eq!(Biome::NETHER_WASTES.carvers.len(), 1);
|
||||||
|
assert!(std::ptr::eq(Biome::NETHER_WASTES.carvers[0], &NETHER_CAVE));
|
||||||
|
|
||||||
|
assert!(Biome::THE_END.carvers.is_empty());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
use pumpkin_data::{Block, BlockState, block_properties::BrownMushroomBlockLikeProperties};
|
use pumpkin_data::tag::Taggable;
|
||||||
|
use pumpkin_data::{Block, BlockState, block_properties::BrownMushroomBlockLikeProperties, tag};
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
math::position::BlockPos,
|
math::position::BlockPos,
|
||||||
random::{RandomGenerator, RandomImpl},
|
random::{RandomGenerator, RandomImpl},
|
||||||
@ -14,6 +15,19 @@ pub fn mushroom_tree_height(random: &mut RandomGenerator) -> i32 {
|
|||||||
height
|
height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn place_mushroom_block<T: GenerationCache>(
|
||||||
|
chunk: &mut T,
|
||||||
|
pos: &BlockPos,
|
||||||
|
state: &'static BlockState,
|
||||||
|
) {
|
||||||
|
let (current_block, current_state) = chunk.get_block_and_state(pos);
|
||||||
|
if current_state.is_air()
|
||||||
|
|| current_block.has_tag(&tag::Block::MINECRAFT_REPLACEABLE_BY_MUSHROOMS)
|
||||||
|
{
|
||||||
|
chunk.set_block_state(&pos.0, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct HugeBrownMushroomFeature;
|
pub struct HugeBrownMushroomFeature;
|
||||||
|
|
||||||
impl HugeBrownMushroomFeature {
|
impl HugeBrownMushroomFeature {
|
||||||
@ -23,14 +37,38 @@ impl HugeBrownMushroomFeature {
|
|||||||
pub fn generate<T: GenerationCache>(
|
pub fn generate<T: GenerationCache>(
|
||||||
&self,
|
&self,
|
||||||
chunk: &mut T,
|
chunk: &mut T,
|
||||||
_min_y: i8,
|
min_y: i8,
|
||||||
_height: u16,
|
height: u16,
|
||||||
_feature: pumpkin_data::placed_feature::PlacedFeature,
|
_feature: pumpkin_data::placed_feature::PlacedFeature,
|
||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let tree_height = mushroom_tree_height(random);
|
let tree_height = mushroom_tree_height(random);
|
||||||
|
|
||||||
|
let min_y = i32::from(min_y);
|
||||||
|
let max_y = min_y + i32::from(height);
|
||||||
|
if pos.0.y < min_y + 1 || pos.0.y + tree_height + 1 > max_y {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let below_block = chunk.get_block(&pos.down());
|
||||||
|
if !below_block.has_tag(&tag::Block::MINECRAFT_HUGE_BROWN_MUSHROOM_CAN_PLACE_ON) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for dy in 0..=tree_height {
|
||||||
|
let radius = if dy <= 3 { 0 } else { Self::FOLIAGE_RADIUS };
|
||||||
|
for dx in -radius..=radius {
|
||||||
|
for dz in -radius..=radius {
|
||||||
|
let check_pos = BlockPos::new(pos.0.x + dx, pos.0.y + dy, pos.0.z + dz);
|
||||||
|
let (block, state) = chunk.get_block_and_state(&check_pos);
|
||||||
|
if !state.is_air() && !block.has_tag(&tag::Block::MINECRAFT_LEAVES) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let radius = Self::FOLIAGE_RADIUS;
|
let radius = Self::FOLIAGE_RADIUS;
|
||||||
let cap_y = pos.0.y + tree_height;
|
let cap_y = pos.0.y + tree_height;
|
||||||
for j in -radius..=radius {
|
for j in -radius..=radius {
|
||||||
@ -52,7 +90,7 @@ impl HugeBrownMushroomFeature {
|
|||||||
};
|
};
|
||||||
let state_id = props.to_state_id(&Block::BROWN_MUSHROOM_BLOCK);
|
let state_id = props.to_state_id(&Block::BROWN_MUSHROOM_BLOCK);
|
||||||
let cap_pos = BlockPos::new(pos.0.x + j, cap_y, pos.0.z + k);
|
let cap_pos = BlockPos::new(pos.0.x + j, cap_y, pos.0.z + k);
|
||||||
chunk.set_block_state(&cap_pos.0, BlockState::from_id(state_id));
|
place_mushroom_block(chunk, &cap_pos, BlockState::from_id(state_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +105,7 @@ impl HugeBrownMushroomFeature {
|
|||||||
let stem_state = BlockState::from_id(stem_props.to_state_id(&Block::MUSHROOM_STEM));
|
let stem_state = BlockState::from_id(stem_props.to_state_id(&Block::MUSHROOM_STEM));
|
||||||
for i in 0..tree_height {
|
for i in 0..tree_height {
|
||||||
let stem_pos = BlockPos::new(pos.0.x, pos.0.y + i, pos.0.z);
|
let stem_pos = BlockPos::new(pos.0.x, pos.0.y + i, pos.0.z);
|
||||||
chunk.set_block_state(&stem_pos.0, stem_state);
|
place_mushroom_block(chunk, &stem_pos, stem_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
use pumpkin_data::{Block, BlockState, block_properties::BrownMushroomBlockLikeProperties};
|
use pumpkin_data::tag::Taggable;
|
||||||
|
use pumpkin_data::{Block, BlockState, block_properties::BrownMushroomBlockLikeProperties, tag};
|
||||||
use pumpkin_util::{math::position::BlockPos, random::RandomGenerator};
|
use pumpkin_util::{math::position::BlockPos, random::RandomGenerator};
|
||||||
|
|
||||||
|
use super::huge_brown_mushroom::place_mushroom_block;
|
||||||
use crate::generation::proto_chunk::GenerationCache;
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
|
|
||||||
pub struct HugeRedMushroomFeature;
|
pub struct HugeRedMushroomFeature;
|
||||||
@ -12,14 +14,42 @@ impl HugeRedMushroomFeature {
|
|||||||
pub fn generate<T: GenerationCache>(
|
pub fn generate<T: GenerationCache>(
|
||||||
&self,
|
&self,
|
||||||
chunk: &mut T,
|
chunk: &mut T,
|
||||||
_min_y: i8,
|
min_y: i8,
|
||||||
_height: u16,
|
height: u16,
|
||||||
_feature: pumpkin_data::placed_feature::PlacedFeature,
|
_feature: pumpkin_data::placed_feature::PlacedFeature,
|
||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let tree_height = super::huge_brown_mushroom::mushroom_tree_height(random);
|
let tree_height = super::huge_brown_mushroom::mushroom_tree_height(random);
|
||||||
|
|
||||||
|
let min_y = i32::from(min_y);
|
||||||
|
let max_y = min_y + i32::from(height);
|
||||||
|
if pos.0.y < min_y + 1 || pos.0.y + tree_height + 1 > max_y {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let below_block = chunk.get_block(&pos.down());
|
||||||
|
if !below_block.has_tag(&tag::Block::MINECRAFT_HUGE_RED_MUSHROOM_CAN_PLACE_ON) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for dy in 0..=tree_height {
|
||||||
|
let radius = if (dy < tree_height && dy >= tree_height - 3) || dy == tree_height {
|
||||||
|
Self::FOLIAGE_RADIUS
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
for dx in -radius..=radius {
|
||||||
|
for dz in -radius..=radius {
|
||||||
|
let check_pos = BlockPos::new(pos.0.x + dx, pos.0.y + dy, pos.0.z + dz);
|
||||||
|
let (block, state) = chunk.get_block_and_state(&check_pos);
|
||||||
|
if !state.is_air() && !block.has_tag(&tag::Block::MINECRAFT_LEAVES) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let radius = Self::FOLIAGE_RADIUS;
|
let radius = Self::FOLIAGE_RADIUS;
|
||||||
for i in (tree_height - 3)..=tree_height {
|
for i in (tree_height - 3)..=tree_height {
|
||||||
let j = if i < tree_height { radius } else { radius - 1 };
|
let j = if i < tree_height { radius } else { radius - 1 };
|
||||||
@ -44,7 +74,7 @@ impl HugeRedMushroomFeature {
|
|||||||
};
|
};
|
||||||
let state_id = props.to_state_id(&Block::RED_MUSHROOM_BLOCK);
|
let state_id = props.to_state_id(&Block::RED_MUSHROOM_BLOCK);
|
||||||
let cap_pos = BlockPos::new(pos.0.x + l, pos.0.y + i, pos.0.z + m);
|
let cap_pos = BlockPos::new(pos.0.x + l, pos.0.y + i, pos.0.z + m);
|
||||||
chunk.set_block_state(&cap_pos.0, BlockState::from_id(state_id));
|
place_mushroom_block(chunk, &cap_pos, BlockState::from_id(state_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,7 +90,7 @@ impl HugeRedMushroomFeature {
|
|||||||
let stem_state = BlockState::from_id(stem_props.to_state_id(&Block::MUSHROOM_STEM));
|
let stem_state = BlockState::from_id(stem_props.to_state_id(&Block::MUSHROOM_STEM));
|
||||||
for i in 0..tree_height {
|
for i in 0..tree_height {
|
||||||
let stem_pos = BlockPos::new(pos.0.x, pos.0.y + i, pos.0.z);
|
let stem_pos = BlockPos::new(pos.0.x, pos.0.y + i, pos.0.z);
|
||||||
chunk.set_block_state(&stem_pos.0, stem_state);
|
place_mushroom_block(chunk, &stem_pos, stem_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|||||||
@ -1,9 +1,7 @@
|
|||||||
use pumpkin_data::BlockState;
|
use pumpkin_data::BlockState;
|
||||||
use pumpkin_data::dimension::Dimension;
|
use pumpkin_data::dimension::Dimension;
|
||||||
use pumpkin_data::material_rule::MaterialRule;
|
use pumpkin_data::material_rule::MaterialRule;
|
||||||
use pumpkin_data::noise_router::{
|
use pumpkin_data::noise_router::BaseNoiseRouters;
|
||||||
END_BASE_NOISE_ROUTER, NETHER_BASE_NOISE_ROUTER, OVERWORLD_BASE_NOISE_ROUTER,
|
|
||||||
};
|
|
||||||
use pumpkin_data::noise_settings::NoiseSettings;
|
use pumpkin_data::noise_settings::NoiseSettings;
|
||||||
|
|
||||||
use super::noise::router::proto_noise_router::ProtoNoiseRouters;
|
use super::noise::router::proto_noise_router::ProtoNoiseRouters;
|
||||||
@ -128,6 +126,7 @@ pub struct VanillaGenerator {
|
|||||||
pub dimension: Dimension,
|
pub dimension: Dimension,
|
||||||
pub settings: &'static NoiseSettings,
|
pub settings: &'static NoiseSettings,
|
||||||
pub surface_rule: &'static MaterialRule,
|
pub surface_rule: &'static MaterialRule,
|
||||||
|
pub biome_supplier: crate::biome::ActiveBiomeSupplier,
|
||||||
pub biome_mixer_seed: i64,
|
pub biome_mixer_seed: i64,
|
||||||
|
|
||||||
pub terrain_cache: TerrainCache,
|
pub terrain_cache: TerrainCache,
|
||||||
@ -137,6 +136,7 @@ pub struct VanillaGenerator {
|
|||||||
pub global_structure_cache: crate::generation::structure::placement::GlobalStructureCache,
|
pub global_structure_cache: crate::generation::structure::placement::GlobalStructureCache,
|
||||||
pub structure_calculator: StructurePlacementCalculator,
|
pub structure_calculator: StructurePlacementCalculator,
|
||||||
pub structure_allowed_biomes: FxHashMap<usize, Vec<u16>>,
|
pub structure_allowed_biomes: FxHashMap<usize, Vec<u16>>,
|
||||||
|
pub enabled_structure_sets: Option<rustc_hash::FxHashSet<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VanillaGenerator {
|
impl VanillaGenerator {
|
||||||
@ -154,30 +154,41 @@ impl VanillaGenerator {
|
|||||||
&mut sampler,
|
&mut sampler,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl GeneratorInit for VanillaGenerator {
|
#[must_use]
|
||||||
fn new(seed: Seed, dimension: Dimension) -> Self {
|
pub fn new_with_settings(
|
||||||
let settings = NoiseSettings::from_dimension(&dimension);
|
seed: Seed,
|
||||||
let surface_rule = MaterialRule::from_dimension(&dimension);
|
dimension: Dimension,
|
||||||
|
settings_name: Option<&str>,
|
||||||
|
) -> Self {
|
||||||
|
Self::new_with_all_settings(seed, dimension, settings_name, None, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn new_with_all_settings(
|
||||||
|
seed: Seed,
|
||||||
|
dimension: Dimension,
|
||||||
|
settings_name: Option<&str>,
|
||||||
|
biome_source: Option<&crate::world_info::BiomeSource>,
|
||||||
|
structure_overrides: Option<&[String]>,
|
||||||
|
) -> Self {
|
||||||
|
let settings = settings_name
|
||||||
|
.and_then(NoiseSettings::from_name)
|
||||||
|
.unwrap_or_else(|| NoiseSettings::from_dimension(&dimension));
|
||||||
|
let surface_rule = settings_name
|
||||||
|
.and_then(MaterialRule::from_name)
|
||||||
|
.unwrap_or_else(|| MaterialRule::from_dimension(&dimension));
|
||||||
|
let biome_supplier =
|
||||||
|
crate::biome::ActiveBiomeSupplier::from_biome_source(biome_source, &dimension);
|
||||||
let random_config = GlobalRandomConfig::new(seed.0, settings.legacy_random_source);
|
let random_config = GlobalRandomConfig::new(seed.0, settings.legacy_random_source);
|
||||||
|
|
||||||
// TODO: The generation settings contains (part of?) the noise routers too; do we keep the separate or
|
let base = settings_name
|
||||||
// use only the generation settings?
|
.and_then(BaseNoiseRouters::from_name)
|
||||||
let base = if dimension == Dimension::OVERWORLD {
|
.unwrap_or_else(|| BaseNoiseRouters::from_dimension(&dimension));
|
||||||
OVERWORLD_BASE_NOISE_ROUTER
|
|
||||||
} else if dimension == Dimension::THE_NETHER {
|
|
||||||
NETHER_BASE_NOISE_ROUTER
|
|
||||||
} else if dimension == Dimension::THE_END {
|
|
||||||
END_BASE_NOISE_ROUTER
|
|
||||||
} else {
|
|
||||||
tracing::error!("Unsupported dimension for noise router: {:?}", dimension);
|
|
||||||
OVERWORLD_BASE_NOISE_ROUTER
|
|
||||||
};
|
|
||||||
let terrain_cache = TerrainCache::from_random(&random_config);
|
let terrain_cache = TerrainCache::from_random(&random_config);
|
||||||
|
|
||||||
let default_block = settings.default_block;
|
let default_block = settings.default_block;
|
||||||
let base_router = ProtoNoiseRouters::generate(&base, &random_config);
|
let base_router = ProtoNoiseRouters::generate(base, &random_config);
|
||||||
let biome_mixer_seed = crate::biome::hash_seed(seed.0);
|
let biome_mixer_seed = crate::biome::hash_seed(seed.0);
|
||||||
|
|
||||||
let mut structure_allowed_biomes = FxHashMap::default();
|
let mut structure_allowed_biomes = FxHashMap::default();
|
||||||
@ -188,19 +199,157 @@ impl GeneratorInit for VanillaGenerator {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let enabled_structure_sets: Option<rustc_hash::FxHashSet<usize>> =
|
||||||
|
structure_overrides.map(|overrides| {
|
||||||
|
overrides
|
||||||
|
.iter()
|
||||||
|
.filter_map(|s| {
|
||||||
|
let clean = s.strip_prefix("minecraft:").unwrap_or(s);
|
||||||
|
StructureSet::NAMES.iter().position(|&name| name == clean)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
});
|
||||||
|
|
||||||
|
let global_structure_cache =
|
||||||
|
crate::generation::structure::placement::GlobalStructureCache::new();
|
||||||
|
|
||||||
|
if dimension == Dimension::OVERWORLD {
|
||||||
|
let pumpkin_data::structures::StructurePlacementType::ConcentricRings(rings) =
|
||||||
|
&StructureSet::STRONGHOLDS.placement.placement_type
|
||||||
|
else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
let strongholds =
|
||||||
|
crate::generation::structure::placement::GlobalStructureCache::calculate_strongholds(
|
||||||
|
seed.0 as i64,
|
||||||
|
rings,
|
||||||
|
&base_router.multi_noise,
|
||||||
|
);
|
||||||
|
global_structure_cache.init_strongholds(strongholds);
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
random_config,
|
random_config,
|
||||||
base_router,
|
base_router,
|
||||||
dimension,
|
dimension,
|
||||||
settings,
|
settings,
|
||||||
surface_rule,
|
surface_rule,
|
||||||
|
biome_supplier,
|
||||||
biome_mixer_seed,
|
biome_mixer_seed,
|
||||||
terrain_cache,
|
terrain_cache,
|
||||||
default_block,
|
default_block,
|
||||||
global_structure_cache:
|
global_structure_cache,
|
||||||
crate::generation::structure::placement::GlobalStructureCache::new(),
|
|
||||||
structure_calculator: StructurePlacementCalculator::new(seed.0 as i64),
|
structure_calculator: StructurePlacementCalculator::new(seed.0 as i64),
|
||||||
structure_allowed_biomes,
|
structure_allowed_biomes,
|
||||||
|
enabled_structure_sets,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GeneratorInit for VanillaGenerator {
|
||||||
|
fn new(seed: Seed, dimension: Dimension) -> Self {
|
||||||
|
Self::new_with_settings(seed, dimension, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vanilla_generator_settings() {
|
||||||
|
let seed = Seed(42);
|
||||||
|
|
||||||
|
// Default overworld
|
||||||
|
let default_gen = VanillaGenerator::new_with_settings(seed, Dimension::OVERWORLD, None);
|
||||||
|
assert_eq!(*default_gen.settings, NoiseSettings::OVERWORLD);
|
||||||
|
|
||||||
|
// Amplified preset
|
||||||
|
let amplified_gen = VanillaGenerator::new_with_settings(
|
||||||
|
seed,
|
||||||
|
Dimension::OVERWORLD,
|
||||||
|
Some("minecraft:amplified"),
|
||||||
|
);
|
||||||
|
assert_eq!(*amplified_gen.settings, NoiseSettings::AMPLIFIED);
|
||||||
|
|
||||||
|
// Large biomes preset
|
||||||
|
let large_biomes_gen = VanillaGenerator::new_with_settings(
|
||||||
|
seed,
|
||||||
|
Dimension::OVERWORLD,
|
||||||
|
Some("minecraft:large_biomes"),
|
||||||
|
);
|
||||||
|
assert_eq!(*large_biomes_gen.settings, NoiseSettings::LARGE_BIOMES);
|
||||||
|
|
||||||
|
// Nether
|
||||||
|
let nether_gen = VanillaGenerator::new_with_settings(
|
||||||
|
seed,
|
||||||
|
Dimension::THE_NETHER,
|
||||||
|
Some("minecraft:nether"),
|
||||||
|
);
|
||||||
|
assert_eq!(*nether_gen.settings, NoiseSettings::NETHER);
|
||||||
|
|
||||||
|
// End
|
||||||
|
let end_gen =
|
||||||
|
VanillaGenerator::new_with_settings(seed, Dimension::THE_END, Some("minecraft:end"));
|
||||||
|
assert_eq!(*end_gen.settings, NoiseSettings::END);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vanilla_generator_structure_overrides() {
|
||||||
|
let seed = Seed(42);
|
||||||
|
|
||||||
|
// No overrides: None means all structure sets enabled
|
||||||
|
let all_gen =
|
||||||
|
VanillaGenerator::new_with_all_settings(seed, Dimension::OVERWORLD, None, None, None);
|
||||||
|
assert!(all_gen.enabled_structure_sets.is_none());
|
||||||
|
|
||||||
|
// Overrides with villages only
|
||||||
|
let village_overrides = vec!["minecraft:villages".to_string()];
|
||||||
|
let filtered_gen = VanillaGenerator::new_with_all_settings(
|
||||||
|
seed,
|
||||||
|
Dimension::OVERWORLD,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(&village_overrides),
|
||||||
|
);
|
||||||
|
let enabled = filtered_gen
|
||||||
|
.enabled_structure_sets
|
||||||
|
.expect("should have enabled structure set filter");
|
||||||
|
let village_idx = StructureSet::NAMES
|
||||||
|
.iter()
|
||||||
|
.position(|&n| n == "villages")
|
||||||
|
.unwrap();
|
||||||
|
let mineshaft_idx = StructureSet::NAMES
|
||||||
|
.iter()
|
||||||
|
.position(|&n| n == "mineshafts")
|
||||||
|
.unwrap();
|
||||||
|
assert!(enabled.contains(&village_idx));
|
||||||
|
assert!(!enabled.contains(&mineshaft_idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vanilla_generator_fixed_biome_source() {
|
||||||
|
use crate::biome::BiomeSupplier;
|
||||||
|
|
||||||
|
let seed = Seed(42);
|
||||||
|
let biome_source = crate::world_info::BiomeSource::Fixed {
|
||||||
|
biome: "minecraft:desert".to_string(),
|
||||||
|
biome_type: "minecraft:fixed".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let desert_gen = VanillaGenerator::new_with_all_settings(
|
||||||
|
seed,
|
||||||
|
Dimension::OVERWORLD,
|
||||||
|
None,
|
||||||
|
Some(&biome_source),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut sampler =
|
||||||
|
crate::generation::noise::router::multi_noise_sampler::MultiNoiseSampler::generate(
|
||||||
|
&desert_gen.base_router.multi_noise,
|
||||||
|
);
|
||||||
|
let sampled_biome = desert_gen.biome_supplier.biome(0, 64, 0, &mut sampler);
|
||||||
|
assert_eq!(sampled_biome.registry_id, "desert");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -109,7 +109,6 @@ pub fn find_nearest_structure_start(
|
|||||||
generator: &WorldGenerator,
|
generator: &WorldGenerator,
|
||||||
) -> Option<BlockPos> {
|
) -> Option<BlockPos> {
|
||||||
use crate::{
|
use crate::{
|
||||||
ProtoChunk,
|
|
||||||
biome::{BiomeSupplier, MultiNoiseBiomeSupplier},
|
biome::{BiomeSupplier, MultiNoiseBiomeSupplier},
|
||||||
generation::{
|
generation::{
|
||||||
noise::router::{
|
noise::router::{
|
||||||
@ -156,15 +155,12 @@ pub fn find_nearest_structure_start(
|
|||||||
region_origin_z + region_z_offset,
|
region_origin_z + region_z_offset,
|
||||||
structure_set.placement.salt,
|
structure_set.placement.salt,
|
||||||
);
|
);
|
||||||
let placement_chunk = ProtoChunk::new(chunk_x, chunk_z, generator);
|
|
||||||
if !should_generate_structure(
|
if !should_generate_structure(
|
||||||
&structure_set.placement,
|
&structure_set.placement,
|
||||||
&noise_generator.structure_calculator,
|
&noise_generator.structure_calculator,
|
||||||
chunk_x,
|
chunk_x,
|
||||||
chunk_z,
|
chunk_z,
|
||||||
global_cache,
|
global_cache,
|
||||||
&placement_chunk,
|
|
||||||
&[],
|
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -194,7 +190,12 @@ pub fn find_nearest_structure_start(
|
|||||||
chunk_z,
|
chunk_z,
|
||||||
random: create_chunk_random(world_seed, chunk_x, chunk_z),
|
random: create_chunk_random(world_seed, chunk_x, chunk_z),
|
||||||
sea_level: settings.sea_level,
|
sea_level: settings.sea_level,
|
||||||
min_y: noise_generator.dimension.min_y,
|
min_y: (settings.shape.min_y as i32)
|
||||||
|
.max(noise_generator.dimension.min_y),
|
||||||
|
height: settings
|
||||||
|
.shape
|
||||||
|
.height
|
||||||
|
.min(noise_generator.dimension.height as u16),
|
||||||
height_sampler: Some(&mut height_sampler),
|
height_sampler: Some(&mut height_sampler),
|
||||||
structure_key: Some(key),
|
structure_key: Some(key),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -18,7 +18,7 @@ pub mod rule;
|
|||||||
pub mod structure;
|
pub mod structure;
|
||||||
mod surface;
|
mod surface;
|
||||||
|
|
||||||
use generator::{GeneratorInit, VanillaGenerator};
|
use generator::VanillaGenerator;
|
||||||
use pumpkin_data::dimension::Dimension;
|
use pumpkin_data::dimension::Dimension;
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
random::xoroshiro128::{Xoroshiro, XoroshiroSplitter},
|
random::xoroshiro128::{Xoroshiro, XoroshiroSplitter},
|
||||||
@ -32,6 +32,42 @@ pub fn get_world_gen(
|
|||||||
is_flat: bool,
|
is_flat: bool,
|
||||||
flat_layers: Vec<generator::FlatLayer>,
|
flat_layers: Vec<generator::FlatLayer>,
|
||||||
flat_biome: String,
|
flat_biome: String,
|
||||||
|
) -> Box<generator::WorldGenerator> {
|
||||||
|
get_world_gen_with_settings(seed, dimension, is_flat, flat_layers, flat_biome, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_world_gen_with_settings(
|
||||||
|
seed: Seed,
|
||||||
|
dimension: Dimension,
|
||||||
|
is_flat: bool,
|
||||||
|
flat_layers: Vec<generator::FlatLayer>,
|
||||||
|
flat_biome: String,
|
||||||
|
generator_settings: Option<&str>,
|
||||||
|
) -> Box<generator::WorldGenerator> {
|
||||||
|
get_world_gen_with_all_settings(
|
||||||
|
seed,
|
||||||
|
dimension,
|
||||||
|
is_flat,
|
||||||
|
flat_layers,
|
||||||
|
flat_biome,
|
||||||
|
generator_settings,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::too_many_arguments)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_world_gen_with_all_settings(
|
||||||
|
seed: Seed,
|
||||||
|
dimension: Dimension,
|
||||||
|
is_flat: bool,
|
||||||
|
flat_layers: Vec<generator::FlatLayer>,
|
||||||
|
flat_biome: String,
|
||||||
|
generator_settings: Option<&str>,
|
||||||
|
biome_source: Option<&crate::world_info::BiomeSource>,
|
||||||
|
structure_overrides: Option<&[String]>,
|
||||||
) -> Box<generator::WorldGenerator> {
|
) -> Box<generator::WorldGenerator> {
|
||||||
if is_flat {
|
if is_flat {
|
||||||
Box::new(generator::WorldGenerator::Flat(Box::new(
|
Box::new(generator::WorldGenerator::Flat(Box::new(
|
||||||
@ -39,7 +75,13 @@ pub fn get_world_gen(
|
|||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
Box::new(generator::WorldGenerator::Noise(Box::new(
|
Box::new(generator::WorldGenerator::Noise(Box::new(
|
||||||
VanillaGenerator::new(seed, dimension),
|
VanillaGenerator::new_with_all_settings(
|
||||||
|
seed,
|
||||||
|
dimension,
|
||||||
|
generator_settings,
|
||||||
|
biome_source,
|
||||||
|
structure_overrides,
|
||||||
|
),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,6 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use pumpkin_data::block_properties::is_air;
|
use pumpkin_data::block_properties::is_air;
|
||||||
use pumpkin_data::chunk::DoublePerlinNoiseParameters;
|
use pumpkin_data::chunk::DoublePerlinNoiseParameters;
|
||||||
use pumpkin_data::dimension::Dimension;
|
|
||||||
use pumpkin_data::fluid::{Fluid, FluidState};
|
use pumpkin_data::fluid::{Fluid, FluidState};
|
||||||
use pumpkin_data::structures::{
|
use pumpkin_data::structures::{
|
||||||
Structure, StructureKeys, StructurePlacementType, StructureSet, WeightedEntry,
|
Structure, StructureKeys, StructurePlacementType, StructureSet, WeightedEntry,
|
||||||
@ -12,14 +11,11 @@ use pumpkin_data::tag::RegistryKey;
|
|||||||
use pumpkin_data::{Block, BlockState, block_properties::blocks_movement, chunk::Biome};
|
use pumpkin_data::{Block, BlockState, block_properties::blocks_movement, chunk::Biome};
|
||||||
use pumpkin_data::{BlockId, BlockStateId, tag};
|
use pumpkin_data::{BlockId, BlockStateId, tag};
|
||||||
use pumpkin_util::random::xoroshiro128::XoroshiroSplitter;
|
use pumpkin_util::random::xoroshiro128::XoroshiroSplitter;
|
||||||
use pumpkin_util::random::{RandomImpl, get_carver_seed};
|
use pumpkin_util::random::{RandomImpl, get_large_feature_seed, legacy_rand::LegacyRand};
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
HeightMap,
|
HeightMap,
|
||||||
math::{block_box::BlockBox, position::BlockPos, vector3::Vector3},
|
math::{block_box::BlockBox, position::BlockPos, vector3::Vector3},
|
||||||
random::{
|
random::{RandomGenerator, get_decorator_seed, worldgen_random::WorldgenRandom},
|
||||||
RandomGenerator, get_decorator_seed, worldgen_random::WorldgenRandom,
|
|
||||||
xoroshiro128::Xoroshiro,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
@ -36,7 +32,7 @@ use super::{
|
|||||||
section_coords,
|
section_coords,
|
||||||
surface::{MaterialRuleContext, estimate_surface_height, terrain::SurfaceTerrainBuilder},
|
surface::{MaterialRuleContext, estimate_surface_height, terrain::SurfaceTerrainBuilder},
|
||||||
};
|
};
|
||||||
use crate::biome::{BiomeSupplier, MultiNoiseBiomeSupplier, end::TheEndBiomeSupplier};
|
use crate::biome::BiomeSupplier;
|
||||||
use crate::chunk::format::LightContainer;
|
use crate::chunk::format::LightContainer;
|
||||||
use crate::chunk::{ChunkData, ChunkHeightmapType, ChunkLight};
|
use crate::chunk::{ChunkData, ChunkHeightmapType, ChunkLight};
|
||||||
use crate::chunk_system::{StagedChunkEnum, generation_cache::SurfaceBiomeNeighborhood};
|
use crate::chunk_system::{StagedChunkEnum, generation_cache::SurfaceBiomeNeighborhood};
|
||||||
@ -65,12 +61,6 @@ use pumpkin_nbt::compound::NbtCompound;
|
|||||||
use crate::generation::structure::template::BlockPlacer;
|
use crate::generation::structure::template::BlockPlacer;
|
||||||
use crate::tick::{ScheduledTick, TickPriority};
|
use crate::tick::{ScheduledTick, TickPriority};
|
||||||
|
|
||||||
enum ActiveSupplier {
|
|
||||||
Overworld(MultiNoiseBiomeSupplier),
|
|
||||||
Nether(MultiNoiseBiomeSupplier),
|
|
||||||
End(TheEndBiomeSupplier),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait GenerationCache: HeightLimitView + BlockAccessor {
|
pub trait GenerationCache: HeightLimitView + BlockAccessor {
|
||||||
fn get_center_chunk_mut(&mut self) -> &mut ProtoChunk;
|
fn get_center_chunk_mut(&mut self) -> &mut ProtoChunk;
|
||||||
fn get_center_chunk(&self) -> &ProtoChunk;
|
fn get_center_chunk(&self) -> &ProtoChunk;
|
||||||
@ -795,18 +785,7 @@ impl ProtoChunk {
|
|||||||
generator: &super::generator::VanillaGenerator,
|
generator: &super::generator::VanillaGenerator,
|
||||||
multi_noise_sampler: &mut MultiNoiseSampler,
|
multi_noise_sampler: &mut MultiNoiseSampler,
|
||||||
) {
|
) {
|
||||||
let dimension = &generator.dimension;
|
let base_supplier: &dyn BiomeSupplier = &generator.biome_supplier;
|
||||||
let active_supplier = if dimension == &Dimension::THE_END {
|
|
||||||
ActiveSupplier::End(TheEndBiomeSupplier)
|
|
||||||
} else if dimension == &Dimension::THE_NETHER {
|
|
||||||
ActiveSupplier::Nether(MultiNoiseBiomeSupplier::NETHER)
|
|
||||||
} else {
|
|
||||||
ActiveSupplier::Overworld(MultiNoiseBiomeSupplier::OVERWORLD)
|
|
||||||
};
|
|
||||||
let base_supplier: &dyn BiomeSupplier = match &active_supplier {
|
|
||||||
ActiveSupplier::End(s) => s,
|
|
||||||
ActiveSupplier::Nether(s) | ActiveSupplier::Overworld(s) => s,
|
|
||||||
};
|
|
||||||
let blender = Blender::empty();
|
let blender = Blender::empty();
|
||||||
let biome_supplier = blender.get_biome_supplier(base_supplier);
|
let biome_supplier = blender.get_biome_supplier(base_supplier);
|
||||||
let min_y = self.bottom_y();
|
let min_y = self.bottom_y();
|
||||||
@ -1289,17 +1268,14 @@ impl ProtoChunk {
|
|||||||
crate::generation::structure::height_sampler::NoiseHeightSampler::new(generator);
|
crate::generation::structure::height_sampler::NoiseHeightSampler::new(generator);
|
||||||
|
|
||||||
for (i, set) in StructureSet::ALL.iter().enumerate() {
|
for (i, set) in StructureSet::ALL.iter().enumerate() {
|
||||||
let allowed_biomes = &generator.structure_allowed_biomes[&i];
|
if let Some(ref enabled) = generator.enabled_structure_sets
|
||||||
|
&& !enabled.contains(&i)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if !should_generate_structure(
|
if !should_generate_structure(&set.placement, calculator, self.x, self.z, global_cache)
|
||||||
&set.placement,
|
{
|
||||||
calculator,
|
|
||||||
self.x,
|
|
||||||
self.z,
|
|
||||||
global_cache,
|
|
||||||
self,
|
|
||||||
allowed_biomes,
|
|
||||||
) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1317,9 +1293,8 @@ impl ProtoChunk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut candidates = set.structures.to_vec();
|
let mut candidates = set.structures.to_vec();
|
||||||
let carver_seed = get_carver_seed(seed, self.x, self.z);
|
let large_feature_seed = get_large_feature_seed(seed, self.x, self.z);
|
||||||
let mut random: RandomGenerator =
|
let mut random = LegacyRand::from_seed(large_feature_seed);
|
||||||
RandomGenerator::Xoroshiro(Xoroshiro::from_seed(carver_seed));
|
|
||||||
|
|
||||||
let mut total_weight: u32 = candidates.iter().map(|e| e.weight).sum();
|
let mut total_weight: u32 = candidates.iter().map(|e| e.weight).sum();
|
||||||
|
|
||||||
@ -1368,7 +1343,7 @@ impl ProtoChunk {
|
|||||||
let center_z = chunk_pos::get_center_z(self.z);
|
let center_z = chunk_pos::get_center_z(self.z);
|
||||||
let start_y = height_sampler.estimate_ocean_floor_height(center_x, center_z);
|
let start_y = height_sampler.estimate_ocean_floor_height(center_x, center_z);
|
||||||
if !crate::generation::structure::structures::ocean_monument::has_valid_biomes(
|
if !crate::generation::structure::structures::ocean_monument::has_valid_biomes(
|
||||||
&MultiNoiseBiomeSupplier::OVERWORLD,
|
&generator.biome_supplier,
|
||||||
&mut sampler,
|
&mut sampler,
|
||||||
self.x,
|
self.x,
|
||||||
self.z,
|
self.z,
|
||||||
@ -1407,7 +1382,6 @@ impl ProtoChunk {
|
|||||||
debug_assert_eq!(self.stage, StagedChunkEnum::StructureStart);
|
debug_assert_eq!(self.stage, StagedChunkEnum::StructureStart);
|
||||||
let random_config = &generator.random_config;
|
let random_config = &generator.random_config;
|
||||||
let settings = generator.settings;
|
let settings = generator.settings;
|
||||||
let dimension = &generator.dimension;
|
|
||||||
let noise_router = &generator.base_router;
|
let noise_router = &generator.base_router;
|
||||||
let global_cache = &generator.global_structure_cache;
|
let global_cache = &generator.global_structure_cache;
|
||||||
let calculator = &generator.structure_calculator;
|
let calculator = &generator.structure_calculator;
|
||||||
@ -1419,18 +1393,7 @@ impl ProtoChunk {
|
|||||||
|
|
||||||
let seed = random_config.seed as i64;
|
let seed = random_config.seed as i64;
|
||||||
|
|
||||||
let active_supplier = if *dimension == Dimension::THE_END {
|
let base_supplier: &dyn BiomeSupplier = &generator.biome_supplier;
|
||||||
ActiveSupplier::End(TheEndBiomeSupplier)
|
|
||||||
} else if *dimension == Dimension::THE_NETHER {
|
|
||||||
ActiveSupplier::Nether(MultiNoiseBiomeSupplier::NETHER)
|
|
||||||
} else {
|
|
||||||
ActiveSupplier::Overworld(MultiNoiseBiomeSupplier::OVERWORLD)
|
|
||||||
};
|
|
||||||
|
|
||||||
let base_supplier: &dyn BiomeSupplier = match &active_supplier {
|
|
||||||
ActiveSupplier::End(s) => s,
|
|
||||||
ActiveSupplier::Nether(s) | ActiveSupplier::Overworld(s) => s,
|
|
||||||
};
|
|
||||||
let blender = Blender::empty();
|
let blender = Blender::empty();
|
||||||
let biome_supplier = blender.get_biome_supplier(base_supplier);
|
let biome_supplier = blender.get_biome_supplier(base_supplier);
|
||||||
let mut multi_noise_sampler = MultiNoiseSampler::generate(&noise_router.multi_noise);
|
let mut multi_noise_sampler = MultiNoiseSampler::generate(&noise_router.multi_noise);
|
||||||
@ -1441,9 +1404,19 @@ impl ProtoChunk {
|
|||||||
let mut references = Vec::new();
|
let mut references = Vec::new();
|
||||||
// Constant across every chunk in the dimension, so hoist it out of the loop
|
// Constant across every chunk in the dimension, so hoist it out of the loop
|
||||||
// and out of the (cached) structure-start computation below.
|
// and out of the (cached) structure-start computation below.
|
||||||
let chunk_min_y = self.bottom_y() as i32;
|
// Matches vanilla WorldGenerationContext:
|
||||||
|
// minY = Math.max(heightAccessor.getMinY(), generator.getMinY())
|
||||||
|
// height = Math.min(heightAccessor.getHeight(), generator.getGenDepth())
|
||||||
|
let chunk_min_y = (self.generation_bottom_y() as i32).max(self.bottom_y() as i32);
|
||||||
|
let chunk_height = self.generation_height().min(self.height());
|
||||||
|
|
||||||
for (set_index, set) in StructureSet::ALL.iter().enumerate() {
|
for (set_index, set) in StructureSet::ALL.iter().enumerate() {
|
||||||
|
if let Some(ref enabled) = generator.enabled_structure_sets
|
||||||
|
&& !enabled.contains(&set_index)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let mut candidate_chunks = Vec::new();
|
let mut candidate_chunks = Vec::new();
|
||||||
|
|
||||||
match &set.placement.placement_type {
|
match &set.placement.placement_type {
|
||||||
@ -1465,15 +1438,8 @@ impl ProtoChunk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StructurePlacementType::ConcentricRings(rings) => {
|
StructurePlacementType::ConcentricRings(_) => {
|
||||||
let allowed_biomes = Self::get_allowed_biomes(set);
|
for &(cx, cz) in global_cache.get_stronghold_chunks() {
|
||||||
let strongholds = global_cache.get_or_calculate_strongholds(
|
|
||||||
seed,
|
|
||||||
rings,
|
|
||||||
self,
|
|
||||||
&allowed_biomes,
|
|
||||||
);
|
|
||||||
for &(cx, cz) in strongholds {
|
|
||||||
if (cx - self.x).abs() <= 8 && (cz - self.z).abs() <= 8 {
|
if (cx - self.x).abs() <= 8 && (cz - self.z).abs() <= 8 {
|
||||||
candidate_chunks.push((cx, cz));
|
candidate_chunks.push((cx, cz));
|
||||||
}
|
}
|
||||||
@ -1488,8 +1454,6 @@ impl ProtoChunk {
|
|||||||
candidate_chunk_x,
|
candidate_chunk_x,
|
||||||
candidate_chunk_z,
|
candidate_chunk_z,
|
||||||
global_cache,
|
global_cache,
|
||||||
self,
|
|
||||||
&generator.structure_allowed_biomes[&set_index],
|
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1520,6 +1484,7 @@ impl ProtoChunk {
|
|||||||
),
|
),
|
||||||
sea_level: settings.sea_level,
|
sea_level: settings.sea_level,
|
||||||
min_y: chunk_min_y,
|
min_y: chunk_min_y,
|
||||||
|
height: chunk_height,
|
||||||
height_sampler: Some(&mut height_sampler),
|
height_sampler: Some(&mut height_sampler),
|
||||||
structure_key: Some(entry.structure),
|
structure_key: Some(entry.structure),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use pumpkin_data::{Block, BlockId, block_properties::blocks_movement};
|
use pumpkin_data::{Block, BlockId, BlockState, block_properties::blocks_movement};
|
||||||
use pumpkin_util::math::vector3::Vector3;
|
use pumpkin_util::math::vector3::Vector3;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
@ -24,6 +24,7 @@ pub struct NoiseHeightSampler<'a> {
|
|||||||
preliminary: SurfaceHeightEstimateSampler<'a>,
|
preliminary: SurfaceHeightEstimateSampler<'a>,
|
||||||
heights: FxHashMap<(i32, i32), i32>,
|
heights: FxHashMap<(i32, i32), i32>,
|
||||||
ocean_floor_heights: FxHashMap<(i32, i32), i32>,
|
ocean_floor_heights: FxHashMap<(i32, i32), i32>,
|
||||||
|
columns: FxHashMap<(i32, i32), (i32, Vec<&'static BlockState>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> NoiseHeightSampler<'a> {
|
impl<'a> NoiseHeightSampler<'a> {
|
||||||
@ -42,10 +43,15 @@ impl<'a> NoiseHeightSampler<'a> {
|
|||||||
preliminary,
|
preliminary,
|
||||||
heights: FxHashMap::default(),
|
heights: FxHashMap::default(),
|
||||||
ocean_floor_heights: FxHashMap::default(),
|
ocean_floor_heights: FxHashMap::default(),
|
||||||
|
columns: FxHashMap::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample_column(&mut self, x: i32, z: i32, ocean_floor: bool) -> i32 {
|
fn ensure_column_sampled(&mut self, x: i32, z: i32) {
|
||||||
|
if self.columns.contains_key(&(x, z)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let settings = self.generator.settings;
|
let settings = self.generator.settings;
|
||||||
let shape = &settings.shape;
|
let shape = &settings.shape;
|
||||||
let fluid_sampler = StandardChunkFluidLevelSampler::new(
|
let fluid_sampler = StandardChunkFluidLevelSampler::new(
|
||||||
@ -77,7 +83,8 @@ impl<'a> NoiseHeightSampler<'a> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let densities = noise.sample_density();
|
let densities = noise.sample_density();
|
||||||
for y in (0..volume.size_y).rev() {
|
let mut blocks = Vec::with_capacity(volume.size_y);
|
||||||
|
for y in 0..volume.size_y {
|
||||||
let block_y = volume.block_y(y);
|
let block_y = volume.block_y(y);
|
||||||
let index = volume.index_unchecked(0, y, 0);
|
let index = volume.index_unchecked(0, y, 0);
|
||||||
let state = noise
|
let state = noise
|
||||||
@ -89,6 +96,20 @@ impl<'a> NoiseHeightSampler<'a> {
|
|||||||
&mut self.preliminary,
|
&mut self.preliminary,
|
||||||
)
|
)
|
||||||
.unwrap_or(self.generator.default_block);
|
.unwrap_or(self.generator.default_block);
|
||||||
|
blocks.push(state);
|
||||||
|
}
|
||||||
|
self.columns
|
||||||
|
.insert((x, z), (i32::from(shape.min_y), blocks));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sample_column(&mut self, x: i32, z: i32, ocean_floor: bool) -> i32 {
|
||||||
|
self.ensure_column_sampled(x, z);
|
||||||
|
let Some((min_y, blocks)) = self.columns.get(&(x, z)) else {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
let min_y = *min_y;
|
||||||
|
for (idx, &state) in blocks.iter().enumerate().rev() {
|
||||||
|
let block_y = min_y + idx as i32;
|
||||||
if if ocean_floor {
|
if if ocean_floor {
|
||||||
blocks_movement(state, BlockId::from_state_id(state.id))
|
blocks_movement(state, BlockId::from_state_id(state.id))
|
||||||
} else {
|
} else {
|
||||||
@ -98,7 +119,7 @@ impl<'a> NoiseHeightSampler<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
i32::from(shape.min_y)
|
min_y
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,4 +145,20 @@ impl HeightSampler for NoiseHeightSampler<'_> {
|
|||||||
self.ocean_floor_heights.insert(key, height);
|
self.ocean_floor_heights.insert(key, height);
|
||||||
height
|
height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sample_column_block(
|
||||||
|
&mut self,
|
||||||
|
block_x: i32,
|
||||||
|
block_z: i32,
|
||||||
|
y: i32,
|
||||||
|
) -> Option<&'static BlockState> {
|
||||||
|
self.ensure_column_sampled(block_x, block_z);
|
||||||
|
let (min_y, blocks) = self.columns.get(&(block_x, block_z))?;
|
||||||
|
let idx = y - *min_y;
|
||||||
|
if idx >= 0 && (idx as usize) < blocks.len() {
|
||||||
|
Some(blocks[idx as usize])
|
||||||
|
} else {
|
||||||
|
Some(Block::AIR.default_state)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -147,7 +147,8 @@ pub fn try_generate_structure(
|
|||||||
chunk_z: chunk.z,
|
chunk_z: chunk.z,
|
||||||
random,
|
random,
|
||||||
sea_level,
|
sea_level,
|
||||||
min_y: chunk.bottom_y() as i32,
|
min_y: (chunk.generation_bottom_y() as i32).max(chunk.bottom_y() as i32),
|
||||||
|
height: chunk.generation_height().min(chunk.height()),
|
||||||
height_sampler,
|
height_sampler,
|
||||||
structure_key: Some(*key),
|
structure_key: Some(*key),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -6,13 +6,18 @@ use pumpkin_data::structures::{
|
|||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
math::floor_div,
|
math::floor_div,
|
||||||
random::{
|
random::{
|
||||||
RandomGenerator, RandomImpl, get_carver_seed, get_region_seed, legacy_rand::LegacyRand,
|
RandomGenerator, RandomImpl, get_large_feature_seed, get_region_seed,
|
||||||
|
legacy_rand::LegacyRand,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use rayon::prelude::*;
|
||||||
use std::f64::consts::PI;
|
use std::f64::consts::PI;
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
use crate::ProtoChunk;
|
use crate::biome::{BiomeSupplier, MultiNoiseBiomeSupplier};
|
||||||
|
use crate::generation::noise::router::{
|
||||||
|
multi_noise_sampler::MultiNoiseSampler, proto_noise_router::ProtoMultiNoiseRouter,
|
||||||
|
};
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use pumpkin_data::structures::StructureKeys;
|
use pumpkin_data::structures::StructureKeys;
|
||||||
|
|
||||||
@ -32,6 +37,13 @@ pub struct GlobalStructureCache {
|
|||||||
/// surrounding chunk whose structure references overlap it.
|
/// surrounding chunk whose structure references overlap it.
|
||||||
structure_starts: OnceLock<DashMap<(StructureKeys, i32, i32), Option<StructurePosition>>>,
|
structure_starts: OnceLock<DashMap<(StructureKeys, i32, i32), Option<StructurePosition>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct RingTask {
|
||||||
|
initial_x: i32,
|
||||||
|
initial_z: i32,
|
||||||
|
search_rng: LegacyRand,
|
||||||
|
}
|
||||||
|
|
||||||
impl GlobalStructureCache {
|
impl GlobalStructureCache {
|
||||||
/// Creates a new, empty global structure cache.
|
/// Creates a new, empty global structure cache.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -48,6 +60,10 @@ impl GlobalStructureCache {
|
|||||||
.map_or(&[], std::vec::Vec::as_slice)
|
.map_or(&[], std::vec::Vec::as_slice)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn init_strongholds(&self, chunks: Vec<(i32, i32)>) {
|
||||||
|
let _ = self.stronghold_chunks.set(chunks);
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the memoized structure start for the given structure and start chunk,
|
/// Returns the memoized structure start for the given structure and start chunk,
|
||||||
/// computing it via `compute` on the first request and caching the result.
|
/// computing it via `compute` on the first request and caching the result.
|
||||||
///
|
///
|
||||||
@ -70,98 +86,154 @@ impl GlobalStructureCache {
|
|||||||
computed
|
computed
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves the list of chunk coordinates for Concentric Ring structures.
|
/// Calculates the 128 ring positions matching vanilla Minecraft's
|
||||||
/// If the cache is empty, it calculates the 128 ring positions mathematically.
|
/// `ChunkGeneratorStructureState.generateRingPositions` exactly.
|
||||||
#[allow(clippy::cast_precision_loss)]
|
#[must_use]
|
||||||
pub fn get_or_calculate_strongholds(
|
pub fn calculate_strongholds(
|
||||||
&self,
|
|
||||||
seed: i64,
|
seed: i64,
|
||||||
placement: &ConcentricRingsStructurePlacement,
|
placement: &ConcentricRingsStructurePlacement,
|
||||||
biome_supplier: &ProtoChunk,
|
multi_noise: &ProtoMultiNoiseRouter,
|
||||||
allowed_biomes: &[u16],
|
) -> Vec<(i32, i32)> {
|
||||||
) -> &[(i32, i32)] {
|
let distance_param = f64::from(placement.distance);
|
||||||
self.stronghold_chunks.get_or_init(|| {
|
let mut spread = placement.spread;
|
||||||
let mut chunks = Vec::with_capacity(placement.count as usize);
|
let count = placement.count;
|
||||||
|
|
||||||
let distance_param = f64::from(placement.distance); // Usually 32
|
let preferred_biomes = pumpkin_data::tag::get_tag_ids(
|
||||||
let mut spread = placement.spread; // Usually 3
|
pumpkin_data::tag::RegistryKey::WorldgenBiome,
|
||||||
let count = placement.count; // Usually 128
|
placement
|
||||||
|
.preferred_biomes
|
||||||
|
.strip_prefix('#')
|
||||||
|
.unwrap_or(placement.preferred_biomes),
|
||||||
|
)
|
||||||
|
.unwrap_or(&[]);
|
||||||
|
|
||||||
// The random generator for stronghold placement is based on the world seed and a fixed salt.
|
let mut random = LegacyRand::from_seed(seed as u64);
|
||||||
// This ensures that the stronghold layout is consistent across all worlds with the same seed.
|
let mut angle = random.next_f64() * PI * 2.0;
|
||||||
let mut random = RandomGenerator::Legacy(LegacyRand::from_seed(seed as u64));
|
let mut position_in_circle = 0;
|
||||||
|
let mut circle = 0;
|
||||||
|
|
||||||
// The initial angle includes a random rotation jitter for the whole world
|
let mut tasks = Vec::with_capacity(count as usize);
|
||||||
let mut angle = random.next_f64() * PI * 2.0;
|
|
||||||
let mut position_in_circle = 0;
|
|
||||||
let mut circle = 0;
|
|
||||||
|
|
||||||
for i in 0..count {
|
for i in 0..count {
|
||||||
// 1. Distance Formula
|
let dist = 4.0 * distance_param
|
||||||
// dist = (4 * spacing) + (spacing * ring_index * 6) + (random_jitter)
|
+ distance_param * f64::from(circle) * 6.0
|
||||||
// The jitter is +/- (spacing * 1.25)
|
+ (random.next_f64() - 0.5) * (distance_param * 2.5);
|
||||||
let dist = 4.0 * distance_param
|
|
||||||
+ distance_param * f64::from(circle) * 6.0
|
|
||||||
+ (random.next_f64() - 0.5) * (distance_param * 2.5);
|
|
||||||
|
|
||||||
let initial_x = (angle.cos() * dist + 0.5).floor() as i32;
|
let initial_x = (angle.cos() * dist + 0.5).floor() as i32;
|
||||||
let initial_z = (angle.sin() * dist + 0.5).floor() as i32;
|
let initial_z = (angle.sin() * dist + 0.5).floor() as i32;
|
||||||
|
|
||||||
// 2. RNG Forking
|
let fork_seed = random.next_i64();
|
||||||
// We must fork/split the random generator for the biome search.
|
let search_rng = LegacyRand::from_seed(fork_seed as u64);
|
||||||
// This keeps the main angle/distance sequence identical across all worlds.
|
|
||||||
let fork_seed = random.next_i64();
|
|
||||||
|
|
||||||
let mut biome_search_generator =
|
tasks.push(RingTask {
|
||||||
RandomGenerator::Legacy(LegacyRand::from_seed(fork_seed as u64));
|
initial_x,
|
||||||
|
initial_z,
|
||||||
|
search_rng,
|
||||||
|
});
|
||||||
|
|
||||||
// 3. Reservoir Sampling Biome Search
|
angle += (PI * 2.0) / f64::from(spread);
|
||||||
// Strongholds search the entire 112-block square
|
position_in_circle += 1;
|
||||||
// and pick one valid location at random (Reservoir Sampling).
|
|
||||||
let mut found_pos = None;
|
|
||||||
let mut found_count = 0;
|
|
||||||
|
|
||||||
let center_block_x = (initial_x << 4) + 8;
|
if position_in_circle == spread {
|
||||||
let center_block_z = (initial_z << 4) + 8;
|
circle += 1;
|
||||||
let step = 4;
|
position_in_circle = 0;
|
||||||
let search_radius = 112;
|
|
||||||
|
|
||||||
for dz in (-search_radius..=search_radius).step_by(step as usize) {
|
spread += 2 * spread / (circle + 1);
|
||||||
for dx in (-search_radius..=search_radius).step_by(step as usize) {
|
spread = spread.min(count - i);
|
||||||
let test_x = center_block_x + dx;
|
angle += random.next_f64() * PI * 2.0;
|
||||||
let test_z = center_block_z + dz;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let biome = biome_supplier.get_biome(test_x, 0, test_z);
|
tasks
|
||||||
|
.into_par_iter()
|
||||||
|
.map(|mut task| {
|
||||||
|
let mut sampler = MultiNoiseSampler::generate(multi_noise);
|
||||||
|
let noise_center_x = (task.initial_x << 2) + 2;
|
||||||
|
let noise_center_z = (task.initial_z << 2) + 2;
|
||||||
|
|
||||||
if allowed_biomes.contains(&(biome.id as u16)) {
|
let mut result = None;
|
||||||
found_count += 1;
|
let mut found = 0;
|
||||||
// Reservoir sampling: Pick the Nth valid biome with 1/N probability
|
|
||||||
if found_pos.is_none()
|
for z in -28..=28 {
|
||||||
|| biome_search_generator.next_bounded_i32(found_count) == 0
|
for x in -28..=28 {
|
||||||
|
let noise_x = noise_center_x + x;
|
||||||
|
let noise_z = noise_center_z + z;
|
||||||
|
let biome = MultiNoiseBiomeSupplier::OVERWORLD.biome(
|
||||||
|
noise_x,
|
||||||
|
0,
|
||||||
|
noise_z,
|
||||||
|
&mut sampler,
|
||||||
|
);
|
||||||
|
if preferred_biomes.contains(&(biome.id as u16)) {
|
||||||
|
if result.is_none() || task.search_rng.next_bounded_i32(found + 1) == 0
|
||||||
{
|
{
|
||||||
found_pos = Some((test_x >> 4, test_z >> 4));
|
result = Some((noise_x >> 2, noise_z >> 2));
|
||||||
}
|
}
|
||||||
|
found += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (final_chunk_x, final_chunk_z) = found_pos.unwrap_or((initial_x, initial_z));
|
result.unwrap_or((task.initial_x, task.initial_z))
|
||||||
chunks.push((final_chunk_x, final_chunk_z));
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
// 4. Dynamic Ring Progression
|
#[must_use]
|
||||||
angle += (PI * 2.0) / f64::from(spread);
|
pub fn calculate_strongholds_without_biomes(
|
||||||
position_in_circle += 1;
|
seed: i64,
|
||||||
|
placement: &ConcentricRingsStructurePlacement,
|
||||||
|
) -> Vec<(i32, i32)> {
|
||||||
|
let distance_param = f64::from(placement.distance);
|
||||||
|
let mut spread = placement.spread;
|
||||||
|
let count = placement.count;
|
||||||
|
|
||||||
if position_in_circle == spread {
|
let mut random = LegacyRand::from_seed(seed as u64);
|
||||||
circle += 1;
|
let mut angle = random.next_f64() * PI * 2.0;
|
||||||
position_in_circle = 0;
|
let mut position_in_circle = 0;
|
||||||
|
let mut circle = 0;
|
||||||
|
|
||||||
spread += 2 * spread / (circle + 1);
|
let mut chunks = Vec::with_capacity(count as usize);
|
||||||
spread = spread.min(count - i);
|
|
||||||
angle += random.next_f64() * PI * 2.0;
|
for i in 0..count {
|
||||||
}
|
let dist = 4.0 * distance_param
|
||||||
|
+ distance_param * f64::from(circle) * 6.0
|
||||||
|
+ (random.next_f64() - 0.5) * (distance_param * 2.5);
|
||||||
|
|
||||||
|
let initial_x = (angle.cos() * dist + 0.5).floor() as i32;
|
||||||
|
let initial_z = (angle.sin() * dist + 0.5).floor() as i32;
|
||||||
|
|
||||||
|
chunks.push((initial_x, initial_z));
|
||||||
|
|
||||||
|
angle += (PI * 2.0) / f64::from(spread);
|
||||||
|
position_in_circle += 1;
|
||||||
|
|
||||||
|
if position_in_circle == spread {
|
||||||
|
circle += 1;
|
||||||
|
position_in_circle = 0;
|
||||||
|
|
||||||
|
spread += 2 * spread / (circle + 1);
|
||||||
|
spread = spread.min(count - i);
|
||||||
|
angle += random.next_f64() * PI * 2.0;
|
||||||
}
|
}
|
||||||
chunks
|
}
|
||||||
|
|
||||||
|
chunks
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the list of chunk coordinates for Concentric Ring structures.
|
||||||
|
/// If the cache is empty, it calculates the 128 ring positions.
|
||||||
|
pub fn get_or_calculate_strongholds(
|
||||||
|
&self,
|
||||||
|
seed: i64,
|
||||||
|
placement: &ConcentricRingsStructurePlacement,
|
||||||
|
multi_noise: Option<&ProtoMultiNoiseRouter>,
|
||||||
|
) -> &[(i32, i32)] {
|
||||||
|
self.stronghold_chunks.get_or_init(|| {
|
||||||
|
multi_noise.map_or_else(
|
||||||
|
|| Self::calculate_strongholds_without_biomes(seed, placement),
|
||||||
|
|multi_noise| Self::calculate_strongholds(seed, placement, multi_noise),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -173,15 +245,12 @@ impl Default for GlobalStructureCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
// #[expect(clippy::too_many_arguments)]
|
|
||||||
pub fn should_generate_structure(
|
pub fn should_generate_structure(
|
||||||
placement: &StructurePlacement,
|
placement: &StructurePlacement,
|
||||||
calculator: &StructurePlacementCalculator,
|
calculator: &StructurePlacementCalculator,
|
||||||
chunk_x: i32,
|
chunk_x: i32,
|
||||||
chunk_z: i32,
|
chunk_z: i32,
|
||||||
global_cache: &GlobalStructureCache,
|
global_cache: &GlobalStructureCache,
|
||||||
biome_supplier: &ProtoChunk,
|
|
||||||
allowed_biomes: &[u16],
|
|
||||||
) -> bool {
|
) -> bool {
|
||||||
is_start_chunk(
|
is_start_chunk(
|
||||||
&placement.placement_type,
|
&placement.placement_type,
|
||||||
@ -190,8 +259,6 @@ pub fn should_generate_structure(
|
|||||||
chunk_z,
|
chunk_z,
|
||||||
placement.salt,
|
placement.salt,
|
||||||
global_cache,
|
global_cache,
|
||||||
biome_supplier,
|
|
||||||
allowed_biomes,
|
|
||||||
) && apply_frequency_reduction(
|
) && apply_frequency_reduction(
|
||||||
placement.frequency_reduction_method,
|
placement.frequency_reduction_method,
|
||||||
calculator.seed,
|
calculator.seed,
|
||||||
@ -205,18 +272,9 @@ pub fn should_generate_structure(
|
|||||||
.strip_prefix("minecraft:")
|
.strip_prefix("minecraft:")
|
||||||
.unwrap_or(zone.other_set);
|
.unwrap_or(zone.other_set);
|
||||||
StructureSet::get(set_name).is_some_and(|set| {
|
StructureSet::get(set_name).is_some_and(|set| {
|
||||||
let allowed_biomes = ProtoChunk::get_allowed_biomes(set);
|
|
||||||
(chunk_x - zone.chunk_count..=chunk_x + zone.chunk_count).any(|x| {
|
(chunk_x - zone.chunk_count..=chunk_x + zone.chunk_count).any(|x| {
|
||||||
(chunk_z - zone.chunk_count..=chunk_z + zone.chunk_count).any(|z| {
|
(chunk_z - zone.chunk_count..=chunk_z + zone.chunk_count).any(|z| {
|
||||||
should_generate_structure(
|
should_generate_structure(&set.placement, calculator, x, z, global_cache)
|
||||||
&set.placement,
|
|
||||||
calculator,
|
|
||||||
x,
|
|
||||||
z,
|
|
||||||
global_cache,
|
|
||||||
biome_supplier,
|
|
||||||
&allowed_biomes,
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -266,14 +324,13 @@ fn should_generate_frequency(
|
|||||||
random.next_f32() < frequency
|
random.next_f32() < frequency
|
||||||
}
|
}
|
||||||
FrequencyReductionMethod::LegacyType3 => {
|
FrequencyReductionMethod::LegacyType3 => {
|
||||||
let carver_seed = get_carver_seed(seed as u64, chunk_x, chunk_z);
|
let feature_seed = get_large_feature_seed(seed as u64, chunk_x, chunk_z);
|
||||||
let mut random = LegacyRand::from_seed(carver_seed);
|
let mut random = LegacyRand::from_seed(feature_seed);
|
||||||
random.next_f64() < f64::from(frequency)
|
random.next_f64() < f64::from(frequency)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(clippy::too_many_arguments)]
|
|
||||||
fn is_start_chunk(
|
fn is_start_chunk(
|
||||||
placement_type: &StructurePlacementType,
|
placement_type: &StructurePlacementType,
|
||||||
calculator: &StructurePlacementCalculator,
|
calculator: &StructurePlacementCalculator,
|
||||||
@ -281,20 +338,14 @@ fn is_start_chunk(
|
|||||||
chunk_z: i32,
|
chunk_z: i32,
|
||||||
salt: u32,
|
salt: u32,
|
||||||
global_cache: &GlobalStructureCache,
|
global_cache: &GlobalStructureCache,
|
||||||
biome_supplier: &ProtoChunk,
|
|
||||||
allowed_biomes: &[u16],
|
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match placement_type {
|
match placement_type {
|
||||||
StructurePlacementType::RandomSpread(placement) => {
|
StructurePlacementType::RandomSpread(placement) => {
|
||||||
is_start_chunk_random_spread(placement, calculator, chunk_x, chunk_z, salt)
|
is_start_chunk_random_spread(placement, calculator, chunk_x, chunk_z, salt)
|
||||||
}
|
}
|
||||||
StructurePlacementType::ConcentricRings(placement) => {
|
StructurePlacementType::ConcentricRings(placement) => {
|
||||||
let strongholds = global_cache.get_or_calculate_strongholds(
|
let strongholds =
|
||||||
calculator.seed,
|
global_cache.get_or_calculate_strongholds(calculator.seed, placement, None);
|
||||||
placement,
|
|
||||||
biome_supplier,
|
|
||||||
allowed_biomes,
|
|
||||||
);
|
|
||||||
strongholds.contains(&(chunk_x, chunk_z))
|
strongholds.contains(&(chunk_x, chunk_z))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -351,24 +402,16 @@ fn is_start_chunk_random_spread(
|
|||||||
}
|
}
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use pumpkin_data::{
|
use pumpkin_data::structures::{
|
||||||
dimension::Dimension,
|
RandomSpreadStructurePlacement, StructurePlacementCalculator, StructureSet,
|
||||||
structures::{RandomSpreadStructurePlacement, StructurePlacementCalculator, StructureSet},
|
|
||||||
};
|
};
|
||||||
use pumpkin_util::random::{
|
use pumpkin_util::random::{
|
||||||
RandomGenerator, RandomImpl, get_region_seed, legacy_rand::LegacyRand,
|
RandomGenerator, RandomImpl, get_region_seed, legacy_rand::LegacyRand,
|
||||||
};
|
};
|
||||||
use pumpkin_util::world_seed::Seed;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::generation::structure::placement::{
|
||||||
ProtoChunk,
|
GlobalStructureCache, apply_frequency_reduction, get_start_chunk_random_spread,
|
||||||
generation::{
|
is_start_chunk, should_generate_structure,
|
||||||
get_world_gen,
|
|
||||||
structure::placement::{
|
|
||||||
GlobalStructureCache, apply_frequency_reduction, get_start_chunk_random_spread,
|
|
||||||
is_start_chunk, should_generate_structure,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -395,14 +438,6 @@ mod tests {
|
|||||||
let seed = 0;
|
let seed = 0;
|
||||||
let calculator = StructurePlacementCalculator::new(seed);
|
let calculator = StructurePlacementCalculator::new(seed);
|
||||||
let cache = GlobalStructureCache::new();
|
let cache = GlobalStructureCache::new();
|
||||||
let generator = get_world_gen(
|
|
||||||
Seed(seed as u64),
|
|
||||||
Dimension::OVERWORLD,
|
|
||||||
false,
|
|
||||||
Vec::new(),
|
|
||||||
String::new(),
|
|
||||||
);
|
|
||||||
let chunk = ProtoChunk::new(0, 0, &generator);
|
|
||||||
let outposts = &StructureSet::PILLAGER_OUTPOSTS;
|
let outposts = &StructureSet::PILLAGER_OUTPOSTS;
|
||||||
let villages = &StructureSet::VILLAGES;
|
let villages = &StructureSet::VILLAGES;
|
||||||
let excluded = (-1002, -595);
|
let excluded = (-1002, -595);
|
||||||
@ -413,8 +448,6 @@ mod tests {
|
|||||||
excluded.1,
|
excluded.1,
|
||||||
outposts.placement.salt,
|
outposts.placement.salt,
|
||||||
&cache,
|
&cache,
|
||||||
&chunk,
|
|
||||||
&[],
|
|
||||||
));
|
));
|
||||||
assert!(apply_frequency_reduction(
|
assert!(apply_frequency_reduction(
|
||||||
outposts.placement.frequency_reduction_method,
|
outposts.placement.frequency_reduction_method,
|
||||||
@ -433,8 +466,6 @@ mod tests {
|
|||||||
excluded.1 + dz,
|
excluded.1 + dz,
|
||||||
villages.placement.salt,
|
villages.placement.salt,
|
||||||
&cache,
|
&cache,
|
||||||
&chunk,
|
|
||||||
&[],
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
@ -445,8 +476,6 @@ mod tests {
|
|||||||
excluded.0,
|
excluded.0,
|
||||||
excluded.1,
|
excluded.1,
|
||||||
&cache,
|
&cache,
|
||||||
&chunk,
|
|
||||||
&[],
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -341,6 +341,7 @@ mod tests {
|
|||||||
random: RandomGenerator::Legacy(LegacyRand::from_seed(seed)),
|
random: RandomGenerator::Legacy(LegacyRand::from_seed(seed)),
|
||||||
sea_level: 63,
|
sea_level: 63,
|
||||||
min_y: 0,
|
min_y: 0,
|
||||||
|
height: 128,
|
||||||
height_sampler: Some(&mut sampler),
|
height_sampler: Some(&mut sampler),
|
||||||
structure_key: None,
|
structure_key: None,
|
||||||
})
|
})
|
||||||
@ -374,6 +375,7 @@ mod tests {
|
|||||||
random: RandomGenerator::Legacy(LegacyRand::from_seed(0)),
|
random: RandomGenerator::Legacy(LegacyRand::from_seed(0)),
|
||||||
sea_level: 63,
|
sea_level: 63,
|
||||||
min_y: 0,
|
min_y: 0,
|
||||||
|
height: 128,
|
||||||
height_sampler: Some(&mut sampler),
|
height_sampler: Some(&mut sampler),
|
||||||
structure_key: None,
|
structure_key: None,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -823,10 +823,9 @@ impl StructureGenerator for JigsawGenerator {
|
|||||||
.structure_key
|
.structure_key
|
||||||
.map(|key| pumpkin_data::structures::Structure::get(&key));
|
.map(|key| pumpkin_data::structures::Structure::get(&key));
|
||||||
|
|
||||||
let height = if context.min_y < 0 { 384 } else { 256 };
|
|
||||||
let start_y = if let Some(s) = structure {
|
let start_y = if let Some(s) = structure {
|
||||||
s.start_height.map_or(context.sea_level, |hp| {
|
s.start_height.map_or(context.sea_level, |hp| {
|
||||||
hp.get(&mut context.random, context.min_y as i8, height)
|
hp.get(&mut context.random, context.min_y as i8, context.height)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
context.sea_level
|
context.sea_level
|
||||||
@ -1012,6 +1011,7 @@ mod tests {
|
|||||||
random: super::super::create_chunk_random(0, 0, 0),
|
random: super::super::create_chunk_random(0, 0, 0),
|
||||||
sea_level: 63,
|
sea_level: 63,
|
||||||
min_y: -64,
|
min_y: -64,
|
||||||
|
height: 384,
|
||||||
height_sampler: None,
|
height_sampler: None,
|
||||||
structure_key: Some(pumpkin_data::structures::StructureKeys::AncientCity),
|
structure_key: Some(pumpkin_data::structures::StructureKeys::AncientCity),
|
||||||
};
|
};
|
||||||
@ -1123,6 +1123,7 @@ mod tests {
|
|||||||
random: super::super::create_chunk_random(SEED, 75, -82),
|
random: super::super::create_chunk_random(SEED, 75, -82),
|
||||||
sea_level: 63,
|
sea_level: 63,
|
||||||
min_y: -64,
|
min_y: -64,
|
||||||
|
height: 384,
|
||||||
height_sampler: Some(&mut height_sampler),
|
height_sampler: Some(&mut height_sampler),
|
||||||
structure_key: Some(pumpkin_data::structures::StructureKeys::PillagerOutpost),
|
structure_key: Some(pumpkin_data::structures::StructureKeys::PillagerOutpost),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -619,7 +619,7 @@ impl JigsawPlacement {
|
|||||||
box_.move_pos(0, y_offset, 0);
|
box_.move_pos(0, y_offset, 0);
|
||||||
let piece_pos = adjusted_position.add(0, y_offset, 0);
|
let piece_pos = adjusted_position.add(0, y_offset, 0);
|
||||||
|
|
||||||
let max_y = context.min_y + 384 - 1;
|
let max_y = context.min_y + context.height as i32 - 1;
|
||||||
if is_start_too_close_to_world_height_limits(context.min_y, max_y, dimension_padding, &box_)
|
if is_start_too_close_to_world_height_limits(context.min_y, max_y, dimension_padding, &box_)
|
||||||
{
|
{
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
|
|||||||
@ -635,6 +635,7 @@ mod tests {
|
|||||||
random: RandomGenerator::Legacy(LegacyRand::from_seed(seed)),
|
random: RandomGenerator::Legacy(LegacyRand::from_seed(seed)),
|
||||||
sea_level: 63,
|
sea_level: 63,
|
||||||
min_y: -64,
|
min_y: -64,
|
||||||
|
height: 384,
|
||||||
height_sampler: Some(&mut sampler),
|
height_sampler: Some(&mut sampler),
|
||||||
structure_key: None,
|
structure_key: None,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -835,6 +835,15 @@ pub trait HeightSampler {
|
|||||||
fn estimate_ocean_floor_height(&mut self, block_x: i32, block_z: i32) -> i32 {
|
fn estimate_ocean_floor_height(&mut self, block_x: i32, block_z: i32) -> i32 {
|
||||||
self.estimate_height(block_x, block_z)
|
self.estimate_height(block_x, block_z)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sample_column_block(
|
||||||
|
&mut self,
|
||||||
|
_block_x: i32,
|
||||||
|
_block_z: i32,
|
||||||
|
_y: i32,
|
||||||
|
) -> Option<&'static pumpkin_data::BlockState> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HeightSampler
|
impl HeightSampler
|
||||||
@ -852,6 +861,7 @@ pub struct StructureGeneratorContext<'a> {
|
|||||||
pub random: RandomGenerator,
|
pub random: RandomGenerator,
|
||||||
pub sea_level: i32,
|
pub sea_level: i32,
|
||||||
pub min_y: i32,
|
pub min_y: i32,
|
||||||
|
pub height: u16,
|
||||||
pub height_sampler: Option<&'a mut dyn HeightSampler>,
|
pub height_sampler: Option<&'a mut dyn HeightSampler>,
|
||||||
pub structure_key: Option<pumpkin_data::structures::StructureKeys>,
|
pub structure_key: Option<pumpkin_data::structures::StructureKeys>,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,9 +45,10 @@ pub const FOSSILS: [&str; 14] = [
|
|||||||
|
|
||||||
/// Vanilla height provider bounds for nether fossils.
|
/// Vanilla height provider bounds for nether fossils.
|
||||||
/// From `nether_fossil.json`: uniform(absolute=32, `below_top=2`).
|
/// From `nether_fossil.json`: uniform(absolute=32, `below_top=2`).
|
||||||
/// Vanilla `BelowTop`: height - 1 + `min_y` - offset = 256 - 1 + 0 - 2 = 253.
|
/// In vanilla Nether, generator gen depth is 128:
|
||||||
|
/// `below_top=2`: height - 1 + `min_y` - offset = 128 - 1 + 0 - 2 = 125.
|
||||||
const HEIGHT_MIN: i32 = 32;
|
const HEIGHT_MIN: i32 = 32;
|
||||||
const HEIGHT_MAX: i32 = 253;
|
const HEIGHT_MAX: i32 = 125;
|
||||||
|
|
||||||
pub struct NetherFossilGenerator;
|
pub struct NetherFossilGenerator;
|
||||||
|
|
||||||
@ -56,11 +57,12 @@ impl StructureGenerator for NetherFossilGenerator {
|
|||||||
&self,
|
&self,
|
||||||
mut context: StructureGeneratorContext<'_>,
|
mut context: StructureGeneratorContext<'_>,
|
||||||
) -> Option<StructurePosition> {
|
) -> Option<StructurePosition> {
|
||||||
// Vanilla random call order:
|
// Vanilla random call order (NetherFossilStructure.java):
|
||||||
// 1. nextInt(16) for X offset within chunk
|
// 1. nextInt(16) for X offset within chunk
|
||||||
// 2. nextInt(16) for Z offset within chunk
|
// 2. nextInt(16) for Z offset within chunk
|
||||||
// 3. height.get(random) for initial Y (uniform 32..254)
|
// 3. height.sample(random, generationContext) for initial Y (uniform 32..125)
|
||||||
// 4. Column scan (no random calls)
|
// 4. Column scan downward to seaLevel (no random calls)
|
||||||
|
// If y <= seaLevel: return empty
|
||||||
// 5. Rotation.getRandom(random) - nextInt(4)
|
// 5. Rotation.getRandom(random) - nextInt(4)
|
||||||
// 6. Util.getRandom(FOSSILS, random) - nextInt(14)
|
// 6. Util.getRandom(FOSSILS, random) - nextInt(14)
|
||||||
|
|
||||||
@ -72,12 +74,37 @@ impl StructureGenerator for NetherFossilGenerator {
|
|||||||
.map(|key| pumpkin_data::structures::Structure::get(&key));
|
.map(|key| pumpkin_data::structures::Structure::get(&key));
|
||||||
|
|
||||||
let initial_y = if let Some(hp) = structure.and_then(|s| s.start_height) {
|
let initial_y = if let Some(hp) = structure.and_then(|s| s.start_height) {
|
||||||
hp.get(&mut context.random, context.min_y as i8, 256)
|
hp.get(&mut context.random, context.min_y as i8, context.height)
|
||||||
} else {
|
} else {
|
||||||
let height_range = HEIGHT_MAX - HEIGHT_MIN + 1;
|
let height_range = HEIGHT_MAX - HEIGHT_MIN + 1;
|
||||||
HEIGHT_MIN + context.random.next_bounded_i32(height_range)
|
HEIGHT_MIN + context.random.next_bounded_i32(height_range)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut y = initial_y;
|
||||||
|
if let Some(sampler) = context.height_sampler.as_deref_mut() {
|
||||||
|
let mut checked_column = false;
|
||||||
|
while y > context.sea_level {
|
||||||
|
let Some(current) = sampler.sample_column_block(x, z, y) else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
checked_column = true;
|
||||||
|
y -= 1;
|
||||||
|
let below = sampler
|
||||||
|
.sample_column_block(x, z, y)
|
||||||
|
.unwrap_or(Block::AIR.default_state);
|
||||||
|
if current.is_air()
|
||||||
|
&& (Block::from_state_id(below.id) == &Block::SOUL_SAND
|
||||||
|
|| below.is_side_solid(BlockDirection::Up))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if checked_column && y <= context.sea_level {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let rotation_index = context.random.next_bounded_i32(4) as u8;
|
let rotation_index = context.random.next_bounded_i32(4) as u8;
|
||||||
let rotation = Rotation::from_index(rotation_index);
|
let rotation = Rotation::from_index(rotation_index);
|
||||||
|
|
||||||
@ -85,23 +112,16 @@ impl StructureGenerator for NetherFossilGenerator {
|
|||||||
let template_name = FOSSILS[template_index];
|
let template_name = FOSSILS[template_index];
|
||||||
|
|
||||||
let template = get_template(template_name)?;
|
let template = get_template(template_name)?;
|
||||||
let position = Vector3::new(x, initial_y, z);
|
let position = Vector3::new(x, y, z);
|
||||||
|
|
||||||
let mut collector = StructurePiecesCollector::default();
|
let mut collector = StructurePiecesCollector::default();
|
||||||
|
|
||||||
let piece = NetherFossilPiece::new(
|
let piece = NetherFossilPiece::new(template, template_name.to_string(), position, rotation);
|
||||||
template,
|
|
||||||
template_name.to_string(),
|
|
||||||
position,
|
|
||||||
rotation,
|
|
||||||
initial_y,
|
|
||||||
context.sea_level,
|
|
||||||
);
|
|
||||||
|
|
||||||
collector.add_piece(Box::new(piece));
|
collector.add_piece(Box::new(piece));
|
||||||
|
|
||||||
Some(StructurePosition {
|
Some(StructurePosition {
|
||||||
start_pos: BlockPos::new(x, initial_y, z),
|
start_pos: BlockPos::new(x, y, z),
|
||||||
collector: Arc::new(collector.into()),
|
collector: Arc::new(collector.into()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -113,8 +133,6 @@ pub struct NetherFossilPiece {
|
|||||||
pub template_name: String,
|
pub template_name: String,
|
||||||
pub place_settings: StructurePlaceSettings,
|
pub place_settings: StructurePlaceSettings,
|
||||||
pub template_position: Vector3<i32>,
|
pub template_position: Vector3<i32>,
|
||||||
pub initial_y: i32,
|
|
||||||
pub sea_level: i32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NetherFossilPiece {
|
impl NetherFossilPiece {
|
||||||
@ -124,8 +142,6 @@ impl NetherFossilPiece {
|
|||||||
template_name: String,
|
template_name: String,
|
||||||
template_position: Vector3<i32>,
|
template_position: Vector3<i32>,
|
||||||
rotation: Rotation,
|
rotation: Rotation,
|
||||||
initial_y: i32,
|
|
||||||
sea_level: i32,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let place_settings = make_settings(rotation);
|
let place_settings = make_settings(rotation);
|
||||||
let bounding_box = template.get_bounding_box(&place_settings, template_position);
|
let bounding_box = template.get_bounding_box(&place_settings, template_position);
|
||||||
@ -136,36 +152,9 @@ impl NetherFossilPiece {
|
|||||||
template_name,
|
template_name,
|
||||||
place_settings,
|
place_settings,
|
||||||
template_position,
|
template_position,
|
||||||
initial_y,
|
|
||||||
sea_level,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Vanilla column scan: search downward from `initial_y` for air above (soul sand OR solid block).
|
|
||||||
/// Returns the Y of the support block, or None if no valid position found above sea level.
|
|
||||||
fn find_placement_y(&self, chunk: &ProtoChunk) -> Option<i32> {
|
|
||||||
let origin = self.template_position;
|
|
||||||
let mut y = self.initial_y;
|
|
||||||
|
|
||||||
while y > self.sea_level {
|
|
||||||
let upper = chunk.get_block_state(&Vector3::new(origin.x, y, origin.z));
|
|
||||||
y -= 1;
|
|
||||||
let lower = chunk.get_block_state(&Vector3::new(origin.x, y, origin.z));
|
|
||||||
|
|
||||||
let upper_state = BlockState::from_id(upper);
|
|
||||||
let lower_state = BlockState::from_id(lower);
|
|
||||||
|
|
||||||
if upper_state.is_air()
|
|
||||||
&& (Block::from_state_id(lower) == &Block::SOUL_SAND
|
|
||||||
|| lower_state.is_side_solid(BlockDirection::Up))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if y <= self.sea_level { None } else { Some(y) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn place_blocks(&self, chunk: &mut ProtoChunk, chunk_box: &BlockBox) {
|
fn place_blocks(&self, chunk: &mut ProtoChunk, chunk_box: &BlockBox) {
|
||||||
let rotation = self.place_settings.get_rotation();
|
let rotation = self.place_settings.get_rotation();
|
||||||
let mirror = self.place_settings.get_mirror();
|
let mirror = self.place_settings.get_mirror();
|
||||||
@ -278,15 +267,6 @@ impl StructurePieceBase for NetherFossilPiece {
|
|||||||
seed: i64,
|
seed: i64,
|
||||||
chunk_box: &BlockBox,
|
chunk_box: &BlockBox,
|
||||||
) {
|
) {
|
||||||
let Some(placement_y) = self.find_placement_y(chunk) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.template_position.y = placement_y;
|
|
||||||
self.piece.bounding_box = self
|
|
||||||
.template
|
|
||||||
.get_bounding_box(&self.place_settings, self.template_position);
|
|
||||||
|
|
||||||
let fossil_bb = self.piece.bounding_box;
|
let fossil_bb = self.piece.bounding_box;
|
||||||
let mut enlarged_box = *chunk_box;
|
let mut enlarged_box = *chunk_box;
|
||||||
enlarged_box.encompass(&fossil_bb);
|
enlarged_box.encompass(&fossil_bb);
|
||||||
@ -311,3 +291,84 @@ fn make_settings(rotation: Rotation) -> StructurePlaceSettings {
|
|||||||
},
|
},
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::generation::structure::structures::HeightSampler;
|
||||||
|
use pumpkin_data::Block;
|
||||||
|
|
||||||
|
struct MockColumnSampler {
|
||||||
|
ground_y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HeightSampler for MockColumnSampler {
|
||||||
|
fn estimate_height(&mut self, _block_x: i32, _block_z: i32) -> i32 {
|
||||||
|
self.ground_y
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sample_column_block(
|
||||||
|
&mut self,
|
||||||
|
_block_x: i32,
|
||||||
|
_block_z: i32,
|
||||||
|
y: i32,
|
||||||
|
) -> Option<&'static BlockState> {
|
||||||
|
if y > self.ground_y {
|
||||||
|
Some(Block::AIR.default_state)
|
||||||
|
} else {
|
||||||
|
Some(Block::NETHERRACK.default_state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nether_fossil_never_places_on_roof() {
|
||||||
|
for seed in 0..100 {
|
||||||
|
let mut sampler = MockColumnSampler { ground_y: 60 };
|
||||||
|
let context = StructureGeneratorContext {
|
||||||
|
seed,
|
||||||
|
chunk_x: 0,
|
||||||
|
chunk_z: 0,
|
||||||
|
random: crate::generation::structure::structures::create_chunk_random(seed, 0, 0),
|
||||||
|
sea_level: 32,
|
||||||
|
min_y: 0,
|
||||||
|
height: 128,
|
||||||
|
height_sampler: Some(&mut sampler),
|
||||||
|
structure_key: Some(pumpkin_data::structures::StructureKeys::NetherFossil),
|
||||||
|
};
|
||||||
|
if let Some(pos) = NetherFossilGenerator.get_structure_position(context) {
|
||||||
|
assert!(
|
||||||
|
pos.start_pos.0.y <= 125,
|
||||||
|
"Fossil placed at {}, above logical nether height!",
|
||||||
|
pos.start_pos.0.y
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
pos.start_pos.0.y >= 32,
|
||||||
|
"Fossil placed at {}, below sea level!",
|
||||||
|
pos.start_pos.0.y
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nether_fossil_rejects_when_no_ground_above_sea_level() {
|
||||||
|
let mut sampler = MockColumnSampler { ground_y: 20 }; // below sea_level (32)
|
||||||
|
let context = StructureGeneratorContext {
|
||||||
|
seed: 42,
|
||||||
|
chunk_x: 0,
|
||||||
|
chunk_z: 0,
|
||||||
|
random: crate::generation::structure::structures::create_chunk_random(42, 0, 0),
|
||||||
|
sea_level: 32,
|
||||||
|
min_y: 0,
|
||||||
|
height: 128,
|
||||||
|
height_sampler: Some(&mut sampler),
|
||||||
|
structure_key: Some(pumpkin_data::structures::StructureKeys::NetherFossil),
|
||||||
|
};
|
||||||
|
assert!(
|
||||||
|
NetherFossilGenerator
|
||||||
|
.get_structure_position(context)
|
||||||
|
.is_none()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -545,6 +545,7 @@ mod tests {
|
|||||||
random: RandomGenerator::Legacy(LegacyRand::from_seed(0)),
|
random: RandomGenerator::Legacy(LegacyRand::from_seed(0)),
|
||||||
sea_level: 63,
|
sea_level: 63,
|
||||||
min_y: -64,
|
min_y: -64,
|
||||||
|
height: 384,
|
||||||
height_sampler: Some(&mut height_sampler),
|
height_sampler: Some(&mut height_sampler),
|
||||||
structure_key: None,
|
structure_key: None,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -13,7 +13,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
palette::has_random_ticking_fluid,
|
palette::has_random_ticking_fluid,
|
||||||
},
|
},
|
||||||
generation::get_world_gen,
|
generation::get_world_gen_with_all_settings,
|
||||||
tick::{OrderedTick, ScheduledTick, TickPriority},
|
tick::{OrderedTick, ScheduledTick, TickPriority},
|
||||||
world::WorldPortalExt,
|
world::WorldPortalExt,
|
||||||
};
|
};
|
||||||
@ -202,42 +202,48 @@ impl Level {
|
|||||||
let mut is_flat = false;
|
let mut is_flat = false;
|
||||||
let mut flat_layers = Vec::new();
|
let mut flat_layers = Vec::new();
|
||||||
let mut flat_biome = "minecraft:plains".to_string();
|
let mut flat_biome = "minecraft:plains".to_string();
|
||||||
|
let mut generator_settings_name: Option<String> = None;
|
||||||
|
let mut biome_source: Option<crate::world_info::BiomeSource> = None;
|
||||||
|
let mut structure_overrides: Option<Vec<String>> = None;
|
||||||
|
|
||||||
if let Some(wgs) = crate::world_info::data_files::read_world_gen_settings(main_folder)
|
if let Some(wgs) = crate::world_info::data_files::read_world_gen_settings(main_folder)
|
||||||
&& let Some(dim_settings) = wgs.dimensions.get(dimension.minecraft_name)
|
&& let Some(dim_settings) = wgs.dimensions.get(dimension.minecraft_name)
|
||||||
&& dim_settings.generator.generator_type == "minecraft:flat"
|
|
||||||
{
|
{
|
||||||
is_flat = true;
|
biome_source.clone_from(&dim_settings.generator.biome_source);
|
||||||
if let Some(crate::world_info::GeneratorSettings::Compound(val)) =
|
|
||||||
|
if dim_settings.generator.generator_type == "minecraft:flat" {
|
||||||
|
is_flat = true;
|
||||||
|
let flat_settings = dim_settings
|
||||||
|
.generator
|
||||||
|
.settings
|
||||||
|
.as_ref()
|
||||||
|
.and_then(crate::world_info::GeneratorSettings::as_flat_settings)
|
||||||
|
.or_else(|| {
|
||||||
|
crate::world_info::FlatLevelGeneratorPreset::from_name("classic_flat")
|
||||||
|
.map(|p| p.settings)
|
||||||
|
});
|
||||||
|
if let Some(flat_settings) = flat_settings {
|
||||||
|
flat_layers = flat_settings.to_flat_layers();
|
||||||
|
structure_overrides = flat_settings.structure_overrides_vec();
|
||||||
|
flat_biome = flat_settings.biome;
|
||||||
|
}
|
||||||
|
} else if let Some(crate::world_info::GeneratorSettings::Reference(s)) =
|
||||||
&dim_settings.generator.settings
|
&dim_settings.generator.settings
|
||||||
{
|
{
|
||||||
if let Some(b) = val.get("biome").and_then(|v| v.as_str()) {
|
generator_settings_name = Some(s.clone());
|
||||||
flat_biome = b.to_string();
|
|
||||||
}
|
|
||||||
if let Some(list) = val.get("layers").and_then(|v| v.as_array()) {
|
|
||||||
for layer in list {
|
|
||||||
let block = layer
|
|
||||||
.get("block")
|
|
||||||
.and_then(|v| v.as_str())
|
|
||||||
.unwrap_or("minecraft:air")
|
|
||||||
.to_string();
|
|
||||||
let height = layer
|
|
||||||
.get("height")
|
|
||||||
.and_then(serde_json::Value::as_i64)
|
|
||||||
.unwrap_or(1) as i32;
|
|
||||||
flat_layers.push(crate::generation::generator::FlatLayer { block, height });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let seed = Seed(seed as u64);
|
let seed = Seed(seed as u64);
|
||||||
let world_gen: Arc<WorldGenerator> = Arc::from(get_world_gen(
|
let world_gen: Arc<WorldGenerator> = Arc::from(get_world_gen_with_all_settings(
|
||||||
seed,
|
seed,
|
||||||
dimension,
|
dimension,
|
||||||
is_flat,
|
is_flat,
|
||||||
flat_layers,
|
flat_layers,
|
||||||
flat_biome,
|
flat_biome,
|
||||||
|
generator_settings_name.as_deref(),
|
||||||
|
biome_source.as_ref(),
|
||||||
|
structure_overrides.as_deref(),
|
||||||
));
|
));
|
||||||
|
|
||||||
let chunk_saver = match &level_config.chunk {
|
let chunk_saver = match &level_config.chunk {
|
||||||
|
|||||||
@ -277,16 +277,18 @@ pub fn read_world_gen_settings(level_folder: &Path) -> Option<WorldGenSettings>
|
|||||||
.get_string("type")
|
.get_string("type")
|
||||||
.unwrap_or("minecraft:multi_noise")
|
.unwrap_or("minecraft:multi_noise")
|
||||||
.to_string();
|
.to_string();
|
||||||
match bs_c.get_string("preset") {
|
if let Some(preset) = bs_c.get_string("preset") {
|
||||||
Some(preset) => {
|
crate::world_info::BiomeSource::WithPreset {
|
||||||
crate::world_info::BiomeSource::WithPreset {
|
preset: preset.to_string(),
|
||||||
preset: preset.to_string(),
|
biome_type,
|
||||||
biome_type,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
None => {
|
} else if let Some(biome) = bs_c.get_string("biome") {
|
||||||
crate::world_info::BiomeSource::Simple { biome_type }
|
crate::world_info::BiomeSource::Fixed {
|
||||||
|
biome: biome.to_string(),
|
||||||
|
biome_type,
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
crate::world_info::BiomeSource::Simple { biome_type }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
dimensions.insert(
|
dimensions.insert(
|
||||||
@ -356,6 +358,10 @@ pub fn write_world_gen_settings(
|
|||||||
bs_comp.put_string("preset", preset.clone());
|
bs_comp.put_string("preset", preset.clone());
|
||||||
bs_comp.put_string("type", biome_type.clone());
|
bs_comp.put_string("type", biome_type.clone());
|
||||||
}
|
}
|
||||||
|
crate::world_info::BiomeSource::Fixed { biome, biome_type } => {
|
||||||
|
bs_comp.put_string("biome", biome.clone());
|
||||||
|
bs_comp.put_string("type", biome_type.clone());
|
||||||
|
}
|
||||||
crate::world_info::BiomeSource::Simple { biome_type } => {
|
crate::world_info::BiomeSource::Simple { biome_type } => {
|
||||||
bs_comp.put_string("type", biome_type.clone());
|
bs_comp.put_string("type", biome_type.clone());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -195,6 +195,18 @@ pub enum GeneratorSettings {
|
|||||||
Compound(serde_json::Value),
|
Compound(serde_json::Value),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GeneratorSettings {
|
||||||
|
#[must_use]
|
||||||
|
pub fn as_flat_settings(&self) -> Option<FlatPresetSettings> {
|
||||||
|
match self {
|
||||||
|
Self::Reference(preset_name) => {
|
||||||
|
FlatLevelGeneratorPreset::from_name(preset_name).map(|p| p.settings)
|
||||||
|
}
|
||||||
|
Self::Compound(val) => serde_json::from_value(val.clone()).ok(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum BiomeSource {
|
pub enum BiomeSource {
|
||||||
@ -203,12 +215,177 @@ pub enum BiomeSource {
|
|||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
biome_type: String,
|
biome_type: String,
|
||||||
},
|
},
|
||||||
|
Fixed {
|
||||||
|
biome: String,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
biome_type: String,
|
||||||
|
},
|
||||||
Simple {
|
Simple {
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
biome_type: String,
|
biome_type: String,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct WorldPreset {
|
||||||
|
pub dimensions: Dimensions,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WorldPreset {
|
||||||
|
pub const NORMAL_RAW: &'static str = include_str!(
|
||||||
|
"../../../../assets/datapacks/26_2/data/minecraft/worldgen/world_preset/normal.json"
|
||||||
|
);
|
||||||
|
pub const AMPLIFIED_RAW: &'static str = include_str!(
|
||||||
|
"../../../../assets/datapacks/26_2/data/minecraft/worldgen/world_preset/amplified.json"
|
||||||
|
);
|
||||||
|
pub const LARGE_BIOMES_RAW: &'static str = include_str!(
|
||||||
|
"../../../../assets/datapacks/26_2/data/minecraft/worldgen/world_preset/large_biomes.json"
|
||||||
|
);
|
||||||
|
pub const FLAT_RAW: &'static str = include_str!(
|
||||||
|
"../../../../assets/datapacks/26_2/data/minecraft/worldgen/world_preset/flat.json"
|
||||||
|
);
|
||||||
|
pub const FLAT_ALL_DIMENSIONS_RAW: &'static str = include_str!(
|
||||||
|
"../../../../assets/datapacks/26_2/data/minecraft/worldgen/world_preset/flat_all_dimensions.json"
|
||||||
|
);
|
||||||
|
pub const SINGLE_BIOME_SURFACE_RAW: &'static str = include_str!(
|
||||||
|
"../../../../assets/datapacks/26_2/data/minecraft/worldgen/world_preset/single_biome_surface.json"
|
||||||
|
);
|
||||||
|
pub const DEBUG_ALL_BLOCK_STATES_RAW: &'static str = include_str!(
|
||||||
|
"../../../../assets/datapacks/26_2/data/minecraft/worldgen/world_preset/debug_all_block_states.json"
|
||||||
|
);
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn from_name(name: &str) -> Option<Self> {
|
||||||
|
let name = name.strip_prefix("minecraft:").unwrap_or(name);
|
||||||
|
let raw = match name {
|
||||||
|
"normal" => Self::NORMAL_RAW,
|
||||||
|
"amplified" => Self::AMPLIFIED_RAW,
|
||||||
|
"large_biomes" => Self::LARGE_BIOMES_RAW,
|
||||||
|
"flat" => Self::FLAT_RAW,
|
||||||
|
"flat_all_dimensions" => Self::FLAT_ALL_DIMENSIONS_RAW,
|
||||||
|
"single_biome_surface" => Self::SINGLE_BIOME_SURFACE_RAW,
|
||||||
|
"debug_all_block_states" => Self::DEBUG_ALL_BLOCK_STATES_RAW,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
serde_json::from_str(raw).ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum StructureOverrides {
|
||||||
|
Single(String),
|
||||||
|
Multiple(Vec<String>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructureOverrides {
|
||||||
|
#[must_use]
|
||||||
|
pub fn to_vec(&self) -> Vec<String> {
|
||||||
|
match self {
|
||||||
|
Self::Single(s) => vec![s.clone()],
|
||||||
|
Self::Multiple(list) => list.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct FlatPresetLayer {
|
||||||
|
pub block: String,
|
||||||
|
#[serde(default = "default_layer_height")]
|
||||||
|
pub height: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn default_layer_height() -> i32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct FlatPresetSettings {
|
||||||
|
pub biome: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub features: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub lakes: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub layers: Vec<FlatPresetLayer>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub structure_overrides: Option<StructureOverrides>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlatPresetSettings {
|
||||||
|
#[must_use]
|
||||||
|
pub fn to_flat_layers(&self) -> Vec<crate::generation::generator::FlatLayer> {
|
||||||
|
self.layers
|
||||||
|
.iter()
|
||||||
|
.map(|l| crate::generation::generator::FlatLayer {
|
||||||
|
block: l.block.clone(),
|
||||||
|
height: l.height,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn structure_overrides_vec(&self) -> Option<Vec<String>> {
|
||||||
|
self.structure_overrides
|
||||||
|
.as_ref()
|
||||||
|
.map(StructureOverrides::to_vec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct FlatLevelGeneratorPreset {
|
||||||
|
pub display: String,
|
||||||
|
pub settings: FlatPresetSettings,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlatLevelGeneratorPreset {
|
||||||
|
pub const BOTTOMLESS_PIT_RAW: &'static str = include_str!(
|
||||||
|
"../../../../assets/datapacks/26_2/data/minecraft/worldgen/flat_level_generator_preset/bottomless_pit.json"
|
||||||
|
);
|
||||||
|
pub const CLASSIC_FLAT_RAW: &'static str = include_str!(
|
||||||
|
"../../../../assets/datapacks/26_2/data/minecraft/worldgen/flat_level_generator_preset/classic_flat.json"
|
||||||
|
);
|
||||||
|
pub const DESERT_RAW: &'static str = include_str!(
|
||||||
|
"../../../../assets/datapacks/26_2/data/minecraft/worldgen/flat_level_generator_preset/desert.json"
|
||||||
|
);
|
||||||
|
pub const OVERWORLD_RAW: &'static str = include_str!(
|
||||||
|
"../../../../assets/datapacks/26_2/data/minecraft/worldgen/flat_level_generator_preset/overworld.json"
|
||||||
|
);
|
||||||
|
pub const REDSTONE_READY_RAW: &'static str = include_str!(
|
||||||
|
"../../../../assets/datapacks/26_2/data/minecraft/worldgen/flat_level_generator_preset/redstone_ready.json"
|
||||||
|
);
|
||||||
|
pub const SNOWY_KINGDOM_RAW: &'static str = include_str!(
|
||||||
|
"../../../../assets/datapacks/26_2/data/minecraft/worldgen/flat_level_generator_preset/snowy_kingdom.json"
|
||||||
|
);
|
||||||
|
pub const THE_VOID_RAW: &'static str = include_str!(
|
||||||
|
"../../../../assets/datapacks/26_2/data/minecraft/worldgen/flat_level_generator_preset/the_void.json"
|
||||||
|
);
|
||||||
|
pub const TUNNELERS_DREAM_RAW: &'static str = include_str!(
|
||||||
|
"../../../../assets/datapacks/26_2/data/minecraft/worldgen/flat_level_generator_preset/tunnelers_dream.json"
|
||||||
|
);
|
||||||
|
pub const WATER_WORLD_RAW: &'static str = include_str!(
|
||||||
|
"../../../../assets/datapacks/26_2/data/minecraft/worldgen/flat_level_generator_preset/water_world.json"
|
||||||
|
);
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn from_name(name: &str) -> Option<Self> {
|
||||||
|
let name = name.strip_prefix("minecraft:").unwrap_or(name);
|
||||||
|
let raw = match name {
|
||||||
|
"bottomless_pit" => Self::BOTTOMLESS_PIT_RAW,
|
||||||
|
"classic_flat" => Self::CLASSIC_FLAT_RAW,
|
||||||
|
"desert" => Self::DESERT_RAW,
|
||||||
|
"overworld" => Self::OVERWORLD_RAW,
|
||||||
|
"redstone_ready" => Self::REDSTONE_READY_RAW,
|
||||||
|
"snowy_kingdom" => Self::SNOWY_KINGDOM_RAW,
|
||||||
|
"the_void" => Self::THE_VOID_RAW,
|
||||||
|
"tunnelers_dream" => Self::TUNNELERS_DREAM_RAW,
|
||||||
|
"water_world" => Self::WATER_WORLD_RAW,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
serde_json::from_str(raw).ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
|
||||||
#[serde(rename_all = "PascalCase")]
|
#[serde(rename_all = "PascalCase")]
|
||||||
pub struct DataPacks {
|
pub struct DataPacks {
|
||||||
@ -223,57 +400,73 @@ pub struct DataPacks {
|
|||||||
impl WorldGenSettings {
|
impl WorldGenSettings {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(seed: Seed) -> Self {
|
pub fn new(seed: Seed) -> Self {
|
||||||
// TODO: Adjust according to enabled worlds
|
Self::from_preset_name("minecraft:normal", seed).unwrap_or_else(|| {
|
||||||
let mut dimensions = Dimensions::new();
|
let mut dimensions = Dimensions::new();
|
||||||
dimensions.insert(
|
dimensions.insert(
|
||||||
"minecraft:overworld".to_string(),
|
"minecraft:overworld".to_string(),
|
||||||
Dimension {
|
Dimension {
|
||||||
generator: Generator {
|
generator: Generator {
|
||||||
settings: Some(GeneratorSettings::Reference(
|
settings: Some(GeneratorSettings::Reference(
|
||||||
"minecraft:overworld".to_string(),
|
"minecraft:overworld".to_string(),
|
||||||
)),
|
)),
|
||||||
biome_source: Some(BiomeSource::WithPreset {
|
biome_source: Some(BiomeSource::WithPreset {
|
||||||
preset: "minecraft:overworld".to_string(),
|
preset: "minecraft:overworld".to_string(),
|
||||||
biome_type: "minecraft:multi_noise".to_string(),
|
biome_type: "minecraft:multi_noise".to_string(),
|
||||||
}),
|
}),
|
||||||
generator_type: "minecraft:noise".to_string(),
|
generator_type: "minecraft:noise".to_string(),
|
||||||
|
},
|
||||||
|
dimension_type: "minecraft:overworld".to_string(),
|
||||||
},
|
},
|
||||||
dimension_type: "minecraft:overworld".to_string(),
|
);
|
||||||
},
|
dimensions.insert(
|
||||||
);
|
"minecraft:the_nether".to_string(),
|
||||||
dimensions.insert(
|
Dimension {
|
||||||
"minecraft:the_nether".to_string(),
|
generator: Generator {
|
||||||
Dimension {
|
settings: Some(GeneratorSettings::Reference(
|
||||||
generator: Generator {
|
"minecraft:nether".to_string(),
|
||||||
settings: Some(GeneratorSettings::Reference("minecraft:nether".to_string())),
|
)),
|
||||||
biome_source: Some(BiomeSource::WithPreset {
|
biome_source: Some(BiomeSource::WithPreset {
|
||||||
preset: "minecraft:nether".to_string(),
|
preset: "minecraft:nether".to_string(),
|
||||||
biome_type: "minecraft:multi_noise".to_string(),
|
biome_type: "minecraft:multi_noise".to_string(),
|
||||||
}),
|
}),
|
||||||
generator_type: "minecraft:noise".to_string(),
|
generator_type: "minecraft:noise".to_string(),
|
||||||
|
},
|
||||||
|
dimension_type: "minecraft:the_nether".to_string(),
|
||||||
},
|
},
|
||||||
dimension_type: "minecraft:the_nether".to_string(),
|
);
|
||||||
},
|
dimensions.insert(
|
||||||
);
|
"minecraft:the_end".to_string(),
|
||||||
dimensions.insert(
|
Dimension {
|
||||||
"minecraft:the_end".to_string(),
|
generator: Generator {
|
||||||
Dimension {
|
settings: Some(GeneratorSettings::Reference("minecraft:end".to_string())),
|
||||||
generator: Generator {
|
biome_source: Some(BiomeSource::Simple {
|
||||||
settings: Some(GeneratorSettings::Reference("minecraft:end".to_string())),
|
biome_type: "minecraft:the_end".to_string(),
|
||||||
biome_source: Some(BiomeSource::Simple {
|
}),
|
||||||
biome_type: "minecraft:the_end".to_string(),
|
generator_type: "minecraft:noise".to_string(),
|
||||||
}),
|
},
|
||||||
generator_type: "minecraft:noise".to_string(),
|
dimension_type: "minecraft:the_end".to_string(),
|
||||||
},
|
},
|
||||||
dimension_type: "minecraft:the_end".to_string(),
|
);
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
|
Self {
|
||||||
|
dimensions,
|
||||||
|
seed: seed.0 as i64,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn from_preset(preset: &WorldPreset, seed: Seed) -> Self {
|
||||||
Self {
|
Self {
|
||||||
dimensions,
|
dimensions: preset.dimensions.clone(),
|
||||||
seed: seed.0 as i64,
|
seed: seed.0 as i64,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn from_preset_name(preset_name: &str, seed: Seed) -> Option<Self> {
|
||||||
|
WorldPreset::from_name(preset_name).map(|preset| Self::from_preset(&preset, seed))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
|
||||||
@ -340,6 +533,15 @@ impl LevelData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn default_with_preset(seed: Seed, preset_name: &str) -> Self {
|
||||||
|
let mut data = Self::default(seed);
|
||||||
|
if let Some(wgs) = WorldGenSettings::from_preset_name(preset_name, seed) {
|
||||||
|
data.world_gen_settings = wgs;
|
||||||
|
}
|
||||||
|
data
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn from_world_generator(
|
pub fn from_world_generator(
|
||||||
seed: Seed,
|
seed: Seed,
|
||||||
@ -386,3 +588,148 @@ impl From<std::io::Error> for WorldInfoError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn world_presets_parse() {
|
||||||
|
let preset_names = [
|
||||||
|
"normal",
|
||||||
|
"amplified",
|
||||||
|
"large_biomes",
|
||||||
|
"flat",
|
||||||
|
"flat_all_dimensions",
|
||||||
|
"single_biome_surface",
|
||||||
|
"debug_all_block_states",
|
||||||
|
];
|
||||||
|
|
||||||
|
for name in preset_names {
|
||||||
|
let preset = WorldPreset::from_name(name)
|
||||||
|
.unwrap_or_else(|| panic!("failed to load preset {name}"));
|
||||||
|
assert!(
|
||||||
|
!preset.dimensions.is_empty(),
|
||||||
|
"preset {name} has no dimensions"
|
||||||
|
);
|
||||||
|
|
||||||
|
let with_prefix = format!("minecraft:{name}");
|
||||||
|
assert!(
|
||||||
|
WorldPreset::from_name(&with_prefix).is_some(),
|
||||||
|
"failed to load preset with prefix {with_prefix}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn world_gen_settings_from_preset() {
|
||||||
|
let seed = Seed(12345);
|
||||||
|
let normal = WorldGenSettings::from_preset_name("normal", seed).unwrap();
|
||||||
|
assert_eq!(normal.seed, 12345);
|
||||||
|
let overworld = normal.dimensions.get("minecraft:overworld").unwrap();
|
||||||
|
assert_eq!(overworld.generator.generator_type, "minecraft:noise");
|
||||||
|
assert_eq!(
|
||||||
|
overworld.generator.settings,
|
||||||
|
Some(GeneratorSettings::Reference(
|
||||||
|
"minecraft:overworld".to_string()
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
let amplified = WorldGenSettings::from_preset_name("amplified", seed).unwrap();
|
||||||
|
let amp_overworld = amplified.dimensions.get("minecraft:overworld").unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
amp_overworld.generator.settings,
|
||||||
|
Some(GeneratorSettings::Reference(
|
||||||
|
"minecraft:amplified".to_string()
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
let large_biomes = WorldGenSettings::from_preset_name("large_biomes", seed).unwrap();
|
||||||
|
let lb_overworld = large_biomes.dimensions.get("minecraft:overworld").unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
lb_overworld.generator.settings,
|
||||||
|
Some(GeneratorSettings::Reference(
|
||||||
|
"minecraft:large_biomes".to_string()
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
let flat = WorldGenSettings::from_preset_name("flat", seed).unwrap();
|
||||||
|
let flat_overworld = flat.dimensions.get("minecraft:overworld").unwrap();
|
||||||
|
assert_eq!(flat_overworld.generator.generator_type, "minecraft:flat");
|
||||||
|
assert!(matches!(
|
||||||
|
flat_overworld.generator.settings,
|
||||||
|
Some(GeneratorSettings::Compound(_))
|
||||||
|
));
|
||||||
|
let flat_settings = flat_overworld
|
||||||
|
.generator
|
||||||
|
.settings
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.as_flat_settings()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(flat_settings.biome, "minecraft:plains");
|
||||||
|
assert_eq!(flat_settings.layers.len(), 3);
|
||||||
|
assert_eq!(flat_settings.to_flat_layers().len(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn flat_level_generator_presets_parse() {
|
||||||
|
let preset_names = [
|
||||||
|
"bottomless_pit",
|
||||||
|
"classic_flat",
|
||||||
|
"desert",
|
||||||
|
"overworld",
|
||||||
|
"redstone_ready",
|
||||||
|
"snowy_kingdom",
|
||||||
|
"the_void",
|
||||||
|
"tunnelers_dream",
|
||||||
|
"water_world",
|
||||||
|
];
|
||||||
|
|
||||||
|
for name in preset_names {
|
||||||
|
let preset = FlatLevelGeneratorPreset::from_name(name)
|
||||||
|
.unwrap_or_else(|| panic!("failed to load flat preset {name}"));
|
||||||
|
assert!(!preset.settings.biome.is_empty());
|
||||||
|
assert!(!preset.settings.layers.is_empty());
|
||||||
|
assert_eq!(
|
||||||
|
preset.settings.layers.len(),
|
||||||
|
preset.settings.to_flat_layers().len()
|
||||||
|
);
|
||||||
|
|
||||||
|
let with_prefix = format!("minecraft:{name}");
|
||||||
|
assert!(
|
||||||
|
FlatLevelGeneratorPreset::from_name(&with_prefix).is_some(),
|
||||||
|
"failed to load flat preset with prefix {with_prefix}"
|
||||||
|
);
|
||||||
|
|
||||||
|
let from_ref = GeneratorSettings::Reference(with_prefix)
|
||||||
|
.as_flat_settings()
|
||||||
|
.expect("failed to parse reference as flat settings");
|
||||||
|
assert_eq!(from_ref.biome, preset.settings.biome);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test classic flat structure overrides (single string "minecraft:villages")
|
||||||
|
let classic = FlatLevelGeneratorPreset::from_name("classic_flat").unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
classic.settings.structure_overrides_vec(),
|
||||||
|
Some(vec!["minecraft:villages".to_string()])
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test tunnelers dream structure overrides (multiple)
|
||||||
|
let tunnelers = FlatLevelGeneratorPreset::from_name("tunnelers_dream").unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
tunnelers.settings.structure_overrides_vec(),
|
||||||
|
Some(vec![
|
||||||
|
"minecraft:mineshafts".to_string(),
|
||||||
|
"minecraft:strongholds".to_string(),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test the void structure overrides (empty list)
|
||||||
|
let void = FlatLevelGeneratorPreset::from_name("the_void").unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
void.settings.structure_overrides_vec(),
|
||||||
|
Some(Vec::<String>::new())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -47,7 +47,7 @@ impl MushroomPlantBlock {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let is_dark_enough = world.is_none_or(|world| world.get_max_local_raw_brightness(pos) < 13);
|
let is_dark_enough = world.is_none_or(|world| world.get_raw_brightness(pos, 0) < 13);
|
||||||
|
|
||||||
is_dark_enough && Self::may_place_on(block_accessor.get_block_state(&below_pos))
|
is_dark_enough && Self::may_place_on(block_accessor.get_block_state(&below_pos))
|
||||||
}
|
}
|
||||||
@ -71,33 +71,40 @@ impl MushroomPlantBlock {
|
|||||||
|
|
||||||
let tree_height = mushroom_tree_height(&mut rand::rng());
|
let tree_height = mushroom_tree_height(&mut rand::rng());
|
||||||
|
|
||||||
if !world.is_in_height_limit(pos.0.y + tree_height + 1) {
|
let min_y = world.get_bottom_y();
|
||||||
|
let max_y = world.get_top_y();
|
||||||
|
if pos.0.y < min_y + 1 || pos.0.y + tree_height + 1 > max_y {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let foliage_radius = if block == &Block::BROWN_MUSHROOM {
|
let below_block = world.get_block(&pos.down());
|
||||||
3
|
let can_place_on = if block == &Block::BROWN_MUSHROOM {
|
||||||
|
below_block.has_tag(&tag::Block::MINECRAFT_HUGE_BROWN_MUSHROOM_CAN_PLACE_ON)
|
||||||
} else {
|
} else {
|
||||||
2
|
below_block.has_tag(&tag::Block::MINECRAFT_HUGE_RED_MUSHROOM_CAN_PLACE_ON)
|
||||||
};
|
};
|
||||||
for dy in 0..=tree_height + 1 {
|
if !can_place_on {
|
||||||
let radius = if dy <= 3 { 0 } else { foliage_radius };
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for dy in 0..=tree_height {
|
||||||
|
let radius = if block == &Block::BROWN_MUSHROOM {
|
||||||
|
if dy <= 3 { 0 } else { 3 }
|
||||||
|
} else if (dy < tree_height && dy >= tree_height - 3) || dy == tree_height {
|
||||||
|
2
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
for dx in -radius..=radius {
|
for dx in -radius..=radius {
|
||||||
for dz in -radius..=radius {
|
for dz in -radius..=radius {
|
||||||
let check_pos = pos.add(dx, dy, dz);
|
let check_pos = pos.add(dx, dy, dz);
|
||||||
if check_pos == *pos {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if !world.is_loaded(&check_pos) {
|
if !world.is_loaded(&check_pos) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let check_state = world.get_block_state(&check_pos);
|
let check_state = world.get_block_state(&check_pos);
|
||||||
let check_block = world.get_block(&check_pos);
|
let check_block = world.get_block(&check_pos);
|
||||||
let can_replace = check_state.is_air()
|
if !check_state.is_air() && !check_block.has_tag(&tag::Block::MINECRAFT_LEAVES)
|
||||||
|| check_block.has_tag(&tag::Block::MINECRAFT_LEAVES)
|
{
|
||||||
|| check_block.has_tag(&tag::Block::MINECRAFT_REPLACEABLE_BY_MUSHROOMS)
|
|
||||||
|| check_state.replaceable();
|
|
||||||
if !can_replace {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,6 +123,14 @@ impl MushroomPlantBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn place_mushroom_block(world: &Arc<World>, pos: &BlockPos, state_id: BlockStateId) {
|
||||||
|
let block = world.get_block(pos);
|
||||||
|
let state = world.get_block_state(pos);
|
||||||
|
if state.is_air() || block.has_tag(&tag::Block::MINECRAFT_REPLACEABLE_BY_MUSHROOMS) {
|
||||||
|
world.set_block_state(pos, state_id, BlockFlags::NOTIFY_ALL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn place_huge_brown_mushroom(world: &Arc<World>, pos: &BlockPos, tree_height: i32) {
|
fn place_huge_brown_mushroom(world: &Arc<World>, pos: &BlockPos, tree_height: i32) {
|
||||||
let radius = 3;
|
let radius = 3;
|
||||||
let cap_y = pos.0.y + tree_height;
|
let cap_y = pos.0.y + tree_height;
|
||||||
@ -138,7 +153,7 @@ fn place_huge_brown_mushroom(world: &Arc<World>, pos: &BlockPos, tree_height: i3
|
|||||||
};
|
};
|
||||||
let state_id = props.to_state_id(&Block::BROWN_MUSHROOM_BLOCK);
|
let state_id = props.to_state_id(&Block::BROWN_MUSHROOM_BLOCK);
|
||||||
let cap_pos = BlockPos::new(pos.0.x + j, cap_y, pos.0.z + k);
|
let cap_pos = BlockPos::new(pos.0.x + j, cap_y, pos.0.z + k);
|
||||||
world.set_block_state(&cap_pos, state_id, BlockFlags::NOTIFY_ALL);
|
place_mushroom_block(world, &cap_pos, state_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +168,7 @@ fn place_huge_brown_mushroom(world: &Arc<World>, pos: &BlockPos, tree_height: i3
|
|||||||
let stem_state = stem_props.to_state_id(&Block::MUSHROOM_STEM);
|
let stem_state = stem_props.to_state_id(&Block::MUSHROOM_STEM);
|
||||||
for i in 0..tree_height {
|
for i in 0..tree_height {
|
||||||
let stem_pos = BlockPos::new(pos.0.x, pos.0.y + i, pos.0.z);
|
let stem_pos = BlockPos::new(pos.0.x, pos.0.y + i, pos.0.z);
|
||||||
world.set_block_state(&stem_pos, stem_state, BlockFlags::NOTIFY_ALL);
|
place_mushroom_block(world, &stem_pos, stem_state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,7 +197,7 @@ fn place_huge_red_mushroom(world: &Arc<World>, pos: &BlockPos, tree_height: i32)
|
|||||||
};
|
};
|
||||||
let state_id = props.to_state_id(&Block::RED_MUSHROOM_BLOCK);
|
let state_id = props.to_state_id(&Block::RED_MUSHROOM_BLOCK);
|
||||||
let cap_pos = BlockPos::new(pos.0.x + l, pos.0.y + i, pos.0.z + m);
|
let cap_pos = BlockPos::new(pos.0.x + l, pos.0.y + i, pos.0.z + m);
|
||||||
world.set_block_state(&cap_pos, state_id, BlockFlags::NOTIFY_ALL);
|
place_mushroom_block(world, &cap_pos, state_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -198,7 +213,7 @@ fn place_huge_red_mushroom(world: &Arc<World>, pos: &BlockPos, tree_height: i32)
|
|||||||
let stem_state = stem_props.to_state_id(&Block::MUSHROOM_STEM);
|
let stem_state = stem_props.to_state_id(&Block::MUSHROOM_STEM);
|
||||||
for i in 0..tree_height {
|
for i in 0..tree_height {
|
||||||
let stem_pos = BlockPos::new(pos.0.x, pos.0.y + i, pos.0.z);
|
let stem_pos = BlockPos::new(pos.0.x, pos.0.y + i, pos.0.z);
|
||||||
world.set_block_state(&stem_pos, stem_state, BlockFlags::NOTIFY_ALL);
|
place_mushroom_block(world, &stem_pos, stem_state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -99,7 +99,8 @@ impl JigsawBlockEntity {
|
|||||||
chunk_z: position.chunk_position().y,
|
chunk_z: position.chunk_position().y,
|
||||||
random: RandomGenerator::Xoroshiro(Xoroshiro::from_seed(rand::rng().next_u64())),
|
random: RandomGenerator::Xoroshiro(Xoroshiro::from_seed(rand::rng().next_u64())),
|
||||||
sea_level: 63,
|
sea_level: 63,
|
||||||
min_y: -64,
|
min_y: world.dimension.min_y,
|
||||||
|
height: world.dimension.height as u16,
|
||||||
height_sampler: None,
|
height_sampler: None,
|
||||||
structure_key: None,
|
structure_key: None,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -200,7 +200,11 @@ impl CommandExecutor for PlaceJigsawExecutor {
|
|||||||
chunk_z: 0,
|
chunk_z: 0,
|
||||||
random,
|
random,
|
||||||
sea_level: settings.sea_level,
|
sea_level: settings.sea_level,
|
||||||
min_y: world_gen.dimension().min_y,
|
min_y: (settings.shape.min_y as i32).max(world_gen.dimension().min_y),
|
||||||
|
height: settings
|
||||||
|
.shape
|
||||||
|
.height
|
||||||
|
.min(world_gen.dimension().height as u16),
|
||||||
height_sampler: None,
|
height_sampler: None,
|
||||||
structure_key: None,
|
structure_key: None,
|
||||||
};
|
};
|
||||||
@ -307,7 +311,11 @@ impl CommandExecutor for PlaceStructureExecutor {
|
|||||||
chunk_z: block_pos.0.z >> 4,
|
chunk_z: block_pos.0.z >> 4,
|
||||||
random,
|
random,
|
||||||
sea_level: settings.sea_level,
|
sea_level: settings.sea_level,
|
||||||
min_y: world_gen.dimension().min_y,
|
min_y: (settings.shape.min_y as i32).max(world_gen.dimension().min_y),
|
||||||
|
height: settings
|
||||||
|
.shape
|
||||||
|
.height
|
||||||
|
.min(world_gen.dimension().height as u16),
|
||||||
height_sampler: None,
|
height_sampler: None,
|
||||||
structure_key: Some(key),
|
structure_key: Some(key),
|
||||||
},
|
},
|
||||||
@ -355,7 +363,11 @@ impl CommandExecutor for PlaceStructureExecutor {
|
|||||||
chunk_z: block_pos.0.z >> 4,
|
chunk_z: block_pos.0.z >> 4,
|
||||||
random,
|
random,
|
||||||
sea_level: settings.sea_level,
|
sea_level: settings.sea_level,
|
||||||
min_y: world_gen.dimension().min_y,
|
min_y: (settings.shape.min_y as i32).max(world_gen.dimension().min_y),
|
||||||
|
height: settings
|
||||||
|
.shape
|
||||||
|
.height
|
||||||
|
.min(world_gen.dimension().height as u16),
|
||||||
height_sampler: None,
|
height_sampler: None,
|
||||||
structure_key: Some(key),
|
structure_key: Some(key),
|
||||||
},
|
},
|
||||||
|
|||||||
@ -5661,12 +5661,16 @@ impl World {
|
|||||||
self.environment_attributes().get_value_activity(baby, pos)
|
self.environment_attributes().get_value_activity(baby, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_max_local_raw_brightness(&self, pos: &BlockPos) -> u8 {
|
pub fn get_raw_brightness(&self, pos: &BlockPos, sky_darken: u8) -> u8 {
|
||||||
let sky_light = (self.get_sky_light_level(pos) as i32 - self.get_sky_darken()).max(0) as u8;
|
let sky_light = self.get_sky_light_level(pos).saturating_sub(sky_darken);
|
||||||
let block_light = self.get_block_light_level(pos).unwrap_or(0);
|
let block_light = self.get_block_light_level(pos).unwrap_or(0);
|
||||||
sky_light.max(block_light)
|
sky_light.max(block_light)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_max_local_raw_brightness(&self, pos: &BlockPos) -> u8 {
|
||||||
|
self.get_raw_brightness(pos, self.get_sky_darken() as u8)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_block_light_level(&self, position: &BlockPos) -> Option<u8> {
|
pub fn get_block_light_level(&self, position: &BlockPos) -> Option<u8> {
|
||||||
self.level
|
self.level
|
||||||
.light_engine
|
.light_engine
|
||||||
@ -7402,7 +7406,7 @@ impl WorldPortalExt for WorldPortal {
|
|||||||
) -> bool {
|
) -> bool {
|
||||||
self.0.block_registry.can_place_at(
|
self.0.block_registry.can_place_at(
|
||||||
None,
|
None,
|
||||||
None,
|
Some(&self.0),
|
||||||
block_accessor,
|
block_accessor,
|
||||||
None,
|
None,
|
||||||
block,
|
block,
|
||||||
|
|||||||
@ -12,6 +12,24 @@ struct GeyserBiomeMapping {
|
|||||||
bedrock_id: u8,
|
bedrock_id: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn deserialize_carvers<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum StringOrVec {
|
||||||
|
String(String),
|
||||||
|
Vec(Vec<String>),
|
||||||
|
}
|
||||||
|
|
||||||
|
match Option::<StringOrVec>::deserialize(deserializer)? {
|
||||||
|
Some(StringOrVec::String(s)) => Ok(vec![s]),
|
||||||
|
Some(StringOrVec::Vec(v)) => Ok(v),
|
||||||
|
None => Ok(Vec::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Raw deserialization shape for a single biome entry from `biome.json`.
|
/// Raw deserialization shape for a single biome entry from `biome.json`.
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Biome {
|
pub struct Biome {
|
||||||
@ -23,7 +41,8 @@ pub struct Biome {
|
|||||||
downfall: f32,
|
downfall: f32,
|
||||||
/// Optional modifier that changes how temperature is applied.
|
/// Optional modifier that changes how temperature is applied.
|
||||||
temperature_modifier: Option<TemperatureModifier>,
|
temperature_modifier: Option<TemperatureModifier>,
|
||||||
//carvers: Vec<String>,
|
#[serde(default, deserialize_with = "deserialize_carvers")]
|
||||||
|
pub carvers: Vec<String>,
|
||||||
/// Nested lists of feature resource-location strings applied during world generation.
|
/// Nested lists of feature resource-location strings applied during world generation.
|
||||||
features: Vec<Vec<String>>,
|
features: Vec<Vec<String>>,
|
||||||
/// Probability per chunk tick that a creature spawns, if not overridden per-biome.
|
/// Probability per chunk tick that a creature spawns, if not overridden per-biome.
|
||||||
@ -261,7 +280,15 @@ pub fn build() -> TokenStream {
|
|||||||
let has_precipitation = biome.has_precipitation;
|
let has_precipitation = biome.has_precipitation;
|
||||||
let temperature = biome.temperature;
|
let temperature = biome.temperature;
|
||||||
let downfall = biome.downfall;
|
let downfall = biome.downfall;
|
||||||
// let carvers = &biome.carvers;
|
let carvers: Vec<TokenStream> = biome
|
||||||
|
.carvers
|
||||||
|
.iter()
|
||||||
|
.map(|c| {
|
||||||
|
let name = c.strip_prefix("minecraft:").unwrap_or(c);
|
||||||
|
let variant_name = format_ident!("{}", name.to_uppercase());
|
||||||
|
quote! { &crate::carver::#variant_name }
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
let features: Vec<TokenStream> = biome
|
let features: Vec<TokenStream> = biome
|
||||||
.features
|
.features
|
||||||
.iter()
|
.iter()
|
||||||
@ -371,6 +398,7 @@ pub fn build() -> TokenStream {
|
|||||||
#downfall
|
#downfall
|
||||||
),
|
),
|
||||||
features: &[#(#features),*],
|
features: &[#(#features),*],
|
||||||
|
carvers: &[#(#carvers),*],
|
||||||
creature_spawn_probability: #creature_spawn_probability,
|
creature_spawn_probability: #creature_spawn_probability,
|
||||||
spawners: #spawners,
|
spawners: #spawners,
|
||||||
spawn_costs: phf::phf_map! {
|
spawn_costs: phf::phf_map! {
|
||||||
@ -401,7 +429,7 @@ pub fn build() -> TokenStream {
|
|||||||
pub registry_id: &'static str,
|
pub registry_id: &'static str,
|
||||||
pub be_network_id: u8,
|
pub be_network_id: u8,
|
||||||
pub weather: Weather,
|
pub weather: Weather,
|
||||||
// carvers: &'static [&str],
|
pub carvers: &'static [&'static crate::carver::CarverConfig],
|
||||||
pub features: &'static [&'static [crate::placed_feature::PlacedFeature]],
|
pub features: &'static [&'static [crate::placed_feature::PlacedFeature]],
|
||||||
pub creature_spawn_probability: f32,
|
pub creature_spawn_probability: f32,
|
||||||
pub spawners: SpawnGroups,
|
pub spawners: SpawnGroups,
|
||||||
|
|||||||
@ -117,29 +117,34 @@ pub fn build() -> TokenStream {
|
|||||||
};
|
};
|
||||||
use pumpkin_util::y_offset::{AboveBottom, Absolute, BelowTop, YOffset};
|
use pumpkin_util::y_offset::{AboveBottom, Absolute, BelowTop, YOffset};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub enum HeightProvider {
|
pub enum HeightProvider {
|
||||||
Uniform(UniformHeightProvider),
|
Uniform(UniformHeightProvider),
|
||||||
Trapezoid(TrapezoidHeightProvider),
|
Trapezoid(TrapezoidHeightProvider),
|
||||||
VeryBiasedToBottom(VeryBiasedToBottomHeightProvider),
|
VeryBiasedToBottom(VeryBiasedToBottomHeightProvider),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct UniformHeightProvider {
|
pub struct UniformHeightProvider {
|
||||||
pub min_inclusive: YOffset,
|
pub min_inclusive: YOffset,
|
||||||
pub max_inclusive: YOffset,
|
pub max_inclusive: YOffset,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct TrapezoidHeightProvider {
|
pub struct TrapezoidHeightProvider {
|
||||||
pub min_inclusive: YOffset,
|
pub min_inclusive: YOffset,
|
||||||
pub max_inclusive: YOffset,
|
pub max_inclusive: YOffset,
|
||||||
pub plateau: Option<i32>,
|
pub plateau: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct VeryBiasedToBottomHeightProvider {
|
pub struct VeryBiasedToBottomHeightProvider {
|
||||||
pub min_inclusive: YOffset,
|
pub min_inclusive: YOffset,
|
||||||
pub max_inclusive: YOffset,
|
pub max_inclusive: YOffset,
|
||||||
pub inner: Option<std::num::NonZero<u32>>,
|
pub inner: Option<std::num::NonZero<u32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct CaveCarverConfig {
|
pub struct CaveCarverConfig {
|
||||||
pub count: IntProvider,
|
pub count: IntProvider,
|
||||||
pub horizontal_radius_multiplier: FloatProvider,
|
pub horizontal_radius_multiplier: FloatProvider,
|
||||||
@ -167,6 +172,7 @@ pub fn build() -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct CanyonShapeConfig {
|
pub struct CanyonShapeConfig {
|
||||||
pub distance_factor: FloatProvider,
|
pub distance_factor: FloatProvider,
|
||||||
pub thickness: FloatProvider,
|
pub thickness: FloatProvider,
|
||||||
@ -177,16 +183,19 @@ pub fn build() -> TokenStream {
|
|||||||
pub y_scale: f32,
|
pub y_scale: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct CanyonCarverConfig {
|
pub struct CanyonCarverConfig {
|
||||||
pub vertical_rotation: FloatProvider,
|
pub vertical_rotation: FloatProvider,
|
||||||
pub shape: CanyonShapeConfig,
|
pub shape: CanyonShapeConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub enum CarverAdditionalConfig {
|
pub enum CarverAdditionalConfig {
|
||||||
Cave(CaveCarverConfig),
|
Cave(CaveCarverConfig),
|
||||||
Canyon(CanyonCarverConfig),
|
Canyon(CanyonCarverConfig),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct CarverConfig {
|
pub struct CarverConfig {
|
||||||
pub probability: f32,
|
pub probability: f32,
|
||||||
pub y: HeightProvider,
|
pub y: HeightProvider,
|
||||||
|
|||||||
@ -598,6 +598,21 @@ pub fn build() -> TokenStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MaterialRule {
|
impl MaterialRule {
|
||||||
|
#[must_use]
|
||||||
|
pub fn from_name(name: &str) -> Option<&'static Self> {
|
||||||
|
let name = name.strip_prefix("minecraft:").unwrap_or(name);
|
||||||
|
match name {
|
||||||
|
"overworld" | "amplified" | "large_biomes" => Some(&OVERWORLD),
|
||||||
|
"nether" => Some(&NETHER),
|
||||||
|
"end" => Some(&END),
|
||||||
|
"bedrock_floor" => Some(&BEDROCK_FLOOR),
|
||||||
|
"bedrock_roof" => Some(&BEDROCK_ROOF),
|
||||||
|
"overworld_caves" => Some(&OVERWORLD_CAVES),
|
||||||
|
"overworld_floating_islands" => Some(&OVERWORLD_FLOATING_ISLANDS),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn from_dimension(dimension: &Dimension) -> &'static Self {
|
pub fn from_dimension(dimension: &Dimension) -> &'static Self {
|
||||||
if dimension == &Dimension::OVERWORLD {
|
if dimension == &Dimension::OVERWORLD {
|
||||||
|
|||||||
@ -2975,12 +2975,16 @@ fn load_vanilla_noise_routers() -> NoiseRouterReprs {
|
|||||||
pub fn build() -> TokenStream {
|
pub fn build() -> TokenStream {
|
||||||
let mut reprs: NoiseRouterReprs = load_vanilla_noise_routers();
|
let mut reprs: NoiseRouterReprs = load_vanilla_noise_routers();
|
||||||
|
|
||||||
let _ = reprs.overworld_amplified;
|
|
||||||
let _ = reprs.overworld_large_biomes;
|
|
||||||
let _ = reprs.end_islands;
|
let _ = reprs.end_islands;
|
||||||
|
|
||||||
let (overworld_router, overworld_compiled) =
|
let (overworld_router, overworld_compiled) =
|
||||||
reprs.overworld.into_token_stream_compiled("overworld");
|
reprs.overworld.into_token_stream_compiled("overworld");
|
||||||
|
let (amplified_router, amplified_compiled) = reprs
|
||||||
|
.overworld_amplified
|
||||||
|
.into_token_stream_compiled("amplified");
|
||||||
|
let (large_biomes_router, large_biomes_compiled) = reprs
|
||||||
|
.overworld_large_biomes
|
||||||
|
.into_token_stream_compiled("large_biomes");
|
||||||
let (nether_router, nether_compiled) = reprs.nether.into_token_stream_compiled("nether");
|
let (nether_router, nether_compiled) = reprs.nether.into_token_stream_compiled("nether");
|
||||||
let (end_router, end_compiled) = reprs.end.into_token_stream_compiled("end");
|
let (end_router, end_compiled) = reprs.end.into_token_stream_compiled("end");
|
||||||
|
|
||||||
@ -3004,6 +3008,8 @@ pub fn build() -> TokenStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#overworld_compiled
|
#overworld_compiled
|
||||||
|
#amplified_compiled
|
||||||
|
#large_biomes_compiled
|
||||||
#nether_compiled
|
#nether_compiled
|
||||||
#end_compiled
|
#end_compiled
|
||||||
|
|
||||||
@ -3366,7 +3372,35 @@ pub fn build() -> TokenStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub const OVERWORLD_BASE_NOISE_ROUTER: BaseNoiseRouters = #overworld_router;
|
pub const OVERWORLD_BASE_NOISE_ROUTER: BaseNoiseRouters = #overworld_router;
|
||||||
|
pub const AMPLIFIED_BASE_NOISE_ROUTER: BaseNoiseRouters = #amplified_router;
|
||||||
|
pub const LARGE_BIOMES_BASE_NOISE_ROUTER: BaseNoiseRouters = #large_biomes_router;
|
||||||
pub const NETHER_BASE_NOISE_ROUTER: BaseNoiseRouters = #nether_router;
|
pub const NETHER_BASE_NOISE_ROUTER: BaseNoiseRouters = #nether_router;
|
||||||
pub const END_BASE_NOISE_ROUTER: BaseNoiseRouters = #end_router;
|
pub const END_BASE_NOISE_ROUTER: BaseNoiseRouters = #end_router;
|
||||||
|
|
||||||
|
impl BaseNoiseRouters {
|
||||||
|
#[must_use]
|
||||||
|
pub fn from_name(name: &str) -> Option<&'static Self> {
|
||||||
|
let name = name.strip_prefix("minecraft:").unwrap_or(name);
|
||||||
|
match name {
|
||||||
|
"overworld" => Some(&OVERWORLD_BASE_NOISE_ROUTER),
|
||||||
|
"amplified" => Some(&LIFIED_BASE_NOISE_ROUTER),
|
||||||
|
"large_biomes" => Some(&LARGE_BIOMES_BASE_NOISE_ROUTER),
|
||||||
|
"nether" => Some(&NETHER_BASE_NOISE_ROUTER),
|
||||||
|
"end" => Some(&END_BASE_NOISE_ROUTER),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn from_dimension(dimension: &crate::dimension::Dimension) -> &'static Self {
|
||||||
|
if dimension == &crate::dimension::Dimension::OVERWORLD {
|
||||||
|
&OVERWORLD_BASE_NOISE_ROUTER
|
||||||
|
} else if dimension == &crate::dimension::Dimension::THE_NETHER {
|
||||||
|
&NETHER_BASE_NOISE_ROUTER
|
||||||
|
} else {
|
||||||
|
&END_BASE_NOISE_ROUTER
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -327,6 +327,7 @@ pub fn build() -> TokenStream {
|
|||||||
use crate::BlockState;
|
use crate::BlockState;
|
||||||
use crate::biome::ParameterPoint;
|
use crate::biome::ParameterPoint;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub struct NoiseSettings {
|
pub struct NoiseSettings {
|
||||||
pub aquifers_enabled: bool,
|
pub aquifers_enabled: bool,
|
||||||
pub ore_veins_enabled: bool,
|
pub ore_veins_enabled: bool,
|
||||||
@ -340,6 +341,7 @@ pub fn build() -> TokenStream {
|
|||||||
|
|
||||||
pub type GenerationSettings = NoiseSettings;
|
pub type GenerationSettings = NoiseSettings;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub struct GenerationShapeConfig {
|
pub struct GenerationShapeConfig {
|
||||||
pub min_y: i8,
|
pub min_y: i8,
|
||||||
pub height: u16,
|
pub height: u16,
|
||||||
@ -392,6 +394,21 @@ pub fn build() -> TokenStream {
|
|||||||
impl NoiseSettings {
|
impl NoiseSettings {
|
||||||
#const_defs
|
#const_defs
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn from_name(name: &str) -> Option<&'static Self> {
|
||||||
|
let name = name.strip_prefix("minecraft:").unwrap_or(name);
|
||||||
|
match name {
|
||||||
|
"overworld" => Some(&Self::OVERWORLD),
|
||||||
|
"amplified" => Some(&Self::AMPLIFIED),
|
||||||
|
"large_biomes" => Some(&Self::LARGE_BIOMES),
|
||||||
|
"nether" => Some(&Self::NETHER),
|
||||||
|
"end" => Some(&Self::END),
|
||||||
|
"caves" => Some(&Self::CAVES),
|
||||||
|
"floating_islands" => Some(&Self::FLOATING_ISLANDS),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn from_dimension(dimension: &Dimension) -> &'static Self {
|
pub fn from_dimension(dimension: &Dimension) -> &'static Self {
|
||||||
if dimension == &Dimension::OVERWORLD {
|
if dimension == &Dimension::OVERWORLD {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user