|
| 1 | +//! This module holds utilities for reference-counting of entities, similar to [`Arc`]. This enables |
| 2 | +//! automatic cleanup of entities that can be referenced in multiple places. |
| 3 | +
|
| 4 | +use core::{ |
| 5 | + fmt::{Debug, Formatter}, |
| 6 | + ops::Deref, |
| 7 | +}; |
| 8 | + |
| 9 | +use bevy_platform::sync::{Arc, Weak}; |
| 10 | +use concurrent_queue::ConcurrentQueue; |
| 11 | + |
| 12 | +use crate::{entity::Entity, system::Commands}; |
| 13 | + |
| 14 | +/// A reference count for an entity. |
| 15 | +/// |
| 16 | +/// This "handle" also stores some optional data, allowing users to customize any shared data |
| 17 | +/// between all references to the entity. |
| 18 | +/// |
| 19 | +/// Once all [`EntityRc`] instances have been dropped, the entity will be queued for destruction. |
| 20 | +/// This means it is possible for the entity to still exist, while its [`EntityRc`] has been |
| 21 | +/// dropped. |
| 22 | +/// |
| 23 | +/// The reverse is also true: a held [`EntityRc`] does not guarantee that the entity still exists. |
| 24 | +/// It can still be explicitly despawned, so users should try to be resilient to this. |
| 25 | +/// |
| 26 | +/// This type has similar semantics to [`Arc`]. |
| 27 | +#[derive(Debug)] |
| 28 | +pub struct EntityRc<T: Send + Sync + 'static = ()>(Arc<EntityRcInner<T>>); |
| 29 | + |
| 30 | +impl<T: Send + Sync + 'static> Clone for EntityRc<T> { |
| 31 | + fn clone(&self) -> Self { |
| 32 | + Self(self.0.clone()) |
| 33 | + } |
| 34 | +} |
| 35 | + |
| 36 | +impl<T: Send + Sync + 'static> EntityRc<T> { |
| 37 | + /// Creates a new [`EntityWeak`] referring to the same entity (and reference count). |
| 38 | + pub fn downgrade(this: &Self) -> EntityWeak<T> { |
| 39 | + EntityWeak { |
| 40 | + entity: this.0.entity, |
| 41 | + weak: Arc::downgrade(&this.0), |
| 42 | + } |
| 43 | + } |
| 44 | + |
| 45 | + /// Returns the entity this reference count refers to. |
| 46 | + pub fn entity(&self) -> Entity { |
| 47 | + self.0.entity |
| 48 | + } |
| 49 | +} |
| 50 | + |
| 51 | +impl<T: Send + Sync + 'static> Deref for EntityRc<T> { |
| 52 | + type Target = T; |
| 53 | + |
| 54 | + fn deref(&self) -> &Self::Target { |
| 55 | + &self.0.payload |
| 56 | + } |
| 57 | +} |
| 58 | + |
| 59 | +/// A "non-owning" reference to a reference-counted entity. |
| 60 | +/// |
| 61 | +/// Holding this handle does not guarantee that the entity will not be cleaned up. This handle |
| 62 | +/// allows "upgrading" to an [`EntityRc`], if the reference count is still positive, which **will** |
| 63 | +/// avoid clean ups. |
| 64 | +/// |
| 65 | +/// This type has similar semantics to [`Weak`]. |
| 66 | +#[derive(Debug)] |
| 67 | +pub struct EntityWeak<T: Send + Sync + 'static = ()> { |
| 68 | + /// The entity being referenced. |
| 69 | + /// |
| 70 | + /// This allows the entity to be referenced even if the reference count has expired. This is |
| 71 | + /// generally useful for cleanup operations. |
| 72 | + entity: Entity, |
| 73 | + /// The underlying weak reference. |
| 74 | + weak: Weak<EntityRcInner<T>>, |
| 75 | +} |
| 76 | + |
| 77 | +impl<T: Send + Sync + 'static> Clone for EntityWeak<T> { |
| 78 | + fn clone(&self) -> Self { |
| 79 | + Self { |
| 80 | + entity: self.entity, |
| 81 | + weak: self.weak.clone(), |
| 82 | + } |
| 83 | + } |
| 84 | +} |
| 85 | + |
| 86 | +impl<T: Send + Sync + 'static> EntityWeak<T> { |
| 87 | + /// Attempts to upgrade the weak reference into an [`EntityRc`], which can keep the entity alive |
| 88 | + /// if successful. |
| 89 | + /// |
| 90 | + /// Returns [`None`] if all [`EntityRc`]s were previously dropped. This does not necessarily |
| 91 | + /// mean that the entity has been despawned yet. |
| 92 | + pub fn upgrade(&self) -> Option<EntityRc<T>> { |
| 93 | + self.weak.upgrade().map(EntityRc) |
| 94 | + } |
| 95 | + |
| 96 | + /// Returns the entity this weak reference count refers to. |
| 97 | + /// |
| 98 | + /// The entity may or may not have been despawned (since the [`EntityRc`]s may have all been |
| 99 | + /// dropped). In order to guarantee the entity remains alive, use [`Self::upgrade`] first. This |
| 100 | + /// accessor exists to support cleanup operations. |
| 101 | + pub fn entity(&self) -> Entity { |
| 102 | + self.entity |
| 103 | + } |
| 104 | +} |
| 105 | + |
| 106 | +/// Data stored inside the shared data for [`EntityRc`]. |
| 107 | +struct EntityRcInner<T: Send + Sync + 'static> { |
| 108 | + /// The concurrent queue to notify when dropping this type. |
| 109 | + drop_notifier: Arc<ConcurrentQueue<Entity>>, |
| 110 | + /// The entity this reference count refers to. |
| 111 | + entity: Entity, |
| 112 | + /// The data that is shared with all reference counts for easy access. |
| 113 | + payload: T, |
| 114 | +} |
| 115 | + |
| 116 | +// Manual impl of Debug to avoid debugging the drop_notifier. |
| 117 | +impl<T: Send + Sync + 'static + Debug> Debug for EntityRcInner<T> { |
| 118 | + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { |
| 119 | + f.debug_struct("EntityRcInner") |
| 120 | + .field("entity", &self.entity) |
| 121 | + .field("payload", &self.payload) |
| 122 | + .finish() |
| 123 | + } |
| 124 | +} |
| 125 | + |
| 126 | +impl<T: Send + Sync + 'static> Drop for EntityRcInner<T> { |
| 127 | + fn drop(&mut self) { |
| 128 | + // Try to push the entity. If the notifier is closed for some reason, that's ok. |
| 129 | + let _ = self.drop_notifier.push(self.entity); |
| 130 | + } |
| 131 | +} |
| 132 | + |
| 133 | +/// Allows creating [`EntityRc`] and handles syncing them with the world. |
| 134 | +/// |
| 135 | +/// Note: this can produce [`EntityRc`] containing any "payload", since the payload is not |
| 136 | +/// accessible during despawn time. This is because it's possible for the entity to be despawned |
| 137 | +/// explicitly even though an [`EntityRc`] is still held - callers should be resilient to this. |
| 138 | +pub struct EntityRcSource { |
| 139 | + /// The concurrent queue used for communicating drop events of [`EntityRcInner`]s. |
| 140 | + // Note: this could be a channel, but `bevy_ecs` already depends on `concurrent_queue`, so use |
| 141 | + // it as a simple channel. |
| 142 | + drop_notifier: Arc<ConcurrentQueue<Entity>>, |
| 143 | +} |
| 144 | + |
| 145 | +impl Default for EntityRcSource { |
| 146 | + fn default() -> Self { |
| 147 | + Self::new() |
| 148 | + } |
| 149 | +} |
| 150 | + |
| 151 | +impl EntityRcSource { |
| 152 | + /// Creates a new source of [`EntityRc`]s. |
| 153 | + /// |
| 154 | + /// Generally, only one [`EntityRcSource`] is needed, but having separate ones allows clean up |
| 155 | + /// operations to occur at different times or different rates. |
| 156 | + pub fn new() -> Self { |
| 157 | + Self { |
| 158 | + drop_notifier: Arc::new(ConcurrentQueue::unbounded()), |
| 159 | + } |
| 160 | + } |
| 161 | + |
| 162 | + /// Creates a new [`EntityRc`] for `entity`, storing the given `payload` in that [`EntityRc`]. |
| 163 | + /// |
| 164 | + /// It is up to the caller to ensure that the provided `entity` does not already have an |
| 165 | + /// [`EntityRc`] associated with it. Providing an `entity` which already has an [`EntityRc`] |
| 166 | + /// will result in two reference counts tracking the same entity and both attempting to despawn |
| 167 | + /// the entity (and more importantly, for a held [`EntityRc`] to have its entity despawned |
| 168 | + /// anyway). |
| 169 | + /// |
| 170 | + /// Providing an `entity` allows this method to be compatible with regular entity allocation |
| 171 | + /// ([`EntityAllocator`](crate::entity::EntityAllocator)), remote entity allocation |
| 172 | + /// ([`RemoteAllocator`](crate::entity::RemoteAllocator)), or even taking an existing entity and |
| 173 | + /// making it reference counted. |
| 174 | + pub fn create_rc<T: Send + Sync + 'static>(&self, entity: Entity, payload: T) -> EntityRc<T> { |
| 175 | + EntityRc(Arc::new(EntityRcInner { |
| 176 | + drop_notifier: self.drop_notifier.clone(), |
| 177 | + entity, |
| 178 | + payload, |
| 179 | + })) |
| 180 | + } |
| 181 | + |
| 182 | + /// Handles any dropped [`EntityRc`]s and despawns the corresponding entities. |
| 183 | + /// |
| 184 | + /// This must be called regularly in order for reference-counted entities to actually be cleaned |
| 185 | + /// up. |
| 186 | + /// |
| 187 | + /// Note: if you have exclusive world access (`&mut World`), you can use |
| 188 | + /// [`World::commands`](crate::world::World::commands) to get an instance of [`Commands`]. |
| 189 | + pub fn handle_dropped_rcs(&self, commands: &mut Commands) { |
| 190 | + for entity in self.drop_notifier.try_iter() { |
| 191 | + let Ok(mut entity) = commands.get_entity(entity) else { |
| 192 | + // We intended to despawn the entity - and the entity is despawned. Someone did our |
| 193 | + // work for us! |
| 194 | + continue; |
| 195 | + }; |
| 196 | + // Also only try to despawn here - if the entity is despawned when this is run, it's not |
| 197 | + // a problem. |
| 198 | + entity.try_despawn(); |
| 199 | + } |
| 200 | + } |
| 201 | +} |
0 commit comments