Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Superflat Worldgeneration #63

Merged
merged 23 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
be98a73
Added abstraction for coordinates.
DaniD3v Aug 24, 2024
a0260ac
`WORLD_Y_START_AT` refactor
DaniD3v Aug 24, 2024
03fcdc2
Imported serde::Deserialize to avoid spelling it out frequently.
DaniD3v Aug 24, 2024
5a21a68
Block structs refractor + added comments
DaniD3v Aug 24, 2024
43164ee
Added abstraction for `BlockId`
DaniD3v Aug 25, 2024
33ec123
Added `BlockIdentifierNotFound` Error to differentiate from `BlockSta…
DaniD3v Aug 25, 2024
fe56d32
Renamed `block_size` to `block_bit_size` for readability
DaniD3v Aug 25, 2024
4565133
Refractored `ChunkData::from_bytes`
DaniD3v Aug 25, 2024
1d1dd2e
Added consts `CHUNK_AREA`, `SUBCHUNK_VOLUME` and `CHUNK_VOLUME`,
DaniD3v Aug 25, 2024
52c34ca
Added `ChunkBlocks` abstraction for `ChunkData`
DaniD3v Aug 25, 2024
a8d9c89
Implemented Default for `ChunkHeightmaps` and `ChunkBlocks`
DaniD3v Aug 25, 2024
bef3dde
Replaced redundant `.map` with `.for_each`
DaniD3v Aug 25, 2024
cf28332
`BlockId` refactor.
DaniD3v Aug 26, 2024
cdb0e6b
Refractored coordinates.rs
DaniD3v Aug 26, 2024
17b2850
Refractored `read_chunk`
DaniD3v Aug 27, 2024
b5b2788
Added ChunkStatus to check if a chunk is fully generated yet.
DaniD3v Aug 27, 2024
eb4d47a
Added an Enum for Biomes
DaniD3v Aug 27, 2024
4031c4d
Added `Seed` Type.
DaniD3v Aug 27, 2024
9149e82
Added WorldGenerator traits and a GenericWorldGenerator.
DaniD3v Aug 28, 2024
43c724d
Added Superflat implementation.
DaniD3v Aug 28, 2024
112a899
Added logic to generate chunks if they can't be read from the world f…
DaniD3v Aug 28, 2024
e2d2b53
Fixed bug where whole chunk can break in one mined block
DaniD3v Aug 28, 2024
ff06d05
Made ChunkNBT struct slightly more readable
DaniD3v Aug 28, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Cargo.lock

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

18 changes: 7 additions & 11 deletions pumpkin-protocol/src/client/play/c_chunk_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,19 @@ pub struct CChunkData<'a>(pub &'a ChunkData);
impl<'a> ClientPacket for CChunkData<'a> {
fn write(&self, buf: &mut crate::bytebuf::ByteBuffer) {
// Chunk X
buf.put_i32(self.0.position.0);
buf.put_i32(self.0.position.x);
// Chunk Z
buf.put_i32(self.0.position.1);
buf.put_i32(self.0.position.z);

let heightmap_nbt =
fastnbt::to_bytes_with_opts(&self.0.heightmaps, fastnbt::SerOpts::network_nbt())
fastnbt::to_bytes_with_opts(&self.0.blocks.heightmap, fastnbt::SerOpts::network_nbt())
.unwrap();
// Heightmaps
buf.put_slice(&heightmap_nbt);

let mut data_buf = ByteBuffer::empty();
self.0.blocks.chunks(16 * 16 * 16).for_each(|chunk| {
let block_count = chunk
.iter()
.dedup()
.filter(|block| **block != 0 && **block != 12959 && **block != 12958)
.count() as i16;
self.0.blocks.iter_subchunks().for_each(|chunk| {
let block_count = chunk.iter().filter(|block| !block.is_air()).count() as i16;
// Block count
data_buf.put_i16(block_count);
//// Block states
Expand Down Expand Up @@ -63,7 +59,7 @@ impl<'a> ClientPacket for CChunkData<'a> {
palette.iter().enumerate().for_each(|(i, id)| {
palette_map.insert(*id, i);
// Palette
data_buf.put_var_int(&VarInt(**id));
data_buf.put_var_int(&VarInt(id.get_id_mojang_repr()));
});
for block_clump in chunk.chunks(64 / block_size as usize) {
let mut out_long: i64 = 0;
Expand All @@ -83,7 +79,7 @@ impl<'a> ClientPacket for CChunkData<'a> {
let mut out_long: i64 = 0;
let mut shift = 0;
for block in block_clump {
out_long |= (*block as i64) << shift;
out_long |= (block.get_id() as i64) << shift;
shift += DIRECT_PALETTE_BITS;
}
block_data_array.push(out_long);
Expand Down
2 changes: 2 additions & 0 deletions pumpkin-world/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ flate2 = "1.0.33"
serde = { version = "1.0", features = ["derive"] }
lazy_static = "1.5.0"
serde_json = "1.0"
static_assertions = "1.1.0"
log.workspace = true

num-traits = "0.2"
num-derive = "0.4"
9 changes: 9 additions & 0 deletions pumpkin-world/src/biome.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use serde::{Deserialize, Serialize};

// TODO make this work with the protocol
#[derive(Serialize, Deserialize, Clone, Copy)]
#[non_exhaustive]
pub enum Biome {
Plains,
// TODO list all Biomes
}
58 changes: 58 additions & 0 deletions pumpkin-world/src/block/block_id.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use std::collections::HashMap;

use serde::Deserialize;

use super::block_registry::BLOCKS;
use crate::level::WorldError;

// 0 is air -> reasonable default
#[derive(Default, Deserialize, Debug, Hash, Clone, Copy, PartialEq, Eq)]
#[serde(transparent)]
pub struct BlockId {
data: u16,
}

impl BlockId {
pub const AIR: Self = Self::from_id(0);

pub fn new(
text_id: &str,
properties: Option<&HashMap<String, String>>,
) -> Result<Self, WorldError> {
let mut block_states = BLOCKS
.get(text_id)
.ok_or(WorldError::BlockIdentifierNotFound)?
.states
.iter();

let block_state = match properties {
Some(properties) => match block_states.find(|state| &state.properties == properties) {
Some(state) => state,
None => return Err(WorldError::BlockStateIdNotFound),
},
None => block_states
.find(|state| state.is_default)
.expect("Every Block should have at least 1 default state"),
};

Ok(block_state.id)
}

pub const fn from_id(id: u16) -> Self {
// TODO: add check if the id is actually valid
Self { data: id }
}

pub fn is_air(&self) -> bool {
self.data == 0 || self.data == 12959 || self.data == 12958
}

pub fn get_id(&self) -> u16 {
self.data
}

/// An i32 is the way mojang internally represents their Blocks
pub fn get_id_mojang_repr(&self) -> i32 {
self.data as i32
}
}
82 changes: 38 additions & 44 deletions pumpkin-world/src/block/block_registry.rs
Original file line number Diff line number Diff line change
@@ -1,58 +1,52 @@
use std::collections::HashMap;

use lazy_static::lazy_static;
use serde::Deserialize;

use crate::level::WorldError;
use super::block_id::BlockId;

const BLOCKS_JSON: &str = include_str!("../../assets/blocks.json");
lazy_static! {
pub static ref BLOCKS: HashMap<String, RegistryBlockType> =
serde_json::from_str(include_str!("../../assets/blocks.json"))
.expect("Could not parse block.json registry.");
}

#[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct BlockDefinition {
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct RegistryBlockDefinition {
/// e.g. minecraft:door or minecraft:button
#[serde(rename = "type")]
kind: String,
block_set_type: Option<String>,
}
pub category: String,

#[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct BlockState {
default: Option<bool>,
id: i64,
properties: Option<HashMap<String, String>>,
/// Specifies the variant of the blocks category.
/// e.g. minecraft:iron_door has the variant iron
#[serde(rename = "block_set_type")]
pub variant: Option<String>,
}

#[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct BlocksElement {
definition: BlockDefinition,
properties: Option<HashMap<String, Vec<String>>>,
states: Vec<BlockState>,
}
/// One possible state of a Block.
/// This could e.g. be an extended piston facing left.
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct RegistryBlockState {
pub id: BlockId,

lazy_static! {
pub static ref BLOCKS: HashMap<String, BlocksElement> =
serde_json::from_str(BLOCKS_JSON).expect("Could not parse block.json registry.");
/// Whether this is the default state of the Block
#[serde(default, rename = "default")]
pub is_default: bool,

/// The propertise active for this `BlockState`.
#[serde(default)]
pub properties: HashMap<String, String>,
}

pub fn block_id_and_properties_to_block_state_id(
block_id: &str,
properties: Option<&HashMap<String, String>>,
) -> Result<i64, WorldError> {
let block = match BLOCKS.get(block_id) {
Some(block) => block,
None => return Err(WorldError::BlockStateIdNotFound),
};
let block_state_id = match properties {
None => Ok(block
.states
.iter()
.find(|state| state.default.unwrap_or(false))
.expect("Each block should have at least one default state")
.id),
Some(properties) => block
.states
.iter()
.find(|state| state.properties.as_ref() == Some(properties))
.map(|state| state.id)
.ok_or(WorldError::BlockStateIdNotFound),
};
block_state_id
/// A fully-fledged block definition.
/// Stores the category, variant, all of the possible states and all of the possible properties.
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct RegistryBlockType {
pub definition: RegistryBlockDefinition,
pub states: Vec<RegistryBlockState>,

// TODO is this safe to remove? It's currently not used in the Project. @lukas0008 @Snowiiii
/// A list of valid property keys/values for a block.
#[serde(default, rename = "properties")]
valid_properties: HashMap<String, Vec<String>>,
}
8 changes: 5 additions & 3 deletions pumpkin-world/src/block/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use crate::vector3::Vector3;
use num_derive::FromPrimitive;

use crate::vector3::Vector3;
pub mod block_id;
mod block_registry;

pub use block_id::BlockId;

pub mod block_registry;
pub use block_registry::BLOCKS;
#[derive(FromPrimitive)]
pub enum BlockFace {
Bottom = 0,
Expand Down
Loading