feat(chunk-gen): add carver
This commit is contained in:
parent
a58ccdd33d
commit
7fe705c7eb
277
pumpkin-codegen/src/carver.rs
Normal file
277
pumpkin-codegen/src/carver.rs
Normal file
@ -0,0 +1,277 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
use serde_json::Value;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs;
|
||||
|
||||
pub fn build() -> TokenStream {
|
||||
let carvers: BTreeMap<String, Value> =
|
||||
serde_json::from_str(&fs::read_to_string("../assets/carver.json").unwrap())
|
||||
.expect("Failed to parse carver.json");
|
||||
|
||||
let mut carver_instances = Vec::new();
|
||||
|
||||
for (name, data) in carvers {
|
||||
let variant_name = format_ident!("{}", name.to_uppercase());
|
||||
let config = &data["config"];
|
||||
let carver_type = data["type"].as_str().unwrap_or("");
|
||||
|
||||
let prob = config["probability"].as_f64().unwrap_or(0.0) as f32;
|
||||
let y = value_to_height_provider(&config["y"]);
|
||||
let y_scale = value_to_float_provider(&config["yScale"]);
|
||||
let lava_level = value_to_y_offset(&config["lava_level"]);
|
||||
|
||||
let replaceable_str = config["replaceable"].as_str().unwrap_or("");
|
||||
let replaceable = if replaceable_str.starts_with('#') {
|
||||
let tag_name = replaceable_str[1..]
|
||||
.to_uppercase()
|
||||
.replace(':', "_")
|
||||
.replace('.', "_")
|
||||
.replace('-', "_");
|
||||
let tag_ident = format_ident!("{}", tag_name);
|
||||
quote! { crate::tag::Block::#tag_ident }
|
||||
} else {
|
||||
// Handle non-tag replaceables if any
|
||||
quote! { crate::tag::Block::MINECRAFT_OVERWORLD_CARVER_REPLACEABLES }
|
||||
};
|
||||
|
||||
let additional = match carver_type {
|
||||
"minecraft:cave" => {
|
||||
let horizontal_radius_multiplier =
|
||||
value_to_float_provider(&config["horizontal_radius_multiplier"]);
|
||||
let vertical_radius_multiplier =
|
||||
value_to_float_provider(&config["vertical_radius_multiplier"]);
|
||||
let floor_level = value_to_float_provider(&config["floor_level"]);
|
||||
quote! {
|
||||
CarverAdditionalConfig::Cave(CaveCarverConfig {
|
||||
horizontal_radius_multiplier: #horizontal_radius_multiplier,
|
||||
vertical_radius_multiplier: #vertical_radius_multiplier,
|
||||
floor_level: #floor_level,
|
||||
})
|
||||
}
|
||||
}
|
||||
"minecraft:nether_cave" => {
|
||||
let horizontal_radius_multiplier =
|
||||
value_to_float_provider(&config["horizontal_radius_multiplier"]);
|
||||
let vertical_radius_multiplier =
|
||||
value_to_float_provider(&config["vertical_radius_multiplier"]);
|
||||
let floor_level = value_to_float_provider(&config["floor_level"]);
|
||||
quote! {
|
||||
CarverAdditionalConfig::NetherCave(CaveCarverConfig {
|
||||
horizontal_radius_multiplier: #horizontal_radius_multiplier,
|
||||
vertical_radius_multiplier: #vertical_radius_multiplier,
|
||||
floor_level: #floor_level,
|
||||
})
|
||||
}
|
||||
}
|
||||
"minecraft:canyon" => {
|
||||
let vertical_rotation = value_to_float_provider(&config["vertical_rotation"]);
|
||||
let shape = &config["shape"];
|
||||
let distance_factor = value_to_float_provider(&shape["distance_factor"]);
|
||||
let thickness = value_to_float_provider(&shape["thickness"]);
|
||||
let width_smoothness = shape["width_smoothness"].as_i64().unwrap_or(0) as i32;
|
||||
let horizontal_radius_factor =
|
||||
value_to_float_provider(&shape["horizontal_radius_factor"]);
|
||||
let vertical_radius_default_factor =
|
||||
shape["vertical_radius_default_factor"].as_f64().unwrap_or(0.0) as f32;
|
||||
let vertical_radius_center_factor =
|
||||
shape["vertical_radius_center_factor"].as_f64().unwrap_or(0.0) as f32;
|
||||
|
||||
quote! {
|
||||
CarverAdditionalConfig::Canyon(CanyonCarverConfig {
|
||||
vertical_rotation: #vertical_rotation,
|
||||
shape: CanyonShapeConfig {
|
||||
distance_factor: #distance_factor,
|
||||
thickness: #thickness,
|
||||
width_smoothness: #width_smoothness,
|
||||
horizontal_radius_factor: #horizontal_radius_factor,
|
||||
vertical_radius_default_factor: #vertical_radius_default_factor,
|
||||
vertical_radius_center_factor: #vertical_radius_center_factor,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
_ => quote! { CarverAdditionalConfig::Cave(CaveCarverConfig::default()) },
|
||||
};
|
||||
|
||||
carver_instances.push(quote! {
|
||||
pub const #variant_name: CarverConfig = CarverConfig {
|
||||
probability: #prob,
|
||||
y: #y,
|
||||
y_scale: #y_scale,
|
||||
lava_level: #lava_level,
|
||||
replaceable: #replaceable,
|
||||
additional: #additional,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
quote! {
|
||||
use pumpkin_util::math::float_provider::{FloatProvider, NormalFloatProvider, ConstantFloatProvider, UniformFloatProvider, TrapezoidFloatProvider, ClampedNormalFloatProvider};
|
||||
use pumpkin_util::y_offset::{YOffset, Absolute, AboveBottom, BelowTop};
|
||||
|
||||
pub enum HeightProvider {
|
||||
Uniform(UniformHeightProvider),
|
||||
Trapezoid(TrapezoidHeightProvider),
|
||||
VeryBiasedToBottom(VeryBiasedToBottomHeightProvider),
|
||||
}
|
||||
|
||||
pub struct UniformHeightProvider {
|
||||
pub min_inclusive: YOffset,
|
||||
pub max_inclusive: YOffset,
|
||||
}
|
||||
|
||||
pub struct TrapezoidHeightProvider {
|
||||
pub min_inclusive: YOffset,
|
||||
pub max_inclusive: YOffset,
|
||||
pub plateau: Option<i32>,
|
||||
}
|
||||
|
||||
pub struct VeryBiasedToBottomHeightProvider {
|
||||
pub min_inclusive: YOffset,
|
||||
pub max_inclusive: YOffset,
|
||||
pub inner: Option<std::num::NonZeroU32>,
|
||||
}
|
||||
|
||||
pub struct CaveCarverConfig {
|
||||
pub horizontal_radius_multiplier: FloatProvider,
|
||||
pub vertical_radius_multiplier: FloatProvider,
|
||||
pub floor_level: FloatProvider,
|
||||
}
|
||||
|
||||
impl CaveCarverConfig {
|
||||
pub const fn default() -> Self {
|
||||
Self {
|
||||
horizontal_radius_multiplier: FloatProvider::Constant(1.0),
|
||||
vertical_radius_multiplier: FloatProvider::Constant(1.0),
|
||||
floor_level: FloatProvider::Constant(-0.7),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CanyonShapeConfig {
|
||||
pub distance_factor: FloatProvider,
|
||||
pub thickness: FloatProvider,
|
||||
pub width_smoothness: i32,
|
||||
pub horizontal_radius_factor: FloatProvider,
|
||||
pub vertical_radius_default_factor: f32,
|
||||
pub vertical_radius_center_factor: f32,
|
||||
}
|
||||
|
||||
pub struct CanyonCarverConfig {
|
||||
pub vertical_rotation: FloatProvider,
|
||||
pub shape: CanyonShapeConfig,
|
||||
}
|
||||
|
||||
pub enum CarverAdditionalConfig {
|
||||
Cave(CaveCarverConfig),
|
||||
NetherCave(CaveCarverConfig),
|
||||
Canyon(CanyonCarverConfig),
|
||||
}
|
||||
|
||||
pub struct CarverConfig {
|
||||
pub probability: f32,
|
||||
pub y: HeightProvider,
|
||||
pub y_scale: FloatProvider,
|
||||
pub lava_level: YOffset,
|
||||
pub replaceable: crate::tag::Tag,
|
||||
pub additional: CarverAdditionalConfig,
|
||||
}
|
||||
|
||||
use super::*;
|
||||
#(#carver_instances)*
|
||||
}
|
||||
}
|
||||
|
||||
fn value_to_float_provider(v: &Value) -> TokenStream {
|
||||
if v.is_number() {
|
||||
let f = v.as_f64().unwrap_or(0.0) as f32;
|
||||
quote! { FloatProvider::Constant(#f) }
|
||||
} else {
|
||||
// This is complex because we need to match the structure of FloatProvider in pumpkin-util
|
||||
// For now, let's handle the common case of uniform
|
||||
let type_str = v["type"].as_str().unwrap_or("");
|
||||
match type_str {
|
||||
"minecraft:uniform" => {
|
||||
let min = v["min_inclusive"].as_f64().unwrap_or(0.0) as f32;
|
||||
let max = v["max_exclusive"].as_f64().unwrap_or(0.0) as f32;
|
||||
quote! { FloatProvider::Object(NormalFloatProvider::Uniform(UniformFloatProvider::new(#min, #max))) }
|
||||
}
|
||||
"minecraft:constant" => {
|
||||
let val = v["value"].as_f64().unwrap_or(0.0) as f32;
|
||||
quote! { FloatProvider::Constant(#val) }
|
||||
}
|
||||
"minecraft:trapezoid" => {
|
||||
let min = v["min"].as_f64().unwrap_or(0.0) as f32;
|
||||
let max = v["max"].as_f64().unwrap_or(0.0) as f32;
|
||||
let plateau = v["plateau"].as_f64().unwrap_or(0.0) as f32;
|
||||
quote! { FloatProvider::Object(NormalFloatProvider::Trapezoid(TrapezoidFloatProvider::new(#min, #max, #plateau))) }
|
||||
}
|
||||
_ => {
|
||||
// Fallback to constant 0 if unknown
|
||||
quote! { FloatProvider::Constant(0.0) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn value_to_height_provider(v: &Value) -> TokenStream {
|
||||
let type_str = v["type"].as_str().unwrap_or("");
|
||||
match type_str {
|
||||
"minecraft:uniform" => {
|
||||
let min = value_to_y_offset(&v["min_inclusive"]);
|
||||
let max = value_to_y_offset(&v["max_inclusive"]);
|
||||
quote! {
|
||||
HeightProvider::Uniform(UniformHeightProvider {
|
||||
min_inclusive: #min,
|
||||
max_inclusive: #max,
|
||||
})
|
||||
}
|
||||
}
|
||||
"minecraft:trapezoid" => {
|
||||
let min = value_to_y_offset(&v["min_inclusive"]);
|
||||
let max = value_to_y_offset(&v["max_inclusive"]);
|
||||
let plateau = if v["plateau"].is_number() {
|
||||
let p = v["plateau"].as_i64().unwrap_or(0) as i32;
|
||||
quote! { Some(#p) }
|
||||
} else {
|
||||
quote! { None }
|
||||
};
|
||||
quote! {
|
||||
HeightProvider::Trapezoid(TrapezoidHeightProvider {
|
||||
min_inclusive: #min,
|
||||
max_inclusive: #max,
|
||||
plateau: #plateau,
|
||||
})
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let zero = value_to_y_offset(&Value::Null);
|
||||
quote! {
|
||||
HeightProvider::Uniform(UniformHeightProvider {
|
||||
min_inclusive: #zero,
|
||||
max_inclusive: #zero,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn value_to_y_offset(v: &Value) -> TokenStream {
|
||||
if v.is_object() {
|
||||
if let Some(abs) = v.get("absolute") {
|
||||
let val = abs.as_i64().unwrap_or(0) as i16;
|
||||
quote! { YOffset::Absolute(Absolute { absolute: #val }) }
|
||||
} else if let Some(ab) = v.get("above_bottom") {
|
||||
let val = ab.as_i64().unwrap_or(0) as i8;
|
||||
quote! { YOffset::AboveBottom(AboveBottom { above_bottom: #val }) }
|
||||
} else if let Some(bt) = v.get("below_top") {
|
||||
let val = bt.as_i64().unwrap_or(0) as i8;
|
||||
quote! { YOffset::BelowTop(BelowTop { below_top: #val }) }
|
||||
} else {
|
||||
quote! { YOffset::Absolute(Absolute { absolute: 0 }) }
|
||||
}
|
||||
} else {
|
||||
quote! { YOffset::Absolute(Absolute { absolute: 0 }) }
|
||||
}
|
||||
}
|
||||
@ -13,6 +13,7 @@ mod attributes;
|
||||
mod biome;
|
||||
mod bitsets;
|
||||
mod block;
|
||||
mod carver;
|
||||
mod chunk_gen_settings;
|
||||
mod chunk_status;
|
||||
mod composter_increase_chance;
|
||||
@ -122,6 +123,7 @@ pub fn main() {
|
||||
configured_feature::build,
|
||||
"configured_features_generated.rs",
|
||||
),
|
||||
(carver::build, "carver.rs"),
|
||||
];
|
||||
build_functions.extend(remap::build());
|
||||
|
||||
|
||||
@ -41,6 +41,7 @@ default = [
|
||||
"potion",
|
||||
"recipe_remainder",
|
||||
"chunk_gen_settings",
|
||||
"carver",
|
||||
]
|
||||
|
||||
item = []
|
||||
@ -76,6 +77,7 @@ structures = []
|
||||
potion = ["potion_brewing"]
|
||||
recipe_remainder = []
|
||||
chunk_gen_settings = []
|
||||
carver = []
|
||||
|
||||
noise_parameter = []
|
||||
biome = []
|
||||
|
||||
155
pumpkin-data/src/generated/carver.rs
Normal file
155
pumpkin-data/src/generated/carver.rs
Normal file
@ -0,0 +1,155 @@
|
||||
/* This file is generated. Do not edit manually. */
|
||||
use pumpkin_util::math::float_provider::{
|
||||
ClampedNormalFloatProvider, ConstantFloatProvider, FloatProvider, NormalFloatProvider,
|
||||
TrapezoidFloatProvider, UniformFloatProvider,
|
||||
};
|
||||
use pumpkin_util::y_offset::{AboveBottom, Absolute, BelowTop, YOffset};
|
||||
pub enum HeightProvider {
|
||||
Uniform(UniformHeightProvider),
|
||||
Trapezoid(TrapezoidHeightProvider),
|
||||
VeryBiasedToBottom(VeryBiasedToBottomHeightProvider),
|
||||
}
|
||||
pub struct UniformHeightProvider {
|
||||
pub min_inclusive: YOffset,
|
||||
pub max_inclusive: YOffset,
|
||||
}
|
||||
pub struct TrapezoidHeightProvider {
|
||||
pub min_inclusive: YOffset,
|
||||
pub max_inclusive: YOffset,
|
||||
pub plateau: Option<i32>,
|
||||
}
|
||||
pub struct VeryBiasedToBottomHeightProvider {
|
||||
pub min_inclusive: YOffset,
|
||||
pub max_inclusive: YOffset,
|
||||
pub inner: Option<std::num::NonZeroU32>,
|
||||
}
|
||||
pub struct CaveCarverConfig {
|
||||
pub horizontal_radius_multiplier: FloatProvider,
|
||||
pub vertical_radius_multiplier: FloatProvider,
|
||||
pub floor_level: FloatProvider,
|
||||
}
|
||||
impl CaveCarverConfig {
|
||||
pub const fn default() -> Self {
|
||||
Self {
|
||||
horizontal_radius_multiplier: FloatProvider::Constant(1.0),
|
||||
vertical_radius_multiplier: FloatProvider::Constant(1.0),
|
||||
floor_level: FloatProvider::Constant(-0.7),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub struct CanyonShapeConfig {
|
||||
pub distance_factor: FloatProvider,
|
||||
pub thickness: FloatProvider,
|
||||
pub width_smoothness: i32,
|
||||
pub horizontal_radius_factor: FloatProvider,
|
||||
pub vertical_radius_default_factor: f32,
|
||||
pub vertical_radius_center_factor: f32,
|
||||
}
|
||||
pub struct CanyonCarverConfig {
|
||||
pub vertical_rotation: FloatProvider,
|
||||
pub shape: CanyonShapeConfig,
|
||||
}
|
||||
pub enum CarverAdditionalConfig {
|
||||
Cave(CaveCarverConfig),
|
||||
NetherCave(CaveCarverConfig),
|
||||
Canyon(CanyonCarverConfig),
|
||||
}
|
||||
pub struct CarverConfig {
|
||||
pub probability: f32,
|
||||
pub y: HeightProvider,
|
||||
pub y_scale: FloatProvider,
|
||||
pub lava_level: YOffset,
|
||||
pub replaceable: crate::tag::Tag,
|
||||
pub additional: CarverAdditionalConfig,
|
||||
}
|
||||
use super::*;
|
||||
pub const CANYON: CarverConfig = CarverConfig {
|
||||
probability: 0.01f32,
|
||||
y: HeightProvider::Uniform(UniformHeightProvider {
|
||||
min_inclusive: YOffset::Absolute(Absolute { absolute: 10i16 }),
|
||||
max_inclusive: YOffset::Absolute(Absolute { absolute: 67i16 }),
|
||||
}),
|
||||
y_scale: FloatProvider::Constant(3f32),
|
||||
lava_level: YOffset::AboveBottom(AboveBottom { above_bottom: 8i8 }),
|
||||
replaceable: crate::tag::Block::MINECRAFT_OVERWORLD_CARVER_REPLACEABLES,
|
||||
additional: CarverAdditionalConfig::Canyon(CanyonCarverConfig {
|
||||
vertical_rotation: FloatProvider::Object(NormalFloatProvider::Uniform(
|
||||
UniformFloatProvider::new(-0.125f32, 0.125f32),
|
||||
)),
|
||||
shape: CanyonShapeConfig {
|
||||
distance_factor: FloatProvider::Object(NormalFloatProvider::Uniform(
|
||||
UniformFloatProvider::new(0.75f32, 1f32),
|
||||
)),
|
||||
thickness: FloatProvider::Object(NormalFloatProvider::Trapezoid(
|
||||
TrapezoidFloatProvider::new(0f32, 6f32, 2f32),
|
||||
)),
|
||||
width_smoothness: 3i32,
|
||||
horizontal_radius_factor: FloatProvider::Object(NormalFloatProvider::Uniform(
|
||||
UniformFloatProvider::new(0.75f32, 1f32),
|
||||
)),
|
||||
vertical_radius_default_factor: 1f32,
|
||||
vertical_radius_center_factor: 0f32,
|
||||
},
|
||||
}),
|
||||
};
|
||||
pub const CAVE: CarverConfig = CarverConfig {
|
||||
probability: 0.15f32,
|
||||
y: HeightProvider::Uniform(UniformHeightProvider {
|
||||
min_inclusive: YOffset::AboveBottom(AboveBottom { above_bottom: 8i8 }),
|
||||
max_inclusive: YOffset::Absolute(Absolute { absolute: 180i16 }),
|
||||
}),
|
||||
y_scale: FloatProvider::Object(NormalFloatProvider::Uniform(UniformFloatProvider::new(
|
||||
0.1f32, 0.9f32,
|
||||
))),
|
||||
lava_level: YOffset::AboveBottom(AboveBottom { above_bottom: 8i8 }),
|
||||
replaceable: crate::tag::Block::MINECRAFT_OVERWORLD_CARVER_REPLACEABLES,
|
||||
additional: CarverAdditionalConfig::Cave(CaveCarverConfig {
|
||||
horizontal_radius_multiplier: FloatProvider::Object(NormalFloatProvider::Uniform(
|
||||
UniformFloatProvider::new(0.7f32, 1.4f32),
|
||||
)),
|
||||
vertical_radius_multiplier: FloatProvider::Object(NormalFloatProvider::Uniform(
|
||||
UniformFloatProvider::new(0.8f32, 1.3f32),
|
||||
)),
|
||||
floor_level: FloatProvider::Object(NormalFloatProvider::Uniform(
|
||||
UniformFloatProvider::new(-1f32, -0.4f32),
|
||||
)),
|
||||
}),
|
||||
};
|
||||
pub const CAVE_EXTRA_UNDERGROUND: CarverConfig = CarverConfig {
|
||||
probability: 0.07f32,
|
||||
y: HeightProvider::Uniform(UniformHeightProvider {
|
||||
min_inclusive: YOffset::AboveBottom(AboveBottom { above_bottom: 8i8 }),
|
||||
max_inclusive: YOffset::Absolute(Absolute { absolute: 47i16 }),
|
||||
}),
|
||||
y_scale: FloatProvider::Object(NormalFloatProvider::Uniform(UniformFloatProvider::new(
|
||||
0.1f32, 0.9f32,
|
||||
))),
|
||||
lava_level: YOffset::AboveBottom(AboveBottom { above_bottom: 8i8 }),
|
||||
replaceable: crate::tag::Block::MINECRAFT_OVERWORLD_CARVER_REPLACEABLES,
|
||||
additional: CarverAdditionalConfig::Cave(CaveCarverConfig {
|
||||
horizontal_radius_multiplier: FloatProvider::Object(NormalFloatProvider::Uniform(
|
||||
UniformFloatProvider::new(0.7f32, 1.4f32),
|
||||
)),
|
||||
vertical_radius_multiplier: FloatProvider::Object(NormalFloatProvider::Uniform(
|
||||
UniformFloatProvider::new(0.8f32, 1.3f32),
|
||||
)),
|
||||
floor_level: FloatProvider::Object(NormalFloatProvider::Uniform(
|
||||
UniformFloatProvider::new(-1f32, -0.4f32),
|
||||
)),
|
||||
}),
|
||||
};
|
||||
pub const NETHER_CAVE: CarverConfig = CarverConfig {
|
||||
probability: 0.2f32,
|
||||
y: HeightProvider::Uniform(UniformHeightProvider {
|
||||
min_inclusive: YOffset::Absolute(Absolute { absolute: 0i16 }),
|
||||
max_inclusive: YOffset::BelowTop(BelowTop { below_top: 1i8 }),
|
||||
}),
|
||||
y_scale: FloatProvider::Constant(0.5f32),
|
||||
lava_level: YOffset::AboveBottom(AboveBottom { above_bottom: 10i8 }),
|
||||
replaceable: crate::tag::Block::MINECRAFT_NETHER_CARVER_REPLACEABLES,
|
||||
additional: CarverAdditionalConfig::NetherCave(CaveCarverConfig {
|
||||
horizontal_radius_multiplier: FloatProvider::Constant(1f32),
|
||||
vertical_radius_multiplier: FloatProvider::Constant(1f32),
|
||||
floor_level: FloatProvider::Constant(-0.7f32),
|
||||
}),
|
||||
};
|
||||
@ -289,3 +289,8 @@ pub use blocks::Block;
|
||||
#[rustfmt::skip]
|
||||
#[path = "generated/chunk_gen_settings.rs"]
|
||||
pub mod chunk_gen_settings;
|
||||
|
||||
#[cfg(feature = "carver")]
|
||||
#[rustfmt::skip]
|
||||
#[path = "generated/carver.rs"]
|
||||
pub mod carver;
|
||||
|
||||
@ -14,7 +14,7 @@ pub struct ChunkHolder {
|
||||
pub occupied: NodeKey,
|
||||
pub occupied_by: EdgeKey,
|
||||
pub public: bool,
|
||||
pub tasks: [NodeKey; 10],
|
||||
pub tasks: [NodeKey; StagedChunkEnum::COUNT],
|
||||
}
|
||||
|
||||
impl Default for ChunkHolder {
|
||||
@ -27,7 +27,7 @@ impl Default for ChunkHolder {
|
||||
occupied: NodeKey::null(),
|
||||
occupied_by: EdgeKey::null(),
|
||||
public: false,
|
||||
tasks: [NodeKey::null(); 10],
|
||||
tasks: [NodeKey::null(); StagedChunkEnum::COUNT],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,7 +129,7 @@ pub struct ChunkLoading {
|
||||
impl ChunkLoading {
|
||||
// pub const FULL_CHUNK_LEVEL: i8 = 33;
|
||||
pub const FULL_CHUNK_LEVEL: i8 = 43;
|
||||
pub const MAX_LEVEL: i8 = 47; // level 47 will be unloaded.
|
||||
pub const MAX_LEVEL: i8 = 48; // level 48 will be unloaded.
|
||||
fn debug_check_error(&self) -> bool {
|
||||
let mut temp = ChunkLevel::default();
|
||||
for (ticket_pos, levels) in &self.ticket {
|
||||
|
||||
@ -25,8 +25,10 @@ pub enum StagedChunkEnum {
|
||||
StructureReferences,
|
||||
/// Chunk with terrain noise generated, ready for surface building
|
||||
Noise,
|
||||
/// Chunk with surface built, ready for features and structures
|
||||
Surface, // SURFACE CARVERS
|
||||
/// Chunk with surface built, ready for carvers
|
||||
Surface,
|
||||
/// Chunk with carvers applied, ready for features and structures
|
||||
Carvers,
|
||||
/// Chunk with features and structures, ready for lighting
|
||||
Features, // FEATURES SPAWN
|
||||
/// Chunk with lighting calculated, ready for finalization
|
||||
@ -44,9 +46,10 @@ impl From<u8> for StagedChunkEnum {
|
||||
4 => Self::StructureReferences,
|
||||
5 => Self::Noise,
|
||||
6 => Self::Surface,
|
||||
7 => Self::Features,
|
||||
8 => Self::Lighting,
|
||||
9 => Self::Full,
|
||||
7 => Self::Carvers,
|
||||
8 => Self::Features,
|
||||
9 => Self::Lighting,
|
||||
10 => Self::Full,
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
@ -61,7 +64,7 @@ impl From<ChunkStatus> for StagedChunkEnum {
|
||||
ChunkStatus::Biomes => Self::Biomes,
|
||||
ChunkStatus::Noise => Self::Noise,
|
||||
ChunkStatus::Surface => Self::Surface,
|
||||
ChunkStatus::Carvers => Self::Surface,
|
||||
ChunkStatus::Carvers => Self::Carvers,
|
||||
ChunkStatus::Features => Self::Features,
|
||||
ChunkStatus::Spawn => Self::Features,
|
||||
ChunkStatus::InitializeLight => Self::Lighting,
|
||||
@ -80,6 +83,7 @@ impl From<StagedChunkEnum> for ChunkStatus {
|
||||
StagedChunkEnum::Biomes => Self::Biomes,
|
||||
StagedChunkEnum::Noise => Self::Noise,
|
||||
StagedChunkEnum::Surface => Self::Surface,
|
||||
StagedChunkEnum::Carvers => Self::Carvers,
|
||||
StagedChunkEnum::Features => Self::Features,
|
||||
StagedChunkEnum::Lighting => Self::Light,
|
||||
StagedChunkEnum::Full => Self::Full,
|
||||
@ -98,17 +102,24 @@ impl StagedChunkEnum {
|
||||
} else if level <= 45 {
|
||||
Self::Features
|
||||
} else if level <= 46 {
|
||||
Self::Carvers
|
||||
} else if level <= 47 {
|
||||
Self::Surface
|
||||
} else {
|
||||
Self::None
|
||||
}
|
||||
}
|
||||
|
||||
/// Total number of state values (0 = None … 9 = Full).
|
||||
/// Total number of state values (0 = None … 10 = Full).
|
||||
pub const COUNT: usize = Self::Full as usize + 1;
|
||||
pub const FULL_DEPENDENCIES: &'static [Self] =
|
||||
&[Self::Full, Self::Lighting, Self::Features, Self::Surface];
|
||||
pub const FULL_RADIUS: i32 = 3;
|
||||
pub const FULL_DEPENDENCIES: &'static [Self] = &[
|
||||
Self::Full,
|
||||
Self::Lighting,
|
||||
Self::Features,
|
||||
Self::Carvers,
|
||||
Self::Surface,
|
||||
];
|
||||
pub const FULL_RADIUS: i32 = 4;
|
||||
#[must_use]
|
||||
pub const fn get_direct_radius(self) -> i32 {
|
||||
// self exclude
|
||||
@ -119,6 +130,7 @@ impl StagedChunkEnum {
|
||||
Self::Biomes => 0,
|
||||
Self::Noise => 0,
|
||||
Self::Surface => 0,
|
||||
Self::Carvers => 0,
|
||||
Self::Features => 1,
|
||||
Self::Lighting => 1,
|
||||
Self::Full => 1,
|
||||
@ -135,6 +147,7 @@ impl StagedChunkEnum {
|
||||
Self::Biomes => 0,
|
||||
Self::Noise => 0,
|
||||
Self::Surface => 0,
|
||||
Self::Carvers => 0,
|
||||
Self::Features => 1,
|
||||
Self::Lighting => 1,
|
||||
Self::Full => 0,
|
||||
@ -161,7 +174,8 @@ impl StagedChunkEnum {
|
||||
],
|
||||
Self::Noise => &[Self::StructureReferences],
|
||||
Self::Surface => &[Self::Noise],
|
||||
Self::Features => &[Self::Surface, Self::Surface],
|
||||
Self::Carvers => &[Self::Surface],
|
||||
Self::Features => &[Self::Carvers, Self::Carvers],
|
||||
Self::Lighting => &[Self::Features, Self::Features],
|
||||
Self::Full => &[Self::Lighting, Self::Lighting],
|
||||
_ => panic!(),
|
||||
@ -179,7 +193,7 @@ impl Chunk {
|
||||
pub fn get_stage_id(&self) -> u8 {
|
||||
match self {
|
||||
Self::Proto(data) => data.stage_id(),
|
||||
Self::Level(_) => 9,
|
||||
Self::Level(_) => StagedChunkEnum::Full as u8,
|
||||
}
|
||||
}
|
||||
pub fn get_proto_chunk_mut(&mut self) -> &mut ProtoChunk {
|
||||
|
||||
@ -367,6 +367,9 @@ impl Cache {
|
||||
StagedChunkEnum::Surface => self.chunks[mid]
|
||||
.get_proto_chunk_mut()
|
||||
.step_to_surface(generator),
|
||||
StagedChunkEnum::Carvers => self.chunks[mid]
|
||||
.get_proto_chunk_mut()
|
||||
.step_to_carvers(generator),
|
||||
StagedChunkEnum::Features => {
|
||||
ProtoChunk::generate_features_and_structure(
|
||||
self,
|
||||
|
||||
329
pumpkin-world/src/generation/carver/canyon.rs
Normal file
329
pumpkin-world/src/generation/carver/canyon.rs
Normal file
@ -0,0 +1,329 @@
|
||||
use super::Carver;
|
||||
use super::cave::get_height;
|
||||
use crate::ProtoChunk;
|
||||
use pumpkin_data::block_state::BlockState;
|
||||
use pumpkin_data::carver::{CarverAdditionalConfig, CarverConfig};
|
||||
use pumpkin_util::math::vector2::Vector2;
|
||||
use pumpkin_util::random::{RandomGenerator, RandomImpl};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub struct CanyonCarver;
|
||||
|
||||
impl Carver for CanyonCarver {
|
||||
fn carve(
|
||||
&self,
|
||||
config: &CarverConfig,
|
||||
chunk: &mut ProtoChunk,
|
||||
random: &mut RandomGenerator,
|
||||
_chunk_pos: &Vector2<i32>,
|
||||
carver_chunk_pos: &Vector2<i32>,
|
||||
) {
|
||||
let CarverAdditionalConfig::Canyon(ref canyon_config) = config.additional else {
|
||||
return;
|
||||
};
|
||||
|
||||
let min_y = chunk.bottom_y() as i32;
|
||||
let height = chunk.height();
|
||||
|
||||
let max_distance = (4 * 2 - 1) * 16;
|
||||
|
||||
let x = (carver_chunk_pos.x << 4) + random.next_bounded_i32(16);
|
||||
let y = get_height(&config.y, random, min_y as i8, height);
|
||||
let z = (carver_chunk_pos.y << 4) + random.next_bounded_i32(16);
|
||||
|
||||
let horizontal_rotation = random.next_f32() * PI * 2.0;
|
||||
let vertical_rotation = canyon_config.vertical_rotation.get(random);
|
||||
let y_scale = config.y_scale.get(random) as f64;
|
||||
let thickness = canyon_config.shape.thickness.get(random);
|
||||
let distance =
|
||||
(max_distance as f32 * canyon_config.shape.distance_factor.get(random)) as i32;
|
||||
|
||||
self.do_carve(
|
||||
config,
|
||||
chunk,
|
||||
random.next_i64(),
|
||||
x as f64,
|
||||
y as f64,
|
||||
z as f64,
|
||||
thickness,
|
||||
horizontal_rotation,
|
||||
vertical_rotation,
|
||||
0,
|
||||
distance,
|
||||
y_scale,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl CanyonCarver {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn do_carve(
|
||||
&self,
|
||||
config: &CarverConfig,
|
||||
chunk: &mut ProtoChunk,
|
||||
tunnel_seed: i64,
|
||||
mut x: f64,
|
||||
mut y: f64,
|
||||
mut z: f64,
|
||||
thickness: f32,
|
||||
mut horizontal_rotation: f32,
|
||||
mut vertical_rotation: f32,
|
||||
step: i32,
|
||||
distance: i32,
|
||||
y_scale: f64,
|
||||
) {
|
||||
let mut random = RandomGenerator::Xoroshiro(
|
||||
pumpkin_util::random::xoroshiro128::Xoroshiro::from_seed(tunnel_seed as u64),
|
||||
);
|
||||
let width_factor_per_height =
|
||||
self.init_width_factors(chunk.height() as usize, config, &mut random);
|
||||
let mut y_rota = 0.0f32;
|
||||
let mut x_rota = 0.0f32;
|
||||
|
||||
let CarverAdditionalConfig::Canyon(ref canyon_config) = config.additional else {
|
||||
return;
|
||||
};
|
||||
|
||||
for current_step in step..distance {
|
||||
let mut horizontal_radius =
|
||||
(1.5 + (current_step as f32 * PI / distance as f32).sin() * thickness) as f64;
|
||||
let mut vertical_radius = horizontal_radius * y_scale;
|
||||
horizontal_radius *= canyon_config
|
||||
.shape
|
||||
.horizontal_radius_factor
|
||||
.get(&mut random) as f64;
|
||||
vertical_radius = self.update_vertical_radius(
|
||||
config,
|
||||
&mut random,
|
||||
vertical_radius,
|
||||
distance as f32,
|
||||
current_step as f32,
|
||||
);
|
||||
|
||||
let xc = vertical_rotation.cos();
|
||||
let xs = vertical_rotation.sin();
|
||||
x += (horizontal_rotation.cos() * xc) as f64;
|
||||
y += xs as f64;
|
||||
z += (horizontal_rotation.sin() * xc) as f64;
|
||||
|
||||
vertical_rotation *= 0.7;
|
||||
vertical_rotation += x_rota * 0.05;
|
||||
horizontal_rotation += y_rota * 0.05;
|
||||
x_rota *= 0.8;
|
||||
y_rota *= 0.5;
|
||||
x_rota += (random.next_f32() - random.next_f32()) * random.next_f32() * 2.0;
|
||||
y_rota += (random.next_f32() - random.next_f32()) * random.next_f32() * 4.0;
|
||||
|
||||
if random.next_bounded_i32(4) != 0 {
|
||||
if !self.can_reach(chunk.x, chunk.z, x, z, current_step, distance, thickness) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.carve_ellipsoid(
|
||||
chunk,
|
||||
config,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
horizontal_radius,
|
||||
vertical_radius,
|
||||
&width_factor_per_height,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn init_width_factors(
|
||||
&self,
|
||||
depth: usize,
|
||||
config: &CarverConfig,
|
||||
random: &mut RandomGenerator,
|
||||
) -> Vec<f32> {
|
||||
let CarverAdditionalConfig::Canyon(ref canyon_config) = config.additional else {
|
||||
return vec![1.0; depth];
|
||||
};
|
||||
let mut width_factor_per_height = vec![0.0; depth];
|
||||
let mut width_factor = 1.0f32;
|
||||
|
||||
for (y_index, item) in width_factor_per_height.iter_mut().enumerate() {
|
||||
if y_index == 0 || random.next_bounded_i32(canyon_config.shape.width_smoothness) == 0 {
|
||||
width_factor = 1.0 + random.next_f32() * random.next_f32();
|
||||
}
|
||||
*item = width_factor * width_factor;
|
||||
}
|
||||
width_factor_per_height
|
||||
}
|
||||
|
||||
fn update_vertical_radius(
|
||||
&self,
|
||||
config: &CarverConfig,
|
||||
random: &mut RandomGenerator,
|
||||
vertical_radius: f64,
|
||||
distance: f32,
|
||||
current_step: f32,
|
||||
) -> f64 {
|
||||
let CarverAdditionalConfig::Canyon(ref canyon_config) = config.additional else {
|
||||
return vertical_radius;
|
||||
};
|
||||
let vertical_multiplier = 1.0 - (0.5 - current_step / distance).abs() * 2.0;
|
||||
let factor = canyon_config.shape.vertical_radius_default_factor
|
||||
+ canyon_config.shape.vertical_radius_center_factor * vertical_multiplier;
|
||||
factor as f64 * vertical_radius * (random.next_inbetween_i32(75, 100) as f64 / 100.0)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn can_reach(
|
||||
&self,
|
||||
chunk_x: i32,
|
||||
chunk_z: i32,
|
||||
x: f64,
|
||||
z: f64,
|
||||
step: i32,
|
||||
distance: i32,
|
||||
thickness: f32,
|
||||
) -> bool {
|
||||
let chunk_middle_x = (chunk_x << 4) + 8;
|
||||
let chunk_middle_z = (chunk_z << 4) + 8;
|
||||
let dx = x - chunk_middle_x as f64;
|
||||
let dz = z - chunk_middle_z as f64;
|
||||
let remaining = (distance - step) as f64;
|
||||
let rr = (thickness + 2.0 + 16.0) as f64;
|
||||
dx * dx + dz * dz - remaining * remaining <= rr * rr
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn carve_ellipsoid(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk,
|
||||
config: &CarverConfig,
|
||||
x: f64,
|
||||
y: f64,
|
||||
z: f64,
|
||||
horizontal_radius: f64,
|
||||
vertical_radius: f64,
|
||||
width_factor_per_height: &[f32],
|
||||
) {
|
||||
let center_x = (chunk.x << 4) as f64 + 8.0;
|
||||
let center_z = (chunk.z << 4) as f64 + 8.0;
|
||||
let max_delta = 16.0 + horizontal_radius * 2.0;
|
||||
|
||||
if (x - center_x).abs() > max_delta || (z - center_z).abs() > max_delta {
|
||||
return;
|
||||
}
|
||||
|
||||
let chunk_min_x = chunk.x << 4;
|
||||
let chunk_min_z = chunk.z << 4;
|
||||
|
||||
let min_x_index = ((x - horizontal_radius).floor() as i32 - chunk_min_x - 1).max(0);
|
||||
let max_x_index = ((x + horizontal_radius).floor() as i32 - chunk_min_x).min(15);
|
||||
|
||||
let min_y = ((y - vertical_radius).floor() as i32 - 1).max(chunk.bottom_y() as i32 + 1);
|
||||
let protected_blocks_on_top = 7;
|
||||
let max_y = ((y + vertical_radius).floor() as i32 + 1)
|
||||
.min(chunk.bottom_y() as i32 + chunk.height() as i32 - 1 - protected_blocks_on_top);
|
||||
|
||||
let min_z_index = ((z - horizontal_radius).floor() as i32 - chunk_min_z - 1).max(0);
|
||||
let max_z_index = ((z + horizontal_radius).floor() as i32 - chunk_min_z).min(15);
|
||||
|
||||
for x_index in min_x_index..=max_x_index {
|
||||
let world_x = chunk_min_x + x_index;
|
||||
let xd = (world_x as f64 + 0.5 - x) / horizontal_radius;
|
||||
|
||||
for z_index in min_z_index..=max_z_index {
|
||||
let world_z = chunk_min_z + z_index;
|
||||
let zd = (world_z as f64 + 0.5 - z) / horizontal_radius;
|
||||
|
||||
if xd * xd + zd * zd < 1.0 {
|
||||
let mut has_grass = false;
|
||||
for world_y in (min_y..=max_y).rev() {
|
||||
let yd = (world_y as f64 - 0.5 - y) / vertical_radius;
|
||||
|
||||
if !self.should_skip(
|
||||
width_factor_per_height,
|
||||
xd,
|
||||
yd,
|
||||
zd,
|
||||
world_y,
|
||||
chunk.bottom_y() as i32,
|
||||
) && !chunk.carving_mask.get(world_x, world_y, world_z)
|
||||
{
|
||||
chunk.carving_mask.set(world_x, world_y, world_z);
|
||||
has_grass |= self
|
||||
.carve_block(chunk, config, world_x, world_y, world_z, has_grass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn should_skip(
|
||||
&self,
|
||||
width_factor_per_height: &[f32],
|
||||
xd: f64,
|
||||
yd: f64,
|
||||
zd: f64,
|
||||
y: i32,
|
||||
min_gen_y: i32,
|
||||
) -> bool {
|
||||
let y_index = (y - min_gen_y) as usize;
|
||||
if y_index == 0 {
|
||||
return true;
|
||||
}
|
||||
(xd * xd + zd * zd) * width_factor_per_height[y_index - 1] as f64 + yd * yd / 6.0 >= 1.0
|
||||
}
|
||||
|
||||
fn carve_block(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk,
|
||||
config: &CarverConfig,
|
||||
x: i32,
|
||||
y: i32,
|
||||
z: i32,
|
||||
mut has_grass: bool,
|
||||
) -> bool {
|
||||
let local_y = y - chunk.bottom_y() as i32;
|
||||
let state_id = chunk.get_block_state_raw(x & 15, local_y, z & 15);
|
||||
let block = pumpkin_data::Block::from_state_id(state_id);
|
||||
|
||||
if block.id == pumpkin_data::Block::WATER.id || block.id == pumpkin_data::Block::LAVA.id {
|
||||
return false;
|
||||
}
|
||||
|
||||
if block.id == pumpkin_data::Block::GRASS_BLOCK.id
|
||||
|| block.id == pumpkin_data::Block::MYCELIUM.id
|
||||
{
|
||||
has_grass = true;
|
||||
}
|
||||
|
||||
if config.replaceable.1.contains(&block.id) {
|
||||
let air = BlockState::from_id(pumpkin_data::Block::AIR.default_state.id);
|
||||
let lava = BlockState::from_id(pumpkin_data::Block::LAVA.default_state.id);
|
||||
|
||||
let lava_y = config
|
||||
.lava_level
|
||||
.get_y(chunk.bottom_y() as i16, chunk.height());
|
||||
|
||||
if y <= lava_y {
|
||||
chunk.set_block_state(x & 15, local_y, z & 15, lava);
|
||||
} else {
|
||||
chunk.set_block_state(x & 15, local_y, z & 15, air);
|
||||
}
|
||||
|
||||
if has_grass {
|
||||
let down_y = y - 1;
|
||||
let local_down_y = down_y - chunk.bottom_y() as i32;
|
||||
if (0..chunk.height() as i32).contains(&local_down_y) {
|
||||
let down_state_id = chunk.get_block_state_raw(x & 15, local_down_y, z & 15);
|
||||
if pumpkin_data::Block::from_state_id(down_state_id).id
|
||||
== pumpkin_data::Block::DIRT.id
|
||||
{
|
||||
// dirt replacement skipped
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
@ -1,166 +1,423 @@
|
||||
// use pumpkin_macros::block_state;
|
||||
// use pumpkin_util::{
|
||||
// math::{float_provider::FloatProvider, vector2::Vector2, vector3::Vector3},
|
||||
// random::RandomGenerator,
|
||||
// };
|
||||
// use serde::Deserialize;
|
||||
use super::Carver;
|
||||
use crate::ProtoChunk;
|
||||
use pumpkin_data::block_state::BlockState;
|
||||
use pumpkin_data::carver::{CarverAdditionalConfig, CarverConfig, HeightProvider};
|
||||
use pumpkin_util::math::vector2::Vector2;
|
||||
use pumpkin_util::random::{RandomGenerator, RandomImpl};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
// use crate::{
|
||||
// ProtoChunk,
|
||||
// block::{BlockState, registry::get_block},
|
||||
// generation::{
|
||||
// aquifer_sampler::{AquiferSamplerImpl, WorldAquiferSampler},
|
||||
// height_limit::HeightLimitView,
|
||||
// height_provider::HeightProvider,
|
||||
// positions::chunk_pos,
|
||||
// section_coords,
|
||||
// y_offset::YOffset,
|
||||
// },
|
||||
// };
|
||||
pub struct CaveCarver;
|
||||
|
||||
// use super::mask::CarvingMask;
|
||||
impl Carver for CaveCarver {
|
||||
fn carve(
|
||||
&self,
|
||||
config: &CarverConfig,
|
||||
chunk: &mut ProtoChunk,
|
||||
random: &mut RandomGenerator,
|
||||
_chunk_pos: &Vector2<i32>,
|
||||
carver_chunk_pos: &Vector2<i32>,
|
||||
) {
|
||||
let (is_nether, cave_config) = match config.additional {
|
||||
CarverAdditionalConfig::Cave(ref c) => (false, c),
|
||||
CarverAdditionalConfig::NetherCave(ref c) => (true, c),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// #[derive(Deserialize)]
|
||||
// pub struct CaveCraver {
|
||||
// vertical_radius_multiplier: FloatProvider,
|
||||
// horizontal_radius_multiplier: FloatProvider,
|
||||
// floor_level: FloatProvider,
|
||||
// y: HeightProvider,
|
||||
// #[serde(rename = "yScale")]
|
||||
// y_scale: FloatProvider,
|
||||
// lava_level: YOffset,
|
||||
// probability: f32,
|
||||
// }
|
||||
// const BRANCH_FACTOR: i32 = 4;
|
||||
// const MAX_CAVE_COUNT: i32 = 15;
|
||||
let min_y = chunk.bottom_y() as i32;
|
||||
let height = chunk.height();
|
||||
|
||||
// impl CaveCraver {
|
||||
// pub fn should_carve(&self, random: &mut RandomGenerator) -> bool {
|
||||
// random.next_f32() <= self.probability
|
||||
// }
|
||||
let max_distance = (4 * 2 - 1) << 4;
|
||||
|
||||
// pub fn carve(
|
||||
// &self,
|
||||
// random: &mut RandomGenerator,
|
||||
// chunk_pos: &Vector2<i32>,
|
||||
// min_y: i8,
|
||||
// height: u16,
|
||||
// ) {
|
||||
// todo!();
|
||||
// let block_coord = section_coords::section_to_block(BRANCH_FACTOR * 2 - 1);
|
||||
// let first_rnd = random.next_bounded_i32(MAX_CAVE_COUNT);
|
||||
// let sec_rnd = random.next_bounded_i32(first_rnd + 1);
|
||||
// let third_rnd = random.next_bounded_i32(sec_rnd + 1);
|
||||
// let range = third_rnd;
|
||||
// for _ in 0..range {
|
||||
// let x = chunk_pos::start_block_x(chunk_pos) + random.next_bounded_i32(16); // offset
|
||||
// let y = self.y.get(random, min_y, height);
|
||||
// let z = chunk_pos::start_block_z(chunk_pos) + random.next_bounded_i32(16); // offset
|
||||
// let vertical = self.vertical_radius_multiplier.get();
|
||||
// let horizontal = self.horizontal_radius_multiplier.get();
|
||||
// let floor = self.floor_level.get();
|
||||
// let mut pitch;
|
||||
// let mut tries = 0;
|
||||
// if random.next_bounded_i32(4) == 0 {
|
||||
// let scale = self.y_scale.get();
|
||||
// pitch = 1.0 + random.next_f32() * 6.0;
|
||||
// tries += random.next_bounded_i32(4);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
let bound = if is_nether { 10 } else { 15 };
|
||||
let c1 = random.next_bounded_i32(bound);
|
||||
let c2 = random.next_bounded_i32(c1 + 1);
|
||||
let cave_count = random.next_bounded_i32(c2 + 1);
|
||||
|
||||
// fn carve_cave(&self, chunk_pos: &Vector2<i32>, width: f64, height: f64) {
|
||||
// todo!();
|
||||
// // let width = 1.5 + 1.5707964f64.sin() * width;
|
||||
// // let height = width * height;
|
||||
// // Self::carve_region(chunk_pos, width, height)
|
||||
// }
|
||||
for _ in 0..cave_count {
|
||||
let x = (carver_chunk_pos.x << 4) + random.next_bounded_i32(16);
|
||||
let y = get_height(&config.y, random, min_y as i8, height) as f64;
|
||||
let z = (carver_chunk_pos.y << 4) + random.next_bounded_i32(16);
|
||||
|
||||
// fn carve_region(
|
||||
// min_y: i8,
|
||||
// chunk_height: u16,
|
||||
// chunk_pos: &Vector2<i32>,
|
||||
// x: f64,
|
||||
// y: f64,
|
||||
// z: f64,
|
||||
// width: f64,
|
||||
// height: f64,
|
||||
// mask: &mut CarvingMask,
|
||||
// floor_level: f64,
|
||||
// ) {
|
||||
// let start_x = chunk_pos::start_block_x(chunk_pos);
|
||||
// let start_z = chunk_pos::start_block_z(chunk_pos);
|
||||
let horizontal_radius_multiplier =
|
||||
cave_config.horizontal_radius_multiplier.get(random) as f64;
|
||||
let vertical_radius_multiplier =
|
||||
cave_config.vertical_radius_multiplier.get(random) as f64;
|
||||
let floor_level = cave_config.floor_level.get(random) as f64;
|
||||
|
||||
// let chunk_center_x = (start_x + 8) as f64;
|
||||
// let chunk_center_z = (start_z + 8) as f64;
|
||||
let mut tunnels = 1;
|
||||
if random.next_bounded_i32(4) == 0 {
|
||||
let y_scale = config.y_scale.get(random) as f64;
|
||||
let thickness = 1.0 + random.next_f32() * 6.0;
|
||||
self.create_room(
|
||||
chunk,
|
||||
x as f64,
|
||||
y,
|
||||
z as f64,
|
||||
thickness,
|
||||
y_scale,
|
||||
config,
|
||||
floor_level,
|
||||
is_nether,
|
||||
);
|
||||
tunnels += random.next_bounded_i32(4);
|
||||
}
|
||||
|
||||
// let max_width = 16.0 + width * 2.0;
|
||||
// if (x - chunk_center_x).abs() > max_width || (z - chunk_center_z).abs() > max_width {
|
||||
// return;
|
||||
// }
|
||||
// let x_start = (x - width).floor() as i32 - start_x - 1.max(0);
|
||||
// let max_x = (x - width).floor() as i32 - start_x - 1.min(15);
|
||||
for _ in 0..tunnels {
|
||||
let horizontal_rotation = random.next_f32() * PI * 2.0;
|
||||
let vertical_rotation = (random.next_f32() - 0.5) / 4.0;
|
||||
let thickness = self.get_thickness(random, is_nether);
|
||||
let distance = max_distance - random.next_bounded_i32(max_distance / 4);
|
||||
|
||||
// let z_start = (z - width).floor() as i32 - start_z - 1.max(0);
|
||||
// let max_z = (z - width).floor() as i32 - start_z - 1.min(15);
|
||||
self.create_tunnel(
|
||||
config,
|
||||
chunk,
|
||||
random.next_i64(),
|
||||
x as f64,
|
||||
y,
|
||||
z as f64,
|
||||
horizontal_radius_multiplier,
|
||||
vertical_radius_multiplier,
|
||||
thickness,
|
||||
horizontal_rotation,
|
||||
vertical_rotation,
|
||||
0,
|
||||
distance,
|
||||
if is_nether { 5.0 } else { 1.0 }, // this.getYScale()
|
||||
floor_level,
|
||||
is_nether,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// let init_height =
|
||||
// (y + height).floor() as i32 + 1.min(min_y as i32 + chunk_height as i32 - 1 - 7);
|
||||
impl CaveCarver {
|
||||
fn get_thickness(&self, random: &mut RandomGenerator, is_nether: bool) -> f32 {
|
||||
if is_nether {
|
||||
(random.next_f32() * 2.0 + random.next_f32()) * 2.0
|
||||
} else {
|
||||
let mut thickness = random.next_f32() * 2.0 + random.next_f32();
|
||||
if random.next_bounded_i32(10) == 0 {
|
||||
thickness *= random.next_f32() * random.next_f32() * 3.0 + 1.0;
|
||||
}
|
||||
thickness
|
||||
}
|
||||
}
|
||||
|
||||
// let end_height = (y - height).floor() as i32 - 1.max(min_y as i32 + 1);
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn create_room(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk,
|
||||
x: f64,
|
||||
y: f64,
|
||||
z: f64,
|
||||
thickness: f32,
|
||||
y_scale: f64,
|
||||
config: &CarverConfig,
|
||||
floor_level: f64,
|
||||
is_nether: bool,
|
||||
) {
|
||||
let horizontal_radius = 1.5 + (PI / 2.0).sin() * thickness;
|
||||
let vertical_radius = horizontal_radius as f64 * y_scale;
|
||||
self.carve_ellipsoid(
|
||||
chunk,
|
||||
config,
|
||||
x + 1.0,
|
||||
y,
|
||||
z,
|
||||
horizontal_radius as f64,
|
||||
vertical_radius,
|
||||
floor_level,
|
||||
is_nether,
|
||||
);
|
||||
}
|
||||
|
||||
// for current_x in max_x..x_start {
|
||||
// let x_offset = chunk_pos::start_block_x(chunk_pos) + current_x;
|
||||
// let x_offwidth = (x_offset as f64 + 0.5 - x) / width;
|
||||
// for current_z in max_z..z_start {
|
||||
// let z_offset = chunk_pos::start_block_z(chunk_pos) + current_z;
|
||||
// let z_offwidth = (z_offset as f64 + 0.5 - z) / width;
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn create_tunnel(
|
||||
&self,
|
||||
config: &CarverConfig,
|
||||
chunk: &mut ProtoChunk,
|
||||
tunnel_seed: i64,
|
||||
mut x: f64,
|
||||
mut y: f64,
|
||||
mut z: f64,
|
||||
horizontal_radius_multiplier: f64,
|
||||
vertical_radius_multiplier: f64,
|
||||
thickness: f32,
|
||||
mut horizontal_rotation: f32,
|
||||
mut vertical_rotation: f32,
|
||||
step: i32,
|
||||
dist: i32,
|
||||
y_scale: f64,
|
||||
floor_level: f64,
|
||||
is_nether: bool,
|
||||
) {
|
||||
let mut random = RandomGenerator::Xoroshiro(
|
||||
pumpkin_util::random::xoroshiro128::Xoroshiro::from_seed(tunnel_seed as u64),
|
||||
);
|
||||
let split_point = random.next_bounded_i32(dist / 2) + dist / 4;
|
||||
let steep = random.next_bounded_i32(6) == 0;
|
||||
let mut y_rota = 0.0f32;
|
||||
let mut x_rota = 0.0f32;
|
||||
|
||||
// if x_offwidth * x_offwidth + z_offwidth * z_offwidth >= 1.0 {
|
||||
// continue;
|
||||
// }
|
||||
// for current_y in init_height..end_height {
|
||||
// let y_offwidth = (current_y as f64 - 0.5 - y) / height;
|
||||
// if Self::is_pos_excluded(
|
||||
// Vector3::new(x_offwidth, y_offwidth, z_offwidth),
|
||||
// floor_level,
|
||||
// ) {
|
||||
// continue;
|
||||
// }
|
||||
// mask.set(current_x, current_y, current_z);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
for current_step in step..dist {
|
||||
let horizontal_radius =
|
||||
1.5 + (PI * current_step as f32 / dist as f32).sin() * thickness;
|
||||
let vertical_radius = horizontal_radius as f64 * y_scale;
|
||||
let cos_x = vertical_rotation.cos();
|
||||
x += (horizontal_rotation.cos() * cos_x) as f64;
|
||||
y += vertical_rotation.sin() as f64;
|
||||
z += (horizontal_rotation.sin() * cos_x) as f64;
|
||||
|
||||
// fn carve_at_point(&self, chunk: &ProtoChunk, pos: Vector3<i32>, min_y: i8, height: u16) {
|
||||
// let state = chunk.get_block_state(&pos);
|
||||
vertical_rotation *= if steep { 0.92 } else { 0.7 };
|
||||
vertical_rotation += x_rota * 0.1;
|
||||
horizontal_rotation += y_rota * 0.1;
|
||||
x_rota *= 0.9;
|
||||
y_rota *= 0.75;
|
||||
x_rota += (random.next_f32() - random.next_f32()) * random.next_f32() * 2.0;
|
||||
y_rota += (random.next_f32() - random.next_f32()) * random.next_f32() * 4.0;
|
||||
|
||||
// // if state.block_id == block_state!("grass_block").block_id || state.block_id == block_state!("mycelium").block_id {
|
||||
// // TODO
|
||||
// // }
|
||||
// let state = self.get_state(pos, min_y, height);
|
||||
// }
|
||||
if current_step == split_point && thickness > 1.0 {
|
||||
self.create_tunnel(
|
||||
config,
|
||||
chunk,
|
||||
random.next_i64(),
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
horizontal_radius_multiplier,
|
||||
vertical_radius_multiplier,
|
||||
random.next_f32() * 0.5 + 0.5,
|
||||
horizontal_rotation - (PI / 2.0),
|
||||
vertical_rotation / 3.0,
|
||||
current_step,
|
||||
dist,
|
||||
1.0,
|
||||
floor_level,
|
||||
is_nether,
|
||||
);
|
||||
self.create_tunnel(
|
||||
config,
|
||||
chunk,
|
||||
random.next_i64(),
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
horizontal_radius_multiplier,
|
||||
vertical_radius_multiplier,
|
||||
random.next_f32() * 0.5 + 0.5,
|
||||
horizontal_rotation + (PI / 2.0),
|
||||
vertical_rotation / 3.0,
|
||||
current_step,
|
||||
dist,
|
||||
1.0,
|
||||
floor_level,
|
||||
is_nether,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// fn get_state(&self, pos: Vector3<i32>, min_y: i8, height: u16) -> Option<BlockState> {
|
||||
// if pos.y <= self.lava_level.get_y(min_y, height) as i32 {
|
||||
// return Some(block_state!("lava"));
|
||||
// }
|
||||
// None
|
||||
// }
|
||||
if random.next_bounded_i32(4) != 0 {
|
||||
if !self.can_reach(chunk.x, chunk.z, x, z, current_step, dist, thickness) {
|
||||
return;
|
||||
}
|
||||
|
||||
// fn is_pos_excluded(scaled: Vector3<f64>, floor_y: f64) -> bool {
|
||||
// if scaled.y <= floor_y {
|
||||
// return true;
|
||||
// }
|
||||
// scaled.x * scaled.x + scaled.y * scaled.y + scaled.z * scaled.z >= 1.0
|
||||
// }
|
||||
self.carve_ellipsoid(
|
||||
chunk,
|
||||
config,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
horizontal_radius as f64 * horizontal_radius_multiplier,
|
||||
vertical_radius * vertical_radius_multiplier,
|
||||
floor_level,
|
||||
is_nether,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fn get_tunnel_width(random: &mut RandomGenerator) -> f32 {
|
||||
// let mut width = random.next_f32() * 2.0 + random.next_f32();
|
||||
// if random.next_bounded_i32(10) == 0 {
|
||||
// width *= random.next_f32() * random.next_f32() * 3.0 + 1.0;
|
||||
// }
|
||||
// width
|
||||
// }
|
||||
// }
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn can_reach(
|
||||
&self,
|
||||
chunk_x: i32,
|
||||
chunk_z: i32,
|
||||
x: f64,
|
||||
z: f64,
|
||||
step: i32,
|
||||
distance: i32,
|
||||
thickness: f32,
|
||||
) -> bool {
|
||||
let chunk_middle_x = (chunk_x << 4) + 8;
|
||||
let chunk_middle_z = (chunk_z << 4) + 8;
|
||||
let dx = x - chunk_middle_x as f64;
|
||||
let dz = z - chunk_middle_z as f64;
|
||||
let remaining = (distance - step) as f64;
|
||||
let rr = (thickness + 2.0 + 16.0) as f64;
|
||||
dx * dx + dz * dz - remaining * remaining <= rr * rr
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn carve_ellipsoid(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk,
|
||||
config: &CarverConfig,
|
||||
x: f64,
|
||||
y: f64,
|
||||
z: f64,
|
||||
horizontal_radius: f64,
|
||||
vertical_radius: f64,
|
||||
floor_level: f64,
|
||||
is_nether: bool,
|
||||
) {
|
||||
let center_x = (chunk.x << 4) as f64 + 8.0;
|
||||
let center_z = (chunk.z << 4) as f64 + 8.0;
|
||||
let max_delta = 16.0 + horizontal_radius * 2.0;
|
||||
|
||||
if (x - center_x).abs() > max_delta || (z - center_z).abs() > max_delta {
|
||||
return;
|
||||
}
|
||||
|
||||
let chunk_min_x = chunk.x << 4;
|
||||
let chunk_min_z = chunk.z << 4;
|
||||
|
||||
let min_x_index = ((x - horizontal_radius).floor() as i32 - chunk_min_x - 1).max(0);
|
||||
let max_x_index = ((x + horizontal_radius).floor() as i32 - chunk_min_x).min(15);
|
||||
|
||||
let min_y = ((y - vertical_radius).floor() as i32 - 1).max(chunk.bottom_y() as i32 + 1);
|
||||
let protected_blocks_on_top = 7;
|
||||
let max_y = ((y + vertical_radius).floor() as i32 + 1)
|
||||
.min(chunk.bottom_y() as i32 + chunk.height() as i32 - 1 - protected_blocks_on_top);
|
||||
|
||||
let min_z_index = ((z - horizontal_radius).floor() as i32 - chunk_min_z - 1).max(0);
|
||||
let max_z_index = ((z + horizontal_radius).floor() as i32 - chunk_min_z).min(15);
|
||||
|
||||
for x_index in min_x_index..=max_x_index {
|
||||
let world_x = chunk_min_x + x_index;
|
||||
let xd = (world_x as f64 + 0.5 - x) / horizontal_radius;
|
||||
|
||||
for z_index in min_z_index..=max_z_index {
|
||||
let world_z = chunk_min_z + z_index;
|
||||
let zd = (world_z as f64 + 0.5 - z) / horizontal_radius;
|
||||
|
||||
if xd * xd + zd * zd < 1.0 {
|
||||
let mut has_grass = false;
|
||||
for world_y in (min_y..=max_y).rev() {
|
||||
let yd = (world_y as f64 + 0.5 - y) / vertical_radius;
|
||||
|
||||
if !self.should_skip(xd, yd, zd, floor_level)
|
||||
&& !chunk.carving_mask.get(world_x, world_y, world_z)
|
||||
{
|
||||
chunk.carving_mask.set(world_x, world_y, world_z);
|
||||
has_grass |= self.carve_block(
|
||||
chunk, config, world_x, world_y, world_z, is_nether, has_grass,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn should_skip(&self, xd: f64, yd: f64, zd: f64, floor_level: f64) -> bool {
|
||||
if yd <= floor_level {
|
||||
true
|
||||
} else {
|
||||
xd * xd + yd * yd + zd * zd >= 1.0
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn carve_block(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk,
|
||||
config: &CarverConfig,
|
||||
x: i32,
|
||||
y: i32,
|
||||
z: i32,
|
||||
is_nether: bool,
|
||||
mut has_grass: bool,
|
||||
) -> bool {
|
||||
let local_y = y - chunk.bottom_y() as i32;
|
||||
let state_id = chunk.get_block_state_raw(x & 15, local_y, z & 15);
|
||||
let block = pumpkin_data::Block::from_state_id(state_id);
|
||||
|
||||
if block.id == pumpkin_data::Block::WATER.id || block.id == pumpkin_data::Block::LAVA.id {
|
||||
return false;
|
||||
}
|
||||
|
||||
if block.id == pumpkin_data::Block::GRASS_BLOCK.id
|
||||
|| block.id == pumpkin_data::Block::MYCELIUM.id
|
||||
{
|
||||
has_grass = true;
|
||||
}
|
||||
|
||||
// Only carve if it's replaceable
|
||||
if config.replaceable.1.contains(&block.id) {
|
||||
// Replace with air or lava
|
||||
let air = BlockState::from_id(pumpkin_data::Block::AIR.default_state.id);
|
||||
let lava = BlockState::from_id(pumpkin_data::Block::LAVA.default_state.id);
|
||||
|
||||
let lava_y = if is_nether {
|
||||
chunk.bottom_y() as i32 + 31
|
||||
} else {
|
||||
config
|
||||
.lava_level
|
||||
.get_y(chunk.bottom_y() as i16, chunk.height())
|
||||
};
|
||||
|
||||
if y <= lava_y {
|
||||
chunk.set_block_state(x & 15, local_y, z & 15, lava);
|
||||
} else {
|
||||
chunk.set_block_state(x & 15, local_y, z & 15, air);
|
||||
}
|
||||
|
||||
if has_grass {
|
||||
let down_y = y - 1;
|
||||
let local_down_y = down_y - chunk.bottom_y() as i32;
|
||||
if (0..chunk.height() as i32).contains(&local_down_y) {
|
||||
let down_state_id = chunk.get_block_state_raw(x & 15, local_down_y, z & 15);
|
||||
if pumpkin_data::Block::from_state_id(down_state_id).id
|
||||
== pumpkin_data::Block::DIRT.id
|
||||
{
|
||||
// dirt replacement skipped
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_height(p: &HeightProvider, random: &mut RandomGenerator, min_y: i8, height: u16) -> i32 {
|
||||
match p {
|
||||
HeightProvider::Uniform(p) => {
|
||||
let min = p.min_inclusive.get_y(min_y as i16, height);
|
||||
let max = p.max_inclusive.get_y(min_y as i16, height);
|
||||
random.next_inbetween_i32(min, max)
|
||||
}
|
||||
HeightProvider::Trapezoid(p) => {
|
||||
let i = p.min_inclusive.get_y(min_y as i16, height);
|
||||
let j = p.max_inclusive.get_y(min_y as i16, height);
|
||||
let plateau = p.plateau.unwrap_or(0);
|
||||
let k = j - i;
|
||||
if plateau >= k {
|
||||
random.next_inbetween_i32(i, j)
|
||||
} else {
|
||||
let l = (k - plateau) / 2;
|
||||
let m = k - l;
|
||||
i + random.next_inbetween_i32(0, m) + random.next_inbetween_i32(0, l)
|
||||
}
|
||||
}
|
||||
HeightProvider::VeryBiasedToBottom(p) => {
|
||||
let min = p.min_inclusive.get_y(min_y as i16, height);
|
||||
let max = p.max_inclusive.get_y(min_y as i16, height);
|
||||
let inner = p.inner.map_or(1, |n| n.get()) as i32;
|
||||
let min_rnd = random.next_inbetween_i32(min + inner, max);
|
||||
let max_rnd = random.next_inbetween_i32(min, min_rnd - 1);
|
||||
random.next_inbetween_i32(min, max_rnd - 1 + inner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,29 +1,41 @@
|
||||
// use pumpkin_util::math::vector3::Vector3;
|
||||
#[derive(Clone)]
|
||||
pub struct CarvingMask {
|
||||
min_y: i32,
|
||||
mask: Vec<u64>,
|
||||
}
|
||||
|
||||
// pub struct CarvingMask {
|
||||
// min_y: i8,
|
||||
// mask: Vec<Vector3<i32>>,
|
||||
// }
|
||||
impl CarvingMask {
|
||||
#[must_use]
|
||||
pub fn new(height: i32, min_y: i32) -> Self {
|
||||
// Each u64 stores 64 bits. Total bits needed: 16 * 16 * height.
|
||||
let total_bits = 16 * 16 * height;
|
||||
let num_u64s = (total_bits as usize).div_ceil(64);
|
||||
Self {
|
||||
min_y,
|
||||
mask: vec![0; num_u64s],
|
||||
}
|
||||
}
|
||||
|
||||
// impl CarvingMask {
|
||||
// pub fn new(height: i16, min_y: i8) -> Self {
|
||||
// Self {
|
||||
// min_y,
|
||||
// mask: vec![Vector3::new(0, 0, 0); 256 * height as usize],
|
||||
// }
|
||||
// }
|
||||
fn get_bit_index(&self, x: i32, y: i32, z: i32) -> usize {
|
||||
// x: 0..16, z: 0..16, y: min_y..min_y+height
|
||||
let rel_x = x & 15;
|
||||
let rel_z = z & 15;
|
||||
let rel_y = y - self.min_y;
|
||||
(rel_x | (rel_z << 4) | (rel_y << 8)) as usize
|
||||
}
|
||||
|
||||
// fn get_index(&self, offset_x: i32, y: i32, offset_z: i32) -> usize {
|
||||
// (offset_x & 0xF | (offset_z & 0xF) << 4 | (y - self.min_y as i32) << 8) as usize
|
||||
// }
|
||||
pub fn set(&mut self, x: i32, y: i32, z: i32) {
|
||||
let bit_index = self.get_bit_index(x, y, z);
|
||||
let u64_idx = bit_index / 64;
|
||||
let bit_in_u64 = bit_index % 64;
|
||||
self.mask[u64_idx] |= 1 << bit_in_u64;
|
||||
}
|
||||
|
||||
// pub fn set(&mut self, offset_x: i32, y: i32, offset_z: i32) {
|
||||
// let index = self.get_index(offset_x, y, offset_z);
|
||||
// self.mask[index] = Vector3::new(offset_x, y, offset_z)
|
||||
// }
|
||||
|
||||
// pub fn get(&mut self, offset_x: i32, y: i32, offset_z: i32) -> bool {
|
||||
// let index = self.get_index(offset_x, y, offset_z);
|
||||
// self.mask.get(index).is_some()
|
||||
// }
|
||||
// }
|
||||
#[must_use]
|
||||
pub fn get(&self, x: i32, y: i32, z: i32) -> bool {
|
||||
let bit_index = self.get_bit_index(x, y, z);
|
||||
let u64_idx = bit_index / 64;
|
||||
let bit_in_u64 = bit_index % 64;
|
||||
(self.mask[u64_idx] >> bit_in_u64) & 1 != 0
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,2 +1,97 @@
|
||||
mod cave;
|
||||
mod mask;
|
||||
pub mod canyon;
|
||||
pub mod cave;
|
||||
pub mod mask;
|
||||
|
||||
use crate::ProtoChunk;
|
||||
use crate::generation::generator::VanillaGenerator;
|
||||
use pumpkin_data::carver::{CANYON, CAVE, CAVE_EXTRA_UNDERGROUND, NETHER_CAVE};
|
||||
use pumpkin_data::carver::{CarverAdditionalConfig, CarverConfig};
|
||||
use pumpkin_util::math::vector2::Vector2;
|
||||
use pumpkin_util::random::{RandomGenerator, RandomImpl, get_carver_seed};
|
||||
|
||||
pub trait Carver {
|
||||
fn carve(
|
||||
&self,
|
||||
config: &CarverConfig,
|
||||
chunk: &mut ProtoChunk,
|
||||
random: &mut RandomGenerator,
|
||||
chunk_pos: &Vector2<i32>,
|
||||
carver_chunk_pos: &Vector2<i32>,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn carve(chunk: &mut ProtoChunk, generator: &VanillaGenerator) {
|
||||
let mut random = RandomGenerator::Xoroshiro(
|
||||
pumpkin_util::random::xoroshiro128::Xoroshiro::from_seed(generator.random_config.seed),
|
||||
);
|
||||
|
||||
// Vanilla applyCarvers uses a range of 8 chunks (17x17 area)
|
||||
let radius = 8;
|
||||
let chunk_x = chunk.x;
|
||||
let chunk_z = chunk.z;
|
||||
let chunk_pos = Vector2::new(chunk_x, chunk_z);
|
||||
|
||||
let overworld_carvers = [&CAVE, &CAVE_EXTRA_UNDERGROUND, &CANYON];
|
||||
|
||||
let nether_carvers = [&NETHER_CAVE];
|
||||
|
||||
let carvers_to_use = if generator.dimension == pumpkin_data::dimension::Dimension::OVERWORLD {
|
||||
&overworld_carvers[..]
|
||||
} else if generator.dimension == pumpkin_data::dimension::Dimension::THE_NETHER {
|
||||
&nether_carvers[..]
|
||||
} else {
|
||||
&[]
|
||||
};
|
||||
|
||||
let cave_carver = cave::CaveCarver;
|
||||
let canyon_carver = canyon::CanyonCarver;
|
||||
|
||||
for dx in -radius..=radius {
|
||||
for dz in -radius..=radius {
|
||||
let carver_x = chunk_x + dx;
|
||||
let carver_z = chunk_z + dz;
|
||||
let carver_chunk_pos = Vector2::new(carver_x, carver_z);
|
||||
|
||||
// In vanilla, carvers are per-biome. Here we use the hardcoded list but
|
||||
// maintain the random seed logic.
|
||||
for (index, &config) in carvers_to_use.iter().enumerate() {
|
||||
let seed = get_carver_seed(
|
||||
&mut random,
|
||||
generator.random_config.seed + index as u64,
|
||||
carver_x,
|
||||
carver_z,
|
||||
);
|
||||
let mut carver_random = RandomGenerator::Xoroshiro(
|
||||
pumpkin_util::random::xoroshiro128::Xoroshiro::from_seed(seed),
|
||||
);
|
||||
|
||||
if should_carve(config, &mut carver_random) {
|
||||
match config.additional {
|
||||
CarverAdditionalConfig::Cave(_) | CarverAdditionalConfig::NetherCave(_) => {
|
||||
cave_carver.carve(
|
||||
config,
|
||||
chunk,
|
||||
&mut carver_random,
|
||||
&chunk_pos,
|
||||
&carver_chunk_pos,
|
||||
);
|
||||
}
|
||||
CarverAdditionalConfig::Canyon(_) => {
|
||||
canyon_carver.carve(
|
||||
config,
|
||||
chunk,
|
||||
&mut carver_random,
|
||||
&chunk_pos,
|
||||
&carver_chunk_pos,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn should_carve(config: &CarverConfig, random: &mut RandomGenerator) -> bool {
|
||||
random.next_f32() <= config.probability
|
||||
}
|
||||
|
||||
@ -168,6 +168,7 @@ pub struct ProtoChunk {
|
||||
bottom_y: i8,
|
||||
pub stage: StagedChunkEnum,
|
||||
pub light: ChunkLight,
|
||||
pub carving_mask: crate::generation::carver::mask::CarvingMask,
|
||||
/// Block entities pending creation when the chunk is finalized.
|
||||
/// These are created from structure templates during world generation.
|
||||
pub pending_block_entities: Vec<NbtCompound>,
|
||||
@ -246,6 +247,10 @@ impl ProtoChunk {
|
||||
.map(|_| LightContainer::new_empty(0))
|
||||
.collect(),
|
||||
},
|
||||
carving_mask: crate::generation::carver::mask::CarvingMask::new(
|
||||
height as i32,
|
||||
dimension.min_y,
|
||||
),
|
||||
pending_block_entities: Vec::new(),
|
||||
}
|
||||
}
|
||||
@ -630,6 +635,14 @@ impl ProtoChunk {
|
||||
self.stage = StagedChunkEnum::Surface;
|
||||
}
|
||||
|
||||
pub fn step_to_carvers(&mut self, generator: &super::generator::VanillaGenerator) {
|
||||
debug_assert_eq!(self.stage, StagedChunkEnum::Surface);
|
||||
|
||||
super::carver::carve(self, generator);
|
||||
|
||||
self.stage = StagedChunkEnum::Carvers;
|
||||
}
|
||||
|
||||
pub fn populate_biomes(
|
||||
&mut self,
|
||||
generator: &super::generator::VanillaGenerator,
|
||||
|
||||
@ -94,6 +94,7 @@ pub struct BedrockClient {
|
||||
//input_sequence_number: AtomicU32,
|
||||
received_sequences: Mutex<HashSet<u32>>,
|
||||
pending_acks: Mutex<Vec<u32>>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
unacked_outgoing_frames: Mutex<HashMap<u32, (u8, Vec<u8>, std::time::Instant)>>,
|
||||
expected_order_index: Mutex<HashMap<u8, u32>>,
|
||||
highest_sequence_index: Mutex<HashMap<u8, u32>>,
|
||||
@ -191,8 +192,7 @@ impl BedrockClient {
|
||||
.await
|
||||
.write_packet(&packet_data, client.address, &client.socket)
|
||||
.await
|
||||
{
|
||||
if !client.close_token.is_cancelled() {
|
||||
&& !client.close_token.is_cancelled() {
|
||||
warn!("Failed to send packet to client: {err}",);
|
||||
client.close_token.cancel();
|
||||
break;
|
||||
@ -200,7 +200,6 @@ impl BedrockClient {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -489,8 +488,8 @@ impl BedrockClient {
|
||||
debug!("Received NACK for sequences: {:?}", nack.sequences);
|
||||
let unacked = self.unacked_outgoing_frames.lock().await;
|
||||
for seq in &nack.sequences {
|
||||
if let Some((_id, data, _timestamp)) = unacked.get(seq) {
|
||||
if let Err(err) = self
|
||||
if let Some((_id, data, _timestamp)) = unacked.get(seq)
|
||||
&& let Err(err) = self
|
||||
.network_writer
|
||||
.lock()
|
||||
.await
|
||||
@ -501,7 +500,6 @@ impl BedrockClient {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_frame_set(
|
||||
self: &Arc<Self>,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user