From 1b89756648ab9c70e4dcd350780e1b9e15ba46c5 Mon Sep 17 00:00:00 2001 From: griffi-gh Date: Wed, 11 Sep 2024 11:28:37 +0200 Subject: [PATCH] Implement saving on the server-side --- kubi-server/src/client.rs | 2 +- kubi-server/src/main.rs | 11 +++++--- kubi-server/src/world.rs | 6 +++- kubi-server/src/world/chunk.rs | 3 ++ kubi-server/src/world/save.rs | 31 +++++++++++++++++++-- kubi-server/src/world/tasks.rs | 50 +++++++++++++++++++++------------- kubi/src/world/queue.rs | 3 ++ 7 files changed, 79 insertions(+), 27 deletions(-) diff --git a/kubi-server/src/client.rs b/kubi-server/src/client.rs index 2b60bf6..d4d2121 100644 --- a/kubi-server/src/client.rs +++ b/kubi-server/src/client.rs @@ -50,7 +50,7 @@ pub fn sync_client_positions( }; //log movement (annoying duh) - log::debug!("dbg: player moved id: {} coords: {} quat: {}", message.client_id, position, direction); + // log::debug!("dbg: player moved id: {} coords: {} quat: {}", message.client_id, position, direction); //Apply position to server-side client let mut trans = (&mut transforms).get(message.entity_id).unwrap(); diff --git a/kubi-server/src/main.rs b/kubi-server/src/main.rs index 3f7f243..eca8753 100644 --- a/kubi-server/src/main.rs +++ b/kubi-server/src/main.rs @@ -1,6 +1,6 @@ -use shipyard::{IntoWorkload, Workload, WorkloadModificator, World}; +use shipyard::{IntoWorkload, SystemModificator, Workload, WorkloadModificator, World}; use std::{thread, time::Duration}; -use kubi_shared::fixed_timestamp::init_fixed_timestamp_storage; +use kubi_shared::fixed_timestamp::{FixedTimestamp, init_fixed_timestamp_storage}; mod util; mod config; @@ -13,7 +13,7 @@ use config::read_config; use server::{bind_server, update_server, log_server_errors}; use client::{init_client_maps, on_client_disconnect, sync_client_positions}; use auth::authenticate_players; -use world::{update_world, init_world}; +use world::{init_world, save::save_modified, update_world}; fn initialize() -> Workload { ( @@ -34,7 +34,10 @@ fn update() -> Workload { update_world, sync_client_positions, on_client_disconnect, - ).into_workload() + ).into_workload(), + save_modified + .into_workload() + .make_fixed(10000, 0), ).into_sequential_workload() } diff --git a/kubi-server/src/world.rs b/kubi-server/src/world.rs index ecc7de8..0e05b27 100644 --- a/kubi-server/src/world.rs +++ b/kubi-server/src/world.rs @@ -250,7 +250,11 @@ fn process_block_queue( let Some(blocks) = &mut chunk.blocks else { return true }; - blocks[block_position.x as usize][block_position.y as usize][block_position.z as usize] = item.block_type; + let block = &mut blocks[block_position.x as usize][block_position.y as usize][block_position.z as usize]; + if item.block_type != *block { + *block = item.block_type; + chunk.data_modified = true; + } false }); if initial_len != queue.queue.len() { diff --git a/kubi-server/src/world/chunk.rs b/kubi-server/src/world/chunk.rs index 7287d19..3fdce80 100644 --- a/kubi-server/src/world/chunk.rs +++ b/kubi-server/src/world/chunk.rs @@ -16,13 +16,16 @@ pub struct Chunk { pub state: ChunkState, pub blocks: Option, pub subscriptions: HashSet>, + pub data_modified: bool, } + impl Chunk { pub fn new() -> Self { Self { state: ChunkState::Nothing, blocks: None, subscriptions: HashSet::with_capacity_and_hasher(4, BuildNoHashHasher::default()), + data_modified: false, } } } diff --git a/kubi-server/src/world/save.rs b/kubi-server/src/world/save.rs index 834203e..9fda708 100644 --- a/kubi-server/src/world/save.rs +++ b/kubi-server/src/world/save.rs @@ -1,7 +1,10 @@ use kubi_shared::data::{io_thread::IOThreadManager, open_local_save_file}; -use shipyard::{AllStoragesView, UniqueView}; - +use shipyard::{AllStoragesView, UniqueView, UniqueViewMut}; use crate::config::ConfigTable; +use super::{ + tasks::{ChunkTask, ChunkTaskManager}, + ChunkManager, +}; pub fn init_save_file(storages: &AllStoragesView) -> Option { let config = storages.borrow::>().unwrap(); @@ -14,3 +17,27 @@ pub fn init_save_file(storages: &AllStoragesView) -> Option { None } } + +pub fn save_modified( + mut chunks: UniqueViewMut, + ctm: UniqueView, +) { + log::info!("Saving..."); + let mut amount_saved = 0; + for (position, chunk) in chunks.chunks.iter_mut() { + if chunk.data_modified { + let Some(data) = chunk.blocks.clone() else { + continue + }; + ctm.run(ChunkTask::SaveChunk { + position: *position, + data: data, + }); + chunk.data_modified = false; + amount_saved += 1; + } + } + if amount_saved > 0 { + log::info!("Queued {} chunks for saving", amount_saved); + } +} \ No newline at end of file diff --git a/kubi-server/src/world/tasks.rs b/kubi-server/src/world/tasks.rs index 3ea5347..cae2d56 100644 --- a/kubi-server/src/world/tasks.rs +++ b/kubi-server/src/world/tasks.rs @@ -12,7 +12,11 @@ pub enum ChunkTask { LoadChunk { position: IVec3, seed: u64, - } + }, + SaveChunk { + position: IVec3, + data: BlockData, + }, } pub enum ChunkTaskResponse { @@ -40,27 +44,35 @@ impl ChunkTaskManager { } 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 }); + match task { + ChunkTask::LoadChunk { position: chunk_position, seed } => { + // 1. Check if the chunk exists in the save file + if let ChunkTask::LoadChunk { position, .. } = &task { + if let Some(iota) = &self.iota { + if iota.chunk_exists(*position) { + iota.send(IOCommand::LoadChunk { position: *position }); + return + } + } } - } - } - // 2. Generate the chunk if it doesn't exist - let sender = self.channel.0.clone(); - self.pool.spawn(move || { - sender.send(match task { - ChunkTask::LoadChunk { position: chunk_position, seed } => { - //unwrap is fine because abort is not possible - let (blocks, queue) = generate_world(chunk_position, seed, None).unwrap(); - ChunkTaskResponse::ChunkLoaded { chunk_position, blocks, queue } + // 2. Generate the chunk if it doesn't exist + let sender = self.channel.0.clone(); + self.pool.spawn(move || { + sender.send({ + //unwrap is fine because abort is not possible + let (blocks, queue) = generate_world(chunk_position, seed, None).unwrap(); + ChunkTaskResponse::ChunkLoaded { chunk_position, blocks, queue } + }).unwrap() + }); + }, + ChunkTask::SaveChunk { position, data } => { + // Save the chunk to the save file + if let Some(iota) = &self.iota { + iota.send(IOCommand::SaveChunk { position, data }); } - }).unwrap() - }) + }, + } } pub fn receive(&self) -> Option { diff --git a/kubi/src/world/queue.rs b/kubi/src/world/queue.rs index 8f46335..100d380 100644 --- a/kubi/src/world/queue.rs +++ b/kubi/src/world/queue.rs @@ -22,6 +22,9 @@ pub fn apply_queued_blocks( if event.soft && *block != Block::Air { return false } + if event.block_type == *block { + return false + } *block = event.block_type; //mark chunk as dirty let (chunk_pos, block_pos) = ChunkStorage::to_chunk_coords(event.position);