Skip to main content

steel_core/chunk/light/
packet.rs

1use steel_protocol::packets::game::LightUpdatePacketData;
2use steel_utils::{ChunkPos, SectionPos, codec::BitSet};
3
4use super::{ChunkLightData, ChunkLightLayerStorage, LightSectionData};
5
6/// Builds protocol light-update data from chunk-owned light sections.
7#[must_use]
8pub fn build_chunk_light_update_packet(
9    light: &ChunkLightData,
10    has_skylight: bool,
11) -> LightUpdatePacketData {
12    let range = light.sky.range();
13    let mut sky_y_mask = range.empty_bit_set();
14    let mut block_y_mask = range.empty_bit_set();
15    let mut empty_sky_y_mask = range.empty_bit_set();
16    let mut empty_block_y_mask = range.empty_bit_set();
17    let mut sky_updates = Vec::new();
18    let mut block_updates = Vec::new();
19
20    for section_index in 0..range.section_count() {
21        if has_skylight {
22            prepare_chunk_section_data(
23                &light.sky,
24                section_index,
25                &mut sky_y_mask,
26                &mut empty_sky_y_mask,
27                &mut sky_updates,
28            );
29        }
30        prepare_chunk_section_data(
31            &light.block,
32            section_index,
33            &mut block_y_mask,
34            &mut empty_block_y_mask,
35            &mut block_updates,
36        );
37    }
38
39    LightUpdatePacketData {
40        sky_y_mask,
41        block_y_mask,
42        empty_sky_y_mask,
43        empty_block_y_mask,
44        sky_updates,
45        block_updates,
46    }
47}
48
49/// Builds protocol light-update data for the changed sections of one chunk column.
50///
51/// Vanilla writes update payloads in ascending light-section-index order. Keep
52/// that order here even though callers pass sets/vectors of changed sections.
53#[must_use]
54pub fn build_chunk_light_update_packet_for_sections(
55    chunk_pos: ChunkPos,
56    light: &ChunkLightData,
57    has_skylight: bool,
58    sky_sections: &[SectionPos],
59    block_sections: &[SectionPos],
60) -> LightUpdatePacketData {
61    let range = light.sky.range();
62    let mut sky_y_mask = range.empty_bit_set();
63    let mut block_y_mask = range.empty_bit_set();
64    let mut empty_sky_y_mask = range.empty_bit_set();
65    let mut empty_block_y_mask = range.empty_bit_set();
66    let mut sky_updates = Vec::new();
67    let mut block_updates = Vec::new();
68
69    for section_index in 0..range.section_count() {
70        let Some(section_y) = range.section_y(section_index) else {
71            continue;
72        };
73        let section_pos = SectionPos::new(chunk_pos.0.x, section_y, chunk_pos.0.y);
74
75        if has_skylight && sky_sections.contains(&section_pos) {
76            prepare_chunk_section_data(
77                &light.sky,
78                section_index,
79                &mut sky_y_mask,
80                &mut empty_sky_y_mask,
81                &mut sky_updates,
82            );
83        }
84
85        if block_sections.contains(&section_pos) {
86            prepare_chunk_section_data(
87                &light.block,
88                section_index,
89                &mut block_y_mask,
90                &mut empty_block_y_mask,
91                &mut block_updates,
92            );
93        }
94    }
95
96    LightUpdatePacketData {
97        sky_y_mask,
98        block_y_mask,
99        empty_sky_y_mask,
100        empty_block_y_mask,
101        sky_updates,
102        block_updates,
103    }
104}
105
106fn prepare_chunk_section_data(
107    storage: &ChunkLightLayerStorage,
108    section_index: usize,
109    mask: &mut BitSet,
110    empty_mask: &mut BitSet,
111    updates: &mut Vec<Vec<u8>>,
112) {
113    let Some(section) = storage.sections().get(section_index) else {
114        return;
115    };
116    let Some(data) = section.visible_data() else {
117        return;
118    };
119
120    prepare_section_data(data, section_index, mask, empty_mask, updates);
121}
122
123fn prepare_section_data(
124    data: &LightSectionData,
125    section_index: usize,
126    mask: &mut BitSet,
127    empty_mask: &mut BitSet,
128    updates: &mut Vec<Vec<u8>>,
129) {
130    if data.is_empty() {
131        empty_mask.set(section_index, true);
132        return;
133    }
134
135    let bytes = data.to_bytes();
136    mask.set(section_index, true);
137    updates.push(bytes.as_ref().to_vec());
138}