Skip to main content

steel_core/chunk_saver/storage/
light.rs

1use super::{
2    ChunkLightData, ChunkLightLayerStorage, ChunkStatus, ChunkStorage, DATA_LAYER_SIZE,
3    LightSection, LightSectionData, PersistentLightData, PersistentLightSection,
4    homogeneous_packed_light_value,
5};
6
7impl ChunkStorage {
8    /// Converts chunk-owned light data to persistent format.
9    pub(super) fn light_to_persistent(light: &ChunkLightData) -> PersistentLightData {
10        PersistentLightData {
11            block: Self::light_layer_to_persistent(&light.block),
12            sky: Self::light_layer_to_persistent(&light.sky),
13        }
14    }
15
16    pub(super) fn light_layer_to_persistent(
17        layer: &ChunkLightLayerStorage,
18    ) -> Vec<PersistentLightSection> {
19        layer
20            .sections()
21            .iter()
22            .enumerate()
23            .filter_map(|(index, section)| {
24                let Ok(section_index) = u32::try_from(index) else {
25                    tracing::warn!(
26                        index,
27                        "Light section index does not fit in persistent format"
28                    );
29                    return None;
30                };
31
32                match section {
33                    LightSection::Missing => None,
34                    LightSection::Visible(data) => {
35                        if data.is_all_zero() {
36                            Some(PersistentLightSection::Uninitialized { section_index })
37                        } else {
38                            Some(PersistentLightSection::Initialized {
39                                section_index,
40                                data: data.to_bytes().as_ref().to_vec(),
41                            })
42                        }
43                    }
44                    LightSection::Internal(data) => {
45                        if data.is_all_zero() {
46                            None
47                        } else {
48                            Some(PersistentLightSection::Internal {
49                                section_index,
50                                data: data.to_bytes().as_ref().to_vec(),
51                            })
52                        }
53                    }
54                }
55            })
56            .collect()
57    }
58
59    pub(super) fn persistent_to_light(
60        persistent: &PersistentLightData,
61        min_y: i32,
62        height: i32,
63        status: ChunkStatus,
64    ) -> ChunkLightData {
65        let mut light = ChunkLightData::for_valid_world_height(min_y, height);
66        if status < ChunkStatus::Light {
67            return light;
68        }
69
70        Self::apply_persistent_light_layer(&mut light.block, &persistent.block, "block");
71        Self::apply_persistent_light_layer(&mut light.sky, &persistent.sky, "sky");
72        light
73            .sky
74            .fill_loaded_missing_sky_sections_below_data_with_zero();
75        light
76    }
77
78    pub(super) fn apply_persistent_light_layer(
79        layer: &mut ChunkLightLayerStorage,
80        persistent: &[PersistentLightSection],
81        layer_name: &str,
82    ) {
83        for section in persistent {
84            let Ok(section_index) = usize::try_from(section.section_index()) else {
85                tracing::warn!(
86                    layer = layer_name,
87                    section_index = section.section_index(),
88                    "Persisted light section index does not fit this platform"
89                );
90                continue;
91            };
92
93            let Some(target) = layer.sections_mut().get_mut(section_index) else {
94                tracing::warn!(
95                    layer = layer_name,
96                    section_index,
97                    "Persisted light section index is outside world light range"
98                );
99                continue;
100            };
101
102            let Some(restored) = Self::persistent_to_light_section(section, layer_name) else {
103                continue;
104            };
105            *target = restored;
106        }
107    }
108
109    pub(super) fn persistent_to_light_section(
110        persistent: &PersistentLightSection,
111        layer_name: &str,
112    ) -> Option<LightSection> {
113        match persistent {
114            PersistentLightSection::Uninitialized { .. } => {
115                Some(LightSection::visible(LightSectionData::homogeneous(0)))
116            }
117            PersistentLightSection::Initialized {
118                section_index,
119                data,
120            } => Self::persistent_light_bytes_to_data(data, *section_index, layer_name)
121                .map(LightSection::visible),
122            PersistentLightSection::Internal {
123                section_index,
124                data,
125            } => {
126                let restored =
127                    Self::persistent_light_bytes_to_data(data, *section_index, layer_name)?;
128                if restored.is_all_zero() {
129                    None
130                } else {
131                    Some(LightSection::internal(restored))
132                }
133            }
134        }
135    }
136
137    pub(super) fn persistent_light_bytes_to_data(
138        data: &[u8],
139        section_index: u32,
140        layer_name: &str,
141    ) -> Option<LightSectionData> {
142        let actual = data.len();
143        let bytes = Box::<[u8]>::from(data);
144        let result: Result<Box<[u8; DATA_LAYER_SIZE]>, Box<[u8]>> = bytes.try_into();
145        let Ok(bytes) = result else {
146            tracing::warn!(
147                layer = layer_name,
148                section_index,
149                actual,
150                expected = DATA_LAYER_SIZE,
151                "Skipping persisted light section with invalid byte length"
152            );
153            return None;
154        };
155
156        if let Some(value) = homogeneous_packed_light_value(&bytes) {
157            Some(LightSectionData::homogeneous(value))
158        } else {
159            Some(LightSectionData::Packed(bytes))
160        }
161    }
162}