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:
Alexander Medvedev 2026-02-03 23:35:31 +01:00
parent 2f29fcef8d
commit 835262a85a
92 changed files with 1379 additions and 18014 deletions

View File

@ -27,6 +27,9 @@ separated_literal_suffix = "deny"
semicolon_outside_block = "deny" semicolon_outside_block = "deny"
redundant_test_prefix = "deny" redundant_test_prefix = "deny"
non_zero_suggestions = "deny" non_zero_suggestions = "deny"
dbg_macro = "deny"
rc_buffer = "deny"
verbose_file_reads = "deny"
string_lit_chars_any = "deny" string_lit_chars_any = "deny"
use_self = "deny" use_self = "deny"
useless_let_if_seq = "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

View File

@ -30,12 +30,12 @@ pub(crate) fn build() -> TokenStream {
let id_lit = LitInt::new(&raw_value.id.to_string(), Span::call_site()); let id_lit = LitInt::new(&raw_value.id.to_string(), Span::call_site());
let default_value_lit = raw_value.default_value; let default_value_lit = raw_value.default_value;
constant_defs.push(quote! { constant_defs.push(quote!(
pub const #constant_ident: Self = Self { pub const #constant_ident: Self = Self {
id: #id_lit, id: #id_lit,
default_value: #default_value_lit, default_value: #default_value_lit,
}; };
}); ));
} }
quote! { quote! {

View File

@ -13,6 +13,7 @@ mod attributes;
mod biome; mod biome;
mod bitsets; mod bitsets;
mod block; mod block;
mod chunk_gen_settings;
mod chunk_status; mod chunk_status;
mod composter_increase_chance; mod composter_increase_chance;
mod damage_type; mod damage_type;
@ -54,9 +55,7 @@ mod world_event;
pub const OUT_DIR: &str = "src/generated"; pub const OUT_DIR: &str = "src/generated";
pub fn main() { pub fn main() {
if let Err(e) = fs::create_dir_all(OUT_DIR) { fs::create_dir_all(OUT_DIR).expect("Failed to create output directory");
eprintln!("Failed to create output directory {OUT_DIR}: {e}");
}
type BuilderFn = fn() -> TokenStream; type BuilderFn = fn() -> TokenStream;
@ -85,6 +84,7 @@ pub fn main() {
(spawn_egg::build, "spawn_egg.rs"), (spawn_egg::build, "spawn_egg.rs"),
(block::build, "block.rs"), (block::build, "block.rs"),
(item::build, "item.rs"), (item::build, "item.rs"),
(chunk_gen_settings::build, "chunk_gen_settings.rs"),
(fluid::build, "fluid.rs"), (fluid::build, "fluid.rs"),
(entity_status::build, "entity_status.rs"), (entity_status::build, "entity_status.rs"),
(tag::build, "tag.rs"), (tag::build, "tag.rs"),
@ -110,8 +110,14 @@ pub fn main() {
build_functions.par_iter().for_each(|(build_fn, file)| { build_functions.par_iter().for_each(|(build_fn, file)| {
let raw_code = build_fn().to_string(); 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);
}); });
} }

View 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
}
}
}
)
}

View File

@ -102,7 +102,7 @@ pub(crate) fn build() -> TokenStream {
}); });
} }
quote! { quote!(
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Dimension { pub struct Dimension {
pub id: u8, pub id: u8,
@ -134,5 +134,5 @@ pub(crate) fn build() -> TokenStream {
} }
} }
impl Eq for Dimension {} impl Eq for Dimension {}
} )
} }

View File

@ -771,13 +771,13 @@ pub(crate) fn build() -> TokenStream {
impl ToResourceLocation for &'static Fluid { impl ToResourceLocation for &'static Fluid {
fn to_resource_location(&self) -> ResourceLocation { fn to_resource_location(&self) -> ResourceLocation {
ResourceLocation::vanilla(self.name) format!("minecraft:{}", self.name)
} }
} }
impl FromResourceLocation for &'static Fluid { impl FromResourceLocation for &'static Fluid {
fn from_resource_location(resource_location: &ResourceLocation) -> Option<Self> { 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))
} }
} }

View File

