mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added chunk data versioning system
This commit is contained in:
parent
5e29847e12
commit
9fcd68acf6
@ -4,9 +4,10 @@ use common::{
|
|||||||
vol::RectRasterableVol,
|
vol::RectRasterableVol,
|
||||||
};
|
};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
use tracing::{info, error};
|
use tracing::{info, error, warn};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use std::{
|
use std::{
|
||||||
|
io::{self, Read as _, Write as _},
|
||||||
fs::File,
|
fs::File,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
};
|
};
|
||||||
@ -33,29 +34,57 @@ impl Default for TerrainPersistence {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TerrainPersistence {
|
impl TerrainPersistence {
|
||||||
fn path_for(&self, key: Vec2<i32>) -> PathBuf {
|
fn path_for(&self, key: Vec2<i32>, ext: &str) -> PathBuf {
|
||||||
let mut path = self.path.clone();
|
let mut path = self.path.clone();
|
||||||
path.push(format!("chunk_{}_{}.dat", key.x, key.y));
|
path.push(format!("chunk_{}_{}.{}", key.x, key.y, ext));
|
||||||
path
|
path
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_chunk(&mut self, key: Vec2<i32>) -> &mut Chunk {
|
pub fn load_chunk(&mut self, key: Vec2<i32>) -> &mut Chunk {
|
||||||
let path = self.path_for(key);
|
let path = self.path_for(key, "dat");
|
||||||
|
let backup_path = self.path_for(key, "dat.backup");
|
||||||
self.chunks
|
self.chunks
|
||||||
.entry(key)
|
.entry(key)
|
||||||
.or_insert_with(|| {
|
.or_insert_with(|| {
|
||||||
File::open(path)
|
File::open(&path)
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|f| bincode::deserialize_from(&f).ok())
|
.map(|f| {
|
||||||
|
let bytes = match std::io::BufReader::new(f).bytes().collect::<Result<Vec<_>, _>>() {
|
||||||
|
Ok(bytes) => bytes,
|
||||||
|
Err(err) => {
|
||||||
|
error!("Failed to load chunk {:?} from disk: {:?}", key, err);
|
||||||
|
return Chunk::default();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
match Chunk::deserialize_from(std::io::Cursor::new(bytes)) {
|
||||||
|
Some(chunk) => chunk,
|
||||||
|
None => {
|
||||||
|
error!("Failed to load chunk {:?}, moving to {:?} instead", key, backup_path);
|
||||||
|
if let Err(err) = std::fs::copy(&path, backup_path)
|
||||||
|
.and_then(|_| std::fs::remove_file(&path))
|
||||||
|
{
|
||||||
|
error!("{:?}", err);
|
||||||
|
}
|
||||||
|
Chunk::default()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
.unwrap_or_else(|| Chunk::default())
|
.unwrap_or_else(|| Chunk::default())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unload_chunk(&mut self, key: Vec2<i32>) {
|
pub fn unload_chunk(&mut self, key: Vec2<i32>) {
|
||||||
if let Some(chunk) = self.chunks.remove(&key) {
|
if let Some(chunk) = self.chunks.remove(&key) {
|
||||||
if chunk.blocks.len() > 0 {
|
if chunk.blocks.len() > 0 { // No need to write if no blocks have ever been written
|
||||||
match File::create(self.path_for(key)) {
|
match File::create(self.path_for(key, "dat")) {
|
||||||
Ok(file) => { bincode::serialize_into(file, &chunk); },
|
Ok(file) => {
|
||||||
|
let mut writer = std::io::BufWriter::new(file);
|
||||||
|
if let Err(err) = bincode::serialize_into::<_, version::Current>(&mut writer, &chunk.prepare())
|
||||||
|
.and_then(|_| Ok(writer.flush()?))
|
||||||
|
{
|
||||||
|
error!("Failed to write chunk to disk: {:?}", err);
|
||||||
|
}
|
||||||
|
},
|
||||||
Err(err) => error!("Failed to create file: {:?}", err),
|
Err(err) => error!("Failed to create file: {:?}", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,7 +109,80 @@ pub struct Chunk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Chunk {
|
impl Chunk {
|
||||||
|
pub fn deserialize_from<R: io::Read + Clone>(reader: R) -> Option<Self> {
|
||||||
|
// Attempt deserialization through various versions
|
||||||
|
if let Ok(data) = bincode::deserialize_from::<_, version::V2>(reader.clone())
|
||||||
|
.map_err(|err| { warn!("Error when loading V2: {:?}", err); err })
|
||||||
|
{
|
||||||
|
Some(Chunk::from(data))
|
||||||
|
} else if let Ok(data) = bincode::deserialize_from::<_, Chunk>(reader.clone())
|
||||||
|
.map_err(|err| { warn!("Error when loading V1: {:?}", err); err })
|
||||||
|
{
|
||||||
|
Some(Chunk::from(data))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare(self) -> version::Current { self.into() }
|
||||||
|
|
||||||
pub fn blocks(&self) -> impl Iterator<Item=(Vec3<i32>, Block)> + '_ {
|
pub fn blocks(&self) -> impl Iterator<Item=(Vec3<i32>, Block)> + '_ {
|
||||||
self.blocks.iter().map(|(k, b)| (*k, *b))
|
self.blocks.iter().map(|(k, b)| (*k, *b))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod version {
|
||||||
|
pub type Current = V2;
|
||||||
|
|
||||||
|
fn version_magic(n: u16) -> u64 {
|
||||||
|
(n as u64) | (0x3352ACEEA789 << 16)
|
||||||
|
}
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// Convert back to current
|
||||||
|
|
||||||
|
impl From<Chunk> for Current {
|
||||||
|
fn from(chunk: Chunk) -> Self {
|
||||||
|
Self { version: version_magic(2), blocks: chunk.blocks
|
||||||
|
.into_iter()
|
||||||
|
.map(|(pos, b)| (pos.x as u8, pos.y as u8, pos.z as i16, b))
|
||||||
|
.collect() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// V1
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct V1 {
|
||||||
|
pub blocks: HashMap<Vec3<i32>, Block>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<V1> for Chunk {
|
||||||
|
fn from(v1: V1) -> Self { Self { blocks: v1.blocks } }
|
||||||
|
}
|
||||||
|
|
||||||
|
// V2
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct V2 {
|
||||||
|
#[serde(deserialize_with = "version::<_, 2>")]
|
||||||
|
pub version: u64,
|
||||||
|
pub blocks: Vec<(u8, u8, i16, Block)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn version<'de, D: serde::Deserializer<'de>, const V: u16>(de: D) -> Result<u64, D::Error> {
|
||||||
|
u64::deserialize(de).and_then(|x| if x == version_magic(V) {
|
||||||
|
Ok(x)
|
||||||
|
} else {
|
||||||
|
Err(serde::de::Error::invalid_value(serde::de::Unexpected::Unsigned(x), &"correct version"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<V2> for Chunk {
|
||||||
|
fn from(v2: V2) -> Self { Self { blocks: v2.blocks
|
||||||
|
.into_iter()
|
||||||
|
.map(|(x, y, z, b)| (Vec3::new(x as i32, y as i32, z as i32), b))
|
||||||
|
.collect() } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user