Skip to content

Commit

Permalink
Expanded ChunkNbt data structure and added serialization support
Browse files Browse the repository at this point in the history
  • Loading branch information
Asurar0 committed Sep 17, 2024
1 parent ada0d82 commit 467a02e
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 32 deletions.
31 changes: 31 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand Down
1 change: 1 addition & 0 deletions pumpkin-world/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
162 changes: 130 additions & 32 deletions pumpkin-world/src/chunk.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,132 @@
//! ## 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},
level::{ChunkNotGeneratedError, WorldError},
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<ChunkSection>,
/// 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<BlockNbtEntity>,
/// 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<ChunkNbtLight>,
/// 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<ChunkNbtEntity>,
/// 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<i32>,
Expand All @@ -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<HashMap<String, String>>,
}

#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
struct ChunkSectionBlockStates {
data: Option<LongArray>,
palette: Vec<PaletteEntry>,
}

#[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<ChunkSectionBlockStates>,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
struct ChunkNbt {
#[expect(dead_code)]
data_version: usize,

#[serde(rename = "sections")]
sections: Vec<ChunkSection>,

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
Expand Down

0 comments on commit 467a02e

Please sign in to comment.