[Fixes #6059] `Entity`'s “ID” should be named “index” instead (#6107)

# Objective

Fixes #6059, changing all incorrect occurrences of ``id`` in the ``entity`` module to ``index``:

* struct level documentation,
* ``id`` struct field,
* ``id`` method and its documentation.

## Solution

Renaming and verifying using CI. 


Co-authored-by: Edvin Kjell <43633999+Edwox@users.noreply.github.com>
This commit is contained in:
Edvin Kjell 2022-11-02 15:19:50 +00:00
parent ed3ecda91d
commit a8a62fcf3d
9 changed files with 133 additions and 118 deletions

View file

@ -543,10 +543,10 @@ impl<'a, 'b> BundleInserter<'a, 'b> {
InsertBundleResult::NewArchetypeSameTable { new_archetype } => { InsertBundleResult::NewArchetypeSameTable { new_archetype } => {
let result = self.archetype.swap_remove(location.index); let result = self.archetype.swap_remove(location.index);
if let Some(swapped_entity) = result.swapped_entity { if let Some(swapped_entity) = result.swapped_entity {
self.entities.meta[swapped_entity.id as usize].location = location; self.entities.meta[swapped_entity.index as usize].location = location;
} }
let new_location = new_archetype.allocate(entity, result.table_row); let new_location = new_archetype.allocate(entity, result.table_row);
self.entities.meta[entity.id as usize].location = new_location; self.entities.meta[entity.index as usize].location = new_location;
// PERF: this could be looked up during Inserter construction and stored (but borrowing makes this nasty) // PERF: this could be looked up during Inserter construction and stored (but borrowing makes this nasty)
let add_bundle = self let add_bundle = self
@ -571,7 +571,7 @@ impl<'a, 'b> BundleInserter<'a, 'b> {
} => { } => {
let result = self.archetype.swap_remove(location.index); let result = self.archetype.swap_remove(location.index);
if let Some(swapped_entity) = result.swapped_entity { if let Some(swapped_entity) = result.swapped_entity {
self.entities.meta[swapped_entity.id as usize].location = location; self.entities.meta[swapped_entity.index as usize].location = location;
} }
// PERF: store "non bundle" components in edge, then just move those to avoid // PERF: store "non bundle" components in edge, then just move those to avoid
// redundant copies // redundant copies
@ -579,7 +579,7 @@ impl<'a, 'b> BundleInserter<'a, 'b> {
.table .table
.move_to_superset_unchecked(result.table_row, new_table); .move_to_superset_unchecked(result.table_row, new_table);
let new_location = new_archetype.allocate(entity, move_result.new_row); let new_location = new_archetype.allocate(entity, move_result.new_row);
self.entities.meta[entity.id as usize].location = new_location; self.entities.meta[entity.index as usize].location = new_location;
// if an entity was moved into this entity's table spot, update its table row // if an entity was moved into this entity's table spot, update its table row
if let Some(swapped_entity) = move_result.swapped_entity { if let Some(swapped_entity) = move_result.swapped_entity {
@ -655,7 +655,7 @@ impl<'a, 'b> BundleSpawner<'a, 'b> {
self.change_tick, self.change_tick,
bundle, bundle,
); );
self.entities.meta[entity.id as usize].location = location; self.entities.meta[entity.index as usize].location = location;
location location
} }

View file

@ -53,7 +53,7 @@ type IdCursor = isize;
/// Lightweight identifier of an [entity](crate::entity). /// Lightweight identifier of an [entity](crate::entity).
/// ///
/// The identifier is implemented using a [generational index]: a combination of an ID and a generation. /// The identifier is implemented using a [generational index]: a combination of an index and a generation.
/// This allows fast insertion after data removal in an array while minimizing loss of spatial locality. /// This allows fast insertion after data removal in an array while minimizing loss of spatial locality.
/// ///
/// [generational index]: https://lucassardois.medium.com/generational-indices-guide-8e3c5f7fd594 /// [generational index]: https://lucassardois.medium.com/generational-indices-guide-8e3c5f7fd594
@ -106,7 +106,7 @@ type IdCursor = isize;
#[derive(Clone, Copy, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] #[derive(Clone, Copy, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub struct Entity { pub struct Entity {
pub(crate) generation: u32, pub(crate) generation: u32,
pub(crate) id: u32, pub(crate) index: u32,
} }
pub enum AllocAtWithoutReplacement { pub enum AllocAtWithoutReplacement {
@ -116,14 +116,14 @@ pub enum AllocAtWithoutReplacement {
} }
impl Entity { impl Entity {
/// Creates a new entity reference with the specified `id` and a generation of 0. /// Creates a new entity reference with the specified `index` and a generation of 0.
/// ///
/// # Note /// # Note
/// ///
/// Spawning a specific `entity` value is __rarely the right choice__. Most apps should favor /// Spawning a specific `entity` value is __rarely the right choice__. Most apps should favor
/// [`Commands::spawn`](crate::system::Commands::spawn). This method should generally /// [`Commands::spawn`](crate::system::Commands::spawn). This method should generally
/// only be used for sharing entities across apps, and only when they have a scheme /// only be used for sharing entities across apps, and only when they have a scheme
/// worked out to share an ID space (which doesn't happen by default). /// worked out to share an index space (which doesn't happen by default).
/// ///
/// In general, one should not try to synchronize the ECS by attempting to ensure that /// In general, one should not try to synchronize the ECS by attempting to ensure that
/// `Entity` lines up between instances, but instead insert a secondary identifier as /// `Entity` lines up between instances, but instead insert a secondary identifier as
@ -163,8 +163,11 @@ impl Entity {
/// } /// }
/// } /// }
/// ``` /// ```
pub const fn from_raw(id: u32) -> Entity { pub const fn from_raw(index: u32) -> Entity {
Entity { id, generation: 0 } Entity {
index,
generation: 0,
}
} }
/// Convert to a form convenient for passing outside of rust. /// Convert to a form convenient for passing outside of rust.
@ -174,7 +177,7 @@ impl Entity {
/// ///
/// No particular structure is guaranteed for the returned bits. /// No particular structure is guaranteed for the returned bits.
pub fn to_bits(self) -> u64 { pub fn to_bits(self) -> u64 {
u64::from(self.generation) << 32 | u64::from(self.id) u64::from(self.generation) << 32 | u64::from(self.index)
} }
/// Reconstruct an `Entity` previously destructured with [`Entity::to_bits`]. /// Reconstruct an `Entity` previously destructured with [`Entity::to_bits`].
@ -183,23 +186,23 @@ impl Entity {
pub const fn from_bits(bits: u64) -> Self { pub const fn from_bits(bits: u64) -> Self {
Self { Self {
generation: (bits >> 32) as u32, generation: (bits >> 32) as u32,
id: bits as u32, index: bits as u32,
} }
} }
/// Return a transiently unique identifier. /// Return a transiently unique identifier.
/// ///
/// No two simultaneously-live entities share the same ID, but dead entities' IDs may collide /// No two simultaneously-live entities share the same index, but dead entities' indices may collide
/// with both live and dead entities. Useful for compactly representing entities within a /// with both live and dead entities. Useful for compactly representing entities within a
/// specific snapshot of the world, such as when serializing. /// specific snapshot of the world, such as when serializing.
#[inline] #[inline]
pub const fn id(self) -> u32 { pub const fn index(self) -> u32 {
self.id self.index
} }
/// Returns the generation of this Entity's id. The generation is incremented each time an /// Returns the generation of this Entity's index. The generation is incremented each time an
/// entity with a given id is despawned. This serves as a "count" of the number of times a /// entity with a given index is despawned. This serves as a "count" of the number of times a
/// given id has been reused (id, generation) pairs uniquely identify a given Entity. /// given index has been reused (index, generation) pairs uniquely identify a given Entity.
#[inline] #[inline]
pub const fn generation(self) -> u32 { pub const fn generation(self) -> u32 {
self.generation self.generation
@ -208,13 +211,13 @@ impl Entity {
impl fmt::Debug for Entity { impl fmt::Debug for Entity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}v{}", self.id, self.generation) write!(f, "{}v{}", self.index, self.generation)
} }
} }
impl SparseSetIndex for Entity { impl SparseSetIndex for Entity {
fn sparse_set_index(&self) -> usize { fn sparse_set_index(&self) -> usize {
self.id() as usize self.index() as usize
} }
fn get_sparse_set_index(value: usize) -> Self { fn get_sparse_set_index(value: usize) -> Self {
@ -228,28 +231,33 @@ pub struct ReserveEntitiesIterator<'a> {
// Metas, so we can recover the current generation for anything in the freelist. // Metas, so we can recover the current generation for anything in the freelist.
meta: &'a [EntityMeta], meta: &'a [EntityMeta],
// Reserved IDs formerly in the freelist to hand out. // Reserved indices formerly in the freelist to hand out.
id_iter: std::slice::Iter<'a, u32>, index_iter: std::slice::Iter<'a, u32>,
// New Entity IDs to hand out, outside the range of meta.len(). // New Entity indices to hand out, outside the range of meta.len().
id_range: std::ops::Range<u32>, index_range: std::ops::Range<u32>,
} }
impl<'a> Iterator for ReserveEntitiesIterator<'a> { impl<'a> Iterator for ReserveEntitiesIterator<'a> {
type Item = Entity; type Item = Entity;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
self.id_iter self.index_iter
.next() .next()
.map(|&id| Entity { .map(|&index| Entity {
generation: self.meta[id as usize].generation, generation: self.meta[index as usize].generation,
id, index,
})
.or_else(|| {
self.index_range.next().map(|index| Entity {
generation: 0,
index,
})
}) })
.or_else(|| self.id_range.next().map(|id| Entity { generation: 0, id }))
} }
fn size_hint(&self) -> (usize, Option<usize>) { fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.id_iter.len() + self.id_range.len(); let len = self.index_iter.len() + self.index_range.len();
(len, Some(len)) (len, Some(len))
} }
} }
@ -344,8 +352,8 @@ impl Entities {
ReserveEntitiesIterator { ReserveEntitiesIterator {
meta: &self.meta[..], meta: &self.meta[..],
id_iter: self.pending[freelist_range].iter(), index_iter: self.pending[freelist_range].iter(),
id_range: new_id_start..new_id_end, index_range: new_id_start..new_id_end,
} }
} }
@ -356,10 +364,10 @@ impl Entities {
let n = self.free_cursor.fetch_sub(1, Ordering::Relaxed); let n = self.free_cursor.fetch_sub(1, Ordering::Relaxed);
if n > 0 { if n > 0 {
// Allocate from the freelist. // Allocate from the freelist.
let id = self.pending[(n - 1) as usize]; let index = self.pending[(n - 1) as usize];
Entity { Entity {
generation: self.meta[id as usize].generation, generation: self.meta[index as usize].generation,
id, index,
} }
} else { } else {
// Grab a new ID, outside the range of `meta.len()`. `flush()` must // Grab a new ID, outside the range of `meta.len()`. `flush()` must
@ -369,7 +377,7 @@ impl Entities {
// and farther beyond `meta.len()`. // and farther beyond `meta.len()`.
Entity { Entity {
generation: 0, generation: 0,
id: u32::try_from(self.meta.len() as IdCursor - n).expect("too many entities"), index: u32::try_from(self.meta.len() as IdCursor - n).expect("too many entities"),
} }
} }
} }
@ -386,17 +394,20 @@ impl Entities {
pub fn alloc(&mut self) -> Entity { pub fn alloc(&mut self) -> Entity {
self.verify_flushed(); self.verify_flushed();
self.len += 1; self.len += 1;
if let Some(id) = self.pending.pop() { if let Some(index) = self.pending.pop() {
let new_free_cursor = self.pending.len() as IdCursor; let new_free_cursor = self.pending.len() as IdCursor;
*self.free_cursor.get_mut() = new_free_cursor; *self.free_cursor.get_mut() = new_free_cursor;
Entity { Entity {
generation: self.meta[id as usize].generation, generation: self.meta[index as usize].generation,
id, index,
} }
} else { } else {
let id = u32::try_from(self.meta.len()).expect("too many entities"); let index = u32::try_from(self.meta.len()).expect("too many entities");
self.meta.push(EntityMeta::EMPTY); self.meta.push(EntityMeta::EMPTY);
Entity { generation: 0, id } Entity {
generation: 0,
index,
}
} }
} }
@ -407,14 +418,15 @@ impl Entities {
pub fn alloc_at(&mut self, entity: Entity) -> Option<EntityLocation> { pub fn alloc_at(&mut self, entity: Entity) -> Option<EntityLocation> {
self.verify_flushed(); self.verify_flushed();
let loc = if entity.id as usize >= self.meta.len() { let loc = if entity.index as usize >= self.meta.len() {
self.pending.extend((self.meta.len() as u32)..entity.id); self.pending.extend((self.meta.len() as u32)..entity.index);
let new_free_cursor = self.pending.len() as IdCursor; let new_free_cursor = self.pending.len() as IdCursor;
*self.free_cursor.get_mut() = new_free_cursor; *self.free_cursor.get_mut() = new_free_cursor;
self.meta.resize(entity.id as usize + 1, EntityMeta::EMPTY); self.meta
.resize(entity.index as usize + 1, EntityMeta::EMPTY);
self.len += 1; self.len += 1;
None None
} else if let Some(index) = self.pending.iter().position(|item| *item == entity.id) { } else if let Some(index) = self.pending.iter().position(|item| *item == entity.index) {
self.pending.swap_remove(index); self.pending.swap_remove(index);
let new_free_cursor = self.pending.len() as IdCursor; let new_free_cursor = self.pending.len() as IdCursor;
*self.free_cursor.get_mut() = new_free_cursor; *self.free_cursor.get_mut() = new_free_cursor;
@ -422,12 +434,12 @@ impl Entities {
None None
} else { } else {
Some(mem::replace( Some(mem::replace(
&mut self.meta[entity.id as usize].location, &mut self.meta[entity.index as usize].location,
EntityMeta::EMPTY.location, EntityMeta::EMPTY.location,
)) ))
}; };
self.meta[entity.id as usize].generation = entity.generation; self.meta[entity.index as usize].generation = entity.generation;
loc loc
} }
@ -438,21 +450,22 @@ impl Entities {
pub fn alloc_at_without_replacement(&mut self, entity: Entity) -> AllocAtWithoutReplacement { pub fn alloc_at_without_replacement(&mut self, entity: Entity) -> AllocAtWithoutReplacement {
self.verify_flushed(); self.verify_flushed();
let result = if entity.id as usize >= self.meta.len() { let result = if entity.index as usize >= self.meta.len() {
self.pending.extend((self.meta.len() as u32)..entity.id); self.pending.extend((self.meta.len() as u32)..entity.index);
let new_free_cursor = self.pending.len() as IdCursor; let new_free_cursor = self.pending.len() as IdCursor;
*self.free_cursor.get_mut() = new_free_cursor; *self.free_cursor.get_mut() = new_free_cursor;
self.meta.resize(entity.id as usize + 1, EntityMeta::EMPTY); self.meta
.resize(entity.index as usize + 1, EntityMeta::EMPTY);
self.len += 1; self.len += 1;
AllocAtWithoutReplacement::DidNotExist AllocAtWithoutReplacement::DidNotExist
} else if let Some(index) = self.pending.iter().position(|item| *item == entity.id) { } else if let Some(index) = self.pending.iter().position(|item| *item == entity.index) {
self.pending.swap_remove(index); self.pending.swap_remove(index);
let new_free_cursor = self.pending.len() as IdCursor; let new_free_cursor = self.pending.len() as IdCursor;
*self.free_cursor.get_mut() = new_free_cursor; *self.free_cursor.get_mut() = new_free_cursor;
self.len += 1; self.len += 1;
AllocAtWithoutReplacement::DidNotExist AllocAtWithoutReplacement::DidNotExist
} else { } else {
let current_meta = &mut self.meta[entity.id as usize]; let current_meta = &mut self.meta[entity.index as usize];
if current_meta.location.archetype_id == ArchetypeId::INVALID { if current_meta.location.archetype_id == ArchetypeId::INVALID {
AllocAtWithoutReplacement::DidNotExist AllocAtWithoutReplacement::DidNotExist
} else if current_meta.generation == entity.generation { } else if current_meta.generation == entity.generation {
@ -462,7 +475,7 @@ impl Entities {
} }
}; };
self.meta[entity.id as usize].generation = entity.generation; self.meta[entity.index as usize].generation = entity.generation;
result result
} }
@ -472,7 +485,7 @@ impl Entities {
pub fn free(&mut self, entity: Entity) -> Option<EntityLocation> { pub fn free(&mut self, entity: Entity) -> Option<EntityLocation> {
self.verify_flushed(); self.verify_flushed();
let meta = &mut self.meta[entity.id as usize]; let meta = &mut self.meta[entity.index as usize];
if meta.generation != entity.generation { if meta.generation != entity.generation {
return None; return None;
} }
@ -480,7 +493,7 @@ impl Entities {
let loc = mem::replace(&mut meta.location, EntityMeta::EMPTY.location); let loc = mem::replace(&mut meta.location, EntityMeta::EMPTY.location);
self.pending.push(entity.id); self.pending.push(entity.index);
let new_free_cursor = self.pending.len() as IdCursor; let new_free_cursor = self.pending.len() as IdCursor;
*self.free_cursor.get_mut() = new_free_cursor; *self.free_cursor.get_mut() = new_free_cursor;
@ -505,7 +518,7 @@ impl Entities {
// This will return false for entities which have been freed, even if // This will return false for entities which have been freed, even if
// not reallocated since the generation is incremented in `free` // not reallocated since the generation is incremented in `free`
pub fn contains(&self, entity: Entity) -> bool { pub fn contains(&self, entity: Entity) -> bool {
self.resolve_from_id(entity.id()) self.resolve_from_id(entity.index())
.map_or(false, |e| e.generation() == entity.generation) .map_or(false, |e| e.generation() == entity.generation)
} }
@ -518,8 +531,8 @@ impl Entities {
/// Returns `Ok(Location { archetype: Archetype::invalid(), index: undefined })` for pending entities. /// Returns `Ok(Location { archetype: Archetype::invalid(), index: undefined })` for pending entities.
pub fn get(&self, entity: Entity) -> Option<EntityLocation> { pub fn get(&self, entity: Entity) -> Option<EntityLocation> {
if (entity.id as usize) < self.meta.len() { if (entity.index as usize) < self.meta.len() {
let meta = &self.meta[entity.id as usize]; let meta = &self.meta[entity.index as usize];
if meta.generation != entity.generation if meta.generation != entity.generation
|| meta.location.archetype_id == ArchetypeId::INVALID || meta.location.archetype_id == ArchetypeId::INVALID
{ {
@ -537,17 +550,20 @@ impl Entities {
/// Note: This method may return [`Entities`](Entity) which are currently free /// Note: This method may return [`Entities`](Entity) which are currently free
/// Note that [`contains`](Entities::contains) will correctly return false for freed /// Note that [`contains`](Entities::contains) will correctly return false for freed
/// entities, since it checks the generation /// entities, since it checks the generation
pub fn resolve_from_id(&self, id: u32) -> Option<Entity> { pub fn resolve_from_id(&self, index: u32) -> Option<Entity> {
let idu = id as usize; let idu = index as usize;
if let Some(&EntityMeta { generation, .. }) = self.meta.get(idu) { if let Some(&EntityMeta { generation, .. }) = self.meta.get(idu) {
Some(Entity { generation, id }) Some(Entity { generation, index })
} else { } else {
// `id` is outside of the meta list - check whether it is reserved but not yet flushed. // `id` is outside of the meta list - check whether it is reserved but not yet flushed.
let free_cursor = self.free_cursor.load(Ordering::Relaxed); let free_cursor = self.free_cursor.load(Ordering::Relaxed);
// If this entity was manually created, then free_cursor might be positive // If this entity was manually created, then free_cursor might be positive
// Returning None handles that case correctly // Returning None handles that case correctly
let num_pending = usize::try_from(-free_cursor).ok()?; let num_pending = usize::try_from(-free_cursor).ok()?;
(idu < self.meta.len() + num_pending).then_some(Entity { generation: 0, id }) (idu < self.meta.len() + num_pending).then_some(Entity {
generation: 0,
index,
})
} }
} }
@ -576,10 +592,10 @@ impl Entities {
let new_meta_len = old_meta_len + -current_free_cursor as usize; let new_meta_len = old_meta_len + -current_free_cursor as usize;
self.meta.resize(new_meta_len, EntityMeta::EMPTY); self.meta.resize(new_meta_len, EntityMeta::EMPTY);
self.len += -current_free_cursor as u32; self.len += -current_free_cursor as u32;
for (id, meta) in self.meta.iter_mut().enumerate().skip(old_meta_len) { for (index, meta) in self.meta.iter_mut().enumerate().skip(old_meta_len) {
init( init(
Entity { Entity {
id: id as u32, index: index as u32,
generation: meta.generation, generation: meta.generation,
}, },
&mut meta.location, &mut meta.location,
@ -591,11 +607,11 @@ impl Entities {
}; };
self.len += (self.pending.len() - new_free_cursor) as u32; self.len += (self.pending.len() - new_free_cursor) as u32;
for id in self.pending.drain(new_free_cursor..) { for index in self.pending.drain(new_free_cursor..) {
let meta = &mut self.meta[id as usize]; let meta = &mut self.meta[index as usize];
init( init(
Entity { Entity {
id, index,
generation: meta.generation, generation: meta.generation,
}, },
&mut meta.location, &mut meta.location,
@ -684,7 +700,7 @@ mod tests {
fn entity_bits_roundtrip() { fn entity_bits_roundtrip() {
let e = Entity { let e = Entity {
generation: 0xDEADBEEF, generation: 0xDEADBEEF,
id: 0xBAADF00D, index: 0xBAADF00D,
}; };
assert_eq!(Entity::from_bits(e.to_bits()), e); assert_eq!(Entity::from_bits(e.to_bits()), e);
} }
@ -719,14 +735,14 @@ mod tests {
#[test] #[test]
fn entity_const() { fn entity_const() {
const C1: Entity = Entity::from_raw(42); const C1: Entity = Entity::from_raw(42);
assert_eq!(42, C1.id); assert_eq!(42, C1.index);
assert_eq!(0, C1.generation); assert_eq!(0, C1.generation);
const C2: Entity = Entity::from_bits(0x0000_00ff_0000_00cc); const C2: Entity = Entity::from_bits(0x0000_00ff_0000_00cc);
assert_eq!(0x0000_00cc, C2.id); assert_eq!(0x0000_00cc, C2.index);
assert_eq!(0x0000_00ff, C2.generation); assert_eq!(0x0000_00ff, C2.generation);
const C3: u32 = Entity::from_raw(33).id(); const C3: u32 = Entity::from_raw(33).index();
assert_eq!(33, C3); assert_eq!(33, C3);
const C4: u32 = Entity::from_bits(0x00dd_00ff_0000_0000).generation(); const C4: u32 = Entity::from_bits(0x00dd_00ff_0000_0000).generation();

View file

@ -1453,7 +1453,7 @@ mod tests {
e4, e4,
Entity { Entity {
generation: 0, generation: 0,
id: 3, index: 3,
}, },
"new entity is created immediately after world_a's max entity" "new entity is created immediately after world_a's max entity"
); );
@ -1487,7 +1487,7 @@ mod tests {
let e4_mismatched_generation = Entity { let e4_mismatched_generation = Entity {
generation: 1, generation: 1,
id: 3, index: 3,
}; };
assert!( assert!(
world_b.get_or_spawn(e4_mismatched_generation).is_none(), world_b.get_or_spawn(e4_mismatched_generation).is_none(),
@ -1506,7 +1506,7 @@ mod tests {
let high_non_existent_entity = Entity { let high_non_existent_entity = Entity {
generation: 0, generation: 0,
id: 6, index: 6,
}; };
world_b world_b
.get_or_spawn(high_non_existent_entity) .get_or_spawn(high_non_existent_entity)
@ -1520,7 +1520,7 @@ mod tests {
let high_non_existent_but_reserved_entity = Entity { let high_non_existent_but_reserved_entity = Entity {
generation: 0, generation: 0,
id: 5, index: 5,
}; };
assert!( assert!(
world_b.get_entity(high_non_existent_but_reserved_entity).is_none(), world_b.get_entity(high_non_existent_but_reserved_entity).is_none(),
@ -1539,19 +1539,19 @@ mod tests {
vec![ vec![
Entity { Entity {
generation: 0, generation: 0,
id: 5 index: 5
}, },
Entity { Entity {
generation: 0, generation: 0,
id: 4 index: 4
}, },
Entity { Entity {
generation: 0, generation: 0,
id: 7, index: 7,
}, },
Entity { Entity {
generation: 0, generation: 0,
id: 8, index: 8,
}, },
], ],
"space between original entities and high entities is used for new entity ids" "space between original entities and high entities is used for new entity ids"
@ -1603,7 +1603,7 @@ mod tests {
let e2 = world.spawn_empty().id(); let e2 = world.spawn_empty().id();
let invalid_e2 = Entity { let invalid_e2 = Entity {
generation: 1, generation: 1,
id: e2.id, index: e2.index,
}; };
let values = vec![(e0, (B(0), C)), (e1, (B(1), C)), (invalid_e2, (B(2), C))]; let values = vec![(e0, (B(0), C)), (e1, (B(1), C)), (invalid_e2, (B(2), C))];

View file

@ -120,18 +120,18 @@ impl ComponentSparseSet {
/// The `value` pointer must point to a valid address that matches the [`Layout`](std::alloc::Layout) /// The `value` pointer must point to a valid address that matches the [`Layout`](std::alloc::Layout)
/// inside the [`ComponentInfo`] given when constructing this sparse set. /// inside the [`ComponentInfo`] given when constructing this sparse set.
pub(crate) unsafe fn insert(&mut self, entity: Entity, value: OwningPtr<'_>, change_tick: u32) { pub(crate) unsafe fn insert(&mut self, entity: Entity, value: OwningPtr<'_>, change_tick: u32) {
if let Some(&dense_index) = self.sparse.get(entity.id()) { if let Some(&dense_index) = self.sparse.get(entity.index()) {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
assert_eq!(entity, self.entities[dense_index as usize]); assert_eq!(entity, self.entities[dense_index as usize]);
self.dense.replace(dense_index as usize, value, change_tick); self.dense.replace(dense_index as usize, value, change_tick);
} else { } else {
let dense_index = self.dense.len(); let dense_index = self.dense.len();
self.dense.push(value, ComponentTicks::new(change_tick)); self.dense.push(value, ComponentTicks::new(change_tick));
self.sparse.insert(entity.id(), dense_index as u32); self.sparse.insert(entity.index(), dense_index as u32);
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
assert_eq!(self.entities.len(), dense_index); assert_eq!(self.entities.len(), dense_index);
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
self.entities.push(entity.id()); self.entities.push(entity.index());
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
self.entities.push(entity); self.entities.push(entity);
} }
@ -141,7 +141,7 @@ impl ComponentSparseSet {
pub fn contains(&self, entity: Entity) -> bool { pub fn contains(&self, entity: Entity) -> bool {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
{ {
if let Some(&dense_index) = self.sparse.get(entity.id()) { if let Some(&dense_index) = self.sparse.get(entity.index()) {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
assert_eq!(entity, self.entities[dense_index as usize]); assert_eq!(entity, self.entities[dense_index as usize]);
true true
@ -150,12 +150,12 @@ impl ComponentSparseSet {
} }
} }
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
self.sparse.contains(entity.id()) self.sparse.contains(entity.index())
} }
#[inline] #[inline]
pub fn get(&self, entity: Entity) -> Option<Ptr<'_>> { pub fn get(&self, entity: Entity) -> Option<Ptr<'_>> {
self.sparse.get(entity.id()).map(|dense_index| { self.sparse.get(entity.index()).map(|dense_index| {
let dense_index = *dense_index as usize; let dense_index = *dense_index as usize;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
assert_eq!(entity, self.entities[dense_index]); assert_eq!(entity, self.entities[dense_index]);
@ -166,7 +166,7 @@ impl ComponentSparseSet {
#[inline] #[inline]
pub fn get_with_ticks(&self, entity: Entity) -> Option<(Ptr<'_>, &UnsafeCell<ComponentTicks>)> { pub fn get_with_ticks(&self, entity: Entity) -> Option<(Ptr<'_>, &UnsafeCell<ComponentTicks>)> {
let dense_index = *self.sparse.get(entity.id())? as usize; let dense_index = *self.sparse.get(entity.index())? as usize;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
assert_eq!(entity, self.entities[dense_index]); assert_eq!(entity, self.entities[dense_index]);
// SAFETY: if the sparse index points to something in the dense vec, it exists // SAFETY: if the sparse index points to something in the dense vec, it exists
@ -180,7 +180,7 @@ impl ComponentSparseSet {
#[inline] #[inline]
pub fn get_ticks(&self, entity: Entity) -> Option<&UnsafeCell<ComponentTicks>> { pub fn get_ticks(&self, entity: Entity) -> Option<&UnsafeCell<ComponentTicks>> {
let dense_index = *self.sparse.get(entity.id())? as usize; let dense_index = *self.sparse.get(entity.index())? as usize;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
assert_eq!(entity, self.entities[dense_index]); assert_eq!(entity, self.entities[dense_index]);
// SAFETY: if the sparse index points to something in the dense vec, it exists // SAFETY: if the sparse index points to something in the dense vec, it exists
@ -191,7 +191,7 @@ impl ComponentSparseSet {
/// it exists). /// it exists).
#[must_use = "The returned pointer must be used to drop the removed component."] #[must_use = "The returned pointer must be used to drop the removed component."]
pub(crate) fn remove_and_forget(&mut self, entity: Entity) -> Option<OwningPtr<'_>> { pub(crate) fn remove_and_forget(&mut self, entity: Entity) -> Option<OwningPtr<'_>> {
self.sparse.remove(entity.id()).map(|dense_index| { self.sparse.remove(entity.index()).map(|dense_index| {
let dense_index = dense_index as usize; let dense_index = dense_index as usize;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
assert_eq!(entity, self.entities[dense_index]); assert_eq!(entity, self.entities[dense_index]);
@ -202,17 +202,17 @@ impl ComponentSparseSet {
if !is_last { if !is_last {
let swapped_entity = self.entities[dense_index]; let swapped_entity = self.entities[dense_index];
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
let idx = swapped_entity; let index = swapped_entity;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
let idx = swapped_entity.id(); let index = swapped_entity.index();
*self.sparse.get_mut(idx).unwrap() = dense_index as u32; *self.sparse.get_mut(index).unwrap() = dense_index as u32;
} }
value value
}) })
} }
pub(crate) fn remove(&mut self, entity: Entity) -> bool { pub(crate) fn remove(&mut self, entity: Entity) -> bool {
if let Some(dense_index) = self.sparse.remove(entity.id()) { if let Some(dense_index) = self.sparse.remove(entity.index()) {
let dense_index = dense_index as usize; let dense_index = dense_index as usize;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
assert_eq!(entity, self.entities[dense_index]); assert_eq!(entity, self.entities[dense_index]);
@ -223,10 +223,10 @@ impl ComponentSparseSet {
if !is_last { if !is_last {
let swapped_entity = self.entities[dense_index]; let swapped_entity = self.entities[dense_index];
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
let idx = swapped_entity; let index = swapped_entity;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
let idx = swapped_entity.id(); let index = swapped_entity.index();
*self.sparse.get_mut(idx).unwrap() = dense_index as u32; *self.sparse.get_mut(index).unwrap() = dense_index as u32;
} }
true true
} else { } else {

View file

@ -369,7 +369,7 @@ impl<'w> EntityMut<'w> {
let old_archetype = &mut archetypes[old_archetype_id]; let old_archetype = &mut archetypes[old_archetype_id];
let remove_result = old_archetype.swap_remove(old_location.index); let remove_result = old_archetype.swap_remove(old_location.index);
if let Some(swapped_entity) = remove_result.swapped_entity { if let Some(swapped_entity) = remove_result.swapped_entity {
entities.meta[swapped_entity.id as usize].location = old_location; entities.meta[swapped_entity.index as usize].location = old_location;
} }
let old_table_row = remove_result.table_row; let old_table_row = remove_result.table_row;
let old_table_id = old_archetype.table_id(); let old_table_id = old_archetype.table_id();
@ -403,7 +403,7 @@ impl<'w> EntityMut<'w> {
}; };
*self_location = new_location; *self_location = new_location;
entities.meta[entity.id as usize].location = new_location; entities.meta[entity.index as usize].location = new_location;
} }
#[deprecated( #[deprecated(
@ -498,7 +498,7 @@ impl<'w> EntityMut<'w> {
} }
let remove_result = archetype.swap_remove(location.index); let remove_result = archetype.swap_remove(location.index);
if let Some(swapped_entity) = remove_result.swapped_entity { if let Some(swapped_entity) = remove_result.swapped_entity {
world.entities.meta[swapped_entity.id as usize].location = location; world.entities.meta[swapped_entity.index as usize].location = location;
} }
table_row = remove_result.table_row; table_row = remove_result.table_row;

View file

@ -483,7 +483,7 @@ impl World {
// SAFETY: entity index was just allocated // SAFETY: entity index was just allocated
self.entities self.entities
.meta .meta
.get_unchecked_mut(entity.id() as usize) .get_unchecked_mut(entity.index() as usize)
.location = location; .location = location;
EntityMut::new(self, entity, location) EntityMut::new(self, entity, location)
} }

View file

@ -8,7 +8,7 @@ use std::collections::BTreeMap;
/// ///
/// # Entity Order /// # Entity Order
/// ///
/// Extracted entities will always be stored in ascending order based on their [id](Entity::id). /// Extracted entities will always be stored in ascending order based on their [id](Entity::index).
/// This means that inserting `Entity(1v0)` then `Entity(0v0)` will always result in the entities /// This means that inserting `Entity(1v0)` then `Entity(0v0)` will always result in the entities
/// being ordered as `[Entity(0v0), Entity(1v0)]`. /// being ordered as `[Entity(0v0), Entity(1v0)]`.
/// ///
@ -100,14 +100,14 @@ impl<'w> DynamicSceneBuilder<'w> {
let type_registry = self.type_registry.read(); let type_registry = self.type_registry.read();
for entity in entities { for entity in entities {
let id = entity.id(); let index = entity.index();
if self.entities.contains_key(&id) { if self.entities.contains_key(&index) {
continue; continue;
} }
let mut entry = DynamicEntity { let mut entry = DynamicEntity {
entity: id, entity: index,
components: Vec::new(), components: Vec::new(),
}; };
@ -125,8 +125,7 @@ impl<'w> DynamicSceneBuilder<'w> {
} }
} }
} }
self.entities.insert(index, entry);
self.entities.insert(id, entry);
} }
drop(type_registry); drop(type_registry);
@ -167,7 +166,7 @@ mod tests {
let scene = builder.build(); let scene = builder.build();
assert_eq!(scene.entities.len(), 1); assert_eq!(scene.entities.len(), 1);
assert_eq!(scene.entities[0].entity, entity.id()); assert_eq!(scene.entities[0].entity, entity.index());
assert_eq!(scene.entities[0].components.len(), 1); assert_eq!(scene.entities[0].components.len(), 1);
assert!(scene.entities[0].components[0].represents::<ComponentA>()); assert!(scene.entities[0].components[0].represents::<ComponentA>());
} }
@ -188,7 +187,7 @@ mod tests {
let scene = builder.build(); let scene = builder.build();
assert_eq!(scene.entities.len(), 1); assert_eq!(scene.entities.len(), 1);
assert_eq!(scene.entities[0].entity, entity.id()); assert_eq!(scene.entities[0].entity, entity.index());
assert_eq!(scene.entities[0].components.len(), 1); assert_eq!(scene.entities[0].components.len(), 1);
assert!(scene.entities[0].components[0].represents::<ComponentA>()); assert!(scene.entities[0].components[0].represents::<ComponentA>());
} }
@ -212,7 +211,7 @@ mod tests {
let scene = builder.build(); let scene = builder.build();
assert_eq!(scene.entities.len(), 1); assert_eq!(scene.entities.len(), 1);
assert_eq!(scene.entities[0].entity, entity.id()); assert_eq!(scene.entities[0].entity, entity.index());
assert_eq!(scene.entities[0].components.len(), 2); assert_eq!(scene.entities[0].components.len(), 2);
assert!(scene.entities[0].components[0].represents::<ComponentA>()); assert!(scene.entities[0].components[0].represents::<ComponentA>());
assert!(scene.entities[0].components[1].represents::<ComponentB>()); assert!(scene.entities[0].components[1].represents::<ComponentB>());
@ -239,10 +238,10 @@ mod tests {
let mut entities = builder.build().entities.into_iter(); let mut entities = builder.build().entities.into_iter();
// Assert entities are ordered // Assert entities are ordered
assert_eq!(entity_a.id(), entities.next().map(|e| e.entity).unwrap()); assert_eq!(entity_a.index(), entities.next().map(|e| e.entity).unwrap());
assert_eq!(entity_b.id(), entities.next().map(|e| e.entity).unwrap()); assert_eq!(entity_b.index(), entities.next().map(|e| e.entity).unwrap());
assert_eq!(entity_c.id(), entities.next().map(|e| e.entity).unwrap()); assert_eq!(entity_c.index(), entities.next().map(|e| e.entity).unwrap());
assert_eq!(entity_d.id(), entities.next().map(|e| e.entity).unwrap()); assert_eq!(entity_d.index(), entities.next().map(|e| e.entity).unwrap());
} }
#[test] #[test]
@ -269,6 +268,6 @@ mod tests {
assert_eq!(scene.entities.len(), 2); assert_eq!(scene.entities.len(), 2);
let mut scene_entities = vec![scene.entities[0].entity, scene.entities[1].entity]; let mut scene_entities = vec![scene.entities[0].entity, scene.entities[1].entity];
scene_entities.sort(); scene_entities.sort();
assert_eq!(scene_entities, [entity_a_b.id(), entity_a.id()]); assert_eq!(scene_entities, [entity_a_b.index(), entity_a.index()]);
} }
} }

View file

@ -525,7 +525,7 @@ pub fn queue_sprites(
); );
view_entities.clear(); view_entities.clear();
view_entities.extend(visible_entities.entities.iter().map(|e| e.id() as usize)); view_entities.extend(visible_entities.entities.iter().map(|e| e.index() as usize));
transparent_phase.items.reserve(extracted_sprites.len()); transparent_phase.items.reserve(extracted_sprites.len());
// Impossible starting values that will be replaced on the first iteration // Impossible starting values that will be replaced on the first iteration
@ -541,7 +541,7 @@ pub fn queue_sprites(
// Batches are merged later (in `batch_phase_system()`), so that they can be interrupted // Batches are merged later (in `batch_phase_system()`), so that they can be interrupted
// by any other phase item (and they can interrupt other items from batching). // by any other phase item (and they can interrupt other items from batching).
for extracted_sprite in extracted_sprites.iter() { for extracted_sprite in extracted_sprites.iter() {
if !view_entities.contains(extracted_sprite.entity.id() as usize) { if !view_entities.contains(extracted_sprite.entity.index() as usize) {
continue; continue;
} }
let new_batch = SpriteBatch { let new_batch = SpriteBatch {

View file

@ -76,7 +76,7 @@ fn load_scene_system(mut commands: Commands, asset_server: Res<AssetServer>) {
// load_scene_example.scn. You should immediately see the changes appear in the console. // load_scene_example.scn. You should immediately see the changes appear in the console.
fn log_system(query: Query<(Entity, &ComponentA), Changed<ComponentA>>) { fn log_system(query: Query<(Entity, &ComponentA), Changed<ComponentA>>) {
for (entity, component_a) in &query { for (entity, component_a) in &query {
info!(" Entity({})", entity.id()); info!(" Entity({})", entity.index());
info!( info!(
" ComponentA: {{ x: {} y: {} }}\n", " ComponentA: {{ x: {} y: {} }}\n",
component_a.x, component_a.y component_a.x, component_a.y