From c144a654c288b7b8c49c4bdd41cd743171e792d0 Mon Sep 17 00:00:00 2001 From: Hennadii Chernyshchyk Date: Fri, 22 Sep 2023 17:47:59 +0300 Subject: [PATCH 1/5] Refactor entity serialization Serializes entities in optimal way by writing its index and generation as separate u32 varints. 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. --- src/client.rs | 17 +++++++++++++---- src/server.rs | 36 ++++++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/client.rs b/src/client.rs index a0ab76ea..3f72c711 100644 --- a/src/client.rs +++ b/src/client.rs @@ -122,7 +122,7 @@ fn deserialize_tick(cursor: &mut Cursor, world: &mut World) -> Result, world: &mut World, @@ -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 { @@ -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, world: &mut World, @@ -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(); } @@ -170,6 +170,15 @@ fn deserialize_despawns( Ok(()) } +/// Deserializes `entity` from index and generation that was separately serialized as varints. +fn deserialize_entity(cursor: &mut Cursor) -> Result { + let index: u32 = DefaultOptions::new().deserialize_from(&mut *cursor)?; + let generation: u32 = DefaultOptions::new().deserialize_from(&mut *cursor)?; + let bits = (generation as u64) << 32 | index as u64; + + Ok(Entity::from_bits(bits)) +} + /// Type of component change. /// /// Parameter for [`deserialize_component_diffs`]. diff --git a/src/server.rs b/src/server.rs index 28af77d4..0f162496 100644 --- a/src/server.rs +++ b/src/server.rs @@ -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 { @@ -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, }) } @@ -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. @@ -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); @@ -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. @@ -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)?; @@ -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)?; @@ -633,13 +633,13 @@ 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) @@ -647,4 +647,12 @@ impl ReplicationBuffer { Ok(()) } + + /// Serializes `entity` in optimal way by writing its index and generation as separate u32 varints. + fn write_entity(&mut self, entity: Entity) -> Result<(), bincode::Error> { + DefaultOptions::new().serialize_into(&mut self.message, &entity.index())?; + DefaultOptions::new().serialize_into(&mut self.message, &entity.generation())?; + + Ok(()) + } } From 713b6c0b26dc3d8f9203cb14708215f3d83f48c8 Mon Sep 17 00:00:00 2001 From: Hennadii Chernyshchyk Date: Fri, 22 Sep 2023 17:54:13 +0300 Subject: [PATCH 2/5] Remove u32 reference for consistency --- src/server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server.rs b/src/server.rs index 0f162496..9367eb4d 100644 --- a/src/server.rs +++ b/src/server.rs @@ -648,7 +648,7 @@ impl ReplicationBuffer { Ok(()) } - /// Serializes `entity` in optimal way by writing its index and generation as separate u32 varints. + /// Serializes `entity` in optimal way by writing its index and generation as separate varints. fn write_entity(&mut self, entity: Entity) -> Result<(), bincode::Error> { DefaultOptions::new().serialize_into(&mut self.message, &entity.index())?; DefaultOptions::new().serialize_into(&mut self.message, &entity.generation())?; From 61ced1dae2eda41d1adc170cc9319e850710e8ef Mon Sep 17 00:00:00 2001 From: Hennadii Chernyshchyk Date: Fri, 22 Sep 2023 17:56:15 +0300 Subject: [PATCH 3/5] Update changelog [skip ci] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8253a300..1c2c5e44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. From f589f7d2aa0d93e0d34b2744ac06a6cfefddff58 Mon Sep 17 00:00:00 2001 From: koe Date: Fri, 22 Sep 2023 11:14:23 -0500 Subject: [PATCH 4/5] don't serialize entity generation if it is zero --- src/client.rs | 18 ++++++++++++++---- src/server.rs | 14 +++++++++++--- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/client.rs b/src/client.rs index 3f72c711..d68fb757 100644 --- a/src/client.rs +++ b/src/client.rs @@ -170,11 +170,21 @@ fn deserialize_despawns( Ok(()) } -/// Deserializes `entity` from index and generation that was separately serialized as varints. +/// Deserializes `entity` from compressed index and generation (see [`ReplicationBuffer::write_entity()`]. fn deserialize_entity(cursor: &mut Cursor) -> Result { - let index: u32 = DefaultOptions::new().deserialize_from(&mut *cursor)?; - let generation: u32 = DefaultOptions::new().deserialize_from(&mut *cursor)?; - let bits = (generation as u64) << 32 | index as u64; + // deserialize flagged entity index + let flagged_index: u64 = DefaultOptions::new().deserialize_from(&mut *cursor)?; + let has_generation = (flagged_index & (1u64)) > 0; + + // get entity generation + let generation = if has_generation { + DefaultOptions::new().deserialize_from(&mut *cursor)? + } else { + 0u32 + }; + + // finalize entity + let bits = (generation as u64) << 32 | (flagged_index >> 1); Ok(Entity::from_bits(bits)) } diff --git a/src/server.rs b/src/server.rs index 9367eb4d..29786363 100644 --- a/src/server.rs +++ b/src/server.rs @@ -648,10 +648,18 @@ impl ReplicationBuffer { Ok(()) } - /// Serializes `entity` in optimal way by writing its index and generation as separate varints. + /// 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> { - DefaultOptions::new().serialize_into(&mut self.message, &entity.index())?; - DefaultOptions::new().serialize_into(&mut self.message, &entity.generation())?; + 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(()) } From 3b9220cf64f4701d87e15689d1d1f7e10c7bfbaa Mon Sep 17 00:00:00 2001 From: Hennadii Chernyshchyk Date: Fri, 22 Sep 2023 21:29:57 +0300 Subject: [PATCH 5/5] Add minor stylistic changes --- src/client.rs | 8 ++------ src/server.rs | 8 +++++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/client.rs b/src/client.rs index d68fb757..58e134ae 100644 --- a/src/client.rs +++ b/src/client.rs @@ -170,20 +170,16 @@ fn deserialize_despawns( Ok(()) } -/// Deserializes `entity` from compressed index and generation (see [`ReplicationBuffer::write_entity()`]. +/// Deserializes `entity` from compressed index and generation, for details see [`ReplicationBuffer::write_entity()`]. fn deserialize_entity(cursor: &mut Cursor) -> Result { - // deserialize flagged entity index let flagged_index: u64 = DefaultOptions::new().deserialize_from(&mut *cursor)?; - let has_generation = (flagged_index & (1u64)) > 0; - - // get entity generation + let has_generation = (flagged_index & 1) > 0; let generation = if has_generation { DefaultOptions::new().deserialize_from(&mut *cursor)? } else { 0u32 }; - // finalize entity let bits = (generation as u64) << 32 | (flagged_index >> 1); Ok(Entity::from_bits(bits)) diff --git a/src/server.rs b/src/server.rs index 29786363..e470651d 100644 --- a/src/server.rs +++ b/src/server.rs @@ -648,14 +648,16 @@ impl ReplicationBuffer { 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). + /// 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())?;