Skip to content

Commit

Permalink
Allow the server to send updated chunks to new clients
Browse files Browse the repository at this point in the history
  • Loading branch information
patowen committed Dec 18, 2023
1 parent 3b5ebd6 commit d3ae1e0
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 6 deletions.
2 changes: 1 addition & 1 deletion client/src/graphics/voxels/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ impl Voxels {
}
while let Some(chunk) = self.worldgen.poll() {
let chunk_id = ChunkId::new(chunk.node, chunk.chunk);
sim.graph.populate_chunk(chunk_id, chunk.voxels);
sim.graph.populate_chunk(chunk_id, chunk.voxels, false);
}

// Determine what to load/render
Expand Down
10 changes: 9 additions & 1 deletion client/src/sim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use common::{
collision_math::Ray,
graph::{Graph, NodeId},
graph_ray_casting,
node::populate_fresh_nodes,
node::{populate_fresh_nodes, VoxelData},
proto::{
self, BlockUpdate, Character, CharacterInput, CharacterState, Command, Component, Position,
},
Expand Down Expand Up @@ -305,6 +305,14 @@ impl Sim {
tracing::error!("Voxel data received from server for ungenerated chunk.")
}
}
for (chunk_id, voxel_data) in msg.modified_chunks {
let Some(voxel_data) = VoxelData::from_serializable(&voxel_data, self.cfg.chunk_size)
else {
tracing::error!("Voxel data received from server is of incorrect dimension");
continue;
};
self.graph.populate_chunk(chunk_id, voxel_data, true);
}
}

