Skip to content

Commit aabc9f0

Browse files
committed
Migrate to the new Entity layout
For details see bevyengine/bevy#19121 bevyengine/bevy#18704 The niche is now in the index, which makes the compression logic even simpler. The index now represented by NonMaxU32, which internally represented as NonZeroU32 with all bits reversed, so we have to xor the bits. I opened a PR to make it more ergonomic and avoid us relying on the internal layout: bevyengine/bevy#21246
1 parent ebd01d7 commit aabc9f0

File tree

4 files changed

+25
-23
lines changed

4 files changed

+25
-23
lines changed

src/compact_entity.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,32 +17,31 @@
1717
1818
use core::fmt::{self, Formatter};
1919

20-
use bevy::prelude::*;
20+
use bevy::{ecs::entity::EntityGeneration, prelude::*};
2121
use serde::{
2222
Deserializer, Serialize, Serializer,
2323
de::{self, SeqAccess, Visitor},
2424
};
2525

2626
/// Serializes an entity by writing its index and generation as separate numbers.
2727
///
28-
/// This reduces the space used for serializers with varint encoding.
28+
/// This reduces the space required when using serializers with varint encoding.
2929
///
30-
/// The index is first prepended with a bit flag to indicate if the generation
31-
/// is serialized or not. It is not serialized if <= 1; note that generations are [`NonZeroU32`](core::num::NonZeroU32)
32-
/// and a value of zero is used in [`Option<Entity>`] to signify [`None`], so generation 1 is the first
33-
/// generation.
30+
/// Since the index can never be [`u32::MAX`], we reuse that extra niche to indicate
31+
/// whether the generation isn't [`EntityGeneration::FIRST`]. If it doesn't,
32+
/// the generation is skipped during serialization.
3433
///
3534
/// See also [`deserialize`] and [`postcard_utils::entity_to_extend_mut`](crate::postcard_utils::entity_to_extend_mut).
3635
pub fn serialize<S: Serializer>(entity: &Entity, serializer: S) -> Result<S::Ok, S::Error> {
37-
let mut flagged_index = (entity.index() as u64) << 1;
38-
let flag = entity.generation() > 1;
39-
flagged_index |= flag as u64;
36+
let mut index = entity.index() << 1;
37+
let has_generation = entity.generation() != EntityGeneration::FIRST;
38+
index |= has_generation as u32;
4039

41-
if flag {
42-
let generation = entity.generation() - 1;
43-
(flagged_index, generation).serialize(serializer)
40+
if has_generation {
41+
let generation = (entity.to_bits() >> 32) as u32; // TODO Bevy 0.17: use `entity.generation().to_bits()`.
42+
(index, generation).serialize(serializer)
4443
} else {
45-
flagged_index.serialize(serializer)
44+
index.serialize(serializer)
4645
}
4746
}
4847

@@ -63,22 +62,23 @@ impl<'de> Visitor<'de> for EntityVisitor {
6362
}
6463

6564
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
66-
let flagged_index: u64 = seq
65+
let index: u32 = seq
6766
.next_element()?
6867
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
6968

70-
let has_generation = (flagged_index & 1) != 0;
69+
let has_generation = (index & 1) != 0;
7170

7271
let generation = if has_generation {
7372
let generation: u32 = seq
7473
.next_element()?
7574
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
76-
generation as u64 + 1
75+
generation as u64
7776
} else {
78-
1
77+
0
7978
};
8079

81-
let bits = (flagged_index >> 1) | (generation << 32);
80+
// TODO Bevy 0.17: Use more ergonomic construction.
81+
let bits = ((index >> 1) ^ u32::MAX) as u64 | (generation << 32);
8282
Ok(Entity::from_bits(bits))
8383
}
8484
}

src/postcard_utils.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ mod tests {
216216

217217
#[test]
218218
fn entity_without_generation() {
219-
let expected_entity = Entity::from_raw(1);
219+
let expected_entity = Entity::from_raw_u32(1).unwrap();
220220
let mut buffer = Vec::new();
221221
entity_to_extend_mut(&expected_entity, &mut buffer).unwrap();
222222
assert_eq!(buffer.len(), 1);
@@ -227,7 +227,8 @@ mod tests {
227227

228228
#[test]
229229
fn entity_with_generation() {
230-
let expected_entity = Entity::from_bits(1 | (2 << 32));
230+
// TODO Bevy 0.17: Use more ergonomic construction.
231+
let expected_entity = Entity::from_bits((1 ^ u32::MAX) as u64 | (1 << 32));
231232
let mut buffer = Vec::new();
232233
entity_to_extend_mut(&expected_entity, &mut buffer).unwrap();
233234
assert_eq!(buffer.len(), 2);

src/shared/server_entity_map.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,8 @@ mod tests {
155155

156156
#[test]
157157
fn mapping() {
158-
const SERVER_ENTITY: Entity = Entity::from_raw(0);
159-
const CLIENT_ENTITY: Entity = Entity::from_raw(1);
158+
const SERVER_ENTITY: Entity = Entity::from_raw_u32(0).unwrap();
159+
const CLIENT_ENTITY: Entity = Entity::from_raw_u32(1).unwrap();
160160

161161
let mut map = ServerEntityMap::default();
162162
assert_eq!(map.server_entry(SERVER_ENTITY).get(), None);

tests/server_event.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,8 @@ fn local_resending() {
187187
.add_server_event::<TestEvent>(Channel::Ordered)
188188
.finish();
189189

190-
const PLACEHOLDER_CLIENT_ID: ClientId = ClientId::Client(Entity::from_raw(1));
190+
const CLIENT_ENTITY: Entity = Entity::from_raw_u32(1).unwrap();
191+
const PLACEHOLDER_CLIENT_ID: ClientId = ClientId::Client(CLIENT_ENTITY);
191192
for (mode, events_count) in [
192193
(SendMode::Broadcast, 1),
193194
(SendMode::Direct(ClientId::Server), 1),

0 commit comments

Comments
 (0)