chore: move chunk gen settings from runtime to compile time parsing
Bin size decreased, Startup time decreased by a lot in debug, lower memory usage
This commit is contained in:
parent
2f29fcef8d
commit
835262a85a
@ -27,6 +27,9 @@ separated_literal_suffix = "deny"
|
||||
semicolon_outside_block = "deny"
|
||||
redundant_test_prefix = "deny"
|
||||
non_zero_suggestions = "deny"
|
||||
dbg_macro = "deny"
|
||||
rc_buffer = "deny"
|
||||
verbose_file_reads = "deny"
|
||||
string_lit_chars_any = "deny"
|
||||
use_self = "deny"
|
||||
useless_let_if_seq = "deny"
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -30,12 +30,12 @@ pub(crate) fn build() -> TokenStream {
|
||||
let id_lit = LitInt::new(&raw_value.id.to_string(), Span::call_site());
|
||||
let default_value_lit = raw_value.default_value;
|
||||
|
||||
constant_defs.push(quote! {
|
||||
constant_defs.push(quote!(
|
||||
pub const #constant_ident: Self = Self {
|
||||
id: #id_lit,
|
||||
default_value: #default_value_lit,
|
||||
};
|
||||
});
|
||||
));
|
||||
}
|
||||
|
||||
quote! {
|
||||
|
||||
@ -13,6 +13,7 @@ mod attributes;
|
||||
mod biome;
|
||||
mod bitsets;
|
||||
mod block;
|
||||
mod chunk_gen_settings;
|
||||
mod chunk_status;
|
||||
mod composter_increase_chance;
|
||||
mod damage_type;
|
||||
@ -54,9 +55,7 @@ mod world_event;
|
||||
pub const OUT_DIR: &str = "src/generated";
|
||||
|
||||
pub fn main() {
|
||||
if let Err(e) = fs::create_dir_all(OUT_DIR) {
|
||||
eprintln!("Failed to create output directory {OUT_DIR}: {e}");
|
||||
}
|
||||
fs::create_dir_all(OUT_DIR).expect("Failed to create output directory");
|
||||
|
||||
type BuilderFn = fn() -> TokenStream;
|
||||
|
||||
@ -85,6 +84,7 @@ pub fn main() {
|
||||
(spawn_egg::build, "spawn_egg.rs"),
|
||||
(block::build, "block.rs"),
|
||||
(item::build, "item.rs"),
|
||||
(chunk_gen_settings::build, "chunk_gen_settings.rs"),
|
||||
(fluid::build, "fluid.rs"),
|
||||
(entity_status::build, "entity_status.rs"),
|
||||
(tag::build, "tag.rs"),
|
||||
@ -110,8 +110,14 @@ pub fn main() {
|
||||
|
||||
build_functions.par_iter().for_each(|(build_fn, file)| {
|
||||
let raw_code = build_fn().to_string();
|
||||
let final_code = format_code(&raw_code);
|
||||
write_generated_file(&final_code.unwrap_or(raw_code), file);
|
||||
|
||||
let header = "/* This file is generated. Do not edit manually. */\n";
|
||||
|
||||
let final_code = format_code(&raw_code)
|
||||
.map(|formatted| format!("{}{}", header, formatted))
|
||||
.unwrap_or_else(|_| format!("{}{}", header, raw_code));
|
||||
|
||||
write_generated_file(&final_code, file);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
536
pumpkin-data/build/chunk_gen_settings.rs
Normal file
536
pumpkin-data/build/chunk_gen_settings.rs
Normal file
@ -0,0 +1,536 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{ToTokens, format_ident, quote};
|
||||
use serde::Deserialize;
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
fs,
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct BlockStateCodecStruct {
|
||||
#[serde(rename = "Name")]
|
||||
pub name: String,
|
||||
#[serde(rename = "Properties")]
|
||||
pub properties: Option<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct GenerationSettingsStruct {
|
||||
#[serde(default)]
|
||||
pub aquifers_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub ore_veins_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub legacy_random_source: bool,
|
||||
pub sea_level: i32,
|
||||
pub default_fluid: BlockStateCodecStruct,
|
||||
pub default_block: BlockStateCodecStruct,
|
||||
#[serde(rename = "noise")]
|
||||
pub shape: GenerationShapeConfigStruct,
|
||||
pub surface_rule: MaterialRuleStruct,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct GenerationShapeConfigStruct {
|
||||
pub min_y: i8,
|
||||
pub height: u16,
|
||||
pub size_horizontal: u8,
|
||||
pub size_vertical: u8,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum MaterialRuleStruct {
|
||||
#[serde(rename = "minecraft:block")]
|
||||
Block { result_state: BlockStateCodecStruct },
|
||||
#[serde(rename = "minecraft:sequence")]
|
||||
Sequence { sequence: Vec<MaterialRuleStruct> },
|
||||
#[serde(rename = "minecraft:condition")]
|
||||
Condition {
|
||||
if_true: MaterialConditionStruct,
|
||||
then_run: Box<MaterialRuleStruct>,
|
||||
},
|
||||
#[serde(rename = "minecraft:bandlands")]
|
||||
Badlands,
|
||||
#[serde(other)]
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum MaterialConditionStruct {
|
||||
#[serde(rename = "minecraft:biome")]
|
||||
Biome { biome_is: Vec<String> },
|
||||
#[serde(rename = "minecraft:noise_threshold")]
|
||||
NoiseThreshold {
|
||||
noise: String,
|
||||
min_threshold: f64,
|
||||
max_threshold: f64,
|
||||
},
|
||||
#[serde(rename = "minecraft:vertical_gradient")]
|
||||
VerticalGradient {
|
||||
random_name: String,
|
||||
true_at_and_below: YOffsetStruct,
|
||||
false_at_and_above: YOffsetStruct,
|
||||
},
|
||||
#[serde(rename = "minecraft:y_above")]
|
||||
YAbove {
|
||||
anchor: YOffsetStruct,
|
||||
surface_depth_multiplier: i32,
|
||||
add_stone_depth: bool,
|
||||
},
|
||||
#[serde(rename = "minecraft:water")]
|
||||
Water {
|
||||
offset: i32,
|
||||
surface_depth_multiplier: i32,
|
||||
add_stone_depth: bool,
|
||||
},
|
||||
#[serde(rename = "minecraft:temperature")]
|
||||
Temperature,
|
||||
#[serde(rename = "minecraft:steep")]
|
||||
Steep,
|
||||
#[serde(rename = "minecraft:not")]
|
||||
Not {
|
||||
invert: Box<MaterialConditionStruct>,
|
||||
},
|
||||
#[serde(rename = "minecraft:hole")]
|
||||
Hole,
|
||||
#[serde(rename = "minecraft:above_preliminary_surface")]
|
||||
AbovePreliminarySurface,
|
||||
#[serde(rename = "minecraft:stone_depth")]
|
||||
StoneDepth {
|
||||
offset: i32,
|
||||
add_surface_depth: bool,
|
||||
secondary_depth_range: i32,
|
||||
surface_type: String, // "ceiling" or "floor"
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum YOffsetStruct {
|
||||
Absolute { absolute: i16 },
|
||||
AboveBottom { above_bottom: i8 },
|
||||
BelowTop { below_top: i8 },
|
||||
}
|
||||
|
||||
// --- ToTokens Implementations ---
|
||||
|
||||
impl ToTokens for BlockStateCodecStruct {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let name = &self.name.strip_prefix("minecraft:").unwrap_or(&self.name);
|
||||
|
||||
let props_gen = if let Some(props) = &self.properties {
|
||||
let keys = props.keys();
|
||||
let values = props.values();
|
||||
quote!(Some(&[#((#keys, #values)),*]))
|
||||
} else {
|
||||
quote!(None)
|
||||
};
|
||||
|
||||
tokens.extend(quote!(
|
||||
BlockBlueprint {
|
||||
name: #name,
|
||||
properties: #props_gen,
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for GenerationSettingsStruct {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let aquifers = self.aquifers_enabled;
|
||||
let ores = self.ore_veins_enabled;
|
||||
let legacy = self.legacy_random_source;
|
||||
let sea_level = self.sea_level;
|
||||
let fluid = &self.default_fluid;
|
||||
let block = &self.default_block;
|
||||
let shape = &self.shape;
|
||||
let rule = &self.surface_rule;
|
||||
|
||||
tokens.extend(quote!(
|
||||
GenerationSettings {
|
||||
aquifers_enabled: #aquifers,
|
||||
ore_veins_enabled: #ores,
|
||||
legacy_random_source: #legacy,
|
||||
sea_level: #sea_level,
|
||||
default_fluid: #fluid,
|
||||
shape: #shape,
|
||||
surface_rule: #rule,
|
||||
default_block: #block,
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for GenerationShapeConfigStruct {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let min_y = self.min_y;
|
||||
let height = self.height;
|
||||
let hor = self.size_horizontal;
|
||||
let ver = self.size_vertical;
|
||||
tokens.extend(quote!(
|
||||
GenerationShapeConfig { min_y: #min_y, height: #height, size_horizontal: #hor, size_vertical: #ver }
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for YOffsetStruct {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self {
|
||||
Self::Absolute { absolute } => {
|
||||
tokens.extend(quote!(YOffset::Absolute(pumpkin_util::y_offset::Absolute { absolute: #absolute })));
|
||||
}
|
||||
Self::AboveBottom { above_bottom } => {
|
||||
tokens.extend(quote!(YOffset::AboveBottom(pumpkin_util::y_offset::AboveBottom { above_bottom: #above_bottom })));
|
||||
}
|
||||
Self::BelowTop { below_top } => {
|
||||
tokens.extend(quote!(YOffset::BelowTop(pumpkin_util::y_offset::BelowTop { below_top: #below_top })));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for MaterialConditionStruct {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self {
|
||||
Self::Biome { biome_is } => {
|
||||
let biomes = biome_is
|
||||
.iter()
|
||||
.map(|b| b.strip_prefix("minecraft:").unwrap_or(b).to_uppercase());
|
||||
let biome_refs: Vec<TokenStream> = biomes
|
||||
.map(|b| {
|
||||
let ident = format_ident!("{}", b);
|
||||
quote!(&crate::biome::Biome::#ident)
|
||||
})
|
||||
.collect();
|
||||
|
||||
tokens.extend(quote!(
|
||||
MaterialCondition::Biome(BiomeMaterialCondition {
|
||||
biome_is: &[#(#biome_refs),*],
|
||||
})
|
||||
));
|
||||
}
|
||||
Self::NoiseThreshold {
|
||||
noise,
|
||||
min_threshold,
|
||||
max_threshold,
|
||||
} => {
|
||||
tokens.extend(quote!(
|
||||
MaterialCondition::NoiseThreshold(NoiseThresholdMaterialCondition {
|
||||
noise: #noise,
|
||||
min_threshold: #min_threshold,
|
||||
max_threshold: #max_threshold,
|
||||
})
|
||||
));
|
||||
}
|
||||
Self::VerticalGradient {
|
||||
random_name,
|
||||
true_at_and_below,
|
||||
false_at_and_above,
|
||||
} => {
|
||||
tokens.extend(quote!(
|
||||
MaterialCondition::VerticalGradient(VerticalGradientMaterialCondition {
|
||||
random_name: #random_name,
|
||||
true_at_and_below: #true_at_and_below,
|
||||
false_at_and_above: #false_at_and_above,
|
||||
})
|
||||
));
|
||||
}
|
||||
Self::YAbove {
|
||||
anchor,
|
||||
surface_depth_multiplier,
|
||||
add_stone_depth,
|
||||
} => {
|
||||
tokens.extend(quote!(
|
||||
MaterialCondition::YAbove(AboveYMaterialCondition {
|
||||
anchor: #anchor,
|
||||
surface_depth_multiplier: #surface_depth_multiplier,
|
||||
add_stone_depth: #add_stone_depth,
|
||||
})
|
||||
));
|
||||
}
|
||||
Self::Water {
|
||||
offset,
|
||||
surface_depth_multiplier,
|
||||
add_stone_depth,
|
||||
} => {
|
||||
tokens.extend(quote!(
|
||||
MaterialCondition::Water(WaterMaterialCondition {
|
||||
offset: #offset,
|
||||
surface_depth_multiplier: #surface_depth_multiplier,
|
||||
add_stone_depth: #add_stone_depth,
|
||||
})
|
||||
));
|
||||
}
|
||||
Self::Temperature => {
|
||||
tokens.extend(quote!(MaterialCondition::Temperature));
|
||||
}
|
||||
Self::Steep => {
|
||||
tokens.extend(quote!(MaterialCondition::Steep));
|
||||
}
|
||||
Self::Not { invert } => {
|
||||
tokens.extend(quote!(
|
||||
MaterialCondition::Not(NotMaterialCondition {
|
||||
invert: &#invert,
|
||||
})
|
||||
));
|
||||
}
|
||||
Self::Hole => {
|
||||
tokens.extend(quote!(MaterialCondition::Hole(HoleMaterialCondition)));
|
||||
}
|
||||
Self::AbovePreliminarySurface => {
|
||||
tokens.extend(quote!(MaterialCondition::AbovePreliminarySurface(
|
||||
SurfaceMaterialCondition
|
||||
)));
|
||||
}
|
||||
Self::StoneDepth {
|
||||
offset,
|
||||
add_surface_depth,
|
||||
secondary_depth_range,
|
||||
surface_type,
|
||||
} => {
|
||||
let surface_type_token = match surface_type.as_str() {
|
||||
"ceiling" => quote!(
|
||||
pumpkin_util::math::vertical_surface_type::VerticalSurfaceType::Ceiling
|
||||
),
|
||||
"floor" => quote!(
|
||||
pumpkin_util::math::vertical_surface_type::VerticalSurfaceType::Floor
|
||||
),
|
||||
_ => quote!(
|
||||
pumpkin_util::math::vertical_surface_type::VerticalSurfaceType::Floor
|
||||
),
|
||||
};
|
||||
|
||||
tokens.extend(quote!(
|
||||
MaterialCondition::StoneDepth(StoneDepthMaterialCondition {
|
||||
offset: #offset,
|
||||
add_surface_depth: #add_surface_depth,
|
||||
secondary_depth_range: #secondary_depth_range,
|
||||
surface_type: #surface_type_token,
|
||||
})
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for MaterialRuleStruct {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self {
|
||||
Self::Block { result_state } => {
|
||||
tokens.extend(quote!(
|
||||
MaterialRule::Block(BlockMaterialRule {
|
||||
result_state: #result_state
|
||||
})
|
||||
));
|
||||
}
|
||||
Self::Sequence { sequence } => {
|
||||
tokens.extend(quote!(
|
||||
MaterialRule::Sequence(SequenceMaterialRule {
|
||||
sequence: &[#(#sequence),*]
|
||||
})
|
||||
));
|
||||
}
|
||||
Self::Condition { if_true, then_run } => {
|
||||
tokens.extend(quote!(
|
||||
MaterialRule::Condition(ConditionMaterialRule {
|
||||
if_true: #if_true,
|
||||
then_run: &#then_run
|
||||
})
|
||||
));
|
||||
}
|
||||
Self::Badlands => {
|
||||
tokens.extend(quote!(MaterialRule::Badlands(BadLandsMaterialRule)));
|
||||
}
|
||||
Self::Unsupported => {
|
||||
tokens.extend(quote!(MaterialRule::Unsupported));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn build() -> TokenStream {
|
||||
println!("cargo:rerun-if-changed=../assets/chunk_gen_settings.json");
|
||||
|
||||
let json: BTreeMap<String, GenerationSettingsStruct> =
|
||||
serde_json::from_str(&fs::read_to_string("../assets/chunk_gen_settings.json").unwrap())
|
||||
.expect("Failed to parse settings.json");
|
||||
|
||||
let mut const_defs = TokenStream::new();
|
||||
|
||||
for (name, settings) in &json {
|
||||
let upper_name = name.to_uppercase();
|
||||
let const_name = format_ident!("{}", upper_name);
|
||||
|
||||
const_defs.extend(quote!(
|
||||
pub const #const_name: GenerationSettings = #settings;
|
||||
));
|
||||
}
|
||||
|
||||
quote!(
|
||||
use crate::dimension::Dimension;
|
||||
use std::{cell::RefCell, num::NonZeroUsize};
|
||||
use pumpkin_util::random::RandomDeriver;
|
||||
use pumpkin_util::y_offset::YOffset;
|
||||
use crate::biome::Biome;
|
||||
use pumpkin_util::y_offset::Absolute;
|
||||
|
||||
pub struct BlockBlueprint {
|
||||
pub name: &'static str,
|
||||
pub properties: Option<&'static [(&'static str, &'static str)]>,
|
||||
}
|
||||
|
||||
pub struct GenerationSettings {
|
||||
pub aquifers_enabled: bool,
|
||||
pub ore_veins_enabled: bool,
|
||||
pub legacy_random_source: bool,
|
||||
pub sea_level: i32,
|
||||
pub default_fluid: BlockBlueprint,
|
||||
pub shape: GenerationShapeConfig,
|
||||
pub surface_rule: MaterialRule,
|
||||
pub default_block: BlockBlueprint,
|
||||
}
|
||||
|
||||
pub struct GenerationShapeConfig {
|
||||
pub min_y: i8,
|
||||
pub height: u16,
|
||||
pub size_horizontal: u8,
|
||||
pub size_vertical: u8,
|
||||
}
|
||||
|
||||
impl GenerationShapeConfig {
|
||||
#[inline] #[must_use]
|
||||
pub fn vertical_cell_block_count(&self) -> u8 { self.size_vertical << 2 }
|
||||
|
||||
#[inline] #[must_use]
|
||||
pub fn horizontal_cell_block_count(&self) -> u8 { self.size_horizontal << 2 }
|
||||
|
||||
#[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, bottom_y: i8, top_y: u16) -> Self {
|
||||
let new_min = self.min_y.max(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(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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BlockMaterialRule {
|
||||
pub result_state: BlockBlueprint,
|
||||
}
|
||||
|
||||
pub struct SequenceMaterialRule {
|
||||
pub sequence: &'static [MaterialRule],
|
||||
}
|
||||
|
||||
pub struct ConditionMaterialRule {
|
||||
pub if_true: MaterialCondition,
|
||||
pub then_run: &'static MaterialRule,
|
||||
}
|
||||
|
||||
pub struct BadLandsMaterialRule;
|
||||
|
||||
pub enum MaterialRule {
|
||||
Block(BlockMaterialRule),
|
||||
Sequence(SequenceMaterialRule),
|
||||
Condition(ConditionMaterialRule),
|
||||
Badlands(BadLandsMaterialRule),
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
|
||||
pub struct BiomeMaterialCondition {
|
||||
pub biome_is: &'static [&'static Biome],
|
||||
}
|
||||
|
||||
pub struct NoiseThresholdMaterialCondition {
|
||||
pub noise: &'static str,
|
||||
pub min_threshold: f64,
|
||||
pub max_threshold: f64,
|
||||
}
|
||||
|
||||
pub struct VerticalGradientMaterialCondition {
|
||||
pub random_name: &'static str,
|
||||
pub true_at_and_below: YOffset,
|
||||
pub false_at_and_above: YOffset,
|
||||
}
|
||||
|
||||
pub struct AboveYMaterialCondition {
|
||||
pub anchor: YOffset,
|
||||
pub surface_depth_multiplier: i32,
|
||||
pub add_stone_depth: bool,
|
||||
}
|
||||
|
||||
pub struct WaterMaterialCondition {
|
||||
pub offset: i32,
|
||||
pub surface_depth_multiplier: i32,
|
||||
pub add_stone_depth: bool,
|
||||
}
|
||||
|
||||
pub struct HoleMaterialCondition;
|
||||
|
||||
pub struct NotMaterialCondition {
|
||||
pub invert: &'static MaterialCondition,
|
||||
}
|
||||
|
||||
pub struct SurfaceMaterialCondition;
|
||||
|
||||
pub struct StoneDepthMaterialCondition {
|
||||
pub offset: i32,
|
||||
pub add_surface_depth: bool,
|
||||
pub secondary_depth_range: i32,
|
||||
pub surface_type: pumpkin_util::math::vertical_surface_type::VerticalSurfaceType,
|
||||
}
|
||||
|
||||
pub enum MaterialCondition {
|
||||
Biome(BiomeMaterialCondition),
|
||||
NoiseThreshold(NoiseThresholdMaterialCondition),
|
||||
VerticalGradient(VerticalGradientMaterialCondition),
|
||||
YAbove(AboveYMaterialCondition),
|
||||
Water(WaterMaterialCondition),
|
||||
Temperature,
|
||||
Steep,
|
||||
Not(NotMaterialCondition),
|
||||
Hole(HoleMaterialCondition),
|
||||
AbovePreliminarySurface(SurfaceMaterialCondition),
|
||||
StoneDepth(StoneDepthMaterialCondition),
|
||||
}
|
||||
|
||||
impl GenerationSettings {
|
||||
#const_defs
|
||||
|
||||
pub fn from_dimension(dimension: &Dimension) -> &'static Self {
|
||||
if dimension == &Dimension::OVERWORLD {
|
||||
&Self::OVERWORLD
|
||||
} else if dimension == &Dimension::THE_NETHER {
|
||||
&Self::NETHER
|
||||
} else {
|
||||
&Self::END
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -102,7 +102,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
});
|
||||
}
|
||||
|
||||
quote! {
|
||||
quote!(
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Dimension {
|
||||
pub id: u8,
|
||||
@ -134,5 +134,5 @@ pub(crate) fn build() -> TokenStream {
|
||||
}
|
||||
}
|
||||
impl Eq for Dimension {}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@ -771,13 +771,13 @@ pub(crate) fn build() -> TokenStream {
|
||||
|
||||
impl ToResourceLocation for &'static Fluid {
|
||||
fn to_resource_location(&self) -> ResourceLocation {
|
||||
ResourceLocation::vanilla(self.name)
|
||||
format!("minecraft:{}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromResourceLocation for &'static Fluid {
|
||||
fn from_resource_location(resource_location: &ResourceLocation) -> Option<Self> {
|
||||
Fluid::from_registry_key(&resource_location.path)
|
||||
Fluid::from_registry_key(&resource_location.strip_prefix(resource_location).unwrap_or(resource_location))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -92,14 +92,13 @@ pub(crate) fn build() -> TokenStream {
|
||||
|
||||
static_regs.iter().map(|static_reg| {
|
||||
let registry_id = if static_reg.registry_id.contains(':') {
|
||||
ResourceLocation::from(static_reg.registry_id)
|
||||
static_reg.registry_id.to_string()
|
||||
} else {
|
||||
ResourceLocation::vanilla(static_reg.registry_id)
|
||||
format!("minecraft:{}", static_reg.registry_id)
|
||||
};
|
||||
|
||||
let registry_entries = static_reg.entries.iter().map(|entry| {
|
||||
let entry_id =
|
||||
ResourceLocation::vanilla(entry.name);
|
||||
let entry_id = format!("minecraft:{}", entry.name);
|
||||
|
||||
RegistryEntryData {
|
||||
entry_id,
|
||||
|
||||
@ -91,13 +91,17 @@ impl Taggable for Block {
|
||||
|
||||
impl ToResourceLocation for &'static Block {
|
||||
fn to_resource_location(&self) -> ResourceLocation {
|
||||
ResourceLocation::vanilla(self.name)
|
||||
format!("minecraft:{}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromResourceLocation for &'static Block {
|
||||
fn from_resource_location(resource_location: &ResourceLocation) -> Option<Self> {
|
||||
Block::from_registry_key(&resource_location.path)
|
||||
Block::from_registry_key(
|
||||
resource_location
|
||||
.strip_prefix(resource_location)
|
||||
.unwrap_or(resource_location),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -20,6 +20,7 @@ pub mod registry;
|
||||
#[path = "generated/screen.rs"]
|
||||
pub mod screen;
|
||||
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[path = "generated/particle.rs"]
|
||||
pub mod particle;
|
||||
@ -193,3 +194,7 @@ pub use block_state::BlockState;
|
||||
pub use block_state::BlockStateRef;
|
||||
pub use blocks::Block;
|
||||
pub use collision_shape::CollisionShape;
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[path = "generated/chunk_gen_settings.rs"]
|
||||
pub mod chunk_gen_settings;
|
||||
|
||||
@ -51,7 +51,6 @@ macro_rules! define_get_number_be {
|
||||
impl<R: Read + Seek> NbtReadHelper<R> {
|
||||
pub fn skip_bytes(&mut self, count: i64) -> Result<()> {
|
||||
self.reader
|
||||
.by_ref()
|
||||
.seek(SeekFrom::Current(count))
|
||||
.map_err(Error::Incomplete)?;
|
||||
Ok(())
|
||||
|
||||
@ -28,34 +28,9 @@ pub struct RegistryEntry {
|
||||
pub data: Option<Box<[u8]>>,
|
||||
}
|
||||
|
||||
// TODO: No unwraps
|
||||
impl RegistryEntry {
|
||||
#[must_use]
|
||||
pub const fn new(entry_id: ResourceLocation, data: Option<Box<[u8]>>) -> Self {
|
||||
Self { entry_id, data }
|
||||
}
|
||||
|
||||
pub fn from_nbt(name: &str, nbt: &impl Serialize) -> Self {
|
||||
let mut data_buf = Vec::new();
|
||||
pumpkin_nbt::serializer::to_bytes_unnamed(nbt, &mut data_buf).unwrap();
|
||||
Self {
|
||||
entry_id: ResourceLocation::vanilla(name),
|
||||
data: Some(data_buf.into_boxed_slice()),
|
||||
}
|
||||
}
|
||||
#[must_use]
|
||||
pub fn none(name: &str) -> Self {
|
||||
Self {
|
||||
entry_id: ResourceLocation::vanilla(name),
|
||||
data: None,
|
||||
}
|
||||
}
|
||||
pub fn from_nbt_custom(name: &str, nbt: &impl Serialize) -> Self {
|
||||
let mut data_buf = Vec::new();
|
||||
pumpkin_nbt::serializer::to_bytes_unnamed(nbt, &mut data_buf).unwrap();
|
||||
Self {
|
||||
entry_id: ResourceLocation::pumpkin(name),
|
||||
data: Some(data_buf.into_boxed_slice()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,6 @@ use pumpkin_data::{
|
||||
tag::{RegistryKey, get_registry_key_tags},
|
||||
};
|
||||
use pumpkin_macros::java_packet;
|
||||
use pumpkin_util::resource_location::ResourceLocation;
|
||||
use pumpkin_util::version::MinecraftVersion;
|
||||
|
||||
#[java_packet(CONFIG_UPDATE_TAGS)]
|
||||
@ -31,9 +30,7 @@ impl ClientPacket for CUpdateTags<'_> {
|
||||
) -> Result<(), WritingError> {
|
||||
let mut write = write;
|
||||
write.write_list(self.tags, |p, registry_key| {
|
||||
p.write_resource_location(&ResourceLocation::vanilla(
|
||||
registry_key.identifier_string(),
|
||||
))?;
|
||||
p.write_string(&format!("minecraft:{}", registry_key.identifier_string(),))?;
|
||||
|
||||
let values = get_registry_key_tags(*version, *registry_key).unwrap();
|
||||
p.write_var_int(&values.len().try_into().map_err(|_| {
|
||||
|
||||
@ -37,7 +37,7 @@ impl ClientPacket for CStopSound {
|
||||
(Some(category), Some(sound_id)) => {
|
||||
write.write_u8(CATEGORY_AND_SOUND)?;
|
||||
write.write_var_int(&VarInt(category as i32))?;
|
||||
write.write_resource_location(sound_id)
|
||||
write.write_string(sound_id)
|
||||
}
|
||||
(Some(category), None) => {
|
||||
write.write_u8(CATEGORY_ONLY)?;
|
||||
@ -45,7 +45,7 @@ impl ClientPacket for CStopSound {
|
||||
}
|
||||
(None, Some(sound_id)) => {
|
||||
write.write_u8(SOUND_ONLY)?;
|
||||
write.write_resource_location(sound_id)
|
||||
write.write_string(sound_id)
|
||||
}
|
||||
(None, None) => write.write_u8(NO_CATEGORY_NO_SOUND),
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ pub struct SConfigCookieResponse {
|
||||
impl ServerPacket for SConfigCookieResponse {
|
||||
fn read(read: impl Read) -> Result<Self, ReadingError> {
|
||||
let mut read = read;
|
||||
let key = read.get_resource_location()?;
|
||||
let key = read.get_string()?;
|
||||
let has_payload = read.get_bool()?;
|
||||
|
||||
if !has_payload {
|
||||
|
||||
@ -26,7 +26,7 @@ impl ServerPacket for SPluginMessage {
|
||||
fn read(read: impl Read) -> Result<Self, ReadingError> {
|
||||
let mut read = read;
|
||||
Ok(Self {
|
||||
channel: read.get_resource_location()?,
|
||||
channel: read.get_string()?,
|
||||
data: read.read_remaining_to_boxed_slice(MAX_PAYLOAD_SIZE)?,
|
||||
})
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ impl ServerPacket for SLoginCookieResponse {
|
||||
fn read(read: impl Read) -> Result<Self, ReadingError> {
|
||||
let mut read = read;
|
||||
|
||||
let key = read.get_resource_location()?;
|
||||
let key = read.get_string()?;
|
||||
let has_payload = read.get_bool()?;
|
||||
|
||||
if !has_payload {
|
||||
|
||||
@ -23,7 +23,7 @@ impl ServerPacket for SCookieResponse {
|
||||
fn read(read: impl Read) -> Result<Self, ReadingError> {
|
||||
let mut read = read;
|
||||
|
||||
let key = read.get_resource_location()?;
|
||||
let key = read.get_string()?;
|
||||
let has_payload = read.get_bool()?;
|
||||
|
||||
if !has_payload {
|
||||
|
||||
@ -526,7 +526,6 @@ impl Serialize for LinkType {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use pumpkin_util::resource_location::ResourceLocation;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
@ -551,7 +550,7 @@ mod test {
|
||||
fn serde_id_or_value() {
|
||||
let mut buf = Vec::new();
|
||||
let event = SoundEvent {
|
||||
sound_name: ResourceLocation::vanilla("test"),
|
||||
sound_name: "test".to_string(),
|
||||
range: Some(1.0),
|
||||
};
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ use crate::{
|
||||
|
||||
pub mod deserializer;
|
||||
use pumpkin_nbt::{serializer::WriteAdaptor, tag::NbtTag};
|
||||
use pumpkin_util::{math::position::BlockPos, resource_location::ResourceLocation};
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use thiserror::Error;
|
||||
pub mod serializer;
|
||||
|
||||
@ -71,7 +71,6 @@ pub trait NetworkReadExt {
|
||||
fn get_var_ulong(&mut self) -> Result<VarULong, ReadingError>;
|
||||
fn get_string_bounded(&mut self, bound: usize) -> Result<String, ReadingError>;
|
||||
fn get_string(&mut self) -> Result<String, ReadingError>;
|
||||
fn get_resource_location(&mut self) -> Result<ResourceLocation, ReadingError>;
|
||||
fn get_uuid(&mut self) -> Result<uuid::Uuid, ReadingError>;
|
||||
fn get_fixed_bitset(&mut self, bits: usize) -> Result<FixedBitSet, ReadingError>;
|
||||
|
||||
@ -171,17 +170,6 @@ impl<R: Read> NetworkReadExt for R {
|
||||
self.get_string_bounded(i32::MAX as usize)
|
||||
}
|
||||
|
||||
fn get_resource_location(&mut self) -> Result<ResourceLocation, ReadingError> {
|
||||
let resource_location = self.get_string_bounded(ResourceLocation::MAX_SIZE.get())?;
|
||||
match resource_location.split_once(':') {
|
||||
Some((namespace, path)) => Ok(ResourceLocation {
|
||||
namespace: namespace.to_string(),
|
||||
path: path.to_string(),
|
||||
}),
|
||||
None => Err(ReadingError::Incomplete("ResourceLocation".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_uuid(&mut self) -> Result<uuid::Uuid, ReadingError> {
|
||||
let mut bytes = [0u8; 16];
|
||||
self.read_exact(&mut bytes)
|
||||
@ -245,7 +233,6 @@ pub trait NetworkWriteExt {
|
||||
fn write_var_long(&mut self, data: &VarLong) -> Result<(), WritingError>;
|
||||
fn write_string_bounded(&mut self, data: &str, bound: usize) -> Result<(), WritingError>;
|
||||
fn write_string(&mut self, data: &str) -> Result<(), WritingError>;
|
||||
fn write_resource_location(&mut self, data: &ResourceLocation) -> Result<(), WritingError>;
|
||||
fn write_block_pos(&mut self, pos: &BlockPos) -> Result<(), WritingError>;
|
||||
|
||||
fn write_uuid(&mut self, data: &uuid::Uuid) -> Result<(), WritingError> {
|
||||
@ -366,10 +353,6 @@ impl<W: Write> NetworkWriteExt for W {
|
||||
self.write_i64_be(pos.as_long())
|
||||
}
|
||||
|
||||
fn write_resource_location(&mut self, data: &ResourceLocation) -> Result<(), WritingError> {
|
||||
self.write_string_bounded(&data.to_string(), ResourceLocation::MAX_SIZE.get())
|
||||
}
|
||||
|
||||
fn write_bitset(&mut self, data: &BitSet) -> Result<(), WritingError> {
|
||||
data.encode(self)
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@ pub mod text;
|
||||
pub mod translation;
|
||||
pub mod version;
|
||||
pub mod world_seed;
|
||||
pub mod y_offset;
|
||||
|
||||
pub mod jwt;
|
||||
|
||||
|
||||
@ -1,99 +1,4 @@
|
||||
use std::{num::NonZeroUsize, str::FromStr};
|
||||
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Visitor};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ResourceLocation {
|
||||
pub namespace: String,
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
impl ResourceLocation {
|
||||
/// The maximum number of bytes for a [`ResourceLocation`] is the same as for a normal [`String`].
|
||||
pub const MAX_SIZE: NonZeroUsize = NonZeroUsize::new(i16::MAX as usize).unwrap();
|
||||
|
||||
#[must_use]
|
||||
pub fn from(location: &str) -> Self {
|
||||
let names = location.split_once(':').unwrap_or_default();
|
||||
Self {
|
||||
namespace: names.0.to_string(),
|
||||
path: names.1.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn vanilla(path: &str) -> Self {
|
||||
Self {
|
||||
namespace: "minecraft".to_string(),
|
||||
path: path.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn pumpkin(path: &str) -> Self {
|
||||
Self {
|
||||
namespace: "pumpkin".to_string(),
|
||||
path: path.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ResourceLocation {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}:{}", self.namespace, self.path)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ResourceLocation {
|
||||
type Err = ();
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.split_once(':') {
|
||||
Some((namespace, path)) => Ok(Self {
|
||||
namespace: namespace.to_string(),
|
||||
path: path.to_string(),
|
||||
}),
|
||||
None => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for ResourceLocation {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serializer.serialize_str(&self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ResourceLocation {
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
struct ResourceLocationVisitor;
|
||||
|
||||
impl Visitor<'_> for ResourceLocationVisitor {
|
||||
type Value = ResourceLocation;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("a valid resource location (namespace:path)")
|
||||
}
|
||||
|
||||
fn visit_string<E: serde::de::Error>(self, v: String) -> Result<Self::Value, E> {
|
||||
self.visit_str(&v)
|
||||
}
|
||||
|
||||
fn visit_str<E: serde::de::Error>(
|
||||
self,
|
||||
resource_location: &str,
|
||||
) -> Result<Self::Value, E> {
|
||||
match resource_location.split_once(':') {
|
||||
Some((namespace, path)) => Ok(ResourceLocation {
|
||||
namespace: namespace.to_string(),
|
||||
path: path.to_string(),
|
||||
}),
|
||||
None => Err(serde::de::Error::custom("resource location can't be split")),
|
||||
}
|
||||
}
|
||||
}
|
||||
deserializer.deserialize_str(ResourceLocationVisitor)
|
||||
}
|
||||
}
|
||||
pub type ResourceLocation = String;
|
||||
|
||||
pub trait ToResourceLocation: Sized {
|
||||
fn to_resource_location(&self) -> ResourceLocation;
|
||||
|
||||
@ -23,14 +23,14 @@ impl YOffset {
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Absolute {
|
||||
absolute: i16,
|
||||
pub absolute: i16,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct AboveBottom {
|
||||
above_bottom: i8,
|
||||
pub above_bottom: i8,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
pub struct BelowTop {
|
||||
below_top: i8,
|
||||
pub below_top: i8,
|
||||
}
|
||||
@ -1,20 +1,21 @@
|
||||
use criterion::{Criterion, criterion_group, criterion_main};
|
||||
use pumpkin_data::noise_router::OVERWORLD_BASE_NOISE_ROUTER;
|
||||
use pumpkin_data::{
|
||||
chunk_gen_settings::GenerationSettings, dimension::Dimension,
|
||||
noise_router::OVERWORLD_BASE_NOISE_ROUTER,
|
||||
};
|
||||
use pumpkin_world::{
|
||||
GENERATION_SETTINGS, GeneratorSetting, GlobalRandomConfig, ProtoNoiseRouters,
|
||||
bench_create_and_populate_biome, bench_create_and_populate_noise,
|
||||
bench_create_and_populate_noise_with_surface, generation::proto_chunk::TerrainCache,
|
||||
GlobalRandomConfig, ProtoNoiseRouters, bench_create_and_populate_biome,
|
||||
bench_create_and_populate_noise, bench_create_and_populate_noise_with_surface,
|
||||
block::to_state_from_blueprint, generation::proto_chunk::TerrainCache,
|
||||
};
|
||||
|
||||
fn bench_terrain_gen(c: &mut Criterion) {
|
||||
let seed = 0;
|
||||
let random_config = GlobalRandomConfig::new(seed, false);
|
||||
let base_router = ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &random_config);
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let surface_config = GenerationSettings::from_dimension(&Dimension::OVERWORLD);
|
||||
let terrain_cache = TerrainCache::from_random(&random_config);
|
||||
let default_block = surface_config.default_block.get_state();
|
||||
let default_state = to_state_from_blueprint(&surface_config.default_block);
|
||||
|
||||
c.bench_function("overworld biome", |b| {
|
||||
b.iter(|| {
|
||||
@ -23,7 +24,7 @@ fn bench_terrain_gen(c: &mut Criterion) {
|
||||
&random_config,
|
||||
surface_config,
|
||||
&terrain_cache,
|
||||
default_block,
|
||||
default_state,
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -35,7 +36,7 @@ fn bench_terrain_gen(c: &mut Criterion) {
|
||||
&random_config,
|
||||
surface_config,
|
||||
&terrain_cache,
|
||||
default_block,
|
||||
default_state,
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -47,7 +48,7 @@ fn bench_terrain_gen(c: &mut Criterion) {
|
||||
&random_config,
|
||||
surface_config,
|
||||
&terrain_cache,
|
||||
default_block,
|
||||
default_state,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -60,13 +60,15 @@ pub fn hash_seed(seed: u64) -> i64 {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use pumpkin_data::{
|
||||
chunk::Biome, dimension::Dimension, noise_router::OVERWORLD_BASE_NOISE_ROUTER,
|
||||
chunk::Biome, chunk_gen_settings::GenerationSettings, dimension::Dimension,
|
||||
noise_router::OVERWORLD_BASE_NOISE_ROUTER,
|
||||
};
|
||||
use pumpkin_util::read_data_from_file;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
GENERATION_SETTINGS, GeneratorSetting, GlobalRandomConfig, ProtoChunk,
|
||||
GlobalRandomConfig, ProtoChunk,
|
||||
block::to_state_from_blueprint,
|
||||
chunk::palette::BIOME_NETWORK_MAX_BITS,
|
||||
generation::noise::router::{
|
||||
multi_noise_sampler::{MultiNoiseSampler, MultiNoiseSamplerBuilderOptions},
|
||||
@ -110,11 +112,10 @@ mod test {
|
||||
let random_config = GlobalRandomConfig::new(seed, false);
|
||||
let noise_router =
|
||||
ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &random_config);
|
||||
let surface_settings = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let surface_settings = GenerationSettings::from_dimension(&Dimension::OVERWORLD);
|
||||
|
||||
//let _terrain_cache = TerrainCache::from_random(&random_config);
|
||||
let default_block = surface_settings.default_block.get_state();
|
||||
let default_block = to_state_from_blueprint(&surface_settings.default_block);
|
||||
|
||||
for data in expected_data {
|
||||
let chunk_x = data.x;
|
||||
|
||||
@ -33,13 +33,15 @@ impl NoiseValuePoint {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use pumpkin_data::{
|
||||
chunk::Biome, dimension::Dimension, noise_router::OVERWORLD_BASE_NOISE_ROUTER,
|
||||
chunk::Biome, chunk_gen_settings::GenerationSettings, dimension::Dimension,
|
||||
noise_router::OVERWORLD_BASE_NOISE_ROUTER,
|
||||
};
|
||||
use pumpkin_util::read_data_from_file;
|
||||
|
||||
use crate::{
|
||||
GENERATION_SETTINGS, GeneratorSetting, GlobalRandomConfig, ProtoChunk,
|
||||
GlobalRandomConfig, ProtoChunk,
|
||||
biome::{BiomeSupplier, MultiNoiseBiomeSupplier},
|
||||
block::to_state_from_blueprint,
|
||||
generation::{
|
||||
noise::router::{
|
||||
multi_noise_sampler::{MultiNoiseSampler, MultiNoiseSamplerBuilderOptions},
|
||||
@ -68,9 +70,8 @@ mod test {
|
||||
let noise_router =
|
||||
ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &random_config);
|
||||
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let surface_config = GenerationSettings::from_dimension(&Dimension::OVERWORLD);
|
||||
|
||||
let _terrain_cache = TerrainCache::from_random(&random_config);
|
||||
// Calculate biome mixer seed
|
||||
let biome_mixer_seed = hash_seed(random_config.seed);
|
||||
@ -79,7 +80,7 @@ mod test {
|
||||
chunk_x,
|
||||
chunk_z,
|
||||
&Dimension::OVERWORLD,
|
||||
surface_config.default_block.get_state(),
|
||||
to_state_from_blueprint(&surface_config.default_block),
|
||||
biome_mixer_seed,
|
||||
);
|
||||
|
||||
|
||||
@ -76,7 +76,7 @@ impl BlockEntity for BarrelBlockEntity {
|
||||
|
||||
fn tick<'a>(
|
||||
&'a self,
|
||||
world: Arc<dyn SimpleWorld>,
|
||||
world: &'a Arc<dyn SimpleWorld>,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
self.viewers
|
||||
|
||||
@ -76,7 +76,7 @@ impl BlockEntity for ChestBlockEntity {
|
||||
|
||||
fn tick<'a>(
|
||||
&'a self,
|
||||
world: Arc<dyn SimpleWorld>,
|
||||
world: &'a Arc<dyn SimpleWorld>,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
self.viewers
|
||||
|
||||
@ -47,7 +47,7 @@ impl BlockEntity for EnderChestBlockEntity {
|
||||
|
||||
fn tick<'a>(
|
||||
&'a self,
|
||||
world: Arc<dyn SimpleWorld>,
|
||||
world: &'a Arc<dyn SimpleWorld>,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
self.viewers
|
||||
|
||||
@ -315,7 +315,7 @@ macro_rules! impl_block_entity_for_cooking {
|
||||
impl $crate::block::entities::BlockEntity for $struct_name {
|
||||
fn tick<'a>(
|
||||
&'a self,
|
||||
world: Arc<dyn $crate::world::SimpleWorld>,
|
||||
world: &'a Arc<dyn $crate::world::SimpleWorld>,
|
||||
) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
let is_burning = self.is_burning();
|
||||
|
||||
@ -77,7 +77,7 @@ impl BlockEntity for HopperBlockEntity {
|
||||
|
||||
fn tick<'a>(
|
||||
&'a self,
|
||||
world: Arc<dyn SimpleWorld>,
|
||||
world: &'a Arc<dyn SimpleWorld>,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
self.ticked_game_time.store(
|
||||
@ -95,7 +95,7 @@ impl BlockEntity for HopperBlockEntity {
|
||||
world.get_block_state(&self.position).await.id,
|
||||
&Block::HOPPER,
|
||||
);
|
||||
self.try_move_items(&state, &world).await;
|
||||
self.try_move_items(&state, world).await;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -81,12 +81,12 @@ impl BlockEntity for MobSpawnerBlockEntity {
|
||||
|
||||
fn tick<'a>(
|
||||
&'a self,
|
||||
world: Arc<dyn SimpleWorld>,
|
||||
world: &'a Arc<dyn SimpleWorld>,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
if let Some(entity_type) = &self.entity_type.load() {
|
||||
if self.delay.load(Ordering::Relaxed) == -1 {
|
||||
self.update_spawns(&world).await;
|
||||
self.update_spawns(world).await;
|
||||
} else {
|
||||
self.delay.fetch_sub(1, Ordering::Relaxed);
|
||||
return;
|
||||
@ -128,7 +128,7 @@ impl BlockEntity for MobSpawnerBlockEntity {
|
||||
update_spawns = true;
|
||||
}
|
||||
if update_spawns {
|
||||
self.update_spawns(&world).await;
|
||||
self.update_spawns(world).await;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -55,7 +55,7 @@ pub trait BlockEntity: Send + Sync {
|
||||
Self: Sized;
|
||||
fn tick<'a>(
|
||||
&'a self,
|
||||
_world: Arc<dyn SimpleWorld>,
|
||||
_world: &'a Arc<dyn SimpleWorld>,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async {})
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ impl BlockEntity for PistonBlockEntity {
|
||||
|
||||
fn tick<'a>(
|
||||
&'a self,
|
||||
world: Arc<dyn SimpleWorld>,
|
||||
world: &'a Arc<dyn SimpleWorld>,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
let current_progress = self.current_progress.load();
|
||||
|
||||
@ -71,7 +71,7 @@ impl BlockEntity for ShulkerBoxBlockEntity {
|
||||
|
||||
fn tick<'a>(
|
||||
&'a self,
|
||||
world: Arc<dyn SimpleWorld>,
|
||||
world: &'a Arc<dyn SimpleWorld>,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
self.viewers
|
||||
|
||||
@ -4,7 +4,7 @@ pub mod viewer;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use pumpkin_data::{Block, BlockState};
|
||||
use pumpkin_data::{Block, BlockState, chunk_gen_settings::BlockBlueprint};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
pub use state::RawBlockState;
|
||||
|
||||
@ -69,6 +69,23 @@ impl BlockStateCodec {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn to_state_id_from_blueprint(print: &BlockBlueprint) -> BlockStateId {
|
||||
let block = Block::from_name(print.name)
|
||||
.unwrap_or_else(|| panic!("Invalid block name in blueprint: {}", print.name));
|
||||
|
||||
match print.properties {
|
||||
Some(props) => block.from_properties(props).to_state_id(block),
|
||||
None => block.default_state.id,
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper to get the full BlockState reference from the blueprint
|
||||
#[must_use]
|
||||
pub fn to_state_from_blueprint(print: &BlockBlueprint) -> &'static BlockState {
|
||||
BlockState::from_id(to_state_id_from_blueprint(print))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use pumpkin_data::Block;
|
||||
|
||||
@ -42,7 +42,7 @@ impl ViewerCountTracker {
|
||||
pub async fn update_viewer_count<T>(
|
||||
&self,
|
||||
entity: &T,
|
||||
world: Arc<dyn SimpleWorld>,
|
||||
world: &Arc<dyn SimpleWorld>,
|
||||
position: &BlockPos,
|
||||
) where
|
||||
T: BlockEntity + ViewerCountListener + 'static,
|
||||
@ -52,12 +52,12 @@ impl ViewerCountTracker {
|
||||
if old != current {
|
||||
match (old, current) {
|
||||
(n, 0) if n > 0 => {
|
||||
entity.on_container_close(&world, position).await;
|
||||
entity.on_container_close(world, position).await;
|
||||
// TODO: world.emitGameEvent(player, GameEvent.CONTAINER_CLOSE, pos);
|
||||
// TODO: this.maxBlockInteractionRange = 0.0;
|
||||
}
|
||||
(0, n) if n > 0 => {
|
||||
entity.on_container_open(&world, position).await;
|
||||
entity.on_container_open(world, position).await;
|
||||
// TODO: world.emitGameEvent(player, GameEvent.CONTAINER_OPEN, pos);
|
||||
// TODO: scheduleBlockTick(world, pos, state);
|
||||
}
|
||||
@ -65,7 +65,7 @@ impl ViewerCountTracker {
|
||||
}
|
||||
|
||||
entity
|
||||
.on_viewer_count_update(&world, position, old, current)
|
||||
.on_viewer_count_update(world, position, old, current)
|
||||
.await;
|
||||
}
|
||||
|
||||
|
||||
@ -351,7 +351,6 @@ impl<S: SingleChunkDataSerializer> AnvilChunkFile<S> {
|
||||
log::trace!("Writing in place: {}", path.display());
|
||||
|
||||
let file = tokio::fs::OpenOptions::new()
|
||||
.read(false)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(false)
|
||||
@ -450,13 +449,7 @@ impl<S: SingleChunkDataSerializer> AnvilChunkFile<S> {
|
||||
let temp_path = path.with_extension("tmp");
|
||||
log::trace!("Writing tmp file to disk: {temp_path:?}");
|
||||
|
||||
let file = tokio::fs::OpenOptions::new()
|
||||
.read(false)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(&temp_path)
|
||||
.await?;
|
||||
let file = tokio::fs::File::create(&temp_path).await?;
|
||||
|
||||
let mut write = BufWriter::new(file);
|
||||
|
||||
|
||||
@ -179,14 +179,7 @@ impl<S: SingleChunkDataSerializer> ChunkSerializer for LinearFile<S> {
|
||||
let temp_path = path.with_extension("tmp");
|
||||
log::trace!("Writing tmp file to disk: {}", temp_path.display());
|
||||
|
||||
let file = tokio::fs::OpenOptions::new()
|
||||
.read(false)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(&temp_path)
|
||||
.await?;
|
||||
|
||||
let file = tokio::fs::File::create(&temp_path).await?;
|
||||
let mut write = BufWriter::new(file);
|
||||
|
||||
// Parse the headers to a buffer
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
use std::{collections::HashMap, io::Cursor, path::PathBuf, pin::Pin};
|
||||
use std::{io::Cursor, path::PathBuf, pin::Pin};
|
||||
|
||||
use bytes::Bytes;
|
||||
use futures::future::join_all;
|
||||
use pumpkin_data::{Block, chunk::ChunkStatus, fluid::Fluid};
|
||||
use pumpkin_nbt::{compound::NbtCompound, from_bytes, nbt_long_array};
|
||||
use rustc_hash::FxHashMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
@ -170,7 +171,7 @@ impl ChunkData {
|
||||
block_ticks: ChunkTickScheduler::from_iter(chunk_data.block_ticks),
|
||||
fluid_ticks: ChunkTickScheduler::from_iter(chunk_data.fluid_ticks),
|
||||
block_entities: {
|
||||
let mut block_entities = HashMap::new();
|
||||
let mut block_entities = FxHashMap::default();
|
||||
for nbt in chunk_data.block_entities {
|
||||
let block_entity = block_entity_from_nbt(&nbt);
|
||||
if let Some(block_entity) = block_entity {
|
||||
@ -289,7 +290,7 @@ impl ChunkEntityData {
|
||||
chunk_entity_data.position[1],
|
||||
)));
|
||||
}
|
||||
let mut map = HashMap::new();
|
||||
let mut map = FxHashMap::default();
|
||||
for entity_nbt in chunk_entity_data.entities {
|
||||
let uuid = if let Some(uuid) = entity_nbt.get_int_array("UUID") {
|
||||
Uuid::from_u128(
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
io::ErrorKind,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
@ -9,7 +8,6 @@ use futures::future::join_all;
|
||||
use log::{error, trace};
|
||||
use pumpkin_util::math::vector2::Vector2;
|
||||
use tokio::{
|
||||
io::AsyncReadExt,
|
||||
join,
|
||||
sync::{OnceCell, RwLock, mpsc},
|
||||
};
|
||||
@ -86,39 +84,31 @@ impl<S: ChunkSerializer<WriteBackend = PathBuf>> ChunkSerializerLazyLoader<S> {
|
||||
|
||||
async fn read_from_disk(&self) -> Result<S, ChunkReadingError> {
|
||||
trace!("Opening file from Disk: {}", self.path.display());
|
||||
let file = tokio::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(false)
|
||||
.create(false)
|
||||
.truncate(false)
|
||||
.open(&self.path)
|
||||
.await
|
||||
.map_err(|err| match err.kind() {
|
||||
ErrorKind::NotFound => ChunkReadingError::ChunkNotExist,
|
||||
kind => ChunkReadingError::IoError(kind),
|
||||
|
||||
let file_bytes = tokio::fs::read(&self.path).await.map_err(|err| {
|
||||
if err.kind() == std::io::ErrorKind::NotFound {
|
||||
ChunkReadingError::ChunkNotExist
|
||||
} else {
|
||||
ChunkReadingError::IoError(err.kind())
|
||||
}
|
||||
});
|
||||
|
||||
let value = match file {
|
||||
Ok(mut file) => {
|
||||
let capacity = match file.metadata().await {
|
||||
Ok(metadata) => metadata.len() as usize,
|
||||
Err(_) => 4096, // A sane default
|
||||
};
|
||||
|
||||
// TODO: Memmap?
|
||||
let mut file_bytes = Vec::with_capacity(capacity);
|
||||
file.read_to_end(&mut file_bytes)
|
||||
.await
|
||||
.map_err(|err| ChunkReadingError::IoError(err.kind()))?;
|
||||
S::read(file_bytes.into())?
|
||||
}
|
||||
Err(ChunkReadingError::ChunkNotExist) => S::default(),
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
|
||||
match file_bytes {
|
||||
Ok(bytes) => {
|
||||
let value = S::read(bytes.into())?;
|
||||
trace!("Successfully read file from Disk: {}", self.path.display());
|
||||
Ok(value)
|
||||
}
|
||||
Err(ChunkReadingError::ChunkNotExist) => {
|
||||
trace!(
|
||||
"Chunk not found, using default for: {}",
|
||||
self.path.display()
|
||||
);
|
||||
Ok(S::default())
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: ChunkSerializer<WriteBackend = PathBuf>> ChunkFileManager<S> {
|
||||
@ -402,13 +392,6 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
fn clean_up_log(&self) -> BoxFuture<'_, ()> {
|
||||
Box::pin(async move {
|
||||
let locks = self.file_locks.read().await;
|
||||
log::debug!("{} File locks remain in cache", locks.len());
|
||||
})
|
||||
}
|
||||
|
||||
fn block_and_await_ongoing_tasks(&self) -> BoxFuture<'_, ()> {
|
||||
Box::pin(async move {
|
||||
//we need to block any other operation
|
||||
|
||||
@ -85,8 +85,6 @@ where
|
||||
/// Tells the `ChunkIO` that no more chunks are loaded in memory
|
||||
fn clear_watched_chunks(&self) -> BoxFuture<'_, ()>;
|
||||
|
||||
fn clean_up_log(&self) -> BoxFuture<'_, ()>;
|
||||
|
||||
/// Ensure that all ongoing operations are finished
|
||||
fn block_and_await_ongoing_tasks(&self) -> BoxFuture<'_, ()>;
|
||||
}
|
||||
|
||||
@ -11,10 +11,11 @@ use pumpkin_data::{Block, BlockState};
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_nbt::nbt_long_array;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use rustc_hash::FxHashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Write;
|
||||
use std::ops::{BitAnd, BitOr};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
|
||||
pub mod format;
|
||||
@ -76,7 +77,7 @@ pub struct ChunkData {
|
||||
pub z: i32,
|
||||
pub block_ticks: ChunkTickScheduler<&'static Block>,
|
||||
pub fluid_ticks: ChunkTickScheduler<&'static Fluid>,
|
||||
pub block_entities: HashMap<BlockPos, Arc<dyn BlockEntity>>,
|
||||
pub block_entities: FxHashMap<BlockPos, Arc<dyn BlockEntity>>,
|
||||
pub light_engine: ChunkLight,
|
||||
pub status: ChunkStatus,
|
||||
pub dirty: bool,
|
||||
@ -88,7 +89,7 @@ pub struct ChunkEntityData {
|
||||
pub x: i32,
|
||||
/// Chunk Z
|
||||
pub z: i32,
|
||||
pub data: HashMap<uuid::Uuid, NbtCompound>,
|
||||
pub data: FxHashMap<uuid::Uuid, NbtCompound>,
|
||||
|
||||
pub dirty: bool,
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ use crate::chunk::io::LoadedData::Loaded;
|
||||
use crate::chunk::{ChunkData, ChunkHeightmapType, ChunkLight, ChunkSections, SubChunk};
|
||||
use crate::generation::biome_coords;
|
||||
use pumpkin_data::block_properties::is_air;
|
||||
use pumpkin_data::chunk_gen_settings::GenerationSettings;
|
||||
use pumpkin_data::dimension::Dimension;
|
||||
use std::default::Default;
|
||||
use std::pin::Pin;
|
||||
@ -18,7 +19,6 @@ use std::pin::Pin;
|
||||
use crate::generation::height_limit::HeightLimitView;
|
||||
|
||||
use crate::generation::proto_chunk::{GenerationCache, TerrainCache};
|
||||
use crate::generation::settings::{GenerationSettings, gen_settings_from_dimension};
|
||||
use crate::level::{Level, SyncChunk};
|
||||
use crate::world::{BlockAccessor, BlockRegistryExt};
|
||||
use crate::{BlockStateId, GlobalRandomConfig, ProtoChunk, ProtoNoiseRouters};
|
||||
@ -1769,7 +1769,7 @@ impl GenerationSchedule {
|
||||
thread::current().name().unwrap_or("unknown")
|
||||
);
|
||||
|
||||
let settings = gen_settings_from_dimension(&level.world_gen.dimension);
|
||||
let settings = GenerationSettings::from_dimension(&level.world_gen.dimension);
|
||||
while let Ok((pos, mut cache, stage)) = recv.recv() {
|
||||
// debug!("generation thread receive chunk pos {pos:?} to stage {stage:?}");
|
||||
cache.advance(
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
use enum_dispatch::enum_dispatch;
|
||||
|
||||
use super::noise::router::density_function::NoisePos;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
pub struct BlendResult {
|
||||
alpha: f64,
|
||||
@ -26,7 +25,7 @@ impl Blender {
|
||||
pub trait BlenderImpl {
|
||||
fn calculate(&self, block_x: i32, block_z: i32) -> BlendResult;
|
||||
|
||||
fn apply_blend_density(&self, pos: &impl NoisePos, density: f64) -> f64;
|
||||
fn apply_blend_density(&self, pos: &Vector3<i32>, density: f64) -> f64;
|
||||
|
||||
fn get_biome_supplier(&self) {
|
||||
todo!()
|
||||
@ -40,7 +39,7 @@ impl BlenderImpl for NoBlendBlender {
|
||||
BlendResult::new(1f64, 1f64)
|
||||
}
|
||||
|
||||
fn apply_blend_density(&self, _pos: &impl NoisePos, density: f64) -> f64 {
|
||||
fn apply_blend_density(&self, _pos: &Vector3<i32>, density: f64) -> f64 {
|
||||
density
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
use pumpkin_data::BlockState;
|
||||
use pumpkin_data::chunk_gen_settings::GenerationSettings;
|
||||
use pumpkin_data::dimension::Dimension;
|
||||
use pumpkin_data::noise_router::{
|
||||
END_BASE_NOISE_ROUTER, NETHER_BASE_NOISE_ROUTER, OVERWORLD_BASE_NOISE_ROUTER,
|
||||
};
|
||||
|
||||
use super::{
|
||||
noise::router::proto_noise_router::ProtoNoiseRouters, settings::gen_settings_from_dimension,
|
||||
};
|
||||
use super::noise::router::proto_noise_router::ProtoNoiseRouters;
|
||||
use crate::block::to_state_from_blueprint;
|
||||
use crate::generation::proto_chunk::TerrainCache;
|
||||
use crate::generation::{GlobalRandomConfig, Seed};
|
||||
|
||||
@ -40,9 +40,9 @@ impl GeneratorInit for VanillaGenerator {
|
||||
unreachable!()
|
||||
};
|
||||
let terrain_cache = TerrainCache::from_random(&random_config);
|
||||
let generation_settings = gen_settings_from_dimension(&dimension);
|
||||
let generation_settings = GenerationSettings::from_dimension(&dimension);
|
||||
|
||||
let default_block = generation_settings.default_block.get_state();
|
||||
let default_block = to_state_from_blueprint(&generation_settings.default_block);
|
||||
let base_router = ProtoNoiseRouters::generate(&base, &random_config);
|
||||
Self {
|
||||
random_config,
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
use pumpkin_util::random::{RandomGenerator, RandomImpl};
|
||||
use pumpkin_util::{
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
y_offset::YOffset,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use super::y_offset::YOffset;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum HeightProvider {
|
||||
|
||||
@ -18,7 +18,6 @@ mod rule_test;
|
||||
pub mod settings;
|
||||
pub mod structure;
|
||||
mod surface;
|
||||
pub mod y_offset;
|
||||
|
||||
use generator::{GeneratorInit, VanillaGenerator};
|
||||
use pumpkin_data::dimension::Dimension;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use pumpkin_data::{Block, BlockState};
|
||||
use pumpkin_util::{
|
||||
math::{clamped_map, floor_div},
|
||||
math::{clamped_map, floor_div, vector3::Vector3},
|
||||
random::{RandomDeriver, RandomDeriverImpl, RandomImpl},
|
||||
};
|
||||
|
||||
@ -11,7 +11,6 @@ use crate::generation::{
|
||||
router::{
|
||||
chunk_density_function::ChunkNoiseFunctionSampleOptions,
|
||||
chunk_noise_router::ChunkNoiseRouter,
|
||||
density_function::{NoisePos, UnblendedNoisePos},
|
||||
surface_height_sampler::SurfaceHeightEstimateSampler,
|
||||
},
|
||||
},
|
||||
@ -237,13 +236,13 @@ impl WorldAquiferSampler {
|
||||
|
||||
fn calculate_density(
|
||||
barrier_sample: &mut Option<f64>,
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
router: &mut ChunkNoiseRouter,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
level_1: &FluidLevel,
|
||||
level_2: &FluidLevel,
|
||||
) -> f64 {
|
||||
let y = pos.y();
|
||||
let y = pos.y;
|
||||
let block_state1 = level_1.get_block(y);
|
||||
let block_state2 = level_2.get_block(y);
|
||||
|
||||
@ -392,7 +391,7 @@ impl WorldAquiferSampler {
|
||||
router: &mut ChunkNoiseRouter,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> i32 {
|
||||
let pos = UnblendedNoisePos::new(block_x, block_y, block_z);
|
||||
let pos = Vector3::new(block_x, block_y, block_z);
|
||||
|
||||
let is_deep_dark = router.erosion(&pos, sample_options) < -0.225f32 as f64
|
||||
&& router.depth(&pos, sample_options) > 0.9f32 as f64;
|
||||
@ -446,9 +445,8 @@ impl WorldAquiferSampler {
|
||||
|
||||
let local_y = y * 40 + 20;
|
||||
|
||||
let sample = router
|
||||
.fluid_level_spread_noise(&UnblendedNoisePos::new(x, y, z), sample_options)
|
||||
* 10f64;
|
||||
let sample =
|
||||
router.fluid_level_spread_noise(&Vector3::new(x, y, z), sample_options) * 10f64;
|
||||
let to_nearest_multiple_of_three = (sample / 3f64).floor() as i32 * 3;
|
||||
let local_height = to_nearest_multiple_of_three + local_y;
|
||||
|
||||
@ -469,7 +467,7 @@ impl WorldAquiferSampler {
|
||||
let y = floor_div(block_y, 40);
|
||||
let z = floor_div(block_z, 64);
|
||||
|
||||
let sample = router.lava_noise(&UnblendedNoisePos::new(x, y, z), sample_options);
|
||||
let sample = router.lava_noise(&Vector3::new(x, y, z), sample_options);
|
||||
|
||||
if sample.abs() > 0.3f64 {
|
||||
return &LAVA_BLOCK;
|
||||
@ -482,7 +480,7 @@ impl WorldAquiferSampler {
|
||||
fn apply_internal(
|
||||
&mut self,
|
||||
router: &mut ChunkNoiseRouter,
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
height_estimator: &mut SurfaceHeightEstimateSampler,
|
||||
density: f64,
|
||||
@ -490,9 +488,9 @@ impl WorldAquiferSampler {
|
||||
if density > 0f64 {
|
||||
None
|
||||
} else {
|
||||
let sample_x = pos.x();
|
||||
let sample_y = pos.y();
|
||||
let sample_z = pos.z();
|
||||
let sample_x = pos.x;
|
||||
let sample_y = pos.y;
|
||||
let sample_z = pos.z;
|
||||
|
||||
let fluid_level = self
|
||||
.fluid_level_sampler
|
||||
@ -639,7 +637,7 @@ impl AquiferSamplerImpl for WorldAquiferSampler {
|
||||
fn apply(
|
||||
&mut self,
|
||||
router: &mut ChunkNoiseRouter,
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
height_estimator: &mut SurfaceHeightEstimateSampler,
|
||||
) -> Option<&'static BlockState> {
|
||||
@ -663,7 +661,7 @@ impl AquiferSamplerImpl for SeaLevelAquiferSampler {
|
||||
fn apply(
|
||||
&mut self,
|
||||
router: &mut ChunkNoiseRouter,
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
_height_estimator: &mut SurfaceHeightEstimateSampler,
|
||||
) -> Option<&'static BlockState> {
|
||||
@ -674,8 +672,8 @@ impl AquiferSamplerImpl for SeaLevelAquiferSampler {
|
||||
} else {
|
||||
Some(
|
||||
self.level_sampler
|
||||
.get_fluid_level(pos.x(), pos.y(), pos.z())
|
||||
.get_block(pos.y())
|
||||
.get_fluid_level(pos.x, pos.y, pos.z)
|
||||
.get_block(pos.y)
|
||||
.default_state,
|
||||
)
|
||||
}
|
||||
@ -687,7 +685,7 @@ pub trait AquiferSamplerImpl {
|
||||
fn apply(
|
||||
&mut self,
|
||||
router: &mut ChunkNoiseRouter,
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
height_estimator: &mut SurfaceHeightEstimateSampler,
|
||||
) -> Option<&'static BlockState>;
|
||||
@ -697,7 +695,11 @@ pub trait AquiferSamplerImpl {
|
||||
mod random_positions_and_hypot {
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use pumpkin_data::noise_router::OVERWORLD_BASE_NOISE_ROUTER;
|
||||
use pumpkin_data::{
|
||||
chunk_gen_settings::GenerationSettings, dimension::Dimension,
|
||||
noise_router::OVERWORLD_BASE_NOISE_ROUTER,
|
||||
};
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
use crate::{
|
||||
block::RawBlockState,
|
||||
@ -708,7 +710,6 @@ mod random_positions_and_hypot {
|
||||
router::{
|
||||
chunk_density_function::{ChunkNoiseFunctionSampleOptions, SampleAction},
|
||||
chunk_noise_router::ChunkNoiseRouter,
|
||||
density_function::UnblendedNoisePos,
|
||||
proto_noise_router::ProtoNoiseRouters,
|
||||
surface_height_sampler::{
|
||||
SurfaceHeightEstimateSampler, SurfaceHeightSamplerBuilderOptions,
|
||||
@ -717,7 +718,6 @@ mod random_positions_and_hypot {
|
||||
},
|
||||
positions::chunk_pos,
|
||||
proto_chunk::StandardChunkFluidLevelSampler,
|
||||
settings::{GENERATION_SETTINGS, GeneratorSetting},
|
||||
},
|
||||
};
|
||||
|
||||
@ -739,9 +739,7 @@ mod random_positions_and_hypot {
|
||||
SurfaceHeightEstimateSampler<'_>,
|
||||
ChunkNoiseFunctionSampleOptions,
|
||||
) {
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let surface_config = GenerationSettings::from_dimension(&Dimension::OVERWORLD);
|
||||
let shape = &surface_config.shape;
|
||||
let chunk_x = 7;
|
||||
let chunk_z = 4;
|
||||
@ -1668,7 +1666,7 @@ mod random_positions_and_hypot {
|
||||
for ((x, y, z, h1, h2), result) in values {
|
||||
let level1 = FluidLevel::new(h1, &WATER_BLOCK);
|
||||
let level2 = FluidLevel::new(h2, &WATER_BLOCK);
|
||||
let pos = UnblendedNoisePos::new(x, y, z);
|
||||
let pos = Vector3::new(x, y, z);
|
||||
let mut sample = None;
|
||||
|
||||
assert_eq!(
|
||||
@ -2656,7 +2654,7 @@ mod random_positions_and_hypot {
|
||||
];
|
||||
|
||||
for ((x, y, z, sample), result) in values {
|
||||
let pos = UnblendedNoisePos::new(x, y, z);
|
||||
let pos = Vector3::new(x, y, z);
|
||||
assert_eq!(
|
||||
aquifer.apply_internal(&mut router, &pos, &env, &mut height_estimator, sample),
|
||||
result.map(|r| r.to_state())
|
||||
|
||||
@ -3,8 +3,8 @@ pub mod ore_sampler;
|
||||
pub mod perlin;
|
||||
pub mod router;
|
||||
|
||||
use pumpkin_data::{Block, BlockState};
|
||||
use pumpkin_util::math::{floor_div, floor_mod};
|
||||
use pumpkin_data::{Block, BlockState, chunk_gen_settings::GenerationShapeConfig};
|
||||
use pumpkin_util::math::{floor_div, floor_mod, vector3::Vector3};
|
||||
|
||||
use crate::generation::{
|
||||
noise::{
|
||||
@ -25,11 +25,10 @@ use super::{
|
||||
WrapperData,
|
||||
},
|
||||
chunk_noise_router::ChunkNoiseRouter,
|
||||
density_function::{IndexToNoisePos, NoisePos, UnblendedNoisePos},
|
||||
density_function::IndexToNoisePos,
|
||||
proto_noise_router::ProtoNoiseRouter,
|
||||
surface_height_sampler::SurfaceHeightEstimateSampler,
|
||||
},
|
||||
settings::GenerationShapeConfig,
|
||||
};
|
||||
|
||||
pub const LAVA_BLOCK: Block = Block::LAVA;
|
||||
@ -47,7 +46,7 @@ impl BlockStateSampler {
|
||||
&mut self,
|
||||
router: &mut ChunkNoiseRouter,
|
||||
random_config: &GlobalRandomConfig,
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
height_estimator: &mut SurfaceHeightEstimateSampler,
|
||||
) -> Option<&'static BlockState> {
|
||||
@ -72,7 +71,7 @@ impl ChainedBlockStateSampler {
|
||||
&mut self,
|
||||
router: &mut ChunkNoiseRouter,
|
||||
random_config: &GlobalRandomConfig,
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
height_estimator: &mut SurfaceHeightEstimateSampler,
|
||||
) -> Option<&'static BlockState> {
|
||||
@ -100,7 +99,7 @@ impl IndexToNoisePos for InterpolationIndexMapper {
|
||||
&self,
|
||||
index: usize,
|
||||
sample_data: Option<&mut ChunkNoiseFunctionSampleOptions>,
|
||||
) -> impl NoisePos + 'static {
|
||||
) -> Vector3<i32> {
|
||||
if let Some(sample_data) = sample_data {
|
||||
sample_data.cache_result_unique_id += 1;
|
||||
sample_data.fill_index = index;
|
||||
@ -109,7 +108,7 @@ impl IndexToNoisePos for InterpolationIndexMapper {
|
||||
let y = (index as i32 + self.minimum_cell_y) * self.vertical_cell_block_count;
|
||||
|
||||
// TODO: Change this when Blender is implemented
|
||||
UnblendedNoisePos::new(self.x, y, self.z)
|
||||
Vector3::new(self.x, y, self.z)
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,7 +126,7 @@ impl IndexToNoisePos for ChunkIndexMapper {
|
||||
&self,
|
||||
index: usize,
|
||||
sample_options: Option<&mut ChunkNoiseFunctionSampleOptions>,
|
||||
) -> impl NoisePos + 'static {
|
||||
) -> Vector3<i32> {
|
||||
let cell_z_position = floor_mod(index, self.horizontal_cell_block_count);
|
||||
let xy_portion = floor_div(index, self.horizontal_cell_block_count);
|
||||
let cell_x_position = floor_mod(xy_portion, self.horizontal_cell_block_count);
|
||||
@ -143,7 +142,7 @@ impl IndexToNoisePos for ChunkIndexMapper {
|
||||
}
|
||||
|
||||
// TODO: Change this when Blender is implemented
|
||||
UnblendedNoisePos::new(
|
||||
Vector3::new(
|
||||
self.start_x + cell_x_position as i32,
|
||||
self.start_y + cell_y_position as i32,
|
||||
self.start_z + cell_z_position as i32,
|
||||
@ -382,7 +381,7 @@ impl<'a> ChunkNoiseGenerator<'a> {
|
||||
height_estimator: &mut SurfaceHeightEstimateSampler,
|
||||
) -> Option<&'static BlockState> {
|
||||
//TODO: Fix this when Blender is added
|
||||
let pos = UnblendedNoisePos::new(start_x + cell_x, start_y + cell_y, start_z + cell_z);
|
||||
let pos = Vector3::new(start_x + cell_x, start_y + cell_y, start_z + cell_z);
|
||||
|
||||
let options = ChunkNoiseFunctionSampleOptions::new(
|
||||
false,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use pumpkin_data::{Block, BlockState};
|
||||
use pumpkin_util::{
|
||||
math::clamped_map,
|
||||
math::{clamped_map, vector3::Vector3},
|
||||
random::{RandomDeriverImpl, RandomImpl},
|
||||
};
|
||||
|
||||
@ -8,7 +8,7 @@ use crate::{
|
||||
GlobalRandomConfig,
|
||||
generation::noise::router::{
|
||||
chunk_density_function::ChunkNoiseFunctionSampleOptions,
|
||||
chunk_noise_router::ChunkNoiseRouter, density_function::NoisePos,
|
||||
chunk_noise_router::ChunkNoiseRouter,
|
||||
},
|
||||
};
|
||||
|
||||
@ -19,7 +19,7 @@ impl OreVeinSampler {
|
||||
&self,
|
||||
router: &mut ChunkNoiseRouter,
|
||||
random_config: &GlobalRandomConfig,
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> Option<&'static BlockState> {
|
||||
let vein_toggle = router.vein_toggle(pos, sample_options);
|
||||
@ -29,7 +29,7 @@ impl OreVeinSampler {
|
||||
&vein_type::IRON
|
||||
};
|
||||
|
||||
let block_y = pos.y();
|
||||
let block_y = pos.y;
|
||||
let max_to_y = vein_type.max_y - block_y;
|
||||
let y_to_min = block_y - vein_type.min_y;
|
||||
if (max_to_y >= 0) && (y_to_min >= 0) {
|
||||
@ -37,10 +37,9 @@ impl OreVeinSampler {
|
||||
let mapped_diff = clamped_map(closest_to_bound as f64, 0f64, 20f64, -0.2f64, 0f64);
|
||||
let abs_sample = vein_toggle.abs();
|
||||
if abs_sample + mapped_diff >= 0.4f32 as f64 {
|
||||
let mut random =
|
||||
random_config
|
||||
let mut random = random_config
|
||||
.ore_random_deriver
|
||||
.split_pos(pos.x(), block_y, pos.z());
|
||||
.split_pos(pos.x, block_y, pos.z);
|
||||
|
||||
let vein_ridged_sample = router.vein_ridged(pos, sample_options);
|
||||
if random.next_f32() <= 0.7f32 && vein_ridged_sample < 0f64 {
|
||||
|
||||
@ -3,10 +3,10 @@ use std::mem;
|
||||
|
||||
use super::{
|
||||
chunk_noise_router::{ChunkNoiseFunctionComponent, MutableChunkNoiseFunctionComponentImpl},
|
||||
density_function::{IndexToNoisePos, NoiseFunctionComponentRange, NoisePos},
|
||||
density_function::{IndexToNoisePos, NoiseFunctionComponentRange},
|
||||
};
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use pumpkin_util::math::{lerp, lerp3};
|
||||
use pumpkin_util::math::{lerp, lerp3, vector3::Vector3};
|
||||
|
||||
use crate::generation::{biome_coords, positions::chunk_pos};
|
||||
|
||||
@ -106,7 +106,7 @@ pub enum SampleAction {
|
||||
}
|
||||
|
||||
pub struct ChunkNoiseFunctionSampleOptions {
|
||||
populating_caches: bool,
|
||||
pub(crate) populating_caches: bool,
|
||||
pub(crate) action: SampleAction,
|
||||
|
||||
// Global IDs for the `CacheOnce` wrapper
|
||||
@ -314,7 +314,7 @@ impl MutableChunkNoiseFunctionComponentImpl for DensityInterpolator {
|
||||
fn sample(
|
||||
&mut self,
|
||||
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> f64 {
|
||||
match &sample_options.action {
|
||||
@ -402,11 +402,11 @@ impl MutableChunkNoiseFunctionComponentImpl for FlatCache {
|
||||
fn sample(
|
||||
&mut self,
|
||||
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> f64 {
|
||||
let absolute_biome_x_position = biome_coords::from_block(pos.x());
|
||||
let absolute_biome_z_position = biome_coords::from_block(pos.z());
|
||||
let absolute_biome_x_position = biome_coords::from_block(pos.x);
|
||||
let absolute_biome_z_position = biome_coords::from_block(pos.z);
|
||||
|
||||
let relative_biome_x_position = absolute_biome_x_position - self.start_biome_x;
|
||||
let relative_biome_z_position = absolute_biome_z_position - self.start_biome_z;
|
||||
@ -498,10 +498,10 @@ impl MutableChunkNoiseFunctionComponentImpl for Cache2D {
|
||||
fn sample(
|
||||
&mut self,
|
||||
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> f64 {
|
||||
let packed_column = chunk_pos::packed(pos.x() as u64, pos.z() as u64);
|
||||
let packed_column = chunk_pos::packed(pos.x as u64, pos.z as u64);
|
||||
if packed_column == self.last_sample_column {
|
||||
self.last_sample_result
|
||||
} else {
|
||||
@ -561,7 +561,7 @@ impl MutableChunkNoiseFunctionComponentImpl for CacheOnce {
|
||||
fn sample(
|
||||
&mut self,
|
||||
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> f64 {
|
||||
match sample_options.action {
|
||||
@ -663,7 +663,7 @@ impl MutableChunkNoiseFunctionComponentImpl for CellCache {
|
||||
fn sample(
|
||||
&mut self,
|
||||
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> f64 {
|
||||
match &sample_options.action {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use pumpkin_data::noise_router::WrapperType;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
use crate::generation::biome_coords;
|
||||
|
||||
@ -10,8 +11,8 @@ use super::{
|
||||
FlatCache, SampleAction,
|
||||
},
|
||||
density_function::{
|
||||
IndexToNoisePos, NoiseFunctionComponentRange, NoisePos, PassThrough,
|
||||
StaticIndependentChunkNoiseFunctionComponentImpl, UnblendedNoisePos,
|
||||
IndexToNoisePos, NoiseFunctionComponentRange, PassThrough,
|
||||
StaticIndependentChunkNoiseFunctionComponentImpl,
|
||||
},
|
||||
proto_noise_router::{
|
||||
DependentProtoNoiseFunctionComponent, IndependentProtoNoiseFunctionComponent,
|
||||
@ -24,7 +25,7 @@ pub trait StaticChunkNoiseFunctionComponentImpl {
|
||||
fn sample(
|
||||
&self,
|
||||
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> f64;
|
||||
|
||||
@ -47,7 +48,7 @@ pub trait MutableChunkNoiseFunctionComponentImpl {
|
||||
fn sample(
|
||||
&mut self,
|
||||
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> f64;
|
||||
|
||||
@ -189,7 +190,7 @@ impl MutableChunkNoiseFunctionComponentImpl for ChunkNoiseFunctionComponent<'_>
|
||||
fn sample(
|
||||
&mut self,
|
||||
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> f64 {
|
||||
match self {
|
||||
@ -225,7 +226,6 @@ impl MutableChunkNoiseFunctionComponentImpl for ChunkNoiseFunctionComponent<'_>
|
||||
mapper,
|
||||
sample_options,
|
||||
),
|
||||
//Self::Panic(message) => panic!("{}", message),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -233,7 +233,7 @@ impl MutableChunkNoiseFunctionComponentImpl for ChunkNoiseFunctionComponent<'_>
|
||||
impl ChunkNoiseFunctionComponent<'_> {
|
||||
pub fn sample_from_stack(
|
||||
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> f64 {
|
||||
let (top_component, component_stack) = component_stack.split_last_mut().unwrap();
|
||||
@ -259,7 +259,7 @@ impl ChunkNoiseDensityFunction<'_> {
|
||||
#[inline]
|
||||
pub fn sample(
|
||||
&mut self,
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> f64 {
|
||||
ChunkNoiseFunctionComponent::sample_from_stack(self.component_stack, pos, sample_options)
|
||||
@ -286,7 +286,7 @@ macro_rules! sample_function {
|
||||
#[inline]
|
||||
pub fn $name(
|
||||
&mut self,
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> f64 {
|
||||
ChunkNoiseFunctionComponent::sample_from_stack(
|
||||
@ -421,11 +421,7 @@ impl<'a> ChunkNoiseRouter<'a> {
|
||||
let block_z_position =
|
||||
biome_coords::to_block(absolute_biome_z_position);
|
||||
|
||||
let pos = UnblendedNoisePos::new(
|
||||
block_x_position,
|
||||
0,
|
||||
block_z_position,
|
||||
);
|
||||
let pos = Vector3::new(block_x_position, 0, block_z_position);
|
||||
|
||||
//NOTE: Due to our stack invariant, what is on the stack is a
|
||||
// valid density function
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use pumpkin_data::noise_router::{BinaryData, BinaryOperation, ClampData, LinearData, UnaryData};
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
use crate::generation::noise::router::{
|
||||
chunk_density_function::ChunkNoiseFunctionSampleOptions,
|
||||
@ -6,8 +7,7 @@ use crate::generation::noise::router::{
|
||||
};
|
||||
|
||||
use super::{
|
||||
IndexToNoisePos, NoiseFunctionComponentRange, NoisePos,
|
||||
StaticIndependentChunkNoiseFunctionComponentImpl,
|
||||
IndexToNoisePos, NoiseFunctionComponentRange, StaticIndependentChunkNoiseFunctionComponentImpl,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -34,7 +34,7 @@ impl NoiseFunctionComponentRange for Constant {
|
||||
}
|
||||
|
||||
impl StaticIndependentChunkNoiseFunctionComponentImpl for Constant {
|
||||
fn sample(&self, _pos: &impl NoisePos) -> f64 {
|
||||
fn sample(&self, _pos: &Vector3<i32>) -> f64 {
|
||||
self.value
|
||||
}
|
||||
|
||||
@ -67,7 +67,7 @@ impl StaticChunkNoiseFunctionComponentImpl for Linear {
|
||||
fn sample(
|
||||
&self,
|
||||
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> f64 {
|
||||
let input_density = ChunkNoiseFunctionComponent::sample_from_stack(
|
||||
@ -120,7 +120,7 @@ impl StaticChunkNoiseFunctionComponentImpl for Binary {
|
||||
fn sample(
|
||||
&self,
|
||||
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> f64 {
|
||||
let input1_density = ChunkNoiseFunctionComponent::sample_from_stack(
|
||||
@ -296,7 +296,7 @@ impl StaticChunkNoiseFunctionComponentImpl for Unary {
|
||||
fn sample(
|
||||
&self,
|
||||
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> f64 {
|
||||
let input_density = ChunkNoiseFunctionComponent::sample_from_stack(
|
||||
@ -370,7 +370,7 @@ impl StaticChunkNoiseFunctionComponentImpl for Clamp {
|
||||
fn sample(
|
||||
&self,
|
||||
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> f64 {
|
||||
let input_density = ChunkNoiseFunctionComponent::sample_from_stack(
|
||||
|
||||
@ -4,7 +4,7 @@ use pumpkin_data::noise_router::{
|
||||
ClampedYGradientData, RangeChoiceData, WeirdScaledData, WeirdScaledMapper,
|
||||
};
|
||||
use pumpkin_util::{
|
||||
math::clamped_map,
|
||||
math::{clamped_map, vector3::Vector3},
|
||||
noise::simplex::SimplexNoiseSampler,
|
||||
random::{RandomImpl, legacy_rand::LegacyRand},
|
||||
};
|
||||
@ -18,8 +18,7 @@ use crate::generation::{
|
||||
};
|
||||
|
||||
use super::{
|
||||
IndexToNoisePos, NoiseFunctionComponentRange, NoisePos,
|
||||
StaticIndependentChunkNoiseFunctionComponentImpl,
|
||||
IndexToNoisePos, NoiseFunctionComponentRange, StaticIndependentChunkNoiseFunctionComponentImpl,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -80,8 +79,8 @@ impl NoiseFunctionComponentRange for EndIsland {
|
||||
}
|
||||
|
||||
impl StaticIndependentChunkNoiseFunctionComponentImpl for EndIsland {
|
||||
fn sample(&self, pos: &impl NoisePos) -> f64 {
|
||||
(Self::sample_2d(&self.sampler, pos.x() / 8, pos.z() / 8) as f64 - 8.0) / 128.0
|
||||
fn sample(&self, pos: &Vector3<i32>) -> f64 {
|
||||
(Self::sample_2d(&self.sampler, pos.x / 8, pos.z / 8) as f64 - 8.0) / 128.0
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,7 +108,7 @@ impl StaticChunkNoiseFunctionComponentImpl for WeirdScaled {
|
||||
fn sample(
|
||||
&self,
|
||||
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> f64 {
|
||||
let input_density = ChunkNoiseFunctionComponent::sample_from_stack(
|
||||
@ -122,9 +121,9 @@ impl StaticChunkNoiseFunctionComponentImpl for WeirdScaled {
|
||||
* self
|
||||
.sampler
|
||||
.sample(
|
||||
pos.x() as f64 / scaled_density,
|
||||
pos.y() as f64 / scaled_density,
|
||||
pos.z() as f64 / scaled_density,
|
||||
pos.x as f64 / scaled_density,
|
||||
pos.y as f64 / scaled_density,
|
||||
pos.z as f64 / scaled_density,
|
||||
)
|
||||
.abs()
|
||||
}
|
||||
@ -150,9 +149,9 @@ impl StaticChunkNoiseFunctionComponentImpl for WeirdScaled {
|
||||
* self
|
||||
.sampler
|
||||
.sample(
|
||||
pos.x() as f64 / scaled_density,
|
||||
pos.y() as f64 / scaled_density,
|
||||
pos.z() as f64 / scaled_density,
|
||||
pos.x as f64 / scaled_density,
|
||||
pos.y as f64 / scaled_density,
|
||||
pos.z as f64 / scaled_density,
|
||||
)
|
||||
.abs();
|
||||
});
|
||||
@ -195,9 +194,9 @@ impl NoiseFunctionComponentRange for ClampedYGradient {
|
||||
}
|
||||
|
||||
impl StaticIndependentChunkNoiseFunctionComponentImpl for ClampedYGradient {
|
||||
fn sample(&self, pos: &impl NoisePos) -> f64 {
|
||||
fn sample(&self, pos: &Vector3<i32>) -> f64 {
|
||||
clamped_map(
|
||||
pos.y() as f64,
|
||||
pos.y as f64,
|
||||
self.data.from_y,
|
||||
self.data.to_y,
|
||||
self.data.from_value,
|
||||
@ -252,7 +251,7 @@ impl StaticChunkNoiseFunctionComponentImpl for RangeChoice {
|
||||
fn sample(
|
||||
&self,
|
||||
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> f64 {
|
||||
let input_sample = ChunkNoiseFunctionComponent::sample_from_stack(
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use pumpkin_data::noise_router::WrapperType;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
// These are for enum_dispatch
|
||||
use super::chunk_density_function::{
|
||||
@ -18,48 +19,12 @@ mod test;
|
||||
#[cfg(test)]
|
||||
mod test_deserializer;
|
||||
|
||||
pub trait NoisePos {
|
||||
fn x(&self) -> i32;
|
||||
fn y(&self) -> i32;
|
||||
fn z(&self) -> i32;
|
||||
}
|
||||
|
||||
pub struct UnblendedNoisePos {
|
||||
x: i32,
|
||||
y: i32,
|
||||
z: i32,
|
||||
}
|
||||
|
||||
impl UnblendedNoisePos {
|
||||
#[must_use]
|
||||
pub const fn new(x: i32, y: i32, z: i32) -> Self {
|
||||
Self { x, y, z }
|
||||
}
|
||||
}
|
||||
|
||||
impl NoisePos for UnblendedNoisePos {
|
||||
#[inline]
|
||||
fn x(&self) -> i32 {
|
||||
self.x
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn y(&self) -> i32 {
|
||||
self.y
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn z(&self) -> i32 {
|
||||
self.z
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IndexToNoisePos {
|
||||
fn at(
|
||||
&self,
|
||||
index: usize,
|
||||
sample_options: Option<&mut ChunkNoiseFunctionSampleOptions>,
|
||||
) -> impl NoisePos + 'static;
|
||||
) -> Vector3<i32>;
|
||||
}
|
||||
|
||||
#[enum_dispatch]
|
||||
@ -70,7 +35,7 @@ pub trait NoiseFunctionComponentRange {
|
||||
|
||||
#[enum_dispatch]
|
||||
pub trait StaticIndependentChunkNoiseFunctionComponentImpl: NoiseFunctionComponentRange {
|
||||
fn sample(&self, pos: &impl NoisePos) -> f64;
|
||||
fn sample(&self, pos: &Vector3<i32>) -> f64;
|
||||
fn fill(&self, array: &mut [f64], mapper: &impl IndexToNoisePos) {
|
||||
array.iter_mut().enumerate().for_each(|(index, value)| {
|
||||
let pos = mapper.at(index, None);
|
||||
|
||||
@ -2,7 +2,9 @@ use std::array;
|
||||
|
||||
use pumpkin_data::noise_router::{InterpolatedNoiseSamplerData, NoiseData, ShiftedNoiseData};
|
||||
use pumpkin_util::{
|
||||
math::clamped_lerp, noise::perlin::OctavePerlinNoiseSampler, random::RandomGenerator,
|
||||
math::{clamped_lerp, vector3::Vector3},
|
||||
noise::perlin::OctavePerlinNoiseSampler,
|
||||
random::RandomGenerator,
|
||||
};
|
||||
|
||||
use crate::generation::{
|
||||
@ -13,9 +15,7 @@ use crate::generation::{
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
NoiseFunctionComponentRange, NoisePos, StaticIndependentChunkNoiseFunctionComponentImpl,
|
||||
};
|
||||
use super::{NoiseFunctionComponentRange, StaticIndependentChunkNoiseFunctionComponentImpl};
|
||||
|
||||
pub struct Noise {
|
||||
sampler: DoublePerlinNoiseSampler,
|
||||
@ -41,11 +41,11 @@ impl NoiseFunctionComponentRange for Noise {
|
||||
}
|
||||
|
||||
impl StaticIndependentChunkNoiseFunctionComponentImpl for Noise {
|
||||
fn sample(&self, pos: &impl NoisePos) -> f64 {
|
||||
fn sample(&self, pos: &Vector3<i32>) -> f64 {
|
||||
self.sampler.sample(
|
||||
pos.x() as f64 * self.data.xz_scale,
|
||||
pos.y() as f64 * self.data.y_scale,
|
||||
pos.z() as f64 * self.data.xz_scale,
|
||||
pos.x as f64 * self.data.xz_scale,
|
||||
pos.y as f64 * self.data.y_scale,
|
||||
pos.z as f64 * self.data.xz_scale,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -78,8 +78,8 @@ impl NoiseFunctionComponentRange for ShiftA {
|
||||
}
|
||||
|
||||
impl StaticIndependentChunkNoiseFunctionComponentImpl for ShiftA {
|
||||
fn sample(&self, pos: &impl NoisePos) -> f64 {
|
||||
shift_sample_3d(&self.sampler, pos.x() as f64, 0.0, pos.z() as f64)
|
||||
fn sample(&self, pos: &Vector3<i32>) -> f64 {
|
||||
shift_sample_3d(&self.sampler, pos.x as f64, 0.0, pos.z as f64)
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,8 +106,8 @@ impl NoiseFunctionComponentRange for ShiftB {
|
||||
}
|
||||
|
||||
impl StaticIndependentChunkNoiseFunctionComponentImpl for ShiftB {
|
||||
fn sample(&self, pos: &impl NoisePos) -> f64 {
|
||||
shift_sample_3d(&self.sampler, pos.z() as f64, pos.x() as f64, 0.0)
|
||||
fn sample(&self, pos: &Vector3<i32>) -> f64 {
|
||||
shift_sample_3d(&self.sampler, pos.z as f64, pos.x as f64, 0.0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,22 +135,22 @@ impl StaticChunkNoiseFunctionComponentImpl for ShiftedNoise {
|
||||
fn sample(
|
||||
&self,
|
||||
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> f64 {
|
||||
let translated_x = pos.x() as f64 * self.data.xz_scale
|
||||
let translated_x = pos.x as f64 * self.data.xz_scale
|
||||
+ ChunkNoiseFunctionComponent::sample_from_stack(
|
||||
&mut component_stack[..=self.input_x_index],
|
||||
pos,
|
||||
sample_options,
|
||||
);
|
||||
let translated_y = pos.y() as f64 * self.data.y_scale
|
||||
let translated_y = pos.y as f64 * self.data.y_scale
|
||||
+ ChunkNoiseFunctionComponent::sample_from_stack(
|
||||
&mut component_stack[..=self.input_y_index],
|
||||
pos,
|
||||
sample_options,
|
||||
);
|
||||
let translated_z = pos.z() as f64 * self.data.xz_scale
|
||||
let translated_z = pos.z as f64 * self.data.xz_scale
|
||||
+ ChunkNoiseFunctionComponent::sample_from_stack(
|
||||
&mut component_stack[..=self.input_z_index],
|
||||
pos,
|
||||
@ -235,10 +235,10 @@ impl NoiseFunctionComponentRange for InterpolatedNoiseSampler {
|
||||
}
|
||||
|
||||
impl StaticIndependentChunkNoiseFunctionComponentImpl for InterpolatedNoiseSampler {
|
||||
fn sample(&self, pos: &impl NoisePos) -> f64 {
|
||||
let d = pos.x() as f64 * self.data.scaled_xz_scale;
|
||||
let e = pos.y() as f64 * self.data.scaled_y_scale;
|
||||
let f = pos.z() as f64 * self.data.scaled_xz_scale;
|
||||
fn sample(&self, pos: &Vector3<i32>) -> f64 {
|
||||
let d = pos.x as f64 * self.data.scaled_xz_scale;
|
||||
let e = pos.y as f64 * self.data.scaled_y_scale;
|
||||
let f = pos.z as f64 * self.data.scaled_xz_scale;
|
||||
|
||||
let g = d / self.data.xz_factor;
|
||||
let h = e / self.data.y_factor;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use pumpkin_util::math::lerp;
|
||||
use pumpkin_util::math::{lerp, vector3::Vector3};
|
||||
|
||||
use crate::generation::noise::router::{
|
||||
chunk_density_function::ChunkNoiseFunctionSampleOptions,
|
||||
@ -6,7 +6,7 @@ use crate::generation::noise::router::{
|
||||
proto_noise_router::ProtoNoiseFunctionComponent,
|
||||
};
|
||||
|
||||
use super::{NoiseFunctionComponentRange, NoisePos};
|
||||
use super::NoiseFunctionComponentRange;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum SplineValue {
|
||||
@ -18,7 +18,7 @@ impl SplineValue {
|
||||
#[inline]
|
||||
fn sample(
|
||||
&self,
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> f32 {
|
||||
@ -151,7 +151,7 @@ impl Spline {
|
||||
|
||||
fn sample(
|
||||
&self,
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> f32 {
|
||||
@ -224,7 +224,7 @@ impl StaticChunkNoiseFunctionComponentImpl for SplineFunction {
|
||||
fn sample(
|
||||
&self,
|
||||
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||
pos: &impl NoisePos,
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> f64 {
|
||||
self.spline.sample(pos, component_stack, sample_options) as f64
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use pumpkin_data::noise_router::OVERWORLD_BASE_NOISE_ROUTER;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use pumpkin_util::{assert_eq_delta, read_data_from_file};
|
||||
use serde::Deserialize;
|
||||
use std::fs;
|
||||
@ -18,26 +19,7 @@ use crate::generation::noise::router::proto_noise_router::{
|
||||
};
|
||||
|
||||
use super::test_deserializer::DensityFunctionRepr;
|
||||
use super::{NoiseFunctionComponentRange, NoisePos, PassThrough};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TestNoisePos {
|
||||
x: i32,
|
||||
y: i32,
|
||||
z: i32,
|
||||
}
|
||||
|
||||
impl NoisePos for TestNoisePos {
|
||||
fn x(&self) -> i32 {
|
||||
self.x
|
||||
}
|
||||
fn y(&self) -> i32 {
|
||||
self.y
|
||||
}
|
||||
fn z(&self) -> i32 {
|
||||
self.z
|
||||
}
|
||||
}
|
||||
use super::{NoiseFunctionComponentRange, PassThrough};
|
||||
|
||||
// This is a dummy value because we are not actually building chunk-specific functions
|
||||
static TEST_OPTIONS: ChunkNoiseFunctionBuilderOptions =
|
||||
@ -131,7 +113,7 @@ macro_rules! sample_surface_router_function {
|
||||
// This is equivalent to a Java `NoiseRouter` after being passed into `NoiseConfig` but before being
|
||||
// passed into `ChunkNoiseGenerator`
|
||||
fn normal_surface_noisified() {
|
||||
let pos = TestNoisePos { x: 0, y: 0, z: 0 };
|
||||
let pos = Vector3 { x: 0, y: 0, z: 0 };
|
||||
// TODO: Move these values to a file and create an extractor for them
|
||||
assert_eq!(
|
||||
sample_noise_router_function!(barrier_noise, pos),
|
||||
@ -307,7 +289,7 @@ fn normal_surface_noisified() {
|
||||
((100, 200, 100), 0.0f64),
|
||||
];
|
||||
for ((x, y, z), value) in values {
|
||||
let pos = TestNoisePos { x, y, z };
|
||||
let pos = Vector3 { x, y, z };
|
||||
assert_eq!(sample_noise_router_function!(vein_toggle, pos), value);
|
||||
}
|
||||
|
||||
@ -439,7 +421,7 @@ fn normal_surface_noisified() {
|
||||
((100, 200, 100), -0.07999999821186066f64),
|
||||
];
|
||||
for ((x, y, z), value) in values {
|
||||
let pos = TestNoisePos { x, y, z };
|
||||
let pos = Vector3 { x, y, z };
|
||||
assert_eq!(sample_noise_router_function!(vein_ridged, pos), value);
|
||||
}
|
||||
|
||||
@ -571,7 +553,7 @@ fn normal_surface_noisified() {
|
||||
((100, 200, 100), 0.3867496985743827f64),
|
||||
];
|
||||
for ((x, y, z), value) in values {
|
||||
let pos = TestNoisePos { x, y, z };
|
||||
let pos = Vector3 { x, y, z };
|
||||
assert_eq!(sample_noise_router_function!(vein_gap, pos), value);
|
||||
}
|
||||
}
|
||||
@ -589,7 +571,7 @@ fn config_final_density() {
|
||||
|
||||
// This is a lot of data it iter over, one two skip a few done
|
||||
for (x, y, z, sample) in expected_data.into_iter().step_by(5) {
|
||||
let pos = TestNoisePos { x, y, z };
|
||||
let pos = Vector3 { x, y, z };
|
||||
assert_eq_delta!(
|
||||
function.sample(&pos, &TEST_SAMPLE_OPTIONS),
|
||||
sample,
|
||||
@ -659,7 +641,7 @@ fn base_sloped_cheese() {
|
||||
let expected_data: Vec<(i32, i32, i32, f64)> =
|
||||
read_data_from_file!("../../../../../assets/converted_sloped_cheese_7_4.json");
|
||||
for (x, y, z, sample) in expected_data {
|
||||
let pos = TestNoisePos { x, y, z };
|
||||
let pos = Vector3 { x, y, z };
|
||||
assert_eq_delta!(
|
||||
function.sample(&pos, &TEST_SAMPLE_OPTIONS),
|
||||
sample,
|
||||
@ -678,7 +660,7 @@ fn base_factor() {
|
||||
let expected_data: Vec<(i32, i32, i32, f64)> =
|
||||
read_data_from_file!("../../../../../assets/converted_factor_7_4.json");
|
||||
for (x, y, z, sample) in expected_data {
|
||||
let pos = TestNoisePos { x, y, z };
|
||||
let pos = Vector3 { x, y, z };
|
||||
assert_eq_delta!(
|
||||
function.sample(&pos, &TEST_SAMPLE_OPTIONS),
|
||||
sample,
|
||||
@ -697,7 +679,7 @@ fn base_depth() {
|
||||
let expected_data: Vec<(i32, i32, i32, f64)> =
|
||||
read_data_from_file!("../../../../../assets/converted_depth_7_4.json");
|
||||
for (x, y, z, sample) in expected_data {
|
||||
let pos = TestNoisePos { x, y, z };
|
||||
let pos = Vector3 { x, y, z };
|
||||
assert_eq_delta!(
|
||||
function.sample(&pos, &TEST_SAMPLE_OPTIONS),
|
||||
sample,
|
||||
@ -716,7 +698,7 @@ fn base_offset() {
|
||||
let expected_data: Vec<(i32, i32, i32, f64)> =
|
||||
read_data_from_file!("../../../../../assets/converted_offset_7_4.json");
|
||||
for (x, y, z, sample) in expected_data {
|
||||
let pos = TestNoisePos { x, y, z };
|
||||
let pos = Vector3 { x, y, z };
|
||||
assert_eq_delta!(
|
||||
function.sample(&pos, &TEST_SAMPLE_OPTIONS),
|
||||
sample,
|
||||
@ -735,7 +717,7 @@ fn base_cave_entrances() {
|
||||
let expected_data: Vec<(i32, i32, i32, f64)> =
|
||||
read_data_from_file!("../../../../../assets/converted_cave_entrances_overworld_7_4.json");
|
||||
for (x, y, z, sample) in expected_data {
|
||||
let pos = TestNoisePos { x, y, z };
|
||||
let pos = Vector3 { x, y, z };
|
||||
assert_eq_delta!(
|
||||
function.sample(&pos, &TEST_SAMPLE_OPTIONS),
|
||||
sample,
|
||||
@ -754,7 +736,7 @@ fn base_3d_noise() {
|
||||
let expected_data: Vec<(i32, i32, i32, f64)> =
|
||||
read_data_from_file!("../../../../../assets/converted_3d_overworld_7_4.json");
|
||||
for (x, y, z, sample) in expected_data {
|
||||
let pos = TestNoisePos { x, y, z };
|
||||
let pos = Vector3 { x, y, z };
|
||||
assert_eq_delta!(
|
||||
function.sample(&pos, &TEST_SAMPLE_OPTIONS),
|
||||
sample,
|
||||
@ -776,7 +758,7 @@ fn base_spahetti_roughness() {
|
||||
"../../../../../assets/converted_cave_spaghetti_rough_overworld_7_4.json"
|
||||
);
|
||||
for (x, y, z, sample) in expected_data {
|
||||
let pos = TestNoisePos { x, y, z };
|
||||
let pos = Vector3 { x, y, z };
|
||||
assert_eq_delta!(
|
||||
function.sample(&pos, &TEST_SAMPLE_OPTIONS),
|
||||
sample,
|
||||
@ -795,7 +777,7 @@ fn base_cave_noodle() {
|
||||
let expected_data: Vec<(i32, i32, i32, f64)> =
|
||||
read_data_from_file!("../../../../../assets/converted_cave_noodle_7_4.json");
|
||||
for (x, y, z, sample) in expected_data {
|
||||
let pos = TestNoisePos { x, y, z };
|
||||
let pos = Vector3 { x, y, z };
|
||||
assert_eq_delta!(
|
||||
function.sample(&pos, &TEST_SAMPLE_OPTIONS),
|
||||
sample,
|
||||
@ -814,7 +796,7 @@ fn base_cave_pillars() {
|
||||
let expected_data: Vec<(i32, i32, i32, f64)> =
|
||||
read_data_from_file!("../../../../../assets/converted_cave_pillar_7_4.json");
|
||||
for (x, y, z, sample) in expected_data {
|
||||
let pos = TestNoisePos { x, y, z };
|
||||
let pos = Vector3 { x, y, z };
|
||||
assert_eq_delta!(
|
||||
function.sample(&pos, &TEST_SAMPLE_OPTIONS),
|
||||
sample,
|
||||
@ -835,7 +817,7 @@ fn base_spaghetti_2d_thickness() {
|
||||
let expected_data: Vec<(i32, i32, i32, f64)> =
|
||||
read_data_from_file!("../../../../../assets/converted_cave_spaghetti_2d_thicc_7_4.json");
|
||||
for (x, y, z, sample) in expected_data {
|
||||
let pos = TestNoisePos { x, y, z };
|
||||
let pos = Vector3 { x, y, z };
|
||||
assert_eq_delta!(
|
||||
function.sample(&pos, &TEST_SAMPLE_OPTIONS),
|
||||
sample,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use pumpkin_data::noise_router::WrapperType;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
use crate::{
|
||||
biome::multi_noise::{NoiseValuePoint, to_long},
|
||||
@ -11,7 +12,7 @@ use super::{
|
||||
SampleAction,
|
||||
},
|
||||
chunk_noise_router::ChunkNoiseFunctionComponent,
|
||||
density_function::{NoiseFunctionComponentRange, PassThrough, UnblendedNoisePos},
|
||||
density_function::{NoiseFunctionComponentRange, PassThrough},
|
||||
proto_noise_router::{ProtoMultiNoiseRouter, ProtoNoiseFunctionComponent},
|
||||
};
|
||||
|
||||
@ -52,7 +53,7 @@ impl<'a> MultiNoiseSampler<'a> {
|
||||
let block_y = biome_coords::to_block(biome_y);
|
||||
let block_z = biome_coords::to_block(biome_z);
|
||||
|
||||
let pos = UnblendedNoisePos::new(block_x, block_y, block_z);
|
||||
let pos = Vector3::new(block_x, block_y, block_z);
|
||||
let sample_options =
|
||||
ChunkNoiseFunctionSampleOptions::new(false, SampleAction::SkipCellCaches, 0, 0, 0);
|
||||
|
||||
@ -103,7 +104,7 @@ impl<'a> MultiNoiseSampler<'a> {
|
||||
}
|
||||
|
||||
pub fn sample_erosion(&mut self, block_x: i32, block_y: i32, block_z: i32) -> f64 {
|
||||
let pos = UnblendedNoisePos::new(block_x, block_y, block_z);
|
||||
let pos = Vector3::new(block_x, block_y, block_z);
|
||||
let sample_options =
|
||||
ChunkNoiseFunctionSampleOptions::new(false, SampleAction::SkipCellCaches, 0, 0, 0);
|
||||
|
||||
@ -178,11 +179,7 @@ impl<'a> MultiNoiseSampler<'a> {
|
||||
let block_z_position =
|
||||
biome_coords::to_block(absolute_biome_z_position);
|
||||
|
||||
let pos = UnblendedNoisePos::new(
|
||||
block_x_position,
|
||||
0,
|
||||
block_z_position,
|
||||
);
|
||||
let pos = Vector3::new(block_x_position, 0, block_z_position);
|
||||
|
||||
//NOTE: Due to our stack invariant, what is on the stack is a
|
||||
// valid density function
|
||||
|
||||
@ -14,7 +14,7 @@ use super::{
|
||||
chunk_density_function::ChunkNoiseFunctionSampleOptions,
|
||||
chunk_noise_router::{ChunkNoiseFunctionComponent, StaticChunkNoiseFunctionComponentImpl},
|
||||
density_function::{
|
||||
IndexToNoisePos, NoiseFunctionComponentRange, NoisePos, PassThrough,
|
||||
IndexToNoisePos, NoiseFunctionComponentRange, PassThrough,
|
||||
StaticIndependentChunkNoiseFunctionComponentImpl, Wrapper,
|
||||
math::{Binary, Clamp, Constant, Linear, Unary},
|
||||
misc::{ClampedYGradient, EndIsland, RangeChoice, WeirdScaled},
|
||||
@ -22,6 +22,7 @@ use super::{
|
||||
spline::{Spline, SplineFunction, SplinePoint, SplineValue},
|
||||
},
|
||||
};
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
#[enum_dispatch(
|
||||
StaticIndependentChunkNoiseFunctionComponentImpl,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use pumpkin_data::noise_router::WrapperType;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::generation::{biome_coords, positions::chunk_pos};
|
||||
|
||||
@ -10,7 +10,7 @@ use super::{
|
||||
SampleAction,
|
||||
},
|
||||
chunk_noise_router::ChunkNoiseFunctionComponent,
|
||||
density_function::{NoiseFunctionComponentRange, PassThrough, UnblendedNoisePos},
|
||||
density_function::{NoiseFunctionComponentRange, PassThrough},
|
||||
proto_noise_router::{ProtoNoiseFunctionComponent, ProtoSurfaceEstimator},
|
||||
};
|
||||
|
||||
@ -58,7 +58,7 @@ pub struct SurfaceHeightEstimateSampler<'a> {
|
||||
component_stack: Box<[ChunkNoiseFunctionComponent<'a>]>,
|
||||
|
||||
// TODO: Can this be a flat map? I think the aquifer sampler samples outside of the chunk
|
||||
cache: HashMap<u64, i32>,
|
||||
cache: FxHashMap<u64, i32>,
|
||||
}
|
||||
|
||||
impl<'a> SurfaceHeightEstimateSampler<'a> {
|
||||
@ -90,7 +90,7 @@ impl<'a> SurfaceHeightEstimateSampler<'a> {
|
||||
let mid = low + ((high - low) / 2);
|
||||
let stepped_mid = mid - (mid % self.y_level_step_count as i32);
|
||||
|
||||
let pos = UnblendedNoisePos::new(aligned_x, stepped_mid, aligned_z);
|
||||
let pos = Vector3::new(aligned_x, stepped_mid, aligned_z);
|
||||
let density = ChunkNoiseFunctionComponent::sample_from_stack(
|
||||
&mut self.component_stack,
|
||||
&pos,
|
||||
@ -172,11 +172,7 @@ impl<'a> SurfaceHeightEstimateSampler<'a> {
|
||||
let block_z_position =
|
||||
biome_coords::to_block(absolute_biome_z_position);
|
||||
|
||||
let pos = UnblendedNoisePos::new(
|
||||
block_x_position,
|
||||
0,
|
||||
block_z_position,
|
||||
);
|
||||
let pos = Vector3::new(block_x_position, 0, block_z_position);
|
||||
|
||||
//NOTE: Due to our stack invariant, what is on the stack is a
|
||||
// valid density function
|
||||
@ -216,7 +212,7 @@ impl<'a> SurfaceHeightEstimateSampler<'a> {
|
||||
minimum_y: build_options.minimum_y,
|
||||
y_level_step_count: build_options.y_level_step_count,
|
||||
|
||||
cache: HashMap::new(),
|
||||
cache: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use std::collections::HashMap;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
|
||||
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::tag;
|
||||
@ -13,6 +13,7 @@ use pumpkin_util::{
|
||||
math::{position::BlockPos, vector3::Vector3},
|
||||
random::{RandomGenerator, get_decorator_seed, xoroshiro128::Xoroshiro},
|
||||
};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use super::{
|
||||
GlobalRandomConfig, biome_coords,
|
||||
@ -23,7 +24,6 @@ use super::{
|
||||
},
|
||||
positions::chunk_pos::{start_block_x, start_block_z},
|
||||
section_coords,
|
||||
settings::GenerationSettings,
|
||||
surface::{MaterialRuleContext, estimate_surface_height, terrain::SurfaceTerrainBuilder},
|
||||
};
|
||||
use crate::chunk::{ChunkData, ChunkHeightmapType};
|
||||
@ -38,6 +38,7 @@ use crate::generation::noise::{CHUNK_DIM, ChunkNoiseGenerator, LAVA_BLOCK, WATER
|
||||
use crate::generation::structure::placement::StructurePlacementCalculator;
|
||||
use crate::generation::structure::structures::StructureInstance;
|
||||
use crate::generation::structure::{STRUCTURE_SETS, STRUCTURES, StructureKeys, WeightedEntry};
|
||||
use crate::generation::surface::rule::try_apply_material_rule;
|
||||
use crate::{
|
||||
BlockStateId, ProtoNoiseRouters,
|
||||
biome::{BiomeSupplier, MultiNoiseBiomeSupplier, end::TheEndBiomeSupplier},
|
||||
@ -142,7 +143,7 @@ pub struct ProtoChunk {
|
||||
flat_ocean_floor_height_map: Box<[i16]>,
|
||||
pub flat_motion_blocking_height_map: Box<[i16]>,
|
||||
pub flat_motion_blocking_no_leaves_height_map: Box<[i16]>,
|
||||
structure_starts: HashMap<StructureKeys, StructureInstance>,
|
||||
structure_starts: FxHashMap<StructureKeys, StructureInstance>,
|
||||
|
||||
// Height of the chunk for indexing
|
||||
height: u16,
|
||||
@ -201,7 +202,7 @@ impl ProtoChunk {
|
||||
flat_ocean_floor_height_map: default_heightmap.clone(),
|
||||
flat_motion_blocking_height_map: default_heightmap.clone(),
|
||||
flat_motion_blocking_no_leaves_height_map: default_heightmap,
|
||||
structure_starts: HashMap::new(),
|
||||
structure_starts: FxHashMap::default(),
|
||||
height,
|
||||
bottom_y: dimension.min_y as i8,
|
||||
stage: StagedChunkEnum::Empty,
|
||||
@ -313,45 +314,24 @@ impl ProtoChunk {
|
||||
self.bottom_y
|
||||
}
|
||||
|
||||
fn maybe_update_surface_height_map(&mut self, local_x: i32, y: i32, local_z: i32) {
|
||||
let index = Self::local_position_to_height_map_index(local_x, local_z);
|
||||
fn maybe_update_surface_height_map(&mut self, index: usize, y: i16) {
|
||||
let current_height = self.flat_surface_height_map[index];
|
||||
|
||||
if y > current_height as i32 {
|
||||
self.flat_surface_height_map[index] = y as _;
|
||||
}
|
||||
self.flat_surface_height_map[index] = current_height.max(y) as _;
|
||||
}
|
||||
|
||||
fn maybe_update_ocean_floor_height_map(&mut self, local_x: i32, y: i32, local_z: i32) {
|
||||
let index = Self::local_position_to_height_map_index(local_x, local_z);
|
||||
fn maybe_update_ocean_floor_height_map(&mut self, index: usize, y: i16) {
|
||||
let current_height = self.flat_ocean_floor_height_map[index];
|
||||
|
||||
if y > current_height as i32 {
|
||||
self.flat_ocean_floor_height_map[index] = y as _;
|
||||
}
|
||||
self.flat_ocean_floor_height_map[index] = current_height.max(y) as _;
|
||||
}
|
||||
|
||||
fn maybe_update_motion_blocking_height_map(&mut self, local_x: i32, y: i32, local_z: i32) {
|
||||
let index = Self::local_position_to_height_map_index(local_x, local_z);
|
||||
fn maybe_update_motion_blocking_height_map(&mut self, index: usize, y: i16) {
|
||||
let current_height = self.flat_motion_blocking_height_map[index];
|
||||
|
||||
if y > current_height as i32 {
|
||||
self.flat_motion_blocking_height_map[index] = y as _;
|
||||
}
|
||||
self.flat_motion_blocking_height_map[index] = current_height.max(y) as _;
|
||||
}
|
||||
|
||||
fn maybe_update_motion_blocking_no_leaves_height_map(
|
||||
&mut self,
|
||||
local_x: i32,
|
||||
y: i32,
|
||||
local_z: i32,
|
||||
) {
|
||||
let index = Self::local_position_to_height_map_index(local_x, local_z);
|
||||
fn maybe_update_motion_blocking_no_leaves_height_map(&mut self, index: usize, y: i16) {
|
||||
let current_height = self.flat_motion_blocking_no_leaves_height_map[index];
|
||||
|
||||
if y > current_height as i32 {
|
||||
self.flat_motion_blocking_no_leaves_height_map[index] = y as _;
|
||||
}
|
||||
self.flat_motion_blocking_no_leaves_height_map[index] = current_height.max(y) as _;
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
@ -407,13 +387,10 @@ impl ProtoChunk {
|
||||
|
||||
#[inline]
|
||||
fn local_pos_to_block_index(&self, x: i32, y: i32, z: i32) -> usize {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
assert!((0..=15).contains(&x));
|
||||
assert!(y < self.height() as i32);
|
||||
assert!(y >= 0);
|
||||
assert!((0..=15).contains(&z));
|
||||
};
|
||||
debug_assert!((0..16).contains(&x), "x out of bounds: {}", x);
|
||||
debug_assert!((0..16).contains(&z), "z out of bounds: {}", z);
|
||||
debug_assert!(y >= 0 && y < self.height() as i32, "y out of bounds: {}", y);
|
||||
|
||||
self.height() as usize * CHUNK_DIM as usize * x as usize
|
||||
+ CHUNK_DIM as usize * y as usize
|
||||
+ z as usize
|
||||
@ -422,22 +399,17 @@ impl ProtoChunk {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn local_biome_pos_to_biome_index(&self, x: i32, y: i32, z: i32) -> usize {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
assert!((0..=3).contains(&x));
|
||||
assert!(
|
||||
y < biome_coords::from_chunk(self.height() as i32) && y >= 0,
|
||||
"{} - {} vs {}",
|
||||
0,
|
||||
biome_coords::from_chunk(self.height() as i32),
|
||||
let biome_height = self.height() as usize >> 2;
|
||||
|
||||
debug_assert!((0..4).contains(&x), "Biome X out of bounds: {}", x);
|
||||
debug_assert!((0..4).contains(&z), "Biome Z out of bounds: {}", z);
|
||||
debug_assert!(
|
||||
y >= 0 && y < biome_height as i32,
|
||||
"Biome Y out of bounds: {}",
|
||||
y
|
||||
);
|
||||
assert!((0..=3).contains(&z));
|
||||
};
|
||||
|
||||
biome_coords::from_block(self.height() as usize)
|
||||
* biome_coords::from_block(CHUNK_DIM as usize)
|
||||
* x as usize
|
||||
biome_height * biome_coords::from_block(CHUNK_DIM as usize) * x as usize
|
||||
+ biome_coords::from_block(CHUNK_DIM as usize) * y as usize
|
||||
+ z as usize
|
||||
}
|
||||
@ -474,18 +446,20 @@ impl ProtoChunk {
|
||||
return;
|
||||
}
|
||||
if !block_state.is_air() {
|
||||
self.maybe_update_surface_height_map(local_x, y, local_z);
|
||||
let index = Self::local_position_to_height_map_index(local_x, local_z);
|
||||
let y = y as i16;
|
||||
self.maybe_update_surface_height_map(index, y);
|
||||
let block = Block::get_raw_id_from_state_id(block_state.id);
|
||||
|
||||
let blocks_movement = blocks_movement(block_state, block);
|
||||
if blocks_movement {
|
||||
self.maybe_update_ocean_floor_height_map(local_x, y, local_z);
|
||||
self.maybe_update_ocean_floor_height_map(index, y);
|
||||
}
|
||||
if blocks_movement || block_state.is_liquid() {
|
||||
self.maybe_update_motion_blocking_height_map(local_x, y, local_z);
|
||||
self.maybe_update_motion_blocking_height_map(index, y);
|
||||
if !tag::Block::MINECRAFT_LEAVES.1.contains(&block) {
|
||||
{
|
||||
self.maybe_update_motion_blocking_no_leaves_height_map(local_x, y, local_z);
|
||||
self.maybe_update_motion_blocking_no_leaves_height_map(index, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -542,7 +516,10 @@ impl ProtoChunk {
|
||||
let start_z = start_block_z(self.z);
|
||||
|
||||
let sampler = FluidLevelSampler::Chunk(StandardChunkFluidLevelSampler::new(
|
||||
FluidLevel::new(settings.sea_level, settings.default_fluid.name),
|
||||
FluidLevel::new(
|
||||
settings.sea_level,
|
||||
Block::from_registry_key(settings.default_fluid.name).unwrap(),
|
||||
),
|
||||
FluidLevel::new(-54, &Block::LAVA),
|
||||
));
|
||||
|
||||
@ -873,7 +850,8 @@ impl ProtoChunk {
|
||||
context.block_pos_y,
|
||||
context.block_pos_z,
|
||||
);
|
||||
let new_state = settings.surface_rule.try_apply(
|
||||
let new_state = try_apply_material_rule(
|
||||
&settings.surface_rule,
|
||||
self,
|
||||
&mut context,
|
||||
surface_height_estimate_sampler,
|
||||
|
||||
@ -1,104 +1,104 @@
|
||||
use std::{collections::HashMap, sync::LazyLock};
|
||||
// use std::{collections::HashMap, sync::LazyLock};
|
||||
|
||||
use pumpkin_data::dimension::Dimension;
|
||||
use pumpkin_util::include_json_static;
|
||||
use serde::Deserialize;
|
||||
// use pumpkin_data::dimension::Dimension;
|
||||
// use pumpkin_util::include_json_static;
|
||||
// use serde::Deserialize;
|
||||
|
||||
use crate::block::BlockStateCodec;
|
||||
// use crate::block::BlockStateCodec;
|
||||
|
||||
use super::{biome_coords::to_block, height_limit::HeightLimitView, surface::rule::MaterialRule};
|
||||
// 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 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()
|
||||
}
|
||||
// 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,
|
||||
}
|
||||
// #[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)
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
// #[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
|
||||
}
|
||||
}
|
||||
// #[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());
|
||||
// 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 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_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
|
||||
};
|
||||
// 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
// Self {
|
||||
// min_y: new_min,
|
||||
// height: new_height,
|
||||
// size_horizontal: self.size_horizontal,
|
||||
// size_vertical: self.size_vertical,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
@ -321,7 +321,7 @@ impl StructurePiece {
|
||||
let block_pos = self.offset_pos(x, y, z);
|
||||
|
||||
if !box_limit.contains_pos(&block_pos) {
|
||||
log::warn!("Structure out of bounds");
|
||||
log::debug!("Structure out of bounds");
|
||||
return Block::AIR.default_state;
|
||||
}
|
||||
|
||||
@ -341,7 +341,7 @@ impl StructurePiece {
|
||||
|
||||
// Bounds and logic checks
|
||||
if !box_limit.contains_pos(&block_pos) {
|
||||
log::warn!("Structure out of bounds");
|
||||
log::debug!("Structure out of bounds");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
use std::{cell::RefCell, num::NonZeroUsize};
|
||||
|
||||
use lru::LruCache;
|
||||
use pumpkin_data::chunk::Biome;
|
||||
use pumpkin_data::{
|
||||
chunk::Biome,
|
||||
chunk_gen_settings::{
|
||||
AboveYMaterialCondition, MaterialCondition, NoiseThresholdMaterialCondition,
|
||||
NotMaterialCondition, StoneDepthMaterialCondition, VerticalGradientMaterialCondition,
|
||||
WaterMaterialCondition,
|
||||
},
|
||||
};
|
||||
use pumpkin_util::{
|
||||
math::{lerp2, vertical_surface_type::VerticalSurfaceType},
|
||||
random::{RandomDeriver, RandomDeriverImpl, RandomImpl},
|
||||
@ -9,7 +13,6 @@ use pumpkin_util::{
|
||||
use serde::Deserialize;
|
||||
|
||||
use terrain::SurfaceTerrainBuilder;
|
||||
use thread_local::ThreadLocal;
|
||||
|
||||
use crate::{
|
||||
ProtoChunk,
|
||||
@ -22,7 +25,6 @@ use super::{
|
||||
proto_noise_router::DoublePerlinNoiseBuilder,
|
||||
surface_height_sampler::SurfaceHeightEstimateSampler,
|
||||
},
|
||||
y_offset::YOffset,
|
||||
};
|
||||
|
||||
pub mod rule;
|
||||
@ -138,49 +140,24 @@ impl<'a> MaterialRuleContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum MaterialCondition {
|
||||
#[serde(rename = "minecraft:biome")]
|
||||
Biome(BiomeMaterialCondition),
|
||||
#[serde(rename = "minecraft:noise_threshold")]
|
||||
NoiseThreshold(NoiseThresholdMaterialCondition),
|
||||
#[serde(rename = "minecraft:vertical_gradient")]
|
||||
VerticalGradient(Box<VerticalGradientMaterialCondition>),
|
||||
#[serde(rename = "minecraft:y_above")]
|
||||
YAbove(AboveYMaterialCondition),
|
||||
#[serde(rename = "minecraft:water")]
|
||||
Water(WaterMaterialCondition),
|
||||
#[serde(rename = "minecraft:temperature")]
|
||||
Temperature,
|
||||
#[serde(rename = "minecraft:steep")]
|
||||
Steep,
|
||||
#[serde(rename = "minecraft:not")]
|
||||
Not(NotMaterialCondition),
|
||||
#[serde(rename = "minecraft:hole")]
|
||||
Hole(HoleMaterialCondition),
|
||||
#[serde(rename = "minecraft:above_preliminary_surface")]
|
||||
AbovePreliminarySurface(SurfaceMaterialCondition),
|
||||
#[serde(rename = "minecraft:stone_depth")]
|
||||
StoneDepth(StoneDepthMaterialCondition),
|
||||
}
|
||||
|
||||
impl MaterialCondition {
|
||||
#[expect(clippy::similar_names)]
|
||||
pub fn test(
|
||||
&self,
|
||||
pub fn test_condition(
|
||||
condition: &MaterialCondition,
|
||||
chunk: &mut ProtoChunk,
|
||||
context: &mut MaterialRuleContext,
|
||||
surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
|
||||
) -> bool {
|
||||
match self {
|
||||
Self::Biome(biome) => biome.test(context),
|
||||
Self::NoiseThreshold(noise_threshold) => noise_threshold.test(context),
|
||||
Self::VerticalGradient(vertical_gradient) => vertical_gradient.test(context),
|
||||
Self::YAbove(above_y) => above_y.test(context),
|
||||
Self::Water(water) => water.test(context),
|
||||
Self::Temperature => {
|
||||
match condition {
|
||||
MaterialCondition::Biome(biome) => BiomeMaterialCondition::test(biome.biome_is, context),
|
||||
MaterialCondition::NoiseThreshold(noise_threshold) => {
|
||||
test_noise_threshold(noise_threshold, context)
|
||||
}
|
||||
MaterialCondition::VerticalGradient(vertical_gradient) => {
|
||||
test_vertical_gradient(vertical_gradient, context)
|
||||
}
|
||||
MaterialCondition::YAbove(above_y) => test_above_y_material(above_y, context),
|
||||
MaterialCondition::Water(water) => test_water_material(water, context),
|
||||
MaterialCondition::Temperature => {
|
||||
let temperature = context.biome.weather.compute_temperature(
|
||||
context.block_pos_x as f64,
|
||||
context.block_pos_y,
|
||||
@ -189,7 +166,7 @@ impl MaterialCondition {
|
||||
);
|
||||
temperature < 0.15f32
|
||||
}
|
||||
Self::Steep => {
|
||||
MaterialCondition::Steep => {
|
||||
let local_x = context.block_pos_x & 15;
|
||||
let local_z = context.block_pos_z & 15;
|
||||
|
||||
@ -211,13 +188,14 @@ impl MaterialCondition {
|
||||
sub_height >= add_height + 4
|
||||
}
|
||||
}
|
||||
Self::Not(not) => not.test(chunk, context, surface_height_estimate_sampler),
|
||||
Self::Hole(_hole) => HoleMaterialCondition::test(context),
|
||||
Self::AbovePreliminarySurface(_above) => {
|
||||
MaterialCondition::Not(not) => {
|
||||
test_not_material(not, chunk, context, surface_height_estimate_sampler)
|
||||
}
|
||||
MaterialCondition::Hole(_hole) => HoleMaterialCondition::test(context),
|
||||
MaterialCondition::AbovePreliminarySurface(_above) => {
|
||||
SurfaceMaterialCondition::test(context, surface_height_estimate_sampler)
|
||||
}
|
||||
Self::StoneDepth(stone_depth) => stone_depth.test(context),
|
||||
}
|
||||
MaterialCondition::StoneDepth(stone_depth) => test_stone_depth(stone_depth, context),
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,42 +208,32 @@ impl HoleMaterialCondition {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct AboveYMaterialCondition {
|
||||
anchor: YOffset,
|
||||
surface_depth_multiplier: i32,
|
||||
add_stone_depth: bool,
|
||||
}
|
||||
|
||||
impl AboveYMaterialCondition {
|
||||
pub const fn test(&self, context: &MaterialRuleContext) -> bool {
|
||||
pub const fn test_above_y_material(
|
||||
condition: &AboveYMaterialCondition,
|
||||
context: &MaterialRuleContext,
|
||||
) -> bool {
|
||||
context.block_pos_y
|
||||
+ if self.add_stone_depth {
|
||||
+ if condition.add_stone_depth {
|
||||
context.stone_depth_above
|
||||
} else {
|
||||
0
|
||||
}
|
||||
>= self.anchor.get_y(context.min_y as i16, context.height)
|
||||
+ context.run_depth * self.surface_depth_multiplier
|
||||
}
|
||||
>= condition.anchor.get_y(context.min_y as i16, context.height)
|
||||
+ context.run_depth * condition.surface_depth_multiplier
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct NotMaterialCondition {
|
||||
invert: Box<MaterialCondition>,
|
||||
}
|
||||
|
||||
impl NotMaterialCondition {
|
||||
pub fn test(
|
||||
&self,
|
||||
pub fn test_not_material(
|
||||
condition: &NotMaterialCondition,
|
||||
chunk: &mut ProtoChunk,
|
||||
context: &mut MaterialRuleContext,
|
||||
surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
|
||||
) -> bool {
|
||||
!self
|
||||
.invert
|
||||
.test(chunk, context, surface_height_estimate_sampler)
|
||||
}
|
||||
!test_condition(
|
||||
condition.invert,
|
||||
chunk,
|
||||
context,
|
||||
surface_height_estimate_sampler,
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@ -324,54 +292,40 @@ pub fn estimate_surface_height(
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct BiomeMaterialCondition {
|
||||
biome_is: Box<[&'static Biome]>,
|
||||
}
|
||||
pub struct BiomeMaterialCondition;
|
||||
|
||||
impl BiomeMaterialCondition {
|
||||
pub fn test(&self, context: &MaterialRuleContext) -> bool {
|
||||
self.biome_is.contains(&context.biome)
|
||||
pub fn test(biome_is: &[&'static Biome], context: &MaterialRuleContext) -> bool {
|
||||
biome_is.contains(&context.biome)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct NoiseThresholdMaterialCondition {
|
||||
noise: String,
|
||||
min_threshold: f64,
|
||||
max_threshold: f64,
|
||||
}
|
||||
|
||||
impl NoiseThresholdMaterialCondition {
|
||||
pub fn test(&self, context: &mut MaterialRuleContext) -> bool {
|
||||
pub fn test_noise_threshold(
|
||||
condition: &NoiseThresholdMaterialCondition,
|
||||
context: &mut MaterialRuleContext,
|
||||
) -> bool {
|
||||
// TODO: we want to cache these
|
||||
let sampler = context
|
||||
.noise_builder
|
||||
.get_noise_sampler_for_id(self.noise.strip_prefix("minecraft:").unwrap());
|
||||
.get_noise_sampler_for_id(condition.noise.strip_prefix("minecraft:").unwrap());
|
||||
let value = sampler.sample(context.block_pos_x as f64, 0.0, context.block_pos_z as f64);
|
||||
value >= self.min_threshold && value <= self.max_threshold
|
||||
}
|
||||
value >= condition.min_threshold && value <= condition.max_threshold
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct StoneDepthMaterialCondition {
|
||||
offset: i32,
|
||||
add_surface_depth: bool,
|
||||
secondary_depth_range: i32,
|
||||
surface_type: VerticalSurfaceType,
|
||||
}
|
||||
|
||||
impl StoneDepthMaterialCondition {
|
||||
pub fn test(&self, context: &mut MaterialRuleContext) -> bool {
|
||||
let stone_depth = match &self.surface_type {
|
||||
pub fn test_stone_depth(
|
||||
condition: &StoneDepthMaterialCondition,
|
||||
context: &mut MaterialRuleContext,
|
||||
) -> bool {
|
||||
let stone_depth = match &condition.surface_type {
|
||||
VerticalSurfaceType::Ceiling => context.stone_depth_below,
|
||||
VerticalSurfaceType::Floor => context.stone_depth_above,
|
||||
};
|
||||
let depth = if self.add_surface_depth {
|
||||
let depth = if condition.add_surface_depth {
|
||||
context.run_depth
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let depth_range = if self.secondary_depth_range == 0 {
|
||||
let depth_range = if condition.secondary_depth_range == 0 {
|
||||
0
|
||||
} else {
|
||||
pumpkin_util::math::map(
|
||||
@ -379,71 +333,45 @@ impl StoneDepthMaterialCondition {
|
||||
-1.0,
|
||||
1.0,
|
||||
0.0,
|
||||
self.secondary_depth_range as f64,
|
||||
condition.secondary_depth_range as f64,
|
||||
) as i32
|
||||
};
|
||||
stone_depth <= 1 + self.offset + depth + depth_range
|
||||
}
|
||||
stone_depth <= 1 + condition.offset + depth + depth_range
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct WaterMaterialCondition {
|
||||
offset: i32,
|
||||
surface_depth_multiplier: i32,
|
||||
add_stone_depth: bool,
|
||||
}
|
||||
|
||||
impl WaterMaterialCondition {
|
||||
pub const fn test(&self, context: &MaterialRuleContext) -> bool {
|
||||
pub const fn test_water_material(
|
||||
condition: &WaterMaterialCondition,
|
||||
context: &MaterialRuleContext,
|
||||
) -> bool {
|
||||
context.fluid_height == i32::MIN
|
||||
|| context.block_pos_y
|
||||
+ (if self.add_stone_depth {
|
||||
+ (if condition.add_stone_depth {
|
||||
context.stone_depth_above
|
||||
} else {
|
||||
0
|
||||
})
|
||||
>= context.fluid_height
|
||||
+ self.offset
|
||||
+ context.run_depth * self.surface_depth_multiplier
|
||||
}
|
||||
+ condition.offset
|
||||
+ context.run_depth * condition.surface_depth_multiplier
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct VerticalGradientMaterialCondition {
|
||||
random_name: String,
|
||||
true_at_and_below: YOffset,
|
||||
false_at_and_above: YOffset,
|
||||
#[serde(skip)]
|
||||
random_deriver: ThreadLocal<RefCell<LruCache<usize, RandomDeriver>>>,
|
||||
}
|
||||
// random_deriver: ThreadLocal<RefCell<LruCache<usize, RandomDeriver>>>,
|
||||
|
||||
impl VerticalGradientMaterialCondition {
|
||||
pub fn test(&self, context: &MaterialRuleContext) -> bool {
|
||||
let true_at = self
|
||||
pub fn test_vertical_gradient(
|
||||
condition: &VerticalGradientMaterialCondition,
|
||||
context: &MaterialRuleContext,
|
||||
) -> bool {
|
||||
let true_at = condition
|
||||
.true_at_and_below
|
||||
.get_y(context.min_y as i16, context.height);
|
||||
let false_at = self
|
||||
let false_at = condition
|
||||
.false_at_and_above
|
||||
.get_y(context.min_y as i16, context.height);
|
||||
|
||||
let context_pointer: *const RandomDeriver = context.random_deriver;
|
||||
let key = context_pointer.addr();
|
||||
|
||||
let mut cache = self
|
||||
let splitter = context
|
||||
.random_deriver
|
||||
.get_or(|| {
|
||||
let cache_size = NonZeroUsize::new(128).unwrap();
|
||||
let cache = LruCache::new(cache_size);
|
||||
RefCell::new(cache)
|
||||
})
|
||||
.borrow_mut();
|
||||
|
||||
let splitter = cache.get_or_insert(key, || {
|
||||
context
|
||||
.random_deriver
|
||||
.split_string(&self.random_name)
|
||||
.next_splitter()
|
||||
});
|
||||
.split_string(condition.random_name)
|
||||
.next_splitter();
|
||||
|
||||
let block_y = context.block_pos_y;
|
||||
if block_y <= true_at {
|
||||
@ -452,9 +380,7 @@ impl VerticalGradientMaterialCondition {
|
||||
if block_y >= false_at {
|
||||
return false;
|
||||
}
|
||||
let mapped =
|
||||
pumpkin_util::math::map(block_y as f32, true_at as f32, false_at as f32, 1.0, 0.0);
|
||||
let mapped = pumpkin_util::math::map(block_y as f32, true_at as f32, false_at as f32, 1.0, 0.0);
|
||||
let mut random = splitter.split_pos(context.block_pos_x, block_y, context.block_pos_z);
|
||||
random.next_f32() < mapped
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,42 +1,37 @@
|
||||
use pumpkin_data::BlockState;
|
||||
use pumpkin_data::{
|
||||
BlockState,
|
||||
chunk_gen_settings::{
|
||||
BlockBlueprint, ConditionMaterialRule, MaterialRule, SequenceMaterialRule,
|
||||
},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use super::{MaterialCondition, MaterialRuleContext};
|
||||
use super::MaterialRuleContext;
|
||||
use crate::{
|
||||
ProtoChunk, block::BlockStateCodec,
|
||||
generation::noise::router::surface_height_sampler::SurfaceHeightEstimateSampler,
|
||||
ProtoChunk,
|
||||
block::to_state_from_blueprint,
|
||||
generation::{
|
||||
noise::router::surface_height_sampler::SurfaceHeightEstimateSampler,
|
||||
surface::test_condition,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum MaterialRule {
|
||||
#[serde(rename = "minecraft:bandlands")]
|
||||
Badlands(BadLandsMaterialRule),
|
||||
#[serde(rename = "minecraft:block")]
|
||||
Block(BlockMaterialRule),
|
||||
#[serde(rename = "minecraft:sequence")]
|
||||
Sequence(SequenceMaterialRule),
|
||||
#[serde(rename = "minecraft:condition")]
|
||||
Condition(ConditionMaterialRule),
|
||||
}
|
||||
|
||||
impl MaterialRule {
|
||||
pub fn try_apply(
|
||||
&self,
|
||||
pub fn try_apply_material_rule(
|
||||
rule: &MaterialRule,
|
||||
chunk: &mut ProtoChunk,
|
||||
context: &mut MaterialRuleContext,
|
||||
surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
|
||||
) -> Option<&'static BlockState> {
|
||||
match self {
|
||||
Self::Badlands(_badlands) => Some(BadLandsMaterialRule::try_apply(context)),
|
||||
Self::Block(block) => Some(block.try_apply()),
|
||||
Self::Sequence(sequence) => {
|
||||
sequence.try_apply(chunk, context, surface_height_estimate_sampler)
|
||||
}
|
||||
Self::Condition(condition) => {
|
||||
condition.try_apply(chunk, context, surface_height_estimate_sampler)
|
||||
match rule {
|
||||
MaterialRule::Badlands(_badlands) => Some(BadLandsMaterialRule::try_apply(context)),
|
||||
MaterialRule::Block(block) => Some(BlockMaterialRule::try_apply(&block.result_state)),
|
||||
MaterialRule::Sequence(sequence) => {
|
||||
try_apply_sequence(sequence, chunk, context, surface_height_estimate_sampler)
|
||||
}
|
||||
MaterialRule::Condition(condition) => {
|
||||
try_apply_condition(condition, chunk, context, surface_height_estimate_sampler)
|
||||
}
|
||||
MaterialRule::Unsupported => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,58 +49,48 @@ impl BadLandsMaterialRule {
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct BlockMaterialRule {
|
||||
result_state: BlockStateCodec,
|
||||
}
|
||||
pub struct BlockMaterialRule;
|
||||
|
||||
impl BlockMaterialRule {
|
||||
pub fn try_apply(&self) -> &'static BlockState {
|
||||
self.result_state.get_state()
|
||||
pub fn try_apply(blueprint: &BlockBlueprint) -> &'static BlockState {
|
||||
to_state_from_blueprint(blueprint)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SequenceMaterialRule {
|
||||
sequence: Vec<MaterialRule>,
|
||||
}
|
||||
|
||||
impl SequenceMaterialRule {
|
||||
pub fn try_apply(
|
||||
&self,
|
||||
pub fn try_apply_sequence(
|
||||
rule: &SequenceMaterialRule,
|
||||
chunk: &mut ProtoChunk,
|
||||
context: &mut MaterialRuleContext,
|
||||
surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
|
||||
) -> Option<&'static BlockState> {
|
||||
for seq in &self.sequence {
|
||||
if let Some(state) = seq.try_apply(chunk, context, surface_height_estimate_sampler) {
|
||||
for seq in rule.sequence {
|
||||
if let Some(state) =
|
||||
try_apply_material_rule(seq, chunk, context, surface_height_estimate_sampler)
|
||||
{
|
||||
return Some(state);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ConditionMaterialRule {
|
||||
if_true: MaterialCondition,
|
||||
then_run: Box<MaterialRule>,
|
||||
}
|
||||
|
||||
impl ConditionMaterialRule {
|
||||
pub fn try_apply(
|
||||
&self,
|
||||
pub fn try_apply_condition(
|
||||
rule: &ConditionMaterialRule,
|
||||
chunk: &mut ProtoChunk,
|
||||
context: &mut MaterialRuleContext,
|
||||
surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
|
||||
) -> Option<&'static BlockState> {
|
||||
if self
|
||||
.if_true
|
||||
.test(chunk, context, surface_height_estimate_sampler)
|
||||
{
|
||||
return self
|
||||
.then_run
|
||||
.try_apply(chunk, context, surface_height_estimate_sampler);
|
||||
if test_condition(
|
||||
&rule.if_true,
|
||||
chunk,
|
||||
context,
|
||||
surface_height_estimate_sampler,
|
||||
) {
|
||||
return try_apply_material_rule(
|
||||
rule.then_run,
|
||||
chunk,
|
||||
context,
|
||||
surface_height_estimate_sampler,
|
||||
);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,7 +153,7 @@ impl SurfaceTerrainBuilder {
|
||||
for y in (chunk.bottom_y() as i32..=elevation_y).rev() {
|
||||
let pos = Vector3::new(global_x, y, global_z);
|
||||
let block_id = chunk.get_block_state(&pos).to_block_id();
|
||||
if block_id == Block::get_raw_id_from_state_id(chunk.default_block.id) {
|
||||
if block_id == chunk.default_block.id {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@ -22,14 +22,13 @@ use pumpkin_data::dimension::Dimension;
|
||||
use pumpkin_data::{Block, block_properties::has_random_ticks, fluid::Fluid};
|
||||
use pumpkin_util::math::{position::BlockPos, vector2::Vector2};
|
||||
use pumpkin_util::world_seed::Seed;
|
||||
use rand::{RngExt, SeedableRng, rngs::SmallRng};
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
use tokio::time::timeout;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
// use std::time::Duration;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
path::PathBuf,
|
||||
sync::{
|
||||
Arc,
|
||||
@ -232,7 +231,7 @@ impl Level {
|
||||
let arc_chunk = Arc::new(RwLock::new(ChunkEntityData {
|
||||
x: pos.x,
|
||||
z: pos.y,
|
||||
data: HashMap::new(),
|
||||
data: FxHashMap::default(),
|
||||
dirty: true,
|
||||
}));
|
||||
|
||||
@ -341,11 +340,6 @@ impl Level {
|
||||
self.loaded_chunks.len()
|
||||
}
|
||||
|
||||
pub async fn clean_up_log(&self) {
|
||||
self.chunk_saver.clean_up_log().await;
|
||||
self.entity_saver.clean_up_log().await;
|
||||
}
|
||||
|
||||
pub fn list_cached(&self) {
|
||||
for entry in self.loaded_chunks.iter() {
|
||||
log::debug!("In map: {:?}", entry.key());
|
||||
@ -404,51 +398,37 @@ impl Level {
|
||||
!self.mark_chunks_as_not_watched(&[chunk]).await.is_empty()
|
||||
}
|
||||
|
||||
// In Level::clean_entity_chunks()
|
||||
pub fn clean_entity_chunks(self: &Arc<Self>, chunks: &[Vector2<i32>]) {
|
||||
// Care needs to be take here because of interweaving case:
|
||||
// 1) Remove chunk from cache
|
||||
// 2) Another player wants same chunk
|
||||
// 3) Load (old) chunk from serializer
|
||||
// 4) Write (new) chunk from serializer
|
||||
// Now outdated chunk data is cached and will be written later
|
||||
|
||||
let chunks_with_no_watchers = chunks
|
||||
let chunks_to_process: Vec<_> = chunks
|
||||
.iter()
|
||||
.filter_map(|pos| {
|
||||
// Only chunks that have no entry in the watcher map or have 0 watchers
|
||||
if self
|
||||
// Only include chunks with no watchers
|
||||
let has_watchers = self
|
||||
.chunk_watchers
|
||||
.get(pos)
|
||||
.is_none_or(|count| count.is_zero())
|
||||
{
|
||||
self.loaded_entity_chunks
|
||||
.get(pos)
|
||||
.map(|chunk| (*pos, chunk.value().clone()))
|
||||
} else {
|
||||
None
|
||||
.is_some_and(|count| !count.is_zero());
|
||||
|
||||
if has_watchers {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Remove immediately to prevent race conditions
|
||||
self.loaded_entity_chunks.remove(pos)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.collect();
|
||||
|
||||
if chunks_to_process.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let level = self.clone();
|
||||
self.spawn_task(async move {
|
||||
let chunks_to_remove = chunks_with_no_watchers.clone();
|
||||
level.write_entity_chunks(chunks_with_no_watchers).await;
|
||||
// Only after we have written the chunks to the serializer do we remove them from the
|
||||
// cache
|
||||
for (pos, _) in chunks_to_remove {
|
||||
let _ = level.loaded_entity_chunks.remove_if(&pos, |_, _| {
|
||||
// Recheck that there is no one watching
|
||||
level
|
||||
.chunk_watchers
|
||||
.get(&pos)
|
||||
.is_none_or(|count| count.is_zero())
|
||||
});
|
||||
}
|
||||
log::debug!("Writing {} entity chunks to disk", chunks_to_process.len());
|
||||
level.write_entity_chunks(chunks_to_process).await;
|
||||
});
|
||||
}
|
||||
|
||||
// Gets random ticks, block ticks and fluid ticks
|
||||
pub async fn get_tick_data(&self) -> TickData {
|
||||
let mut ticks = TickData {
|
||||
block_ticks: Vec::new(),
|
||||
@ -457,75 +437,52 @@ impl Level {
|
||||
block_entities: Vec::new(),
|
||||
};
|
||||
|
||||
let mut rng = SmallRng::from_rng(&mut rand::rng());
|
||||
let r = rand::random::<u32>();
|
||||
|
||||
for chunk_sync in self.loaded_chunks.iter() {
|
||||
// Try to get a READ lock first.
|
||||
// Most chunks won't have pending ticks, so this is very fast.
|
||||
let chunk = chunk_sync.read().await;
|
||||
let mut chunk = chunk_sync.write().await;
|
||||
|
||||
let chunk_x_base = chunk.x * 16;
|
||||
let chunk_z_base = chunk.z * 16;
|
||||
|
||||
let mut section_blocks = Vec::new();
|
||||
for i in 0..chunk.section.sections.len() {
|
||||
let mut section_block_data = Vec::new();
|
||||
|
||||
//TODO use game rules to determine how many random ticks to perform
|
||||
for _ in 0..3 {
|
||||
let r = rng.random::<u32>();
|
||||
let x_offset = (r & 0xF) as i32;
|
||||
let y_offset = ((r >> 4) & 0xF) as i32 - 32;
|
||||
let z_offset = (r >> 8 & 0xF) as i32;
|
||||
|
||||
let random_pos = BlockPos::new(
|
||||
chunk_x_base + x_offset,
|
||||
i as i32 * 16 + y_offset,
|
||||
chunk_z_base + z_offset,
|
||||
);
|
||||
|
||||
let block_state_id = chunk
|
||||
.section
|
||||
.get_block_absolute_y(x_offset as usize, random_pos.0.y, z_offset as usize)
|
||||
.unwrap_or(Block::AIR.default_state.id);
|
||||
|
||||
section_block_data.push((random_pos, block_state_id));
|
||||
}
|
||||
section_blocks.push(section_block_data);
|
||||
}
|
||||
|
||||
for section_data in section_blocks {
|
||||
ticks
|
||||
.random_ticks
|
||||
.extend(
|
||||
section_data
|
||||
.into_iter()
|
||||
.filter_map(|(random_pos, block_state_id)| {
|
||||
has_random_ticks(block_state_id).then_some(ScheduledTick {
|
||||
position: random_pos,
|
||||
delay: 0,
|
||||
priority: TickPriority::Normal,
|
||||
value: (),
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
let section_count = chunk.section.sections.len();
|
||||
|
||||
ticks
|
||||
.block_entities
|
||||
.extend(chunk.block_entities.values().cloned());
|
||||
|
||||
drop(chunk);
|
||||
let mut chunk_write = chunk_sync.write().await;
|
||||
ticks
|
||||
.block_ticks
|
||||
.append(&mut chunk_write.block_ticks.step_tick());
|
||||
ticks
|
||||
.fluid_ticks
|
||||
.append(&mut chunk_write.fluid_ticks.step_tick());
|
||||
for i in 0..section_count {
|
||||
let y_base = i as i32 * 16;
|
||||
for _ in 0..3 {
|
||||
let x_offset = (r & 0xF) as usize;
|
||||
let z_offset = (r >> 8 & 0xF) as usize;
|
||||
let y_in_section = ((r >> 4) & 0xF) as i32;
|
||||
let absolute_y = y_base + y_in_section;
|
||||
|
||||
if let Some(block_state_id) = chunk
|
||||
.section
|
||||
.get_block_absolute_y(x_offset, absolute_y, z_offset)
|
||||
&& has_random_ticks(block_state_id)
|
||||
{
|
||||
ticks.random_ticks.push(ScheduledTick {
|
||||
position: BlockPos::new(
|
||||
chunk_x_base + x_offset as i32,
|
||||
absolute_y,
|
||||
chunk_z_base + z_offset as i32,
|
||||
),
|
||||
delay: 0,
|
||||
priority: TickPriority::Normal,
|
||||
value: (),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ticks.block_ticks.append(&mut chunk.block_ticks.step_tick());
|
||||
ticks.fluid_ticks.append(&mut chunk.fluid_ticks.step_tick());
|
||||
}
|
||||
|
||||
ticks.block_ticks.sort_unstable();
|
||||
ticks.fluid_ticks.sort_unstable();
|
||||
|
||||
ticks
|
||||
}
|
||||
|
||||
@ -573,12 +530,20 @@ impl Level {
|
||||
lock.send_change();
|
||||
};
|
||||
|
||||
if let Some(chunk) = self.loaded_chunks.get(&pos) {
|
||||
let chunk = if let Some(chunk) = self.loaded_chunks.get(&pos) {
|
||||
chunk.clone()
|
||||
} else {
|
||||
recv.await
|
||||
.expect("Chunk listener dropped without sending chunk")
|
||||
};
|
||||
|
||||
{
|
||||
let mut lock = self.chunk_loading.lock().unwrap();
|
||||
lock.remove_ticket(pos, 31);
|
||||
lock.send_change();
|
||||
}
|
||||
|
||||
chunk
|
||||
}
|
||||
|
||||
async fn load_single_entity_chunk(
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use generation::settings::GenerationSettings;
|
||||
use pumpkin_data::{BlockState, dimension::Dimension};
|
||||
use pumpkin_data::{
|
||||
Block, BlockState, chunk_gen_settings::GenerationSettings, dimension::Dimension,
|
||||
};
|
||||
use pumpkin_util::math::vector2::Vector2;
|
||||
|
||||
pub mod biome;
|
||||
@ -45,7 +46,7 @@ macro_rules! global_path {
|
||||
// TODO: is there a way to do in-file benches?
|
||||
pub use generation::{
|
||||
GlobalRandomConfig, noise::router::proto_noise_router::ProtoNoiseRouters,
|
||||
proto_chunk::ProtoChunk, settings::GENERATION_SETTINGS, settings::GeneratorSetting,
|
||||
proto_chunk::ProtoChunk,
|
||||
};
|
||||
|
||||
use crate::generation::{
|
||||
@ -78,7 +79,10 @@ pub fn bench_create_and_populate_noise(
|
||||
let generation_shape = &settings.shape;
|
||||
let horizontal_cell_count = CHUNK_DIM / generation_shape.horizontal_cell_block_count();
|
||||
let sampler = FluidLevelSampler::Chunk(StandardChunkFluidLevelSampler::new(
|
||||
FluidLevel::new(settings.sea_level, settings.default_fluid.name),
|
||||
FluidLevel::new(
|
||||
settings.sea_level,
|
||||
Block::from_registry_key(settings.default_fluid.name).unwrap(),
|
||||
),
|
||||
FluidLevel::new(-54, &pumpkin_data::Block::LAVA),
|
||||
));
|
||||
|
||||
@ -205,7 +209,10 @@ pub fn bench_create_and_populate_noise_with_surface(
|
||||
|
||||
// Noise sampler
|
||||
let sampler = FluidLevelSampler::Chunk(StandardChunkFluidLevelSampler::new(
|
||||
FluidLevel::new(settings.sea_level, settings.default_fluid.name),
|
||||
FluidLevel::new(
|
||||
settings.sea_level,
|
||||
Block::from_registry_key(settings.default_fluid.name).unwrap(),
|
||||
),
|
||||
FluidLevel::new(-54, &pumpkin_data::Block::LAVA),
|
||||
));
|
||||
|
||||
|
||||
@ -121,7 +121,7 @@ where
|
||||
nbt.put_int("z", self.position.0.z);
|
||||
nbt.put_int("t", self.delay as i32);
|
||||
nbt.put_int("p", self.priority as i32);
|
||||
nbt.put_string("i", self.value.to_resource_location().to_string());
|
||||
nbt.put_string("i", self.value.to_resource_location());
|
||||
nbt.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::tick::{MAX_TICK_DELAY, OrderedTick, ScheduledTick};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ChunkTickScheduler<T> {
|
||||
tick_queue: [Vec<OrderedTick<T>>; MAX_TICK_DELAY],
|
||||
queued_ticks: HashSet<(BlockPos, T)>,
|
||||
queued_ticks: FxHashSet<(BlockPos, T)>,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
@ -79,7 +78,7 @@ impl<T> Default for ChunkTickScheduler<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
tick_queue: std::array::from_fn(|_| Vec::new()),
|
||||
queued_ticks: HashSet::new(),
|
||||
queued_ticks: FxHashSet::default(),
|
||||
offset: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use std::{
|
||||
fs::OpenOptions,
|
||||
fs::File,
|
||||
io::{Cursor, Read},
|
||||
path::Path,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
@ -82,10 +82,9 @@ impl WorldInfoReader for AnvilLevelInfo {
|
||||
fn read_world_info(&self, level_folder: &Path) -> Result<LevelData, WorldInfoError> {
|
||||
let path = level_folder.join(LEVEL_DAT_FILE_NAME);
|
||||
|
||||
let world_info_file = OpenOptions::new().read(true).open(path)?;
|
||||
let mut compression_reader = GzDecoder::new(world_info_file);
|
||||
let world_info_file = File::open(path)?;
|
||||
let mut buf = Vec::new();
|
||||
let _ = compression_reader.read_to_end(&mut buf)?;
|
||||
GzDecoder::new(world_info_file).read_to_end(&mut buf)?;
|
||||
|
||||
check_file_data_version(&buf)?;
|
||||
check_file_level_version(&buf)?;
|
||||
@ -114,11 +113,7 @@ impl WorldInfoWriter for AnvilLevelInfo {
|
||||
|
||||
// open file
|
||||
let path = level_folder.join(LEVEL_DAT_FILE_NAME);
|
||||
let world_info_file = OpenOptions::new()
|
||||
.truncate(true)
|
||||
.create(true)
|
||||
.write(true)
|
||||
.open(path)?;
|
||||
let world_info_file = File::create(path)?;
|
||||
|
||||
// write compressed data into file
|
||||
let compression_writer = GzEncoder::new(world_info_file, Compression::best());
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_data::{
|
||||
Block, BlockDirection, BlockState, FacingExt,
|
||||
@ -15,6 +15,7 @@ use pumpkin_world::{
|
||||
world::BlockFlags,
|
||||
};
|
||||
use rand::RngExt;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::{
|
||||
block::{
|
||||
@ -377,7 +378,7 @@ async fn move_piston(
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut moved_blocks_map: HashMap<BlockPos, &BlockState> = HashMap::new();
|
||||
let mut moved_blocks_map: FxHashMap<BlockPos, &BlockState> = FxHashMap::default();
|
||||
let moved_blocks: Vec<BlockPos> = handler.moved_blocks;
|
||||
|
||||
let mut moved_block_states: Vec<&BlockState> = Vec::new();
|
||||
|
||||
@ -97,7 +97,7 @@ use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_world::BlockStateId;
|
||||
use pumpkin_world::item::ItemStack;
|
||||
use pumpkin_world::world::{BlockAccessor, BlockFlags, BlockRegistryExt};
|
||||
use std::collections::HashMap;
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
@ -310,8 +310,8 @@ impl BlockActionResult {
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct BlockRegistry {
|
||||
blocks: HashMap<u16, Arc<dyn BlockBehaviour>>,
|
||||
fluids: HashMap<u16, Arc<dyn FluidBehaviour>>,
|
||||
blocks: FxHashMap<u16, Arc<dyn BlockBehaviour>>,
|
||||
fluids: FxHashMap<u16, Arc<dyn FluidBehaviour>>,
|
||||
}
|
||||
|
||||
impl BlockRegistryExt for BlockRegistry {
|
||||
|
||||
@ -30,7 +30,7 @@ impl CommandExecutor for Executor {
|
||||
[
|
||||
TextComponent::text(players.len().to_string()),
|
||||
TextComponent::text(server.basic_config.max_players.to_string()),
|
||||
TextComponent::text(get_player_names(players)),
|
||||
TextComponent::text(get_player_names(&players)),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
@ -39,7 +39,7 @@ impl CommandExecutor for Executor {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_player_names(players: Vec<Arc<Player>>) -> String {
|
||||
fn get_player_names(players: &[Arc<Player>]) -> String {
|
||||
let mut names = String::new();
|
||||
for player in players {
|
||||
if !names.is_empty() {
|
||||
|
||||
@ -6,7 +6,6 @@ use crate::command::{
|
||||
},
|
||||
tree::{CommandTree, builder::argument},
|
||||
};
|
||||
use pumpkin_util::resource_location::ResourceLocation;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
const NAMES: [&str; 1] = ["stopsound"];
|
||||
@ -37,7 +36,7 @@ impl CommandExecutor for Executor {
|
||||
sound
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.map(|s| ResourceLocation::vanilla(s.to_name()))
|
||||
.map(|s| format!("minecraft:{}", s.to_name()))
|
||||
.ok(),
|
||||
category.as_ref().map(|s| **s).ok(),
|
||||
)
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use pumpkin_protocol::java::client::play::CommandSuggestion;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use super::args::ConsumedArgs;
|
||||
|
||||
@ -56,8 +57,8 @@ impl CommandError {
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CommandDispatcher {
|
||||
pub commands: HashMap<String, Command>,
|
||||
pub permissions: HashMap<String, String>,
|
||||
pub commands: FxHashMap<String, Command>,
|
||||
pub permissions: FxHashMap<String, String>,
|
||||
}
|
||||
|
||||
/// Stores registered [`CommandTree`]s and dispatches commands to them.
|
||||
|
||||
@ -2,7 +2,7 @@ use core::f32;
|
||||
use std::sync::{
|
||||
Arc,
|
||||
atomic::{
|
||||
AtomicBool, AtomicU32,
|
||||
AtomicBool, AtomicU8, AtomicU32,
|
||||
Ordering::{self},
|
||||
},
|
||||
};
|
||||
@ -27,7 +27,7 @@ pub struct ItemEntity {
|
||||
// These cannot be atomic values because we mutate their state based on what they are; we run
|
||||
// into the ABA problem
|
||||
item_stack: Mutex<ItemStack>,
|
||||
pickup_delay: Mutex<u8>,
|
||||
pickup_delay: AtomicU8,
|
||||
health: AtomicCell<f32>,
|
||||
never_despawn: AtomicBool,
|
||||
never_pickup: AtomicBool,
|
||||
@ -47,7 +47,7 @@ impl ItemEntity {
|
||||
entity,
|
||||
item_stack: Mutex::new(item_stack),
|
||||
item_age: AtomicU32::new(0),
|
||||
pickup_delay: Mutex::new(10), // Vanilla pickup delay is 10 ticks
|
||||
pickup_delay: AtomicU8::new(10), // Vanilla pickup delay is 10 ticks
|
||||
health: AtomicCell::new(5.0),
|
||||
never_despawn: AtomicBool::new(false),
|
||||
never_pickup: AtomicBool::new(false),
|
||||
@ -66,7 +66,7 @@ impl ItemEntity {
|
||||
entity,
|
||||
item_stack: Mutex::new(item_stack),
|
||||
item_age: AtomicU32::new(0),
|
||||
pickup_delay: Mutex::new(pickup_delay), // Vanilla pickup delay is 10 ticks
|
||||
pickup_delay: AtomicU8::new(pickup_delay), // Vanilla pickup delay is 10 ticks
|
||||
health: AtomicCell::new(5.0),
|
||||
never_despawn: AtomicBool::new(false),
|
||||
never_pickup: AtomicBool::new(false),
|
||||
@ -109,8 +109,6 @@ impl ItemEntity {
|
||||
}
|
||||
|
||||
async fn try_merge_with(&self, other: &Self) {
|
||||
// Check if merge is possible
|
||||
|
||||
let self_stack = self.item_stack.lock().await;
|
||||
|
||||
let other_stack = other.item_stack.lock().await;
|
||||
@ -164,11 +162,10 @@ impl ItemEntity {
|
||||
target.never_pickup.store(never_pickup, Ordering::Relaxed);
|
||||
|
||||
if !never_pickup {
|
||||
let mut target_delay = target.pickup_delay.lock().await;
|
||||
|
||||
let delay = (*target_delay).max(*source.pickup_delay.lock().await);
|
||||
|
||||
*target_delay = delay;
|
||||
let source_delay = source.pickup_delay.load(Ordering::Relaxed);
|
||||
target
|
||||
.pickup_delay
|
||||
.fetch_max(source_delay, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
if empty1 {
|
||||
@ -195,11 +192,11 @@ impl EntityBase for ItemEntity {
|
||||
) -> EntityBaseFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
let entity = &self.entity;
|
||||
entity.tick(caller.clone(), server).await;
|
||||
{
|
||||
let mut delay = self.pickup_delay.lock().await;
|
||||
*delay = delay.saturating_sub(1);
|
||||
};
|
||||
self.pickup_delay
|
||||
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |val| {
|
||||
Some(val.saturating_sub(1))
|
||||
})
|
||||
.ok();
|
||||
|
||||
let original_velo = entity.velocity.load();
|
||||
|
||||
@ -321,7 +318,7 @@ impl EntityBase for ItemEntity {
|
||||
let velocity_dirty = entity.velocity_dirty.swap(false, Ordering::SeqCst)
|
||||
|| entity.touching_water.load(Ordering::SeqCst)
|
||||
|| entity.touching_lava.load(Ordering::SeqCst)
|
||||
|| entity.velocity.load().sub(&original_velo).length_squared() > 0.01;
|
||||
|| entity.velocity.load().sub(&original_velo).length_squared() > 0.1;
|
||||
|
||||
if velocity_dirty {
|
||||
entity.send_pos_rot().await;
|
||||
@ -373,18 +370,17 @@ impl EntityBase for ItemEntity {
|
||||
|
||||
fn on_player_collision<'a>(&'a self, player: &'a Arc<Player>) -> EntityBaseFuture<'a, ()> {
|
||||
Box::pin(async {
|
||||
let can_pickup = {
|
||||
let delay = self.pickup_delay.lock().await;
|
||||
*delay == 0
|
||||
};
|
||||
if self.pickup_delay.load(Ordering::Relaxed) > 0
|
||||
|| player.living_entity.health.load() <= 0.0
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if can_pickup
|
||||
&& player.living_entity.health.load() > 0.0
|
||||
&& (player
|
||||
if player
|
||||
.inventory
|
||||
.insert_stack_anywhere(&mut *self.item_stack.lock().await)
|
||||
.await
|
||||
|| player.is_creative())
|
||||
|| player.is_creative()
|
||||
{
|
||||
player
|
||||
.client
|
||||
@ -402,7 +398,6 @@ impl EntityBase for ItemEntity {
|
||||
.await
|
||||
.send_content_updates()
|
||||
.await;
|
||||
|
||||
if self.item_stack.lock().await.is_empty() {
|
||||
self.entity.remove().await;
|
||||
} else {
|
||||
|
||||
@ -203,16 +203,15 @@ impl ChunkManager {
|
||||
|
||||
{
|
||||
let mut lock = level.chunk_loading.lock().unwrap();
|
||||
lock.add_ticket(
|
||||
center,
|
||||
ChunkLoading::get_level_from_view_distance(view_distance),
|
||||
);
|
||||
let new_level = ChunkLoading::get_level_from_view_distance(view_distance);
|
||||
lock.add_ticket(center, new_level);
|
||||
|
||||
if old_center != center || old_view_distance != view_distance {
|
||||
lock.remove_ticket(
|
||||
old_center,
|
||||
ChunkLoading::get_level_from_view_distance(old_view_distance),
|
||||
);
|
||||
let old_level = ChunkLoading::get_level_from_view_distance(old_view_distance);
|
||||
// Don't remove if it would be the same ticket
|
||||
if old_center != center || old_level != new_level {
|
||||
lock.remove_ticket(old_center, old_level);
|
||||
}
|
||||
}
|
||||
lock.send_change();
|
||||
};
|
||||
@ -604,8 +603,6 @@ impl Player {
|
||||
level.loaded_chunk_count(),
|
||||
);
|
||||
|
||||
level.clean_up_log().await;
|
||||
|
||||
//self.world().level.list_cached();
|
||||
}
|
||||
|
||||
@ -1743,7 +1740,7 @@ impl Player {
|
||||
self.client
|
||||
.send_packet_now(&CRespawn::new(
|
||||
(new_world.dimension.id).into(),
|
||||
ResourceLocation::from(new_world.dimension.minecraft_name),
|
||||
new_world.dimension.minecraft_name.to_string(),
|
||||
biome::hash_seed(new_world.level.seed.0), // seed
|
||||
self.gamemode.load() as u8,
|
||||
self.gamemode.load() as i8,
|
||||
@ -2666,7 +2663,7 @@ impl NBTStorage for Player {
|
||||
|
||||
nbt.put_string(
|
||||
"Dimension",
|
||||
ResourceLocation::from(self.world().dimension.minecraft_name).to_string(),
|
||||
self.world().dimension.minecraft_name.to_string(),
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
@ -7,14 +7,14 @@ use pumpkin_data::item::Item;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use pumpkin_world::item::ItemStack;
|
||||
use std::collections::HashMap;
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::{ItemBehaviour, ItemMetadata};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ItemRegistry {
|
||||
items: HashMap<u16, Arc<dyn ItemBehaviour>>,
|
||||
items: FxHashMap<u16, Arc<dyn ItemBehaviour>>,
|
||||
}
|
||||
|
||||
impl ItemRegistry {
|
||||
|
||||
@ -402,7 +402,7 @@ impl BedrockClient {
|
||||
Self::handle_ack(&Ack::read(reader)?);
|
||||
}
|
||||
RAKNET_NACK => {
|
||||
dbg!("received nack, client is missing packets");
|
||||
log::debug!("received nack, client is missing packets");
|
||||
}
|
||||
0x80..0x8d => {
|
||||
self.handle_frame_set(server, FrameSet::read(reader)?).await;
|
||||
|
||||
@ -22,6 +22,8 @@ use pumpkin_protocol::{
|
||||
};
|
||||
use pumpkin_util::{Hand, text::TextComponent, version::MinecraftVersion};
|
||||
|
||||
const BRAND_CHANNEL_PREFIX: &str = "minecraft:brand";
|
||||
|
||||
impl JavaClient {
|
||||
pub async fn handle_client_information_config(
|
||||
&self,
|
||||
@ -59,11 +61,7 @@ impl JavaClient {
|
||||
|
||||
pub async fn handle_plugin_message(&self, plugin_message: SPluginMessage) {
|
||||
log::debug!("Handling plugin message");
|
||||
if plugin_message
|
||||
.channel
|
||||
.to_string()
|
||||
.starts_with("minecraft:brand")
|
||||
{
|
||||
if plugin_message.channel.starts_with(BRAND_CHANNEL_PREFIX) {
|
||||
log::debug!("Got a client brand");
|
||||
match str::from_utf8(&plugin_message.data) {
|
||||
Ok(brand) => *self.brand.lock().await = Some(brand.to_string()),
|
||||
|
||||
@ -57,7 +57,6 @@ pub mod config;
|
||||
pub mod handshake;
|
||||
pub mod login;
|
||||
pub mod play;
|
||||
pub mod remapper;
|
||||
pub mod status;
|
||||
|
||||
use crate::entity::player::Player;
|
||||
|
||||
@ -1 +0,0 @@
|
||||
pub mod registry;
|
||||
@ -1 +0,0 @@
|
||||
|
||||
@ -9,14 +9,15 @@ use pumpkin_protocol::{
|
||||
java::client::{config::CPluginMessage, status::CStatusResponse},
|
||||
};
|
||||
use pumpkin_world::CURRENT_MC_VERSION;
|
||||
use std::{fs::File, io::Read, path::Path};
|
||||
use std::{
|
||||
fs::{self},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
const DEFAULT_ICON: &[u8] = include_bytes!("../../../assets/default_icon.png");
|
||||
|
||||
fn load_icon_from_file<P: AsRef<Path>>(path: P) -> Result<String, Box<dyn error::Error>> {
|
||||
let mut icon_file = File::open(path)?;
|
||||
let mut buf = Vec::new();
|
||||
icon_file.read_to_end(&mut buf)?;
|
||||
let buf = fs::read(path)?;
|
||||
Ok(load_icon_from_bytes(&buf))
|
||||
}
|
||||
|
||||
|
||||
@ -38,10 +38,8 @@ impl KeyStore {
|
||||
}
|
||||
|
||||
fn generate_private_key() -> RsaPrivateKey {
|
||||
// Found out that OsRng is faster than rand::thread_rng here
|
||||
let mut rng = rand::rng();
|
||||
|
||||
// let pub_key = RsaPublicKey::from(&priv_key);
|
||||
RsaPrivateKey::new(&mut rng, 1024).expect("Failed to generate a key")
|
||||
}
|
||||
|
||||
|
||||
@ -585,6 +585,20 @@ impl Server {
|
||||
players
|
||||
}
|
||||
|
||||
pub fn for_each_player<F>(&self, mut f: F)
|
||||
where
|
||||
F: FnMut(&Arc<Player>),
|
||||
{
|
||||
let worlds = self.worlds.load();
|
||||
|
||||
for world in worlds.iter() {
|
||||
let players = world.players.load();
|
||||
for player in players.iter() {
|
||||
f(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a random player from any of the worlds, or `None` if all worlds are empty.
|
||||
pub fn get_random_player(&self) -> Option<Arc<Player>> {
|
||||
let players = self.get_all_players();
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_data::{Block, BlockState};
|
||||
use pumpkin_util::math::{position::BlockPos, vector3::Vector3};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::{
|
||||
block::{ExplodeArgs, drop_loot},
|
||||
@ -23,13 +23,14 @@ impl Explosion {
|
||||
async fn get_blocks_to_destroy(
|
||||
&self,
|
||||
world: &World,
|
||||
) -> HashMap<BlockPos, (&'static Block, &'static BlockState)> {
|
||||
// The hashmap will prevent position duplicates.
|
||||
let mut map = HashMap::new();
|
||||
) -> FxHashMap<BlockPos, (&'static Block, &'static BlockState)> {
|
||||
// Somethings are not vanilla here but make it way faster
|
||||
let mut map = FxHashMap::default();
|
||||
let random_val = rand::random::<f32>();
|
||||
for x in 0..16 {
|
||||
for y in 0..16 {
|
||||
'block2: for z in 0..16 {
|
||||
if x != 0 && x != 15 && z != 0 && z != 15 && y != 0 && y != 15 {
|
||||
if x > 0 && x < 15 && y > 0 && y < 15 && z > 0 && z < 15 {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -46,7 +47,7 @@ impl Explosion {
|
||||
let mut pos_y = self.pos.y + 0.0625;
|
||||
let mut pos_z = self.pos.z;
|
||||
|
||||
let mut h = self.power * rand::random::<f32>().mul_add(0.6, 0.7);
|
||||
let mut h = self.power * random_val.mul_add(0.6, 0.7);
|
||||
while h > 0.0 {
|
||||
let block_pos = BlockPos::floored(pos_x, pos_y, pos_z);
|
||||
let (block, state) = world.get_block_and_state(&block_pos).await;
|
||||
@ -61,11 +62,10 @@ impl Explosion {
|
||||
let resistance =
|
||||
fluid_state.blast_resistance.max(block.blast_resistance);
|
||||
h -= resistance * 0.3;
|
||||
}
|
||||
|
||||
if h > 0.0 {
|
||||
map.insert(block_pos, (block, state));
|
||||
}
|
||||
}
|
||||
pos_x += x * 0.3;
|
||||
pos_y += y * 0.3;
|
||||
pos_z += z * 0.3;
|
||||
@ -82,13 +82,10 @@ impl Explosion {
|
||||
let blocks = self.get_blocks_to_destroy(world).await;
|
||||
// TODO: Entity damage, fire
|
||||
for (pos, (block, state)) in &blocks {
|
||||
if state.is_air() {
|
||||
continue;
|
||||
}
|
||||
let pumpkin_block = world.block_registry.get_pumpkin_block(block.id);
|
||||
|
||||
world.set_block_state(pos, 0, BlockFlags::NOTIFY_ALL).await;
|
||||
|
||||
let pumpkin_block = world.block_registry.get_pumpkin_block(block.id);
|
||||
|
||||
if pumpkin_block.is_none_or(|s| s.should_drop_items_on_explosion()) {
|
||||
let params = LootContextParameters {
|
||||
block_state: Some(state),
|
||||
|
||||
@ -35,6 +35,7 @@ use crossbeam::queue::SegQueue;
|
||||
use explosion::Explosion;
|
||||
use pumpkin_config::BasicConfiguration;
|
||||
use pumpkin_data::block_properties::is_air;
|
||||
use pumpkin_data::chunk_gen_settings::GenerationSettings;
|
||||
use pumpkin_data::data_component_impl::EquipmentSlot;
|
||||
use pumpkin_data::dimension::Dimension;
|
||||
use pumpkin_data::entity::MobCategory;
|
||||
@ -135,7 +136,6 @@ pub mod weather;
|
||||
use crate::world::natural_spawner::{SpawnState, spawn_for_chunk};
|
||||
use pumpkin_data::effect::StatusEffect;
|
||||
use pumpkin_world::chunk::ChunkHeightmapType::MotionBlocking;
|
||||
use pumpkin_world::generation::settings::gen_settings_from_dimension;
|
||||
use uuid::Uuid;
|
||||
use weather::Weather;
|
||||
|
||||
@ -217,7 +217,7 @@ impl World {
|
||||
server: Weak<Server>,
|
||||
) -> Self {
|
||||
// TODO
|
||||
let generation_settings = gen_settings_from_dimension(&dimension);
|
||||
let generation_settings = GenerationSettings::from_dimension(&dimension);
|
||||
|
||||
// Load portal POI from disk (PoiStorage::new automatically loads from disk if files exist)
|
||||
let portal_poi = portal::PortalPoiStorage::new(&level.level_folder.root_folder);
|
||||
@ -768,11 +768,8 @@ impl World {
|
||||
}
|
||||
}
|
||||
|
||||
let mut spawning_chunks: Vec<(Vector2<i32>, Arc<RwLock<ChunkData>>)> =
|
||||
spawning_chunks_map.into_iter().collect();
|
||||
|
||||
let mut spawn_state =
|
||||
SpawnState::new(spawning_chunks.len() as i32, &self.entities, self).await; // TODO store it
|
||||
SpawnState::new(spawning_chunks_map.len() as i32, &self.entities, self).await; // TODO store it
|
||||
|
||||
// TODO gamerule this.spawnEnemies || this.spawnFriendlies
|
||||
let spawn_passives = self.level_time.lock().await.time_of_day % 400 == 0;
|
||||
@ -785,7 +782,8 @@ impl World {
|
||||
);
|
||||
|
||||
// log::debug!("spawning list size {}", spawn_list.len());
|
||||
|
||||
let mut spawning_chunks: Vec<(Vector2<i32>, Arc<RwLock<ChunkData>>)> =
|
||||
spawning_chunks_map.into_iter().collect();
|
||||
spawning_chunks.shuffle(&mut rng());
|
||||
|
||||
// TODO i think it can be multithread
|
||||
@ -794,9 +792,10 @@ impl World {
|
||||
.await;
|
||||
}
|
||||
|
||||
for block_entity in tick_data.block_entities {
|
||||
let world: Arc<dyn SimpleWorld> = self.clone();
|
||||
block_entity.tick(world).await;
|
||||
|
||||
for block_entity in tick_data.block_entities {
|
||||
block_entity.tick(&world).await;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3001,13 +3000,16 @@ impl World {
|
||||
|
||||
pub async fn drop_stack(self: &Arc<Self>, pos: &BlockPos, stack: ItemStack) {
|
||||
let height = EntityType::ITEM.dimension[1] / 2.0;
|
||||
let pos = Vector3::new(
|
||||
f64::from(pos.0.x) + 0.5 + rand::rng().random_range(-0.25..0.25),
|
||||
f64::from(pos.0.y) + 0.5 + rand::rng().random_range(-0.25..0.25) - f64::from(height),
|
||||
f64::from(pos.0.z) + 0.5 + rand::rng().random_range(-0.25..0.25),
|
||||
);
|
||||
let spawn_pos = {
|
||||
let mut r = rand::rng();
|
||||
Vector3::new(
|
||||
f64::from(pos.0.x) + 0.5 + r.random_range(-0.25..0.25),
|
||||
f64::from(pos.0.y) + 0.5 + r.random_range(-0.25..0.25) - f64::from(height),
|
||||
f64::from(pos.0.z) + 0.5 + r.random_range(-0.25..0.25),
|
||||
)
|
||||
};
|
||||
|
||||
let entity = Entity::new(self.clone(), pos, &EntityType::ITEM);
|
||||
let entity = Entity::new(self.clone(), spawn_pos, &EntityType::ITEM);
|
||||
let item_entity = Arc::new(ItemEntity::new(entity, stack).await);
|
||||
self.spawn_entity(item_entity).await;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user