Skip to content

Commit

Permalink
Refactor entity serialization (#51)
Browse files Browse the repository at this point in the history
* Refactor entity serialization

Serializes entities in optimal way by writing
its index and generation as separate u32 varints and
entity generation if it is zero by prepending a bit flag to index.

Also update related docs. Looks like in Rust documentation they don't
link types that are already in signature and refer to variable names
which is make sense.

---------

Co-authored-by: koe <[email protected]>
  • Loading branch information
Shatur and UkoeHB authored Sep 22, 2023
1 parent a42e964 commit 54c4185
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Serialize entities in optimal way by writing its index and generation as separate varints.
- Hide `ReplicationId`, `ReplicationInfo` and related methods from `ReplicationRules` from public API.
- Rename `ReplicationRules::replication_id` into `ReplicationRules::replication_marker_id`.
- Use serialization buffer cache per client for replication.
Expand Down
23 changes: 19 additions & 4 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ fn deserialize_tick(cursor: &mut Cursor<Bytes>, world: &mut World) -> Result<boo
}
}

/// Deserializes component [`DiffKind`] and applies them to the [`World`].
/// Deserializes component diffs of `diff_kind` and applies them to the `world`.
fn deserialize_component_diffs(
cursor: &mut Cursor<Bytes>,
world: &mut World,
Expand All @@ -132,7 +132,7 @@ fn deserialize_component_diffs(
) -> Result<(), bincode::Error> {
let entities_count: u16 = bincode::deserialize_from(&mut *cursor)?;
for _ in 0..entities_count {
let entity = DefaultOptions::new().deserialize_from(&mut *cursor)?;
let entity = deserialize_entity(&mut *cursor)?;
let mut entity = entity_map.get_by_server_or_spawn(world, entity);
let components_count: u8 = bincode::deserialize_from(&mut *cursor)?;
for _ in 0..components_count {
Expand All @@ -150,7 +150,7 @@ fn deserialize_component_diffs(
Ok(())
}

/// Deserializes despawns and applies them to the [`World`].
/// Deserializes despawns and applies them to the `world`.
fn deserialize_despawns(
cursor: &mut Cursor<Bytes>,
world: &mut World,
Expand All @@ -161,7 +161,7 @@ fn deserialize_despawns(
// The entity might have already been deleted with the last diff,
// but the server might not yet have received confirmation from the
// client and could include the deletion in the latest diff.
let server_entity = DefaultOptions::new().deserialize_from(&mut *cursor)?;
let server_entity = deserialize_entity(&mut *cursor)?;
if let Some(client_entity) = entity_map.remove_by_server(server_entity) {
world.entity_mut(client_entity).despawn_recursive();
}
Expand All @@ -170,6 +170,21 @@ fn deserialize_despawns(
Ok(())
}

/// Deserializes `entity` from compressed index and generation, for details see [`ReplicationBuffer::write_entity()`].
fn deserialize_entity(cursor: &mut Cursor<Bytes>) -> Result<Entity, bincode::Error> {
let flagged_index: u64 = DefaultOptions::new().deserialize_from(&mut *cursor)?;
let has_generation = (flagged_index & 1) > 0;
let generation = if has_generation {
DefaultOptions::new().deserialize_from(&mut *cursor)?
} else {
0u32
};

let bits = (generation as u64) << 32 | (flagged_index >> 1);

Ok(Entity::from_bits(bits))
}

/// Type of component change.
///
/// Parameter for [`deserialize_component_diffs`].
Expand Down
46 changes: 32 additions & 14 deletions src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,14 +446,14 @@ struct ReplicationBuffer {
/// The number of empty arrays at the end. Can be removed using [`Self::trim_empty_arrays`]
trailing_empty_arrays: usize,

/// Position of the entity map from last call of [`Self::start_entity_data`] or [`Self::write_current_entity`].
/// Position of entity after [`Self::start_entity_data`] or its data after [`Self::write_data_entity`].
entity_data_pos: u64,

/// Length of the map that updated automatically after writing data.
/// Length of the data for entity that updated automatically after writing data.
entity_data_len: u8,

/// Entity from last call of [`Self::start_entity_data`].
current_entity: Entity,
data_entity: Entity,
}

impl ReplicationBuffer {
Expand All @@ -476,7 +476,7 @@ impl ReplicationBuffer {
trailing_empty_arrays: Default::default(),
entity_data_pos: Default::default(),
entity_data_len: Default::default(),
current_entity: Entity::PLACEHOLDER,
data_entity: Entity::PLACEHOLDER,
})
}

Expand Down Expand Up @@ -547,7 +547,7 @@ impl ReplicationBuffer {
self.trailing_empty_arrays = 0;
}

/// Starts writing entity and its data by remembering [`Entity`].
/// Starts writing entity and its data by remembering `entity`.
///
/// Arrays can contain component changes or removals inside.
/// Length will be increased automatically after writing data.
Expand All @@ -556,14 +556,14 @@ impl ReplicationBuffer {
fn start_entity_data(&mut self, entity: Entity) {
debug_assert_eq!(self.entity_data_len, 0);

self.current_entity = entity;
self.data_entity = entity;
}

/// Writes entity for current data and updates remembered position for it to write length later.
///
/// Should be called only after first data write.
fn write_current_entity(&mut self) -> Result<(), bincode::Error> {
DefaultOptions::new().serialize_into(&mut self.message, &self.current_entity)?;
fn write_data_entity(&mut self) -> Result<(), bincode::Error> {
self.write_entity(self.data_entity)?;
self.entity_data_pos = self.message.position();
self.message
.set_position(self.entity_data_pos + mem::size_of_val(&self.entity_data_len) as u64);
Expand Down Expand Up @@ -595,7 +595,7 @@ impl ReplicationBuffer {
Ok(())
}

/// Serializes [`ReplicationId`] and component into the buffer data.
/// Serializes `replication_id` and component from `ptr` into the buffer data.
///
/// Should be called only inside entity data.
/// Increases entity data length by 1.
Expand All @@ -607,7 +607,7 @@ impl ReplicationBuffer {
ptr: Ptr,
) -> Result<(), bincode::Error> {
if self.entity_data_len == 0 {
self.write_current_entity()?;
self.write_data_entity()?;
}

DefaultOptions::new().serialize_into(&mut self.message, &replication_id)?;
Expand All @@ -617,14 +617,14 @@ impl ReplicationBuffer {
Ok(())
}

/// Serializes [`ReplicationId`] of the removed component into the buffer data.
/// Serializes `replication_id` of the removed component into the buffer data.
///
/// Should be called only inside entity data.
/// Increases entity data length by 1.
/// See also [`Self::start_entity_data`].
fn write_removal(&mut self, replication_id: ReplicationId) -> Result<(), bincode::Error> {
if self.entity_data_len == 0 {
self.write_current_entity()?;
self.write_data_entity()?;
}

DefaultOptions::new().serialize_into(&mut self.message, &replication_id)?;
Expand All @@ -633,18 +633,36 @@ impl ReplicationBuffer {
Ok(())
}

/// Serializes despawned [`Entity`].
/// Serializes despawned `entity`.
///
/// Should be called only inside array.
/// Increases array length by 1.
/// See also [`Self::start_array`].
fn write_despawn(&mut self, entity: Entity) -> Result<(), bincode::Error> {
DefaultOptions::new().serialize_into(&mut self.message, &entity)?;
self.write_entity(entity)?;
self.array_len = self
.array_len
.checked_add(1)
.ok_or(bincode::ErrorKind::SizeLimit)?;

Ok(())
}

/// Serializes `entity` by writing its index and generation as separate varints.
///
/// The index is first prepended with a bit flag to indicate if the generation
/// is serialized or not (it is not serialized if equal to zero).
#[inline]
fn write_entity(&mut self, entity: Entity) -> Result<(), bincode::Error> {
let mut flagged_index = (entity.index() as u64) << 1;
let flag = entity.generation() > 0;
flagged_index |= flag as u64;

DefaultOptions::new().serialize_into(&mut self.message, &flagged_index)?;
if flag {
DefaultOptions::new().serialize_into(&mut self.message, &entity.generation())?;
}

Ok(())
}
}

0 comments on commit 54c4185

Please sign in to comment.