mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
chore: move structure settings from runtime to compile time parsing
This commit is contained in:
32
Cargo.lock
generated
32
Cargo.lock
generated
@@ -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]]
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"),
|
||||
|
||||
466
pumpkin-data/build/structures.rs
Normal file
466
pumpkin-data/build/structures.rs
Normal file
@@ -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<WeightedEntryStruct>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct WeightedEntryStruct {
|
||||
pub structure: String,
|
||||
pub weight: u32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct StructurePlacementStruct {
|
||||
frequency_reduction_method: Option<String>,
|
||||
frequency: Option<f32>,
|
||||
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<String>,
|
||||
},
|
||||
#[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<String, StructureStruct> =
|
||||
serde_json::from_str(&fs::read_to_string("../assets/structures.json").unwrap())
|
||||
.expect("Failed to parse structures.json");
|
||||
|
||||
let structure_sets_json: HashMap<String, StructureSetStruct> =
|
||||
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<FrequencyReductionMethod>,
|
||||
pub frequency: Option<f32>,
|
||||
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<SpreadType>,
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<HashMap<GeneratorSetting, GenerationSettings>> =
|
||||
// LazyLock::new(
|
||||
// || include_json_static!("../../../assets/chunk_gen_settings.json", HashMap<GeneratorSetting, GenerationSettings>),
|
||||
// );
|
||||
|
||||
// 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,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
@@ -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<WeightedEntry>,
|
||||
}
|
||||
#[must_use]
|
||||
pub fn try_generate_structure(
|
||||
key: &StructureKeys,
|
||||
structure: &Structure,
|
||||
seed: i64,
|
||||
chunk: &ProtoChunk,
|
||||
sea_level: i32,
|
||||
) -> Option<StructurePosition> {
|
||||
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<StructurePosition> {
|
||||
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<HashMap<StructureKeys, Structure>> = LazyLock::new(
|
||||
|| include_json_static!("../../../../assets/structures.json", HashMap<StructureKeys, Structure>),
|
||||
);
|
||||
|
||||
pub static STRUCTURE_SETS: LazyLock<HashMap<String, StructureSet>> = LazyLock::new(
|
||||
|| include_json_static!("../../../../assets/structure_set.json", HashMap<String, StructureSet>),
|
||||
);
|
||||
|
||||
@@ -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<FrequencyReductionMethod>,
|
||||
frequency: Option<f32>,
|
||||
fn apply_frequency_reduction(
|
||||
method: Option<FrequencyReductionMethod>,
|
||||
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<SpreadType>,
|
||||
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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user