Snapshot und Zustandsprüfsumme
Der Snapshot enthält den vollständigen Zustand der Welt hinter einem Kopf mit Format und Version. Er dient als Speicherstand und als Grundlage der Prüfsumme. Das Konzept ist entsprechend korrigiert: Der Speicherstand ist der vollständige Zustand, nicht Startzahl plus Änderungen. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,4 +14,5 @@ pub mod map;
|
||||
pub mod mapgen;
|
||||
pub mod player;
|
||||
pub mod rng;
|
||||
pub mod snapshot;
|
||||
pub mod world;
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
//! Complete, versioned encoding of a world. Saves, joining players and state
|
||||
//! checksums all use it.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use crate::codec::{DecodeError, Reader, Writer};
|
||||
use crate::hash::fnv1a64;
|
||||
use crate::map::Map;
|
||||
use crate::player::{Player, MAX_KNOWN_PLAYERS};
|
||||
use crate::world::World;
|
||||
|
||||
const MAGIC: [u8; 4] = *b"GRWS";
|
||||
/// Version of the snapshot format. Raise it whenever the encoded state
|
||||
/// changes.
|
||||
pub const SNAPSHOT_VERSION: u32 = 1;
|
||||
|
||||
/// Reason a snapshot could not be restored.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum SnapshotError {
|
||||
/// The bytes do not start with the snapshot marker.
|
||||
NotASnapshot,
|
||||
/// The snapshot was written by an incompatible version of the game.
|
||||
UnsupportedVersion(u32),
|
||||
/// The snapshot is damaged.
|
||||
Corrupt(DecodeError),
|
||||
}
|
||||
|
||||
impl fmt::Display for SnapshotError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
SnapshotError::NotASnapshot => write!(f, "not a snapshot"),
|
||||
SnapshotError::UnsupportedVersion(version) => {
|
||||
write!(f, "unsupported snapshot version {version}")
|
||||
}
|
||||
SnapshotError::Corrupt(error) => write!(f, "damaged snapshot: {error}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for SnapshotError {}
|
||||
|
||||
impl From<DecodeError> for SnapshotError {
|
||||
fn from(error: DecodeError) -> Self {
|
||||
SnapshotError::Corrupt(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl World {
|
||||
fn encode_state(&self, w: &mut Writer) {
|
||||
w.u64(self.seed);
|
||||
w.u64(self.tick);
|
||||
self.map.encode(w);
|
||||
w.u8(self.players.len() as u8);
|
||||
for player in &self.players {
|
||||
player.encode(w);
|
||||
}
|
||||
}
|
||||
|
||||
/// Checksum of the complete world state. Two worlds with the same value
|
||||
/// are in the same state, apart from hash collisions.
|
||||
pub fn state_hash(&self) -> u64 {
|
||||
let mut w = Writer::new();
|
||||
self.encode_state(&mut w);
|
||||
fnv1a64(&w.into_bytes())
|
||||
}
|
||||
|
||||
/// Encodes the complete world behind a header that names format and
|
||||
/// version.
|
||||
pub fn to_snapshot(&self) -> Vec<u8> {
|
||||
let mut w = Writer::new();
|
||||
w.raw(&MAGIC);
|
||||
w.u32(SNAPSHOT_VERSION);
|
||||
self.encode_state(&mut w);
|
||||
w.into_bytes()
|
||||
}
|
||||
|
||||
/// Restores a world from bytes written by [`World::to_snapshot`].
|
||||
pub fn from_snapshot(bytes: &[u8]) -> Result<World, SnapshotError> {
|
||||
let mut r = Reader::new(bytes);
|
||||
let magic = r
|
||||
.raw(MAGIC.len())
|
||||
.map_err(|_| SnapshotError::NotASnapshot)?;
|
||||
if magic != MAGIC.as_slice() {
|
||||
return Err(SnapshotError::NotASnapshot);
|
||||
}
|
||||
let version = r.u32()?;
|
||||
if version != SNAPSHOT_VERSION {
|
||||
return Err(SnapshotError::UnsupportedVersion(version));
|
||||
}
|
||||
let seed = r.u64()?;
|
||||
let tick = r.u64()?;
|
||||
let map = Map::decode(&mut r)?;
|
||||
let count = r.u8()? as usize;
|
||||
if count > MAX_KNOWN_PLAYERS {
|
||||
return Err(DecodeError::InvalidValue("player count").into());
|
||||
}
|
||||
let mut players = Vec::with_capacity(count);
|
||||
for _ in 0..count {
|
||||
players.push(Player::decode(&mut r)?);
|
||||
}
|
||||
r.finish()?;
|
||||
Ok(World {
|
||||
seed,
|
||||
tick,
|
||||
map,
|
||||
players,
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user