Skip to content

Commit

Permalink
Save and load all modified chunks
Browse files Browse the repository at this point in the history
  • Loading branch information
patowen committed Dec 18, 2023
1 parent 664a2f4 commit add052c
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 9 deletions.
5 changes: 5 additions & 0 deletions common/src/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,11 @@ impl Graph {
node.0
}

#[inline]
pub fn from_hash(&self, hash: u128) -> NodeId {
NodeId(hash)
}

/// Ensure all shorter neighbors of a not-yet-created child node exist and return them, excluding the given parent node
fn populate_shorter_neighbors_of_child(
&mut self,
Expand Down
10 changes: 10 additions & 0 deletions save/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,16 @@ impl Reader<'_> {
.map_err(GetError::DecompressionFailed)?;
Ok(Some(Character::decode(&*self.accum)?))
}

/// Temporary function to load all voxel-related save data at once.
/// TODO: Replace this implementation with a streaming implementation
/// that does not require loading everything at once
pub fn get_all_voxel_node_ids(&mut self) -> Result<Vec<u128>, GetError> {
self.voxel_nodes
.iter()?
.map(|n| Ok(n.map_err(GetError::from)?.0.value()))
.collect()
}
}

fn decompress(
Expand Down
2 changes: 1 addition & 1 deletion server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ impl Server {
fn new(params: SimConfig, save: Save) -> Self {
let cfg = Arc::new(params);
Self {
sim: Sim::new(cfg.clone()),
sim: Sim::new(cfg.clone(), &save),
cfg,
clients: DenseSlotMap::default(),
save,
Expand Down
92 changes: 84 additions & 8 deletions server/src/sim.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use std::sync::Arc;

use common::proto::BlockUpdate;
use anyhow::Context;
use common::dodeca::Vertex;
use common::node::VoxelData;
use common::proto::{BlockUpdate, SerializableVoxelData};
use common::{node::ChunkId, GraphEntities};
use fxhash::{FxHashMap, FxHashSet};
use hecs::Entity;
Expand Down Expand Up @@ -31,29 +34,36 @@ pub struct Sim {
entity_ids: FxHashMap<EntityId, Entity>,
world: hecs::World,
graph: Graph,
pending_modified_chunks: FxHashMap<ChunkId, VoxelData>,
spawns: Vec<Entity>,
despawns: Vec<EntityId>,
graph_entities: GraphEntities,
dirty_nodes: FxHashSet<NodeId>,
dirty_voxel_nodes: FxHashSet<NodeId>,
modified_chunks: FxHashSet<ChunkId>,
}

impl Sim {
pub fn new(cfg: Arc<SimConfig>) -> Self {
pub fn new(cfg: Arc<SimConfig>, save: &save::Save) -> Self {
let mut result = Self {
rng: SmallRng::from_entropy(),
step: 0,
entity_ids: FxHashMap::default(),
world: hecs::World::new(),
graph: Graph::new(cfg.chunk_size),
pending_modified_chunks: FxHashMap::default(),
spawns: Vec::new(),
despawns: Vec::new(),
graph_entities: GraphEntities::new(),
dirty_nodes: FxHashSet::default(),
dirty_voxel_nodes: FxHashSet::default(),
modified_chunks: FxHashSet::default(),
cfg,
};

result
.load_all_voxels(save)
.expect("save file must be of a valid format");
ensure_nearby(
&mut result.graph,
&Position::origin(),
Expand Down Expand Up @@ -85,16 +95,43 @@ impl Sim {
}

let dirty_nodes = self.dirty_nodes.drain().collect::<Vec<_>>();
let dirty_voxel_nodes = self.dirty_voxel_nodes.drain().collect::<Vec<_>>();
for node in dirty_nodes {
let entities = self.snapshot_node(node);
writer.put_entity_node(self.graph.hash_of(node), &entities)?;
}
for node in dirty_voxel_nodes {
let voxels = self.snapshot_voxel_node(node);
writer.put_voxel_node(self.graph.hash_of(node), &voxels)?;
}

drop(writer);
tx.commit()?;
Ok(())
}

fn load_all_voxels(&mut self, save: &save::Save) -> anyhow::Result<()> {
let read_guard = save.read()?;
let mut read = read_guard.get()?;
for node_hash in read.get_all_voxel_node_ids()? {
let Some(voxel_node) = read.get_voxel_node(node_hash)? else {
continue;
};
for chunk in voxel_node.chunks.iter() {
let voxels: SerializableVoxelData = postcard::from_bytes(&chunk.voxels)?;
let vertex = Vertex::iter()
.nth(chunk.vertex as usize)
.context("deserializing vertex ID")?;
self.pending_modified_chunks.insert(
ChunkId::new(self.graph.from_hash(node_hash), vertex),
VoxelData::from_serializable(&voxels, self.cfg.chunk_size)
.context("deserializing voxel data")?,
);
}
}
Ok(())
}

fn snapshot_node(&self, node: NodeId) -> save::EntityNode {
let mut ids = Vec::new();
let mut character_transforms = Vec::new();
Expand Down Expand Up @@ -127,6 +164,30 @@ impl Sim {
}
}

fn snapshot_voxel_node(&self, node: NodeId) -> save::VoxelNode {
let mut chunks = vec![];
let node_data = self.graph.get(node).as_ref().unwrap();
for vertex in Vertex::iter() {
if !self.modified_chunks.contains(&ChunkId::new(node, vertex)) {
continue;
}
let mut serialized_voxels = Vec::new();
let Chunk::Populated { ref voxels, .. } = node_data.chunks[vertex] else {
panic!("Unknown chunk listed as modified");
};
postcard_helpers::serialize(
&voxels.to_serializable(self.cfg.chunk_size),
&mut serialized_voxels,
)
.unwrap();
chunks.push(save::Chunk {
vertex: vertex as u32,
voxels: serialized_voxels,
})
}
save::VoxelNode { chunks }
}

pub fn spawn_character(&mut self, hello: ClientHello) -> (EntityId, Entity) {
let id = self.new_id();
info!(%id, name = %hello.name, "spawning character");
Expand Down Expand Up @@ -241,13 +302,17 @@ impl Sim {
ensure_nearby(&mut self.graph, position, f64::from(self.cfg.view_distance));
}

let fresh_nodes = self.graph.fresh().to_vec();
populate_fresh_nodes(&mut self.graph);

let mut accepted_block_updates: Vec<BlockUpdate> = vec![];

for block_update in pending_block_updates.into_iter() {
if !self.graph.update_block(&block_update) {
tracing::warn!("Block update received from ungenerated chunk");
}
self.modified_chunks.insert(block_update.chunk_id);
self.dirty_voxel_nodes.insert(block_update.chunk_id.node);
accepted_block_updates.push(block_update);
}

Expand All @@ -257,16 +322,28 @@ impl Sim {
let id = *self.world.get::<&EntityId>(entity).unwrap();
spawns.push((id, dump_entity(&self.world, entity)));
}
if !self.graph.fresh().is_empty() {

let mut modified_chunks = vec![];
for fresh_node in fresh_nodes.iter().copied() {
for vertex in Vertex::iter() {
let chunk = ChunkId::new(fresh_node, vertex);
if let Some(voxel_data) = self.pending_modified_chunks.remove(&chunk) {
modified_chunks.push((chunk, voxel_data.to_serializable(self.cfg.chunk_size)));
self.modified_chunks.insert(chunk);
self.graph.populate_chunk(chunk, voxel_data, true)
}
}
}

if !fresh_nodes.is_empty() {
trace!(count = self.graph.fresh().len(), "broadcasting fresh nodes");
}

let spawns = Spawns {
step: self.step,
spawns,
despawns: std::mem::take(&mut self.despawns),
nodes: self
.graph
.fresh()
nodes: fresh_nodes
.iter()
.filter_map(|&id| {
let side = self.graph.parent(id)?;
Expand All @@ -277,9 +354,8 @@ impl Sim {
})
.collect(),
block_updates: accepted_block_updates,
modified_chunks: vec![],
modified_chunks,
};
populate_fresh_nodes(&mut self.graph);

// We want to load all chunks that a player can interact with in a single step, so chunk_generation_distance
// is set up to cover that distance.
Expand Down

0 comments on commit add052c

Please sign in to comment.