use std::io::{Read, Write};
use mcproto_codec::{
error::{CodecError, CodecKind, CodecOperation, InvalidEncodingReason},
io::{read_exact_counted, write_all_counted},
varint::{VarIntRead, VarIntWrite},
};
use crate::{BitSet, PrefixedArray, TypeCodec};
pub const LIGHT_ARRAY_LENGTH: usize = 2048;
pub const LIGHT_VALUES_PER_ARRAY: usize = LIGHT_ARRAY_LENGTH * 2;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LightArray(
pub [u8; LIGHT_ARRAY_LENGTH],
);
impl LightArray {
#[must_use]
pub fn light_level(&self, index: usize) -> Option<u8> {
if index >= LIGHT_VALUES_PER_ARRAY {
return None;
}
let byte = self.0[index / 2];
Some(if index % 2 == 0 {
byte & 0x0f
} else {
byte >> 4
})
}
#[must_use]
pub const fn as_bytes(&self) -> &[u8; LIGHT_ARRAY_LENGTH] {
&self.0
}
#[must_use]
pub fn into_bytes(self) -> [u8; LIGHT_ARRAY_LENGTH] {
self.0
}
}
impl Default for LightArray {
fn default() -> Self {
Self([0; LIGHT_ARRAY_LENGTH])
}
}
impl From<[u8; LIGHT_ARRAY_LENGTH]> for LightArray {
fn from(bytes: [u8; LIGHT_ARRAY_LENGTH]) -> Self {
Self(bytes)
}
}
impl TypeCodec for LightArray {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
let prefix_size = writer
.write_varint_with_size(LIGHT_ARRAY_LENGTH as i32)
.map_err(|error| error.with_context(CodecKind::LightArray))?;
write_all_counted(writer, &self.0, CodecKind::LightArray, prefix_size)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let (length, prefix_size) = reader
.read_varint_with_size()
.map_err(|error| error.with_context(CodecKind::LightArray))?;
if length < 0 {
return Err(CodecError::invalid_encoding(
CodecKind::LightArray,
prefix_size,
InvalidEncodingReason::NegativeLength { value: length },
));
}
if length as usize != LIGHT_ARRAY_LENGTH {
return Err(CodecError::invalid_encoding(
CodecKind::LightArray,
prefix_size,
InvalidEncodingReason::ArrayLengthMismatch {
expected: LIGHT_ARRAY_LENGTH,
actual: length as usize,
},
));
}
let mut bytes = [0; LIGHT_ARRAY_LENGTH];
read_exact_counted(reader, &mut bytes, CodecKind::LightArray, prefix_size)?;
Ok(Self(bytes))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct LightData {
pub sky_light_mask: BitSet,
pub block_light_mask: BitSet,
pub empty_sky_light_mask: BitSet,
pub empty_block_light_mask: BitSet,
pub sky_light_arrays: PrefixedArray<LightArray>,
pub block_light_arrays: PrefixedArray<LightArray>,
}
impl LightData {
#[must_use]
pub fn expected_sky_light_array_count(&self) -> usize {
set_bit_count(&self.sky_light_mask)
}
#[must_use]
pub fn expected_block_light_array_count(&self) -> usize {
set_bit_count(&self.block_light_mask)
}
fn validate_array_counts(&self, operation: CodecOperation) -> Result<(), CodecError> {
validate_array_count(
self.expected_sky_light_array_count(),
self.sky_light_arrays.len(),
operation,
)?;
validate_array_count(
self.expected_block_light_array_count(),
self.block_light_arrays.len(),
operation,
)
}
}
impl TypeCodec for LightData {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
self.validate_array_counts(CodecOperation::Write)?;
self.sky_light_mask
.encode(writer)
.map_err(|error| error.with_context(CodecKind::LightData))?;
self.block_light_mask
.encode(writer)
.map_err(|error| error.with_context(CodecKind::LightData))?;
self.empty_sky_light_mask
.encode(writer)
.map_err(|error| error.with_context(CodecKind::LightData))?;
self.empty_block_light_mask
.encode(writer)
.map_err(|error| error.with_context(CodecKind::LightData))?;
self.sky_light_arrays
.encode(writer)
.map_err(|error| error.with_context(CodecKind::LightData))?;
self.block_light_arrays
.encode(writer)
.map_err(|error| error.with_context(CodecKind::LightData))
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let value = Self {
sky_light_mask: decode_field(reader)?,
block_light_mask: decode_field(reader)?,
empty_sky_light_mask: decode_field(reader)?,
empty_block_light_mask: decode_field(reader)?,
sky_light_arrays: decode_field(reader)?,
block_light_arrays: decode_field(reader)?,
};
value.validate_array_counts(CodecOperation::Read)?;
Ok(value)
}
}
fn decode_field<T: TypeCodec>(reader: &mut impl Read) -> Result<T, CodecError> {
T::decode(reader).map_err(|error| error.with_context(CodecKind::LightData))
}
fn set_bit_count(mask: &BitSet) -> usize {
mask.0.iter().map(|word| word.count_ones() as usize).sum()
}
fn validate_array_count(
expected: usize,
actual: usize,
operation: CodecOperation,
) -> Result<(), CodecError> {
if expected == actual {
return Ok(());
}
Err(CodecError::invalid_encoding_for_operation(
CodecKind::LightData,
operation,
0,
InvalidEncodingReason::ArrayLengthMismatch { expected, actual },
))
}