Read and Write light data using Anvil
Now we are reading and writing light data to and from the anvil format and send it in the chunk and light packet
This commit is contained in:
parent
f0636bb924
commit
fbd95cb693
@ -60,4 +60,4 @@ serde_json = "1.0"
|
|||||||
|
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
|
|
||||||
tempfile = "3.17.1"
|
tempfile = "3.19.1"
|
||||||
|
|||||||
@ -29,11 +29,11 @@ aes = "0.8"
|
|||||||
cfb8 = "0.8"
|
cfb8 = "0.8"
|
||||||
|
|
||||||
# compression
|
# compression
|
||||||
async-compression = { version = "0.4.20", features = ["tokio", "zlib"] }
|
async-compression = { version = "0.4.22", features = ["tokio", "zlib"] }
|
||||||
|
|
||||||
take_mut = "0.2.2"
|
take_mut = "0.2.2"
|
||||||
bitflags = "2.9.0"
|
bitflags = "2.9.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
# Validate correctness
|
# Validate correctness
|
||||||
flate2 = "1.1.0"
|
flate2 = "1.1.1"
|
||||||
|
|||||||
@ -8,10 +8,7 @@ use crate::{
|
|||||||
|
|
||||||
use pumpkin_data::packet::clientbound::PLAY_LEVEL_CHUNK_WITH_LIGHT;
|
use pumpkin_data::packet::clientbound::PLAY_LEVEL_CHUNK_WITH_LIGHT;
|
||||||
use pumpkin_macros::packet;
|
use pumpkin_macros::packet;
|
||||||
use pumpkin_world::chunk::{
|
use pumpkin_world::chunk::{ChunkData, palette::NetworkPalette};
|
||||||
ChunkData,
|
|
||||||
palette::{BlockPalette, NetworkPalette},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[packet(PLAY_LEVEL_CHUNK_WITH_LIGHT)]
|
#[packet(PLAY_LEVEL_CHUNK_WITH_LIGHT)]
|
||||||
pub struct CChunkData<'a>(pub &'a ChunkData);
|
pub struct CChunkData<'a>(pub &'a ChunkData);
|
||||||
@ -45,21 +42,40 @@ impl ClientPacket for CChunkData<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut data_buf = Vec::new();
|
let mut data_buf = Vec::new();
|
||||||
let mut light_buf = Vec::new();
|
|
||||||
|
|
||||||
for section in self.0.section.sections.iter() {
|
let mut sky_light_buf = Vec::new();
|
||||||
// 2 blocks per byte for block lights
|
let mut sky_light_empty_mask = 0;
|
||||||
let chunk_light_len = BlockPalette::VOLUME / 2;
|
let mut sky_light_mask = 0;
|
||||||
// TODO: Implement, currently default to full bright
|
let mut block_light_buf = Vec::new();
|
||||||
let chunk_light = vec![0xFFu8; chunk_light_len];
|
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(|_| {
|
for (i, section) in self.0.section.sections.iter().enumerate() {
|
||||||
WritingError::Message(format!(
|
// Write sky light
|
||||||
"{} is not representable as a VarInt!",
|
if let Some(sky_light) = §ion.sky_light {
|
||||||
chunk_light_len
|
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())
|
||||||
light_buf.write_slice(&chunk_light)?;
|
})?)?;
|
||||||
|
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
|
// Block count
|
||||||
let non_empty_block_count = section.block_states.non_air_block_count() as i16;
|
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
|
// TODO: block entities
|
||||||
write.write_var_int(&VarInt(0))?;
|
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
|
// All of the chunks, this is not optimal and uses way more data than needed but will be
|
||||||
// overhauled with a full lighting system.
|
// 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
|
// Block Light Mask
|
||||||
write.write_bitset(&BitSet(Box::new([0])))?;
|
write.write_bitset(&BitSet(Box::new([block_light_mask])))?;
|
||||||
// Empty Sky 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
|
// Empty Block Light Mask
|
||||||
write.write_bitset(&BitSet(Box::new([0])))?;
|
write.write_bitset(&BitSet(Box::new([block_light_empty_mask])))?;
|
||||||
|
|
||||||
// Sky light
|
// Sky light
|
||||||
write.write_var_int(&self.0.section.sections.len().try_into().map_err(|_| {
|
write.write_var_int(&VarInt(sky_light_buf.len() as i32))?;
|
||||||
WritingError::Message(format!(
|
for sky_buf in sky_light_buf {
|
||||||
"{} is not representable as a VarInt!",
|
write.write_slice(&sky_buf)?;
|
||||||
self.0.section.sections.len()
|
}
|
||||||
))
|
|
||||||
})?)?;
|
|
||||||
write.write_slice(&light_buf)?;
|
|
||||||
|
|
||||||
// Block Lighting
|
// Block Light
|
||||||
write.write_var_int(&VarInt(0))
|
write.write_var_int(&VarInt(block_light_buf.len() as i32))?;
|
||||||
|
for block_buf in block_light_buf {
|
||||||
|
write.write_slice(&block_buf)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ pumpkin-protocol = { path = "../pumpkin-protocol" }
|
|||||||
pumpkin-nbt = { path = "../pumpkin-nbt" }
|
pumpkin-nbt = { path = "../pumpkin-nbt" }
|
||||||
pumpkin-util = { path = "../pumpkin-util" }
|
pumpkin-util = { path = "../pumpkin-util" }
|
||||||
|
|
||||||
indexmap = { version = "2.8", features = ["serde"] }
|
indexmap = { version = "2.9", features = ["serde"] }
|
||||||
|
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
|||||||
@ -40,7 +40,7 @@ zstd = "0.13.3"
|
|||||||
|
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
file-guard = "0.2"
|
file-guard = "0.2"
|
||||||
indexmap = "2.8"
|
indexmap = "2.9"
|
||||||
enum_dispatch = "0.3"
|
enum_dispatch = "0.3"
|
||||||
noise = "0.9"
|
noise = "0.9"
|
||||||
derive-getters = "0.5.0"
|
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"] }
|
criterion = { version = "0.5", features = ["html_reports", "async_tokio"] }
|
||||||
temp-dir = "0.1.14"
|
temp-dir = "0.1.14"
|
||||||
# Print log info inside tests when needed
|
# Print log info inside tests when needed
|
||||||
env_logger = "0.11.7"
|
env_logger = "0.11.8"
|
||||||
# Allows us to modify the config
|
# Allows us to modify the config
|
||||||
pumpkin-config = { path = "../pumpkin-config", features = ["test_helper"] }
|
pumpkin-config = { path = "../pumpkin-config", features = ["test_helper"] }
|
||||||
# Deserialize NaN and Inf
|
# Deserialize NaN and Inf
|
||||||
|
|||||||
@ -827,6 +827,8 @@ pub fn chunk_to_bytes(chunk_data: &ChunkData) -> Result<Vec<u8>, ChunkSerializin
|
|||||||
y: i as i8 + section_coords::block_to_section(chunk_data.section.min_y) as i8,
|
y: i as i8 + section_coords::block_to_section(chunk_data.section.min_y) as i8,
|
||||||
block_states,
|
block_states,
|
||||||
biomes,
|
biomes,
|
||||||
|
block_light: section.block_light.clone(), // :c
|
||||||
|
sky_light: section.sky_light.clone(), // :c
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -54,6 +54,8 @@ impl ChunkData {
|
|||||||
.map(|section| SubChunk {
|
.map(|section| SubChunk {
|
||||||
block_states: BlockPalette::from_disk_nbt(section.block_states),
|
block_states: BlockPalette::from_disk_nbt(section.block_states),
|
||||||
biomes: BiomePalette::from_disk_nbt(section.biomes),
|
biomes: BiomePalette::from_disk_nbt(section.biomes),
|
||||||
|
block_light: section.block_light,
|
||||||
|
sky_light: section.sky_light,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let min_y = section_coords::section_to_block(chunk_data.min_y_section);
|
let min_y = section_coords::section_to_block(chunk_data.min_y_section);
|
||||||
@ -101,11 +103,10 @@ impl ChunkData {
|
|||||||
struct ChunkSectionNBT {
|
struct ChunkSectionNBT {
|
||||||
block_states: ChunkSectionBlockStates,
|
block_states: ChunkSectionBlockStates,
|
||||||
biomes: ChunkSectionBiomes,
|
biomes: ChunkSectionBiomes,
|
||||||
// TODO
|
#[serde(rename = "BlockLight", skip_serializing_if = "Option::is_none")]
|
||||||
// #[serde(rename = "BlockLight", skip_serializing_if = "Option::is_none")]
|
block_light: Option<Box<[u8]>>,
|
||||||
// block_light: Option<Box<[u8]>>,
|
#[serde(rename = "SkyLight", skip_serializing_if = "Option::is_none")]
|
||||||
// #[serde(rename = "SkyLight", skip_serializing_if = "Option::is_none")]
|
sky_light: Option<Box<[u8]>>,
|
||||||
// sky_light: Option<Box<[u8]>>,
|
|
||||||
#[serde(rename = "Y")]
|
#[serde(rename = "Y")]
|
||||||
y: i8,
|
y: i8,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -129,6 +129,7 @@ pub struct ChunkSections {
|
|||||||
impl ChunkSections {
|
impl ChunkSections {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn dump_blocks(&self) -> Vec<u16> {
|
pub fn dump_blocks(&self) -> Vec<u16> {
|
||||||
|
// TODO: this is not optimal, we could use rust iters
|
||||||
let mut dump = Vec::new();
|
let mut dump = Vec::new();
|
||||||
for section in self.sections.iter() {
|
for section in self.sections.iter() {
|
||||||
section.block_states.for_each(|raw_id| {
|
section.block_states.for_each(|raw_id| {
|
||||||
@ -140,6 +141,7 @@ impl ChunkSections {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn dump_biomes(&self) -> Vec<u8> {
|
pub fn dump_biomes(&self) -> Vec<u8> {
|
||||||
|
// TODO: this is not optimal, we could use rust iters
|
||||||
let mut dump = Vec::new();
|
let mut dump = Vec::new();
|
||||||
for section in self.sections.iter() {
|
for section in self.sections.iter() {
|
||||||
section.biomes.for_each(|raw_id| {
|
section.biomes.for_each(|raw_id| {
|
||||||
@ -154,6 +156,19 @@ impl ChunkSections {
|
|||||||
pub struct SubChunk {
|
pub struct SubChunk {
|
||||||
pub block_states: BlockPalette,
|
pub block_states: BlockPalette,
|
||||||
pub biomes: BiomePalette,
|
pub biomes: BiomePalette,
|
||||||
|
pub block_light: Option<Box<[u8]>>,
|
||||||
|
pub sky_light: Option<Box<[u8]>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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)]
|
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||||
|
|||||||
@ -43,7 +43,7 @@ impl WorldGenerator for VanillaGenerator {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let sub_chunks = generation_settings.shape.height as usize / BlockPalette::SIZE;
|
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 sections = ChunkSections::new(sections, generation_settings.shape.min_y as i32);
|
||||||
|
|
||||||
let mut proto_chunk = ProtoChunk::new(
|
let mut proto_chunk = ProtoChunk::new(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user