1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
use std::ops::Range;

use fastnbt::{error::Result, from_bytes};
/// 1.2 to 1.12
pub mod pre13;
/// 1.13 to 1.17
pub mod pre18;

mod block;
mod chunk;
mod heightmaps;
mod section;
mod section_data;
mod section_tower;

pub use block::*;
pub use chunk::*;
pub use heightmaps::*;
pub use section::*;
pub use section_data::*;
pub use section_tower::*;

use once_cell::sync::Lazy;

use crate::{biome::Biome, Chunk, HeightMode};

pub static AIR: Lazy<Block> = Lazy::new(|| Block {
    name: "minecraft:air".to_owned(),
    encoded: "minecraft:air|".to_owned(),
    archetype: BlockArchetype::Airy,
});
pub static SNOW_BLOCK: Lazy<Block> = Lazy::new(|| Block {
    name: "minecraft:snow_block".to_owned(),
    encoded: "minecraft:snow_block|".to_owned(),
    archetype: BlockArchetype::Snowy,
});

/// A Minecraft chunk.
#[derive(Debug)]
pub enum JavaChunk {
    Post18(CurrentJavaChunk),
    Pre18(pre18::JavaChunk),
    Pre13(pre13::JavaChunk),
}

impl JavaChunk {
    pub fn from_bytes(data: &[u8]) -> Result<Self> {
        let chunk: Result<CurrentJavaChunk> = from_bytes(data);

        match chunk {
            Ok(chunk) => Ok(Self::Post18(chunk)),
            Err(_) => match from_bytes::<pre18::JavaChunk>(data) {
                Ok(chunk) => Ok(Self::Pre18(chunk)),
                Err(_) => Ok(Self::Pre13(from_bytes::<pre13::JavaChunk>(data)?)),
            },
        }
    }
}

// TODO: Find a better way to dispatch these methods.
impl Chunk for JavaChunk {
    fn status(&self) -> String {
        match self {
            JavaChunk::Post18(c) => c.status(),
            JavaChunk::Pre18(c) => c.status(),
            JavaChunk::Pre13(c) => c.status(),
        }
    }

    fn surface_height(&self, x: usize, z: usize, mode: HeightMode) -> isize {
        match self {
            JavaChunk::Post18(c) => c.surface_height(x, z, mode),
            JavaChunk::Pre18(c) => c.surface_height(x, z, mode),
            JavaChunk::Pre13(c) => c.surface_height(x, z, mode),
        }
    }

    fn biome(&self, x: usize, y: isize, z: usize) -> Option<Biome> {
        match self {
            JavaChunk::Post18(c) => c.biome(x, y, z),
            JavaChunk::Pre18(c) => c.biome(x, y, z),
            JavaChunk::Pre13(c) => c.biome(x, y, z),
        }
    }

    fn block(&self, x: usize, y: isize, z: usize) -> Option<&Block> {
        match self {
            JavaChunk::Post18(c) => c.block(x, y, z),
            JavaChunk::Pre18(c) => c.block(x, y, z),
            JavaChunk::Pre13(c) => c.block(x, y, z),
        }
    }

    fn y_range(&self) -> Range<isize> {
        match self {
            JavaChunk::Post18(c) => c.y_range(),
            JavaChunk::Pre18(c) => c.y_range(),
            JavaChunk::Pre13(c) => c.y_range(),
        }
    }
}