From 11b6abb0d4808689346dea6822dc716a33d024bb Mon Sep 17 00:00:00 2001 From: Vijfhoek Date: Thu, 3 Jun 2021 00:08:49 +0200 Subject: [PATCH] Implement chunk loading and saving --- .gitignore | 4 ++ Cargo.lock | 68 ++++++++++++++++++++++++ Cargo.toml | 4 ++ src/chunk.rs | 111 +++++++++++++++++++++++++++++++++++---- src/state/world_state.rs | 40 +++++++++++--- 5 files changed, 209 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 8a177c1..b2b5659 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,7 @@ /generated/ /flamegraph.svg /perf.data* +/chunks/ + +profile.txt +callgrind.out.* diff --git a/Cargo.lock b/Cargo.lock index 601e532..734004d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1128,8 +1128,12 @@ dependencies = [ "log", "noise", "rayon", + "rmp-serde", + "serde", + "serde_repr", "wgpu", "winit", + "zstd", ] [[package]] @@ -1603,6 +1607,27 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +[[package]] +name = "rmp" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f55e5fa1446c4d5dd1f5daeed2a4fe193071771a2636274d0d7a3b082aa7ad6" +dependencies = [ + "byteorder", + "num-traits", +] + +[[package]] +name = "rmp-serde" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "839395ef53057db96b84c9238ab29e1a13f2e5c8ec9f66bef853ab4197303924" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + [[package]] name = "ryu" version = "1.0.5" @@ -1626,6 +1651,9 @@ name = "serde" version = "1.0.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" +dependencies = [ + "serde_derive", +] [[package]] name = "serde_derive" @@ -1649,6 +1677,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "slab" version = "0.4.3" @@ -2023,3 +2062,32 @@ dependencies = [ "maybe-uninit", "pkg-config", ] + +[[package]] +name = "zstd" +version = "0.8.2+zstd.1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c83508bcbbdc9c3abcf77e8e56773d3ffcd2479e0933caab2e7d6b5a9e183aae" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "4.1.0+zstd.1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d30375f78e185ca4c91930f42ea2c0162f9aa29737032501f93b79266d985ae7" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "1.6.0+zstd.1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2141bed8922b427761470e6bbfeff255da94fa20b0bbeab0d9297fcaf71e3aa7" +dependencies = [ + "cc", + "libc", +] diff --git a/Cargo.toml b/Cargo.toml index 02c9931..6c47552 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,11 @@ noise = "0.7.0" rayon = "1.5.1" wgpu = "0.8.1" winit = { version = "0.25.0", default_features = false, features = ["x11", "web-sys"] } +serde_repr = "0.1.7" +rmp-serde = "0.15.4" itertools = "0.10.0" +serde = { version = "1.0.126", features = ["derive"] } +zstd = "0.8.2" [profile.release] debug = true diff --git a/src/chunk.rs b/src/chunk.rs index 7772795..8c2832b 100644 --- a/src/chunk.rs +++ b/src/chunk.rs @@ -1,4 +1,8 @@ -use std::{collections::VecDeque, usize}; +use std::{ + collections::VecDeque, + io::{Read, Write}, + usize, +}; use crate::{geometry::Geometry, quad::Quad, vertex::BlockVertex}; use ahash::{AHashMap, AHashSet}; @@ -6,17 +10,26 @@ use cgmath::{Point3, Vector3}; use noise::utils::{NoiseMapBuilder, PlaneMapBuilder}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use serde::Serialize; +use serde::{ + de::{SeqAccess, Visitor}, + ser::{SerializeSeq, Serializer}, + Deserialize, +}; +use serde_repr::{Deserialize_repr, Serialize_repr}; + #[allow(dead_code)] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)] +#[repr(u8)] pub enum BlockType { - Cobblestone, - Dirt, - Stone, - Grass, - Bedrock, - Sand, - Gravel, - Water, + Cobblestone = 1, + Dirt = 2, + Stone = 3, + Grass = 4, + Bedrock = 5, + Sand = 6, + Gravel = 7, + Water = 8, } impl BlockType { @@ -50,7 +63,7 @@ pub const FACE_FRONT: FaceFlags = 32; pub const FACE_ALL: FaceFlags = FACE_LEFT | FACE_RIGHT | FACE_BOTTOM | FACE_TOP | FACE_BACK | FACE_FRONT; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub struct Block { pub block_type: BlockType, } @@ -60,10 +73,63 @@ pub const CHUNK_ISIZE: isize = CHUNK_SIZE as isize; type ChunkBlocks = [[[Option; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE]; +#[derive(Clone, Default)] pub struct Chunk { pub blocks: ChunkBlocks, } +impl Serialize for Chunk { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(CHUNK_SIZE.pow(3)))?; + for layer in self.blocks.iter() { + for row in layer { + for block in row { + seq.serialize_element(block)?; + } + } + } + seq.end() + } +} + +struct ChunkVisitor; + +impl<'de> Visitor<'de> for ChunkVisitor { + type Value = Chunk; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a chunk") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut chunk = Chunk::default(); + for layer in chunk.blocks.iter_mut() { + for row in layer { + for block in row { + *block = seq.next_element()?.unwrap(); + } + } + } + + Ok(chunk) + } +} + +impl<'de> Deserialize<'de> for Chunk { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_seq(ChunkVisitor) + } +} + impl Chunk { pub fn generate(chunk_x: i32, chunk_y: i32, chunk_z: i32) -> Self { let fbm = noise::Fbm::new(); @@ -333,4 +399,27 @@ impl Chunk { Self::quads_to_geometry(quads) } + + pub fn save(&self, position: Point3) -> anyhow::Result<()> { + let data = rmp_serde::encode::to_vec_named(self)?; + let compressed = zstd::block::compress(&data, 0)?; + + let path = format!("chunks/{}_{}_{}.bin", position.x, position.y, position.z); + let mut file = std::fs::File::create(&path)?; + file.write(&compressed)?; + + Ok(()) + } + + pub fn load(&mut self, position: Point3) -> anyhow::Result<()> { + let path = format!("chunks/{}_{}_{}.bin", position.x, position.y, position.z); + let mut file = std::fs::File::open(&path)?; + + let mut compressed = Vec::new(); + file.read_to_end(&mut compressed)?; + let data = zstd::block::decompress(&compressed, 1024 * 1024)?; + + *self = rmp_serde::decode::from_slice(&data)?; + Ok(()) + } } diff --git a/src/state/world_state.rs b/src/state/world_state.rs index 5d763e1..ecc2050 100644 --- a/src/state/world_state.rs +++ b/src/state/world_state.rs @@ -1,4 +1,7 @@ -use std::time::{Duration, Instant}; +use std::{ + collections::VecDeque, + time::{Duration, Instant}, +}; use ahash::AHashMap; use cgmath::{EuclideanSpace, InnerSpace, Point3, Rad, Vector2, Vector3}; @@ -36,6 +39,8 @@ pub struct WorldState { pub world: World, pub chunk_buffers: AHashMap, GeometryBuffers>, + pub chunk_save_queue: VecDeque>, + pub chunk_load_queue: VecDeque>, time: Time, time_buffer: wgpu::Buffer, wireframe: bool, @@ -336,6 +341,8 @@ impl WorldState { world, chunk_buffers: AHashMap::new(), + chunk_load_queue: VecDeque::new(), + chunk_save_queue: VecDeque::new(), wireframe: false, highlighted: None, @@ -487,21 +494,23 @@ impl WorldState { VirtualKeyCode::A => self.left_pressed = pressed, VirtualKeyCode::D => self.right_pressed = pressed, VirtualKeyCode::F2 if pressed => self.creative = !self.creative, + VirtualKeyCode::F3 if pressed => self.chunk_save_queue.extend(self.world.chunks.keys()), + VirtualKeyCode::F4 if pressed => self.chunk_load_queue.extend(self.world.chunks.keys()), VirtualKeyCode::Space => { - self.up_speed = if self.creative { - if pressed { + self.up_speed = if pressed { + if self.creative { 1.0 } else { - 0.0 + 0.6 } } else { - 0.6 + 0.0 } } VirtualKeyCode::LShift if self.creative => { self.up_speed = if pressed { -1.0 } else { 0.0 } } - VirtualKeyCode::LControl => self.sprinting = state == ElementState::Pressed, + VirtualKeyCode::LControl => self.sprinting = pressed, _ => (), } } @@ -510,7 +519,7 @@ impl WorldState { self.world .get_block( position.x as isize, - (position.y - 1.62) as isize, + (position.y - 1.8) as isize, position.z as isize, ) .is_some() @@ -556,6 +565,23 @@ impl WorldState { } pub fn update(&mut self, dt: Duration, render_context: &RenderContext) { + if let Some(position) = self.chunk_load_queue.pop_front() { + let chunk = self.world.chunks.entry(position).or_default(); + if let Err(err) = chunk.load(position) { + eprintln!("Failed to load chunk {:?}: {:?}", position, err); + } else { + self.update_chunk_geometry(render_context, position); + println!("Loaded chunk {:?}", position); + } + } else if let Some(position) = self.chunk_save_queue.pop_front() { + let chunk = self.world.chunks.get(&position).unwrap(); + if let Err(err) = chunk.save(position) { + eprintln!("Failed to save chunk {:?}: {:?}", position, err); + } else { + println!("Saved chunk {:?}", position); + } + } + self.update_position(dt); self.update_aim(render_context);