From 6896698883f46e9381c6b750e20274a772914e8c Mon Sep 17 00:00:00 2001 From: crumblingstatue Date: Sun, 9 Apr 2023 01:31:34 +0200 Subject: [PATCH] First phase of serialization --- Cargo.lock | 45 ++++++++++++++++++++++++- Cargo.toml | 5 ++- design/save.md | 2 +- src/app.rs | 14 +++----- src/game.rs | 2 +- src/player.rs | 10 +++++- src/world.rs | 89 +++++++++++++++++++++++++++++++++++++++++++++++++- 7 files changed, 152 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 103acf5..b0ab8f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -136,6 +136,9 @@ name = "cc" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +dependencies = [ + "jobserver", +] [[package]] name = "cfg-expr" @@ -550,6 +553,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.61" @@ -631,6 +643,7 @@ dependencies = [ "sfml", "sfml-xt", "worldgen", + "zstd", ] [[package]] @@ -929,9 +942,9 @@ dependencies = [ [[package]] name = "s2dc" version = "0.1.0" -source = "git+https://github.com/crumblingstatue/s2dc.git#468ab3ca6de2d05f1eb5cdd8d869055d9157fb76" dependencies = [ "num-traits", + "serde", ] [[package]] @@ -1404,3 +1417,33 @@ name = "worldgen" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9b77fba24c873b60af296c1f0c5639114186d1ac3c99f880a7bb590bdc04065" + +[[package]] +name = "zstd" +version = "0.12.3+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "6.0.5+zstd.1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.8+zstd.1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +dependencies = [ + "cc", + "libc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index 77fa2b6..708bc8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,9 +21,12 @@ rmp-serde = "1.1.1" serde = { version = "1.0.159", features = ["derive"] } log = "0.4.17" env_logger = "0.10.0" +zstd = "0.12.3" [dependencies.s2dc] -git = "https://github.com/crumblingstatue/s2dc.git" +#git = "https://github.com/crumblingstatue/s2dc.git" +path = "/home/dew/projects/github/s2dc" +features = ["serde"] [dependencies.sfml-xt] git = "https://github.com/crumblingstatue/sfml-xt.git" diff --git a/design/save.md b/design/save.md index cb0b808..9a74b6e 100644 --- a/design/save.md +++ b/design/save.md @@ -5,7 +5,7 @@ Each folder has: - Subfolder for regions (regions/) Each region subfolder has: -r.x.y.dat files for each chunk, where x and y are the region coordinates. +x.y.rgn files for each chunk, where x and y are the region coordinates. # Regions A region stores multiple chunks in a single file for more optimized storage. diff --git a/src/app.rs b/src/app.rs index 3b61347..a6b572e 100644 --- a/src/app.rs +++ b/src/app.rs @@ -18,7 +18,6 @@ use crate::{ input::Input, math::{center_offset, TILE_SIZE}, res::Res, - world::{ChkPosSc, TPosSc, CHUNK_EXTENT, REGION_CHUNK_EXTENT}, }; /// Application level state (includes game and ui state, etc.) @@ -74,6 +73,7 @@ impl App { gamedebug_core::inc_frame(); } self.game.tile_db.try_save(); + self.game.world.save(); } fn do_event_handling(&mut self) { @@ -215,14 +215,10 @@ impl App { mouse_tpos.y, self.game.world.tile_at_mut(mouse_tpos, &self.game.worldgen) ); - let m_chk_x = (mouse_tpos.x / CHUNK_EXTENT as TPosSc) as ChkPosSc; - let m_chk_y = (mouse_tpos.y / CHUNK_EXTENT as TPosSc) as ChkPosSc; - imm!("@ chunk {m_chk_x}, {m_chk_y}"); - imm!( - "@ region {}, {}", - m_chk_x / REGION_CHUNK_EXTENT as ChkPosSc, - m_chk_y / REGION_CHUNK_EXTENT as ChkPosSc - ); + let m_chk = mouse_tpos.to_chunk(); + imm!("@ chunk {}, {}", m_chk.x, m_chk.y); + let (m_chk_x, m_chk_y) = m_chk.region(); + imm!("@ region {m_chk_x}, {m_chk_y}"); if self.debug.freecam && self.input.pressed(Key::P) { self.game.world.player.col_en.en.pos.x = wpos.x as i32; self.game.world.player.col_en.en.pos.y = wpos.y as i32; diff --git a/src/game.rs b/src/game.rs index 2988031..0f5077e 100644 --- a/src/game.rs +++ b/src/game.rs @@ -142,7 +142,7 @@ impl Default for GameState { spawn_point.y -= 1104; Self { camera_offset: spawn_point, - world: World::new(spawn_point), + world: World::new(spawn_point, "TestWorld"), gravity: 0.55, tile_to_place: 1, current_biome: Biome::Surface, diff --git a/src/player.rs b/src/player.rs index 18b614a..062b9a5 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,12 +1,13 @@ use egui_inspect::{derive::Inspect, inspect}; use s2dc::{vec2, MobileEntity}; +use serde::{Deserialize, Serialize}; use crate::{ math::{WorldPos, TILE_SIZE}, world::{TPosSc, TilePos}, }; -#[derive(Debug, Inspect)] +#[derive(Debug, Inspect, Serialize, Deserialize)] pub struct Player { #[inspect_with(inspect_mobile_entity)] pub col_en: MobileEntity, @@ -50,4 +51,11 @@ impl Player { pub fn feet_y(&self) -> i32 { self.col_en.en.pos.y + self.col_en.en.bb.y } + + pub(crate) fn save(&self) { + log::info!( + "{:?}", + std::fs::write("player.dat", rmp_serde::to_vec(self).unwrap()) + ); + } } diff --git a/src/world.rs b/src/world.rs index 4f588f6..ac71e9d 100644 --- a/src/world.rs +++ b/src/world.rs @@ -1,5 +1,8 @@ +use std::path::Path; + use egui_inspect::derive::Inspect; use fnv::FnvHashMap; +use serde::{Deserialize, Serialize}; use crate::{math::WorldPos, player::Player, worldgen::Worldgen}; @@ -11,6 +14,23 @@ pub struct ChunkPos { pub y: ChkPosSc, } +impl ChunkPos { + /// Returns the region this chunk position belongs to + pub fn region(&self) -> (u8, u8) { + ( + (self.x / REGION_CHUNK_EXTENT as ChkPosSc) as u8, + (self.y / REGION_CHUNK_EXTENT as ChkPosSc) as u8, + ) + } + /// Returns the local position in the region (0-7) + pub fn local(&self) -> (u8, u8) { + ( + (self.x % REGION_CHUNK_EXTENT as ChkPosSc) as u8, + (self.y % REGION_CHUNK_EXTENT as ChkPosSc) as u8, + ) + } +} + #[derive(Debug, Inspect)] pub struct World { /// The currently loaded chunks @@ -19,16 +39,79 @@ pub struct World { /// In other words, the age of the world. pub ticks: u64, pub player: Player, + pub name: String, } impl World { - pub fn new(spawn_point: WorldPos) -> Self { + pub fn new(spawn_point: WorldPos, name: &str) -> Self { Self { chunks: Default::default(), ticks: Default::default(), player: Player::new_at(spawn_point), + name: name.to_string(), } } + pub fn save(&self) { + log::info!("{:?}", std::fs::create_dir(&self.name)); + std::env::set_current_dir(&self.name).unwrap(); + self.save_meta(); + self.player.save(); + self.save_chunks(); + } + pub fn save_meta(&self) { + let meta = WorldMetaSave { + name: self.name.clone(), + ticks: self.ticks, + }; + log::info!( + "{:?}", + std::fs::write("world.dat", rmp_serde::to_vec(&meta).unwrap()) + ); + } + pub fn save_chunks(&self) { + for (pos, chk) in self.chunks.iter() { + let (reg_x, reg_y) = pos.region(); + let reg_file_name = format!("{reg_x}.{reg_y}.rgn"); + dbg!(®_file_name); + if !Path::new(®_file_name).exists() { + log::info!( + "{:?}", + std::fs::write(®_file_name, zstd::encode_all(&[][..], 0).unwrap()) + ); + } + let mut decomp = zstd::decode_all(&std::fs::read(®_file_name).unwrap()[..]).unwrap(); + let (loc_x, loc_y) = pos.local(); + dbg!(loc_x, loc_y); + let loc_idx = (loc_y * REGION_CHUNK_EXTENT) + loc_x; + dbg!(loc_idx); + let byte_idx = loc_idx as usize * CHUNK_BYTES; + dbg!(byte_idx); + let end_idx = byte_idx + CHUNK_BYTES; + dbg!(end_idx); + if decomp.len() < end_idx + 1 { + decomp.resize(end_idx + 1, 0); + } + for (i, tile) in chk.tiles.iter().enumerate() { + let off = byte_idx + (i * TILE_BYTES); + decomp[off..off + 2].copy_from_slice(&tile.bg.to_le_bytes()); + decomp[off + 2..off + 4].copy_from_slice(&tile.mid.to_le_bytes()); + decomp[off + 4..off + 6].copy_from_slice(&tile.fg.to_le_bytes()); + } + log::info!( + "{:?}", + std::fs::write(®_file_name, zstd::encode_all(&decomp[..], 0).unwrap()) + ); + } + } +} + +const CHUNK_BYTES: usize = CHUNK_N_TILES * TILE_BYTES; +const TILE_BYTES: usize = 3 * 2; + +#[derive(Serialize, Deserialize)] +struct WorldMetaSave { + name: String, + ticks: u64, } impl World { @@ -72,6 +155,10 @@ impl TilePos { }; (chk, local) } + + pub(crate) fn to_chunk(self) -> ChunkPos { + self.to_chunk_and_local().0 + } } fn chk_pos(tile: TPosSc) -> ChkPosSc {