diff --git a/Cargo.toml b/Cargo.toml index 3ca5752bd..a5b0c432b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,4 +60,4 @@ serde_json = "1.0" async-trait = "0.1" -tempfile = "3.17.1" \ No newline at end of file +tempfile = "3.19.1" diff --git a/pumpkin-protocol/Cargo.toml b/pumpkin-protocol/Cargo.toml index e3e843b7e..cb0881055 100644 --- a/pumpkin-protocol/Cargo.toml +++ b/pumpkin-protocol/Cargo.toml @@ -29,11 +29,11 @@ aes = "0.8" cfb8 = "0.8" # compression -async-compression = { version = "0.4.20", features = ["tokio", "zlib"] } +async-compression = { version = "0.4.22", features = ["tokio", "zlib"] } take_mut = "0.2.2" bitflags = "2.9.0" [dev-dependencies] # Validate correctness -flate2 = "1.1.0" +flate2 = "1.1.1" diff --git a/pumpkin-protocol/src/client/play/chunk_data.rs b/pumpkin-protocol/src/client/play/chunk_data.rs index 60b38e0e6..f8b1d4506 100644 --- a/pumpkin-protocol/src/client/play/chunk_data.rs +++ b/pumpkin-protocol/src/client/play/chunk_data.rs @@ -8,10 +8,7 @@ use crate::{ use pumpkin_data::packet::clientbound::PLAY_LEVEL_CHUNK_WITH_LIGHT; use pumpkin_macros::packet; -use pumpkin_world::chunk::{ - ChunkData, - palette::{BlockPalette, NetworkPalette}, -}; +use pumpkin_world::chunk::{ChunkData, palette::NetworkPalette}; #[packet(PLAY_LEVEL_CHUNK_WITH_LIGHT)] pub struct CChunkData<'a>(pub &'a ChunkData); @@ -45,21 +42,40 @@ impl ClientPacket for CChunkData<'_> { } let mut data_buf = Vec::new(); - let mut light_buf = Vec::new(); - for section in self.0.section.sections.iter() { - // 2 blocks per byte for block lights - let chunk_light_len = BlockPalette::VOLUME / 2; - // TODO: Implement, currently default to full bright - let chunk_light = vec![0xFFu8; chunk_light_len]; + let mut sky_light_buf = Vec::new(); + let mut sky_light_empty_mask = 0; + let mut sky_light_mask = 0; + let mut block_light_buf = Vec::new(); + let mut block_light_empty_mask = 0; + let mut block_light_mask = 0; - light_buf.write_var_int(&chunk_light_len.try_into().map_err(|_| { - WritingError::Message(format!( - "{} is not representable as a VarInt!", - chunk_light_len - )) - })?)?; - light_buf.write_slice(&chunk_light)?; + for (i, section) in self.0.section.sections.iter().enumerate() { + // Write sky light + if let Some(sky_light) = §ion.sky_light { + let mut buf = Vec::new(); + buf.write_var_int(&sky_light.len().try_into().map_err(|_| { + WritingError::Message("sky_light not representable as a VarInt!".to_string()) + })?)?; + buf.write_slice(sky_light)?; + sky_light_buf.push(buf); + sky_light_mask |= 1 << i; + } else { + sky_light_empty_mask |= 1 << i; + } + + // Write block light + if let Some(block_light) = §ion.block_light { + let mut buf = Vec::new(); + buf.write_var_int(&block_light.len().try_into().map_err(|_| { + WritingError::Message("block_light not representable as a VarInt!".to_string()) + })?)?; + buf.write_slice(block_light)?; + block_light_buf.push(buf); + block_light_mask |= 1 << i; + } else { + block_light_empty_mask |= 1 << i; + } // Block count let non_empty_block_count = section.block_states.non_air_block_count() as i16; @@ -131,27 +147,29 @@ impl ClientPacket for CChunkData<'_> { // TODO: block entities write.write_var_int(&VarInt(0))?; - // Sky Light Mask // All of the chunks, this is not optimal and uses way more data than needed but will be // overhauled with a full lighting system. - write.write_bitset(&BitSet(Box::new([0b01111111111111111111111110])))?; + + // Sky Light Mask + write.write_bitset(&BitSet(Box::new([sky_light_mask])))?; // Block Light Mask - write.write_bitset(&BitSet(Box::new([0])))?; + write.write_bitset(&BitSet(Box::new([block_light_mask])))?; // Empty Sky Light Mask - write.write_bitset(&BitSet(Box::new([0])))?; + write.write_bitset(&BitSet(Box::new([sky_light_empty_mask])))?; // Empty Block Light Mask - write.write_bitset(&BitSet(Box::new([0])))?; + write.write_bitset(&BitSet(Box::new([block_light_empty_mask])))?; // Sky light - write.write_var_int(&self.0.section.sections.len().try_into().map_err(|_| { - WritingError::Message(format!( - "{} is not representable as a VarInt!", - self.0.section.sections.len() - )) - })?)?; - write.write_slice(&light_buf)?; + write.write_var_int(&VarInt(sky_light_buf.len() as i32))?; + for sky_buf in sky_light_buf { + write.write_slice(&sky_buf)?; + } - // Block Lighting - write.write_var_int(&VarInt(0)) + // Block Light + write.write_var_int(&VarInt(block_light_buf.len() as i32))?; + for block_buf in block_light_buf { + write.write_slice(&block_buf)?; + } + Ok(()) } } diff --git a/pumpkin-registry/Cargo.toml b/pumpkin-registry/Cargo.toml index 2b240da7b..93b7f9aa7 100644 --- a/pumpkin-registry/Cargo.toml +++ b/pumpkin-registry/Cargo.toml @@ -9,7 +9,7 @@ pumpkin-protocol = { path = "../pumpkin-protocol" } pumpkin-nbt = { path = "../pumpkin-nbt" } pumpkin-util = { path = "../pumpkin-util" } -indexmap = { version = "2.8", features = ["serde"] } +indexmap = { version = "2.9", features = ["serde"] } serde.workspace = true serde_json.workspace = true diff --git a/pumpkin-world/Cargo.toml b/pumpkin-world/Cargo.toml index a6b7cf6b9..a13d9e47b 100644 --- a/pumpkin-world/Cargo.toml +++ b/pumpkin-world/Cargo.toml @@ -40,7 +40,7 @@ zstd = "0.13.3" itertools = "0.14.0" file-guard = "0.2" -indexmap = "2.8" +indexmap = "2.9" enum_dispatch = "0.3" noise = "0.9" derive-getters = "0.5.0" @@ -54,7 +54,7 @@ tokio-util = { version = "0.7.14", features = ["rt"] } criterion = { version = "0.5", features = ["html_reports", "async_tokio"] } temp-dir = "0.1.14" # Print log info inside tests when needed -env_logger = "0.11.7" +env_logger = "0.11.8" # Allows us to modify the config pumpkin-config = { path = "../pumpkin-config", features = ["test_helper"] } # Deserialize NaN and Inf diff --git a/pumpkin-world/src/chunk/format/anvil.rs b/pumpkin-world/src/chunk/format/anvil.rs index 8834799bc..33c910441 100644 --- a/pumpkin-world/src/chunk/format/anvil.rs +++ b/pumpkin-world/src/chunk/format/anvil.rs @@ -827,6 +827,8 @@ pub fn chunk_to_bytes(chunk_data: &ChunkData) -> Result, ChunkSerializin y: i as i8 + section_coords::block_to_section(chunk_data.section.min_y) as i8, block_states, biomes, + block_light: section.block_light.clone(), // :c + sky_light: section.sky_light.clone(), // :c }); } diff --git a/pumpkin-world/src/chunk/format/mod.rs b/pumpkin-world/src/chunk/format/mod.rs index 6243c4264..509ee1c9b 100644 --- a/pumpkin-world/src/chunk/format/mod.rs +++ b/pumpkin-world/src/chunk/format/mod.rs @@ -54,6 +54,8 @@ impl ChunkData { .map(|section| SubChunk { block_states: BlockPalette::from_disk_nbt(section.block_states), biomes: BiomePalette::from_disk_nbt(section.biomes), + block_light: section.block_light, + sky_light: section.sky_light, }) .collect(); let min_y = section_coords::section_to_block(chunk_data.min_y_section); @@ -101,11 +103,10 @@ impl ChunkData { struct ChunkSectionNBT { block_states: ChunkSectionBlockStates, biomes: ChunkSectionBiomes, - // TODO - // #[serde(rename = "BlockLight", skip_serializing_if = "Option::is_none")] - // block_light: Option>, - // #[serde(rename = "SkyLight", skip_serializing_if = "Option::is_none")] - // sky_light: Option>, + #[serde(rename = "BlockLight", skip_serializing_if = "Option::is_none")] + block_light: Option>, + #[serde(rename = "SkyLight", skip_serializing_if = "Option::is_none")] + sky_light: Option>, #[serde(rename = "Y")] y: i8, } diff --git a/pumpkin-world/src/chunk/mod.rs b/pumpkin-world/src/chunk/mod.rs index c2d3218ce..86aed2adb 100644 --- a/pumpkin-world/src/chunk/mod.rs +++ b/pumpkin-world/src/chunk/mod.rs @@ -129,6 +129,7 @@ pub struct ChunkSections { impl ChunkSections { #[cfg(test)] pub fn dump_blocks(&self) -> Vec { + // TODO: this is not optimal, we could use rust iters let mut dump = Vec::new(); for section in self.sections.iter() { section.block_states.for_each(|raw_id| { @@ -140,6 +141,7 @@ impl ChunkSections { #[cfg(test)] pub fn dump_biomes(&self) -> Vec { + // TODO: this is not optimal, we could use rust iters let mut dump = Vec::new(); for section in self.sections.iter() { section.biomes.for_each(|raw_id| { @@ -154,6 +156,19 @@ impl ChunkSections { pub struct SubChunk { pub block_states: BlockPalette, pub biomes: BiomePalette, + pub block_light: Option>, + pub sky_light: Option>, +} + +impl SubChunk { + pub fn max_light() -> Self { + let chunk_light_len = BlockPalette::VOLUME / 2; + Self { + block_light: Some(vec![0xFFu8; chunk_light_len].into_boxed_slice()), + sky_light: Some(vec![0xFFu8; chunk_light_len].into_boxed_slice()), + ..Default::default() + } + } } #[derive(Deserialize, Serialize, Debug, Clone)] diff --git a/pumpkin-world/src/generation/implementation/mod.rs b/pumpkin-world/src/generation/implementation/mod.rs index 37991dca7..7072d30f2 100644 --- a/pumpkin-world/src/generation/implementation/mod.rs +++ b/pumpkin-world/src/generation/implementation/mod.rs @@ -43,7 +43,7 @@ impl WorldGenerator for VanillaGenerator { .unwrap(); let sub_chunks = generation_settings.shape.height as usize / BlockPalette::SIZE; - let sections = (0..sub_chunks).map(|_| SubChunk::default()).collect(); + let sections = (0..sub_chunks).map(|_| SubChunk::max_light()).collect(); let mut sections = ChunkSections::new(sections, generation_settings.shape.min_y as i32); let mut proto_chunk = ProtoChunk::new(