@ -92,14 +92,13 @@ pub(crate) fn build() -> TokenStream {
static_regs.iter().map(|static_reg| { static_regs.iter().map(|static_reg| {
let registry_id = if static_reg.registry_id.contains(':') { let registry_id = if static_reg.registry_id.contains(':') {
ResourceLocation::from(static_reg.registry_id) static_reg.registry_id.to_string()
} else { } else {
ResourceLocation::vanilla(static_reg.registry_id) format!("minecraft:{}", static_reg.registry_id)
}; };
let registry_entries = static_reg.entries.iter().map(|entry| { let registry_entries = static_reg.entries.iter().map(|entry| {
let entry_id = let entry_id = format!("minecraft:{}", entry.name);
ResourceLocation::vanilla(entry.name);
RegistryEntryData { RegistryEntryData {
entry_id, entry_id,

View File

@ -91,13 +91,17 @@ impl Taggable for Block {
impl ToResourceLocation for &'static Block { impl ToResourceLocation for &'static Block {
fn to_resource_location(&self) -> ResourceLocation { fn to_resource_location(&self) -> ResourceLocation {
ResourceLocation::vanilla(self.name) format!("minecraft:{}", self.name)
} }
} }
impl FromResourceLocation for &'static Block { impl FromResourceLocation for &'static Block {
fn from_resource_location(resource_location: &ResourceLocation) -> Option<Self> { 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),
)
} }
} }

View File

@ -20,6 +20,7 @@ pub mod registry;
#[path = "generated/screen.rs"] #[path = "generated/screen.rs"]
pub mod screen; pub mod screen;
#[rustfmt::skip] #[rustfmt::skip]
#[path = "generated/particle.rs"] #[path = "generated/particle.rs"]
pub mod particle; pub mod particle;
@ -193,3 +194,7 @@ pub use block_state::BlockState;
pub use block_state::BlockStateRef; pub use block_state::BlockStateRef;
pub use blocks::Block; pub use blocks::Block;
pub use collision_shape::CollisionShape; pub use collision_shape::CollisionShape;
#[rustfmt::skip]
#[path = "generated/chunk_gen_settings.rs"]
pub mod chunk_gen_settings;

View File

@ -51,7 +51,6 @@ macro_rules! define_get_number_be {
impl<R: Read + Seek> NbtReadHelper<R> { impl<R: Read + Seek> NbtReadHelper<R> {
pub fn skip_bytes(&mut self, count: i64) -> Result<()> { pub fn skip_bytes(&mut self, count: i64) -> Result<()> {
self.reader self.reader
.by_ref()
.seek(SeekFrom::Current(count)) .seek(SeekFrom::Current(count))
.map_err(Error::Incomplete)?; .map_err(Error::Incomplete)?;
Ok(()) Ok(())

View File

@ -28,34 +28,9 @@ pub struct RegistryEntry {
pub data: Option<Box<[u8]>>, pub data: Option<Box<[u8]>>,
} }
// TODO: No unwraps
impl RegistryEntry { impl RegistryEntry {
#[must_use] #[must_use]
pub const fn new(entry_id: ResourceLocation, data: Option<Box<[u8]>>) -> Self { pub const fn new(entry_id: ResourceLocation, data: Option<Box<[u8]>>) -> Self {
Self { entry_id, data } 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()),
}
}
} }

View File

@ -8,7 +8,6 @@ use pumpkin_data::{
tag::{RegistryKey, get_registry_key_tags}, tag::{RegistryKey, get_registry_key_tags},
}; };
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::resource_location::ResourceLocation;
use pumpkin_util::version::MinecraftVersion; use pumpkin_util::version::MinecraftVersion;
#[java_packet(CONFIG_UPDATE_TAGS)] #[java_packet(CONFIG_UPDATE_TAGS)]
@ -31,9 +30,7 @@ impl ClientPacket for CUpdateTags<'_> {
) -> Result<(), WritingError> { ) -> Result<(), WritingError> {
let mut write = write; let mut write = write;
write.write_list(self.tags, |p, registry_key| { write.write_list(self.tags, |p, registry_key| {
p.write_resource_location(&ResourceLocation::vanilla( p.write_string(&format!("minecraft:{}", registry_key.identifier_string(),))?;
registry_key.identifier_string(),
))?;
let values = get_registry_key_tags(*version, *registry_key).unwrap(); let values = get_registry_key_tags(*version, *registry_key).unwrap();
p.write_var_int(&values.len().try_into().map_err(|_| { p.write_var_int(&values.len().try_into().map_err(|_| {

View File

@ -37,7 +37,7 @@ impl ClientPacket for CStopSound {
(Some(category), Some(sound_id)) => { (Some(category), Some(sound_id)) => {
write.write_u8(CATEGORY_AND_SOUND)?; write.write_u8(CATEGORY_AND_SOUND)?;
write.write_var_int(&VarInt(category as i32))?; write.write_var_int(&VarInt(category as i32))?;
write.write_resource_location(sound_id) write.write_string(sound_id)
} }
(Some(category), None) => { (Some(category), None) => {
write.write_u8(CATEGORY_ONLY)?; write.write_u8(CATEGORY_ONLY)?;
@ -45,7 +45,7 @@ impl ClientPacket for CStopSound {
} }
(None, Some(sound_id)) => { (None, Some(sound_id)) => {
write.write_u8(SOUND_ONLY)?; 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), (None, None) => write.write_u8(NO_CATEGORY_NO_SOUND),
} }

View File

@ -26,7 +26,7 @@ pub struct SConfigCookieResponse {
impl ServerPacket for SConfigCookieResponse { impl ServerPacket for SConfigCookieResponse {
fn read(read: impl Read) -> Result<Self, ReadingError> { fn read(read: impl Read) -> Result<Self, ReadingError> {
let mut read = read; let mut read = read;
let key = read.get_resource_location()?; let key = read.get_string()?;
let has_payload = read.get_bool()?; let has_payload = read.get_bool()?;
if !has_payload { if !has_payload {

View File

@ -26,7 +26,7 @@ impl ServerPacket for SPluginMessage {
fn read(read: impl Read) -> Result<Self, ReadingError> { fn read(read: impl Read) -> Result<Self, ReadingError> {
let mut read = read; let mut read = read;
Ok(Self { Ok(Self {
channel: read.get_resource_location()?, channel: read.get_string()?,
data: read.read_remaining_to_boxed_slice(MAX_PAYLOAD_SIZE)?, data: read.read_remaining_to_boxed_slice(MAX_PAYLOAD_SIZE)?,
}) })
} }

View File

@ -22,7 +22,7 @@ impl ServerPacket for SLoginCookieResponse {
fn read(read: impl Read) -> Result<Self, ReadingError> { fn read(read: impl Read) -> Result<Self, ReadingError> {
let mut read = read; let mut read = read;
let key = read.get_resource_location()?; let key = read.get_string()?;
let has_payload = read.get_bool()?; let has_payload = read.get_bool()?;
if !has_payload { if !has_payload {

View File

@ -23,7 +23,7 @@ impl ServerPacket for SCookieResponse {
fn read(read: impl Read) -> Result<Self, ReadingError> { fn read(read: impl Read) -> Result<Self, ReadingError> {
let mut read = read; let mut read = read;
let key = read.get_resource_location()?; let key = read.get_string()?;
let has_payload = read.get_bool()?; let has_payload = read.get_bool()?;
if !has_payload { if !has_payload {

View File

@ -526,7 +526,6 @@ impl Serialize for LinkType {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use pumpkin_util::resource_location::ResourceLocation;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
@ -551,7 +550,7 @@ mod test {
fn serde_id_or_value() { fn serde_id_or_value() {
let mut buf = Vec::new(); let mut buf = Vec::new();
let event = SoundEvent { let event = SoundEvent {
sound_name: ResourceLocation::vanilla("test"), sound_name: "test".to_string(),
range: Some(1.0), range: Some(1.0),
}; };

View File

@ -10,7 +10,7 @@ use crate::{
pub mod deserializer; pub mod deserializer;
use pumpkin_nbt::{serializer::WriteAdaptor, tag::NbtTag}; 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; use thiserror::Error;
pub mod serializer; pub mod serializer;
@ -71,7 +71,6 @@ pub trait NetworkReadExt {
fn get_var_ulong(&mut self) -> Result<VarULong, ReadingError>; fn get_var_ulong(&mut self) -> Result<VarULong, ReadingError>;
fn get_string_bounded(&mut self, bound: usize) -> Result<String, ReadingError>; fn get_string_bounded(&mut self, bound: usize) -> Result<String, ReadingError>;
fn get_string(&mut self) -> 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_uuid(&mut self) -> Result<uuid::Uuid, ReadingError>;
fn get_fixed_bitset(&mut self, bits: usize) -> Result<FixedBitSet, 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) 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> { fn get_uuid(&mut self) -> Result<uuid::Uuid, ReadingError> {
let mut bytes = [0u8; 16]; let mut bytes = [0u8; 16];
self.read_exact(&mut bytes) self.read_exact(&mut bytes)
@ -245,7 +233,6 @@ pub trait NetworkWriteExt {
fn write_var_long(&mut self, data: &VarLong) -> Result<(), WritingError>; 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_bounded(&mut self, data: &str, bound: usize) -> Result<(), WritingError>;
fn write_string(&mut self, data: &str) -> 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_block_pos(&mut self, pos: &BlockPos) -> Result<(), WritingError>;
fn write_uuid(&mut self, data: &uuid::Uuid) -> 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()) 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> { fn write_bitset(&mut self, data: &BitSet) -> Result<(), WritingError> {
data.encode(self) data.encode(self)
} }

View File

@ -22,6 +22,7 @@ pub mod text;
pub mod translation; pub mod translation;
pub mod version; pub mod version;
pub mod world_seed; pub mod world_seed;
pub mod y_offset;
pub mod jwt; pub mod jwt;

View File

@ -1,99 +1,4 @@
use std::{num::NonZeroUsize, str::FromStr}; pub type ResourceLocation = String;
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 trait ToResourceLocation: Sized { pub trait ToResourceLocation: Sized {
fn to_resource_location(&self) -> ResourceLocation; fn to_resource_location(&self) -> ResourceLocation;

View File

@ -23,14 +23,14 @@ impl YOffset {
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct Absolute { pub struct Absolute {
absolute: i16, pub absolute: i16,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct AboveBottom { pub struct AboveBottom {
above_bottom: i8, pub above_bottom: i8,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct BelowTop { pub struct BelowTop {
below_top: i8, pub below_top: i8,
} }

View File

@ -1,20 +1,21 @@
use criterion::{Criterion, criterion_group, criterion_main}; 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::{ use pumpkin_world::{
GENERATION_SETTINGS, GeneratorSetting, GlobalRandomConfig, ProtoNoiseRouters, GlobalRandomConfig, ProtoNoiseRouters, bench_create_and_populate_biome,
bench_create_and_populate_biome, bench_create_and_populate_noise, bench_create_and_populate_noise, bench_create_and_populate_noise_with_surface,
bench_create_and_populate_noise_with_surface, generation::proto_chunk::TerrainCache, block::to_state_from_blueprint, generation::proto_chunk::TerrainCache,
}; };
fn bench_terrain_gen(c: &mut Criterion) { fn bench_terrain_gen(c: &mut Criterion) {
let seed = 0; let seed = 0;
let random_config = GlobalRandomConfig::new(seed, false); let random_config = GlobalRandomConfig::new(seed, false);
let base_router = ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &random_config); let base_router = ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &random_config);
let surface_config = GENERATION_SETTINGS let surface_config = GenerationSettings::from_dimension(&Dimension::OVERWORLD);
.get(&GeneratorSetting::Overworld)
.unwrap();
let terrain_cache = TerrainCache::from_random(&random_config); 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| { c.bench_function("overworld biome", |b| {
b.iter(|| { b.iter(|| {
@ -23,7 +24,7 @@ fn bench_terrain_gen(c: &mut Criterion) {
&random_config, &random_config,
surface_config, surface_config,
&terrain_cache, &terrain_cache,
default_block, default_state,
); );
}); });
}); });
@ -35,7 +36,7 @@ fn bench_terrain_gen(c: &mut Criterion) {
&random_config, &random_config,
surface_config, surface_config,
&terrain_cache, &terrain_cache,
default_block, default_state,
); );
}); });
}); });
@ -47,7 +48,7 @@ fn bench_terrain_gen(c: &mut Criterion) {
&random_config, &random_config,
surface_config, surface_config,
&terrain_cache, &terrain_cache,
default_block, default_state,
); );
}); });
}); });

View File

@ -60,13 +60,15 @@ pub fn hash_seed(seed: u64) -> i64 {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use pumpkin_data::{ 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 pumpkin_util::read_data_from_file;
use serde::Deserialize; use serde::Deserialize;
use crate::{ use crate::{
GENERATION_SETTINGS, GeneratorSetting, GlobalRandomConfig, ProtoChunk, GlobalRandomConfig, ProtoChunk,
block::to_state_from_blueprint,
chunk::palette::BIOME_NETWORK_MAX_BITS, chunk::palette::BIOME_NETWORK_MAX_BITS,
generation::noise::router::{ generation::noise::router::{
multi_noise_sampler::{MultiNoiseSampler, MultiNoiseSamplerBuilderOptions}, multi_noise_sampler::{MultiNoiseSampler, MultiNoiseSamplerBuilderOptions},
@ -110,11 +112,10 @@ mod test {
let random_config = GlobalRandomConfig::new(seed, false); let random_config = GlobalRandomConfig::new(seed, false);
let noise_router = let noise_router =
ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &random_config); ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &random_config);
let surface_settings = GENERATION_SETTINGS let surface_settings = GenerationSettings::from_dimension(&Dimension::OVERWORLD);
.get(&GeneratorSetting::Overworld)
.unwrap();
//let _terrain_cache = TerrainCache::from_random(&random_config); //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 { for data in expected_data {
let chunk_x = data.x; let chunk_x = data.x;

View File

@ -33,13 +33,15 @@ impl NoiseValuePoint {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use pumpkin_data::{ 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 pumpkin_util::read_data_from_file;
use crate::{ use crate::{
GENERATION_SETTINGS, GeneratorSetting, GlobalRandomConfig, ProtoChunk, GlobalRandomConfig, ProtoChunk,
biome::{BiomeSupplier, MultiNoiseBiomeSupplier}, biome::{BiomeSupplier, MultiNoiseBiomeSupplier},
block::to_state_from_blueprint,
generation::{ generation::{
noise::router::{ noise::router::{
multi_noise_sampler::{MultiNoiseSampler, MultiNoiseSamplerBuilderOptions}, multi_noise_sampler::{MultiNoiseSampler, MultiNoiseSamplerBuilderOptions},
@ -68,9 +70,8 @@ mod test {
let noise_router = let noise_router =
ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &random_config); ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &random_config);
let surface_config = GENERATION_SETTINGS let surface_config = GenerationSettings::from_dimension(&Dimension::OVERWORLD);
.get(&GeneratorSetting::Overworld)
.unwrap();
let _terrain_cache = TerrainCache::from_random(&random_config); let _terrain_cache = TerrainCache::from_random(&random_config);
// Calculate biome mixer seed // Calculate biome mixer seed
let biome_mixer_seed = hash_seed(random_config.seed); let biome_mixer_seed = hash_seed(random_config.seed);
@ -79,7 +80,7 @@ mod test {
chunk_x, chunk_x,
chunk_z, chunk_z,
&Dimension::OVERWORLD, &Dimension::OVERWORLD,
surface_config.default_block.get_state(), to_state_from_blueprint(&surface_config.default_block),
biome_mixer_seed, biome_mixer_seed,
); );

View File

@ -76,7 +76,7 @@ impl BlockEntity for BarrelBlockEntity {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
world: Arc<dyn SimpleWorld>, world: &'a Arc<dyn SimpleWorld>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> { ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move { Box::pin(async move {
self.viewers self.viewers

View File

@ -76,7 +76,7 @@ impl BlockEntity for ChestBlockEntity {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
world: Arc<dyn SimpleWorld>, world: &'a Arc<dyn SimpleWorld>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> { ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move { Box::pin(async move {
self.viewers self.viewers

View File

@ -47,7 +47,7 @@ impl BlockEntity for EnderChestBlockEntity {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
world: Arc<dyn SimpleWorld>, world: &'a Arc<dyn SimpleWorld>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> { ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move { Box::pin(async move {
self.viewers self.viewers

View File

@ -315,7 +315,7 @@ macro_rules! impl_block_entity_for_cooking {
impl $crate::block::entities::BlockEntity for $struct_name { impl $crate::block::entities::BlockEntity for $struct_name {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
world: Arc<dyn $crate::world::SimpleWorld>, world: &'a Arc<dyn $crate::world::SimpleWorld>,
) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send + 'a>> { ) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move { Box::pin(async move {
let is_burning = self.is_burning(); let is_burning = self.is_burning();

View File

@ -77,7 +77,7 @@ impl BlockEntity for HopperBlockEntity {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
world: Arc<dyn SimpleWorld>, world: &'a Arc<dyn SimpleWorld>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> { ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move { Box::pin(async move {
self.ticked_game_time.store( self.ticked_game_time.store(
@ -95,7 +95,7 @@ impl BlockEntity for HopperBlockEntity {
world.get_block_state(&self.position).await.id, world.get_block_state(&self.position).await.id,
&Block::HOPPER, &Block::HOPPER,
); );
self.try_move_items(&state, &world).await; self.try_move_items(&state, world).await;
} }
}) })
} }

View File

@ -81,12 +81,12 @@ impl BlockEntity for MobSpawnerBlockEntity {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
world: Arc<dyn SimpleWorld>, world: &'a Arc<dyn SimpleWorld>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> { ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move { Box::pin(async move {
if let Some(entity_type) = &self.entity_type.load() { if let Some(entity_type) = &self.entity_type.load() {
if self.delay.load(Ordering::Relaxed) == -1 { if self.delay.load(Ordering::Relaxed) == -1 {
self.update_spawns(&world).await; self.update_spawns(world).await;
} else { } else {
self.delay.fetch_sub(1, Ordering::Relaxed); self.delay.fetch_sub(1, Ordering::Relaxed);
return; return;
@ -128,7 +128,7 @@ impl BlockEntity for MobSpawnerBlockEntity {
update_spawns = true; update_spawns = true;
} }
if update_spawns { if update_spawns {
self.update_spawns(&world).await; self.update_spawns(world).await;
} }
} }
}) })

View File

@ -55,7 +55,7 @@ pub trait BlockEntity: Send + Sync {
Self: Sized; Self: Sized;
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
_world: Arc<dyn SimpleWorld>, _world: &'a Arc<dyn SimpleWorld>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> { ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async {}) Box::pin(async {})
} }

View File

@ -60,7 +60,7 @@ impl BlockEntity for PistonBlockEntity {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
world: Arc<dyn SimpleWorld>, world: &'a Arc<dyn SimpleWorld>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> { ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move { Box::pin(async move {
let current_progress = self.current_progress.load(); let current_progress = self.current_progress.load();

View File

@ -71,7 +71,7 @@ impl BlockEntity for ShulkerBoxBlockEntity {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
world: Arc<dyn SimpleWorld>, world: &'a Arc<dyn SimpleWorld>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> { ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move { Box::pin(async move {
self.viewers self.viewers

View File

@ -4,7 +4,7 @@ pub mod viewer;
use std::collections::HashMap; use std::collections::HashMap;
use pumpkin_data::{Block, BlockState}; use pumpkin_data::{Block, BlockState, chunk_gen_settings::BlockBlueprint};
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub use state::RawBlockState; 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)] #[cfg(test)]
mod test { mod test {
use pumpkin_data::Block; use pumpkin_data::Block;

View File

@ -42,7 +42,7 @@ impl ViewerCountTracker {
pub async fn update_viewer_count<T>( pub async fn update_viewer_count<T>(
&self, &self,
entity: &T, entity: &T,
world: Arc<dyn SimpleWorld>, world: &Arc<dyn SimpleWorld>,
position: &BlockPos, position: &BlockPos,
) where ) where
T: BlockEntity + ViewerCountListener + 'static, T: BlockEntity + ViewerCountListener + 'static,
@ -52,12 +52,12 @@ impl ViewerCountTracker {
if old != current { if old != current {
match (old, current) { match (old, current) {
(n, 0) if n > 0 => { (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: world.emitGameEvent(player, GameEvent.CONTAINER_CLOSE, pos);
// TODO: this.maxBlockInteractionRange = 0.0; // TODO: this.maxBlockInteractionRange = 0.0;
} }
(0, n) if n > 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: world.emitGameEvent(player, GameEvent.CONTAINER_OPEN, pos);
// TODO: scheduleBlockTick(world, pos, state); // TODO: scheduleBlockTick(world, pos, state);
} }
@ -65,7 +65,7 @@ impl ViewerCountTracker {
} }
entity entity
.on_viewer_count_update(&world, position, old, current) .on_viewer_count_update(world, position, old, current)
.await; .await;
} }

View File

@ -351,7 +351,6 @@ impl<S: SingleChunkDataSerializer> AnvilChunkFile<S> {
log::trace!("Writing in place: {}", path.display()); log::trace!("Writing in place: {}", path.display());
let file = tokio::fs::OpenOptions::new() let file = tokio::fs::OpenOptions::new()
.read(false)
.write(true) .write(true)
.create(true) .create(true)
.truncate(false) .truncate(false)
@ -450,13 +449,7 @@ impl<S: SingleChunkDataSerializer> AnvilChunkFile<S> {
let temp_path = path.with_extension("tmp"); let temp_path = path.with_extension("tmp");
log::trace!("Writing tmp file to disk: {temp_path:?}"); log::trace!("Writing tmp file to disk: {temp_path:?}");
let file = tokio::fs::OpenOptions::new() let file = tokio::fs::File::create(&temp_path).await?;
.read(false)
.write(true)
.create(true)
.truncate(true)
.open(&temp_path)
.await?;
let mut write = BufWriter::new(file); let mut write = BufWriter::new(file);

View File

@ -179,14 +179,7 @@ impl<S: SingleChunkDataSerializer> ChunkSerializer for LinearFile<S> {
let temp_path = path.with_extension("tmp"); let temp_path = path.with_extension("tmp");
log::trace!("Writing tmp file to disk: {}", temp_path.display()); log::trace!("Writing tmp file to disk: {}", temp_path.display());
let file = tokio::fs::OpenOptions::new() let file = tokio::fs::File::create(&temp_path).await?;
.read(false)
.write(true)
.create(true)
.truncate(true)
.open(&temp_path)
.await?;
let mut write = BufWriter::new(file); let mut write = BufWriter::new(file);
// Parse the headers to a buffer // Parse the headers to a buffer

View File

@ -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 bytes::Bytes;
use futures::future::join_all; use futures::future::join_all;
use pumpkin_data::{Block, chunk::ChunkStatus, fluid::Fluid}; use pumpkin_data::{Block, chunk::ChunkStatus, fluid::Fluid};
use pumpkin_nbt::{compound::NbtCompound, from_bytes, nbt_long_array}; use pumpkin_nbt::{compound::NbtCompound, from_bytes, nbt_long_array};
use rustc_hash::FxHashMap;
use uuid::Uuid; use uuid::Uuid;
use crate::{ use crate::{
@ -170,7 +171,7 @@ impl ChunkData {
block_ticks: ChunkTickScheduler::from_iter(chunk_data.block_ticks), block_ticks: ChunkTickScheduler::from_iter(chunk_data.block_ticks),
fluid_ticks: ChunkTickScheduler::from_iter(chunk_data.fluid_ticks), fluid_ticks: ChunkTickScheduler::from_iter(chunk_data.fluid_ticks),
block_entities: { block_entities: {
let mut block_entities = HashMap::new(); let mut block_entities = FxHashMap::default();
for nbt in chunk_data.block_entities { for nbt in chunk_data.block_entities {
let block_entity = block_entity_from_nbt(&nbt); let block_entity = block_entity_from_nbt(&nbt);
if let Some(block_entity) = block_entity { if let Some(block_entity) = block_entity {
@ -289,7 +290,7 @@ impl ChunkEntityData {
chunk_entity_data.position[1], chunk_entity_data.position[1],
))); )));
} }
let mut map = HashMap::new(); let mut map = FxHashMap::default();
for entity_nbt in chunk_entity_data.entities { for entity_nbt in chunk_entity_data.entities {
let uuid = if let Some(uuid) = entity_nbt.get_int_array("UUID") { let uuid = if let Some(uuid) = entity_nbt.get_int_array("UUID") {
Uuid::from_u128( Uuid::from_u128(

View File

@ -1,6 +1,5 @@
use std::{ use std::{
collections::BTreeMap, collections::BTreeMap,
io::ErrorKind,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::Arc, sync::Arc,
}; };
@ -9,7 +8,6 @@ use futures::future::join_all;
use log::{error, trace}; use log::{error, trace};
use pumpkin_util::math::vector2::Vector2; use pumpkin_util::math::vector2::Vector2;
use tokio::{ use tokio::{
io::AsyncReadExt,
join, join,
sync::{OnceCell, RwLock, mpsc}, sync::{OnceCell, RwLock, mpsc},
}; };
@ -86,38 +84,30 @@ impl<S: ChunkSerializer<WriteBackend = PathBuf>> ChunkSerializerLazyLoader<S> {
async fn read_from_disk(&self) -> Result<S, ChunkReadingError> { async fn read_from_disk(&self) -> Result<S, ChunkReadingError> {
trace!("Opening file from Disk: {}", self.path.display()); 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 value = match file { let file_bytes = tokio::fs::read(&self.path).await.map_err(|err| {
Ok(mut file) => { if err.kind() == std::io::ErrorKind::NotFound {
let capacity = match file.metadata().await { ChunkReadingError::ChunkNotExist
Ok(metadata) => metadata.len() as usize, } else {
Err(_) => 4096, // A sane default ChunkReadingError::IoError(err.kind())
};
// 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),
};
trace!("Successfully read file from Disk: {}", self.path.display()); match file_bytes {
Ok(value) 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),
}
} }
} }
@ -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<'_, ()> { fn block_and_await_ongoing_tasks(&self) -> BoxFuture<'_, ()> {
Box::pin(async move { Box::pin(async move {
//we need to block any other operation //we need to block any other operation

View File

@ -85,8 +85,6 @@ where
/// Tells the `ChunkIO` that no more chunks are loaded in memory /// Tells the `ChunkIO` that no more chunks are loaded in memory
fn clear_watched_chunks(&self) -> BoxFuture<'_, ()>; fn clear_watched_chunks(&self) -> BoxFuture<'_, ()>;
fn clean_up_log(&self) -> BoxFuture<'_, ()>;
/// Ensure that all ongoing operations are finished /// Ensure that all ongoing operations are finished
fn block_and_await_ongoing_tasks(&self) -> BoxFuture<'_, ()>; fn block_and_await_ongoing_tasks(&self) -> BoxFuture<'_, ()>;
} }

View File

@ -11,10 +11,11 @@ use pumpkin_data::{Block, BlockState};
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;
use pumpkin_nbt::nbt_long_array; use pumpkin_nbt::nbt_long_array;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::Write; use std::fmt::Write;
use std::ops::{BitAnd, BitOr}; use std::ops::{BitAnd, BitOr};
use std::{collections::HashMap, sync::Arc}; use std::sync::Arc;
use thiserror::Error; use thiserror::Error;
pub mod format; pub mod format;
@ -76,7 +77,7 @@ pub struct ChunkData {
pub z: i32, pub z: i32,
pub block_ticks: ChunkTickScheduler<&'static Block>, pub block_ticks: ChunkTickScheduler<&'static Block>,
pub fluid_ticks: ChunkTickScheduler<&'static Fluid>, 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 light_engine: ChunkLight,
pub status: ChunkStatus, pub status: ChunkStatus,
pub dirty: bool, pub dirty: bool,
@ -88,7 +89,7 @@ pub struct ChunkEntityData {
pub x: i32, pub x: i32,
/// Chunk Z /// Chunk Z
pub z: i32, pub z: i32,
pub data: HashMap<uuid::Uuid, NbtCompound>, pub data: FxHashMap<uuid::Uuid, NbtCompound>,
pub dirty: bool, pub dirty: bool,
} }

View File

@ -11,6 +11,7 @@ use crate::chunk::io::LoadedData::Loaded;
use crate::chunk::{ChunkData, ChunkHeightmapType, ChunkLight, ChunkSections, SubChunk}; use crate::chunk::{ChunkData, ChunkHeightmapType, ChunkLight, ChunkSections, SubChunk};
use crate::generation::biome_coords; use crate::generation::biome_coords;
use pumpkin_data::block_properties::is_air; use pumpkin_data::block_properties::is_air;
use pumpkin_data::chunk_gen_settings::GenerationSettings;
use pumpkin_data::dimension::Dimension; use pumpkin_data::dimension::Dimension;
use std::default::Default; use std::default::Default;
use std::pin::Pin; use std::pin::Pin;
@ -18,7 +19,6 @@ use std::pin::Pin;
use crate::generation::height_limit::HeightLimitView; use crate::generation::height_limit::HeightLimitView;
use crate::generation::proto_chunk::{GenerationCache, TerrainCache}; use crate::generation::proto_chunk::{GenerationCache, TerrainCache};
use crate::generation::settings::{GenerationSettings, gen_settings_from_dimension};
use crate::level::{Level, SyncChunk}; use crate::level::{Level, SyncChunk};
use crate::world::{BlockAccessor, BlockRegistryExt}; use crate::world::{BlockAccessor, BlockRegistryExt};
use crate::{BlockStateId, GlobalRandomConfig, ProtoChunk, ProtoNoiseRouters}; use crate::{BlockStateId, GlobalRandomConfig, ProtoChunk, ProtoNoiseRouters};
@ -1769,7 +1769,7 @@ impl GenerationSchedule {
thread::current().name().unwrap_or("unknown") 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() { while let Ok((pos, mut cache, stage)) = recv.recv() {
// debug!("generation thread receive chunk pos {pos:?} to stage {stage:?}"); // debug!("generation thread receive chunk pos {pos:?} to stage {stage:?}");
cache.advance( cache.advance(

View File

@ -1,6 +1,5 @@
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use pumpkin_util::math::vector3::Vector3;
use super::noise::router::density_function::NoisePos;
pub struct BlendResult { pub struct BlendResult {
alpha: f64, alpha: f64,
@ -26,7 +25,7 @@ impl Blender {
pub trait BlenderImpl { pub trait BlenderImpl {
fn calculate(&self, block_x: i32, block_z: i32) -> BlendResult; 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) { fn get_biome_supplier(&self) {
todo!() todo!()
@ -40,7 +39,7 @@ impl BlenderImpl for NoBlendBlender {
BlendResult::new(1f64, 1f64) 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 density
} }
} }

View File

@ -1,12 +1,12 @@
use pumpkin_data::BlockState; use pumpkin_data::BlockState;
use pumpkin_data::chunk_gen_settings::GenerationSettings;
use pumpkin_data::dimension::Dimension; use pumpkin_data::dimension::Dimension;
use pumpkin_data::noise_router::{ use pumpkin_data::noise_router::{
END_BASE_NOISE_ROUTER, NETHER_BASE_NOISE_ROUTER, OVERWORLD_BASE_NOISE_ROUTER, END_BASE_NOISE_ROUTER, NETHER_BASE_NOISE_ROUTER, OVERWORLD_BASE_NOISE_ROUTER,
}; };
use super::{ use super::noise::router::proto_noise_router::ProtoNoiseRouters;
noise::router::proto_noise_router::ProtoNoiseRouters, settings::gen_settings_from_dimension, use crate::block::to_state_from_blueprint;
};
use crate::generation::proto_chunk::TerrainCache; use crate::generation::proto_chunk::TerrainCache;
use crate::generation::{GlobalRandomConfig, Seed}; use crate::generation::{GlobalRandomConfig, Seed};
@ -40,9 +40,9 @@ impl GeneratorInit for VanillaGenerator {
unreachable!() unreachable!()
}; };
let terrain_cache = TerrainCache::from_random(&random_config); 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); let base_router = ProtoNoiseRouters::generate(&base, &random_config);
Self { Self {
random_config, random_config,

View File

@ -1,10 +1,11 @@
use std::num::NonZeroU32; use std::num::NonZeroU32;
use pumpkin_util::random::{RandomGenerator, RandomImpl}; use pumpkin_util::{
random::{RandomGenerator, RandomImpl},
y_offset::YOffset,
};
use serde::Deserialize; use serde::Deserialize;
use super::y_offset::YOffset;
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
pub enum HeightProvider { pub enum HeightProvider {

View File

@ -18,7 +18,6 @@ mod rule_test;
pub mod settings; pub mod settings;
pub mod structure; pub mod structure;
mod surface; mod surface;
pub mod y_offset;
use generator::{GeneratorInit, VanillaGenerator}; use generator::{GeneratorInit, VanillaGenerator};
use pumpkin_data::dimension::Dimension; use pumpkin_data::dimension::Dimension;

View File

@ -1,7 +1,7 @@
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use pumpkin_data::{Block, BlockState}; use pumpkin_data::{Block, BlockState};
use pumpkin_util::{ use pumpkin_util::{
math::{clamped_map, floor_div}, math::{clamped_map, floor_div, vector3::Vector3},
random::{RandomDeriver, RandomDeriverImpl, RandomImpl}, random::{RandomDeriver, RandomDeriverImpl, RandomImpl},
}; };
@ -11,7 +11,6 @@ use crate::generation::{
router::{ router::{
chunk_density_function::ChunkNoiseFunctionSampleOptions, chunk_density_function::ChunkNoiseFunctionSampleOptions,
chunk_noise_router::ChunkNoiseRouter, chunk_noise_router::ChunkNoiseRouter,
density_function::{NoisePos, UnblendedNoisePos},
surface_height_sampler::SurfaceHeightEstimateSampler, surface_height_sampler::SurfaceHeightEstimateSampler,
}, },
}, },
@ -237,13 +236,13 @@ impl WorldAquiferSampler {
fn calculate_density( fn calculate_density(
barrier_sample: &mut Option<f64>, barrier_sample: &mut Option<f64>,
pos: &impl NoisePos, pos: &Vector3<i32>,
router: &mut ChunkNoiseRouter, router: &mut ChunkNoiseRouter,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
level_1: &FluidLevel, level_1: &FluidLevel,
level_2: &FluidLevel, level_2: &FluidLevel,
) -> f64 { ) -> f64 {
let y = pos.y(); let y = pos.y;
let block_state1 = level_1.get_block(y); let block_state1 = level_1.get_block(y);
let block_state2 = level_2.get_block(y); let block_state2 = level_2.get_block(y);
@ -392,7 +391,7 @@ impl WorldAquiferSampler {
router: &mut ChunkNoiseRouter, router: &mut ChunkNoiseRouter,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> i32 { ) -> 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 let is_deep_dark = router.erosion(&pos, sample_options) < -0.225f32 as f64
&& router.depth(&pos, sample_options) > 0.9f32 as f64; && router.depth(&pos, sample_options) > 0.9f32 as f64;
@ -446,9 +445,8 @@ impl WorldAquiferSampler {
let local_y = y * 40 + 20; let local_y = y * 40 + 20;
let sample = router let sample =
.fluid_level_spread_noise(&UnblendedNoisePos::new(x, y, z), sample_options) router.fluid_level_spread_noise(&Vector3::new(x, y, z), sample_options) * 10f64;
* 10f64;
let to_nearest_multiple_of_three = (sample / 3f64).floor() as i32 * 3; let to_nearest_multiple_of_three = (sample / 3f64).floor() as i32 * 3;
let local_height = to_nearest_multiple_of_three + local_y; let local_height = to_nearest_multiple_of_three + local_y;
@ -469,7 +467,7 @@ impl WorldAquiferSampler {
let y = floor_div(block_y, 40); let y = floor_div(block_y, 40);
let z = floor_div(block_z, 64); 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 { if sample.abs() > 0.3f64 {
return &LAVA_BLOCK; return &LAVA_BLOCK;
@ -482,7 +480,7 @@ impl WorldAquiferSampler {
fn apply_internal( fn apply_internal(
&mut self, &mut self,
router: &mut ChunkNoiseRouter, router: &mut ChunkNoiseRouter,
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
height_estimator: &mut SurfaceHeightEstimateSampler, height_estimator: &mut SurfaceHeightEstimateSampler,
density: f64, density: f64,
@ -490,9 +488,9 @@ impl WorldAquiferSampler {
if density > 0f64 { if density > 0f64 {
None None
} else { } else {
let sample_x = pos.x(); let sample_x = pos.x;
let sample_y = pos.y(); let sample_y = pos.y;
let sample_z = pos.z(); let sample_z = pos.z;
let fluid_level = self let fluid_level = self
.fluid_level_sampler .fluid_level_sampler
@ -639,7 +637,7 @@ impl AquiferSamplerImpl for WorldAquiferSampler {
fn apply( fn apply(
&mut self, &mut self,
router: &mut ChunkNoiseRouter, router: &mut ChunkNoiseRouter,
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
height_estimator: &mut SurfaceHeightEstimateSampler, height_estimator: &mut SurfaceHeightEstimateSampler,
) -> Option<&'static BlockState> { ) -> Option<&'static BlockState> {
@ -663,7 +661,7 @@ impl AquiferSamplerImpl for SeaLevelAquiferSampler {
fn apply( fn apply(
&mut self, &mut self,
router: &mut ChunkNoiseRouter, router: &mut ChunkNoiseRouter,
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
_height_estimator: &mut SurfaceHeightEstimateSampler, _height_estimator: &mut SurfaceHeightEstimateSampler,
) -> Option<&'static BlockState> { ) -> Option<&'static BlockState> {
@ -674,8 +672,8 @@ impl AquiferSamplerImpl for SeaLevelAquiferSampler {
} else { } else {
Some( Some(
self.level_sampler self.level_sampler
.get_fluid_level(pos.x(), pos.y(), pos.z()) .get_fluid_level(pos.x, pos.y, pos.z)
.get_block(pos.y()) .get_block(pos.y)
.default_state, .default_state,
) )
} }
@ -687,7 +685,7 @@ pub trait AquiferSamplerImpl {
fn apply( fn apply(
&mut self, &mut self,
router: &mut ChunkNoiseRouter, router: &mut ChunkNoiseRouter,
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
height_estimator: &mut SurfaceHeightEstimateSampler, height_estimator: &mut SurfaceHeightEstimateSampler,
) -> Option<&'static BlockState>; ) -> Option<&'static BlockState>;
@ -697,7 +695,11 @@ pub trait AquiferSamplerImpl {
mod random_positions_and_hypot { mod random_positions_and_hypot {
use std::sync::LazyLock; 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::{ use crate::{
block::RawBlockState, block::RawBlockState,
@ -708,7 +710,6 @@ mod random_positions_and_hypot {
router::{ router::{
chunk_density_function::{ChunkNoiseFunctionSampleOptions, SampleAction}, chunk_density_function::{ChunkNoiseFunctionSampleOptions, SampleAction},
chunk_noise_router::ChunkNoiseRouter, chunk_noise_router::ChunkNoiseRouter,
density_function::UnblendedNoisePos,
proto_noise_router::ProtoNoiseRouters, proto_noise_router::ProtoNoiseRouters,
surface_height_sampler::{ surface_height_sampler::{
SurfaceHeightEstimateSampler, SurfaceHeightSamplerBuilderOptions, SurfaceHeightEstimateSampler, SurfaceHeightSamplerBuilderOptions,
@ -717,7 +718,6 @@ mod random_positions_and_hypot {
}, },
positions::chunk_pos, positions::chunk_pos,
proto_chunk::StandardChunkFluidLevelSampler, proto_chunk::StandardChunkFluidLevelSampler,
settings::{GENERATION_SETTINGS, GeneratorSetting},
}, },
}; };
@ -739,9 +739,7 @@ mod random_positions_and_hypot {
SurfaceHeightEstimateSampler<'_>, SurfaceHeightEstimateSampler<'_>,
ChunkNoiseFunctionSampleOptions, ChunkNoiseFunctionSampleOptions,
) { ) {
let surface_config = GENERATION_SETTINGS let surface_config = GenerationSettings::from_dimension(&Dimension::OVERWORLD);
.get(&GeneratorSetting::Overworld)
.unwrap();
let shape = &surface_config.shape; let shape = &surface_config.shape;
let chunk_x = 7; let chunk_x = 7;
let chunk_z = 4; let chunk_z = 4;
@ -1668,7 +1666,7 @@ mod random_positions_and_hypot {
for ((x, y, z, h1, h2), result) in values { for ((x, y, z, h1, h2), result) in values {
let level1 = FluidLevel::new(h1, &WATER_BLOCK); let level1 = FluidLevel::new(h1, &WATER_BLOCK);
let level2 = FluidLevel::new(h2, &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; let mut sample = None;
assert_eq!( assert_eq!(
@ -2656,7 +2654,7 @@ mod random_positions_and_hypot {
]; ];
for ((x, y, z, sample), result) in values { for ((x, y, z, sample), result) in values {
let pos = UnblendedNoisePos::new(x, y, z); let pos = Vector3::new(x, y, z);
assert_eq!( assert_eq!(
aquifer.apply_internal(&mut router, &pos, &env, &mut height_estimator, sample), aquifer.apply_internal(&mut router, &pos, &env, &mut height_estimator, sample),
result.map(|r| r.to_state()) result.map(|r| r.to_state())

View File

@ -3,8 +3,8 @@ pub mod ore_sampler;
pub mod perlin; pub mod perlin;
pub mod router; pub mod router;
use pumpkin_data::{Block, BlockState}; use pumpkin_data::{Block, BlockState, chunk_gen_settings::GenerationShapeConfig};
use pumpkin_util::math::{floor_div, floor_mod}; use pumpkin_util::math::{floor_div, floor_mod, vector3::Vector3};
use crate::generation::{ use crate::generation::{
noise::{ noise::{
@ -25,11 +25,10 @@ use super::{
WrapperData, WrapperData,
}, },
chunk_noise_router::ChunkNoiseRouter, chunk_noise_router::ChunkNoiseRouter,
density_function::{IndexToNoisePos, NoisePos, UnblendedNoisePos}, density_function::IndexToNoisePos,
proto_noise_router::ProtoNoiseRouter, proto_noise_router::ProtoNoiseRouter,
surface_height_sampler::SurfaceHeightEstimateSampler, surface_height_sampler::SurfaceHeightEstimateSampler,
}, },
settings::GenerationShapeConfig,
}; };
pub const LAVA_BLOCK: Block = Block::LAVA; pub const LAVA_BLOCK: Block = Block::LAVA;
@ -47,7 +46,7 @@ impl BlockStateSampler {
&mut self, &mut self,
router: &mut ChunkNoiseRouter, router: &mut ChunkNoiseRouter,
random_config: &GlobalRandomConfig, random_config: &GlobalRandomConfig,
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
height_estimator: &mut SurfaceHeightEstimateSampler, height_estimator: &mut SurfaceHeightEstimateSampler,
) -> Option<&'static BlockState> { ) -> Option<&'static BlockState> {
@ -72,7 +71,7 @@ impl ChainedBlockStateSampler {
&mut self, &mut self,
router: &mut ChunkNoiseRouter, router: &mut ChunkNoiseRouter,
random_config: &GlobalRandomConfig, random_config: &GlobalRandomConfig,
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
height_estimator: &mut SurfaceHeightEstimateSampler, height_estimator: &mut SurfaceHeightEstimateSampler,
) -> Option<&'static BlockState> { ) -> Option<&'static BlockState> {
@ -100,7 +99,7 @@ impl IndexToNoisePos for InterpolationIndexMapper {
&self, &self,
index: usize, index: usize,
sample_data: Option<&mut ChunkNoiseFunctionSampleOptions>, sample_data: Option<&mut ChunkNoiseFunctionSampleOptions>,
) -> impl NoisePos + 'static { ) -> Vector3<i32> {
if let Some(sample_data) = sample_data { if let Some(sample_data) = sample_data {
sample_data.cache_result_unique_id += 1; sample_data.cache_result_unique_id += 1;
sample_data.fill_index = index; 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; let y = (index as i32 + self.minimum_cell_y) * self.vertical_cell_block_count;
// TODO: Change this when Blender is implemented // 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, &self,
index: usize, index: usize,
sample_options: Option<&mut ChunkNoiseFunctionSampleOptions>, sample_options: Option<&mut ChunkNoiseFunctionSampleOptions>,
) -> impl NoisePos + 'static { ) -> Vector3<i32> {
let cell_z_position = floor_mod(index, self.horizontal_cell_block_count); let cell_z_position = floor_mod(index, self.horizontal_cell_block_count);
let xy_portion = floor_div(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); 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 // TODO: Change this when Blender is implemented
UnblendedNoisePos::new( Vector3::new(
self.start_x + cell_x_position as i32, self.start_x + cell_x_position as i32,
self.start_y + cell_y_position as i32, self.start_y + cell_y_position as i32,
self.start_z + cell_z_position as i32, self.start_z + cell_z_position as i32,
@ -382,7 +381,7 @@ impl<'a> ChunkNoiseGenerator<'a> {
height_estimator: &mut SurfaceHeightEstimateSampler, height_estimator: &mut SurfaceHeightEstimateSampler,
) -> Option<&'static BlockState> { ) -> Option<&'static BlockState> {
//TODO: Fix this when Blender is added //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( let options = ChunkNoiseFunctionSampleOptions::new(
false, false,

View File

@ -1,6 +1,6 @@
use pumpkin_data::{Block, BlockState}; use pumpkin_data::{Block, BlockState};
use pumpkin_util::{ use pumpkin_util::{
math::clamped_map, math::{clamped_map, vector3::Vector3},
random::{RandomDeriverImpl, RandomImpl}, random::{RandomDeriverImpl, RandomImpl},
}; };
@ -8,7 +8,7 @@ use crate::{
GlobalRandomConfig, GlobalRandomConfig,
generation::noise::router::{ generation::noise::router::{
chunk_density_function::ChunkNoiseFunctionSampleOptions, chunk_density_function::ChunkNoiseFunctionSampleOptions,
chunk_noise_router::ChunkNoiseRouter, density_function::NoisePos, chunk_noise_router::ChunkNoiseRouter,
}, },
}; };
@ -19,7 +19,7 @@ impl OreVeinSampler {
&self, &self,
router: &mut ChunkNoiseRouter, router: &mut ChunkNoiseRouter,
random_config: &GlobalRandomConfig, random_config: &GlobalRandomConfig,
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> Option<&'static BlockState> { ) -> Option<&'static BlockState> {
let vein_toggle = router.vein_toggle(pos, sample_options); let vein_toggle = router.vein_toggle(pos, sample_options);
@ -29,7 +29,7 @@ impl OreVeinSampler {
&vein_type::IRON &vein_type::IRON
}; };
let block_y = pos.y(); let block_y = pos.y;
let max_to_y = vein_type.max_y - block_y; let max_to_y = vein_type.max_y - block_y;
let y_to_min = block_y - vein_type.min_y; let y_to_min = block_y - vein_type.min_y;
if (max_to_y >= 0) && (y_to_min >= 0) { 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 mapped_diff = clamped_map(closest_to_bound as f64, 0f64, 20f64, -0.2f64, 0f64);
let abs_sample = vein_toggle.abs(); let abs_sample = vein_toggle.abs();
if abs_sample + mapped_diff >= 0.4f32 as f64 { if abs_sample + mapped_diff >= 0.4f32 as f64 {
let mut random = let mut random = random_config
random_config .ore_random_deriver
.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); let vein_ridged_sample = router.vein_ridged(pos, sample_options);
if random.next_f32() <= 0.7f32 && vein_ridged_sample < 0f64 { if random.next_f32() <= 0.7f32 && vein_ridged_sample < 0f64 {

View File

@ -3,10 +3,10 @@ use std::mem;
use super::{ use super::{
chunk_noise_router::{ChunkNoiseFunctionComponent, MutableChunkNoiseFunctionComponentImpl}, chunk_noise_router::{ChunkNoiseFunctionComponent, MutableChunkNoiseFunctionComponentImpl},
density_function::{IndexToNoisePos, NoiseFunctionComponentRange, NoisePos}, density_function::{IndexToNoisePos, NoiseFunctionComponentRange},
}; };
use enum_dispatch::enum_dispatch; 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}; use crate::generation::{biome_coords, positions::chunk_pos};
@ -106,7 +106,7 @@ pub enum SampleAction {
} }
pub struct ChunkNoiseFunctionSampleOptions { pub struct ChunkNoiseFunctionSampleOptions {
populating_caches: bool, pub(crate) populating_caches: bool,
pub(crate) action: SampleAction, pub(crate) action: SampleAction,
// Global IDs for the `CacheOnce` wrapper // Global IDs for the `CacheOnce` wrapper
@ -314,7 +314,7 @@ impl MutableChunkNoiseFunctionComponentImpl for DensityInterpolator {
fn sample( fn sample(
&mut self, &mut self,
component_stack: &mut [ChunkNoiseFunctionComponent], component_stack: &mut [ChunkNoiseFunctionComponent],
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> f64 { ) -> f64 {
match &sample_options.action { match &sample_options.action {
@ -402,11 +402,11 @@ impl MutableChunkNoiseFunctionComponentImpl for FlatCache {
fn sample( fn sample(
&mut self, &mut self,
component_stack: &mut [ChunkNoiseFunctionComponent], component_stack: &mut [ChunkNoiseFunctionComponent],
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> f64 { ) -> f64 {
let absolute_biome_x_position = biome_coords::from_block(pos.x()); 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_z_position = biome_coords::from_block(pos.z);
let relative_biome_x_position = absolute_biome_x_position - self.start_biome_x; 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; let relative_biome_z_position = absolute_biome_z_position - self.start_biome_z;
@ -498,10 +498,10 @@ impl MutableChunkNoiseFunctionComponentImpl for Cache2D {
fn sample( fn sample(
&mut self, &mut self,
component_stack: &mut [ChunkNoiseFunctionComponent], component_stack: &mut [ChunkNoiseFunctionComponent],
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> f64 { ) -> 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 { if packed_column == self.last_sample_column {
self.last_sample_result self.last_sample_result
} else { } else {
@ -561,7 +561,7 @@ impl MutableChunkNoiseFunctionComponentImpl for CacheOnce {
fn sample( fn sample(
&mut self, &mut self,
component_stack: &mut [ChunkNoiseFunctionComponent], component_stack: &mut [ChunkNoiseFunctionComponent],
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> f64 { ) -> f64 {
match sample_options.action { match sample_options.action {
@ -663,7 +663,7 @@ impl MutableChunkNoiseFunctionComponentImpl for CellCache {
fn sample( fn sample(
&mut self, &mut self,
component_stack: &mut [ChunkNoiseFunctionComponent], component_stack: &mut [ChunkNoiseFunctionComponent],
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> f64 { ) -> f64 {
match &sample_options.action { match &sample_options.action {

View File

@ -1,5 +1,6 @@
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use pumpkin_data::noise_router::WrapperType; use pumpkin_data::noise_router::WrapperType;
use pumpkin_util::math::vector3::Vector3;
use crate::generation::biome_coords; use crate::generation::biome_coords;
@ -10,8 +11,8 @@ use super::{
FlatCache, SampleAction, FlatCache, SampleAction,
}, },
density_function::{ density_function::{
IndexToNoisePos, NoiseFunctionComponentRange, NoisePos, PassThrough, IndexToNoisePos, NoiseFunctionComponentRange, PassThrough,
StaticIndependentChunkNoiseFunctionComponentImpl, UnblendedNoisePos, StaticIndependentChunkNoiseFunctionComponentImpl,
}, },
proto_noise_router::{ proto_noise_router::{
DependentProtoNoiseFunctionComponent, IndependentProtoNoiseFunctionComponent, DependentProtoNoiseFunctionComponent, IndependentProtoNoiseFunctionComponent,
@ -24,7 +25,7 @@ pub trait StaticChunkNoiseFunctionComponentImpl {
fn sample( fn sample(
&self, &self,
component_stack: &mut [ChunkNoiseFunctionComponent], component_stack: &mut [ChunkNoiseFunctionComponent],
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> f64; ) -> f64;
@ -47,7 +48,7 @@ pub trait MutableChunkNoiseFunctionComponentImpl {
fn sample( fn sample(
&mut self, &mut self,
component_stack: &mut [ChunkNoiseFunctionComponent], component_stack: &mut [ChunkNoiseFunctionComponent],
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> f64; ) -> f64;
@ -189,7 +190,7 @@ impl MutableChunkNoiseFunctionComponentImpl for ChunkNoiseFunctionComponent<'_>
fn sample( fn sample(
&mut self, &mut self,
component_stack: &mut [ChunkNoiseFunctionComponent], component_stack: &mut [ChunkNoiseFunctionComponent],
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> f64 { ) -> f64 {
match self { match self {
@ -225,7 +226,6 @@ impl MutableChunkNoiseFunctionComponentImpl for ChunkNoiseFunctionComponent<'_>
mapper, mapper,
sample_options, sample_options,
), ),
//Self::Panic(message) => panic!("{}", message),
} }
} }
} }
@ -233,7 +233,7 @@ impl MutableChunkNoiseFunctionComponentImpl for ChunkNoiseFunctionComponent<'_>
impl ChunkNoiseFunctionComponent<'_> { impl ChunkNoiseFunctionComponent<'_> {
pub fn sample_from_stack( pub fn sample_from_stack(
component_stack: &mut [ChunkNoiseFunctionComponent], component_stack: &mut [ChunkNoiseFunctionComponent],
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> f64 { ) -> f64 {
let (top_component, component_stack) = component_stack.split_last_mut().unwrap(); let (top_component, component_stack) = component_stack.split_last_mut().unwrap();
@ -259,7 +259,7 @@ impl ChunkNoiseDensityFunction<'_> {
#[inline] #[inline]
pub fn sample( pub fn sample(
&mut self, &mut self,
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> f64 { ) -> f64 {
ChunkNoiseFunctionComponent::sample_from_stack(self.component_stack, pos, sample_options) ChunkNoiseFunctionComponent::sample_from_stack(self.component_stack, pos, sample_options)
@ -286,7 +286,7 @@ macro_rules! sample_function {
#[inline] #[inline]
pub fn $name( pub fn $name(
&mut self, &mut self,
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> f64 { ) -> f64 {
ChunkNoiseFunctionComponent::sample_from_stack( ChunkNoiseFunctionComponent::sample_from_stack(
@ -421,11 +421,7 @@ impl<'a> ChunkNoiseRouter<'a> {
let block_z_position = let block_z_position =
biome_coords::to_block(absolute_biome_z_position); biome_coords::to_block(absolute_biome_z_position);
let pos = UnblendedNoisePos::new( let pos = Vector3::new(block_x_position, 0, block_z_position);
block_x_position,
0,
block_z_position,
);
//NOTE: Due to our stack invariant, what is on the stack is a //NOTE: Due to our stack invariant, what is on the stack is a
// valid density function // valid density function

View File

@ -1,4 +1,5 @@
use pumpkin_data::noise_router::{BinaryData, BinaryOperation, ClampData, LinearData, UnaryData}; use pumpkin_data::noise_router::{BinaryData, BinaryOperation, ClampData, LinearData, UnaryData};
use pumpkin_util::math::vector3::Vector3;
use crate::generation::noise::router::{ use crate::generation::noise::router::{
chunk_density_function::ChunkNoiseFunctionSampleOptions, chunk_density_function::ChunkNoiseFunctionSampleOptions,
@ -6,8 +7,7 @@ use crate::generation::noise::router::{
}; };
use super::{ use super::{
IndexToNoisePos, NoiseFunctionComponentRange, NoisePos, IndexToNoisePos, NoiseFunctionComponentRange, StaticIndependentChunkNoiseFunctionComponentImpl,
StaticIndependentChunkNoiseFunctionComponentImpl,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -34,7 +34,7 @@ impl NoiseFunctionComponentRange for Constant {
} }
impl StaticIndependentChunkNoiseFunctionComponentImpl for Constant { impl StaticIndependentChunkNoiseFunctionComponentImpl for Constant {
fn sample(&self, _pos: &impl NoisePos) -> f64 { fn sample(&self, _pos: &Vector3<i32>) -> f64 {
self.value self.value
} }
@ -67,7 +67,7 @@ impl StaticChunkNoiseFunctionComponentImpl for Linear {
fn sample( fn sample(
&self, &self,
component_stack: &mut [ChunkNoiseFunctionComponent], component_stack: &mut [ChunkNoiseFunctionComponent],
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> f64 { ) -> f64 {
let input_density = ChunkNoiseFunctionComponent::sample_from_stack( let input_density = ChunkNoiseFunctionComponent::sample_from_stack(
@ -120,7 +120,7 @@ impl StaticChunkNoiseFunctionComponentImpl for Binary {
fn sample( fn sample(
&self, &self,
component_stack: &mut [ChunkNoiseFunctionComponent], component_stack: &mut [ChunkNoiseFunctionComponent],
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> f64 { ) -> f64 {
let input1_density = ChunkNoiseFunctionComponent::sample_from_stack( let input1_density = ChunkNoiseFunctionComponent::sample_from_stack(
@ -296,7 +296,7 @@ impl StaticChunkNoiseFunctionComponentImpl for Unary {
fn sample( fn sample(
&self, &self,
component_stack: &mut [ChunkNoiseFunctionComponent], component_stack: &mut [ChunkNoiseFunctionComponent],
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> f64 { ) -> f64 {
let input_density = ChunkNoiseFunctionComponent::sample_from_stack( let input_density = ChunkNoiseFunctionComponent::sample_from_stack(
@ -370,7 +370,7 @@ impl StaticChunkNoiseFunctionComponentImpl for Clamp {
fn sample( fn sample(
&self, &self,
component_stack: &mut [ChunkNoiseFunctionComponent], component_stack: &mut [ChunkNoiseFunctionComponent],
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> f64 { ) -> f64 {
let input_density = ChunkNoiseFunctionComponent::sample_from_stack( let input_density = ChunkNoiseFunctionComponent::sample_from_stack(

View File

@ -4,7 +4,7 @@ use pumpkin_data::noise_router::{
ClampedYGradientData, RangeChoiceData, WeirdScaledData, WeirdScaledMapper, ClampedYGradientData, RangeChoiceData, WeirdScaledData, WeirdScaledMapper,
}; };
use pumpkin_util::{ use pumpkin_util::{
math::clamped_map, math::{clamped_map, vector3::Vector3},
noise::simplex::SimplexNoiseSampler, noise::simplex::SimplexNoiseSampler,
random::{RandomImpl, legacy_rand::LegacyRand}, random::{RandomImpl, legacy_rand::LegacyRand},
}; };
@ -18,8 +18,7 @@ use crate::generation::{
}; };
use super::{ use super::{
IndexToNoisePos, NoiseFunctionComponentRange, NoisePos, IndexToNoisePos, NoiseFunctionComponentRange, StaticIndependentChunkNoiseFunctionComponentImpl,
StaticIndependentChunkNoiseFunctionComponentImpl,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -80,8 +79,8 @@ impl NoiseFunctionComponentRange for EndIsland {
} }
impl StaticIndependentChunkNoiseFunctionComponentImpl for EndIsland { impl StaticIndependentChunkNoiseFunctionComponentImpl for EndIsland {
fn sample(&self, pos: &impl NoisePos) -> f64 { fn sample(&self, pos: &Vector3<i32>) -> f64 {
(Self::sample_2d(&self.sampler, pos.x() / 8, pos.z() / 8) as f64 - 8.0) / 128.0 (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( fn sample(
&self, &self,
component_stack: &mut [ChunkNoiseFunctionComponent], component_stack: &mut [ChunkNoiseFunctionComponent],
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> f64 { ) -> f64 {
let input_density = ChunkNoiseFunctionComponent::sample_from_stack( let input_density = ChunkNoiseFunctionComponent::sample_from_stack(
@ -122,9 +121,9 @@ impl StaticChunkNoiseFunctionComponentImpl for WeirdScaled {
* self * self
.sampler .sampler
.sample( .sample(
pos.x() as f64 / scaled_density, pos.x as f64 / scaled_density,
pos.y() as f64 / scaled_density, pos.y as f64 / scaled_density,
pos.z() as f64 / scaled_density, pos.z as f64 / scaled_density,
) )
.abs() .abs()
} }
@ -150,9 +149,9 @@ impl StaticChunkNoiseFunctionComponentImpl for WeirdScaled {
* self * self
.sampler .sampler
.sample( .sample(
pos.x() as f64 / scaled_density, pos.x as f64 / scaled_density,
pos.y() as f64 / scaled_density, pos.y as f64 / scaled_density,
pos.z() as f64 / scaled_density, pos.z as f64 / scaled_density,
) )
.abs(); .abs();
}); });
@ -195,9 +194,9 @@ impl NoiseFunctionComponentRange for ClampedYGradient {
} }
impl StaticIndependentChunkNoiseFunctionComponentImpl for ClampedYGradient { impl StaticIndependentChunkNoiseFunctionComponentImpl for ClampedYGradient {
fn sample(&self, pos: &impl NoisePos) -> f64 { fn sample(&self, pos: &Vector3<i32>) -> f64 {
clamped_map( clamped_map(
pos.y() as f64, pos.y as f64,
self.data.from_y, self.data.from_y,
self.data.to_y, self.data.to_y,
self.data.from_value, self.data.from_value,
@ -252,7 +251,7 @@ impl StaticChunkNoiseFunctionComponentImpl for RangeChoice {
fn sample( fn sample(
&self, &self,
component_stack: &mut [ChunkNoiseFunctionComponent], component_stack: &mut [ChunkNoiseFunctionComponent],
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> f64 { ) -> f64 {
let input_sample = ChunkNoiseFunctionComponent::sample_from_stack( let input_sample = ChunkNoiseFunctionComponent::sample_from_stack(

View File

@ -1,5 +1,6 @@
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use pumpkin_data::noise_router::WrapperType; use pumpkin_data::noise_router::WrapperType;
use pumpkin_util::math::vector3::Vector3;
// These are for enum_dispatch // These are for enum_dispatch
use super::chunk_density_function::{ use super::chunk_density_function::{
@ -18,48 +19,12 @@ mod test;
#[cfg(test)] #[cfg(test)]
mod test_deserializer; 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 { pub trait IndexToNoisePos {
fn at( fn at(
&self, &self,
index: usize, index: usize,
sample_options: Option<&mut ChunkNoiseFunctionSampleOptions>, sample_options: Option<&mut ChunkNoiseFunctionSampleOptions>,
) -> impl NoisePos + 'static; ) -> Vector3<i32>;
} }
#[enum_dispatch] #[enum_dispatch]
@ -70,7 +35,7 @@ pub trait NoiseFunctionComponentRange {
#[enum_dispatch] #[enum_dispatch]
pub trait StaticIndependentChunkNoiseFunctionComponentImpl: NoiseFunctionComponentRange { 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) { fn fill(&self, array: &mut [f64], mapper: &impl IndexToNoisePos) {
array.iter_mut().enumerate().for_each(|(index, value)| { array.iter_mut().enumerate().for_each(|(index, value)| {
let pos = mapper.at(index, None); let pos = mapper.at(index, None);

View File

@ -2,7 +2,9 @@ use std::array;
use pumpkin_data::noise_router::{InterpolatedNoiseSamplerData, NoiseData, ShiftedNoiseData}; use pumpkin_data::noise_router::{InterpolatedNoiseSamplerData, NoiseData, ShiftedNoiseData};
use pumpkin_util::{ use pumpkin_util::{
math::clamped_lerp, noise::perlin::OctavePerlinNoiseSampler, random::RandomGenerator, math::{clamped_lerp, vector3::Vector3},
noise::perlin::OctavePerlinNoiseSampler,
random::RandomGenerator,
}; };
use crate::generation::{ use crate::generation::{
@ -13,9 +15,7 @@ use crate::generation::{
}, },
}; };
use super::{ use super::{NoiseFunctionComponentRange, StaticIndependentChunkNoiseFunctionComponentImpl};
NoiseFunctionComponentRange, NoisePos, StaticIndependentChunkNoiseFunctionComponentImpl,
};
pub struct Noise { pub struct Noise {
sampler: DoublePerlinNoiseSampler, sampler: DoublePerlinNoiseSampler,
@ -41,11 +41,11 @@ impl NoiseFunctionComponentRange for Noise {
} }
impl StaticIndependentChunkNoiseFunctionComponentImpl for Noise { impl StaticIndependentChunkNoiseFunctionComponentImpl for Noise {
fn sample(&self, pos: &impl NoisePos) -> f64 { fn sample(&self, pos: &Vector3<i32>) -> f64 {
self.sampler.sample( self.sampler.sample(
pos.x() as f64 * self.data.xz_scale, pos.x as f64 * self.data.xz_scale,
pos.y() as f64 * self.data.y_scale, pos.y as f64 * self.data.y_scale,
pos.z() as f64 * self.data.xz_scale, pos.z as f64 * self.data.xz_scale,
) )
} }
} }
@ -78,8 +78,8 @@ impl NoiseFunctionComponentRange for ShiftA {
} }
impl StaticIndependentChunkNoiseFunctionComponentImpl for ShiftA { impl StaticIndependentChunkNoiseFunctionComponentImpl for ShiftA {
fn sample(&self, pos: &impl NoisePos) -> f64 { fn sample(&self, pos: &Vector3<i32>) -> f64 {
shift_sample_3d(&self.sampler, pos.x() as f64, 0.0, pos.z() as 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 { impl StaticIndependentChunkNoiseFunctionComponentImpl for ShiftB {
fn sample(&self, pos: &impl NoisePos) -> f64 { fn sample(&self, pos: &Vector3<i32>) -> f64 {
shift_sample_3d(&self.sampler, pos.z() as f64, pos.x() as f64, 0.0) 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( fn sample(
&self, &self,
component_stack: &mut [ChunkNoiseFunctionComponent], component_stack: &mut [ChunkNoiseFunctionComponent],
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> f64 { ) -> 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( + ChunkNoiseFunctionComponent::sample_from_stack(
&mut component_stack[..=self.input_x_index], &mut component_stack[..=self.input_x_index],
pos, pos,
sample_options, 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( + ChunkNoiseFunctionComponent::sample_from_stack(
&mut component_stack[..=self.input_y_index], &mut component_stack[..=self.input_y_index],
pos, pos,
sample_options, 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( + ChunkNoiseFunctionComponent::sample_from_stack(
&mut component_stack[..=self.input_z_index], &mut component_stack[..=self.input_z_index],
pos, pos,
@ -235,10 +235,10 @@ impl NoiseFunctionComponentRange for InterpolatedNoiseSampler {
} }
impl StaticIndependentChunkNoiseFunctionComponentImpl for InterpolatedNoiseSampler { impl StaticIndependentChunkNoiseFunctionComponentImpl for InterpolatedNoiseSampler {
fn sample(&self, pos: &impl NoisePos) -> f64 { fn sample(&self, pos: &Vector3<i32>) -> f64 {
let d = pos.x() as f64 * self.data.scaled_xz_scale; let d = pos.x as f64 * self.data.scaled_xz_scale;
let e = pos.y() as f64 * self.data.scaled_y_scale; let e = pos.y as f64 * self.data.scaled_y_scale;
let f = pos.z() as f64 * self.data.scaled_xz_scale; let f = pos.z as f64 * self.data.scaled_xz_scale;
let g = d / self.data.xz_factor; let g = d / self.data.xz_factor;
let h = e / self.data.y_factor; let h = e / self.data.y_factor;

View File

@ -1,4 +1,4 @@
use pumpkin_util::math::lerp; use pumpkin_util::math::{lerp, vector3::Vector3};
use crate::generation::noise::router::{ use crate::generation::noise::router::{
chunk_density_function::ChunkNoiseFunctionSampleOptions, chunk_density_function::ChunkNoiseFunctionSampleOptions,
@ -6,7 +6,7 @@ use crate::generation::noise::router::{
proto_noise_router::ProtoNoiseFunctionComponent, proto_noise_router::ProtoNoiseFunctionComponent,
}; };
use super::{NoiseFunctionComponentRange, NoisePos}; use super::NoiseFunctionComponentRange;
#[derive(Clone)] #[derive(Clone)]
pub enum SplineValue { pub enum SplineValue {
@ -18,7 +18,7 @@ impl SplineValue {
#[inline] #[inline]
fn sample( fn sample(
&self, &self,
pos: &impl NoisePos, pos: &Vector3<i32>,
component_stack: &mut [ChunkNoiseFunctionComponent], component_stack: &mut [ChunkNoiseFunctionComponent],
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> f32 { ) -> f32 {
@ -151,7 +151,7 @@ impl Spline {
fn sample( fn sample(
&self, &self,
pos: &impl NoisePos, pos: &Vector3<i32>,
component_stack: &mut [ChunkNoiseFunctionComponent], component_stack: &mut [ChunkNoiseFunctionComponent],
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> f32 { ) -> f32 {
@ -224,7 +224,7 @@ impl StaticChunkNoiseFunctionComponentImpl for SplineFunction {
fn sample( fn sample(
&self, &self,
component_stack: &mut [ChunkNoiseFunctionComponent], component_stack: &mut [ChunkNoiseFunctionComponent],
pos: &impl NoisePos, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> f64 { ) -> f64 {
self.spline.sample(pos, component_stack, sample_options) as f64 self.spline.sample(pos, component_stack, sample_options) as f64

View File

@ -1,4 +1,5 @@
use pumpkin_data::noise_router::OVERWORLD_BASE_NOISE_ROUTER; 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 pumpkin_util::{assert_eq_delta, read_data_from_file};
use serde::Deserialize; use serde::Deserialize;
use std::fs; use std::fs;
@ -18,26 +19,7 @@ use crate::generation::noise::router::proto_noise_router::{
}; };
use super::test_deserializer::DensityFunctionRepr; use super::test_deserializer::DensityFunctionRepr;
use super::{NoiseFunctionComponentRange, NoisePos, PassThrough}; use super::{NoiseFunctionComponentRange, 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
}
}
// This is a dummy value because we are not actually building chunk-specific functions // This is a dummy value because we are not actually building chunk-specific functions
static TEST_OPTIONS: ChunkNoiseFunctionBuilderOptions = 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 // This is equivalent to a Java `NoiseRouter` after being passed into `NoiseConfig` but before being
// passed into `ChunkNoiseGenerator` // passed into `ChunkNoiseGenerator`
fn normal_surface_noisified() { 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 // TODO: Move these values to a file and create an extractor for them
assert_eq!( assert_eq!(
sample_noise_router_function!(barrier_noise, pos), sample_noise_router_function!(barrier_noise, pos),
@ -307,7 +289,7 @@ fn normal_surface_noisified() {
((100, 200, 100), 0.0f64), ((100, 200, 100), 0.0f64),
]; ];
for ((x, y, z), value) in values { 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); assert_eq!(sample_noise_router_function!(vein_toggle, pos), value);
} }
@ -439,7 +421,7 @@ fn normal_surface_noisified() {
((100, 200, 100), -0.07999999821186066f64), ((100, 200, 100), -0.07999999821186066f64),
]; ];
for ((x, y, z), value) in values { 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); assert_eq!(sample_noise_router_function!(vein_ridged, pos), value);
} }
@ -571,7 +553,7 @@ fn normal_surface_noisified() {
((100, 200, 100), 0.3867496985743827f64), ((100, 200, 100), 0.3867496985743827f64),
]; ];
for ((x, y, z), value) in values { 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); 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 // 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) { 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!( assert_eq_delta!(
function.sample(&pos, &TEST_SAMPLE_OPTIONS), function.sample(&pos, &TEST_SAMPLE_OPTIONS),
sample, sample,
@ -659,7 +641,7 @@ fn base_sloped_cheese() {
let expected_data: Vec<(i32, i32, i32, f64)> = let expected_data: Vec<(i32, i32, i32, f64)> =
read_data_from_file!("../../../../../assets/converted_sloped_cheese_7_4.json"); read_data_from_file!("../../../../../assets/converted_sloped_cheese_7_4.json");
for (x, y, z, sample) in expected_data { for (x, y, z, sample) in expected_data {
let pos = TestNoisePos { x, y, z }; let pos = Vector3 { x, y, z };
assert_eq_delta!( assert_eq_delta!(
function.sample(&pos, &TEST_SAMPLE_OPTIONS), function.sample(&pos, &TEST_SAMPLE_OPTIONS),
sample, sample,
@ -678,7 +660,7 @@ fn base_factor() {
let expected_data: Vec<(i32, i32, i32, f64)> = let expected_data: Vec<(i32, i32, i32, f64)> =
read_data_from_file!("../../../../../assets/converted_factor_7_4.json"); read_data_from_file!("../../../../../assets/converted_factor_7_4.json");
for (x, y, z, sample) in expected_data { for (x, y, z, sample) in expected_data {
let pos = TestNoisePos { x, y, z }; let pos = Vector3 { x, y, z };
assert_eq_delta!( assert_eq_delta!(
function.sample(&pos, &TEST_SAMPLE_OPTIONS), function.sample(&pos, &TEST_SAMPLE_OPTIONS),
sample, sample,
@ -697,7 +679,7 @@ fn base_depth() {
let expected_data: Vec<(i32, i32, i32, f64)> = let expected_data: Vec<(i32, i32, i32, f64)> =
read_data_from_file!("../../../../../assets/converted_depth_7_4.json"); read_data_from_file!("../../../../../assets/converted_depth_7_4.json");
for (x, y, z, sample) in expected_data { for (x, y, z, sample) in expected_data {
let pos = TestNoisePos { x, y, z }; let pos = Vector3 { x, y, z };
assert_eq_delta!( assert_eq_delta!(
function.sample(&pos, &TEST_SAMPLE_OPTIONS), function.sample(&pos, &TEST_SAMPLE_OPTIONS),
sample, sample,
@ -716,7 +698,7 @@ fn base_offset() {
let expected_data: Vec<(i32, i32, i32, f64)> = let expected_data: Vec<(i32, i32, i32, f64)> =
read_data_from_file!("../../../../../assets/converted_offset_7_4.json"); read_data_from_file!("../../../../../assets/converted_offset_7_4.json");
for (x, y, z, sample) in expected_data { for (x, y, z, sample) in expected_data {
let pos = TestNoisePos { x, y, z }; let pos = Vector3 { x, y, z };
assert_eq_delta!( assert_eq_delta!(
function.sample(&pos, &TEST_SAMPLE_OPTIONS), function.sample(&pos, &TEST_SAMPLE_OPTIONS),
sample, sample,
@ -735,7 +717,7 @@ fn base_cave_entrances() {
let expected_data: Vec<(i32, i32, i32, f64)> = let expected_data: Vec<(i32, i32, i32, f64)> =
read_data_from_file!("../../../../../assets/converted_cave_entrances_overworld_7_4.json"); read_data_from_file!("../../../../../assets/converted_cave_entrances_overworld_7_4.json");
for (x, y, z, sample) in expected_data { for (x, y, z, sample) in expected_data {
let pos = TestNoisePos { x, y, z }; let pos = Vector3 { x, y, z };
assert_eq_delta!( assert_eq_delta!(
function.sample(&pos, &TEST_SAMPLE_OPTIONS), function.sample(&pos, &TEST_SAMPLE_OPTIONS),
sample, sample,
@ -754,7 +736,7 @@ fn base_3d_noise() {
let expected_data: Vec<(i32, i32, i32, f64)> = let expected_data: Vec<(i32, i32, i32, f64)> =
read_data_from_file!("../../../../../assets/converted_3d_overworld_7_4.json"); read_data_from_file!("../../../../../assets/converted_3d_overworld_7_4.json");
for (x, y, z, sample) in expected_data { for (x, y, z, sample) in expected_data {
let pos = TestNoisePos { x, y, z }; let pos = Vector3 { x, y, z };
assert_eq_delta!( assert_eq_delta!(
function.sample(&pos, &TEST_SAMPLE_OPTIONS), function.sample(&pos, &TEST_SAMPLE_OPTIONS),
sample, sample,
@ -776,7 +758,7 @@ fn base_spahetti_roughness() {
"../../../../../assets/converted_cave_spaghetti_rough_overworld_7_4.json" "../../../../../assets/converted_cave_spaghetti_rough_overworld_7_4.json"
); );
for (x, y, z, sample) in expected_data { for (x, y, z, sample) in expected_data {
let pos = TestNoisePos { x, y, z }; let pos = Vector3 { x, y, z };
assert_eq_delta!( assert_eq_delta!(
function.sample(&pos, &TEST_SAMPLE_OPTIONS), function.sample(&pos, &TEST_SAMPLE_OPTIONS),
sample, sample,
@ -795,7 +777,7 @@ fn base_cave_noodle() {
let expected_data: Vec<(i32, i32, i32, f64)> = let expected_data: Vec<(i32, i32, i32, f64)> =
read_data_from_file!("../../../../../assets/converted_cave_noodle_7_4.json"); read_data_from_file!("../../../../../assets/converted_cave_noodle_7_4.json");
for (x, y, z, sample) in expected_data { for (x, y, z, sample) in expected_data {
let pos = TestNoisePos { x, y, z }; let pos = Vector3 { x, y, z };
assert_eq_delta!( assert_eq_delta!(
function.sample(&pos, &TEST_SAMPLE_OPTIONS), function.sample(&pos, &TEST_SAMPLE_OPTIONS),
sample, sample,
@ -814,7 +796,7 @@ fn base_cave_pillars() {
let expected_data: Vec<(i32, i32, i32, f64)> = let expected_data: Vec<(i32, i32, i32, f64)> =
read_data_from_file!("../../../../../assets/converted_cave_pillar_7_4.json"); read_data_from_file!("../../../../../assets/converted_cave_pillar_7_4.json");
for (x, y, z, sample) in expected_data { for (x, y, z, sample) in expected_data {
let pos = TestNoisePos { x, y, z }; let pos = Vector3 { x, y, z };
assert_eq_delta!( assert_eq_delta!(
function.sample(&pos, &TEST_SAMPLE_OPTIONS), function.sample(&pos, &TEST_SAMPLE_OPTIONS),
sample, sample,
@ -835,7 +817,7 @@ fn base_spaghetti_2d_thickness() {
let expected_data: Vec<(i32, i32, i32, f64)> = let expected_data: Vec<(i32, i32, i32, f64)> =
read_data_from_file!("../../../../../assets/converted_cave_spaghetti_2d_thicc_7_4.json"); read_data_from_file!("../../../../../assets/converted_cave_spaghetti_2d_thicc_7_4.json");
for (x, y, z, sample) in expected_data { for (x, y, z, sample) in expected_data {
let pos = TestNoisePos { x, y, z }; let pos = Vector3 { x, y, z };
assert_eq_delta!( assert_eq_delta!(
function.sample(&pos, &TEST_SAMPLE_OPTIONS), function.sample(&pos, &TEST_SAMPLE_OPTIONS),
sample, sample,

View File

@ -1,4 +1,5 @@
use pumpkin_data::noise_router::WrapperType; use pumpkin_data::noise_router::WrapperType;
use pumpkin_util::math::vector3::Vector3;
use crate::{ use crate::{
biome::multi_noise::{NoiseValuePoint, to_long}, biome::multi_noise::{NoiseValuePoint, to_long},
@ -11,7 +12,7 @@ use super::{
SampleAction, SampleAction,
}, },
chunk_noise_router::ChunkNoiseFunctionComponent, chunk_noise_router::ChunkNoiseFunctionComponent,
density_function::{NoiseFunctionComponentRange, PassThrough, UnblendedNoisePos}, density_function::{NoiseFunctionComponentRange, PassThrough},
proto_noise_router::{ProtoMultiNoiseRouter, ProtoNoiseFunctionComponent}, proto_noise_router::{ProtoMultiNoiseRouter, ProtoNoiseFunctionComponent},
}; };
@ -52,7 +53,7 @@ impl<'a> MultiNoiseSampler<'a> {
let block_y = biome_coords::to_block(biome_y); let block_y = biome_coords::to_block(biome_y);
let block_z = biome_coords::to_block(biome_z); 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 = let sample_options =
ChunkNoiseFunctionSampleOptions::new(false, SampleAction::SkipCellCaches, 0, 0, 0); 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 { 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 = let sample_options =
ChunkNoiseFunctionSampleOptions::new(false, SampleAction::SkipCellCaches, 0, 0, 0); ChunkNoiseFunctionSampleOptions::new(false, SampleAction::SkipCellCaches, 0, 0, 0);
@ -178,11 +179,7 @@ impl<'a> MultiNoiseSampler<'a> {
let block_z_position = let block_z_position =
biome_coords::to_block(absolute_biome_z_position); biome_coords::to_block(absolute_biome_z_position);
let pos = UnblendedNoisePos::new( let pos = Vector3::new(block_x_position, 0, block_z_position);
block_x_position,
0,
block_z_position,
);
//NOTE: Due to our stack invariant, what is on the stack is a //NOTE: Due to our stack invariant, what is on the stack is a
// valid density function // valid density function

View File

@ -14,7 +14,7 @@ use super::{
chunk_density_function::ChunkNoiseFunctionSampleOptions, chunk_density_function::ChunkNoiseFunctionSampleOptions,
chunk_noise_router::{ChunkNoiseFunctionComponent, StaticChunkNoiseFunctionComponentImpl}, chunk_noise_router::{ChunkNoiseFunctionComponent, StaticChunkNoiseFunctionComponentImpl},
density_function::{ density_function::{
IndexToNoisePos, NoiseFunctionComponentRange, NoisePos, PassThrough, IndexToNoisePos, NoiseFunctionComponentRange, PassThrough,
StaticIndependentChunkNoiseFunctionComponentImpl, Wrapper, StaticIndependentChunkNoiseFunctionComponentImpl, Wrapper,
math::{Binary, Clamp, Constant, Linear, Unary}, math::{Binary, Clamp, Constant, Linear, Unary},
misc::{ClampedYGradient, EndIsland, RangeChoice, WeirdScaled}, misc::{ClampedYGradient, EndIsland, RangeChoice, WeirdScaled},
@ -22,6 +22,7 @@ use super::{
spline::{Spline, SplineFunction, SplinePoint, SplineValue}, spline::{Spline, SplineFunction, SplinePoint, SplineValue},
}, },
}; };
use pumpkin_util::math::vector3::Vector3;
#[enum_dispatch( #[enum_dispatch(
StaticIndependentChunkNoiseFunctionComponentImpl, StaticIndependentChunkNoiseFunctionComponentImpl,

View File

@ -1,6 +1,6 @@
use std::collections::HashMap;
use pumpkin_data::noise_router::WrapperType; use pumpkin_data::noise_router::WrapperType;
use pumpkin_util::math::vector3::Vector3;
use rustc_hash::FxHashMap;
use crate::generation::{biome_coords, positions::chunk_pos}; use crate::generation::{biome_coords, positions::chunk_pos};
@ -10,7 +10,7 @@ use super::{
SampleAction, SampleAction,
}, },
chunk_noise_router::ChunkNoiseFunctionComponent, chunk_noise_router::ChunkNoiseFunctionComponent,
density_function::{NoiseFunctionComponentRange, PassThrough, UnblendedNoisePos}, density_function::{NoiseFunctionComponentRange, PassThrough},
proto_noise_router::{ProtoNoiseFunctionComponent, ProtoSurfaceEstimator}, proto_noise_router::{ProtoNoiseFunctionComponent, ProtoSurfaceEstimator},
}; };
@ -58,7 +58,7 @@ pub struct SurfaceHeightEstimateSampler<'a> {
component_stack: Box<[ChunkNoiseFunctionComponent<'a>]>, component_stack: Box<[ChunkNoiseFunctionComponent<'a>]>,
// TODO: Can this be a flat map? I think the aquifer sampler samples outside of the chunk // 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> { impl<'a> SurfaceHeightEstimateSampler<'a> {
@ -90,7 +90,7 @@ impl<'a> SurfaceHeightEstimateSampler<'a> {
let mid = low + ((high - low) / 2); let mid = low + ((high - low) / 2);
let stepped_mid = mid - (mid % self.y_level_step_count as i32); 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( let density = ChunkNoiseFunctionComponent::sample_from_stack(
&mut self.component_stack, &mut self.component_stack,
&pos, &pos,
@ -172,11 +172,7 @@ impl<'a> SurfaceHeightEstimateSampler<'a> {
let block_z_position = let block_z_position =
biome_coords::to_block(absolute_biome_z_position); biome_coords::to_block(absolute_biome_z_position);
let pos = UnblendedNoisePos::new( let pos = Vector3::new(block_x_position, 0, block_z_position);
block_x_position,
0,
block_z_position,
);
//NOTE: Due to our stack invariant, what is on the stack is a //NOTE: Due to our stack invariant, what is on the stack is a
// valid density function // valid density function
@ -216,7 +212,7 @@ impl<'a> SurfaceHeightEstimateSampler<'a> {
minimum_y: build_options.minimum_y, minimum_y: build_options.minimum_y,
y_level_step_count: build_options.y_level_step_count, y_level_step_count: build_options.y_level_step_count,
cache: HashMap::new(), cache: FxHashMap::default(),
} }
} }
} }

View File

@ -1,8 +1,8 @@
use std::collections::HashMap;
use std::pin::Pin; use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use pumpkin_data::block_properties::is_air; use pumpkin_data::block_properties::is_air;
use pumpkin_data::chunk_gen_settings::GenerationSettings;
use pumpkin_data::dimension::Dimension; use pumpkin_data::dimension::Dimension;
use pumpkin_data::fluid::{Fluid, FluidState}; use pumpkin_data::fluid::{Fluid, FluidState};
use pumpkin_data::tag; use pumpkin_data::tag;
@ -13,6 +13,7 @@ use pumpkin_util::{
math::{position::BlockPos, vector3::Vector3}, math::{position::BlockPos, vector3::Vector3},
random::{RandomGenerator, get_decorator_seed, xoroshiro128::Xoroshiro}, random::{RandomGenerator, get_decorator_seed, xoroshiro128::Xoroshiro},
}; };
use rustc_hash::FxHashMap;
use super::{ use super::{
GlobalRandomConfig, biome_coords, GlobalRandomConfig, biome_coords,
@ -23,7 +24,6 @@ use super::{
}, },
positions::chunk_pos::{start_block_x, start_block_z}, positions::chunk_pos::{start_block_x, start_block_z},
section_coords, section_coords,
settings::GenerationSettings,
surface::{MaterialRuleContext, estimate_surface_height, terrain::SurfaceTerrainBuilder}, surface::{MaterialRuleContext, estimate_surface_height, terrain::SurfaceTerrainBuilder},
}; };
use crate::chunk::{ChunkData, ChunkHeightmapType}; 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::placement::StructurePlacementCalculator;
use crate::generation::structure::structures::StructureInstance; use crate::generation::structure::structures::StructureInstance;
use crate::generation::structure::{STRUCTURE_SETS, STRUCTURES, StructureKeys, WeightedEntry}; use crate::generation::structure::{STRUCTURE_SETS, STRUCTURES, StructureKeys, WeightedEntry};
use crate::generation::surface::rule::try_apply_material_rule;
use crate::{ use crate::{
BlockStateId, ProtoNoiseRouters, BlockStateId, ProtoNoiseRouters,
biome::{BiomeSupplier, MultiNoiseBiomeSupplier, end::TheEndBiomeSupplier}, biome::{BiomeSupplier, MultiNoiseBiomeSupplier, end::TheEndBiomeSupplier},
@ -142,7 +143,7 @@ pub struct ProtoChunk {
flat_ocean_floor_height_map: Box<[i16]>, flat_ocean_floor_height_map: Box<[i16]>,
pub flat_motion_blocking_height_map: Box<[i16]>, pub flat_motion_blocking_height_map: Box<[i16]>,
pub flat_motion_blocking_no_leaves_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 of the chunk for indexing
height: u16, height: u16,
@ -201,7 +202,7 @@ impl ProtoChunk {
flat_ocean_floor_height_map: default_heightmap.clone(), flat_ocean_floor_height_map: default_heightmap.clone(),
flat_motion_blocking_height_map: default_heightmap.clone(), flat_motion_blocking_height_map: default_heightmap.clone(),
flat_motion_blocking_no_leaves_height_map: default_heightmap, flat_motion_blocking_no_leaves_height_map: default_heightmap,
structure_starts: HashMap::new(), structure_starts: FxHashMap::default(),
height, height,
bottom_y: dimension.min_y as i8, bottom_y: dimension.min_y as i8,
stage: StagedChunkEnum::Empty, stage: StagedChunkEnum::Empty,
@ -313,45 +314,24 @@ impl ProtoChunk {
self.bottom_y self.bottom_y
} }
fn maybe_update_surface_height_map(&mut self, local_x: i32, y: i32, local_z: i32) { fn maybe_update_surface_height_map(&mut self, index: usize, y: i16) {
let index = Self::local_position_to_height_map_index(local_x, local_z);
let current_height = self.flat_surface_height_map[index]; let current_height = self.flat_surface_height_map[index];
self.flat_surface_height_map[index] = current_height.max(y) as _;
if y > current_height as i32 {
self.flat_surface_height_map[index] = y as _;
}
} }
fn maybe_update_ocean_floor_height_map(&mut self, local_x: i32, y: i32, local_z: i32) { fn maybe_update_ocean_floor_height_map(&mut self, index: usize, y: i16) {
let index = Self::local_position_to_height_map_index(local_x, local_z);
let current_height = self.flat_ocean_floor_height_map[index]; let current_height = self.flat_ocean_floor_height_map[index];
self.flat_ocean_floor_height_map[index] = current_height.max(y) as _;
if y > current_height as i32 {
self.flat_ocean_floor_height_map[index] = y as _;
}
} }
fn maybe_update_motion_blocking_height_map(&mut self, local_x: i32, y: i32, local_z: i32) { fn maybe_update_motion_blocking_height_map(&mut self, index: usize, y: i16) {
let index = Self::local_position_to_height_map_index(local_x, local_z);
let current_height = self.flat_motion_blocking_height_map[index]; let current_height = self.flat_motion_blocking_height_map[index];
self.flat_motion_blocking_height_map[index] = current_height.max(y) as _;
if y > current_height as i32 {
self.flat_motion_blocking_height_map[index] = y as _;
}
} }
fn maybe_update_motion_blocking_no_leaves_height_map( fn maybe_update_motion_blocking_no_leaves_height_map(&mut self, index: usize, y: i16) {
&mut self,
local_x: i32,
y: i32,
local_z: i32,
) {
let index = Self::local_position_to_height_map_index(local_x, local_z);
let current_height = self.flat_motion_blocking_no_leaves_height_map[index]; let current_height = self.flat_motion_blocking_no_leaves_height_map[index];
self.flat_motion_blocking_no_leaves_height_map[index] = current_height.max(y) as _;
if y > current_height as i32 {
self.flat_motion_blocking_no_leaves_height_map[index] = y as _;
}
} }
#[must_use] #[must_use]
@ -407,13 +387,10 @@ impl ProtoChunk {
#[inline] #[inline]
fn local_pos_to_block_index(&self, x: i32, y: i32, z: i32) -> usize { fn local_pos_to_block_index(&self, x: i32, y: i32, z: i32) -> usize {
#[cfg(debug_assertions)] debug_assert!((0..16).contains(&x), "x out of bounds: {}", x);
{ debug_assert!((0..16).contains(&z), "z out of bounds: {}", z);
assert!((0..=15).contains(&x)); debug_assert!(y >= 0 && y < self.height() as i32, "y out of bounds: {}", y);
assert!(y < self.height() as i32);
assert!(y >= 0);
assert!((0..=15).contains(&z));
};
self.height() as usize * CHUNK_DIM as usize * x as usize self.height() as usize * CHUNK_DIM as usize * x as usize
+ CHUNK_DIM as usize * y as usize + CHUNK_DIM as usize * y as usize
+ z as usize + z as usize
@ -422,22 +399,17 @@ impl ProtoChunk {
#[inline] #[inline]
#[must_use] #[must_use]
pub fn local_biome_pos_to_biome_index(&self, x: i32, y: i32, z: i32) -> usize { pub fn local_biome_pos_to_biome_index(&self, x: i32, y: i32, z: i32) -> usize {
#[cfg(debug_assertions)] let biome_height = self.height() as usize >> 2;
{
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),
y
);
assert!((0..=3).contains(&z));
};
biome_coords::from_block(self.height() as usize) debug_assert!((0..4).contains(&x), "Biome X out of bounds: {}", x);
* biome_coords::from_block(CHUNK_DIM as usize) debug_assert!((0..4).contains(&z), "Biome Z out of bounds: {}", z);
* x as usize debug_assert!(
y >= 0 && y < biome_height as i32,
"Biome Y out of bounds: {}",
y
);
biome_height * biome_coords::from_block(CHUNK_DIM as usize) * x as usize
+ biome_coords::from_block(CHUNK_DIM as usize) * y as usize + biome_coords::from_block(CHUNK_DIM as usize) * y as usize
+ z as usize + z as usize
} }
@ -474,18 +446,20 @@ impl ProtoChunk {
return; return;
} }
if !block_state.is_air() { 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 block = Block::get_raw_id_from_state_id(block_state.id);
let blocks_movement = blocks_movement(block_state, block); let blocks_movement = blocks_movement(block_state, block);
if blocks_movement { 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() { 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) { 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 start_z = start_block_z(self.z);
let sampler = FluidLevelSampler::Chunk(StandardChunkFluidLevelSampler::new( 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), FluidLevel::new(-54, &Block::LAVA),
)); ));
@ -873,7 +850,8 @@ impl ProtoChunk {
context.block_pos_y, context.block_pos_y,
context.block_pos_z, context.block_pos_z,
); );
let new_state = settings.surface_rule.try_apply( let new_state = try_apply_material_rule(
&settings.surface_rule,
self, self,
&mut context, &mut context,
surface_height_estimate_sampler, surface_height_estimate_sampler,

View File

@ -1,104 +1,104 @@
use std::{collections::HashMap, sync::LazyLock}; // use std::{collections::HashMap, sync::LazyLock};
use pumpkin_data::dimension::Dimension; // use pumpkin_data::dimension::Dimension;
use pumpkin_util::include_json_static; // use pumpkin_util::include_json_static;
use serde::Deserialize; // 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>> = // pub static GENERATION_SETTINGS: LazyLock<HashMap<GeneratorSetting, GenerationSettings>> =
LazyLock::new( // LazyLock::new(
|| include_json_static!("../../../assets/chunk_gen_settings.json", HashMap<GeneratorSetting, GenerationSettings>), // || include_json_static!("../../../assets/chunk_gen_settings.json", HashMap<GeneratorSetting, GenerationSettings>),
); // );
pub fn gen_settings_from_dimension(dimension: &Dimension) -> &GenerationSettings { // pub fn gen_settings_from_dimension(dimension: &Dimension) -> &GenerationSettings {
let setting = if dimension == &Dimension::OVERWORLD { // let setting = if dimension == &Dimension::OVERWORLD {
GeneratorSetting::Overworld // GeneratorSetting::Overworld
} else if dimension == &Dimension::THE_NETHER { // } else if dimension == &Dimension::THE_NETHER {
GeneratorSetting::Nether // GeneratorSetting::Nether
} else { // } else {
GeneratorSetting::End // GeneratorSetting::End
}; // };
GENERATION_SETTINGS.get(&setting).unwrap() // GENERATION_SETTINGS.get(&setting).unwrap()
} // }
#[derive(Deserialize, Hash, PartialEq, Eq)] // #[derive(Deserialize, Hash, PartialEq, Eq)]
#[serde(rename_all = "snake_case")] // #[serde(rename_all = "snake_case")]
pub enum GeneratorSetting { // pub enum GeneratorSetting {
Overworld, // Overworld,
LargeBiomes, // LargeBiomes,
Amplified, // Amplified,
Nether, // Nether,
End, // End,
Caves, // Caves,
FloatingIslands, // FloatingIslands,
} // }
#[derive(Deserialize)] // #[derive(Deserialize)]
pub struct GenerationSettings { // pub struct GenerationSettings {
pub aquifers_enabled: bool, // pub aquifers_enabled: bool,
pub ore_veins_enabled: bool, // pub ore_veins_enabled: bool,
pub legacy_random_source: bool, // pub legacy_random_source: bool,
pub sea_level: i32, // pub sea_level: i32,
pub default_fluid: BlockStateCodec, // pub default_fluid: BlockStateCodec,
#[serde(rename = "noise")] // #[serde(rename = "noise")]
pub shape: GenerationShapeConfig, // pub shape: GenerationShapeConfig,
pub surface_rule: MaterialRule, // pub surface_rule: MaterialRule,
pub default_block: BlockStateCodec, // pub default_block: BlockStateCodec,
} // }
#[derive(Deserialize)] // #[derive(Deserialize)]
pub struct GenerationShapeConfig { // pub struct GenerationShapeConfig {
pub min_y: i8, // pub min_y: i8,
pub height: u16, // pub height: u16,
size_horizontal: u8, // size_horizontal: u8,
size_vertical: u8, // size_vertical: u8,
} // }
impl GenerationShapeConfig { // impl GenerationShapeConfig {
#[inline] // #[inline]
#[must_use] // #[must_use]
pub fn vertical_cell_block_count(&self) -> u8 { // pub fn vertical_cell_block_count(&self) -> u8 {
to_block(self.size_vertical) // to_block(self.size_vertical)
} // }
#[inline] // #[inline]
#[must_use] // #[must_use]
pub fn horizontal_cell_block_count(&self) -> u8 { // pub fn horizontal_cell_block_count(&self) -> u8 {
to_block(self.size_horizontal) // to_block(self.size_horizontal)
} // }
#[must_use] // #[must_use]
pub const fn max_y(&self) -> u16 { // pub const fn max_y(&self) -> u16 {
if self.min_y >= 0 { // if self.min_y >= 0 {
self.height + self.min_y as u16 // self.height + self.min_y as u16
} else { // } else {
(self.height as i32 + self.min_y as i32) as u16 // (self.height as i32 + self.min_y as i32) as u16
} // }
} // }
pub fn trim_height(&self, limit: &dyn HeightLimitView) -> Self { // pub fn trim_height(&self, limit: &dyn HeightLimitView) -> Self {
let new_min = self.min_y.max(limit.bottom_y()); // let new_min = self.min_y.max(limit.bottom_y());
let this_top = if self.min_y >= 0 { // let this_top = if self.min_y >= 0 {
self.height + self.min_y as u16 // self.height + self.min_y as u16
} else { // } else {
self.height - self.min_y.unsigned_abs() as u16 // 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 { // let new_height = if new_min >= 0 {
new_top - new_min as u16 // new_top - new_min as u16
} else { // } else {
new_top + new_min.unsigned_abs() as u16 // new_top + new_min.unsigned_abs() as u16
}; // };
Self { // Self {
min_y: new_min, // min_y: new_min,
height: new_height, // height: new_height,
size_horizontal: self.size_horizontal, // size_horizontal: self.size_horizontal,
size_vertical: self.size_vertical, // size_vertical: self.size_vertical,
} // }
} // }
} // }

View File

@ -321,7 +321,7 @@ impl StructurePiece {
let block_pos = self.offset_pos(x, y, z); let block_pos = self.offset_pos(x, y, z);
if !box_limit.contains_pos(&block_pos) { if !box_limit.contains_pos(&block_pos) {
log::warn!("Structure out of bounds"); log::debug!("Structure out of bounds");
return Block::AIR.default_state; return Block::AIR.default_state;
} }
@ -341,7 +341,7 @@ impl StructurePiece {
// Bounds and logic checks // Bounds and logic checks
if !box_limit.contains_pos(&block_pos) { if !box_limit.contains_pos(&block_pos) {
log::warn!("Structure out of bounds"); log::debug!("Structure out of bounds");
return; return;
} }

View File

@ -1,7 +1,11 @@
use std::{cell::RefCell, num::NonZeroUsize}; use pumpkin_data::{
chunk::Biome,
use lru::LruCache; chunk_gen_settings::{
use pumpkin_data::chunk::Biome; AboveYMaterialCondition, MaterialCondition, NoiseThresholdMaterialCondition,
NotMaterialCondition, StoneDepthMaterialCondition, VerticalGradientMaterialCondition,
WaterMaterialCondition,
},
};
use pumpkin_util::{ use pumpkin_util::{
math::{lerp2, vertical_surface_type::VerticalSurfaceType}, math::{lerp2, vertical_surface_type::VerticalSurfaceType},
random::{RandomDeriver, RandomDeriverImpl, RandomImpl}, random::{RandomDeriver, RandomDeriverImpl, RandomImpl},
@ -9,7 +13,6 @@ use pumpkin_util::{
use serde::Deserialize; use serde::Deserialize;
use terrain::SurfaceTerrainBuilder; use terrain::SurfaceTerrainBuilder;
use thread_local::ThreadLocal;
use crate::{ use crate::{
ProtoChunk, ProtoChunk,
@ -22,7 +25,6 @@ use super::{
proto_noise_router::DoublePerlinNoiseBuilder, proto_noise_router::DoublePerlinNoiseBuilder,
surface_height_sampler::SurfaceHeightEstimateSampler, surface_height_sampler::SurfaceHeightEstimateSampler,
}, },
y_offset::YOffset,
}; };
pub mod rule; pub mod rule;
@ -138,86 +140,62 @@ impl<'a> MaterialRuleContext<'a> {
} }
} }
#[derive(Deserialize)] #[expect(clippy::similar_names)]
#[serde(tag = "type")] pub fn test_condition(
#[serde(rename_all = "snake_case")] condition: &MaterialCondition,
pub enum MaterialCondition { chunk: &mut ProtoChunk,
#[serde(rename = "minecraft:biome")] context: &mut MaterialRuleContext,
Biome(BiomeMaterialCondition), surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
#[serde(rename = "minecraft:noise_threshold")] ) -> bool {
NoiseThreshold(NoiseThresholdMaterialCondition), match condition {
#[serde(rename = "minecraft:vertical_gradient")] MaterialCondition::Biome(biome) => BiomeMaterialCondition::test(biome.biome_is, context),
VerticalGradient(Box<VerticalGradientMaterialCondition>), MaterialCondition::NoiseThreshold(noise_threshold) => {
#[serde(rename = "minecraft:y_above")] test_noise_threshold(noise_threshold, context)
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,
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 => {
let temperature = context.biome.weather.compute_temperature(
context.block_pos_x as f64,
context.block_pos_y,
context.block_pos_z as f64,
context.sea_level,
);
temperature < 0.15f32
}
Self::Steep => {
let local_x = context.block_pos_x & 15;
let local_z = context.block_pos_z & 15;
let local_z_sub = 0.max(local_z - 1);
let local_z_add = 15.min(local_z + 1);
let sub_height = chunk.top_block_height_exclusive(local_x, local_z_sub);
let add_height = chunk.top_block_height_exclusive(local_x, local_z_add);
if add_height >= sub_height + 4 {
true
} else {
let local_x_sub = 0.max(local_x - 1);
let local_x_add = 15.min(local_x + 1);
let sub_height = chunk.top_block_height_exclusive(local_x_sub, local_z);
let add_height = chunk.top_block_height_exclusive(local_x_add, local_z);
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) => {
SurfaceMaterialCondition::test(context, surface_height_estimate_sampler)
}
Self::StoneDepth(stone_depth) => stone_depth.test(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,
context.block_pos_z as f64,
context.sea_level,
);
temperature < 0.15f32
}
MaterialCondition::Steep => {
let local_x = context.block_pos_x & 15;
let local_z = context.block_pos_z & 15;
let local_z_sub = 0.max(local_z - 1);
let local_z_add = 15.min(local_z + 1);
let sub_height = chunk.top_block_height_exclusive(local_x, local_z_sub);
let add_height = chunk.top_block_height_exclusive(local_x, local_z_add);
if add_height >= sub_height + 4 {
true
} else {
let local_x_sub = 0.max(local_x - 1);
let local_x_add = 15.min(local_x + 1);
let sub_height = chunk.top_block_height_exclusive(local_x_sub, local_z);
let add_height = chunk.top_block_height_exclusive(local_x_add, local_z);
sub_height >= add_height + 4
}
}
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)
}
MaterialCondition::StoneDepth(stone_depth) => test_stone_depth(stone_depth, context),
} }
} }
@ -230,42 +208,32 @@ impl HoleMaterialCondition {
} }
} }
#[derive(Deserialize)] pub const fn test_above_y_material(
pub struct AboveYMaterialCondition { condition: &AboveYMaterialCondition,
anchor: YOffset, context: &MaterialRuleContext,
surface_depth_multiplier: i32, ) -> bool {
add_stone_depth: bool, context.block_pos_y
+ if condition.add_stone_depth {
context.stone_depth_above
} else {
0
}
>= condition.anchor.get_y(context.min_y as i16, context.height)
+ context.run_depth * condition.surface_depth_multiplier
} }
impl AboveYMaterialCondition { pub fn test_not_material(
pub const fn test(&self, context: &MaterialRuleContext) -> bool { condition: &NotMaterialCondition,
context.block_pos_y chunk: &mut ProtoChunk,
+ if self.add_stone_depth { context: &mut MaterialRuleContext,
context.stone_depth_above surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
} else { ) -> bool {
0 !test_condition(
} condition.invert,
>= self.anchor.get_y(context.min_y as i16, context.height) chunk,
+ context.run_depth * self.surface_depth_multiplier context,
} surface_height_estimate_sampler,
} )
#[derive(Deserialize)]
pub struct NotMaterialCondition {
invert: Box<MaterialCondition>,
}
impl NotMaterialCondition {
pub fn test(
&self,
chunk: &mut ProtoChunk,
context: &mut MaterialRuleContext,
surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
) -> bool {
!self
.invert
.test(chunk, context, surface_height_estimate_sampler)
}
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -324,137 +292,95 @@ pub fn estimate_surface_height(
} }
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct BiomeMaterialCondition { pub struct BiomeMaterialCondition;
biome_is: Box<[&'static Biome]>,
}
impl BiomeMaterialCondition { impl BiomeMaterialCondition {
pub fn test(&self, context: &MaterialRuleContext) -> bool { pub fn test(biome_is: &[&'static Biome], context: &MaterialRuleContext) -> bool {
self.biome_is.contains(&context.biome) biome_is.contains(&context.biome)
} }
} }
#[derive(Deserialize)] pub fn test_noise_threshold(
pub struct NoiseThresholdMaterialCondition { condition: &NoiseThresholdMaterialCondition,
noise: String, context: &mut MaterialRuleContext,
min_threshold: f64, ) -> bool {
max_threshold: f64, // TODO: we want to cache these
let sampler = context
.noise_builder
.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 >= condition.min_threshold && value <= condition.max_threshold
} }
impl NoiseThresholdMaterialCondition { pub fn test_stone_depth(
pub fn test(&self, context: &mut MaterialRuleContext) -> bool { condition: &StoneDepthMaterialCondition,
// TODO: we want to cache these context: &mut MaterialRuleContext,
let sampler = context ) -> bool {
.noise_builder let stone_depth = match &condition.surface_type {
.get_noise_sampler_for_id(self.noise.strip_prefix("minecraft:").unwrap()); VerticalSurfaceType::Ceiling => context.stone_depth_below,
let value = sampler.sample(context.block_pos_x as f64, 0.0, context.block_pos_z as f64); VerticalSurfaceType::Floor => context.stone_depth_above,
value >= self.min_threshold && value <= self.max_threshold };
} let depth = if condition.add_surface_depth {
context.run_depth
} else {
0
};
let depth_range = if condition.secondary_depth_range == 0 {
0
} else {
pumpkin_util::math::map(
context.get_secondary_depth(),
-1.0,
1.0,
0.0,
condition.secondary_depth_range as f64,
) as i32
};
stone_depth <= 1 + condition.offset + depth + depth_range
} }
#[derive(Deserialize)] pub const fn test_water_material(
pub struct StoneDepthMaterialCondition { condition: &WaterMaterialCondition,
offset: i32, context: &MaterialRuleContext,
add_surface_depth: bool, ) -> bool {
secondary_depth_range: i32, context.fluid_height == i32::MIN
surface_type: VerticalSurfaceType, || context.block_pos_y
} + (if condition.add_stone_depth {
context.stone_depth_above
impl StoneDepthMaterialCondition { } else {
pub fn test(&self, context: &mut MaterialRuleContext) -> bool { 0
let stone_depth = match &self.surface_type {
VerticalSurfaceType::Ceiling => context.stone_depth_below,
VerticalSurfaceType::Floor => context.stone_depth_above,
};
let depth = if self.add_surface_depth {
context.run_depth
} else {
0
};
let depth_range = if self.secondary_depth_range == 0 {
0
} else {
pumpkin_util::math::map(
context.get_secondary_depth(),
-1.0,
1.0,
0.0,
self.secondary_depth_range as f64,
) as i32
};
stone_depth <= 1 + self.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 {
context.fluid_height == i32::MIN
|| context.block_pos_y
+ (if self.add_stone_depth {
context.stone_depth_above
} else {
0
})
>= context.fluid_height
+ self.offset
+ context.run_depth * self.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>>>,
}
impl VerticalGradientMaterialCondition {
pub fn test(&self, context: &MaterialRuleContext) -> bool {
let true_at = self
.true_at_and_below
.get_y(context.min_y as i16, context.height);
let false_at = self
.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
.random_deriver
.get_or(|| {
let cache_size = NonZeroUsize::new(128).unwrap();
let cache = LruCache::new(cache_size);
RefCell::new(cache)
}) })
.borrow_mut(); >= context.fluid_height
+ condition.offset
let splitter = cache.get_or_insert(key, || { + context.run_depth * condition.surface_depth_multiplier
context }
.random_deriver
.split_string(&self.random_name) // random_deriver: ThreadLocal<RefCell<LruCache<usize, RandomDeriver>>>,
.next_splitter()
}); pub fn test_vertical_gradient(
condition: &VerticalGradientMaterialCondition,
let block_y = context.block_pos_y; context: &MaterialRuleContext,
if block_y <= true_at { ) -> bool {
return true; let true_at = condition
} .true_at_and_below
if block_y >= false_at { .get_y(context.min_y as i16, context.height);
return false; let false_at = condition
} .false_at_and_above
let mapped = .get_y(context.min_y as i16, context.height);
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); let splitter = context
random.next_f32() < mapped .random_deriver
} .split_string(condition.random_name)
.next_splitter();
let block_y = context.block_pos_y;
if block_y <= true_at {
return true;
}
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 mut random = splitter.split_pos(context.block_pos_x, block_y, context.block_pos_z);
random.next_f32() < mapped
} }

View File

@ -1,42 +1,37 @@
use pumpkin_data::BlockState; use pumpkin_data::{
BlockState,
chunk_gen_settings::{
BlockBlueprint, ConditionMaterialRule, MaterialRule, SequenceMaterialRule,
},
};
use serde::Deserialize; use serde::Deserialize;
use super::{MaterialCondition, MaterialRuleContext}; use super::MaterialRuleContext;
use crate::{ use crate::{
ProtoChunk, block::BlockStateCodec, ProtoChunk,
generation::noise::router::surface_height_sampler::SurfaceHeightEstimateSampler, block::to_state_from_blueprint,
generation::{
noise::router::surface_height_sampler::SurfaceHeightEstimateSampler,
surface::test_condition,
},
}; };
#[derive(Deserialize)] pub fn try_apply_material_rule(
#[serde(tag = "type")] rule: &MaterialRule,
pub enum MaterialRule { chunk: &mut ProtoChunk,
#[serde(rename = "minecraft:bandlands")] context: &mut MaterialRuleContext,
Badlands(BadLandsMaterialRule), surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
#[serde(rename = "minecraft:block")] ) -> Option<&'static BlockState> {
Block(BlockMaterialRule), match rule {
#[serde(rename = "minecraft:sequence")] MaterialRule::Badlands(_badlands) => Some(BadLandsMaterialRule::try_apply(context)),
Sequence(SequenceMaterialRule), MaterialRule::Block(block) => Some(BlockMaterialRule::try_apply(&block.result_state)),
#[serde(rename = "minecraft:condition")] MaterialRule::Sequence(sequence) => {
Condition(ConditionMaterialRule), try_apply_sequence(sequence, chunk, context, surface_height_estimate_sampler)
}
impl MaterialRule {
pub fn try_apply(
&self,
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)
}
} }
MaterialRule::Condition(condition) => {
try_apply_condition(condition, chunk, context, surface_height_estimate_sampler)
}
MaterialRule::Unsupported => todo!(),
} }
} }
@ -54,58 +49,48 @@ impl BadLandsMaterialRule {
} }
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct BlockMaterialRule { pub struct BlockMaterialRule;
result_state: BlockStateCodec,
}
impl BlockMaterialRule { impl BlockMaterialRule {
pub fn try_apply(&self) -> &'static BlockState { pub fn try_apply(blueprint: &BlockBlueprint) -> &'static BlockState {
self.result_state.get_state() to_state_from_blueprint(blueprint)
} }
} }
#[derive(Deserialize)] pub fn try_apply_sequence(
pub struct SequenceMaterialRule { rule: &SequenceMaterialRule,
sequence: Vec<MaterialRule>, chunk: &mut ProtoChunk,
} context: &mut MaterialRuleContext,
surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
impl SequenceMaterialRule { ) -> Option<&'static BlockState> {
pub fn try_apply( for seq in rule.sequence {
&self, if let Some(state) =
chunk: &mut ProtoChunk, try_apply_material_rule(seq, chunk, context, surface_height_estimate_sampler)
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) {
return Some(state);
}
}
None
}
}
#[derive(Deserialize)]
pub struct ConditionMaterialRule {
if_true: MaterialCondition,
then_run: Box<MaterialRule>,
}
impl ConditionMaterialRule {
pub fn try_apply(
&self,
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 return Some(state);
.then_run
.try_apply(chunk, context, surface_height_estimate_sampler);
} }
None
} }
None
}
pub fn try_apply_condition(
rule: &ConditionMaterialRule,
chunk: &mut ProtoChunk,
context: &mut MaterialRuleContext,
surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
) -> Option<&'static BlockState> {
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
} }

View File

@ -153,7 +153,7 @@ impl SurfaceTerrainBuilder {
for y in (chunk.bottom_y() as i32..=elevation_y).rev() { for y in (chunk.bottom_y() as i32..=elevation_y).rev() {
let pos = Vector3::new(global_x, y, global_z); let pos = Vector3::new(global_x, y, global_z);
let block_id = chunk.get_block_state(&pos).to_block_id(); 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; break;
} }

View File

@ -22,14 +22,13 @@ use pumpkin_data::dimension::Dimension;
use pumpkin_data::{Block, block_properties::has_random_ticks, fluid::Fluid}; use pumpkin_data::{Block, block_properties::has_random_ticks, fluid::Fluid};
use pumpkin_util::math::{position::BlockPos, vector2::Vector2}; use pumpkin_util::math::{position::BlockPos, vector2::Vector2};
use pumpkin_util::world_seed::Seed; use pumpkin_util::world_seed::Seed;
use rand::{RngExt, SeedableRng, rngs::SmallRng}; use rustc_hash::FxHashMap;
use std::sync::Mutex; use std::sync::Mutex;
use std::time::Duration; use std::time::Duration;
use tokio::time::timeout; use tokio::time::timeout;
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
// use std::time::Duration; // use std::time::Duration;
use std::{ use std::{
collections::HashMap,
path::PathBuf, path::PathBuf,
sync::{ sync::{
Arc, Arc,
@ -232,7 +231,7 @@ impl Level {
let arc_chunk = Arc::new(RwLock::new(ChunkEntityData { let arc_chunk = Arc::new(RwLock::new(ChunkEntityData {
x: pos.x, x: pos.x,
z: pos.y, z: pos.y,
data: HashMap::new(), data: FxHashMap::default(),
dirty: true, dirty: true,
})); }));
@ -341,11 +340,6 @@ impl Level {
self.loaded_chunks.len() 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) { pub fn list_cached(&self) {
for entry in self.loaded_chunks.iter() { for entry in self.loaded_chunks.iter() {
log::debug!("In map: {:?}", entry.key()); log::debug!("In map: {:?}", entry.key());
@ -404,51 +398,37 @@ impl Level {
!self.mark_chunks_as_not_watched(&[chunk]).await.is_empty() !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>]) { pub fn clean_entity_chunks(self: &Arc<Self>, chunks: &[Vector2<i32>]) {
// Care needs to be take here because of interweaving case: let chunks_to_process: Vec<_> = chunks
// 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
.iter() .iter()
.filter_map(|pos| { .filter_map(|pos| {
// Only chunks that have no entry in the watcher map or have 0 watchers // Only include chunks with no watchers
if self let has_watchers = self
.chunk_watchers .chunk_watchers
.get(pos) .get(pos)
.is_none_or(|count| count.is_zero()) .is_some_and(|count| !count.is_zero());
{
self.loaded_entity_chunks if has_watchers {
.get(pos) return None;
.map(|chunk| (*pos, chunk.value().clone()))
} else {
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(); let level = self.clone();
self.spawn_task(async move { self.spawn_task(async move {
let chunks_to_remove = chunks_with_no_watchers.clone(); log::debug!("Writing {} entity chunks to disk", chunks_to_process.len());
level.write_entity_chunks(chunks_with_no_watchers).await; level.write_entity_chunks(chunks_to_process).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())
});
}
}); });
} }
// Gets random ticks, block ticks and fluid ticks
pub async fn get_tick_data(&self) -> TickData { pub async fn get_tick_data(&self) -> TickData {
let mut ticks = TickData { let mut ticks = TickData {
block_ticks: Vec::new(), block_ticks: Vec::new(),
@ -457,75 +437,52 @@ impl Level {
block_entities: Vec::new(), 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() { for chunk_sync in self.loaded_chunks.iter() {
// Try to get a READ lock first. let mut chunk = chunk_sync.write().await;
// Most chunks won't have pending ticks, so this is very fast.
let chunk = chunk_sync.read().await;
let chunk_x_base = chunk.x * 16; let chunk_x_base = chunk.x * 16;
let chunk_z_base = chunk.z * 16; let chunk_z_base = chunk.z * 16;
let section_count = chunk.section.sections.len();
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: (),
})
}),
);
}
ticks ticks
.block_entities .block_entities
.extend(chunk.block_entities.values().cloned()); .extend(chunk.block_entities.values().cloned());
drop(chunk); for i in 0..section_count {
let mut chunk_write = chunk_sync.write().await; let y_base = i as i32 * 16;
ticks for _ in 0..3 {
.block_ticks let x_offset = (r & 0xF) as usize;
.append(&mut chunk_write.block_ticks.step_tick()); let z_offset = (r >> 8 & 0xF) as usize;
ticks let y_in_section = ((r >> 4) & 0xF) as i32;
.fluid_ticks let absolute_y = y_base + y_in_section;
.append(&mut chunk_write.fluid_ticks.step_tick());
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.block_ticks.sort_unstable();
ticks.fluid_ticks.sort_unstable(); ticks.fluid_ticks.sort_unstable();
ticks ticks
} }
@ -573,12 +530,20 @@ impl Level {
lock.send_change(); 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() chunk.clone()
} else { } else {
recv.await recv.await
.expect("Chunk listener dropped without sending chunk") .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( async fn load_single_entity_chunk(

View File

@ -1,5 +1,6 @@
use generation::settings::GenerationSettings; use pumpkin_data::{
use pumpkin_data::{BlockState, dimension::Dimension}; Block, BlockState, chunk_gen_settings::GenerationSettings, dimension::Dimension,
};
use pumpkin_util::math::vector2::Vector2; use pumpkin_util::math::vector2::Vector2;
pub mod biome; pub mod biome;
@ -45,7 +46,7 @@ macro_rules! global_path {
// TODO: is there a way to do in-file benches? // TODO: is there a way to do in-file benches?
pub use generation::{ pub use generation::{
GlobalRandomConfig, noise::router::proto_noise_router::ProtoNoiseRouters, GlobalRandomConfig, noise::router::proto_noise_router::ProtoNoiseRouters,
proto_chunk::ProtoChunk, settings::GENERATION_SETTINGS, settings::GeneratorSetting, proto_chunk::ProtoChunk,
}; };
use crate::generation::{ use crate::generation::{
@ -78,7 +79,10 @@ pub fn bench_create_and_populate_noise(
let generation_shape = &settings.shape; let generation_shape = &settings.shape;
let horizontal_cell_count = CHUNK_DIM / generation_shape.horizontal_cell_block_count(); let horizontal_cell_count = CHUNK_DIM / generation_shape.horizontal_cell_block_count();
let sampler = FluidLevelSampler::Chunk(StandardChunkFluidLevelSampler::new( 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), FluidLevel::new(-54, &pumpkin_data::Block::LAVA),
)); ));
@ -205,7 +209,10 @@ pub fn bench_create_and_populate_noise_with_surface(
// Noise sampler // Noise sampler
let sampler = FluidLevelSampler::Chunk(StandardChunkFluidLevelSampler::new( 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), FluidLevel::new(-54, &pumpkin_data::Block::LAVA),
)); ));

View File

@ -121,7 +121,7 @@ where
nbt.put_int("z", self.position.0.z); nbt.put_int("z", self.position.0.z);
nbt.put_int("t", self.delay as i32); nbt.put_int("t", self.delay as i32);
nbt.put_int("p", self.priority 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) nbt.serialize(serializer)
} }
} }

View File

@ -1,13 +1,12 @@
use std::collections::HashSet;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use rustc_hash::FxHashSet;
use crate::tick::{MAX_TICK_DELAY, OrderedTick, ScheduledTick}; use crate::tick::{MAX_TICK_DELAY, OrderedTick, ScheduledTick};
#[derive(Clone)] #[derive(Clone)]
pub struct ChunkTickScheduler<T> { pub struct ChunkTickScheduler<T> {
tick_queue: [Vec<OrderedTick<T>>; MAX_TICK_DELAY], tick_queue: [Vec<OrderedTick<T>>; MAX_TICK_DELAY],
queued_ticks: HashSet<(BlockPos, T)>, queued_ticks: FxHashSet<(BlockPos, T)>,
offset: usize, offset: usize,
} }
@ -79,7 +78,7 @@ impl<T> Default for ChunkTickScheduler<T> {
fn default() -> Self { fn default() -> Self {
Self { Self {
tick_queue: std::array::from_fn(|_| Vec::new()), tick_queue: std::array::from_fn(|_| Vec::new()),
queued_ticks: HashSet::new(), queued_ticks: FxHashSet::default(),
offset: 0, offset: 0,
} }
} }

View File

@ -1,5 +1,5 @@
use std::{ use std::{
fs::OpenOptions, fs::File,
io::{Cursor, Read}, io::{Cursor, Read},
path::Path, path::Path,
time::{SystemTime, UNIX_EPOCH}, time::{SystemTime, UNIX_EPOCH},
@ -82,10 +82,9 @@ impl WorldInfoReader for AnvilLevelInfo {
fn read_world_info(&self, level_folder: &Path) -> Result<LevelData, WorldInfoError> { fn read_world_info(&self, level_folder: &Path) -> Result<LevelData, WorldInfoError> {
let path = level_folder.join(LEVEL_DAT_FILE_NAME); let path = level_folder.join(LEVEL_DAT_FILE_NAME);
let world_info_file = OpenOptions::new().read(true).open(path)?; let world_info_file = File::open(path)?;
let mut compression_reader = GzDecoder::new(world_info_file);
let mut buf = Vec::new(); 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_data_version(&buf)?;
check_file_level_version(&buf)?; check_file_level_version(&buf)?;
@ -114,11 +113,7 @@ impl WorldInfoWriter for AnvilLevelInfo {
// open file // open file
let path = level_folder.join(LEVEL_DAT_FILE_NAME); let path = level_folder.join(LEVEL_DAT_FILE_NAME);
let world_info_file = OpenOptions::new() let world_info_file = File::create(path)?;
.truncate(true)
.create(true)
.write(true)
.open(path)?;
// write compressed data into file // write compressed data into file
let compression_writer = GzEncoder::new(world_info_file, Compression::best()); let compression_writer = GzEncoder::new(world_info_file, Compression::best());

View File

@ -1,4 +1,4 @@
use std::{collections::HashMap, sync::Arc}; use std::sync::Arc;
use pumpkin_data::{ use pumpkin_data::{
Block, BlockDirection, BlockState, FacingExt, Block, BlockDirection, BlockState, FacingExt,
@ -15,6 +15,7 @@ use pumpkin_world::{
world::BlockFlags, world::BlockFlags,
}; };
use rand::RngExt; use rand::RngExt;
use rustc_hash::FxHashMap;
use crate::{ use crate::{
block::{ block::{
@ -377,7 +378,7 @@ async fn move_piston(
return false; 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 moved_blocks: Vec<BlockPos> = handler.moved_blocks;
let mut moved_block_states: Vec<&BlockState> = Vec::new(); let mut moved_block_states: Vec<&BlockState> = Vec::new();

View File

@ -97,7 +97,7 @@ use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
use pumpkin_world::item::ItemStack; use pumpkin_world::item::ItemStack;
use pumpkin_world::world::{BlockAccessor, BlockFlags, BlockRegistryExt}; use pumpkin_world::world::{BlockAccessor, BlockFlags, BlockRegistryExt};
use std::collections::HashMap; use rustc_hash::FxHashMap;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::Mutex; use tokio::sync::Mutex;
@ -310,8 +310,8 @@ impl BlockActionResult {
#[derive(Default)] #[derive(Default)]
pub struct BlockRegistry { pub struct BlockRegistry {
blocks: HashMap<u16, Arc<dyn BlockBehaviour>>, blocks: FxHashMap<u16, Arc<dyn BlockBehaviour>>,
fluids: HashMap<u16, Arc<dyn FluidBehaviour>>, fluids: FxHashMap<u16, Arc<dyn FluidBehaviour>>,
} }
impl BlockRegistryExt for BlockRegistry { impl BlockRegistryExt for BlockRegistry {

View File

@ -30,7 +30,7 @@ impl CommandExecutor for Executor {
[ [
TextComponent::text(players.len().to_string()), TextComponent::text(players.len().to_string()),
TextComponent::text(server.basic_config.max_players.to_string()), TextComponent::text(server.basic_config.max_players.to_string()),
TextComponent::text(get_player_names(players)), TextComponent::text(get_player_names(&players)),
], ],
)) ))
.await; .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(); let mut names = String::new();
for player in players { for player in players {
if !names.is_empty() { if !names.is_empty() {

View File

@ -6,7 +6,6 @@ use crate::command::{
}, },
tree::{CommandTree, builder::argument}, tree::{CommandTree, builder::argument},
}; };
use pumpkin_util::resource_location::ResourceLocation;
use pumpkin_util::text::TextComponent; use pumpkin_util::text::TextComponent;
const NAMES: [&str; 1] = ["stopsound"]; const NAMES: [&str; 1] = ["stopsound"];
@ -37,7 +36,7 @@ impl CommandExecutor for Executor {
sound sound
.as_ref() .as_ref()
.cloned() .cloned()
.map(|s| ResourceLocation::vanilla(s.to_name())) .map(|s| format!("minecraft:{}", s.to_name()))
.ok(), .ok(),
category.as_ref().map(|s| **s).ok(), category.as_ref().map(|s| **s).ok(),
) )

View File

@ -1,5 +1,6 @@
use pumpkin_protocol::java::client::play::CommandSuggestion; use pumpkin_protocol::java::client::play::CommandSuggestion;
use pumpkin_util::text::TextComponent; use pumpkin_util::text::TextComponent;
use rustc_hash::FxHashMap;
use super::args::ConsumedArgs; use super::args::ConsumedArgs;
@ -56,8 +57,8 @@ impl CommandError {
#[derive(Default)] #[derive(Default)]
pub struct CommandDispatcher { pub struct CommandDispatcher {
pub commands: HashMap<String, Command>, pub commands: FxHashMap<String, Command>,
pub permissions: HashMap<String, String>, pub permissions: FxHashMap<String, String>,
} }
/// Stores registered [`CommandTree`]s and dispatches commands to them. /// Stores registered [`CommandTree`]s and dispatches commands to them.

View File

@ -2,7 +2,7 @@ use core::f32;
use std::sync::{ use std::sync::{
Arc, Arc,
atomic::{ atomic::{
AtomicBool, AtomicU32, AtomicBool, AtomicU8, AtomicU32,
Ordering::{self}, 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 // These cannot be atomic values because we mutate their state based on what they are; we run
// into the ABA problem // into the ABA problem
item_stack: Mutex<ItemStack>, item_stack: Mutex<ItemStack>,
pickup_delay: Mutex<u8>, pickup_delay: AtomicU8,
health: AtomicCell<f32>, health: AtomicCell<f32>,
never_despawn: AtomicBool, never_despawn: AtomicBool,
never_pickup: AtomicBool, never_pickup: AtomicBool,
@ -47,7 +47,7 @@ impl ItemEntity {
entity, entity,
item_stack: Mutex::new(item_stack), item_stack: Mutex::new(item_stack),
item_age: AtomicU32::new(0), 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), health: AtomicCell::new(5.0),
never_despawn: AtomicBool::new(false), never_despawn: AtomicBool::new(false),
never_pickup: AtomicBool::new(false), never_pickup: AtomicBool::new(false),
@ -66,7 +66,7 @@ impl ItemEntity {
entity, entity,
item_stack: Mutex::new(item_stack), item_stack: Mutex::new(item_stack),
item_age: AtomicU32::new(0), 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), health: AtomicCell::new(5.0),
never_despawn: AtomicBool::new(false), never_despawn: AtomicBool::new(false),
never_pickup: AtomicBool::new(false), never_pickup: AtomicBool::new(false),
@ -109,8 +109,6 @@ impl ItemEntity {
} }
async fn try_merge_with(&self, other: &Self) { async fn try_merge_with(&self, other: &Self) {
// Check if merge is possible
let self_stack = self.item_stack.lock().await; let self_stack = self.item_stack.lock().await;
let other_stack = other.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); target.never_pickup.store(never_pickup, Ordering::Relaxed);
if !never_pickup { if !never_pickup {
let mut target_delay = target.pickup_delay.lock().await; let source_delay = source.pickup_delay.load(Ordering::Relaxed);
target
let delay = (*target_delay).max(*source.pickup_delay.lock().await); .pickup_delay
.fetch_max(source_delay, Ordering::Relaxed);
*target_delay = delay;
} }
if empty1 { if empty1 {
@ -195,11 +192,11 @@ impl EntityBase for ItemEntity {
) -> EntityBaseFuture<'a, ()> { ) -> EntityBaseFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {
let entity = &self.entity; let entity = &self.entity;
entity.tick(caller.clone(), server).await; self.pickup_delay
{ .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |val| {
let mut delay = self.pickup_delay.lock().await; Some(val.saturating_sub(1))
*delay = delay.saturating_sub(1); })
}; .ok();
let original_velo = entity.velocity.load(); let original_velo = entity.velocity.load();
@ -321,7 +318,7 @@ impl EntityBase for ItemEntity {
let velocity_dirty = entity.velocity_dirty.swap(false, Ordering::SeqCst) let velocity_dirty = entity.velocity_dirty.swap(false, Ordering::SeqCst)
|| entity.touching_water.load(Ordering::SeqCst) || entity.touching_water.load(Ordering::SeqCst)
|| entity.touching_lava.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 { if velocity_dirty {
entity.send_pos_rot().await; 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, ()> { fn on_player_collision<'a>(&'a self, player: &'a Arc<Player>) -> EntityBaseFuture<'a, ()> {
Box::pin(async { Box::pin(async {
let can_pickup = { if self.pickup_delay.load(Ordering::Relaxed) > 0
let delay = self.pickup_delay.lock().await; || player.living_entity.health.load() <= 0.0
*delay == 0 {
}; return;
}
if can_pickup if player
&& player.living_entity.health.load() > 0.0 .inventory
&& (player .insert_stack_anywhere(&mut *self.item_stack.lock().await)
.inventory .await
.insert_stack_anywhere(&mut *self.item_stack.lock().await) || player.is_creative()
.await
|| player.is_creative())
{ {
player player
.client .client
@ -402,7 +398,6 @@ impl EntityBase for ItemEntity {
.await .await
.send_content_updates() .send_content_updates()
.await; .await;
if self.item_stack.lock().await.is_empty() { if self.item_stack.lock().await.is_empty() {
self.entity.remove().await; self.entity.remove().await;
} else { } else {

View File

@ -203,16 +203,15 @@ impl ChunkManager {
{ {
let mut lock = level.chunk_loading.lock().unwrap(); let mut lock = level.chunk_loading.lock().unwrap();
lock.add_ticket( let new_level = ChunkLoading::get_level_from_view_distance(view_distance);
center, lock.add_ticket(center, new_level);
ChunkLoading::get_level_from_view_distance(view_distance),
);
if old_center != center || old_view_distance != view_distance { if old_center != center || old_view_distance != view_distance {
lock.remove_ticket( let old_level = ChunkLoading::get_level_from_view_distance(old_view_distance);
old_center, // Don't remove if it would be the same ticket
ChunkLoading::get_level_from_view_distance(old_view_distance), if old_center != center || old_level != new_level {
); lock.remove_ticket(old_center, old_level);
}
} }
lock.send_change(); lock.send_change();
}; };
@ -604,8 +603,6 @@ impl Player {
level.loaded_chunk_count(), level.loaded_chunk_count(),
); );
level.clean_up_log().await;
//self.world().level.list_cached(); //self.world().level.list_cached();
} }
@ -1743,7 +1740,7 @@ impl Player {
self.client self.client
.send_packet_now(&CRespawn::new( .send_packet_now(&CRespawn::new(
(new_world.dimension.id).into(), (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 biome::hash_seed(new_world.level.seed.0), // seed
self.gamemode.load() as u8, self.gamemode.load() as u8,
self.gamemode.load() as i8, self.gamemode.load() as i8,
@ -2666,7 +2663,7 @@ impl NBTStorage for Player {
nbt.put_string( nbt.put_string(
"Dimension", "Dimension",
ResourceLocation::from(self.world().dimension.minecraft_name).to_string(), self.world().dimension.minecraft_name.to_string(),
); );
}) })
} }

View File

@ -7,14 +7,14 @@ use pumpkin_data::item::Item;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::item::ItemStack; use pumpkin_world::item::ItemStack;
use std::collections::HashMap; use rustc_hash::FxHashMap;
use std::sync::Arc; use std::sync::Arc;
use super::{ItemBehaviour, ItemMetadata}; use super::{ItemBehaviour, ItemMetadata};
#[derive(Default)] #[derive(Default)]
pub struct ItemRegistry { pub struct ItemRegistry {
items: HashMap<u16, Arc<dyn ItemBehaviour>>, items: FxHashMap<u16, Arc<dyn ItemBehaviour>>,
} }
impl ItemRegistry { impl ItemRegistry {

View File

@ -402,7 +402,7 @@ impl BedrockClient {
Self::handle_ack(&Ack::read(reader)?); Self::handle_ack(&Ack::read(reader)?);
} }
RAKNET_NACK => { RAKNET_NACK => {
dbg!("received nack, client is missing packets"); log::debug!("received nack, client is missing packets");
} }
0x80..0x8d => { 0x80..0x8d => {
self.handle_frame_set(server, FrameSet::read(reader)?).await; self.handle_frame_set(server, FrameSet::read(reader)?).await;

View File

@ -22,6 +22,8 @@ use pumpkin_protocol::{
}; };
use pumpkin_util::{Hand, text::TextComponent, version::MinecraftVersion}; use pumpkin_util::{Hand, text::TextComponent, version::MinecraftVersion};
const BRAND_CHANNEL_PREFIX: &str = "minecraft:brand";
impl JavaClient { impl JavaClient {
pub async fn handle_client_information_config( pub async fn handle_client_information_config(
&self, &self,
@ -59,11 +61,7 @@ impl JavaClient {
pub async fn handle_plugin_message(&self, plugin_message: SPluginMessage) { pub async fn handle_plugin_message(&self, plugin_message: SPluginMessage) {
log::debug!("Handling plugin message"); log::debug!("Handling plugin message");
if plugin_message if plugin_message.channel.starts_with(BRAND_CHANNEL_PREFIX) {
.channel
.to_string()
.starts_with("minecraft:brand")
{
log::debug!("Got a client brand"); log::debug!("Got a client brand");
match str::from_utf8(&plugin_message.data) { match str::from_utf8(&plugin_message.data) {
Ok(brand) => *self.brand.lock().await = Some(brand.to_string()), Ok(brand) => *self.brand.lock().await = Some(brand.to_string()),

View File

@ -57,7 +57,6 @@ pub mod config;
pub mod handshake; pub mod handshake;
pub mod login; pub mod login;
pub mod play; pub mod play;
pub mod remapper;
pub mod status; pub mod status;
use crate::entity::player::Player; use crate::entity::player::Player;

View File

@ -1 +0,0 @@
pub mod registry;

View File

@ -1 +0,0 @@

View File

@ -9,14 +9,15 @@ use pumpkin_protocol::{
java::client::{config::CPluginMessage, status::CStatusResponse}, java::client::{config::CPluginMessage, status::CStatusResponse},
}; };
use pumpkin_world::CURRENT_MC_VERSION; 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"); 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>> { fn load_icon_from_file<P: AsRef<Path>>(path: P) -> Result<String, Box<dyn error::Error>> {
let mut icon_file = File::open(path)?; let buf = fs::read(path)?;
let mut buf = Vec::new();
icon_file.read_to_end(&mut buf)?;
Ok(load_icon_from_bytes(&buf)) Ok(load_icon_from_bytes(&buf))
} }

View File

@ -38,10 +38,8 @@ impl KeyStore {
} }
fn generate_private_key() -> RsaPrivateKey { fn generate_private_key() -> RsaPrivateKey {
// Found out that OsRng is faster than rand::thread_rng here
let mut rng = rand::rng(); let mut rng = rand::rng();
// let pub_key = RsaPublicKey::from(&priv_key);
RsaPrivateKey::new(&mut rng, 1024).expect("Failed to generate a key") RsaPrivateKey::new(&mut rng, 1024).expect("Failed to generate a key")
} }

View File

@ -585,6 +585,20 @@ impl Server {
players 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. /// 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>> { pub fn get_random_player(&self) -> Option<Arc<Player>> {
let players = self.get_all_players(); let players = self.get_all_players();

View File

@ -1,8 +1,8 @@
use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use pumpkin_data::{Block, BlockState}; use pumpkin_data::{Block, BlockState};
use pumpkin_util::math::{position::BlockPos, vector3::Vector3}; use pumpkin_util::math::{position::BlockPos, vector3::Vector3};
use rustc_hash::FxHashMap;
use crate::{ use crate::{
block::{ExplodeArgs, drop_loot}, block::{ExplodeArgs, drop_loot},
@ -23,13 +23,14 @@ impl Explosion {
async fn get_blocks_to_destroy( async fn get_blocks_to_destroy(
&self, &self,
world: &World, world: &World,
) -> HashMap<BlockPos, (&'static Block, &'static BlockState)> { ) -> FxHashMap<BlockPos, (&'static Block, &'static BlockState)> {
// The hashmap will prevent position duplicates. // Somethings are not vanilla here but make it way faster
let mut map = HashMap::new(); let mut map = FxHashMap::default();
let random_val = rand::random::<f32>();
for x in 0..16 { for x in 0..16 {
for y in 0..16 { for y in 0..16 {
'block2: for z 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; continue;
} }
@ -46,7 +47,7 @@ impl Explosion {
let mut pos_y = self.pos.y + 0.0625; let mut pos_y = self.pos.y + 0.0625;
let mut pos_z = self.pos.z; 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 { while h > 0.0 {
let block_pos = BlockPos::floored(pos_x, pos_y, pos_z); let block_pos = BlockPos::floored(pos_x, pos_y, pos_z);
let (block, state) = world.get_block_and_state(&block_pos).await; let (block, state) = world.get_block_and_state(&block_pos).await;
@ -61,10 +62,9 @@ impl Explosion {
let resistance = let resistance =
fluid_state.blast_resistance.max(block.blast_resistance); fluid_state.blast_resistance.max(block.blast_resistance);
h -= resistance * 0.3; h -= resistance * 0.3;
} if h > 0.0 {
map.insert(block_pos, (block, state));
if h > 0.0 { }
map.insert(block_pos, (block, state));
} }
pos_x += x * 0.3; pos_x += x * 0.3;
pos_y += y * 0.3; pos_y += y * 0.3;
@ -82,13 +82,10 @@ impl Explosion {
let blocks = self.get_blocks_to_destroy(world).await; let blocks = self.get_blocks_to_destroy(world).await;
// TODO: Entity damage, fire // TODO: Entity damage, fire
for (pos, (block, state)) in &blocks { 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; 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()) { if pumpkin_block.is_none_or(|s| s.should_drop_items_on_explosion()) {
let params = LootContextParameters { let params = LootContextParameters {
block_state: Some(state), block_state: Some(state),

View File

@ -35,6 +35,7 @@ use crossbeam::queue::SegQueue;
use explosion::Explosion; use explosion::Explosion;
use pumpkin_config::BasicConfiguration; use pumpkin_config::BasicConfiguration;
use pumpkin_data::block_properties::is_air; use pumpkin_data::block_properties::is_air;
use pumpkin_data::chunk_gen_settings::GenerationSettings;
use pumpkin_data::data_component_impl::EquipmentSlot; use pumpkin_data::data_component_impl::EquipmentSlot;
use pumpkin_data::dimension::Dimension; use pumpkin_data::dimension::Dimension;
use pumpkin_data::entity::MobCategory; use pumpkin_data::entity::MobCategory;
@ -135,7 +136,6 @@ pub mod weather;
use crate::world::natural_spawner::{SpawnState, spawn_for_chunk}; use crate::world::natural_spawner::{SpawnState, spawn_for_chunk};
use pumpkin_data::effect::StatusEffect; use pumpkin_data::effect::StatusEffect;
use pumpkin_world::chunk::ChunkHeightmapType::MotionBlocking; use pumpkin_world::chunk::ChunkHeightmapType::MotionBlocking;
use pumpkin_world::generation::settings::gen_settings_from_dimension;
use uuid::Uuid; use uuid::Uuid;
use weather::Weather; use weather::Weather;
@ -217,7 +217,7 @@ impl World {
server: Weak<Server>, server: Weak<Server>,
) -> Self { ) -> Self {
// TODO // 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) // 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); 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 = 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 // TODO gamerule this.spawnEnemies || this.spawnFriendlies
let spawn_passives = self.level_time.lock().await.time_of_day % 400 == 0; 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()); // 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()); spawning_chunks.shuffle(&mut rng());
// TODO i think it can be multithread // TODO i think it can be multithread
@ -794,9 +792,10 @@ impl World {
.await; .await;
} }
let world: Arc<dyn SimpleWorld> = self.clone();
for block_entity in tick_data.block_entities { for block_entity in tick_data.block_entities {
let world: Arc<dyn SimpleWorld> = self.clone(); block_entity.tick(&world).await;
block_entity.tick(world).await;
} }
} }
@ -3001,13 +3000,16 @@ impl World {
pub async fn drop_stack(self: &Arc<Self>, pos: &BlockPos, stack: ItemStack) { pub async fn drop_stack(self: &Arc<Self>, pos: &BlockPos, stack: ItemStack) {
let height = EntityType::ITEM.dimension[1] / 2.0; let height = EntityType::ITEM.dimension[1] / 2.0;
let pos = Vector3::new( let spawn_pos = {
f64::from(pos.0.x) + 0.5 + rand::rng().random_range(-0.25..0.25), let mut r = rand::rng();
f64::from(pos.0.y) + 0.5 + rand::rng().random_range(-0.25..0.25) - f64::from(height), Vector3::new(
f64::from(pos.0.z) + 0.5 + rand::rng().random_range(-0.25..0.25), 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); let item_entity = Arc::new(ItemEntity::new(entity, stack).await);
self.spawn_entity(item_entity).await; self.spawn_entity(item_entity).await;
} }