Skip to main content

mc_core/world/anvil/
encode.rs

1use std::collections::hash_map::Entry;
2use std::collections::HashMap;
3use std::io::Write;
4
5use crate::heightmap::HeightmapType;
6use crate::world::chunk::Chunk;
7use crate::block::BlockState;
8use crate::util::{PackedArray, PackedIterator};
9
10use nbt::encode::write_compound_tag;
11use nbt::CompoundTag;
12
13
14/// Decode the NBT data from a reader and delegate chunk decoding to `decode_chunk`.
15pub fn encode_chunk_to_writer(writer: &mut impl Write, chunk: &Chunk) {
16    let mut root = CompoundTag::new();
17    encode_chunk(&mut root, chunk);
18    write_compound_tag(writer, &root).unwrap();
19}
20
21pub fn encode_chunk(tag_root: &mut CompoundTag, chunk: &Chunk) {
22
23    let (cx, cz) = chunk.get_position();
24
25    tag_root.insert_i32("DataVersion", 2586);
26
27    tag_root.insert_compound_tag("Level", {
28
29        let mut tag_level = CompoundTag::new();
30
31        tag_level.insert_i32("xPos", cx);
32        tag_level.insert_i32("zPos", cz);
33        // Core crate don't support status, support for status is not planned in near future
34        // because chunks should be generated independently, this is opposed to Notchian gen.
35        tag_level.insert_str("Status", "full");
36
37        tag_level.insert_i32_vec("Biomes", {
38            chunk.iter_biomes().map(|biome| biome.get_id()).collect()
39        });
40
41        tag_level.insert_compound_tag_vec("Sections", {
42            chunk.iter_loaded_sub_chunks()
43                .map(|(cy, sc)| {
44
45                    let mut tag_section = CompoundTag::new();
46                    tag_section.insert_i8("Y", cy);
47
48                    let mut palette_indices = HashMap::new();
49                    let mut tag_palette = Vec::new();
50                    let mut packed_blocks = PackedArray::new(4096, 4, None);
51
52                    for (idx, state) in sc.iter_blocks().enumerate() {
53                        let sid = match palette_indices.entry(state.get_key()) {
54                            Entry::Occupied(o) => *o.get(),
55                            Entry::Vacant(v) => {
56                                tag_palette.push(encode_block_state(state));
57                                *v.insert(tag_palette.len() - 1)
58                            }
59                        };
60                        packed_blocks.set_with_resize(idx, sid as u64);
61                    }
62
63                    tag_section.insert_compound_tag_vec("Palette", tag_palette);
64                    tag_section.insert_i64_vec("BlockStates", packed_blocks.into_inner()
65                        .into_iter()
66                        .map(|val| val as i64)
67                        .collect());
68
69                    tag_section
70
71                })
72        });
73
74        let mut tag_heightmaps = CompoundTag::new();
75
76        for heightmap_type in chunk.get_env().heightmaps.iter_heightmap_types() {
77            if let Some(arr) = encode_heightmap(chunk, heightmap_type) {
78                tag_heightmaps.insert_i64_vec(heightmap_type.get_name(), arr);
79            }
80        }
81
82        tag_level
83
84    });
85
86}
87
88pub fn encode_block_state(state: &'static BlockState) -> CompoundTag {
89
90    let block = state.get_block();
91    let mut tag_block = CompoundTag::new();
92    tag_block.insert_str("Name", block.get_name());
93
94    if let Some(it) = state.iter_raw_states() {
95        let mut tag_props = CompoundTag::new();
96        for (name, value) in it {
97            tag_props.insert_str(name, value);
98        }
99        tag_block.insert_compound_tag("Properties", tag_props);
100    }
101
102    tag_block
103
104}
105
106pub fn encode_heightmap(chunk: &Chunk, heightmap_type: &'static HeightmapType) -> Option<Vec<i64>> {
107    let (byte_size, it) = chunk.iter_heightmap_raw_columns(heightmap_type)?;
108    Some(it.pack_aligned(byte_size).map(|v| v as i64).collect())
109}