94 lines
3 KiB
Rust
94 lines
3 KiB
Rust
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
//! Sparse, baseline-relative chunk storage.
|
|
|
|
use super::{BlockId, Chunk, ChunkPos};
|
|
use serde::{Deserialize, Serialize};
|
|
use std::collections::BTreeMap;
|
|
|
|
/// A modified chunk stored as a sparse set of edits layered on top of a deterministic worldgen baseline.
|
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub struct ChunkData {
|
|
/// The position of the chunk, identifying which baseline the edits are relative to.
|
|
pos: ChunkPos,
|
|
/// The worldgen version the baseline is pinned to, so the same baseline is reproduced across generator updates.
|
|
worldgen_version: u32,
|
|
/// The sparse edit set, mapping a local voxel index (`0..CHUNK_VOLUME`) to the block placed there.
|
|
edits: BTreeMap<u32, BlockId>,
|
|
}
|
|
|
|
impl ChunkData {
|
|
/// Initializes an unmodified chunk relative to the baseline at `pos` and `worldgen_version`.
|
|
#[must_use]
|
|
pub fn new(pos: ChunkPos, worldgen_version: u32) -> Self {
|
|
Self {
|
|
pos,
|
|
worldgen_version,
|
|
edits: BTreeMap::new(),
|
|
}
|
|
}
|
|
|
|
/// Returns the position identifying which baseline the edits are relative to.
|
|
#[must_use]
|
|
pub fn pos(&self) -> ChunkPos {
|
|
self.pos
|
|
}
|
|
|
|
/// Returns the worldgen version the baseline is pinned to.
|
|
#[must_use]
|
|
pub fn worldgen_version(&self) -> u32 {
|
|
self.worldgen_version
|
|
}
|
|
|
|
/// Records `block` at the voxel `local_index`, overwriting any prior edit there.
|
|
pub fn set(&mut self, local_index: u32, block: BlockId) {
|
|
self.edits.insert(local_index, block);
|
|
}
|
|
|
|
/// Returns `true` when no edits are stored, i.e. the chunk matches its baseline and may be omitted from persistence.
|
|
#[must_use]
|
|
pub fn is_unmodified(&self) -> bool {
|
|
self.edits.is_empty()
|
|
}
|
|
|
|
/// Reconstructs the dense [`Chunk`] by applying the stored edits on top of `baseline`.
|
|
#[must_use]
|
|
pub fn materialize(&self, baseline: &Chunk) -> Chunk {
|
|
let mut chunk = baseline.clone();
|
|
for (&local_index, &block) in &self.edits {
|
|
chunk.blocks[local_index as usize] = block;
|
|
}
|
|
chunk
|
|
}
|
|
|
|
/// Computes the sparse delta of `current` against `baseline`, storing only the voxels that differ.
|
|
#[must_use]
|
|
pub fn from_diff(
|
|
pos: ChunkPos,
|
|
worldgen_version: u32,
|
|
baseline: &Chunk,
|
|
current: &Chunk,
|
|
) -> ChunkData {
|
|
let mut data = ChunkData::new(pos, worldgen_version);
|
|
for (i, (&base, &cur)) in baseline
|
|
.blocks
|
|
.iter()
|
|
.zip(current.blocks.iter())
|
|
.enumerate()
|
|
{
|
|
if base != cur {
|
|
#[expect(
|
|
clippy::cast_possible_truncation,
|
|
reason = "i ranges over 0..CHUNK_VOLUME, which fits in u32"
|
|
)]
|
|
data.set(i as u32, cur);
|
|
}
|
|
}
|
|
data
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[path = "../tests/chunk_data.rs"]
|
|
mod tests;
|