1use crate::error::EntityAccessError;
19
20#[derive(Clone, Copy)]
33pub struct Entity {
34 index: u32,
35 generation: u32,
36 name: &'static str,
37}
38
39impl core::fmt::Debug for Entity {
43 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
44 f.debug_struct("Entity")
45 .field("name", &self.name)
46 .field("bits", &self.to_bits())
47 .finish()
48 }
49}
50impl PartialEq for Entity {
51 fn eq(&self, other: &Self) -> bool {
52 self.index == other.index && self.generation == other.generation
53 }
54}
55
56impl Eq for Entity {}
57
58impl PartialOrd for Entity {
59 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
60 Some(self.cmp(other))
61 }
62}
63
64impl Ord for Entity {
65 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
66 (self.index, self.generation).cmp(&(other.index, other.generation))
67 }
68}
69
70impl Entity {
71 pub const fn from_raw(index: u32, generation: u32) -> Self {
77 Self {
78 index,
79 generation,
80 name: "",
81 }
82 }
83
84 #[must_use]
86 pub const fn name(self) -> &'static str {
87 self.name
88 }
89
90 #[must_use]
92 pub const fn index(self) -> u32 {
93 self.index
94 }
95
96 #[must_use]
98 pub const fn generation(self) -> u32 {
99 self.generation
100 }
101
102 #[must_use]
108 pub const fn to_bits(self) -> u64 {
109 ((self.generation as u64) << 32) | self.index as u64
110 }
111
112 #[must_use]
118 pub const fn from_bits(bits: u64) -> Self {
119 Self::from_raw(bits as u32, (bits >> 32) as u32)
120 }
121}
122
123#[cfg(feature = "reflect")]
130#[derive(facet::Facet)]
131#[facet(transparent)]
132pub struct EntityProxy(pub u64);
133
134#[cfg(feature = "reflect")]
135impl From<EntityProxy> for Entity {
136 fn from(proxy: EntityProxy) -> Self {
137 Entity::from_bits(proxy.0)
138 }
139}
140
141#[cfg(feature = "reflect")]
142impl From<&Entity> for EntityProxy {
143 fn from(value: &Entity) -> Self {
144 Self(value.to_bits())
145 }
146}
147
148#[cfg(feature = "reflect")]
154#[derive(facet::Facet)]
155#[facet(transparent)]
156pub struct EntityVecProxy(pub Vec<u64>);
157
158#[cfg(feature = "reflect")]
159impl From<EntityVecProxy> for Vec<Entity> {
160 fn from(proxy: EntityVecProxy) -> Self {
161 proxy.0.into_iter().map(Entity::from_bits).collect()
162 }
163}
164
165#[cfg(feature = "reflect")]
166impl From<&Vec<Entity>> for EntityVecProxy {
167 fn from(value: &Vec<Entity>) -> Self {
168 Self(value.iter().map(|entity| entity.to_bits()).collect())
169 }
170}
171
172#[derive(Clone, Copy, PartialEq, Eq, Debug)]
179pub struct Location {
180 pub(crate) archetype: u32,
181 pub(crate) row: u32,
182}
183
184impl Location {
185 #[inline]
187 pub fn archetype(self) -> u32 {
188 self.archetype
189 }
190
191 #[inline]
193 pub fn row(self) -> usize {
194 self.row as usize
195 }
196}
197
198#[derive(Clone, Copy)]
199struct Meta {
200 generation: u32,
201 location: Option<Location>,
202 reserved: bool,
205}
206
207#[derive(Default)]
209pub(crate) struct Entities {
210 meta: Vec<Meta>,
211 free: Vec<u32>,
214 reserved: usize,
218}
219
220impl Entities {
221 pub(crate) fn contains(&self, entity: Entity) -> bool {
223 self.location(entity).is_ok()
224 }
225
226 #[inline]
232 pub(crate) fn location(&self, entity: Entity) -> Result<Location, EntityAccessError> {
233 let Some(meta) = self.meta.get(entity.index as usize) else {
234 return Err(EntityAccessError::NotAlive { entity });
235 };
236 if meta.generation != entity.generation {
237 return Err(EntityAccessError::NotAlive { entity });
238 }
239 meta.location.ok_or(EntityAccessError::NotAlive { entity })
240 }
241
242 pub(crate) fn set_location(&mut self, entity: Entity, location: Location) {
247 let meta = self
248 .meta
249 .get_mut(entity.index as usize)
250 .unwrap_or_else(|| panic!("{entity:?} names no entity index"));
251 assert_eq!(
252 meta.generation, entity.generation,
253 "{entity:?} is a stale handle"
254 );
255 meta.location = Some(location);
256 }
257
258 fn index(&mut self) -> usize {
260 match self.free.pop() {
261 Some(index) => index as usize,
262 None => {
263 self.meta.push(Meta {
264 generation: 0,
265 location: None,
266 reserved: false,
267 });
268 self.meta.len() - 1
269 }
270 }
271 }
272
273 pub(crate) fn reserve(&mut self, name: &'static str) -> Entity {
275 let index = self.index();
276 self.meta[index].reserved = true;
277 self.reserved += 1;
278 Entity {
279 index: index as u32,
280 generation: self.meta[index].generation,
281 name,
282 }
283 }
284
285 pub(crate) fn alloc(&mut self, name: &'static str) -> Entity {
287 let index = self.index();
288 Entity {
289 index: index as u32,
290 generation: self.meta[index].generation,
291 name,
292 }
293 }
294
295 pub(crate) fn is_reserved(&self, entity: Entity) -> bool {
298 self.meta
299 .get(entity.index as usize)
300 .is_some_and(|meta| meta.reserved && meta.generation == entity.generation)
301 }
302
303 pub(crate) fn take_reserved(&mut self, entity: Entity) -> bool {
308 if !self.is_reserved(entity) {
309 return false;
310 }
311 self.meta[entity.index as usize].reserved = false;
312 self.reserved -= 1;
313 true
314 }
315
316 pub(crate) fn free(&mut self, entity: Entity) -> bool {
322 let Some(meta) = self.meta.get_mut(entity.index as usize) else {
323 return false;
324 };
325 if meta.generation != entity.generation {
326 return false;
327 }
328 if !meta.reserved && meta.location.is_none() {
329 return false;
330 }
331 meta.location = None;
332 meta.generation = meta.generation.wrapping_add(1);
333 if meta.reserved {
334 meta.reserved = false;
335 self.reserved -= 1;
336 }
337 self.free.push(entity.index);
338 true
339 }
340}
341#[cfg(test)]
342mod tests {
343 use super::*;
344
345 #[test]
346 fn alloc_reuses_freed_indices_with_a_new_generation() {
347 let mut entities = Entities::default();
348 let first = entities.alloc("test");
349 assert_eq!(first.index(), 0);
350 assert_eq!(first.generation(), 0);
351
352 entities.set_location(
353 first,
354 Location {
355 archetype: 0,
356 row: 0,
357 },
358 );
359 assert!(entities.free(first));
360
361 let second = entities.alloc("test");
362 assert_eq!(second.index(), 0, "the index is reused");
363 assert_ne!(second.generation(), first.generation(), "but not the id");
364 assert!(!entities.contains(first), "the old handle is dead");
365 }
366
367 #[test]
368 fn reserved_entities_are_not_alive_until_spawned() {
369 let mut entities = Entities::default();
370 let entity = entities.reserve("test");
371 assert!(entities.is_reserved(entity));
372 assert!(!entities.contains(entity));
373
374 entities.set_location(
375 entity,
376 Location {
377 archetype: 0,
378 row: 0,
379 },
380 );
381 assert!(
382 entities.take_reserved(entity),
383 "the reservation is consumed"
384 );
385 assert!(entities.contains(entity));
386 assert!(!entities.take_reserved(entity), "and only once");
387 }
388
389 #[test]
390 fn freeing_a_stale_handle_fails() {
391 let mut entities = Entities::default();
392 let entity = entities.alloc("test");
393 entities.set_location(
394 entity,
395 Location {
396 archetype: 0,
397 row: 0,
398 },
399 );
400 assert!(entities.free(entity));
401 assert!(!entities.free(entity), "the handle is already stale");
402 }
403
404 #[test]
405 fn freeing_retires_a_handle_and_keeps_the_other() {
406 let mut entities = Entities::default();
407 let first = entities.alloc("test");
408 let second = entities.alloc("test");
409 entities.set_location(
410 first,
411 Location {
412 archetype: 0,
413 row: 0,
414 },
415 );
416 entities.set_location(
417 second,
418 Location {
419 archetype: 0,
420 row: 1,
421 },
422 );
423 assert!(entities.free(first));
424 assert!(!entities.contains(first));
425 assert!(entities.contains(second));
426 }
427
428 #[test]
429 fn entity_bits_round_trip() {
430 let entity = Entity::from_raw(7, 3);
431 assert_eq!(entity.index(), 7);
432 assert_eq!(entity.generation(), 3);
433 assert_eq!(entity.to_bits(), (3u64 << 32) | 7);
434 }
435
436 #[test]
437 fn the_name_is_a_label_and_not_part_of_the_identity() {
438 let mut entities = Entities::default();
439 let named = entities.reserve("ball");
440 let nameless = Entity::from_raw(named.index(), named.generation());
441 assert_eq!(named, nameless, "a name does not change the handle");
442 assert_eq!(named.to_bits(), nameless.to_bits(), "nor its bits");
443 assert_eq!(named.name(), "ball");
444 assert_eq!(nameless.name(), "", "a restored handle has no name");
445 assert_eq!(
446 Entity::from_bits(named.to_bits()).name(),
447 "",
448 "bits carry no name"
449 );
450 }
451
452 #[test]
453 fn a_debug_message_names_the_entity() {
454 let mut entities = Entities::default();
455 let printed = format!("{:?}", entities.reserve("ball"));
456 assert!(printed.contains("ball"), "{printed} names the entity");
457 }
458}