mcproto_types/
lightdata.rs1use std::io::{Read, Write};
4
5use mcproto_codec::{
6 error::{CodecError, CodecKind, CodecOperation, InvalidEncodingReason},
7 io::{read_exact_counted, write_all_counted},
8 varint::{VarIntRead, VarIntWrite},
9};
10
11use crate::{BitSet, PrefixedArray, TypeCodec};
12
13pub const LIGHT_ARRAY_LENGTH: usize = 2048;
15
16pub const LIGHT_VALUES_PER_ARRAY: usize = LIGHT_ARRAY_LENGTH * 2;
18
19#[derive(Debug, Clone, PartialEq, Eq, Hash)]
25pub struct LightArray(
26 pub [u8; LIGHT_ARRAY_LENGTH],
28);
29
30impl LightArray {
31 #[must_use]
33 pub fn light_level(&self, index: usize) -> Option<u8> {
34 if index >= LIGHT_VALUES_PER_ARRAY {
35 return None;
36 }
37 let byte = self.0[index / 2];
38 Some(if index % 2 == 0 {
39 byte & 0x0f
40 } else {
41 byte >> 4
42 })
43 }
44
45 #[must_use]
47 pub const fn as_bytes(&self) -> &[u8; LIGHT_ARRAY_LENGTH] {
48 &self.0
49 }
50
51 #[must_use]
53 pub fn into_bytes(self) -> [u8; LIGHT_ARRAY_LENGTH] {
54 self.0
55 }
56}
57
58impl Default for LightArray {
59 fn default() -> Self {
60 Self([0; LIGHT_ARRAY_LENGTH])
61 }
62}
63
64impl From<[u8; LIGHT_ARRAY_LENGTH]> for LightArray {
65 fn from(bytes: [u8; LIGHT_ARRAY_LENGTH]) -> Self {
66 Self(bytes)
67 }
68}
69
70impl TypeCodec for LightArray {
71 fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
72 let prefix_size = writer
73 .write_varint_with_size(LIGHT_ARRAY_LENGTH as i32)
74 .map_err(|error| error.with_context(CodecKind::LightArray))?;
75 write_all_counted(writer, &self.0, CodecKind::LightArray, prefix_size)
76 }
77
78 fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
79 let (length, prefix_size) = reader
80 .read_varint_with_size()
81 .map_err(|error| error.with_context(CodecKind::LightArray))?;
82 if length < 0 {
83 return Err(CodecError::invalid_encoding(
84 CodecKind::LightArray,
85 prefix_size,
86 InvalidEncodingReason::NegativeLength { value: length },
87 ));
88 }
89 if length as usize != LIGHT_ARRAY_LENGTH {
90 return Err(CodecError::invalid_encoding(
91 CodecKind::LightArray,
92 prefix_size,
93 InvalidEncodingReason::ArrayLengthMismatch {
94 expected: LIGHT_ARRAY_LENGTH,
95 actual: length as usize,
96 },
97 ));
98 }
99
100 let mut bytes = [0; LIGHT_ARRAY_LENGTH];
101 read_exact_counted(reader, &mut bytes, CodecKind::LightArray, prefix_size)?;
102 Ok(Self(bytes))
103 }
104}
105
106#[derive(Debug, Clone, PartialEq, Eq, Default)]
137pub struct LightData {
138 pub sky_light_mask: BitSet,
140 pub block_light_mask: BitSet,
142 pub empty_sky_light_mask: BitSet,
144 pub empty_block_light_mask: BitSet,
146 pub sky_light_arrays: PrefixedArray<LightArray>,
148 pub block_light_arrays: PrefixedArray<LightArray>,
150}
151
152impl LightData {
153 #[must_use]
155 pub fn expected_sky_light_array_count(&self) -> usize {
156 set_bit_count(&self.sky_light_mask)
157 }
158
159 #[must_use]
161 pub fn expected_block_light_array_count(&self) -> usize {
162 set_bit_count(&self.block_light_mask)
163 }
164
165 fn validate_array_counts(&self, operation: CodecOperation) -> Result<(), CodecError> {
166 validate_array_count(
167 self.expected_sky_light_array_count(),
168 self.sky_light_arrays.len(),
169 operation,
170 )?;
171 validate_array_count(
172 self.expected_block_light_array_count(),
173 self.block_light_arrays.len(),
174 operation,
175 )
176 }
177}
178
179impl TypeCodec for LightData {
180 fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
181 self.validate_array_counts(CodecOperation::Write)?;
182 self.sky_light_mask
183 .encode(writer)
184 .map_err(|error| error.with_context(CodecKind::LightData))?;
185 self.block_light_mask
186 .encode(writer)
187 .map_err(|error| error.with_context(CodecKind::LightData))?;
188 self.empty_sky_light_mask
189 .encode(writer)
190 .map_err(|error| error.with_context(CodecKind::LightData))?;
191 self.empty_block_light_mask
192 .encode(writer)
193 .map_err(|error| error.with_context(CodecKind::LightData))?;
194 self.sky_light_arrays
195 .encode(writer)
196 .map_err(|error| error.with_context(CodecKind::LightData))?;
197 self.block_light_arrays
198 .encode(writer)
199 .map_err(|error| error.with_context(CodecKind::LightData))
200 }
201
202 fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
203 let value = Self {
204 sky_light_mask: decode_field(reader)?,
205 block_light_mask: decode_field(reader)?,
206 empty_sky_light_mask: decode_field(reader)?,
207 empty_block_light_mask: decode_field(reader)?,
208 sky_light_arrays: decode_field(reader)?,
209 block_light_arrays: decode_field(reader)?,
210 };
211 value.validate_array_counts(CodecOperation::Read)?;
212 Ok(value)
213 }
214}
215
216fn decode_field<T: TypeCodec>(reader: &mut impl Read) -> Result<T, CodecError> {
217 T::decode(reader).map_err(|error| error.with_context(CodecKind::LightData))
218}
219
220fn set_bit_count(mask: &BitSet) -> usize {
221 mask.0.iter().map(|word| word.count_ones() as usize).sum()
222}
223
224fn validate_array_count(
225 expected: usize,
226 actual: usize,
227 operation: CodecOperation,
228) -> Result<(), CodecError> {
229 if expected == actual {
230 return Ok(());
231 }
232 Err(CodecError::invalid_encoding_for_operation(
233 CodecKind::LightData,
234 operation,
235 0,
236 InvalidEncodingReason::ArrayLengthMismatch { expected, actual },
237 ))
238}