From 884551089cd2b64c12c9f8db81e5ca48468d17f5 Mon Sep 17 00:00:00 2001 From: griffi-gh Date: Tue, 3 Sep 2024 15:47:04 +0200 Subject: [PATCH] Initial server save file integration (NO SAVING YET, ONLY LOADING) --- Server.toml | 1 + kubi-server/src/config.rs | 3 +- kubi-server/src/main.rs | 2 + kubi-server/src/world.rs | 9 ++-- kubi-server/src/world/save.rs | 16 +++++++ kubi-server/src/world/tasks.rs | 48 ++++++++++++++++---- kubi-shared/src/data.rs | 21 ++++++++- kubi-shared/src/data/io_thread.rs | 9 ++++ {kubi => kubi-shared}/src/fixed_timestamp.rs | 0 kubi-shared/src/lib.rs | 1 + kubi/src/init.rs | 18 +------- kubi/src/lib.rs | 3 +- kubi/src/world/loading.rs | 2 +- 13 files changed, 100 insertions(+), 33 deletions(-) create mode 100644 kubi-server/src/world/save.rs rename {kubi => kubi-shared}/src/fixed_timestamp.rs (100%) diff --git a/Server.toml b/Server.toml index 48065e9..a1dc9c7 100644 --- a/Server.toml +++ b/Server.toml @@ -4,6 +4,7 @@ max_clients = 32 timeout_ms = 10000 [world] +file = "world.kubi" seed = 0xfeb_face_dead_cafe preheat_radius = 8 diff --git a/kubi-server/src/config.rs b/kubi-server/src/config.rs index 561403d..079e577 100644 --- a/kubi-server/src/config.rs +++ b/kubi-server/src/config.rs @@ -1,6 +1,6 @@ use shipyard::{AllStoragesView, Unique}; use serde::{Serialize, Deserialize}; -use std::{fs, net::SocketAddr}; +use std::{fs, net::SocketAddr, path::PathBuf}; #[derive(Serialize, Deserialize)] pub struct ConfigTableServer { @@ -12,6 +12,7 @@ pub struct ConfigTableServer { #[derive(Serialize, Deserialize)] pub struct ConfigTableWorld { + pub file: Option, pub seed: u64, pub preheat_radius: u32, } diff --git a/kubi-server/src/main.rs b/kubi-server/src/main.rs index 790388a..3f7f243 100644 --- a/kubi-server/src/main.rs +++ b/kubi-server/src/main.rs @@ -1,5 +1,6 @@ use shipyard::{IntoWorkload, Workload, WorkloadModificator, World}; use std::{thread, time::Duration}; +use kubi_shared::fixed_timestamp::init_fixed_timestamp_storage; mod util; mod config; @@ -16,6 +17,7 @@ use world::{update_world, init_world}; fn initialize() -> Workload { ( + init_fixed_timestamp_storage, read_config, bind_server, init_client_maps, diff --git a/kubi-server/src/world.rs b/kubi-server/src/world.rs index 841b11a..ecc7de8 100644 --- a/kubi-server/src/world.rs +++ b/kubi-server/src/world.rs @@ -24,12 +24,13 @@ use crate::{ pub mod chunk; pub mod tasks; +pub mod save; use chunk::Chunk; use self::{ tasks::{ChunkTaskManager, ChunkTask, ChunkTaskResponse, init_chunk_task_manager}, - chunk::ChunkState + chunk::ChunkState, }; #[derive(Unique, Default)] @@ -106,7 +107,7 @@ fn process_chunk_requests( chunk.state = ChunkState::Loading; chunk.subscriptions.insert(message.client_id); chunk_manager.chunks.insert(chunk_position, chunk); - task_manager.spawn_task(ChunkTask::LoadChunk { + task_manager.run(ChunkTask::LoadChunk { position: chunk_position, seed: config.world.seed, }); @@ -278,7 +279,7 @@ pub fn preheat_world( let mut chunk = Chunk::new(); chunk.state = ChunkState::Loading; chunk_manager.chunks.insert(chunk_position, chunk); - task_manager.spawn_task(ChunkTask::LoadChunk { + task_manager.run(ChunkTask::LoadChunk { position: chunk_position, seed: config.world.seed, }); @@ -292,7 +293,7 @@ pub fn init_world() -> Workload { init_chunk_manager_and_block_queue.before_all(preheat_world), init_chunk_task_manager.before_all(preheat_world), preheat_world, - ).into_workload() + ).into_sequential_workload() } pub fn update_world() -> Workload { diff --git a/kubi-server/src/world/save.rs b/kubi-server/src/world/save.rs new file mode 100644 index 0000000..834203e --- /dev/null +++ b/kubi-server/src/world/save.rs @@ -0,0 +1,16 @@ +use kubi_shared::data::{io_thread::IOThreadManager, open_local_save_file}; +use shipyard::{AllStoragesView, UniqueView}; + +use crate::config::ConfigTable; + +pub fn init_save_file(storages: &AllStoragesView) -> Option { + let config = storages.borrow::>().unwrap(); + if let Some(file_path) = &config.world.file { + log::info!("Initializing save file from {:?}", file_path); + let save = open_local_save_file(&file_path).unwrap(); + Some(IOThreadManager::new(save)) + } else { + log::warn!("No save file specified, world will not be saved"); + None + } +} diff --git a/kubi-server/src/world/tasks.rs b/kubi-server/src/world/tasks.rs index 40aa615..3ea5347 100644 --- a/kubi-server/src/world/tasks.rs +++ b/kubi-server/src/world/tasks.rs @@ -4,10 +4,9 @@ use glam::IVec3; use rayon::{ThreadPool, ThreadPoolBuilder}; use anyhow::Result; use kubi_shared::{ - chunk::BlockData, - worldgen::generate_world, - queue::QueuedBlock, + chunk::BlockData, data::io_thread::{IOCommand, IOResponse, IOThreadManager}, queue::QueuedBlock, worldgen::generate_world }; +use super::save::init_save_file; pub enum ChunkTask { LoadChunk { @@ -28,15 +27,30 @@ pub enum ChunkTaskResponse { pub struct ChunkTaskManager { channel: (Sender, Receiver), pool: ThreadPool, + iota: Option, } + impl ChunkTaskManager { - pub fn new() -> Result { + pub fn new(iota: Option) -> Result { Ok(Self { channel: unbounded(), - pool: ThreadPoolBuilder::new().build()? + pool: ThreadPoolBuilder::new().build()?, + iota, }) } - pub fn spawn_task(&self, task: ChunkTask) { + + pub fn run(&self, task: ChunkTask) { + // 1. Check if the chunk exists in the save file + #[allow(irrefutable_let_patterns)] + if let ChunkTask::LoadChunk { position, .. } = &task { + if let Some(iota) = &self.iota { + if iota.chunk_exists(*position) { + iota.send(IOCommand::LoadChunk { position: *position }); + } + } + } + + // 2. Generate the chunk if it doesn't exist let sender = self.channel.0.clone(); self.pool.spawn(move || { sender.send(match task { @@ -48,13 +62,31 @@ impl ChunkTaskManager { }).unwrap() }) } + pub fn receive(&self) -> Option { - self.channel.1.try_recv().ok() + // Try to receive IO results first + // If there are none, try to receive worldgen results + self.iota.as_ref().map(|iota| { + iota.poll_single().map(|response| match response { + IOResponse::ChunkLoaded { position, data } => ChunkTaskResponse::ChunkLoaded { + chunk_position: position, + blocks: data.expect("chunk data exists in the header, but was not loaded"), + queue: Vec::with_capacity(0) + }, + _ => panic!("Unexpected response from IO thread"), + }) + }).flatten().or_else(|| { + self.channel.1.try_recv().ok() + }) } } pub fn init_chunk_task_manager( storages: AllStoragesView ) { - storages.add_unique(ChunkTaskManager::new().expect("ChunkTaskManager Init failed")); + let iota = init_save_file(&storages); + storages.add_unique( + ChunkTaskManager::new(iota) + .expect("ChunkTaskManager Init failed") + ); } diff --git a/kubi-shared/src/data.rs b/kubi-shared/src/data.rs index 8c0ae41..1274130 100644 --- a/kubi-shared/src/data.rs +++ b/kubi-shared/src/data.rs @@ -1,7 +1,8 @@ use std::{ - fs::File, mem::size_of, + fs::{File, OpenOptions}, io::{Read, Seek, SeekFrom, Write}, + path::Path, borrow::Cow, sync::{Arc, RwLock} }; @@ -182,3 +183,21 @@ impl WorldSaveFile { Arc::clone(&self.header) } } + +/// Utility function to open a local save file, creating it if it doesn't exist +pub fn open_local_save_file(path: &Path) -> Result { + let mut save_file = WorldSaveFile::new({ + OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(path)? + }); + if save_file.file.metadata().unwrap().len() == 0 { + save_file.initialize()?; + } else { + save_file.load_data()?; + } + Ok(save_file) +} + diff --git a/kubi-shared/src/data/io_thread.rs b/kubi-shared/src/data/io_thread.rs index 5991d40..eac20fd 100644 --- a/kubi-shared/src/data/io_thread.rs +++ b/kubi-shared/src/data/io_thread.rs @@ -172,6 +172,11 @@ impl IOSingleThread { self.tx.send(cmd).unwrap(); } + /// Poll the IO thread for a single response (non-blocking) + pub fn poll_single(&self) -> Option { + self.rx.try_recv().ok() + } + /// Poll the IO thread for responses (non-blocking) pub fn poll(&self) -> TryIter { self.rx.try_iter() @@ -227,6 +232,10 @@ impl IOThreadManager { self.thread.send(cmd); } + pub fn poll_single(&self) -> Option { + self.thread.poll_single() + } + pub fn poll(&self) -> TryIter { self.thread.poll() } diff --git a/kubi/src/fixed_timestamp.rs b/kubi-shared/src/fixed_timestamp.rs similarity index 100% rename from kubi/src/fixed_timestamp.rs rename to kubi-shared/src/fixed_timestamp.rs diff --git a/kubi-shared/src/lib.rs b/kubi-shared/src/lib.rs index f262514..0c8b4c3 100644 --- a/kubi-shared/src/lib.rs +++ b/kubi-shared/src/lib.rs @@ -8,3 +8,4 @@ pub mod entity; pub mod player; pub mod queue; pub mod data; +pub mod fixed_timestamp; diff --git a/kubi/src/init.rs b/kubi/src/init.rs index 009e9c4..fba8a51 100644 --- a/kubi/src/init.rs +++ b/kubi/src/init.rs @@ -5,23 +5,7 @@ use crate::{ networking::{GameType, ServerAddress}, state::{GameState, NextState} }; -use kubi_shared::data::{io_thread::IOThreadManager, WorldSaveFile}; - -fn open_local_save_file(path: &Path) -> Result { - let mut save_file = WorldSaveFile::new({ - OpenOptions::new() - .read(true) - .write(true) - .create(true) - .open(path)? - }); - if save_file.file.metadata().unwrap().len() == 0 { - save_file.initialize()?; - } else { - save_file.load_data()?; - } - Ok(save_file) -} +use kubi_shared::data::{io_thread::IOThreadManager, WorldSaveFile, open_local_save_file}; pub fn initialize_from_args( all_storages: AllStoragesView, diff --git a/kubi/src/lib.rs b/kubi/src/lib.rs index 2a9ab45..27c7074 100644 --- a/kubi/src/lib.rs +++ b/kubi/src/lib.rs @@ -23,7 +23,9 @@ use winit::{ use glam::vec3; use std::time::Instant; +//TODO remove these re-exports pub(crate) use kubi_shared::transform; +pub(crate) use kubi_shared::fixed_timestamp; mod ui; pub(crate) use ui::{ @@ -51,7 +53,6 @@ pub(crate) mod hui_integration; pub(crate) mod networking; pub(crate) mod init; pub(crate) mod color; -pub(crate) mod fixed_timestamp; pub(crate) mod filesystem; pub(crate) mod client_physics; pub(crate) mod chat; diff --git a/kubi/src/world/loading.rs b/kubi/src/world/loading.rs index c06afbf..9a42f75 100644 --- a/kubi/src/world/loading.rs +++ b/kubi/src/world/loading.rs @@ -24,7 +24,7 @@ use super::{ queue::BlockUpdateQueue, }; -const WORLD_SEED: u64 = 0xbeef_face_dead_cafe; +const WORLD_SEED: u64 = 0xfeb_face_dead_cafe; const MAX_CHUNK_OPS_INGAME: usize = 8; const MAX_CHUNK_OPS: usize = 32;