mirror of
https://github.com/griffi-gh/kubi.git
synced 2024-12-23 04:18:21 -06:00
Initial server save file integration (NO SAVING YET, ONLY LOADING)
This commit is contained in:
parent
37e68912eb
commit
884551089c
|
@ -4,6 +4,7 @@ max_clients = 32
|
|||
timeout_ms = 10000
|
||||
|
||||
[world]
|
||||
file = "world.kubi"
|
||||
seed = 0xfeb_face_dead_cafe
|
||||
preheat_radius = 8
|
||||
|
||||
|
|
|
@ -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<PathBuf>,
|
||||
pub seed: u64,
|
||||
pub preheat_radius: u32,
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
16
kubi-server/src/world/save.rs
Normal file
16
kubi-server/src/world/save.rs
Normal file
|
@ -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<IOThreadManager> {
|
||||
let config = storages.borrow::<UniqueView<ConfigTable>>().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
|
||||
}
|
||||
}
|
|
@ -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<ChunkTaskResponse>, Receiver<ChunkTaskResponse>),
|
||||
pool: ThreadPool,
|
||||
iota: Option<IOThreadManager>,
|
||||
}
|
||||
|
||||
impl ChunkTaskManager {
|
||||
pub fn new() -> Result<Self> {
|
||||
pub fn new(iota: Option<IOThreadManager>) -> Result<Self> {
|
||||
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<ChunkTaskResponse> {
|
||||
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")
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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<WorldSaveFile> {
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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<IOResponse> {
|
||||
self.rx.try_recv().ok()
|
||||
}
|
||||
|
||||
/// Poll the IO thread for responses (non-blocking)
|
||||
pub fn poll(&self) -> TryIter<IOResponse> {
|
||||
self.rx.try_iter()
|
||||
|
@ -227,6 +232,10 @@ impl IOThreadManager {
|
|||
self.thread.send(cmd);
|
||||
}
|
||||
|
||||
pub fn poll_single(&self) -> Option<IOResponse> {
|
||||
self.thread.poll_single()
|
||||
}
|
||||
|
||||
pub fn poll(&self) -> TryIter<IOResponse> {
|
||||
self.thread.poll()
|
||||
}
|
||||
|
|
|
@ -8,3 +8,4 @@ pub mod entity;
|
|||
pub mod player;
|
||||
pub mod queue;
|
||||
pub mod data;
|
||||
pub mod fixed_timestamp;
|
||||
|
|
|
@ -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<WorldSaveFile> {
|
||||
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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue