use fastnbt::{self, LongArray, Value};
use serde::Deserialize;
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Namespace {
Minecraft,
Custom(String),
}
impl From<&str> for Namespace {
fn from(value: &str) -> Self {
if value == "minecraft" {
Self::Minecraft
} else {
Self::Custom(value.into())
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct NamespacedKey {
pub namespace: Namespace,
pub key: String,
}
impl NamespacedKey {
pub fn new(namespace: impl AsRef<str>, key: String) -> Self {
Self {
namespace: Namespace::from(namespace.as_ref()),
key,
}
}
pub const fn minecraft(key: String) -> Self {
Self {
namespace: Namespace::Minecraft,
key,
}
}
}
impl From<&str> for NamespacedKey {
fn from(value: &str) -> Self {
if let Some((ns, k)) = value.split_once(':') {
Self::new(ns, k.into())
} else {
Self::minecraft(value.into())
}
}
}
impl<'de> serde::Deserialize<'de> for NamespacedKey {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
Ok(NamespacedKey::from(<&str>::deserialize(deserializer)?))
}
}
#[derive(Deserialize, Debug, Clone, PartialEq)]
pub struct ChunkNbt {
#[serde(rename = "DataVersion")]
pub data_version: i32,
#[serde(rename = "xPos")]
pub x_pos: i32,
#[serde(rename = "zPos")]
pub z_pos: i32,
#[serde(rename = "yPos")]
pub y_pos: i32,
#[serde(rename = "Status")]
pub status: NamespacedKey,
#[serde(rename = "LastUpdate")]
pub last_update: i64,
pub block_entities: Vec<Value>, #[serde(rename = "Heightmaps")]
pub height_maps: HeightMaps,
pub fluid_ticks: Vec<Value>, pub block_ticks: Vec<Value>,
#[serde(rename = "InhabitedTime")]
pub inhabited_time: i64,
pub blending_data: Option<BlendingData>,
#[serde(rename = "PostProcessing")]
pub post_processing: [Vec<Value>; 24],
pub structures: Value,
pub sections: Vec<ChunkSection>,
}
#[derive(Deserialize, Debug, Clone, PartialEq)]
pub struct BlendingData {
pub min_section: i32,
pub max_section: i32,
}
#[derive(Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub struct HeightMaps {
pub motion_blocking: Option<HeightMap>,
pub motion_blocking_no_leaves: Option<HeightMap>,
pub ocean_floor: Option<HeightMap>,
pub ocean_floor_wg: Option<HeightMap>,
pub world_surface: Option<HeightMap>,
pub world_surface_wg: Option<HeightMap>,
}
#[derive(Deserialize, Debug, Clone, PartialEq)]
#[serde(transparent)]
pub struct HeightMap {
raw: LongArray,
}
impl HeightMap {
pub fn get_height(&self, block_x: u32, block_z: u32) -> i32 {
assert!(block_x < 16);
assert!(block_z < 16);
let index = (block_z * 16 + block_x) as usize;
let num = self.raw[index / 7] as u64 >> ((index % 7) * 9) & (2u64.pow(9) - 1);
num as i32 - 65
}
}
#[derive(Deserialize, Debug, PartialEq, Clone)]
pub struct BlockStates {
pub palette: Vec<BlockState>,
pub data: Option<LongArray>,
}
#[derive(Deserialize, Debug, PartialEq, Clone)]
pub struct BlockState {
#[serde(rename = "Name")]
pub name: NamespacedKey,
#[serde(rename = "Properties")]
pub properties: Option<Value>,
}
#[derive(Deserialize, Debug, PartialEq, Clone)]
pub struct Biomes {
pub palette: Vec<String>,
pub data: Option<LongArray>,
}
#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct TileTick {
#[serde(rename = "i")]
pub id: String,
#[serde(rename = "p")]
pub priority: i32,
#[serde(rename = "t")]
pub ticks: i32,
pub x: i32,
pub y: i32,
pub z: i32,
}
#[derive(Deserialize, Debug, PartialEq, Clone)]
pub struct ChunkSection {
pub block_states: Option<BlockStates>,
#[serde(rename = "Y")]
pub y: i8,
pub biomes: Option<Biomes>,
}