diff --git a/Cargo.lock b/Cargo.lock index 36929ef5d..896e1a552 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -312,13 +312,13 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chacha20" -version = "0.10.0-rc.9" +version = "0.10.0-rc.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c81d916c6ae06736ec667b51f95ee5ff660a75f4ea6ce1bd932c942365c0ea43" +checksum = "c536927023d1c432e6e23a25ef45f6756094eac2ab460db5fb17a772acdfd312" dependencies = [ "cfg-if", "cpufeatures", - "rand_core 0.10.0-rc-6", + "rand_core 0.10.0", ] [[package]] @@ -630,9 +630,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crossfire" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9c756fd351d7bc1f95698a33a90ce39c8f7458335cc7e95132d3d36124902a" +checksum = "86db11309bffee9435040d648c13c3efbdbcdcd3ead9872e65ce9dff4c3f79ce" dependencies = [ "crossbeam-utils", "futures-core", @@ -666,7 +666,7 @@ checksum = "053c3561863ce55e3226ecc48b08679f4b66cb1b92b9afb42c2c402dfe8b9b51" dependencies = [ "ctutils", "num-traits", - "rand_core 0.10.0-rc-6", + "rand_core 0.10.0", "serdect", "zeroize", ] @@ -698,7 +698,7 @@ checksum = "334a79c97c0b7fa536716dc132fd417d0afbf471440a41fc25a5d9f66d8771cd" dependencies = [ "crypto-bigint 0.7.0-rc.22", "libm", - "rand_core 0.10.0-rc-6", + "rand_core 0.10.0", ] [[package]] @@ -938,9 +938,9 @@ checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" [[package]] name = "flate2" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", "miniz_oxide", @@ -1099,7 +1099,7 @@ dependencies = [ "cfg-if", "libc", "r-efi", - "rand_core 0.10.0-rc-6", + "rand_core 0.10.0", "wasip2", "wasip3", ] @@ -2355,11 +2355,11 @@ dependencies = [ [[package]] name = "rand" version = "0.10.0-rc.8" -source = "git+https://github.com/rust-random/rand#a4c9a8e565fa310de423e3ba89593b647b721e8c" +source = "git+https://github.com/rust-random/rand#9c98f59e8b042e5c7c714e933e49b384a4ce75a6" dependencies = [ "chacha20", "getrandom 0.4.0-rc.1", - "rand_core 0.10.0-rc-6", + "rand_core 0.10.0", ] [[package]] @@ -2373,9 +2373,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.10.0-rc-6" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70765ff7112b0fb2d272d24d9a2f907fc206211304328fe58b2db15a5649ef28" +checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" [[package]] name = "rayon" @@ -2471,7 +2471,7 @@ dependencies = [ "digest 0.11.0-rc.9", "pkcs1", "pkcs8 0.11.0-rc.10", - "rand_core 0.10.0-rc-6", + "rand_core 0.10.0", "sha1", "signature 3.0.0-rc.9", "spki 0.8.0-rc.4", @@ -2752,7 +2752,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ad0ce3b3f8efd7406f22e2ca5d02be21cdf3b3d1d53ab141f784de8965c7c7e" dependencies = [ "digest 0.11.0-rc.9", - "rand_core 0.10.0-rc-6", + "rand_core 0.10.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f75a47a47..c2958c865 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -118,12 +118,12 @@ console-subscriber = { version = "0.5.0", default-features = false } crc-fast = "1.10.0" criterion = { version = "0.8", default-features = false } crossbeam-utils = "0.8.21" -crossfire = { version= "3.0.3", features = ["compat"]} +crossfire = { version= "3.0.4", features = ["compat"]} dashmap = "6.1" ecdsa = "0.16.9" enum_dispatch = "0.3.13" env_logger = "0.11.8" -flate2 = "1.1.8" +flate2 = "1.1.9" heck = "0.5" hmac = "=0.13.0-rc.4" indexmap = "2.13" diff --git a/pumpkin-data/build/build.rs b/pumpkin-data/build/build.rs index 1011081c2..f839c1aa5 100644 --- a/pumpkin-data/build/build.rs +++ b/pumpkin-data/build/build.rs @@ -48,6 +48,7 @@ mod screen; mod sound; mod sound_category; mod spawn_egg; +mod structures; mod tag; mod tracked_data; mod world_event; @@ -84,6 +85,7 @@ pub fn main() { (spawn_egg::build, "spawn_egg.rs"), (block::build, "block.rs"), (item::build, "item.rs"), + (structures::build, "structures.rs"), (chunk_gen_settings::build, "chunk_gen_settings.rs"), (fluid::build, "fluid.rs"), (entity_status::build, "entity_status.rs"), diff --git a/pumpkin-data/build/structures.rs b/pumpkin-data/build/structures.rs new file mode 100644 index 000000000..e279f0103 --- /dev/null +++ b/pumpkin-data/build/structures.rs @@ -0,0 +1,466 @@ +use proc_macro2::TokenStream; +use quote::{ToTokens, format_ident, quote}; +use serde::Deserialize; +use std::{collections::HashMap, fs}; + +#[derive(Deserialize)] +pub struct StructureSetStruct { + pub placement: StructurePlacementStruct, + pub structures: Vec, +} + +#[derive(Deserialize, Clone)] +pub struct WeightedEntryStruct { + pub structure: String, + pub weight: u32, +} + +#[derive(Deserialize)] +pub struct StructurePlacementStruct { + frequency_reduction_method: Option, + frequency: Option, + salt: u32, + #[serde(flatten)] + r#type: StructurePlacementTypeStruct, +} + +#[derive(Deserialize)] +#[serde(tag = "type")] +pub enum StructurePlacementTypeStruct { + #[serde(rename = "minecraft:random_spread")] + RandomSpread { + spacing: i32, + separation: i32, + spread_type: Option, + }, + #[serde(rename = "minecraft:concentric_rings")] + ConcentricRings { + spread: i32, + distance: i32, + count: i32, + preferred_biomes: String, + }, +} + +#[derive(Deserialize, Clone)] +pub struct StructureStruct { + pub biomes: String, + pub step: String, +} + +impl ToTokens for StructureSetStruct { + fn to_tokens(&self, tokens: &mut TokenStream) { + let placement = &self.placement; + let structures = &self.structures; + + tokens.extend(quote!( + StructureSet { + placement: #placement, + structures: &[#(#structures),*], + } + )); + } +} + +impl ToTokens for WeightedEntryStruct { + fn to_tokens(&self, tokens: &mut TokenStream) { + let structure = structure_key_to_token(&self.structure); + let weight = self.weight; + + tokens.extend(quote!( + WeightedEntry { + structure: #structure, + weight: #weight, + } + )); + } +} + +impl ToTokens for StructurePlacementStruct { + fn to_tokens(&self, tokens: &mut TokenStream) { + let frequency_reduction = match &self.frequency_reduction_method { + Some(method) => { + let method_token = frequency_reduction_to_token(method); + quote!(Some(#method_token)) + } + None => quote!(None), + }; + + let frequency = match self.frequency { + Some(f) => quote!(Some(#f)), + None => quote!(None), + }; + + let salt = self.salt; + let placement_type = &self.r#type; + + tokens.extend(quote!( + StructurePlacement { + frequency_reduction_method: #frequency_reduction, + frequency: #frequency, + salt: #salt, + placement_type: #placement_type, + } + )); + } +} + +impl ToTokens for StructurePlacementTypeStruct { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Self::RandomSpread { + spacing, + separation, + spread_type, + } => { + let spread_type_token = match spread_type { + Some(st) => { + let st_token = spread_type_to_token(st); + quote!(Some(#st_token)) + } + None => quote!(None), + }; + + tokens.extend(quote!( + StructurePlacementType::RandomSpread(RandomSpreadStructurePlacement { + spacing: #spacing, + separation: #separation, + spread_type: #spread_type_token, + }) + )); + } + Self::ConcentricRings { + spread, + distance, + count, + preferred_biomes, + } => { + tokens.extend(quote!( + StructurePlacementType::ConcentricRings(ConcentricRingsStructurePlacement { + spread: #spread, + distance: #distance, + count: #count, + preferred_biomes: #preferred_biomes, + }) + )); + } + } + } +} + +impl ToTokens for StructureStruct { + fn to_tokens(&self, tokens: &mut TokenStream) { + let biomes = &self.biomes; + let step = generation_step_to_token(&self.step); + + tokens.extend(quote!( + Structure { + biomes: #biomes, + step: #step, + } + )); + } +} + +// Helper functions + +fn structure_key_to_token(key: &str) -> TokenStream { + let stripped = key.strip_prefix("minecraft:").unwrap_or(key); + + match stripped { + "pillager_outpost" => quote!(StructureKeys::PillagerOutpost), + "mineshaft" => quote!(StructureKeys::Mineshaft), + "mineshaft_mesa" => quote!(StructureKeys::MineshaftMesa), + "mansion" => quote!(StructureKeys::Mansion), + "jungle_pyramid" => quote!(StructureKeys::JunglePyramid), + "desert_pyramid" => quote!(StructureKeys::DesertPyramid), + "igloo" => quote!(StructureKeys::Igloo), + "shipwreck" => quote!(StructureKeys::Shipwreck), + "shipwreck_beached" => quote!(StructureKeys::ShipwreckBeached), + "swamp_hut" => quote!(StructureKeys::SwampHut), + "stronghold" => quote!(StructureKeys::Stronghold), + "monument" => quote!(StructureKeys::Monument), + "ocean_ruin_cold" => quote!(StructureKeys::OceanRuinCold), + "ocean_ruin_warm" => quote!(StructureKeys::OceanRuinWarm), + "fortress" => quote!(StructureKeys::Fortress), + "nether_fossil" => quote!(StructureKeys::NetherFossil), + "end_city" => quote!(StructureKeys::EndCity), + "buried_treasure" => quote!(StructureKeys::BuriedTreasure), + "bastion_remnant" => quote!(StructureKeys::BastionRemnant), + "village_plains" => quote!(StructureKeys::VillagePlains), + "village_desert" => quote!(StructureKeys::VillageDesert), + "village_savanna" => quote!(StructureKeys::VillageSavanna), + "village_snowy" => quote!(StructureKeys::VillageSnowy), + "village_taiga" => quote!(StructureKeys::VillageTaiga), + "ruined_portal" => quote!(StructureKeys::RuinedPortal), + "ruined_portal_desert" => quote!(StructureKeys::RuinedPortalDesert), + "ruined_portal_jungle" => quote!(StructureKeys::RuinedPortalJungle), + "ruined_portal_swamp" => quote!(StructureKeys::RuinedPortalSwamp), + "ruined_portal_mountain" => quote!(StructureKeys::RuinedPortalMountain), + "ruined_portal_ocean" => quote!(StructureKeys::RuinedPortalOcean), + "ruined_portal_nether" => quote!(StructureKeys::RuinedPortalNether), + "ancient_city" => quote!(StructureKeys::AncientCity), + "trail_ruins" => quote!(StructureKeys::TrailRuins), + "trial_chambers" => quote!(StructureKeys::TrialChambers), + _ => panic!("Unknown structure key: {}", stripped), + } +} + +fn frequency_reduction_to_token(method: &str) -> TokenStream { + match method { + "default" => quote!(FrequencyReductionMethod::Default), + "legacy_type_1" => quote!(FrequencyReductionMethod::LegacyType1), + "legacy_type_2" => quote!(FrequencyReductionMethod::LegacyType2), + "legacy_type_3" => quote!(FrequencyReductionMethod::LegacyType3), + _ => quote!(FrequencyReductionMethod::Default), + } +} + +fn spread_type_to_token(spread: &str) -> TokenStream { + match spread { + "linear" => quote!(SpreadType::Linear), + "triangular" => quote!(SpreadType::Triangular), + _ => quote!(SpreadType::Linear), + } +} + +fn generation_step_to_token(step: &str) -> TokenStream { + match step { + "raw_generation" => quote!(GenerationStep::RawGeneration), + "lakes" => quote!(GenerationStep::Lakes), + "local_modifications" => quote!(GenerationStep::LocalModifications), + "underground_structures" => quote!(GenerationStep::UndergroundStructures), + "surface_structures" => quote!(GenerationStep::SurfaceStructures), + "strongholds" => quote!(GenerationStep::Strongholds), + "underground_ores" => quote!(GenerationStep::UndergroundOres), + "underground_decoration" => quote!(GenerationStep::UndergroundDecoration), + "fluid_springs" => quote!(GenerationStep::FluidSprings), + "vegetal_decoration" => quote!(GenerationStep::VegetalDecoration), + "top_layer_modification" => quote!(GenerationStep::TopLayerModification), + _ => panic!("Unknown generation step: {}", step), + } +} + +pub(crate) fn build() -> TokenStream { + println!("cargo:rerun-if-changed=../assets/structures.json"); + println!("cargo:rerun-if-changed=../assets/structure_set.json"); + + let structures_json: HashMap = + serde_json::from_str(&fs::read_to_string("../assets/structures.json").unwrap()) + .expect("Failed to parse structures.json"); + + let structure_sets_json: HashMap = + serde_json::from_str(&fs::read_to_string("../assets/structure_set.json").unwrap()) + .expect("Failed to parse structure_set.json"); + + let mut structure_const_defs = TokenStream::new(); + let mut structure_lookup_arms = TokenStream::new(); + + for (name, structure) in &structures_json { + let stripped_name = name.strip_prefix("minecraft:").unwrap_or(name); + let upper_name = stripped_name.to_uppercase(); + let const_name = format_ident!("{}", upper_name); + let key_variant = structure_key_to_token(name); + + structure_const_defs.extend(quote!( + pub const #const_name: Structure = #structure; + )); + + structure_lookup_arms.extend(quote!( + #key_variant => &Self::#const_name, + )); + } + + let mut structure_set_const_defs = TokenStream::new(); + let mut structure_set_lookup_arms = TokenStream::new(); + let mut all_structure_set_idents = Vec::new(); + + for (name, structure_set) in &structure_sets_json { + let stripped_name = name.strip_prefix("minecraft:").unwrap_or(name); + let upper_name = stripped_name.to_uppercase().replace('/', "_"); + let const_name = format_ident!("{}", upper_name); + + structure_set_const_defs.extend(quote!( + pub const #const_name: StructureSet = #structure_set; + )); + + structure_set_lookup_arms.extend(quote!( + #stripped_name => Some(&Self::#const_name), + )); + + all_structure_set_idents.push(const_name); + } + + quote!( + use pumpkin_util::math::floor_div; + use pumpkin_util::random::{ + RandomGenerator, RandomImpl, get_carver_seed, get_region_seed, + legacy_rand::LegacyRand, xoroshiro128::Xoroshiro, + }; + + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] + pub enum StructureKeys { + PillagerOutpost, + Mineshaft, + MineshaftMesa, + Mansion, + JunglePyramid, + DesertPyramid, + Igloo, + Shipwreck, + ShipwreckBeached, + SwampHut, + Stronghold, + Monument, + OceanRuinCold, + OceanRuinWarm, + Fortress, + NetherFossil, + EndCity, + BuriedTreasure, + BastionRemnant, + VillagePlains, + VillageDesert, + VillageSavanna, + VillageSnowy, + VillageTaiga, + RuinedPortal, + RuinedPortalDesert, + RuinedPortalJungle, + RuinedPortalSwamp, + RuinedPortalMountain, + RuinedPortalOcean, + RuinedPortalNether, + AncientCity, + TrailRuins, + TrialChambers, + } + + pub struct StructureSet { + pub placement: StructurePlacement, + pub structures: &'static [WeightedEntry], + } + + #[derive(Clone)] + pub struct WeightedEntry { + pub structure: StructureKeys, + pub weight: u32, + } + + pub struct StructurePlacement { + pub frequency_reduction_method: Option, + pub frequency: Option, + pub salt: u32, + pub placement_type: StructurePlacementType, + } + + #[derive(Clone, Copy)] + pub enum FrequencyReductionMethod { + Default, + LegacyType1, + LegacyType2, + LegacyType3, + } + + pub enum StructurePlacementType { + RandomSpread(RandomSpreadStructurePlacement), + ConcentricRings(ConcentricRingsStructurePlacement), + } + + pub struct RandomSpreadStructurePlacement { + pub spacing: i32, + pub separation: i32, + pub spread_type: Option, + } + + pub struct ConcentricRingsStructurePlacement { + pub spread: i32, + pub distance: i32, + pub count: i32, + pub preferred_biomes: &'static str, + } + + #[derive(Clone, Copy)] + pub enum SpreadType { + Linear, + Triangular, + } + + impl SpreadType { + pub fn get(&self, random: &mut RandomGenerator, bound: i32) -> i32 { + match self { + Self::Linear => random.next_bounded_i32(bound), + Self::Triangular => i32::midpoint( + random.next_bounded_i32(bound), + random.next_bounded_i32(bound), + ), + } + } + } + + pub struct Structure { + pub biomes: &'static str, + pub step: GenerationStep, + } + + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub enum GenerationStep { + RawGeneration, + Lakes, + LocalModifications, + UndergroundStructures, + SurfaceStructures, + Strongholds, + UndergroundOres, + UndergroundDecoration, + FluidSprings, + VegetalDecoration, + TopLayerModification, + } + + impl GenerationStep { + #[must_use] + pub const fn ordinal(&self) -> usize { + *self as usize + } + } + + pub struct StructurePlacementCalculator { + pub seed: i64, + } + + impl StructurePlacementCalculator { + #[must_use] + pub const fn new(seed: i64) -> Self { + Self { seed } + } + } + + impl Structure { + #structure_const_defs + + #[must_use] + pub const fn get(key: &StructureKeys) -> &'static Self { + match *key { + #structure_lookup_arms + } + } + } + + impl StructureSet { + #structure_set_const_defs + + pub const ALL: &'static [StructureSet] = &[ + #(Self::#all_structure_set_idents),* + ]; + + #[must_use] + pub fn get(name: &str) -> Option<&'static Self> { + match name { + #structure_set_lookup_arms + _ => None, + } + } + } + ) +} diff --git a/pumpkin-data/src/lib.rs b/pumpkin-data/src/lib.rs index 92369ff73..2781259a3 100644 --- a/pumpkin-data/src/lib.rs +++ b/pumpkin-data/src/lib.rs @@ -170,6 +170,10 @@ pub mod fuels; #[path = "generated/effect.rs"] pub mod effect; +#[rustfmt::skip] +#[path = "generated/structures.rs"] +pub mod structures; + #[rustfmt::skip] #[path = "generated/potion.rs"] pub mod potion; diff --git a/pumpkin-world/src/generation/mod.rs b/pumpkin-world/src/generation/mod.rs index 8330966d7..b0f91d5c8 100644 --- a/pumpkin-world/src/generation/mod.rs +++ b/pumpkin-world/src/generation/mod.rs @@ -15,7 +15,6 @@ pub mod proto_chunk; pub mod proto_chunk_test; pub mod rule; mod rule_test; -pub mod settings; pub mod structure; mod surface; diff --git a/pumpkin-world/src/generation/proto_chunk.rs b/pumpkin-world/src/generation/proto_chunk.rs index bb09baf9b..ae4c042b6 100644 --- a/pumpkin-world/src/generation/proto_chunk.rs +++ b/pumpkin-world/src/generation/proto_chunk.rs @@ -5,6 +5,9 @@ use pumpkin_data::block_properties::is_air; use pumpkin_data::chunk_gen_settings::GenerationSettings; use pumpkin_data::dimension::Dimension; use pumpkin_data::fluid::{Fluid, FluidState}; +use pumpkin_data::structures::{ + Structure, StructureKeys, StructurePlacementCalculator, StructureSet, WeightedEntry, +}; use pumpkin_data::tag; use pumpkin_data::{Block, BlockState, block_properties::blocks_movement, chunk::Biome}; use pumpkin_util::random::{RandomImpl, get_carver_seed}; @@ -35,9 +38,9 @@ use crate::generation::noise::aquifer_sampler::{ use crate::generation::noise::perlin::DoublePerlinNoiseSampler; use crate::generation::noise::router::surface_height_sampler::SurfaceHeightSamplerBuilderOptions; use crate::generation::noise::{CHUNK_DIM, ChunkNoiseGenerator, LAVA_BLOCK, WATER_BLOCK}; -use crate::generation::structure::placement::StructurePlacementCalculator; +use crate::generation::structure::placement::should_generate_structure; use crate::generation::structure::structures::StructureInstance; -use crate::generation::structure::{STRUCTURE_SETS, STRUCTURES, StructureKeys, WeightedEntry}; +use crate::generation::structure::try_generate_structure; use crate::generation::surface::rule::try_apply_material_rule; use crate::{ BlockStateId, ProtoNoiseRouters, @@ -972,9 +975,8 @@ impl ProtoChunk { let center_z = center_chunk.z; for (id, instance) in ¢er_chunk.structure_starts { - if let Some(s) = STRUCTURES.get(id) - && s.step.ordinal() != step - { + let s = Structure::get(id); + if s.step.ordinal() != step { continue; } @@ -1005,9 +1007,8 @@ impl ProtoChunk { if let Some(neighbor) = cache.try_get_proto_chunk(neighbor_x, neighbor_z) { for (id, instance) in &neighbor.structure_starts { - if let Some(s) = STRUCTURES.get(id) - && s.step.ordinal() != step - { + let s = Structure::get(id); + if s.step.ordinal() != step { continue; } @@ -1078,8 +1079,8 @@ impl ProtoChunk { let seed = random_config.seed; let calculator = StructurePlacementCalculator::new(seed as i64); - for (_set_name, set) in STRUCTURE_SETS.iter() { - if !set.placement.should_generate(&calculator, self.x, self.z) { + for set in StructureSet::ALL { + if !should_generate_structure(&set.placement, &calculator, self.x, self.z) { continue; } @@ -1090,7 +1091,7 @@ impl ProtoChunk { continue; } - let mut candidates = set.structures.clone(); + let mut candidates = set.structures.to_vec(); let mut random: RandomGenerator = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(seed)); let carver_seed = get_carver_seed(&mut random, seed, self.x, self.z); @@ -1130,17 +1131,19 @@ impl ProtoChunk { entry: &WeightedEntry, random_config: &GlobalRandomConfig, ) -> bool { - if let Some(structure) = STRUCTURES.get(&entry.structure) { - let position = - entry - .structure - .try_generate(structure, random_config.seed as i64, self, sea_level); + let structure = Structure::get(&entry.structure); + let position = try_generate_structure( + &entry.structure, + structure, + random_config.seed as i64, + self, + sea_level, + ); - if let Some(pos) = position { - self.structure_starts - .insert(entry.structure.clone(), StructureInstance::Start(pos)); - return true; - } + if let Some(pos) = position { + self.structure_starts + .insert(entry.structure, StructureInstance::Start(pos)); + return true; } false } @@ -1175,7 +1178,7 @@ impl ProtoChunk { .get_bounding_box() .intersects_raw_xz(start_x, start_z, end_x, end_z) { - references.push((key.clone(), start_data.start_pos)); + references.push((*key, start_data.start_pos)); } } } diff --git a/pumpkin-world/src/generation/settings.rs b/pumpkin-world/src/generation/settings.rs deleted file mode 100644 index 585a270b2..000000000 --- a/pumpkin-world/src/generation/settings.rs +++ /dev/null @@ -1,104 +0,0 @@ -// use std::{collections::HashMap, sync::LazyLock}; - -// use pumpkin_data::dimension::Dimension; -// use pumpkin_util::include_json_static; -// use serde::Deserialize; - -// use crate::block::BlockStateCodec; - -// use super::{biome_coords::to_block, height_limit::HeightLimitView, surface::rule::MaterialRule}; - -// pub static GENERATION_SETTINGS: LazyLock> = -// LazyLock::new( -// || include_json_static!("../../../assets/chunk_gen_settings.json", HashMap), -// ); - -// pub fn gen_settings_from_dimension(dimension: &Dimension) -> &GenerationSettings { -// let setting = if dimension == &Dimension::OVERWORLD { -// GeneratorSetting::Overworld -// } else if dimension == &Dimension::THE_NETHER { -// GeneratorSetting::Nether -// } else { -// GeneratorSetting::End -// }; -// GENERATION_SETTINGS.get(&setting).unwrap() -// } - -// #[derive(Deserialize, Hash, PartialEq, Eq)] -// #[serde(rename_all = "snake_case")] -// pub enum GeneratorSetting { -// Overworld, -// LargeBiomes, -// Amplified, -// Nether, -// End, -// Caves, -// FloatingIslands, -// } -// #[derive(Deserialize)] -// pub struct GenerationSettings { -// pub aquifers_enabled: bool, -// pub ore_veins_enabled: bool, -// pub legacy_random_source: bool, -// pub sea_level: i32, -// pub default_fluid: BlockStateCodec, -// #[serde(rename = "noise")] -// pub shape: GenerationShapeConfig, -// pub surface_rule: MaterialRule, -// pub default_block: BlockStateCodec, -// } -// #[derive(Deserialize)] -// pub struct GenerationShapeConfig { -// pub min_y: i8, -// pub height: u16, -// size_horizontal: u8, -// size_vertical: u8, -// } - -// impl GenerationShapeConfig { -// #[inline] -// #[must_use] -// pub fn vertical_cell_block_count(&self) -> u8 { -// to_block(self.size_vertical) -// } - -// #[inline] -// #[must_use] -// pub fn horizontal_cell_block_count(&self) -> u8 { -// to_block(self.size_horizontal) -// } - -// #[must_use] -// pub const fn max_y(&self) -> u16 { -// if self.min_y >= 0 { -// self.height + self.min_y as u16 -// } else { -// (self.height as i32 + self.min_y as i32) as u16 -// } -// } - -// pub fn trim_height(&self, limit: &dyn HeightLimitView) -> Self { -// let new_min = self.min_y.max(limit.bottom_y()); - -// let this_top = if self.min_y >= 0 { -// self.height + self.min_y as u16 -// } else { -// self.height - self.min_y.unsigned_abs() as u16 -// }; - -// let new_top = this_top.min(limit.top_y()); - -// let new_height = if new_min >= 0 { -// new_top - new_min as u16 -// } else { -// new_top + new_min.unsigned_abs() as u16 -// }; - -// Self { -// min_y: new_min, -// height: new_height, -// size_horizontal: self.size_horizontal, -// size_vertical: self.size_vertical, -// } -// } -// } diff --git a/pumpkin-world/src/generation/structure/mod.rs b/pumpkin-world/src/generation/structure/mod.rs index 01c9da53e..24b613dd8 100644 --- a/pumpkin-world/src/generation/structure/mod.rs +++ b/pumpkin-world/src/generation/structure/mod.rs @@ -1,20 +1,16 @@ -use std::{collections::HashMap, sync::LazyLock}; - -use pumpkin_data::tag::{RegistryKey, get_tag_ids}; -use pumpkin_util::include_json_static; -use serde::Deserialize; +use pumpkin_data::{ + structures::{Structure, StructureKeys}, + tag::{RegistryKey, get_tag_ids}, +}; use crate::{ ProtoChunk, generation::{ biome_coords, - structure::{ - placement::StructurePlacement, - structures::{ - StructureGenerator, StructureGeneratorContext, StructurePosition, - buried_treasure::BuriedTreasureGenerator, create_chunk_random, - stronghold::StrongholdGenerator, swamp_hut::SwampHutGenerator, - }, + structure::structures::{ + StructureGenerator, StructureGeneratorContext, StructurePosition, + buried_treasure::BuriedTreasureGenerator, create_chunk_random, + stronghold::StrongholdGenerator, swamp_hut::SwampHutGenerator, }, }, }; @@ -24,223 +20,60 @@ pub mod placement; pub mod shiftable_piece; pub mod structures; -#[derive(Deserialize)] -pub struct StructureSet { - pub placement: StructurePlacement, - pub structures: Vec, -} +#[must_use] +pub fn try_generate_structure( + key: &StructureKeys, + structure: &Structure, + seed: i64, + chunk: &ProtoChunk, + sea_level: i32, +) -> Option { + let random = create_chunk_random(seed, chunk.x, chunk.z); + let context = StructureGeneratorContext { + seed, + chunk_x: chunk.x, + chunk_z: chunk.z, + random, + sea_level, + min_y: chunk.bottom_y() as i32, + }; -#[derive(Deserialize, Clone)] -pub struct WeightedEntry { - pub structure: StructureKeys, - pub weight: u32, -} - -// #[derive(Deserialize)] -// pub enum StructureType { -// #[serde(rename = "minecraft:buried_treasure")] -// BuriedTreasure(BuriedTreasureGenerator), -// #[serde(rename = "minecraft:desert_pyramid")] -// DesertPyramid, -// #[serde(rename = "minecraft:end_city")] -// EndCity, -// #[serde(rename = "minecraft:fortress")] -// NetherFortress(NetherFortressGenerator), -// #[serde(rename = "minecraft:igloo")] -// Igloo, -// #[serde(rename = "minecraft:jigsaw")] -// Jigsaw, -// #[serde(rename = "minecraft:jungle_temple")] -// JungleTemple, -// #[serde(rename = "minecraft:mineshaft")] -// Mineshaft, -// #[serde(rename = "minecraft:nether_fossil")] -// NetherFossil, -// #[serde(rename = "minecraft:ocean_monument")] -// OceanMonument, -// #[serde(rename = "minecraft:ocean_ruin")] -// OceanRuin, -// #[serde(rename = "minecraft:ruined_portal")] -// RuinedPortal, -// #[serde(rename = "minecraft:shipwreck")] -// Shipwreck, -// #[serde(rename = "minecraft:stronghold")] -// Stronghold, -// #[serde(rename = "minecraft:swamp_hut")] -// SwampHut(SwampHutGenerator), -// #[serde(rename = "minecraft:woodland_mansion")] -// WoodlandMansion, -// } - -#[derive(Deserialize, Clone, PartialEq, Eq, Hash, Debug)] -pub enum StructureKeys { - #[serde(rename = "minecraft:pillager_outpost")] - PillagerOutpost, - #[serde(rename = "minecraft:mineshaft")] - Mineshaft, - #[serde(rename = "minecraft:mineshaft_mesa")] - MineshaftMesa, - #[serde(rename = "minecraft:mansion")] - Mansion, - #[serde(rename = "minecraft:jungle_pyramid")] - JunglePyramid, - #[serde(rename = "minecraft:desert_pyramid")] - DesertPyramid, - #[serde(rename = "minecraft:igloo")] - Igloo, - #[serde(rename = "minecraft:shipwreck")] - Shipwreck, - #[serde(rename = "minecraft:shipwreck_beached")] - ShipwreckBeached, - #[serde(rename = "minecraft:swamp_hut")] - SwampHut, - #[serde(rename = "minecraft:stronghold")] - Stronghold, - #[serde(rename = "minecraft:monument")] - Monument, - #[serde(rename = "minecraft:ocean_ruin_cold")] - OceanRuinCold, - #[serde(rename = "minecraft:ocean_ruin_warm")] - OceanRuinWarm, - #[serde(rename = "minecraft:fortress")] - Fortress, - #[serde(rename = "minecraft:nether_fossil")] - NetherFossil, - #[serde(rename = "minecraft:end_city")] - EndCity, - #[serde(rename = "minecraft:buried_treasure")] - BuriedTreasure, - #[serde(rename = "minecraft:bastion_remnant")] - BastionRemnant, - #[serde(rename = "minecraft:village_plains")] - VillagePlains, - #[serde(rename = "minecraft:village_desert")] - VillageDesert, - #[serde(rename = "minecraft:village_savanna")] - VillageSavanna, - #[serde(rename = "minecraft:village_snowy")] - VillageSnowy, - #[serde(rename = "minecraft:village_taiga")] - VillageTaiga, - #[serde(rename = "minecraft:ruined_portal")] - RuinedPortal, - #[serde(rename = "minecraft:ruined_portal_desert")] - RuinedPortalDesert, - #[serde(rename = "minecraft:ruined_portal_jungle")] - RuinedPortalJungle, - #[serde(rename = "minecraft:ruined_portal_swamp")] - RuinedPortalSwamp, - #[serde(rename = "minecraft:ruined_portal_mountain")] - RuinedPortalMountain, - #[serde(rename = "minecraft:ruined_portal_ocean")] - RuinedPortalOcean, - #[serde(rename = "minecraft:ruined_portal_nether")] - RuinedPortalNether, - #[serde(rename = "minecraft:ancient_city")] - AncientCity, - #[serde(rename = "minecraft:trail_ruins")] - TrailRuins, - #[serde(rename = "minecraft:trial_chambers")] - TrialChambers, -} - -impl StructureKeys { - #[must_use] - pub fn try_generate( - &self, - structure: &Structure, - seed: i64, - chunk: &ProtoChunk, - sea_level: i32, - ) -> Option { - let random = create_chunk_random(seed, chunk.x, chunk.z); - let context = StructureGeneratorContext { - seed, - chunk_x: chunk.x, - chunk_z: chunk.z, - random, - sea_level, - min_y: chunk.bottom_y() as i32, - }; - let structure_pos = match self { - Self::BuriedTreasure => { - BuriedTreasureGenerator::get_structure_position(&BuriedTreasureGenerator, context) - } - // StructureKeys::Fortress => { - // NetherFortressGenerator::get_structure_position(&NetherFortressGenerator, context) - // } - Self::SwampHut => { - SwampHutGenerator::get_structure_position(&SwampHutGenerator, context) - } - Self::Stronghold => { - StrongholdGenerator::get_structure_position(&StrongholdGenerator, context) - } - // StructureKeys::DesertPyramid => DesertTempleGenerator::get_structure_position( - // &DesertTempleGenerator, - // context - // ), - // TODO - _ => None, - }; - - if let Some(pos) = structure_pos { - // Get the biome at the structure's starting position - let current_biome = chunk.get_biome_id( - biome_coords::from_block(pos.start_pos.0.x), - biome_coords::from_block(pos.start_pos.0.y), - biome_coords::from_block(pos.start_pos.0.z), - ) as u16; - let biomes = get_tag_ids( - RegistryKey::WorldgenBiome, - structure - .biomes - .strip_prefix("#") - .unwrap_or(&structure.biomes), - ) - .unwrap(); - // Check if the biome is allowed for this structure - if biomes.contains(¤t_biome) { - return Some(pos); - } + let structure_pos = match key { + StructureKeys::BuriedTreasure => { + BuriedTreasureGenerator::get_structure_position(&BuriedTreasureGenerator, context) } + StructureKeys::SwampHut => { + SwampHutGenerator::get_structure_position(&SwampHutGenerator, context) + } + StructureKeys::Stronghold => { + StrongholdGenerator::get_structure_position(&StrongholdGenerator, context) + } + // TODO: Implement other structure types + _ => None, + }; - None + if let Some(pos) = structure_pos { + // Get the biome at the structure's starting position + let current_biome = chunk.get_biome_id( + biome_coords::from_block(pos.start_pos.0.x), + biome_coords::from_block(pos.start_pos.0.y), + biome_coords::from_block(pos.start_pos.0.z), + ) as u16; + + let biomes = get_tag_ids( + RegistryKey::WorldgenBiome, + structure + .biomes + .strip_prefix("#") + .unwrap_or(structure.biomes), + ) + .unwrap(); + + // Check if the biome is allowed for this structure + if biomes.contains(¤t_biome) { + return Some(pos); + } } + + None } - -#[derive(Deserialize, Clone, PartialEq, Eq, Hash, Debug)] -pub struct Structure { - pub biomes: String, - pub step: GenerationStep, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum GenerationStep { - RawGeneration, - Lakes, - LocalModifications, - UndergroundStructures, - SurfaceStructures, - Strongholds, - UndergroundOres, - UndergroundDecoration, - FluidSprings, - VegetalDecoration, - TopLayerModification, -} - -impl GenerationStep { - #[must_use] - pub const fn ordinal(&self) -> usize { - *self as usize - } -} - -pub static STRUCTURES: LazyLock> = LazyLock::new( - || include_json_static!("../../../../assets/structures.json", HashMap), -); - -pub static STRUCTURE_SETS: LazyLock> = LazyLock::new( - || include_json_static!("../../../../assets/structure_set.json", HashMap), -); diff --git a/pumpkin-world/src/generation/structure/placement.rs b/pumpkin-world/src/generation/structure/placement.rs index 793da5d3b..47c1a10b0 100644 --- a/pumpkin-world/src/generation/structure/placement.rs +++ b/pumpkin-world/src/generation/structure/placement.rs @@ -1,3 +1,7 @@ +use pumpkin_data::structures::{ + ConcentricRingsStructurePlacement, FrequencyReductionMethod, RandomSpreadStructurePlacement, + SpreadType, StructurePlacement, StructurePlacementCalculator, StructurePlacementType, +}; use pumpkin_util::{ math::floor_div, random::{ @@ -5,238 +9,149 @@ use pumpkin_util::{ xoroshiro128::Xoroshiro, }, }; -use serde::Deserialize; +#[must_use] +pub fn should_generate_structure( + placement: &StructurePlacement, + calculator: &StructurePlacementCalculator, + chunk_x: i32, + chunk_z: i32, +) -> bool { + is_start_chunk( + &placement.placement_type, + calculator, + chunk_x, + chunk_z, + placement.salt, + ) && apply_frequency_reduction( + placement.frequency_reduction_method, + calculator.seed, + chunk_x, + chunk_z, + placement.salt, + placement.frequency.unwrap_or(1.0), + ) +} -#[derive(Deserialize)] -pub struct StructurePlacement { - frequency_reduction_method: Option, - frequency: Option, +fn apply_frequency_reduction( + method: Option, + seed: i64, + chunk_x: i32, + chunk_z: i32, salt: u32, - #[serde(flatten)] - r#type: StructurePlacementType, -} - -impl StructurePlacement { - #[must_use] - pub fn should_generate( - &self, - calculator: &StructurePlacementCalculator, - chunk_x: i32, - chunk_z: i32, - ) -> bool { - self.r#type - .is_start_chunk(calculator, chunk_x, chunk_z, self.salt) - && self.apply_frequency_reduction(calculator.seed, chunk_x, chunk_z) - // TODO: add exclusion_zone, only used for pillager_outposts + frequency: f32, +) -> bool { + if frequency >= 1.0 { + return true; } - fn apply_frequency_reduction(&self, seed: i64, chunk_x: i32, chunk_z: i32) -> bool { - let frequency = self.frequency.unwrap_or(1.0); - frequency >= 1.0 - || self - .frequency_reduction_method - .as_ref() - .unwrap_or(&FrequencyReductionMethod::Default) - .should_generate(seed, chunk_x, chunk_z, self.salt, frequency) - } + let method = method.unwrap_or(FrequencyReductionMethod::Default); + should_generate_frequency(method, seed, chunk_x, chunk_z, salt, frequency) } -#[derive(Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum FrequencyReductionMethod { - Default, - #[serde(rename = "legacy_type_1")] - LegacyType1, - #[serde(rename = "legacy_type_2")] - LegacyType2, - #[serde(rename = "legacy_type_3")] - LegacyType3, -} - -impl FrequencyReductionMethod { - #[must_use] - pub fn should_generate( - &self, - seed: i64, - chunk_x: i32, - chunk_z: i32, - salt: u32, - frequency: f32, - ) -> bool { - match self { - Self::Default => { - let region_seed = get_region_seed(seed as u64, chunk_x, chunk_z, salt); - let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(region_seed)); - random.next_f32() < frequency - } - Self::LegacyType1 => { - let x = chunk_x >> 4; - let z = chunk_z >> 4; - let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed( - (x ^ z << 4) as u64 ^ seed as u64, - )); - random.next_i32(); // yeah mojang just does that and does not use the value - random.next_bounded_i32((1.0 / frequency) as i32) == 0 - } - Self::LegacyType2 => { - let region_seed = get_region_seed(seed as u64, chunk_x, chunk_z, 10387320); - let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(region_seed)); - random.next_f32() < frequency - } - Self::LegacyType3 => { - let mut random: RandomGenerator = - RandomGenerator::Xoroshiro(Xoroshiro::from_seed(seed as u64)); - let carver_seed = get_carver_seed(&mut random, seed as u64, chunk_x, chunk_z); - let mut random: RandomGenerator = - RandomGenerator::Xoroshiro(Xoroshiro::from_seed(carver_seed)); - - random.next_f64() < frequency as f64 - } +fn should_generate_frequency( + method: FrequencyReductionMethod, + seed: i64, + chunk_x: i32, + chunk_z: i32, + salt: u32, + frequency: f32, +) -> bool { + match method { + FrequencyReductionMethod::Default => { + let region_seed = get_region_seed(seed as u64, chunk_x, chunk_z, salt); + let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(region_seed)); + random.next_f32() < frequency + } + FrequencyReductionMethod::LegacyType1 => { + let x = chunk_x >> 4; + let z = chunk_z >> 4; + let mut random = + RandomGenerator::Xoroshiro(Xoroshiro::from_seed((x ^ z << 4) as u64 ^ seed as u64)); + random.next_i32(); + random.next_bounded_i32((1.0 / frequency) as i32) == 0 + } + FrequencyReductionMethod::LegacyType2 => { + let region_seed = get_region_seed(seed as u64, chunk_x, chunk_z, 10387320); + let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(region_seed)); + random.next_f32() < frequency + } + FrequencyReductionMethod::LegacyType3 => { + let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(seed as u64)); + let carver_seed = get_carver_seed(&mut random, seed as u64, chunk_x, chunk_z); + let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(carver_seed)); + random.next_f64() < frequency as f64 } } } -#[derive(Deserialize)] -#[serde(tag = "type")] -pub enum StructurePlacementType { - #[serde(rename = "minecraft:random_spread")] - RandomSpread(RandomSpreadStructurePlacement), - #[serde(rename = "minecraft:concentric_rings")] - ConcentricRings(ConcentricRingsStructurePlacement), -} - -impl StructurePlacementType { - #[must_use] - pub fn is_start_chunk( - &self, - calculator: &StructurePlacementCalculator, - chunk_x: i32, - chunk_z: i32, - salt: u32, - ) -> bool { - match self { - Self::RandomSpread(placement) => { - placement.is_start_chunk(calculator, chunk_x, chunk_z, salt) - } - // This is needed for Stronghold, since it is placed in rings - Self::ConcentricRings(placement) => { - placement.is_start_chunk(calculator, chunk_x, chunk_z, salt) - } +fn is_start_chunk( + placement_type: &StructurePlacementType, + calculator: &StructurePlacementCalculator, + chunk_x: i32, + chunk_z: i32, + salt: u32, +) -> bool { + match placement_type { + StructurePlacementType::RandomSpread(placement) => { + is_start_chunk_random_spread(placement, calculator, chunk_x, chunk_z, salt) + } + StructurePlacementType::ConcentricRings(placement) => { + is_start_chunk_concentric_rings(placement, calculator, chunk_x, chunk_z, salt) } } } -#[derive(Deserialize)] -pub struct RandomSpreadStructurePlacement { - spacing: i32, - separation: i32, - spread_type: Option, +fn get_start_chunk_random_spread( + placement: &RandomSpreadStructurePlacement, + seed: i64, + chunk_x: i32, + chunk_z: i32, + salt: u32, +) -> (i32, i32) { + let x = floor_div(chunk_x, placement.spacing); + let z = floor_div(chunk_z, placement.spacing); + let region_seed = get_region_seed(seed as u64, x, z, salt); + let mut random = RandomGenerator::Legacy(LegacyRand::from_seed(region_seed)); + let bound = placement.spacing - placement.separation; + let spread_type = placement.spread_type.unwrap_or(SpreadType::Linear); + let rand_x = spread_type.get(&mut random, bound); + let rand_z = spread_type.get(&mut random, bound); + ( + x * placement.spacing + rand_x, + z * placement.spacing + rand_z, + ) } -#[derive(Deserialize)] -pub struct ConcentricRingsStructurePlacement { - spread: i32, - distance: i32, - count: i32, - preferred_biomes: String, +fn is_start_chunk_random_spread( + placement: &RandomSpreadStructurePlacement, + calculator: &StructurePlacementCalculator, + chunk_x: i32, + chunk_z: i32, + salt: u32, +) -> bool { + let pos = get_start_chunk_random_spread(placement, calculator.seed, chunk_x, chunk_z, salt); + (chunk_x == pos.0) && (chunk_z == pos.1) } -impl ConcentricRingsStructurePlacement { - #[must_use] - pub fn is_start_chunk( - &self, - _calculator: &StructurePlacementCalculator, - _chunk_x: i32, - _chunk_z: i32, - _salt: u32, - ) -> bool { - // TODO: this is entirely wrong, but the original logic is too complex for now - // let x = (chunk_x << 4) as f64; - // let z = (chunk_z << 4) as f64; - // let distance_sq = x * x + z * z; - // let distance = distance_sq.sqrt(); - - // let ring_width = 1000.0; - // let ring_gap = 2000.0; - - // let in_ring = (distance % (ring_width + ring_gap)) < ring_width; - - // if !in_ring { - // return false; - // } - - // let seed = (chunk_x as i64).wrapping_mul(341873128712) - // ^ (chunk_z as i64).wrapping_mul(132897987541) - // ^ (salt as i64); - - // (seed.abs() % 400) == 0 - rand::random_bool(1.0 / 100.0) - } -} - -#[derive(Deserialize)] -#[serde(rename_all = "lowercase")] -pub enum SpreadType { - Linear, - Triangular, -} - -impl SpreadType { - pub fn get(&self, random: &mut RandomGenerator, bound: i32) -> i32 { - match self { - Self::Linear => random.next_bounded_i32(bound), - Self::Triangular => i32::midpoint( - random.next_bounded_i32(bound), - random.next_bounded_i32(bound), - ), - } - } -} - -impl RandomSpreadStructurePlacement { - fn get_start_chunk(&self, seed: i64, chunk_x: i32, chunk_z: i32, salt: u32) -> (i32, i32) { - let x = floor_div(chunk_x, self.spacing); - let z = floor_div(chunk_z, self.spacing); - let region_seed = get_region_seed(seed as u64, x, z, salt); - let mut random = RandomGenerator::Legacy(LegacyRand::from_seed(region_seed)); - let bound = self.spacing - self.separation; - let spread_type = self.spread_type.as_ref().unwrap_or(&SpreadType::Linear); - let rand_x = spread_type.get(&mut random, bound); - let rand_z = spread_type.get(&mut random, bound); - (x * self.spacing + rand_x, z * self.spacing + rand_z) - } - - #[must_use] - pub fn is_start_chunk( - &self, - calculator: &StructurePlacementCalculator, - chunk_x: i32, - chunk_z: i32, - salt: u32, - ) -> bool { - let pos = self.get_start_chunk(calculator.seed, chunk_x, chunk_z, salt); - (chunk_x == pos.0) && (chunk_z == pos.1) - } -} - -pub struct StructurePlacementCalculator { - pub seed: i64, -} - -impl StructurePlacementCalculator { - #[must_use] - pub const fn new(seed: i64) -> Self { - Self { seed } - } +fn is_start_chunk_concentric_rings( + _placement: &ConcentricRingsStructurePlacement, + _calculator: &StructurePlacementCalculator, + _chunk_x: i32, + _chunk_z: i32, + _salt: u32, +) -> bool { + // TODO: Implement proper concentric rings logic + rand::random_bool(1.0 / 100.0) } #[cfg(test)] mod tests { + use pumpkin_data::structures::RandomSpreadStructurePlacement; use pumpkin_util::random::{ RandomGenerator, RandomImpl, get_region_seed, legacy_rand::LegacyRand, }; - use crate::generation::structure::placement::RandomSpreadStructurePlacement; + use crate::generation::structure::placement::get_start_chunk_random_spread; #[test] fn get_start_chunk_random() { @@ -252,7 +167,7 @@ mod tests { separation: 8, spread_type: None, }; - let (x, z) = random.get_start_chunk(123, 1, 1, 14357620); + let (x, z) = get_start_chunk_random_spread(&random, 123, 1, 1, 14357620); assert_eq!(x, 5); assert_eq!(z, 4); }