diff --git a/.gitignore b/.gitignore index 762885d..39b6d35 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ target/ _src _visualizer.json + +*.kubi diff --git a/Cargo.lock b/Cargo.lock index f12cdeb..933fd6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1033,11 +1033,13 @@ dependencies = [ "glam", "hashbrown 0.14.0", "nohash-hasher", + "num_enum 0.7.1", "postcard", "rand", "rand_xoshiro", "serde", "shipyard", + "static_assertions", "strum", ] @@ -1223,7 +1225,7 @@ dependencies = [ "bitflags 1.3.2", "jni-sys", "ndk-sys", - "num_enum", + "num_enum 0.5.11", "raw-window-handle 0.5.2", "thiserror", ] @@ -1371,7 +1373,16 @@ version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" dependencies = [ - "num_enum_derive", + "num_enum_derive 0.5.11", +] + +[[package]] +name = "num_enum" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683751d591e6d81200c39fb0d1032608b77724f34114db54f571ff1317b337c0" +dependencies = [ + "num_enum_derive 0.7.1", ] [[package]] @@ -1386,6 +1397,18 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "num_enum_derive" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.23", +] + [[package]] name = "objc" version = "0.2.7" diff --git a/kubi-shared/Cargo.toml b/kubi-shared/Cargo.toml index 61c2cc6..95d20f2 100644 --- a/kubi-shared/Cargo.toml +++ b/kubi-shared/Cargo.toml @@ -8,6 +8,7 @@ publish = false glam = { version = "0.24", features = ["debug-glam-assert", "fast-math", "serde"] } shipyard = { git = "https://github.com/leudz/shipyard", rev = "0934b426eb9a8", default-features = false, features = ["std"] } strum = { version = "0.25", features = ["derive"] } +num_enum = "0.7" postcard = { version = "1.0", features = ["alloc"] } serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } bincode = "1.3" @@ -17,9 +18,8 @@ rand = { version = "0.8", default_features = false, features = ["std", "min_cons rand_xoshiro = "0.6" hashbrown = { version = "0.14", features = ["serde"] } nohash-hasher = "0.2" - #bytemuck = { version = "1.14", features = ["derive"] } -#static_assertions = "1.1" +static_assertions = "1.1" [features] default = [] diff --git a/kubi-shared/src/block.rs b/kubi-shared/src/block.rs index b2f0bee..f6950f2 100644 --- a/kubi-shared/src/block.rs +++ b/kubi-shared/src/block.rs @@ -1,5 +1,6 @@ use serde::{Serialize, Deserialize}; use strum::EnumIter; +use num_enum::TryFromPrimitive; #[derive(Serialize, Deserialize, Clone, Copy, Debug, EnumIter)] #[repr(u8)] @@ -22,7 +23,7 @@ pub enum BlockTexture { WaterSolid, } -#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, EnumIter)] +#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, EnumIter, TryFromPrimitive)] #[repr(u8)] pub enum Block { Air, diff --git a/kubi-shared/src/data.rs b/kubi-shared/src/data.rs index 919f2dc..9780b6c 100644 --- a/kubi-shared/src/data.rs +++ b/kubi-shared/src/data.rs @@ -2,13 +2,20 @@ use std::{ fs::File, mem::size_of, io::{Read, Seek, SeekFrom, Write}, - borrow::Cow + borrow::Cow, + sync::{Arc, RwLock} }; +use num_enum::TryFromPrimitive; use serde::{Serialize, Deserialize}; -use glam::IVec2; +use glam::IVec3; use hashbrown::HashMap; use anyhow::Result; -use crate::{block::Block, chunk::{CHUNK_SIZE, BlockDataRef}}; +use shipyard::Unique; +use static_assertions::const_assert_eq; +use crate::{ + block::Block, + chunk::{CHUNK_SIZE, BlockDataRef, BlockData} +}; const SECTOR_SIZE: usize = CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE * size_of::(); const RESERVED_SIZE: usize = 1048576; //~1mb (16 sectors assuming 32x32x32 world of 1byte blocks) @@ -20,13 +27,13 @@ const HEADER_MAGIC_STR: [u8; 4] = *b"KUBI"; const HEADER_MAGIC_IDENTITY: u32 = 1; // #[repr(transparent)] -// struct IVec2Hash(IVec2); +// struct IVec3Hash(IVec3); #[derive(Serialize, Deserialize)] -struct WorldSaveDataHeader { +pub struct WorldSaveDataHeader { pub name: Cow<'static, str>, pub seed: u64, sector_count: u32, - chunk_map: HashMap, + chunk_map: HashMap, } impl Default for WorldSaveDataHeader { @@ -40,11 +47,14 @@ impl Default for WorldSaveDataHeader { } } -struct WorldSaveFile { +#[derive(Unique)] +pub struct WorldSaveFile { pub file: File, pub header: WorldSaveDataHeader, } +pub type SharedSaveFile = Arc>; + impl WorldSaveFile { pub fn new(file: File) -> Self { WorldSaveFile { @@ -82,13 +92,23 @@ impl WorldSaveFile { Ok(()) } + pub fn initialize(&mut self) -> Result<()> { + self.write_header()?; + Ok(()) + } + + pub fn load_data(&mut self) -> Result<()> { + self.read_header()?; + Ok(()) + } + fn allocate_sector(&mut self) -> u32 { let value = self.header.sector_count + 1; self.header.sector_count += 1; value } - pub fn save_chunk(&mut self, position: IVec2, data: &BlockDataRef) -> Result<()> { + pub fn save_chunk(&mut self, position: IVec3, data: &BlockDataRef) -> Result<()> { let mut header_modified = false; let sector = self.header.chunk_map.get(&position).copied().unwrap_or_else(|| { header_modified = true; @@ -96,7 +116,8 @@ impl WorldSaveFile { }); let offset = sector as u64 * SECTOR_SIZE as u64; - //SAFETY: *nuzzles* t-t-twust me pwease OwO + + const_assert_eq!(size_of::(), 1); let data: &[u8; SECTOR_SIZE] = unsafe { std::mem::transmute(data) }; self.file.seek(SeekFrom::Start(offset))?; @@ -108,4 +129,41 @@ impl WorldSaveFile { self.file.sync_data()?; Ok(()) } + + ///TODO partial chunk commit (No need to write whole 32kb for a single block change!) + pub fn chunk_set_block() { + todo!() + } + + pub fn chunk_exists(&self, position: IVec3) -> bool { + self.header.chunk_map.contains_key(&position) + } + + pub fn load_chunk(&mut self, position: IVec3) -> Result> { + let Some(§or) = self.header.chunk_map.get(&position) else { + return Ok(None); + }; + + let mut buffer = Box::new([0u8; CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE * size_of::()]); + let offset = sector as u64 * SECTOR_SIZE as u64; + + self.file.seek(SeekFrom::Start(offset))?; + self.file.read_exact(&mut buffer[..])?; + + //should be safe under these conditions: + //Block is a single byte + //All block data bytes are in valid range + const_assert_eq!(size_of::(), 1); + for &byte in &buffer[..] { + let block = Block::try_from_primitive(byte); + match block { + //Sanity check, not actually required: (should NEVER happen) + Ok(block) => debug_assert_eq!(byte, block as u8), + Err(_) => anyhow::bail!("invalid block data"), + } + } + let data: BlockData = unsafe { std::mem::transmute(buffer) }; + + Ok(Some(data)) + } } diff --git a/kubi/src/init.rs b/kubi/src/init.rs index c36a747..a542a0e 100644 --- a/kubi/src/init.rs +++ b/kubi/src/init.rs @@ -1,9 +1,26 @@ use shipyard::{AllStoragesView, UniqueViewMut}; -use std::{env, net::SocketAddr}; +use std::{env, net::SocketAddr, fs::OpenOptions, path::{Path, PathBuf}, str::FromStr, sync::{Arc, RwLock}}; +use anyhow::Result; use crate::{ networking::{GameType, ServerAddress}, state::{GameState, NextState} }; +use kubi_shared::data::{WorldSaveFile, SharedSaveFile}; + +fn open_local_save_file(path: &Path) -> Result { + let mut save_file = WorldSaveFile::new({ + OpenOptions::new() + .read(true) + .write(true) + .open("world.kbi")? + }); + if save_file.file.metadata().unwrap().len() == 0 { + save_file.initialize()?; + } else { + save_file.load_data()?; + } + Ok(save_file) +} pub fn initialize_from_args( all_storages: AllStoragesView, diff --git a/kubi/src/lib.rs b/kubi/src/lib.rs index c43306c..889d76d 100644 --- a/kubi/src/lib.rs +++ b/kubi/src/lib.rs @@ -1,16 +1,14 @@ #![allow(clippy::too_many_arguments)] // allowed because systems often need a lot of arguments use shipyard::{ - World, Workload, IntoWorkload, - UniqueView, UniqueViewMut, - NonSendSync, WorkloadModificator, + World, Workload, IntoWorkload, + UniqueView, UniqueViewMut, + NonSendSync, WorkloadModificator, SystemModificator }; -use glium::{ - glutin::{ - event_loop::{EventLoop, ControlFlow}, - event::{Event, WindowEvent} - } +use glium::glutin::{ + event_loop::{EventLoop, ControlFlow}, + event::{Event, WindowEvent} }; use glam::vec3; use std::time::Instant;