mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added simple, dumb chunk persistence
This commit is contained in:
parent
54eb2a3ff7
commit
86ea5444ab
@ -26,6 +26,7 @@ network = { package = "veloren-network", path = "../network", features = ["metri
|
|||||||
specs = { git = "https://github.com/amethyst/specs.git", features = ["shred-derive"], rev = "f985bec5d456f7b0dd8aae99848f9473c2cd9d46" }
|
specs = { git = "https://github.com/amethyst/specs.git", features = ["shred-derive"], rev = "f985bec5d456f7b0dd8aae99848f9473c2cd9d46" }
|
||||||
specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", rev = "8be2abcddf8f524cb5876e8dd20a7e47cfaf7573" }
|
specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", rev = "8be2abcddf8f524cb5876e8dd20a7e47cfaf7573" }
|
||||||
|
|
||||||
|
bincode = "1.3.2"
|
||||||
num_cpus = "1.0"
|
num_cpus = "1.0"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
vek = { version = "0.14.1", features = ["serde"] }
|
vek = { version = "0.14.1", features = ["serde"] }
|
||||||
|
@ -32,6 +32,7 @@ pub mod state_ext;
|
|||||||
pub mod sys;
|
pub mod sys;
|
||||||
#[cfg(not(feature = "worldgen"))] mod test_world;
|
#[cfg(not(feature = "worldgen"))] mod test_world;
|
||||||
pub mod wiring;
|
pub mod wiring;
|
||||||
|
pub mod terrain_persistence;
|
||||||
|
|
||||||
// Reexports
|
// Reexports
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
@ -54,6 +55,7 @@ use crate::{
|
|||||||
rtsim::RtSim,
|
rtsim::RtSim,
|
||||||
state_ext::StateExt,
|
state_ext::StateExt,
|
||||||
sys::sentinel::{DeletedEntities, TrackedComps},
|
sys::sentinel::{DeletedEntities, TrackedComps},
|
||||||
|
terrain_persistence::TerrainPersistence,
|
||||||
};
|
};
|
||||||
#[cfg(not(feature = "worldgen"))]
|
#[cfg(not(feature = "worldgen"))]
|
||||||
use common::grid::Grid;
|
use common::grid::Grid;
|
||||||
@ -213,6 +215,7 @@ impl Server {
|
|||||||
state.ecs_mut().insert(ecs_system_metrics);
|
state.ecs_mut().insert(ecs_system_metrics);
|
||||||
state.ecs_mut().insert(tick_metrics);
|
state.ecs_mut().insert(tick_metrics);
|
||||||
state.ecs_mut().insert(physics_metrics);
|
state.ecs_mut().insert(physics_metrics);
|
||||||
|
state.ecs_mut().insert(TerrainPersistence::default());
|
||||||
state
|
state
|
||||||
.ecs_mut()
|
.ecs_mut()
|
||||||
.write_resource::<SlowJobPool>()
|
.write_resource::<SlowJobPool>()
|
||||||
@ -823,6 +826,8 @@ impl Server {
|
|||||||
pub fn cleanup(&mut self) {
|
pub fn cleanup(&mut self) {
|
||||||
// Cleanup the local state
|
// Cleanup the local state
|
||||||
self.state.cleanup();
|
self.state.cleanup();
|
||||||
|
|
||||||
|
self.state.ecs().write_resource::<TerrainPersistence>().unload_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_client(
|
fn initialize_client(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{client::Client, presence::Presence, Settings};
|
use crate::{client::Client, presence::Presence, Settings, TerrainPersistence};
|
||||||
use common::{
|
use common::{
|
||||||
comp::{
|
comp::{
|
||||||
Admin, CanBuild, ControlEvent, Controller, ForceUpdate, Health, Ori, Player, Pos, SkillSet,
|
Admin, CanBuild, ControlEvent, Controller, ForceUpdate, Health, Ori, Player, Pos, SkillSet,
|
||||||
@ -36,6 +36,7 @@ impl Sys {
|
|||||||
settings: &Read<'_, Settings>,
|
settings: &Read<'_, Settings>,
|
||||||
build_areas: &Read<'_, BuildAreas>,
|
build_areas: &Read<'_, BuildAreas>,
|
||||||
player_physics_settings: &mut Write<'_, PlayerPhysicsSettings>,
|
player_physics_settings: &mut Write<'_, PlayerPhysicsSettings>,
|
||||||
|
terrain_persistence: &mut Write<'_, TerrainPersistence>,
|
||||||
maybe_player: &Option<&Player>,
|
maybe_player: &Option<&Player>,
|
||||||
maybe_admin: &Option<&Admin>,
|
maybe_admin: &Option<&Admin>,
|
||||||
msg: ClientGeneral,
|
msg: ClientGeneral,
|
||||||
@ -198,7 +199,10 @@ impl Sys {
|
|||||||
.filter(|aabb| aabb.contains_point(pos))
|
.filter(|aabb| aabb.contains_point(pos))
|
||||||
.and_then(|_| terrain.get(pos).ok())
|
.and_then(|_| terrain.get(pos).ok())
|
||||||
{
|
{
|
||||||
block_changes.set(pos, block.into_vacant());
|
let block = block.into_vacant();
|
||||||
|
block_changes.set(pos, block);
|
||||||
|
// TODO: Only modify if succeeded
|
||||||
|
terrain_persistence.set_block(pos, block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,6 +221,8 @@ impl Sys {
|
|||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
block_changes.try_set(pos, block);
|
block_changes.try_set(pos, block);
|
||||||
|
// TODO: Only modify if succeeded
|
||||||
|
terrain_persistence.set_block(pos, block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -287,6 +293,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
Read<'a, Settings>,
|
Read<'a, Settings>,
|
||||||
Read<'a, BuildAreas>,
|
Read<'a, BuildAreas>,
|
||||||
Write<'a, PlayerPhysicsSettings>,
|
Write<'a, PlayerPhysicsSettings>,
|
||||||
|
Write<'a, TerrainPersistence>,
|
||||||
ReadStorage<'a, Player>,
|
ReadStorage<'a, Player>,
|
||||||
ReadStorage<'a, Admin>,
|
ReadStorage<'a, Admin>,
|
||||||
);
|
);
|
||||||
@ -315,6 +322,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
settings,
|
settings,
|
||||||
build_areas,
|
build_areas,
|
||||||
mut player_physics_settings,
|
mut player_physics_settings,
|
||||||
|
mut terrain_persistence,
|
||||||
players,
|
players,
|
||||||
admins,
|
admins,
|
||||||
): Self::SystemData,
|
): Self::SystemData,
|
||||||
@ -349,6 +357,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
&settings,
|
&settings,
|
||||||
&build_areas,
|
&build_areas,
|
||||||
&mut player_physics_settings,
|
&mut player_physics_settings,
|
||||||
|
&mut terrain_persistence,
|
||||||
&player,
|
&player,
|
||||||
&maybe_admin,
|
&maybe_admin,
|
||||||
msg,
|
msg,
|
||||||
|
@ -7,6 +7,7 @@ use crate::{
|
|||||||
settings::Settings,
|
settings::Settings,
|
||||||
SpawnPoint, Tick,
|
SpawnPoint, Tick,
|
||||||
};
|
};
|
||||||
|
use crate::TerrainPersistence;
|
||||||
use common::{
|
use common::{
|
||||||
comp::{self, agent, bird_medium, Alignment, BehaviorCapability, ForceUpdate, Pos},
|
comp::{self, agent, bird_medium, Alignment, BehaviorCapability, ForceUpdate, Pos},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
@ -14,6 +15,7 @@ use common::{
|
|||||||
npc::NPC_NAMES,
|
npc::NPC_NAMES,
|
||||||
terrain::TerrainGrid,
|
terrain::TerrainGrid,
|
||||||
LoadoutBuilder, SkillSetBuilder,
|
LoadoutBuilder, SkillSetBuilder,
|
||||||
|
vol::WriteVol,
|
||||||
};
|
};
|
||||||
use common_ecs::{Job, Origin, Phase, System};
|
use common_ecs::{Job, Origin, Phase, System};
|
||||||
use common_net::msg::{SerializedTerrainChunk, ServerGeneral};
|
use common_net::msg::{SerializedTerrainChunk, ServerGeneral};
|
||||||
@ -98,6 +100,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
WriteExpect<'a, TerrainGrid>,
|
WriteExpect<'a, TerrainGrid>,
|
||||||
Write<'a, TerrainChanges>,
|
Write<'a, TerrainChanges>,
|
||||||
WriteExpect<'a, RtSim>,
|
WriteExpect<'a, RtSim>,
|
||||||
|
WriteExpect<'a, TerrainPersistence>,
|
||||||
WriteStorage<'a, Pos>,
|
WriteStorage<'a, Pos>,
|
||||||
ReadStorage<'a, Presence>,
|
ReadStorage<'a, Presence>,
|
||||||
ReadStorage<'a, Client>,
|
ReadStorage<'a, Client>,
|
||||||
@ -122,6 +125,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
mut terrain,
|
mut terrain,
|
||||||
mut terrain_changes,
|
mut terrain_changes,
|
||||||
mut rtsim,
|
mut rtsim,
|
||||||
|
mut terrain_persistence,
|
||||||
mut positions,
|
mut positions,
|
||||||
presences,
|
presences,
|
||||||
clients,
|
clients,
|
||||||
@ -136,7 +140,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
// Also, send the chunk data to anybody that is close by.
|
// Also, send the chunk data to anybody that is close by.
|
||||||
let mut new_chunks = Vec::new();
|
let mut new_chunks = Vec::new();
|
||||||
'insert_terrain_chunks: while let Some((key, res)) = chunk_generator.recv_new_chunk() {
|
'insert_terrain_chunks: while let Some((key, res)) = chunk_generator.recv_new_chunk() {
|
||||||
let (chunk, supplement) = match res {
|
let (mut chunk, supplement) = match res {
|
||||||
Ok((chunk, supplement)) => (chunk, supplement),
|
Ok((chunk, supplement)) => (chunk, supplement),
|
||||||
Err(Some(entity)) => {
|
Err(Some(entity)) => {
|
||||||
if let Some(client) = clients.get(entity) {
|
if let Some(client) = clients.get(entity) {
|
||||||
@ -152,6 +156,10 @@ impl<'a> System<'a> for Sys {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
for (key, block) in terrain_persistence.load_chunk(key).blocks() {
|
||||||
|
chunk.set(key, block);
|
||||||
|
}
|
||||||
|
|
||||||
// Arcify the chunk
|
// Arcify the chunk
|
||||||
let chunk = Arc::new(chunk);
|
let chunk = Arc::new(chunk);
|
||||||
|
|
||||||
@ -391,6 +399,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
});
|
});
|
||||||
|
|
||||||
for key in chunks_to_remove {
|
for key in chunks_to_remove {
|
||||||
|
terrain_persistence.unload_chunk(key);
|
||||||
|
|
||||||
// TODO: code duplication for chunk insertion between here and state.rs
|
// TODO: code duplication for chunk insertion between here and state.rs
|
||||||
if terrain.remove(key).is_some() {
|
if terrain.remove(key).is_some() {
|
||||||
terrain_changes.removed_chunks.insert(key);
|
terrain_changes.removed_chunks.insert(key);
|
||||||
|
86
server/src/terrain_persistence.rs
Normal file
86
server/src/terrain_persistence.rs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
use hashbrown::HashMap;
|
||||||
|
use common::{
|
||||||
|
terrain::{Block, TerrainChunk},
|
||||||
|
vol::RectRasterableVol,
|
||||||
|
};
|
||||||
|
use vek::*;
|
||||||
|
use tracing::{info, error};
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
path::PathBuf,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct TerrainPersistence {
|
||||||
|
path: PathBuf,
|
||||||
|
chunks: HashMap<Vec2<i32>, Chunk>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TerrainPersistence {
|
||||||
|
fn default() -> Self {
|
||||||
|
let mut path = PathBuf::from(std::env::var("VELOREN_TERRAIN_DATA").unwrap_or_else(|_| String::new()));
|
||||||
|
path.push("chunks");
|
||||||
|
|
||||||
|
std::fs::create_dir_all(&path).unwrap();
|
||||||
|
|
||||||
|
info!("Using {:?} as the terrain persistence path", path);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
path,
|
||||||
|
chunks: HashMap::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TerrainPersistence {
|
||||||
|
fn path_for(&self, key: Vec2<i32>) -> PathBuf {
|
||||||
|
let mut path = self.path.clone();
|
||||||
|
path.push(format!("chunk_{}_{}.dat", key.x, key.y));
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_chunk(&mut self, key: Vec2<i32>) -> &mut Chunk {
|
||||||
|
let path = self.path_for(key);
|
||||||
|
self.chunks
|
||||||
|
.entry(key)
|
||||||
|
.or_insert_with(|| {
|
||||||
|
File::open(path)
|
||||||
|
.ok()
|
||||||
|
.and_then(|f| bincode::deserialize_from(&f).ok())
|
||||||
|
.unwrap_or_else(|| Chunk::default())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unload_chunk(&mut self, key: Vec2<i32>) {
|
||||||
|
if let Some(chunk) = self.chunks.remove(&key) {
|
||||||
|
if chunk.blocks.len() > 0 {
|
||||||
|
match File::create(self.path_for(key)) {
|
||||||
|
Ok(file) => { bincode::serialize_into(file, &chunk); },
|
||||||
|
Err(err) => error!("Failed to create file: {:?}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unload_all(&mut self) {
|
||||||
|
for key in self.chunks.keys().copied().collect::<Vec<_>>() {
|
||||||
|
self.unload_chunk(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_block(&mut self, pos: Vec3<i32>, block: Block) {
|
||||||
|
let key = pos.xy().map2(TerrainChunk::RECT_SIZE, |e, sz| e.div_euclid(sz as i32));
|
||||||
|
self.load_chunk(key).blocks.insert(pos - key * TerrainChunk::RECT_SIZE.map(|e| e as i32), block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Serialize, Deserialize)]
|
||||||
|
pub struct Chunk {
|
||||||
|
blocks: HashMap<Vec3<i32>, Block>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Chunk {
|
||||||
|
pub fn blocks(&self) -> impl Iterator<Item=(Vec3<i32>, Block)> + '_ {
|
||||||
|
self.blocks.iter().map(|(k, b)| (*k, *b))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user