fn spawn(
Expand Down
54 changes: 51 additions & 3 deletions common/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::collision_math::Ray;
use crate::dodeca::Vertex;
use crate::graph::{Graph, NodeId};
use crate::lru_slab::SlotId;
use crate::proto::{BlockUpdate, Position};
use crate::proto::{BlockUpdate, Position, SerializableVoxelData};
use crate::world::Material;
use crate::worldgen::NodeState;
use crate::{math, Chunks};
Expand Down Expand Up @@ -107,7 +107,7 @@ impl Graph {
}

/// Populates a chunk with the given voxel data and ensures that margins are correctly cleared if necessary.
pub fn populate_chunk(&mut self, chunk: ChunkId, mut new_data: VoxelData) {
pub fn populate_chunk(&mut self, chunk: ChunkId, mut new_data: VoxelData, modified: bool) {
// New solid chunks should have their margin cleared if they are adjacent to any modified chunks.
// See the function description of VoxelData::clear_margin for why this is necessary.
if new_data.is_solid() {
Expand All @@ -127,10 +127,16 @@ impl Graph {
}
}

// Existing adjacent solid chunks should have their margins cleared if the chunk we're populating is modified.
// See the function description of VoxelData::clear_margin for why this is necessary.
if modified {
self.clear_adjacent_solid_chunk_margins(chunk);
}

// After clearing any margins we needed to clear, we can now insert the data into the graph
*self.get_chunk_mut(chunk).unwrap() = Chunk::Populated {
voxels: new_data,
modified: false,
modified,
surface: None,
old_surface: None,
};
Expand Down Expand Up @@ -319,6 +325,48 @@ impl VoxelData {
VoxelData::Solid(_) => true,
}
}

/// Returns a `VoxelData` with void margins based on the given `SerializableVoxelData`, or `None` if
/// the `SerializableVoxelData` came from a `VoxelData` with the wrong dimension.
pub fn from_serializable(serializable: &SerializableVoxelData, dimension: u8) -> Option<Self> {
if serializable.voxels.len() != usize::from(dimension).pow(3) {
return None;
}

let mut data = vec![Material::Void; (usize::from(dimension) + 2).pow(3)];
let mut input_index = 0;
for z in 0..dimension {
for y in 0..dimension {
for x in 0..dimension {
// We cannot use a linear copy here because `data` has margins, while `serializable.voxels` does not.
data[Coords([x, y, z]).to_index(dimension)] = serializable.voxels[input_index];
input_index += 1;
}
}
}
Some(VoxelData::Dense(data.into_boxed_slice()))
}

/// Returns a `SerializableVoxelData` corresponding to `self`. Assumes that`self` is `Dense` and
/// has the right dimension, as it will panic or return incorrect data otherwise.
pub fn to_serializable(&self, dimension: u8) -> SerializableVoxelData {
let VoxelData::Dense(data) = self else {
panic!("Only dense chunks can be serialized.");
};

let mut serializable: Vec<Material> = Vec::with_capacity(usize::from(dimension).pow(3));
for z in 0..dimension {
for y in 0..dimension {
for x in 0..dimension {
// We cannot use a linear copy here because `data` has margins, while `serializable.voxels` does not.
serializable.push(data[Coords([x, y, z]).to_index(dimension)]);
}
}
}
SerializableVoxelData {
voxels: serializable,
}
}
}

/// Contains the context needed to know the locations of individual cubes within a chunk in the chunk's coordinate
Expand Down
6 changes: 6 additions & 0 deletions common/src/proto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ pub struct Spawns {
pub despawns: Vec<EntityId>,
pub nodes: Vec<FreshNode>,
pub block_updates: Vec<BlockUpdate>,
pub modified_chunks: Vec<(ChunkId, SerializableVoxelData)>,
}

#[derive(Debug, Serialize, Deserialize)]
Expand All @@ -82,6 +83,11 @@ pub struct BlockUpdate {
pub new_material: Material,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct SerializableVoxelData {
pub voxels: Vec<Material>,
}

#[derive(Debug, Serialize, Deserialize)]
pub enum Component {
Character(Character),
Expand Down
1 change: 1 addition & 0 deletions server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ impl Server {
|| !spawns.despawns.is_empty()
|| !spawns.nodes.is_empty()
|| !spawns.block_updates.is_empty()
|| !spawns.modified_chunks.is_empty()
{
handles.ordered.try_send(spawns.clone())
} else {
Expand Down
18 changes: 17 additions & 1 deletion server/src/sim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub struct Sim {
despawns: Vec<EntityId>,
graph_entities: GraphEntities,
dirty_nodes: FxHashSet<NodeId>,
modified_chunks: FxHashSet<ChunkId>,
}

impl Sim {
Expand All @@ -49,6 +50,7 @@ impl Sim {
despawns: Vec::new(),
graph_entities: GraphEntities::new(),
dirty_nodes: FxHashSet::default(),
modified_chunks: FxHashSet::default(),
cfg,
};

Expand Down Expand Up @@ -188,10 +190,22 @@ impl Sim {
.map(|(side, parent)| FreshNode { side, parent })
.collect(),
block_updates: Vec::new(),
modified_chunks: Vec::new(),
};
for (entity, &id) in &mut self.world.query::<&EntityId>() {
spawns.spawns.push((id, dump_entity(&self.world, entity)));
}
for &chunk_id in self.modified_chunks.iter() {
let voxels =
match self.graph.get(chunk_id.node).as_ref().unwrap().chunks[chunk_id.vertex] {
Chunk::Populated { ref voxels, .. } => voxels,
_ => panic!("ungenerated chunk is marked as modified"),
};

spawns
.modified_chunks
.push((chunk_id, voxels.to_serializable(self.cfg.chunk_size)));
}
spawns
}

Expand Down Expand Up @@ -233,6 +247,7 @@ impl Sim {
if !self.graph.update_block(&block_update) {
tracing::warn!("Block update received from ungenerated chunk");
}
self.modified_chunks.insert(block_update.chunk_id);
accepted_block_updates.push(block_update);
}

Expand Down Expand Up @@ -262,6 +277,7 @@ impl Sim {
})
.collect(),
block_updates: accepted_block_updates,
modified_chunks: vec![],
};
populate_fresh_nodes(&mut self.graph);

Expand Down Expand Up @@ -290,7 +306,7 @@ impl Sim {
ChunkParams::new(self.cfg.chunk_size, &self.graph, chunk)
{
self.graph
.populate_chunk(chunk, params.generate_voxels());
.populate_chunk(chunk, params.generate_voxels(), false);
}
}
}
Expand Down

0 comments on commit d3ae1e0

Please sign in to comment.