From 467a02e0ae9054df147d199d422b9cdbd89e43f1 Mon Sep 17 00:00:00 2001 From: Asurar0 Date: Sun, 15 Sep 2024 20:37:23 +0200 Subject: [PATCH 1/2] Expanded ChunkNbt data structure and added serialization support --- Cargo.lock | 31 +++++++ Cargo.toml | 1 + pumpkin-world/Cargo.toml | 1 + pumpkin-world/src/chunk.rs | 162 +++++++++++++++++++++++++++++-------- 4 files changed, 163 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1c1cbac2c..bb44370c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1493,6 +1493,15 @@ dependencies = [ "rustix", ] +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "mime" version = "0.3.17" @@ -1996,6 +2005,7 @@ dependencies = [ "rayon", "serde", "serde_json", + "speedy", "static_assertions", "thiserror", "tokio", @@ -2567,6 +2577,27 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "speedy" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da1992073f0e55aab599f4483c460598219b4f9ff0affa124b33580ab511e25a" +dependencies = [ + "memoffset", + "speedy-derive", +] + +[[package]] +name = "speedy-derive" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658f2ca5276b92c3dfd65fa88316b4e032ace68f88d7570b43967784c0bac5ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "spin" version = "0.9.8" diff --git a/Cargo.toml b/Cargo.toml index 38d79ccf3..0c9f0bec1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ tokio = { version = "1.40", features = [ "io-util", "sync", ] } +speedy = "0.8.7" rayon = "1.10.0" uuid = { version = "1.10.0", features = ["serde", "v3", "v4"] } derive_more = { version = "1.0.0", features = ["full"] } diff --git a/pumpkin-world/Cargo.toml b/pumpkin-world/Cargo.toml index 90be69d5c..98e915000 100644 --- a/pumpkin-world/Cargo.toml +++ b/pumpkin-world/Cargo.toml @@ -7,6 +7,7 @@ edition.workspace = true pumpkin-core = { path = "../pumpkin-core"} fastnbt = { git = "https://github.com/owengage/fastnbt.git" } +speedy.workspace = true tokio.workspace = true rayon.workspace = true derive_more.workspace = true diff --git a/pumpkin-world/src/chunk.rs b/pumpkin-world/src/chunk.rs index dd5aa643b..08e87bf47 100644 --- a/pumpkin-world/src/chunk.rs +++ b/pumpkin-world/src/chunk.rs @@ -1,11 +1,19 @@ +//! ## Chunk +//! +//! This module defines a minecraft chunk data strcture. +//! + +// ========================= Imports ========================= + use std::cmp::max; use std::collections::HashMap; use std::ops::Index; use fastnbt::LongArray; -use pumpkin_core::math::vector2::Vector2; use serde::{Deserialize, Serialize}; +use pumpkin_core::math::vector2::Vector2; + use crate::{ block::BlockId, coordinates::{ChunkRelativeBlockCoordinates, Height}, @@ -13,10 +21,112 @@ use crate::{ WORLD_HEIGHT, }; +// ======================== Constants ======================== + const CHUNK_AREA: usize = 16 * 16; const SUBCHUNK_VOLUME: usize = CHUNK_AREA * 16; const CHUNK_VOLUME: usize = CHUNK_AREA * WORLD_HEIGHT; +// ======================== NBT Structure ======================== +// This section defines some data structure designed and used by Minecraft +// java implementation. They might not be used as defined by Pumpkin for +// its core working. +// + +#[derive(Serialize, Deserialize, Debug)] +#[allow(dead_code)] +#[serde(rename_all = "PascalCase")] +/// `ChunkNbt` +/// +/// This data structure stores a chunk information as described by a regional +/// Minecraft Anvil file. They are stored in NBT format and have been updated +/// for Minecraft 1.18. +pub struct ChunkNbt { + /// Version of the chunk NBT structure. + data_version: i32, + /// X position of the chunk (in chunks, from the origin, not relative to region). + #[serde(rename = "xPos")] + x_pos: i32, + /// Z position of the chunk (in chunks, from the origin, not relative to region). + #[serde(rename = "zPos")] + z_pos: i32, + /// Lowest Y section position in the chunk (e.g. -4 in 1.18). + #[serde(rename = "yPos")] + y_pos: i32, + /// Defines the world generation status of this chunk. + status: ChunkStatus, + /// Tick when the chunk was last saved. + last_update: i64, + /// List of compound tags, each tag is a section (also known as sub-chunk). All + /// ections in the world's height are present in this list, even those who are + /// empty (filled with air). + #[serde(rename = "sections")] + sections: Vec, + /// Each TAG_Compound in this list defines a block entity in the chunk. If this list is empty, it becomes a list of End tags. + #[serde(rename = "block_entities")] + #[serde(skip)] + block_entities: Vec, + /// Several different heightmaps corresponding to 256 values compacted at 9 bits per value + heightmaps: ChunkHeightmaps, + /// A List of 16 lists that store positions of light sources per chunk section as shorts, only for proto-chunks + #[serde(skip)] + lights: Vec, + /// A list of entities in the proto-chunks, used when generating. As of 1.17, this list is not present for fully generated chunks and entities are moved to a separated region files once the chunk is generated. + #[serde(skip)] + entities: Vec, + /// TODO + #[serde(rename = "fluid_ticks")] + #[serde(skip)] + fluid_ticks: (), + /// TODO + #[serde(rename = "block_ticks")] + #[serde(skip)] + block_ticks: (), + /// TODO + #[serde(skip)] + inhabited_time: i64, + /// TODO + #[serde(rename = "blending_data")] + #[serde(skip)] + blending_data: ChunkNbtBlendingData, + /// TODO + #[serde(skip)] + post_processing: (), + /// TODO + #[serde(skip)] + structures: (), +} + +#[derive(Serialize, Deserialize, Debug)] +/// A block entity (not related to entity) is used by Minecraft to store information +/// about a block that can't be stored in the block's block states. Also known as +/// *"tile entities"* in prior versions of the game. +pub enum BlockNbtEntity { + // TODO +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct ChunkNbtLight { + // TODO +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct ChunkNbtEntity { + // TODO +} + +#[derive(Serialize, Deserialize, Default, Debug)] +/// Biome blending data +pub struct ChunkNbtBlendingData { + min_section: i32, + max_section: i32, +} + +// ======================== Pumpkin Structure ======================== +// This section defines structures that are used by +// +// + pub struct ChunkData { pub blocks: ChunkBlocks, pub position: Vector2, @@ -33,75 +143,63 @@ pub struct ChunkBlocks { pub heightmap: ChunkHeightmaps, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "PascalCase")] struct PaletteEntry { name: String, properties: Option>, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] struct ChunkSectionBlockStates { data: Option, palette: Vec, } -#[derive(Deserialize, Serialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "UPPERCASE")] pub struct ChunkHeightmaps { motion_blocking: LongArray, world_surface: LongArray, } -#[derive(Deserialize, Debug)] -#[expect(dead_code)] +#[derive(Serialize, Deserialize, Debug)] struct ChunkSection { #[serde(rename = "Y")] y: i32, block_states: Option, } -#[derive(Deserialize, Debug)] -#[serde(rename_all = "PascalCase")] -struct ChunkNbt { - #[expect(dead_code)] - data_version: usize, - - #[serde(rename = "sections")] - sections: Vec, - - heightmaps: ChunkHeightmaps, -} - -#[derive(Deserialize, Debug, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] #[serde(tag = "Status")] +#[repr(u32)] enum ChunkStatus { #[serde(rename = "minecraft:empty")] - Empty, + Empty = 0, #[serde(rename = "minecraft:structure_starts")] - StructureStarts, + StructureStarts = 1, #[serde(rename = "minecraft:structure_references")] - StructureReferences, + StructureReferences = 2, #[serde(rename = "minecraft:biomes")] - Biomes, + Biomes = 3, #[serde(rename = "minecraft:noise")] - Noise, + Noise = 4, #[serde(rename = "minecraft:surface")] - Surface, + Surface = 5, #[serde(rename = "minecraft:carvers")] - Carvers, + Carvers = 6, #[serde(rename = "minecraft:liquid_carvers")] - LiquidCarvers, + LiquidCarvers = 7, #[serde(rename = "minecraft:features")] - Features, + Features = 8, #[serde(rename = "minecraft:initialize_light")] - Light, + Light = 9, #[serde(rename = "minecraft:spawn")] - Spawn, + Spawn = 10, #[serde(rename = "minecraft:heightmaps")] - Heightmaps, + Heightmaps = 11, #[serde(rename = "minecraft:full")] - Full, + Full = 12, } /// The Heightmap for a completely empty chunk From 6ec8d90d5d99eaeb430553ac4dd86d3406e6d6e1 Mon Sep 17 00:00:00 2001 From: Asurar0 Date: Wed, 18 Sep 2024 18:03:34 +0200 Subject: [PATCH 2/2] Review edits --- Cargo.lock | 31 ------------------------------- Cargo.toml | 1 - pumpkin-world/Cargo.toml | 1 - pumpkin-world/src/chunk.rs | 26 +++++++++++++------------- 4 files changed, 13 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bb44370c4..1c1cbac2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1493,15 +1493,6 @@ dependencies = [ "rustix", ] -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - [[package]] name = "mime" version = "0.3.17" @@ -2005,7 +1996,6 @@ dependencies = [ "rayon", "serde", "serde_json", - "speedy", "static_assertions", "thiserror", "tokio", @@ -2577,27 +2567,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "speedy" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da1992073f0e55aab599f4483c460598219b4f9ff0affa124b33580ab511e25a" -dependencies = [ - "memoffset", - "speedy-derive", -] - -[[package]] -name = "speedy-derive" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658f2ca5276b92c3dfd65fa88316b4e032ace68f88d7570b43967784c0bac5ac" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "spin" version = "0.9.8" diff --git a/Cargo.toml b/Cargo.toml index 0c9f0bec1..38d79ccf3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,6 @@ tokio = { version = "1.40", features = [ "io-util", "sync", ] } -speedy = "0.8.7" rayon = "1.10.0" uuid = { version = "1.10.0", features = ["serde", "v3", "v4"] } derive_more = { version = "1.0.0", features = ["full"] } diff --git a/pumpkin-world/Cargo.toml b/pumpkin-world/Cargo.toml index 98e915000..90be69d5c 100644 --- a/pumpkin-world/Cargo.toml +++ b/pumpkin-world/Cargo.toml @@ -7,7 +7,6 @@ edition.workspace = true pumpkin-core = { path = "../pumpkin-core"} fastnbt = { git = "https://github.com/owengage/fastnbt.git" } -speedy.workspace = true tokio.workspace = true rayon.workspace = true derive_more.workspace = true diff --git a/pumpkin-world/src/chunk.rs b/pumpkin-world/src/chunk.rs index 08e87bf47..6bbdb2210 100644 --- a/pumpkin-world/src/chunk.rs +++ b/pumpkin-world/src/chunk.rs @@ -175,31 +175,31 @@ struct ChunkSection { #[repr(u32)] enum ChunkStatus { #[serde(rename = "minecraft:empty")] - Empty = 0, + Empty, #[serde(rename = "minecraft:structure_starts")] - StructureStarts = 1, + StructureStarts, #[serde(rename = "minecraft:structure_references")] - StructureReferences = 2, + StructureReferences, #[serde(rename = "minecraft:biomes")] - Biomes = 3, + Biomes, #[serde(rename = "minecraft:noise")] - Noise = 4, + Noise, #[serde(rename = "minecraft:surface")] - Surface = 5, + Surface, #[serde(rename = "minecraft:carvers")] - Carvers = 6, + Carvers, #[serde(rename = "minecraft:liquid_carvers")] - LiquidCarvers = 7, + LiquidCarvers, #[serde(rename = "minecraft:features")] - Features = 8, + Features, #[serde(rename = "minecraft:initialize_light")] - Light = 9, + Light, #[serde(rename = "minecraft:spawn")] - Spawn = 10, + Spawn, #[serde(rename = "minecraft:heightmaps")] - Heightmaps = 11, + Heightmaps, #[serde(rename = "minecraft:full")] - Full = 12, + Full, } /// The Heightmap for a completely empty chunk