//! Entity handling types.1//!2//! An **entity** exclusively owns zero or more [component] instances, all of different types, and can dynamically acquire or lose them over its lifetime.3//!4//! **empty entity**: Entity with zero components.5//! **pending entity**: Entity reserved, but not flushed yet (see [`Entities::flush`] docs for reference).6//! **reserved entity**: same as **pending entity**.7//! **invalid entity**: **pending entity** flushed with invalid (see [`Entities::flush_as_invalid`] docs for reference).8//!9//! See [`Entity`] to learn more.10//!11//! [component]: crate::component::Component12//!13//! # Usage14//!15//! Operations involving entities and their components are performed either from a system by submitting commands,16//! or from the outside (or from an exclusive system) by directly using [`World`] methods:17//!18//! |Operation|Command|Method|19//! |:---:|:---:|:---:|20//! |Spawn an entity with components|[`Commands::spawn`]|[`World::spawn`]|21//! |Spawn an entity without components|[`Commands::spawn_empty`]|[`World::spawn_empty`]|22//! |Despawn an entity|[`EntityCommands::despawn`]|[`World::despawn`]|23//! |Insert a component, bundle, or tuple of components and bundles to an entity|[`EntityCommands::insert`]|[`EntityWorldMut::insert`]|24//! |Remove a component, bundle, or tuple of components and bundles from an entity|[`EntityCommands::remove`]|[`EntityWorldMut::remove`]|25//!26//! [`World`]: crate::world::World27//! [`Commands::spawn`]: crate::system::Commands::spawn28//! [`Commands::spawn_empty`]: crate::system::Commands::spawn_empty29//! [`EntityCommands::despawn`]: crate::system::EntityCommands::despawn30//! [`EntityCommands::insert`]: crate::system::EntityCommands::insert31//! [`EntityCommands::remove`]: crate::system::EntityCommands::remove32//! [`World::spawn`]: crate::world::World::spawn33//! [`World::spawn_empty`]: crate::world::World::spawn_empty34//! [`World::despawn`]: crate::world::World::despawn35//! [`EntityWorldMut::insert`]: crate::world::EntityWorldMut::insert36//! [`EntityWorldMut::remove`]: crate::world::EntityWorldMut::remove3738mod clone_entities;39mod entity_set;40mod map_entities;41#[cfg(feature = "bevy_reflect")]42use bevy_reflect::Reflect;43#[cfg(all(feature = "bevy_reflect", feature = "serialize"))]44use bevy_reflect::{ReflectDeserialize, ReflectSerialize};4546pub use clone_entities::*;47use derive_more::derive::Display;48pub use entity_set::*;49pub use map_entities::*;5051mod hash;52pub use hash::*;5354pub mod hash_map;55pub mod hash_set;5657pub use hash_map::EntityHashMap;58pub use hash_set::EntityHashSet;5960pub mod index_map;61pub mod index_set;6263pub use index_map::EntityIndexMap;64pub use index_set::EntityIndexSet;6566pub mod unique_array;67pub mod unique_slice;68pub mod unique_vec;6970use nonmax::NonMaxU32;71pub use unique_array::{UniqueEntityArray, UniqueEntityEquivalentArray};72pub use unique_slice::{UniqueEntityEquivalentSlice, UniqueEntitySlice};73pub use unique_vec::{UniqueEntityEquivalentVec, UniqueEntityVec};7475use crate::{76archetype::{ArchetypeId, ArchetypeRow},77change_detection::MaybeLocation,78component::{CheckChangeTicks, Tick},79storage::{SparseSetIndex, TableId, TableRow},80};81use alloc::vec::Vec;82use bevy_platform::sync::atomic::Ordering;83use core::{fmt, hash::Hash, mem, num::NonZero, panic::Location};84use log::warn;8586#[cfg(feature = "serialize")]87use serde::{Deserialize, Serialize};8889#[cfg(target_has_atomic = "64")]90use bevy_platform::sync::atomic::AtomicI64 as AtomicIdCursor;91#[cfg(target_has_atomic = "64")]92type IdCursor = i64;9394/// Most modern platforms support 64-bit atomics, but some less-common platforms95/// do not. This fallback allows compilation using a 32-bit cursor instead, with96/// the caveat that some conversions may fail (and panic) at runtime.97#[cfg(not(target_has_atomic = "64"))]98use bevy_platform::sync::atomic::AtomicIsize as AtomicIdCursor;99#[cfg(not(target_has_atomic = "64"))]100type IdCursor = isize;101102/// This represents the row or "index" of an [`Entity`] within the [`Entities`] table.103/// This is a lighter weight version of [`Entity`].104///105/// This is a unique identifier for an entity in the world.106/// This differs from [`Entity`] in that [`Entity`] is unique for all entities total (unless the [`Entity::generation`] wraps),107/// but this is only unique for entities that are active.108///109/// This can be used over [`Entity`] to improve performance in some cases,110/// but improper use can cause this to identify a different entity than intended.111/// Use with caution.112#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)]113#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]114#[cfg_attr(feature = "bevy_reflect", reflect(opaque))]115#[cfg_attr(feature = "bevy_reflect", reflect(Hash, PartialEq, Debug, Clone))]116#[repr(transparent)]117pub struct EntityRow(NonMaxU32);118119impl EntityRow {120const PLACEHOLDER: Self = Self(NonMaxU32::MAX);121122/// Constructs a new [`EntityRow`] from its index.123pub const fn new(index: NonMaxU32) -> Self {124Self(index)125}126127/// Gets the index of the entity.128#[inline(always)]129pub const fn index(self) -> u32 {130self.0.get()131}132133/// Gets some bits that represent this value.134/// The bits are opaque and should not be regarded as meaningful.135#[inline(always)]136const fn to_bits(self) -> u32 {137// SAFETY: NonMax is repr transparent.138unsafe { mem::transmute::<NonMaxU32, u32>(self.0) }139}140141/// Reconstruct an [`EntityRow`] previously destructured with [`EntityRow::to_bits`].142///143/// Only useful when applied to results from `to_bits` in the same instance of an application.144///145/// # Panics146///147/// This method will likely panic if given `u32` values that did not come from [`EntityRow::to_bits`].148#[inline]149const fn from_bits(bits: u32) -> Self {150Self::try_from_bits(bits).expect("Attempted to initialize invalid bits as an entity row")151}152153/// Reconstruct an [`EntityRow`] previously destructured with [`EntityRow::to_bits`].154///155/// Only useful when applied to results from `to_bits` in the same instance of an application.156///157/// This method is the fallible counterpart to [`EntityRow::from_bits`].158#[inline(always)]159const fn try_from_bits(bits: u32) -> Option<Self> {160match NonZero::<u32>::new(bits) {161// SAFETY: NonMax and NonZero are repr transparent.162Some(underlying) => Some(Self(unsafe {163mem::transmute::<NonZero<u32>, NonMaxU32>(underlying)164})),165None => None,166}167}168}169170impl SparseSetIndex for EntityRow {171#[inline]172fn sparse_set_index(&self) -> usize {173self.index() as usize174}175176#[inline]177fn get_sparse_set_index(value: usize) -> Self {178Self::from_bits(value as u32)179}180}181182/// This tracks different versions or generations of an [`EntityRow`].183/// Importantly, this can wrap, meaning each generation is not necessarily unique per [`EntityRow`].184///185/// This should be treated as a opaque identifier, and its internal representation may be subject to change.186///187/// # Aliasing188///189/// Internally [`EntityGeneration`] wraps a `u32`, so it can't represent *every* possible generation.190/// Eventually, generations can (and do) wrap or alias.191/// This can cause [`Entity`] and [`EntityGeneration`] values to be equal while still referring to different conceptual entities.192/// This can cause some surprising behavior:193///194/// ```195/// # use bevy_ecs::entity::EntityGeneration;196/// let (aliased, did_alias) = EntityGeneration::FIRST.after_versions(1u32 << 31).after_versions_and_could_alias(1u32 << 31);197/// assert!(did_alias);198/// assert!(EntityGeneration::FIRST == aliased);199/// ```200///201/// This can cause some unintended side effects.202/// See [`Entity`] docs for practical concerns and how to minimize any risks.203#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Display)]204#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]205#[cfg_attr(feature = "bevy_reflect", reflect(opaque))]206#[cfg_attr(feature = "bevy_reflect", reflect(Hash, PartialEq, Debug, Clone))]207#[repr(transparent)]208pub struct EntityGeneration(u32);209210impl EntityGeneration {211/// Represents the first generation of an [`EntityRow`].212pub const FIRST: Self = Self(0);213214/// Non-wrapping difference between two generations after which a signed interpretation becomes negative.215const DIFF_MAX: u32 = 1u32 << 31;216217/// Gets some bits that represent this value.218/// The bits are opaque and should not be regarded as meaningful.219#[inline(always)]220pub const fn to_bits(self) -> u32 {221self.0222}223224/// Reconstruct an [`EntityGeneration`] previously destructured with [`EntityGeneration::to_bits`].225///226/// Only useful when applied to results from `to_bits` in the same instance of an application.227#[inline]228pub const fn from_bits(bits: u32) -> Self {229Self(bits)230}231232/// Returns the [`EntityGeneration`] that would result from this many more `versions` of the corresponding [`EntityRow`] from passing.233#[inline]234pub const fn after_versions(self, versions: u32) -> Self {235Self(self.0.wrapping_add(versions))236}237238/// Identical to [`after_versions`](Self::after_versions) but also returns a `bool` indicating if,239/// after these `versions`, one such version could conflict with a previous one.240///241/// If this happens, this will no longer uniquely identify a version of an [`EntityRow`].242/// This is called entity aliasing.243#[inline]244pub const fn after_versions_and_could_alias(self, versions: u32) -> (Self, bool) {245let raw = self.0.overflowing_add(versions);246(Self(raw.0), raw.1)247}248249/// Compares two generations.250///251/// Generations that are later will be [`Greater`](core::cmp::Ordering::Greater) than earlier ones.252///253/// ```254/// # use bevy_ecs::entity::EntityGeneration;255/// # use core::cmp::Ordering;256/// let later_generation = EntityGeneration::FIRST.after_versions(400);257/// assert_eq!(EntityGeneration::FIRST.cmp_approx(&later_generation), Ordering::Less);258///259/// let (aliased, did_alias) = EntityGeneration::FIRST.after_versions(400).after_versions_and_could_alias(u32::MAX);260/// assert!(did_alias);261/// assert_eq!(EntityGeneration::FIRST.cmp_approx(&aliased), Ordering::Less);262/// ```263///264/// Ordering will be incorrect and [non-transitive](https://en.wikipedia.org/wiki/Transitive_relation)265/// for distant generations:266///267/// ```should_panic268/// # use bevy_ecs::entity::EntityGeneration;269/// # use core::cmp::Ordering;270/// let later_generation = EntityGeneration::FIRST.after_versions(3u32 << 31);271/// let much_later_generation = later_generation.after_versions(3u32 << 31);272///273/// // while these orderings are correct and pass assertions...274/// assert_eq!(EntityGeneration::FIRST.cmp_approx(&later_generation), Ordering::Less);275/// assert_eq!(later_generation.cmp_approx(&much_later_generation), Ordering::Less);276///277/// // ... this ordering is not and the assertion fails!278/// assert_eq!(EntityGeneration::FIRST.cmp_approx(&much_later_generation), Ordering::Less);279/// ```280///281/// Because of this, `EntityGeneration` does not implement `Ord`/`PartialOrd`.282#[inline]283pub const fn cmp_approx(&self, other: &Self) -> core::cmp::Ordering {284use core::cmp::Ordering;285match self.0.wrapping_sub(other.0) {2860 => Ordering::Equal,2871..Self::DIFF_MAX => Ordering::Greater,288_ => Ordering::Less,289}290}291}292293/// Lightweight identifier of an [entity](crate::entity).294///295/// The identifier is implemented using a [generational index]: a combination of an index ([`EntityRow`]) and a generation ([`EntityGeneration`]).296/// This allows fast insertion after data removal in an array while minimizing loss of spatial locality.297///298/// These identifiers are only valid on the [`World`] it's sourced from. Attempting to use an `Entity` to299/// fetch entity components or metadata from a different world will either fail or return unexpected results.300///301/// [generational index]: https://lucassardois.medium.com/generational-indices-guide-8e3c5f7fd594302///303/// # Aliasing304///305/// Once an entity is despawned, it ceases to exist.306/// However, its [`Entity`] id is still present, and may still be contained in some data.307/// This becomes problematic because it is possible for a later entity to be spawned at the exact same id!308/// If this happens, which is rare but very possible, it will be logged.309///310/// Aliasing can happen without warning.311/// Holding onto a [`Entity`] id corresponding to an entity well after that entity was despawned can cause un-intuitive behavior for both ordering, and comparing in general.312/// To prevent these bugs, it is generally best practice to stop holding an [`Entity`] or [`EntityGeneration`] value as soon as you know it has been despawned.313/// If you must do otherwise, do not assume the [`Entity`] corresponds to the same conceptual entity it originally did.314/// See [`EntityGeneration`]'s docs for more information about aliasing and why it occurs.315///316/// # Stability warning317/// For all intents and purposes, `Entity` should be treated as an opaque identifier. The internal bit318/// representation is liable to change from release to release as are the behaviors or performance319/// characteristics of any of its trait implementations (i.e. `Ord`, `Hash`, etc.). This means that changes in320/// `Entity`'s representation, though made readable through various functions on the type, are not considered321/// breaking changes under [SemVer].322///323/// In particular, directly serializing with `Serialize` and `Deserialize` make zero guarantee of long324/// term wire format compatibility. Changes in behavior will cause serialized `Entity` values persisted325/// to long term storage (i.e. disk, databases, etc.) will fail to deserialize upon being updated.326///327/// # Usage328///329/// This data type is returned by iterating a `Query` that has `Entity` as part of its query fetch type parameter ([learn more]).330/// It can also be obtained by calling [`EntityCommands::id`] or [`EntityWorldMut::id`].331///332/// ```333/// # use bevy_ecs::prelude::*;334/// # #[derive(Component)]335/// # struct SomeComponent;336/// fn setup(mut commands: Commands) {337/// // Calling `spawn` returns `EntityCommands`.338/// let entity = commands.spawn(SomeComponent).id();339/// }340///341/// fn exclusive_system(world: &mut World) {342/// // Calling `spawn` returns `EntityWorldMut`.343/// let entity = world.spawn(SomeComponent).id();344/// }345/// #346/// # bevy_ecs::system::assert_is_system(setup);347/// # bevy_ecs::system::assert_is_system(exclusive_system);348/// ```349///350/// It can be used to refer to a specific entity to apply [`EntityCommands`], or to call [`Query::get`] (or similar methods) to access its components.351///352/// ```353/// # use bevy_ecs::prelude::*;354/// #355/// # #[derive(Component)]356/// # struct Expired;357/// #358/// fn dispose_expired_food(mut commands: Commands, query: Query<Entity, With<Expired>>) {359/// for food_entity in &query {360/// commands.entity(food_entity).despawn();361/// }362/// }363/// #364/// # bevy_ecs::system::assert_is_system(dispose_expired_food);365/// ```366///367/// [learn more]: crate::system::Query#entity-id-access368/// [`EntityCommands::id`]: crate::system::EntityCommands::id369/// [`EntityWorldMut::id`]: crate::world::EntityWorldMut::id370/// [`EntityCommands`]: crate::system::EntityCommands371/// [`Query::get`]: crate::system::Query::get372/// [`World`]: crate::world::World373/// [SemVer]: https://semver.org/374#[derive(Clone, Copy)]375#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]376#[cfg_attr(feature = "bevy_reflect", reflect(opaque))]377#[cfg_attr(feature = "bevy_reflect", reflect(Hash, PartialEq, Debug, Clone))]378#[cfg_attr(379all(feature = "bevy_reflect", feature = "serialize"),380reflect(Serialize, Deserialize)381)]382// Alignment repr necessary to allow LLVM to better output383// optimized codegen for `to_bits`, `PartialEq` and `Ord`.384#[repr(C, align(8))]385pub struct Entity {386// Do not reorder the fields here. The ordering is explicitly used by repr(C)387// to make this struct equivalent to a u64.388#[cfg(target_endian = "little")]389row: EntityRow,390generation: EntityGeneration,391#[cfg(target_endian = "big")]392row: EntityRow,393}394395// By not short-circuiting in comparisons, we get better codegen.396// See <https://github.com/rust-lang/rust/issues/117800>397impl PartialEq for Entity {398#[inline]399fn eq(&self, other: &Entity) -> bool {400// By using `to_bits`, the codegen can be optimized out even401// further potentially. Relies on the correct alignment/field402// order of `Entity`.403self.to_bits() == other.to_bits()404}405}406407impl Eq for Entity {}408409// The derive macro codegen output is not optimal and can't be optimized as well410// by the compiler. This impl resolves the issue of non-optimal codegen by relying411// on comparing against the bit representation of `Entity` instead of comparing412// the fields. The result is then LLVM is able to optimize the codegen for Entity413// far beyond what the derive macro can.414// See <https://github.com/rust-lang/rust/issues/106107>415impl PartialOrd for Entity {416#[inline]417fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {418// Make use of our `Ord` impl to ensure optimal codegen output419Some(self.cmp(other))420}421}422423// The derive macro codegen output is not optimal and can't be optimized as well424// by the compiler. This impl resolves the issue of non-optimal codegen by relying425// on comparing against the bit representation of `Entity` instead of comparing426// the fields. The result is then LLVM is able to optimize the codegen for Entity427// far beyond what the derive macro can.428// See <https://github.com/rust-lang/rust/issues/106107>429impl Ord for Entity {430#[inline]431fn cmp(&self, other: &Self) -> core::cmp::Ordering {432// This will result in better codegen for ordering comparisons, plus433// avoids pitfalls with regards to macro codegen relying on property434// position when we want to compare against the bit representation.435self.to_bits().cmp(&other.to_bits())436}437}438439impl Hash for Entity {440#[inline]441fn hash<H: core::hash::Hasher>(&self, state: &mut H) {442self.to_bits().hash(state);443}444}445446impl Entity {447/// Construct an [`Entity`] from a raw `row` value and a non-zero `generation` value.448/// Ensure that the generation value is never greater than `0x7FFF_FFFF`.449#[inline(always)]450pub(crate) const fn from_raw_and_generation(451row: EntityRow,452generation: EntityGeneration,453) -> Entity {454Self { row, generation }455}456457/// An entity ID with a placeholder value. This may or may not correspond to an actual entity,458/// and should be overwritten by a new value before being used.459///460/// ## Examples461///462/// Initializing a collection (e.g. `array` or `Vec`) with a known size:463///464/// ```no_run465/// # use bevy_ecs::prelude::*;466/// // Create a new array of size 10 filled with invalid entity ids.467/// let mut entities: [Entity; 10] = [Entity::PLACEHOLDER; 10];468///469/// // ... replace the entities with valid ones.470/// ```471///472/// Deriving [`Reflect`] for a component that has an `Entity` field:473///474/// ```no_run475/// # use bevy_ecs::{prelude::*, component::*};476/// # use bevy_reflect::Reflect;477/// #[derive(Reflect, Component)]478/// #[reflect(Component)]479/// pub struct MyStruct {480/// pub entity: Entity,481/// }482///483/// impl FromWorld for MyStruct {484/// fn from_world(_world: &mut World) -> Self {485/// Self {486/// entity: Entity::PLACEHOLDER,487/// }488/// }489/// }490/// ```491pub const PLACEHOLDER: Self = Self::from_raw(EntityRow::PLACEHOLDER);492493/// Creates a new entity ID with the specified `row` and a generation of 1.494///495/// # Note496///497/// Spawning a specific `entity` value is __rarely the right choice__. Most apps should favor498/// [`Commands::spawn`](crate::system::Commands::spawn). This method should generally499/// only be used for sharing entities across apps, and only when they have a scheme500/// worked out to share an index space (which doesn't happen by default).501///502/// In general, one should not try to synchronize the ECS by attempting to ensure that503/// `Entity` lines up between instances, but instead insert a secondary identifier as504/// a component.505#[inline(always)]506pub const fn from_raw(row: EntityRow) -> Entity {507Self::from_raw_and_generation(row, EntityGeneration::FIRST)508}509510/// This is equivalent to [`from_raw`](Self::from_raw) except that it takes a `u32` instead of an [`EntityRow`].511///512/// Returns `None` if the row is `u32::MAX`.513#[inline(always)]514pub const fn from_raw_u32(row: u32) -> Option<Entity> {515match NonMaxU32::new(row) {516Some(row) => Some(Self::from_raw(EntityRow::new(row))),517None => None,518}519}520521/// Convert to a form convenient for passing outside of rust.522///523/// Only useful for identifying entities within the same instance of an application. Do not use524/// for serialization between runs.525///526/// No particular structure is guaranteed for the returned bits.527#[inline(always)]528pub const fn to_bits(self) -> u64 {529self.row.to_bits() as u64 | ((self.generation.to_bits() as u64) << 32)530}531532/// Reconstruct an `Entity` previously destructured with [`Entity::to_bits`].533///534/// Only useful when applied to results from `to_bits` in the same instance of an application.535///536/// # Panics537///538/// This method will likely panic if given `u64` values that did not come from [`Entity::to_bits`].539#[inline]540pub const fn from_bits(bits: u64) -> Self {541if let Some(id) = Self::try_from_bits(bits) {542id543} else {544panic!("Attempted to initialize invalid bits as an entity")545}546}547548/// Reconstruct an `Entity` previously destructured with [`Entity::to_bits`].549///550/// Only useful when applied to results from `to_bits` in the same instance of an application.551///552/// This method is the fallible counterpart to [`Entity::from_bits`].553#[inline(always)]554pub const fn try_from_bits(bits: u64) -> Option<Self> {555let raw_row = bits as u32;556let raw_gen = (bits >> 32) as u32;557558if let Some(row) = EntityRow::try_from_bits(raw_row) {559Some(Self {560row,561generation: EntityGeneration::from_bits(raw_gen),562})563} else {564None565}566}567568/// Return a transiently unique identifier.569/// See also [`EntityRow`].570///571/// No two simultaneously-live entities share the same row, but dead entities' indices may collide572/// with both live and dead entities. Useful for compactly representing entities within a573/// specific snapshot of the world, such as when serializing.574#[inline]575pub const fn row(self) -> EntityRow {576self.row577}578579/// Equivalent to `self.row().index()`. See [`Self::row`] for details.580#[inline]581pub const fn index(self) -> u32 {582self.row.index()583}584585/// Returns the generation of this Entity's row. The generation is incremented each time an586/// entity with a given row is despawned. This serves as a "count" of the number of times a587/// given row has been reused (row, generation) pairs uniquely identify a given Entity.588#[inline]589pub const fn generation(self) -> EntityGeneration {590self.generation591}592}593594#[cfg(feature = "serialize")]595impl Serialize for Entity {596fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>597where598S: serde::Serializer,599{600serializer.serialize_u64(self.to_bits())601}602}603604#[cfg(feature = "serialize")]605impl<'de> Deserialize<'de> for Entity {606fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>607where608D: serde::Deserializer<'de>,609{610use serde::de::Error;611let id: u64 = Deserialize::deserialize(deserializer)?;612Entity::try_from_bits(id)613.ok_or_else(|| D::Error::custom("Attempting to deserialize an invalid entity."))614}615}616617/// Outputs the short entity identifier, including the index and generation.618///619/// This takes the format: `{index}v{generation}`.620///621/// For [`Entity::PLACEHOLDER`], this outputs `PLACEHOLDER`.622///623/// For a unique [`u64`] representation, use [`Entity::to_bits`].624impl fmt::Debug for Entity {625fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {626fmt::Display::fmt(self, f)627}628}629630/// Outputs the short entity identifier, including the index and generation.631///632/// This takes the format: `{index}v{generation}`.633///634/// For [`Entity::PLACEHOLDER`], this outputs `PLACEHOLDER`.635impl fmt::Display for Entity {636fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {637if self == &Self::PLACEHOLDER {638write!(f, "PLACEHOLDER")639} else {640write!(f, "{}v{}", self.index(), self.generation())641}642}643}644645impl SparseSetIndex for Entity {646#[inline]647fn sparse_set_index(&self) -> usize {648self.row().sparse_set_index()649}650651#[inline]652fn get_sparse_set_index(value: usize) -> Self {653Entity::from_raw(EntityRow::get_sparse_set_index(value))654}655}656657/// An [`Iterator`] returning a sequence of [`Entity`] values from658pub struct ReserveEntitiesIterator<'a> {659// Metas, so we can recover the current generation for anything in the freelist.660meta: &'a [EntityMeta],661662// Reserved indices formerly in the freelist to hand out.663freelist_indices: core::slice::Iter<'a, EntityRow>,664665// New Entity indices to hand out, outside the range of meta.len().666new_indices: core::ops::Range<u32>,667}668669impl<'a> Iterator for ReserveEntitiesIterator<'a> {670type Item = Entity;671672fn next(&mut self) -> Option<Self::Item> {673self.freelist_indices674.next()675.map(|&row| {676Entity::from_raw_and_generation(row, self.meta[row.index() as usize].generation)677})678.or_else(|| {679self.new_indices.next().map(|index| {680// SAFETY: This came from an exclusive range so the max can't be hit.681let row = unsafe { EntityRow::new(NonMaxU32::new_unchecked(index)) };682Entity::from_raw(row)683})684})685}686687fn size_hint(&self) -> (usize, Option<usize>) {688let len = self.freelist_indices.len() + self.new_indices.len();689(len, Some(len))690}691}692693impl<'a> ExactSizeIterator for ReserveEntitiesIterator<'a> {}694695impl<'a> core::iter::FusedIterator for ReserveEntitiesIterator<'a> {}696697// SAFETY: Newly reserved entity values are unique.698unsafe impl EntitySetIterator for ReserveEntitiesIterator<'_> {}699700/// A [`World`]'s internal metadata store on all of its entities.701///702/// Contains metadata on:703/// - The generation of every entity.704/// - The alive/dead status of a particular entity. (i.e. "has entity 3 been despawned?")705/// - The location of the entity's components in memory (via [`EntityLocation`])706///707/// [`World`]: crate::world::World708#[derive(Debug)]709pub struct Entities {710meta: Vec<EntityMeta>,711712/// The `pending` and `free_cursor` fields describe three sets of Entity IDs713/// that have been freed or are in the process of being allocated:714///715/// - The `freelist` IDs, previously freed by `free()`. These IDs are available to any of716/// [`alloc`], [`reserve_entity`] or [`reserve_entities`]. Allocation will always prefer717/// these over brand new IDs.718///719/// - The `reserved` list of IDs that were once in the freelist, but got reserved by720/// [`reserve_entities`] or [`reserve_entity`]. They are now waiting for [`flush`] to make them721/// fully allocated.722///723/// - The count of new IDs that do not yet exist in `self.meta`, but which we have handed out724/// and reserved. [`flush`] will allocate room for them in `self.meta`.725///726/// The contents of `pending` look like this:727///728/// ```txt729/// ----------------------------730/// | freelist | reserved |731/// ----------------------------732/// ^ ^733/// free_cursor pending.len()734/// ```735///736/// As IDs are allocated, `free_cursor` is atomically decremented, moving737/// items from the freelist into the reserved list by sliding over the boundary.738///739/// Once the freelist runs out, `free_cursor` starts going negative.740/// The more negative it is, the more IDs have been reserved starting exactly at741/// the end of `meta.len()`.742///743/// This formulation allows us to reserve any number of IDs first from the freelist744/// and then from the new IDs, using only a single atomic subtract.745///746/// Once [`flush`] is done, `free_cursor` will equal `pending.len()`.747///748/// [`alloc`]: Entities::alloc749/// [`reserve_entity`]: Entities::reserve_entity750/// [`reserve_entities`]: Entities::reserve_entities751/// [`flush`]: Entities::flush752pending: Vec<EntityRow>,753free_cursor: AtomicIdCursor,754}755756impl Entities {757pub(crate) const fn new() -> Self {758Entities {759meta: Vec::new(),760pending: Vec::new(),761free_cursor: AtomicIdCursor::new(0),762}763}764765/// Reserve entity IDs concurrently.766///767/// Storage for entity generation and location is lazily allocated by calling [`flush`](Entities::flush).768#[expect(769clippy::allow_attributes,770reason = "`clippy::unnecessary_fallible_conversions` may not always lint."771)]772#[allow(773clippy::unnecessary_fallible_conversions,774reason = "`IdCursor::try_from` may fail on 32-bit platforms."775)]776pub fn reserve_entities(&self, count: u32) -> ReserveEntitiesIterator<'_> {777// Use one atomic subtract to grab a range of new IDs. The range might be778// entirely nonnegative, meaning all IDs come from the freelist, or entirely779// negative, meaning they are all new IDs to allocate, or a mix of both.780let range_end = self.free_cursor.fetch_sub(781IdCursor::try_from(count)782.expect("64-bit atomic operations are not supported on this platform."),783Ordering::Relaxed,784);785let range_start = range_end786- IdCursor::try_from(count)787.expect("64-bit atomic operations are not supported on this platform.");788789let freelist_range = range_start.max(0) as usize..range_end.max(0) as usize;790791let (new_id_start, new_id_end) = if range_start >= 0 {792// We satisfied all requests from the freelist.793(0, 0)794} else {795// We need to allocate some new Entity IDs outside of the range of self.meta.796//797// `range_start` covers some negative territory, e.g. `-3..6`.798// Since the nonnegative values `0..6` are handled by the freelist, that799// means we need to handle the negative range here.800//801// In this example, we truncate the end to 0, leaving us with `-3..0`.802// Then we negate these values to indicate how far beyond the end of `meta.end()`803// to go, yielding `meta.len()+0 .. meta.len()+3`.804let base = self.meta.len() as IdCursor;805806let new_id_end = u32::try_from(base - range_start).expect("too many entities");807808// `new_id_end` is in range, so no need to check `start`.809let new_id_start = (base - range_end.min(0)) as u32;810811(new_id_start, new_id_end)812};813814ReserveEntitiesIterator {815meta: &self.meta[..],816freelist_indices: self.pending[freelist_range].iter(),817new_indices: new_id_start..new_id_end,818}819}820821/// Reserve one entity ID concurrently.822///823/// Equivalent to `self.reserve_entities(1).next().unwrap()`, but more efficient.824pub fn reserve_entity(&self) -> Entity {825let n = self.free_cursor.fetch_sub(1, Ordering::Relaxed);826if n > 0 {827// Allocate from the freelist.828let row = self.pending[(n - 1) as usize];829Entity::from_raw_and_generation(row, self.meta[row.index() as usize].generation)830} else {831// Grab a new ID, outside the range of `meta.len()`. `flush()` must832// eventually be called to make it valid.833//834// As `self.free_cursor` goes more and more negative, we return IDs farther835// and farther beyond `meta.len()`.836let raw = self.meta.len() as IdCursor - n;837if raw >= u32::MAX as IdCursor {838panic!("too many entities");839}840// SAFETY: We just checked the bounds841let row = unsafe { EntityRow::new(NonMaxU32::new_unchecked(raw as u32)) };842Entity::from_raw(row)843}844}845846/// Check that we do not have pending work requiring `flush()` to be called.847fn verify_flushed(&mut self) {848debug_assert!(849!self.needs_flush(),850"flush() needs to be called before this operation is legal"851);852}853854/// Allocate an entity ID directly.855pub fn alloc(&mut self) -> Entity {856self.verify_flushed();857if let Some(row) = self.pending.pop() {858let new_free_cursor = self.pending.len() as IdCursor;859*self.free_cursor.get_mut() = new_free_cursor;860Entity::from_raw_and_generation(row, self.meta[row.index() as usize].generation)861} else {862let index = u32::try_from(self.meta.len())863.ok()864.and_then(NonMaxU32::new)865.expect("too many entities");866self.meta.push(EntityMeta::EMPTY);867Entity::from_raw(EntityRow::new(index))868}869}870871/// Destroy an entity, allowing it to be reused.872///873/// Returns the `Option<EntityLocation>` of the entity or `None` if the `entity` was not present.874///875/// Must not be called while reserved entities are awaiting `flush()`.876pub fn free(&mut self, entity: Entity) -> Option<EntityIdLocation> {877self.verify_flushed();878879let meta = &mut self.meta[entity.index() as usize];880if meta.generation != entity.generation {881return None;882}883884let (new_generation, aliased) = meta.generation.after_versions_and_could_alias(1);885meta.generation = new_generation;886if aliased {887warn!(888"Entity({}) generation wrapped on Entities::free, aliasing may occur",889entity.row()890);891}892893let loc = mem::replace(&mut meta.location, EntityMeta::EMPTY.location);894895self.pending.push(entity.row());896897let new_free_cursor = self.pending.len() as IdCursor;898*self.free_cursor.get_mut() = new_free_cursor;899Some(loc)900}901902/// Ensure at least `n` allocations can succeed without reallocating.903#[expect(904clippy::allow_attributes,905reason = "`clippy::unnecessary_fallible_conversions` may not always lint."906)]907#[allow(908clippy::unnecessary_fallible_conversions,909reason = "`IdCursor::try_from` may fail on 32-bit platforms."910)]911pub fn reserve(&mut self, additional: u32) {912self.verify_flushed();913914let freelist_size = *self.free_cursor.get_mut();915let shortfall = IdCursor::try_from(additional)916.expect("64-bit atomic operations are not supported on this platform.")917- freelist_size;918if shortfall > 0 {919self.meta.reserve(shortfall as usize);920}921}922923/// Returns true if the [`Entities`] contains [`entity`](Entity).924// This will return false for entities which have been freed, even if925// not reallocated since the generation is incremented in `free`926pub fn contains(&self, entity: Entity) -> bool {927self.resolve_from_id(entity.row())928.is_some_and(|e| e.generation() == entity.generation())929}930931/// Clears all [`Entity`] from the World.932pub fn clear(&mut self) {933self.meta.clear();934self.pending.clear();935*self.free_cursor.get_mut() = 0;936}937938/// Returns the [`EntityLocation`] of an [`Entity`].939/// Note: for pending entities and entities not participating in the ECS (entities with a [`EntityIdLocation`] of `None`), returns `None`.940#[inline]941pub fn get(&self, entity: Entity) -> Option<EntityLocation> {942self.get_id_location(entity).flatten()943}944945/// Returns the [`EntityIdLocation`] of an [`Entity`].946/// Note: for pending entities, returns `None`.947#[inline]948pub fn get_id_location(&self, entity: Entity) -> Option<EntityIdLocation> {949self.meta950.get(entity.index() as usize)951.filter(|meta| meta.generation == entity.generation)952.map(|meta| meta.location)953}954955/// Updates the location of an [`Entity`].956/// This must be called when moving the components of the existing entity around in storage.957///958/// # Safety959/// - `index` must be a valid entity index.960/// - `location` must be valid for the entity at `index` or immediately made valid afterwards961/// before handing control to unknown code.962#[inline]963pub(crate) unsafe fn set(&mut self, index: u32, location: EntityIdLocation) {964// SAFETY: Caller guarantees that `index` a valid entity index965let meta = unsafe { self.meta.get_unchecked_mut(index as usize) };966meta.location = location;967}968969/// Mark an [`Entity`] as spawned or despawned in the given tick.970///971/// # Safety972/// - `index` must be a valid entity index.973#[inline]974pub(crate) unsafe fn mark_spawn_despawn(&mut self, index: u32, by: MaybeLocation, tick: Tick) {975// SAFETY: Caller guarantees that `index` a valid entity index976let meta = unsafe { self.meta.get_unchecked_mut(index as usize) };977meta.spawned_or_despawned = SpawnedOrDespawned { by, tick };978}979980/// Increments the `generation` of a freed [`Entity`]. The next entity ID allocated with this981/// `index` will count `generation` starting from the prior `generation` + the specified982/// value + 1.983///984/// Does nothing if no entity with this `index` has been allocated yet.985pub(crate) fn reserve_generations(&mut self, index: u32, generations: u32) -> bool {986if (index as usize) >= self.meta.len() {987return false;988}989990let meta = &mut self.meta[index as usize];991if meta.location.is_none() {992meta.generation = meta.generation.after_versions(generations);993true994} else {995false996}997}998999/// Get the [`Entity`] with a given id, if it exists in this [`Entities`] collection1000/// Returns `None` if this [`Entity`] is outside of the range of currently reserved Entities1001///1002/// Note: This method may return [`Entities`](Entity) which are currently free1003/// Note that [`contains`](Entities::contains) will correctly return false for freed1004/// entities, since it checks the generation1005pub fn resolve_from_id(&self, row: EntityRow) -> Option<Entity> {1006let idu = row.index() as usize;1007if let Some(&EntityMeta { generation, .. }) = self.meta.get(idu) {1008Some(Entity::from_raw_and_generation(row, generation))1009} else {1010// `id` is outside of the meta list - check whether it is reserved but not yet flushed.1011let free_cursor = self.free_cursor.load(Ordering::Relaxed);1012// If this entity was manually created, then free_cursor might be positive1013// Returning None handles that case correctly1014let num_pending = usize::try_from(-free_cursor).ok()?;1015(idu < self.meta.len() + num_pending).then_some(Entity::from_raw(row))1016}1017}10181019fn needs_flush(&mut self) -> bool {1020*self.free_cursor.get_mut() != self.pending.len() as IdCursor1021}10221023/// Allocates space for entities previously reserved with [`reserve_entity`](Entities::reserve_entity) or1024/// [`reserve_entities`](Entities::reserve_entities), then initializes each one using the supplied function.1025///1026/// See [`EntityLocation`] for details on its meaning and how to set it.1027///1028/// # Safety1029/// Flush _must_ set the entity location to the correct [`ArchetypeId`] for the given [`Entity`]1030/// each time init is called. This _can_ be [`ArchetypeId::INVALID`], provided the [`Entity`]1031/// has not been assigned to an [`Archetype`][crate::archetype::Archetype].1032///1033/// Note: freshly-allocated entities (ones which don't come from the pending list) are guaranteed1034/// to be initialized with the invalid archetype.1035pub unsafe fn flush(1036&mut self,1037mut init: impl FnMut(Entity, &mut EntityIdLocation),1038by: MaybeLocation,1039tick: Tick,1040) {1041let free_cursor = self.free_cursor.get_mut();1042let current_free_cursor = *free_cursor;10431044let new_free_cursor = if current_free_cursor >= 0 {1045current_free_cursor as usize1046} else {1047let old_meta_len = self.meta.len();1048let new_meta_len = old_meta_len + -current_free_cursor as usize;1049self.meta.resize(new_meta_len, EntityMeta::EMPTY);1050for (index, meta) in self.meta.iter_mut().enumerate().skip(old_meta_len) {1051// SAFETY: the index is less than the meta length, which can not exceeded u32::MAX1052let row = EntityRow::new(unsafe { NonMaxU32::new_unchecked(index as u32) });1053init(1054Entity::from_raw_and_generation(row, meta.generation),1055&mut meta.location,1056);1057meta.spawned_or_despawned = SpawnedOrDespawned { by, tick };1058}10591060*free_cursor = 0;106101062};10631064for row in self.pending.drain(new_free_cursor..) {1065let meta = &mut self.meta[row.index() as usize];1066init(1067Entity::from_raw_and_generation(row, meta.generation),1068&mut meta.location,1069);1070meta.spawned_or_despawned = SpawnedOrDespawned { by, tick };1071}1072}10731074/// Flushes all reserved entities to an "invalid" state. Attempting to retrieve them will return `None`1075/// unless they are later populated with a valid archetype.1076pub fn flush_as_invalid(&mut self, by: MaybeLocation, tick: Tick) {1077// SAFETY: as per `flush` safety docs, the archetype id can be set to [`ArchetypeId::INVALID`] if1078// the [`Entity`] has not been assigned to an [`Archetype`][crate::archetype::Archetype], which is the case here1079unsafe {1080self.flush(1081|_entity, location| {1082*location = None;1083},1084by,1085tick,1086);1087}1088}10891090/// The count of all entities in the [`World`] that have ever been allocated1091/// including the entities that are currently freed.1092///1093/// This does not include entities that have been reserved but have never been1094/// allocated yet.1095///1096/// [`World`]: crate::world::World1097#[inline]1098pub fn total_count(&self) -> usize {1099self.meta.len()1100}11011102/// The count of all entities in the [`World`] that are used,1103/// including both those allocated and those reserved, but not those freed.1104///1105/// [`World`]: crate::world::World1106#[inline]1107pub fn used_count(&self) -> usize {1108(self.meta.len() as isize - self.free_cursor.load(Ordering::Relaxed) as isize) as usize1109}11101111/// The count of all entities in the [`World`] that have ever been allocated or reserved, including those that are freed.1112/// This is the value that [`Self::total_count()`] would return if [`Self::flush()`] were called right now.1113///1114/// [`World`]: crate::world::World1115#[inline]1116pub fn total_prospective_count(&self) -> usize {1117self.meta.len() + (-self.free_cursor.load(Ordering::Relaxed)).min(0) as usize1118}11191120/// The count of currently allocated entities.1121#[inline]1122pub fn len(&self) -> u32 {1123// `pending`, by definition, can't be bigger than `meta`.1124(self.meta.len() - self.pending.len()) as u321125}11261127/// Checks if any entity is currently active.1128#[inline]1129pub fn is_empty(&self) -> bool {1130self.len() == 01131}11321133/// Try to get the source code location from which this entity has last been1134/// spawned, despawned or flushed.1135///1136/// Returns `None` if its index has been reused by another entity1137/// or if this entity has never existed.1138pub fn entity_get_spawned_or_despawned_by(1139&self,1140entity: Entity,1141) -> MaybeLocation<Option<&'static Location<'static>>> {1142MaybeLocation::new_with_flattened(|| {1143self.entity_get_spawned_or_despawned(entity)1144.map(|spawned_or_despawned| spawned_or_despawned.by)1145})1146}11471148/// Try to get the [`Tick`] at which this entity has last been1149/// spawned, despawned or flushed.1150///1151/// Returns `None` if its index has been reused by another entity or if this entity1152/// has never been spawned.1153pub fn entity_get_spawn_or_despawn_tick(&self, entity: Entity) -> Option<Tick> {1154self.entity_get_spawned_or_despawned(entity)1155.map(|spawned_or_despawned| spawned_or_despawned.tick)1156}11571158/// Try to get the [`SpawnedOrDespawned`] related to the entity's last spawn,1159/// despawn or flush.1160///1161/// Returns `None` if its index has been reused by another entity or if1162/// this entity has never been spawned.1163#[inline]1164fn entity_get_spawned_or_despawned(&self, entity: Entity) -> Option<SpawnedOrDespawned> {1165self.meta1166.get(entity.index() as usize)1167.filter(|meta|1168// Generation is incremented immediately upon despawn1169(meta.generation == entity.generation)1170|| meta.location.is_none()1171&& (meta.generation == entity.generation.after_versions(1)))1172.map(|meta| meta.spawned_or_despawned)1173}11741175/// Returns the source code location from which this entity has last been spawned1176/// or despawned and the Tick of when that happened.1177///1178/// # Safety1179///1180/// The entity index must belong to an entity that is currently alive or, if it1181/// despawned, was not overwritten by a new entity of the same index.1182#[inline]1183pub(crate) unsafe fn entity_get_spawned_or_despawned_unchecked(1184&self,1185entity: Entity,1186) -> (MaybeLocation, Tick) {1187// SAFETY: caller ensures entity is allocated1188let meta = unsafe { self.meta.get_unchecked(entity.index() as usize) };1189(meta.spawned_or_despawned.by, meta.spawned_or_despawned.tick)1190}11911192#[inline]1193pub(crate) fn check_change_ticks(&mut self, check: CheckChangeTicks) {1194for meta in &mut self.meta {1195meta.spawned_or_despawned.tick.check_tick(check);1196}1197}11981199/// Constructs a message explaining why an entity does not exist, if known.1200pub(crate) fn entity_does_not_exist_error_details(1201&self,1202entity: Entity,1203) -> EntityDoesNotExistDetails {1204EntityDoesNotExistDetails {1205location: self.entity_get_spawned_or_despawned_by(entity),1206}1207}1208}12091210/// An error that occurs when a specified [`Entity`] does not exist.1211#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]1212#[error("The entity with ID {entity} {details}")]1213pub struct EntityDoesNotExistError {1214/// The entity's ID.1215pub entity: Entity,1216/// Details on why the entity does not exist, if available.1217pub details: EntityDoesNotExistDetails,1218}12191220impl EntityDoesNotExistError {1221pub(crate) fn new(entity: Entity, entities: &Entities) -> Self {1222Self {1223entity,1224details: entities.entity_does_not_exist_error_details(entity),1225}1226}1227}12281229/// Helper struct that, when printed, will write the appropriate details1230/// regarding an entity that did not exist.1231#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]1232pub struct EntityDoesNotExistDetails {1233location: MaybeLocation<Option<&'static Location<'static>>>,1234}12351236impl fmt::Display for EntityDoesNotExistDetails {1237fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {1238match self.location.into_option() {1239Some(Some(location)) => write!(f, "was despawned by {location}"),1240Some(None) => write!(1241f,1242"does not exist (index has been reused or was never spawned)"1243),1244None => write!(1245f,1246"does not exist (enable `track_location` feature for more details)"1247),1248}1249}1250}12511252#[derive(Copy, Clone, Debug)]1253struct EntityMeta {1254/// The current [`EntityGeneration`] of the [`EntityRow`].1255generation: EntityGeneration,1256/// The current location of the [`EntityRow`].1257location: EntityIdLocation,1258/// Location and tick of the last spawn, despawn or flush of this entity.1259spawned_or_despawned: SpawnedOrDespawned,1260}12611262#[derive(Copy, Clone, Debug)]1263struct SpawnedOrDespawned {1264by: MaybeLocation,1265tick: Tick,1266}12671268impl EntityMeta {1269/// meta for **pending entity**1270const EMPTY: EntityMeta = EntityMeta {1271generation: EntityGeneration::FIRST,1272location: None,1273spawned_or_despawned: SpawnedOrDespawned {1274by: MaybeLocation::caller(),1275tick: Tick::new(0),1276},1277};1278}12791280/// A location of an entity in an archetype.1281#[derive(Copy, Clone, Debug, PartialEq)]1282pub struct EntityLocation {1283/// The ID of the [`Archetype`] the [`Entity`] belongs to.1284///1285/// [`Archetype`]: crate::archetype::Archetype1286pub archetype_id: ArchetypeId,12871288/// The index of the [`Entity`] within its [`Archetype`].1289///1290/// [`Archetype`]: crate::archetype::Archetype1291pub archetype_row: ArchetypeRow,12921293/// The ID of the [`Table`] the [`Entity`] belongs to.1294///1295/// [`Table`]: crate::storage::Table1296pub table_id: TableId,12971298/// The index of the [`Entity`] within its [`Table`].1299///1300/// [`Table`]: crate::storage::Table1301pub table_row: TableRow,1302}13031304/// An [`Entity`] id may or may not correspond to a valid conceptual entity.1305/// If it does, the conceptual entity may or may not have a location.1306/// If it has no location, the [`EntityLocation`] will be `None`.1307/// An location of `None` means the entity effectively does not exist; it has an id, but is not participating in the ECS.1308/// This is different from a location in the empty archetype, which is participating (queryable, etc) but just happens to have no components.1309///1310/// Setting a location to `None` is often helpful when you want to destruct an entity or yank it from the ECS without allowing another system to reuse the id for something else.1311/// It is also useful for reserving an id; commands will often allocate an `Entity` but not provide it a location until the command is applied.1312pub type EntityIdLocation = Option<EntityLocation>;13131314#[cfg(test)]1315mod tests {1316use super::*;1317use alloc::format;13181319#[test]1320fn entity_niche_optimization() {1321assert_eq!(size_of::<Entity>(), size_of::<Option<Entity>>());1322}13231324#[test]1325fn entity_bits_roundtrip() {1326let r = EntityRow::new(NonMaxU32::new(0xDEADBEEF).unwrap());1327assert_eq!(EntityRow::from_bits(r.to_bits()), r);13281329// Generation cannot be greater than 0x7FFF_FFFF else it will be an invalid Entity id1330let e = Entity::from_raw_and_generation(1331EntityRow::new(NonMaxU32::new(0xDEADBEEF).unwrap()),1332EntityGeneration::from_bits(0x5AADF00D),1333);1334assert_eq!(Entity::from_bits(e.to_bits()), e);1335}13361337#[test]1338fn reserve_entity_len() {1339let mut e = Entities::new();1340e.reserve_entity();1341// SAFETY: entity_location is left invalid1342unsafe { e.flush(|_, _| {}, MaybeLocation::caller(), Tick::default()) };1343assert_eq!(e.len(), 1);1344}13451346#[test]1347fn get_reserved_and_invalid() {1348let mut entities = Entities::new();1349let e = entities.reserve_entity();1350assert!(entities.contains(e));1351assert!(entities.get(e).is_none());13521353// SAFETY: entity_location is left invalid1354unsafe {1355entities.flush(1356|_entity, _location| {1357// do nothing ... leaving entity location invalid1358},1359MaybeLocation::caller(),1360Tick::default(),1361);1362};13631364assert!(entities.contains(e));1365assert!(entities.get(e).is_none());1366}13671368#[test]1369fn entity_const() {1370const C1: Entity = Entity::from_raw(EntityRow::new(NonMaxU32::new(42).unwrap()));1371assert_eq!(42, C1.index());1372assert_eq!(0, C1.generation().to_bits());13731374const C2: Entity = Entity::from_bits(0x0000_00ff_0000_00cc);1375assert_eq!(!0x0000_00cc, C2.index());1376assert_eq!(0x0000_00ff, C2.generation().to_bits());13771378const C3: u32 = Entity::from_raw(EntityRow::new(NonMaxU32::new(33).unwrap())).index();1379assert_eq!(33, C3);13801381const C4: u32 = Entity::from_bits(0x00dd_00ff_1111_1111)1382.generation()1383.to_bits();1384assert_eq!(0x00dd_00ff, C4);1385}13861387#[test]1388fn reserve_generations() {1389let mut entities = Entities::new();1390let entity = entities.alloc();1391entities.free(entity);13921393assert!(entities.reserve_generations(entity.index(), 1));1394}13951396#[test]1397fn reserve_generations_and_alloc() {1398const GENERATIONS: u32 = 10;13991400let mut entities = Entities::new();1401let entity = entities.alloc();1402entities.free(entity);14031404assert!(entities.reserve_generations(entity.index(), GENERATIONS));14051406// The very next entity allocated should be a further generation on the same index1407let next_entity = entities.alloc();1408assert_eq!(next_entity.index(), entity.index());1409assert!(next_entity1410.generation()1411.cmp_approx(&entity.generation().after_versions(GENERATIONS))1412.is_gt());1413}14141415#[test]1416#[expect(1417clippy::nonminimal_bool,1418reason = "This intentionally tests all possible comparison operators as separate functions; thus, we don't want to rewrite these comparisons to use different operators."1419)]1420fn entity_comparison() {1421assert_eq!(1422Entity::from_raw_and_generation(1423EntityRow::new(NonMaxU32::new(123).unwrap()),1424EntityGeneration::from_bits(456)1425),1426Entity::from_raw_and_generation(1427EntityRow::new(NonMaxU32::new(123).unwrap()),1428EntityGeneration::from_bits(456)1429)1430);1431assert_ne!(1432Entity::from_raw_and_generation(1433EntityRow::new(NonMaxU32::new(123).unwrap()),1434EntityGeneration::from_bits(789)1435),1436Entity::from_raw_and_generation(1437EntityRow::new(NonMaxU32::new(123).unwrap()),1438EntityGeneration::from_bits(456)1439)1440);1441assert_ne!(1442Entity::from_raw_and_generation(1443EntityRow::new(NonMaxU32::new(123).unwrap()),1444EntityGeneration::from_bits(456)1445),1446Entity::from_raw_and_generation(1447EntityRow::new(NonMaxU32::new(123).unwrap()),1448EntityGeneration::from_bits(789)1449)1450);1451assert_ne!(1452Entity::from_raw_and_generation(1453EntityRow::new(NonMaxU32::new(123).unwrap()),1454EntityGeneration::from_bits(456)1455),1456Entity::from_raw_and_generation(1457EntityRow::new(NonMaxU32::new(456).unwrap()),1458EntityGeneration::from_bits(123)1459)1460);14611462// ordering is by generation then by index14631464assert!(1465Entity::from_raw_and_generation(1466EntityRow::new(NonMaxU32::new(123).unwrap()),1467EntityGeneration::from_bits(456)1468) >= Entity::from_raw_and_generation(1469EntityRow::new(NonMaxU32::new(123).unwrap()),1470EntityGeneration::from_bits(456)1471)1472);1473assert!(1474Entity::from_raw_and_generation(1475EntityRow::new(NonMaxU32::new(123).unwrap()),1476EntityGeneration::from_bits(456)1477) <= Entity::from_raw_and_generation(1478EntityRow::new(NonMaxU32::new(123).unwrap()),1479EntityGeneration::from_bits(456)1480)1481);1482assert!(1483!(Entity::from_raw_and_generation(1484EntityRow::new(NonMaxU32::new(123).unwrap()),1485EntityGeneration::from_bits(456)1486) < Entity::from_raw_and_generation(1487EntityRow::new(NonMaxU32::new(123).unwrap()),1488EntityGeneration::from_bits(456)1489))1490);1491assert!(1492!(Entity::from_raw_and_generation(1493EntityRow::new(NonMaxU32::new(123).unwrap()),1494EntityGeneration::from_bits(456)1495) > Entity::from_raw_and_generation(1496EntityRow::new(NonMaxU32::new(123).unwrap()),1497EntityGeneration::from_bits(456)1498))1499);15001501assert!(1502Entity::from_raw_and_generation(1503EntityRow::new(NonMaxU32::new(9).unwrap()),1504EntityGeneration::from_bits(1)1505) < Entity::from_raw_and_generation(1506EntityRow::new(NonMaxU32::new(1).unwrap()),1507EntityGeneration::from_bits(9)1508)1509);1510assert!(1511Entity::from_raw_and_generation(1512EntityRow::new(NonMaxU32::new(1).unwrap()),1513EntityGeneration::from_bits(9)1514) > Entity::from_raw_and_generation(1515EntityRow::new(NonMaxU32::new(9).unwrap()),1516EntityGeneration::from_bits(1)1517)1518);15191520assert!(1521Entity::from_raw_and_generation(1522EntityRow::new(NonMaxU32::new(1).unwrap()),1523EntityGeneration::from_bits(1)1524) > Entity::from_raw_and_generation(1525EntityRow::new(NonMaxU32::new(2).unwrap()),1526EntityGeneration::from_bits(1)1527)1528);1529assert!(1530Entity::from_raw_and_generation(1531EntityRow::new(NonMaxU32::new(1).unwrap()),1532EntityGeneration::from_bits(1)1533) >= Entity::from_raw_and_generation(1534EntityRow::new(NonMaxU32::new(2).unwrap()),1535EntityGeneration::from_bits(1)1536)1537);1538assert!(1539Entity::from_raw_and_generation(1540EntityRow::new(NonMaxU32::new(2).unwrap()),1541EntityGeneration::from_bits(2)1542) < Entity::from_raw_and_generation(1543EntityRow::new(NonMaxU32::new(1).unwrap()),1544EntityGeneration::from_bits(2)1545)1546);1547assert!(1548Entity::from_raw_and_generation(1549EntityRow::new(NonMaxU32::new(2).unwrap()),1550EntityGeneration::from_bits(2)1551) <= Entity::from_raw_and_generation(1552EntityRow::new(NonMaxU32::new(1).unwrap()),1553EntityGeneration::from_bits(2)1554)1555);1556}15571558// Feel free to change this test if needed, but it seemed like an important1559// part of the best-case performance changes in PR#9903.1560#[test]1561fn entity_hash_keeps_similar_ids_together() {1562use core::hash::BuildHasher;1563let hash = EntityHash;15641565let first_id = 0xC0FFEE << 8;1566let first_hash = hash.hash_one(Entity::from_raw(EntityRow::new(1567NonMaxU32::new(first_id).unwrap(),1568)));15691570for i in 1..=255 {1571let id = first_id + i;1572let hash = hash.hash_one(Entity::from_raw(EntityRow::new(1573NonMaxU32::new(id).unwrap(),1574)));1575assert_eq!(first_hash.wrapping_sub(hash) as u32, i);1576}1577}15781579#[test]1580fn entity_hash_id_bitflip_affects_high_7_bits() {1581use core::hash::BuildHasher;15821583let hash = EntityHash;15841585let first_id = 0xC0FFEE;1586let first_hash = hash.hash_one(Entity::from_raw(EntityRow::new(1587NonMaxU32::new(first_id).unwrap(),1588))) >> 57;15891590for bit in 0..u32::BITS {1591let id = first_id ^ (1 << bit);1592let hash = hash.hash_one(Entity::from_raw(EntityRow::new(1593NonMaxU32::new(id).unwrap(),1594))) >> 57;1595assert_ne!(hash, first_hash);1596}1597}15981599#[test]1600fn entity_generation_is_approximately_ordered() {1601use core::cmp::Ordering;16021603let old = EntityGeneration::FIRST;1604let middle = old.after_versions(1);1605let younger_before_ord_wrap = middle.after_versions(EntityGeneration::DIFF_MAX);1606let younger_after_ord_wrap = younger_before_ord_wrap.after_versions(1);16071608assert_eq!(middle.cmp_approx(&old), Ordering::Greater);1609assert_eq!(middle.cmp_approx(&middle), Ordering::Equal);1610assert_eq!(middle.cmp_approx(&younger_before_ord_wrap), Ordering::Less);1611assert_eq!(1612middle.cmp_approx(&younger_after_ord_wrap),1613Ordering::Greater1614);1615}16161617#[test]1618fn entity_debug() {1619let entity = Entity::from_raw(EntityRow::new(NonMaxU32::new(42).unwrap()));1620let string = format!("{entity:?}");1621assert_eq!(string, "42v0");16221623let entity = Entity::PLACEHOLDER;1624let string = format!("{entity:?}");1625assert_eq!(string, "PLACEHOLDER");1626}16271628#[test]1629fn entity_display() {1630let entity = Entity::from_raw(EntityRow::new(NonMaxU32::new(42).unwrap()));1631let string = format!("{entity}");1632assert_eq!(string, "42v0");16331634let entity = Entity::PLACEHOLDER;1635let string = format!("{entity}");1636assert_eq!(string, "PLACEHOLDER");1637}1638}163916401641