mod custom_storage;
mod delete_any;
mod retain;
pub use custom_storage::CustomStorageAccess;
pub use delete_any::{CustomDeleteAny, TupleDeleteAny};
pub use retain::TupleRetainStorage;
use crate::atomic_refcell::{ARef, ARefMut, AtomicRefCell};
use crate::borrow::Borrow;
use crate::component::{Component, Unique};
use crate::entities::Entities;
use crate::entity_id::EntityId;
use crate::get_component::GetComponent;
use crate::get_unique::GetUnique;
use crate::iter_component::{IntoIterRef, IterComponent};
use crate::memory_usage::AllStoragesMemoryUsage;
use crate::public_transport::RwLock;
use crate::public_transport::ShipyardRwLock;
use crate::r#mut::Mut;
use crate::reserve::BulkEntityIter;
use crate::sparse_set::{BulkAddEntity, SparseSet, TupleAddComponent, TupleDelete, TupleRemove};
#[cfg(feature = "std")]
use crate::std_thread_id_generator;
use crate::storage::{SBox, Storage, StorageId};
use crate::system::AllSystem;
use crate::tracking::{TrackingTimestamp, TupleTrack};
use crate::unique::UniqueStorage;
use crate::views::EntitiesViewMut;
use crate::{error, ShipHashMap};
use alloc::boxed::Box;
use alloc::sync::Arc;
use core::any::type_name;
use core::hash::BuildHasherDefault;
use core::marker::PhantomData;
use core::sync::atomic::AtomicU32;
use hashbrown::hash_map::Entry;
#[allow(missing_docs)]
pub struct MissingLock;
#[allow(missing_docs)]
pub struct LockPresent;
#[allow(missing_docs)]
pub struct MissingThreadId;
#[allow(missing_docs)]
pub struct ThreadIdPresent;
pub(crate) struct AllStoragesBuilder<Lock, ThreadId> {
custom_lock: Option<Box<dyn ShipyardRwLock + Send + Sync>>,
custom_thread_id: Option<Arc<dyn Fn() -> u64 + Send + Sync>>,
_phantom: PhantomData<(Lock, ThreadId)>,
}
impl<Lock, ThreadId> AllStoragesBuilder<Lock, ThreadId> {
#[cfg(feature = "std")]
pub(crate) fn new() -> AllStoragesBuilder<LockPresent, ThreadIdPresent> {
AllStoragesBuilder {
custom_lock: None,
custom_thread_id: Some(Arc::new(std_thread_id_generator)),
_phantom: PhantomData,
}
}
#[cfg(all(not(feature = "std"), not(feature = "thread_local")))]
pub(crate) fn new() -> AllStoragesBuilder<MissingLock, ThreadIdPresent> {
AllStoragesBuilder {
custom_lock: None,
custom_thread_id: None,
_phantom: PhantomData,
}
}
#[cfg(all(not(feature = "std"), feature = "thread_local"))]
pub(crate) fn new() -> AllStoragesBuilder<MissingLock, MissingThreadId> {
AllStoragesBuilder {
custom_lock: None,
custom_thread_id: None,
_phantom: PhantomData,
}
}
pub(crate) fn with_custom_lock<L: ShipyardRwLock + Send + Sync>(
self,
) -> AllStoragesBuilder<LockPresent, ThreadId> {
AllStoragesBuilder {
custom_lock: Some(L::new()),
custom_thread_id: self.custom_thread_id,
_phantom: PhantomData,
}
}
#[cfg(feature = "thread_local")]
pub(crate) fn with_custom_thread_id(
self,
thread_id: impl Fn() -> u64 + Send + Sync + 'static,
) -> AllStoragesBuilder<Lock, ThreadIdPresent> {
AllStoragesBuilder {
custom_lock: self.custom_lock,
custom_thread_id: Some(Arc::new(thread_id)),
_phantom: PhantomData,
}
}
}
impl AllStoragesBuilder<LockPresent, ThreadIdPresent> {
pub(crate) fn build(self, counter: Arc<AtomicU32>) -> AtomicRefCell<AllStorages> {
let mut storages = ShipHashMap::with_hasher(BuildHasherDefault::default());
storages.insert(StorageId::of::<Entities>(), SBox::new(Entities::new()));
let storages = if let Some(custom_lock) = self.custom_lock {
RwLock::new_custom(custom_lock, storages)
} else {
#[cfg(feature = "std")]
{
RwLock::new_std(storages)
}
#[cfg(not(feature = "std"))]
{
unreachable!()
}
};
#[cfg(feature = "thread_local")]
let thread_id_generator = self.custom_thread_id.unwrap();
#[cfg(feature = "thread_local")]
let main_thread_id = (thread_id_generator)();
#[cfg(feature = "thread_local")]
{
AtomicRefCell::new_non_send(
AllStorages {
storages,
main_thread_id,
thread_id_generator: thread_id_generator.clone(),
counter,
},
thread_id_generator,
)
}
#[cfg(not(feature = "thread_local"))]
{
AtomicRefCell::new(AllStorages { storages, counter })
}
}
}
pub struct AllStorages {
pub(crate) storages: RwLock<ShipHashMap<StorageId, SBox>>,
#[cfg(feature = "thread_local")]
main_thread_id: u64,
#[cfg(feature = "thread_local")]
thread_id_generator: Arc<dyn Fn() -> u64 + Send + Sync>,
counter: Arc<AtomicU32>,
}
#[cfg(not(feature = "thread_local"))]
unsafe impl Send for AllStorages {}
unsafe impl Sync for AllStorages {}
impl AllStorages {
#[cfg(feature = "std")]
pub(crate) fn new(counter: Arc<AtomicU32>) -> Self {
let mut storages = ShipHashMap::with_hasher(BuildHasherDefault::default());
storages.insert(StorageId::of::<Entities>(), SBox::new(Entities::new()));
AllStorages {
storages: RwLock::new_std(storages),
#[cfg(feature = "thread_local")]
main_thread_id: (std_thread_id_generator)(),
#[cfg(feature = "thread_local")]
thread_id_generator: Arc::new(std_thread_id_generator),
counter,
}
}
pub fn add_unique<T: Send + Sync + Unique>(&self, component: T) {
let storage_id = StorageId::of::<UniqueStorage<T>>();
self.storages
.write()
.entry(storage_id)
.insert(SBox::new(UniqueStorage::new(
component,
self.get_tracking_timestamp(),
)));
}
#[cfg(feature = "thread_local")]
pub fn add_unique_non_send<T: Sync + Unique>(&self, component: T) {
if (self.thread_id_generator)() == self.main_thread_id {
let storage_id = StorageId::of::<UniqueStorage<T>>();
self.storages.write().entry(storage_id).or_insert_with(|| {
SBox::new_non_send(
UniqueStorage::new(component, self.get_tracking_timestamp()),
self.thread_id_generator.clone(),
)
});
}
}
#[cfg(feature = "thread_local")]
pub fn add_unique_non_sync<T: Send + Unique>(&self, component: T) {
let storage_id = StorageId::of::<UniqueStorage<T>>();
self.storages.write().entry(storage_id).or_insert_with(|| {
SBox::new_non_sync(UniqueStorage::new(component, self.get_tracking_timestamp()))
});
}
#[cfg(feature = "thread_local")]
pub fn add_unique_non_send_sync<T: Unique>(&self, component: T) {
if (self.thread_id_generator)() == self.main_thread_id {
let storage_id = StorageId::of::<UniqueStorage<T>>();
self.storages.write().entry(storage_id).or_insert_with(|| {
SBox::new_non_send_sync(
UniqueStorage::new(component, self.get_tracking_timestamp()),
self.thread_id_generator.clone(),
)
});
}
}
pub fn remove_unique<T: Unique>(&self) -> Result<T, error::UniqueRemove> {
let storage_id = StorageId::of::<UniqueStorage<T>>();
{
let mut storages = self.storages.write();
let storage = if let Entry::Occupied(entry) = storages.entry(storage_id) {
if let Some(err) = unsafe { &*entry.get().0 }.borrow_mut().err() {
return Err(error::UniqueRemove::StorageBorrow((type_name::<T>(), err)));
} else {
entry.remove()
}
} else {
return Err(error::UniqueRemove::MissingUnique(type_name::<T>()));
};
let unique: Box<AtomicRefCell<UniqueStorage<T>>> =
unsafe { Box::from_raw(storage.0 as *mut AtomicRefCell<UniqueStorage<T>>) };
core::mem::forget(storage);
Ok(unique.into_inner().value)
}
}
pub fn delete_entity(&mut self, entity: EntityId) -> bool {
let mut entities = self.entities_mut().unwrap();
if entities.delete_unchecked(entity) {
drop(entities);
self.strip(entity);
true
} else {
false
}
}
#[track_caller]
pub fn strip(&mut self, entity: EntityId) {
let current = self.get_current();
for storage in self.storages.get_mut().values_mut() {
unsafe { &mut *storage.0 }.get_mut().delete(entity, current);
}
}
pub fn retain_storage<S: TupleRetainStorage>(&mut self, entity: EntityId) {
S::retain(self, entity);
}
#[track_caller]
pub fn retain_storage_by_id(&mut self, entity: EntityId, excluded_storage: &[StorageId]) {
let current = self.get_current();
for (storage_id, storage) in self.storages.get_mut().iter_mut() {
if !excluded_storage.contains(storage_id) {
unsafe { &mut *storage.0 }.get_mut().delete(entity, current);
}
}
}
#[track_caller]
pub fn clear(&mut self) {
let current = self.get_current();
for storage in self.storages.get_mut().values_mut() {
unsafe { &mut *storage.0 }.get_mut().clear(current);
}
}
#[track_caller]
pub fn clear_all_removed_and_deleted(&mut self) {
for storage in self.storages.get_mut().values_mut() {
unsafe { &mut *storage.0 }
.get_mut()
.clear_all_removed_and_deleted();
}
}
#[track_caller]
pub fn clear_all_removed_and_deleted_older_than_timestamp(
&mut self,
timestamp: TrackingTimestamp,
) {
for storage in self.storages.get_mut().values_mut() {
unsafe { &mut *storage.0 }
.get_mut()
.clear_all_removed_and_deleted_older_than_timestamp(timestamp);
}
}
#[track_caller]
pub fn retain<T: Component + Send + Sync>(&mut self, f: impl FnMut(EntityId, &T) -> bool) {
let current = self.get_current();
self.exclusive_storage_mut::<SparseSet<T>>()
.unwrap()
.private_retain(current, f);
}
#[track_caller]
pub fn retain_mut<T: Component + Send + Sync>(
&mut self,
f: impl FnMut(EntityId, Mut<'_, T>) -> bool,
) {
let current = self.get_current();
self.exclusive_storage_mut::<SparseSet<T>>()
.unwrap()
.private_retain_mut(current, f);
}
#[inline]
pub fn add_entity<T: TupleAddComponent>(&mut self, component: T) -> EntityId {
let current = self.get_current();
let entity = self.exclusive_storage_mut::<Entities>().unwrap().generate();
component.add_component(self, entity, current);
entity
}
#[inline]
pub fn bulk_add_entity<T: BulkAddEntity>(&mut self, source: T) -> BulkEntityIter<'_> {
source.bulk_add_entity(self)
}
#[track_caller]
#[inline]
pub fn add_component<T: TupleAddComponent>(&mut self, entity: EntityId, component: T) {
let current = self.get_current();
if self
.exclusive_storage_mut::<Entities>()
.unwrap()
.is_alive(entity)
{
component.add_component(self, entity, current);
} else {
panic!("{:?}", error::AddComponent::EntityIsNotAlive);
}
}
#[inline]
pub fn delete_component<C: TupleDelete>(&mut self, entity: EntityId) {
C::delete(self, entity);
}
#[inline]
pub fn remove<C: TupleRemove>(&mut self, entity: EntityId) -> C::Out {
C::remove(self, entity)
}
#[doc = "Borrows the requested storage(s), if it doesn't exist it'll get created.
You can use a tuple to get multiple storages at once.
You can use:
* [View]\\<T\\> for a shared access to `T` storage
* [ViewMut]\\<T\\> for an exclusive access to `T` storage
* [EntitiesView] for a shared access to the entity storage
* [EntitiesViewMut] for an exclusive reference to the entity storage
* [UniqueView]\\<T\\> for a shared access to a `T` unique storage
* [UniqueViewMut]\\<T\\> for an exclusive access to a `T` unique storage
* `Option<V>` with one or multiple views for fallible access to one or more storages"]
#[cfg_attr(
all(feature = "thread_local", docsrs),
doc = "* <span style=\"display: table;color: #2f2f2f;background-color: #C4ECFF;border-width: 1px;border-style: solid;border-color: #7BA5DB;padding: 3px;margin-bottom: 5px; font-size: 90%\">This is supported on <strong><code style=\"background-color: #C4ECFF\">feature=\"thread_local\"</code></strong> only:</span>"
)]
#[cfg_attr(
all(feature = "thread_local", docsrs),
doc = " * [NonSend]<[View]\\<T\\>> for a shared access to a `T` storage where `T` isn't `Send`
* [NonSend]<[ViewMut]\\<T\\>> for an exclusive access to a `T` storage where `T` isn't `Send`
[NonSend] and [UniqueView]/[UniqueViewMut] can be used together to access a `!Send` unique storage."
)]
#[cfg_attr(
all(feature = "thread_local", not(docsrs)),
doc = "* [NonSend]<[View]\\<T\\>> for a shared access to a `T` storage where `T` isn't `Send`
* [NonSend]<[ViewMut]\\<T\\>> for an exclusive access to a `T` storage where `T` isn't `Send`
[NonSend] and [UniqueView]/[UniqueViewMut] can be used together to access a `!Send` unique storage."
)]
#[cfg_attr(
not(feature = "thread_local"),
doc = "* NonSend: must activate the *thread_local* feature"
)]
#[cfg_attr(
all(feature = "thread_local", docsrs),
doc = " * [NonSync]<[View]\\<T\\>> for a shared access to a `T` storage where `T` isn't `Sync`
* [NonSync]<[ViewMut]\\<T\\>> for an exclusive access to a `T` storage where `T` isn't `Sync`
[NonSync] and [UniqueView]/[UniqueViewMut] can be used together to access a `!Sync` unique storage."
)]
#[cfg_attr(
all(feature = "thread_local", not(docsrs)),
doc = "* [NonSync]<[View]\\<T\\>> for a shared access to a `T` storage where `T` isn't `Sync`
* [NonSync]<[ViewMut]\\<T\\>> for an exclusive access to a `T` storage where `T` isn't `Sync`
[NonSync] and [UniqueView]/[UniqueViewMut] can be used together to access a `!Sync` unique storage."
)]
#[cfg_attr(
not(feature = "thread_local"),
doc = "* NonSync: must activate the *thread_local* feature"
)]
#[cfg_attr(
all(feature = "thread_local", docsrs),
doc = " * [NonSendSync]<[View]\\<T\\>> for a shared access to a `T` storage where `T` isn't `Send` nor `Sync`
* [NonSendSync]<[ViewMut]\\<T\\>> for an exclusive access to a `T` storage where `T` isn't `Send` nor `Sync`
[NonSendSync] and [UniqueView]/[UniqueViewMut] can be used together to access a `!Send + !Sync` unique storage."
)]
#[cfg_attr(
all(feature = "thread_local", not(docsrs)),
doc = "* [NonSendSync]<[View]\\<T\\>> for a shared access to a `T` storage where `T` isn't `Send` nor `Sync`
* [NonSendSync]<[ViewMut]\\<T\\>> for an exclusive access to a `T` storage where `T` isn't `Send` nor `Sync`
[NonSendSync] and [UniqueView]/[UniqueViewMut] can be used together to access a `!Send + !Sync` unique storage."
)]
#[cfg_attr(
not(feature = "thread_local"),
doc = "* NonSendSync: must activate the *thread_local* feature"
)]
#[doc = "
### Borrows
- Storage (exclusive or shared)
### Errors
- Storage borrow failed.
- Unique storage did not exist.
### Example
```
use shipyard::{AllStoragesViewMut, Component, EntitiesView, View, ViewMut, World};
#[derive(Component)]
struct U32(u32);
#[derive(Component)]
struct USIZE(usize);
let world = World::new();
let all_storages = world.borrow::<AllStoragesViewMut>().unwrap();
let u32s = all_storages.borrow::<View<U32>>().unwrap();
let (entities, mut usizes) = all_storages
.borrow::<(EntitiesView, ViewMut<USIZE>)>()
.unwrap();
```
[EntitiesView]: crate::Entities
[EntitiesViewMut]: crate::Entities
[View]: crate::View
[ViewMut]: crate::ViewMut
[UniqueView]: crate::UniqueView
[UniqueViewMut]: crate::UniqueViewMut"]
#[cfg_attr(feature = "thread_local", doc = "[NonSend]: crate::NonSend")]
#[cfg_attr(feature = "thread_local", doc = "[NonSync]: crate::NonSync")]
#[cfg_attr(feature = "thread_local", doc = "[NonSendSync]: crate::NonSendSync")]
pub fn borrow<V: Borrow>(&self) -> Result<V::View<'_>, error::GetStorage> {
let current = self.get_current();
V::borrow(self, None, None, current)
}
#[doc = "Borrows the requested storages, runs the function and evaluates to the function's return value.
Data can be passed to the function, this always has to be a single type but you can use a tuple if needed.
You can use:
* [View]\\<T\\> for a shared access to `T` storage
* [ViewMut]\\<T\\> for an exclusive access to `T` storage
* [EntitiesView] for a shared access to the entity storage
* [EntitiesViewMut] for an exclusive reference to the entity storage
* [UniqueView]\\<T\\> for a shared access to a `T` unique storage
* [UniqueViewMut]\\<T\\> for an exclusive access to a `T` unique storage
* `Option<V>` with one or multiple views for fallible access to one or more storages"]
#[cfg_attr(
all(feature = "thread_local", docsrs),
doc = "* <span style=\"display: table;color: #2f2f2f;background-color: #C4ECFF;border-width: 1px;border-style: solid;border-color: #7BA5DB;padding: 3px;margin-bottom: 5px; font-size: 90%\">This is supported on <strong><code style=\"background-color: #C4ECFF\">feature=\"thread_local\"</code></strong> only:</span>"
)]
#[cfg_attr(
all(feature = "thread_local", docsrs),
doc = " * [NonSend]<[View]\\<T\\>> for a shared access to a `T` storage where `T` isn't `Send`
* [NonSend]<[ViewMut]\\<T\\>> for an exclusive access to a `T` storage where `T` isn't `Send`
[NonSend] and [UniqueView]/[UniqueViewMut] can be used together to access a `!Send` unique storage."
)]
#[cfg_attr(
all(feature = "thread_local", not(docsrs)),
doc = "* [NonSend]<[View]\\<T\\>> for a shared access to a `T` storage where `T` isn't `Send`
* [NonSend]<[ViewMut]\\<T\\>> for an exclusive access to a `T` storage where `T` isn't `Send`
[NonSend] and [UniqueView]/[UniqueViewMut] can be used together to access a `!Send` unique storage."
)]
#[cfg_attr(
not(feature = "thread_local"),
doc = "* NonSend: must activate the *thread_local* feature"
)]
#[cfg_attr(
all(feature = "thread_local", docsrs),
doc = " * [NonSync]<[View]\\<T\\>> for a shared access to a `T` storage where `T` isn't `Sync`
* [NonSync]<[ViewMut]\\<T\\>> for an exclusive access to a `T` storage where `T` isn't `Sync`
[NonSync] and [UniqueView]/[UniqueViewMut] can be used together to access a `!Sync` unique storage."
)]
#[cfg_attr(
all(feature = "thread_local", not(docsrs)),
doc = "* [NonSync]<[View]\\<T\\>> for a shared access to a `T` storage where `T` isn't `Sync`
* [NonSync]<[ViewMut]\\<T\\>> for an exclusive access to a `T` storage where `T` isn't `Sync`
[NonSync] and [UniqueView]/[UniqueViewMut] can be used together to access a `!Sync` unique storage."
)]
#[cfg_attr(
not(feature = "thread_local"),
doc = "* NonSync: must activate the *thread_local* feature"
)]
#[cfg_attr(
all(feature = "thread_local", docsrs),
doc = " * [NonSendSync]<[View]\\<T\\>> for a shared access to a `T` storage where `T` isn't `Send` nor `Sync`
* [NonSendSync]<[ViewMut]\\<T\\>> for an exclusive access to a `T` storage where `T` isn't `Send` nor `Sync`
[NonSendSync] and [UniqueView]/[UniqueViewMut] can be used together to access a `!Send + !Sync` unique storage."
)]
#[cfg_attr(
all(feature = "thread_local", not(docsrs)),
doc = "* [NonSendSync]<[View]\\<T\\>> for a shared access to a `T` storage where `T` isn't `Send` nor `Sync`
* [NonSendSync]<[ViewMut]\\<T\\>> for an exclusive access to a `T` storage where `T` isn't `Send` nor `Sync`
[NonSendSync] and [UniqueView]/[UniqueViewMut] can be used together to access a `!Send + !Sync` unique storage."
)]
#[cfg_attr(
not(feature = "thread_local"),
doc = "* NonSendSync: must activate the *thread_local* feature"
)]
#[doc = "
### Borrows
- Storage (exclusive or shared)
### Panics
- Storage borrow failed.
- Unique storage did not exist.
- Error returned by user.
[EntitiesView]: crate::Entities
[EntitiesViewMut]: crate::Entities
[World]: crate::World
[View]: crate::View
[ViewMut]: crate::ViewMut
[UniqueView]: crate::UniqueView
[UniqueViewMut]: crate::UniqueViewMut"]
#[cfg_attr(feature = "thread_local", doc = "[NonSend]: crate::NonSend")]
#[cfg_attr(feature = "thread_local", doc = "[NonSync]: crate::NonSync")]
#[cfg_attr(feature = "thread_local", doc = "[NonSendSync]: crate::NonSendSync")]
#[track_caller]
pub fn run_with_data<Data, B, S: AllSystem<(Data,), B>>(
&self,
system: S,
data: Data,
) -> S::Return {
#[cfg(feature = "tracing")]
let system_span = tracing::info_span!("system", name = ?type_name::<S>());
#[cfg(feature = "tracing")]
let _system_span = system_span.enter();
system
.run((data,), self)
.map_err(error::Run::GetStorage)
.unwrap()
}
#[doc = "Borrows the requested storages, runs the function and evaluates to the function's return value.
You can use:
* [View]\\<T\\> for a shared access to `T` storage
* [ViewMut]\\<T\\> for an exclusive access to `T` storage
* [EntitiesView] for a shared access to the entity storage
* [EntitiesViewMut] for an exclusive reference to the entity storage
* [UniqueView]\\<T\\> for a shared access to a `T` unique storage
* [UniqueViewMut]\\<T\\> for an exclusive access to a `T` unique storage
* `Option<V>` with one or multiple views for fallible access to one or more storages"]
#[cfg_attr(
all(feature = "thread_local", docsrs),
doc = "* <span style=\"display: table;color: #2f2f2f;background-color: #C4ECFF;border-width: 1px;border-style: solid;border-color: #7BA5DB;padding: 3px;margin-bottom: 5px; font-size: 90%\">This is supported on <strong><code style=\"background-color: #C4ECFF\">feature=\"thread_local\"</code></strong> only:</span>"
)]
#[cfg_attr(
all(feature = "thread_local", docsrs),
doc = " * [NonSend]<[View]\\<T\\>> for a shared access to a `T` storage where `T` isn't `Send`
* [NonSend]<[ViewMut]\\<T\\>> for an exclusive access to a `T` storage where `T` isn't `Send`
[NonSend] and [UniqueView]/[UniqueViewMut] can be used together to access a `!Send` unique storage."
)]
#[cfg_attr(
all(feature = "thread_local", not(docsrs)),
doc = "* [NonSend]<[View]\\<T\\>> for a shared access to a `T` storage where `T` isn't `Send`
* [NonSend]<[ViewMut]\\<T\\>> for an exclusive access to a `T` storage where `T` isn't `Send`
[NonSend] and [UniqueView]/[UniqueViewMut] can be used together to access a `!Send` unique storage."
)]
#[cfg_attr(
not(feature = "thread_local"),
doc = "* NonSend: must activate the *thread_local* feature"
)]
#[cfg_attr(
all(feature = "thread_local", docsrs),
doc = " * [NonSync]<[View]\\<T\\>> for a shared access to a `T` storage where `T` isn't `Sync`
* [NonSync]<[ViewMut]\\<T\\>> for an exclusive access to a `T` storage where `T` isn't `Sync`
[NonSync] and [UniqueView]/[UniqueViewMut] can be used together to access a `!Sync` unique storage."
)]
#[cfg_attr(
all(feature = "thread_local", not(docsrs)),
doc = "* [NonSync]<[View]\\<T\\>> for a shared access to a `T` storage where `T` isn't `Sync`
* [NonSync]<[ViewMut]\\<T\\>> for an exclusive access to a `T` storage where `T` isn't `Sync`
[NonSync] and [UniqueView]/[UniqueViewMut] can be used together to access a `!Sync` unique storage."
)]
#[cfg_attr(
not(feature = "thread_local"),
doc = "* NonSync: must activate the *thread_local* feature"
)]
#[cfg_attr(
all(feature = "thread_local", docsrs),
doc = " * [NonSendSync]<[View]\\<T\\>> for a shared access to a `T` storage where `T` isn't `Send` nor `Sync`
* [NonSendSync]<[ViewMut]\\<T\\>> for an exclusive access to a `T` storage where `T` isn't `Send` nor `Sync`
[NonSendSync] and [UniqueView]/[UniqueViewMut] can be used together to access a `!Send + !Sync` unique storage."
)]
#[cfg_attr(
all(feature = "thread_local", not(docsrs)),
doc = "* [NonSendSync]<[View]\\<T\\>> for a shared access to a `T` storage where `T` isn't `Send` nor `Sync`
* [NonSendSync]<[ViewMut]\\<T\\>> for an exclusive access to a `T` storage where `T` isn't `Send` nor `Sync`
[NonSendSync] and [UniqueView]/[UniqueViewMut] can be used together to access a `!Send + !Sync` unique storage."
)]
#[cfg_attr(
not(feature = "thread_local"),
doc = "* NonSendSync: must activate the *thread_local* feature"
)]
#[doc = "
### Borrows
- Storage (exclusive or shared)
### Panics
- Storage borrow failed.
- Unique storage did not exist.
- Error returned by user.
### Example
```
use shipyard::{AllStoragesViewMut, Component, View, ViewMut, World};
#[derive(Component)]
struct I32(i32);
#[derive(Component)]
struct U32(u32);
#[derive(Component)]
struct USIZE(usize);
fn sys1(i32s: View<I32>) -> i32 {
0
}
let world = World::new();
let all_storages = world.borrow::<AllStoragesViewMut>().unwrap();
all_storages
.run(|usizes: View<USIZE>, mut u32s: ViewMut<U32>| {
// -- snip --
});
let i = all_storages.run(sys1);
```
[EntitiesView]: crate::Entities
[EntitiesViewMut]: crate::Entities
[View]: crate::View
[ViewMut]: crate::ViewMut
[UniqueView]: crate::UniqueView
[UniqueViewMut]: crate::UniqueViewMut"]
#[cfg_attr(feature = "thread_local", doc = "[NonSend]: crate::NonSend")]
#[cfg_attr(feature = "thread_local", doc = "[NonSync]: crate::NonSync")]
#[cfg_attr(feature = "thread_local", doc = "[NonSendSync]: crate::NonSendSync")]
#[track_caller]
pub fn run<B, S: AllSystem<(), B>>(&self, system: S) -> S::Return {
#[cfg(feature = "tracing")]
let system_span = tracing::info_span!("system", name = ?type_name::<S>());
#[cfg(feature = "tracing")]
let _system_span = system_span.enter();
system
.run((), self)
.map_err(error::Run::GetStorage)
.unwrap()
}
pub fn delete_any<T: TupleDeleteAny>(&mut self) {
T::delete_any(self);
}
pub(crate) fn entities(&self) -> Result<ARef<'_, &'_ Entities>, error::GetStorage> {
let storage_id = StorageId::of::<Entities>();
let storages = self.storages.read();
let storage = storages.get(&storage_id).unwrap();
let storage = unsafe { &*storage.0 }.borrow();
drop(storages);
match storage {
Ok(storage) => Ok(ARef::map(storage, |storage| {
storage.as_any().downcast_ref().unwrap()
})),
Err(err) => Err(error::GetStorage::Entities(err)),
}
}
pub(crate) fn entities_mut(&self) -> Result<ARefMut<'_, &'_ mut Entities>, error::GetStorage> {
let storage_id = StorageId::of::<Entities>();
let storages = self.storages.read();
let storage = storages.get(&storage_id).unwrap();
let storage = unsafe { &*storage.0 }.borrow_mut();
drop(storages);
match storage {
Ok(storage) => Ok(ARefMut::map(storage, |storage| {
storage.as_any_mut().downcast_mut().unwrap()
})),
Err(err) => Err(error::GetStorage::Entities(err)),
}
}
pub(crate) fn exclusive_storage_mut<T: 'static>(
&mut self,
) -> Result<&mut T, error::GetStorage> {
self.exclusive_storage_mut_by_id(StorageId::of::<T>())
}
#[track_caller]
pub(crate) fn exclusive_storage_mut_by_id<T: 'static>(
&mut self,
storage_id: StorageId,
) -> Result<&mut T, error::GetStorage> {
if let Some(storage) = self.storages.get_mut().get_mut(&storage_id) {
let storage = unsafe { &mut *storage.0 }
.get_mut()
.as_any_mut()
.downcast_mut()
.unwrap();
Ok(storage)
} else {
Err(error::GetStorage::MissingStorage {
name: Some(type_name::<T>()),
id: StorageId::of::<T>(),
})
}
}
pub(crate) fn exclusive_storage_or_insert_mut<T, F>(
&mut self,
storage_id: StorageId,
f: F,
) -> &mut T
where
T: 'static + Storage + Send + Sync,
F: FnOnce() -> T,
{
let storages = self.storages.get_mut();
unsafe {
&mut *storages
.entry(storage_id)
.or_insert_with(|| SBox::new(f()))
.0
}
.get_mut()
.as_any_mut()
.downcast_mut()
.unwrap()
}
#[cfg(feature = "thread_local")]
#[track_caller]
pub(crate) fn exclusive_storage_or_insert_non_send_mut<T, F>(
&mut self,
storage_id: StorageId,
f: F,
) -> &mut T
where
T: 'static + Storage + Sync,
F: FnOnce() -> T,
{
let storages = self.storages.get_mut();
unsafe {
&mut *storages
.entry(storage_id)
.or_insert_with(|| SBox::new_non_send(f(), self.thread_id_generator.clone()))
.0
}
.get_mut()
.as_any_mut()
.downcast_mut()
.unwrap()
}
#[cfg(feature = "thread_local")]
pub(crate) fn exclusive_storage_or_insert_non_sync_mut<T, F>(
&mut self,
storage_id: StorageId,
f: F,
) -> &mut T
where
T: 'static + Storage + Send,
F: FnOnce() -> T,
{
let storages = self.storages.get_mut();
unsafe {
&mut *storages
.entry(storage_id)
.or_insert_with(|| SBox::new_non_sync(f()))
.0
}
.get_mut()
.as_any_mut()
.downcast_mut()
.unwrap()
}
#[cfg(feature = "thread_local")]
#[track_caller]
pub(crate) fn exclusive_storage_or_insert_non_send_sync_mut<T, F>(
&mut self,
storage_id: StorageId,
f: F,
) -> &mut T
where
T: 'static + Storage,
F: FnOnce() -> T,
{
let storages = self.storages.get_mut();
unsafe {
&mut *storages
.entry(storage_id)
.or_insert_with(|| SBox::new_non_send_sync(f(), self.thread_id_generator.clone()))
.0
}
.get_mut()
.as_any_mut()
.downcast_mut()
.unwrap()
}
#[inline]
pub fn spawn(&mut self, entity: EntityId) -> bool {
self.exclusive_storage_mut::<Entities>()
.unwrap()
.spawn(entity)
}
pub fn memory_usage(&self) -> AllStoragesMemoryUsage<'_> {
AllStoragesMemoryUsage(self)
}
#[inline]
pub(crate) fn get_current(&self) -> TrackingTimestamp {
TrackingTimestamp::new(
self.counter
.fetch_add(1, core::sync::atomic::Ordering::Acquire),
)
}
pub fn get_tracking_timestamp(&self) -> TrackingTimestamp {
TrackingTimestamp::new(self.counter.load(core::sync::atomic::Ordering::Acquire))
}
pub fn track_insertion<T: TupleTrack>(&mut self) -> &mut AllStorages {
T::track_insertion(self);
self
}
pub fn track_modification<T: TupleTrack>(&mut self) -> &mut AllStorages {
T::track_modification(self);
self
}
pub fn track_deletion<T: TupleTrack>(&mut self) -> &mut AllStorages {
T::track_deletion(self);
self
}
pub fn track_removal<T: TupleTrack>(&mut self) -> &mut AllStorages {
T::track_removal(self);
self
}
pub fn track_all<T: TupleTrack>(&mut self) {
T::track_all(self);
}
#[doc = "Retrieve components of `entity`.
Multiple components can be queried at the same time using a tuple.
You can use:
* `&T` for a shared access to `T` component
* `&mut T` for an exclusive access to `T` component"]
#[cfg_attr(
all(feature = "thread_local", docsrs),
doc = "* <span style=\"display: table;color: #2f2f2f;background-color: #C4ECFF;border-width: 1px;border-style: solid;border-color: #7BA5DB;padding: 3px;margin-bottom: 5px; font-size: 90%\">This is supported on <strong><code style=\"background-color: #C4ECFF\">feature=\"thread_local\"</code></strong> only:</span>"
)]
#[cfg_attr(
all(feature = "thread_local"),
doc = "* [NonSend]<&T> for a shared access to a `T` component where `T` isn't `Send`
* [NonSend]<&mut T> for an exclusive access to a `T` component where `T` isn't `Send`
* [NonSync]<&T> for a shared access to a `T` component where `T` isn't `Sync`
* [NonSync]<&mut T> for an exclusive access to a `T` component where `T` isn't `Sync`
* [NonSendSync]<&T> for a shared access to a `T` component where `T` isn't `Send` nor `Sync`
* [NonSendSync]<&mut T> for an exclusive access to a `T` component where `T` isn't `Send` nor `Sync`"
)]
#[cfg_attr(
not(feature = "thread_local"),
doc = "* NonSend: must activate the *thread_local* feature
* NonSync: must activate the *thread_local* feature
* NonSendSync: must activate the *thread_local* feature"
)]
#[doc = "
### Borrows
- [AllStorages] (shared) + storage (exclusive or shared)
### Errors
- [AllStorages] borrow failed.
- Storage borrow failed.
- Entity does not have the component.
### Example
```
use shipyard::{AllStoragesViewMut, Component, World};
#[derive(Component, Debug, PartialEq, Eq)]
struct U32(u32);
#[derive(Component, Debug, PartialEq, Eq)]
struct USIZE(usize);
let world = World::new();
let mut all_storages = world.borrow::<AllStoragesViewMut>().unwrap();
let entity = all_storages.add_entity((USIZE(0), U32(1)));
let (i, j) = all_storages.get::<(&USIZE, &mut U32)>(entity).unwrap();
assert!(*i == &USIZE(0));
assert!(*j == &U32(1));
```"]
#[cfg_attr(
feature = "thread_local",
doc = "[NonSend]: crate::NonSend
[NonSync]: crate::NonSync
[NonSendSync]: crate::NonSendSync"
)]
#[inline]
pub fn get<T: GetComponent>(
&self,
entity: EntityId,
) -> Result<T::Out<'_>, error::GetComponent> {
let current = self.get_current();
T::get(self, None, current, entity)
}
#[doc = "Retrieve a unique component.
You can use:
* `&T` for a shared access to `T` unique component
* `&mut T` for an exclusive access to `T` unique component"]
#[cfg_attr(
all(feature = "thread_local", docsrs),
doc = "* <span style=\"display: table;color: #2f2f2f;background-color: #C4ECFF;border-width: 1px;border-style: solid;border-color: #7BA5DB;padding: 3px;margin-bottom: 5px; font-size: 90%\">This is supported on <strong><code style=\"background-color: #C4ECFF\">feature=\"thread_local\"</code></strong> only:</span>"
)]
#[cfg_attr(
all(feature = "thread_local"),
doc = "* [NonSend]<&T> for a shared access to a `T` unique component where `T` isn't `Send`
* [NonSend]<&mut T> for an exclusive access to a `T` unique component where `T` isn't `Send`
* [NonSync]<&T> for a shared access to a `T` unique component where `T` isn't `Sync`
* [NonSync]<&mut T> for an exclusive access to a `T` unique component where `T` isn't `Sync`
* [NonSendSync]<&T> for a shared access to a `T` unique component where `T` isn't `Send` nor `Sync`
* [NonSendSync]<&mut T> for an exclusive access to a `T` unique component where `T` isn't `Send` nor `Sync`"
)]
#[cfg_attr(
not(feature = "thread_local"),
doc = "* NonSend: must activate the *thread_local* feature
* NonSync: must activate the *thread_local* feature
* NonSendSync: must activate the *thread_local* feature"
)]
#[doc = "
### Borrows
- [AllStorages] (shared) + storage (exclusive or shared)
### Errors
- [AllStorages] borrow failed.
- Storage borrow failed.
### Example
```
use shipyard::{AllStoragesViewMut, Unique, World};
#[derive(Unique, Debug, PartialEq, Eq)]
struct U32(u32);
let world = World::new();
let mut all_storages = world.borrow::<AllStoragesViewMut>().unwrap();
all_storages.add_unique(U32(0));
let i = all_storages.get_unique::<&U32>().unwrap();
assert!(*i == U32(0));
```"]
#[cfg_attr(
feature = "thread_local",
doc = "[NonSend]: crate::NonSend
[NonSync]: crate::NonSync
[NonSendSync]: crate::NonSendSync"
)]
#[inline]
pub fn get_unique<T: GetUnique>(&self) -> Result<T::Out<'_>, error::GetStorage> {
T::get_unique(self, None)
}
#[doc = "Iterate components.
Multiple components can be iterated at the same time using a tuple.
You can use:
* `&T` for a shared access to `T` component
* `&mut T` for an exclusive access to `T` component"]
#[cfg_attr(
all(feature = "thread_local", docsrs),
doc = "* <span style=\"display: table;color: #2f2f2f;background-color: #C4ECFF;border-width: 1px;border-style: solid;border-color: #7BA5DB;padding: 3px;margin-bottom: 5px; font-size: 90%\">This is supported on <strong><code style=\"background-color: #C4ECFF\">feature=\"thread_local\"</code></strong> only:</span>"
)]
#[cfg_attr(
all(feature = "thread_local"),
doc = "* [NonSend]<&T> for a shared access to a `T` component where `T` isn't `Send`
* [NonSend]<&mut T> for an exclusive access to a `T` component where `T` isn't `Send`
* [NonSync]<&T> for a shared access to a `T` component where `T` isn't `Sync`
* [NonSync]<&mut T> for an exclusive access to a `T` component where `T` isn't `Sync`
* [NonSendSync]<&T> for a shared access to a `T` component where `T` isn't `Send` nor `Sync`
* [NonSendSync]<&mut T> for an exclusive access to a `T` component where `T` isn't `Send` nor `Sync`"
)]
#[cfg_attr(
not(feature = "thread_local"),
doc = "* NonSend: must activate the *thread_local* feature
* NonSync: must activate the *thread_local* feature
* NonSendSync: must activate the *thread_local* feature"
)]
#[doc = "
### Borrows
- [AllStorages] (shared)
### Panics
- [AllStorages] borrow failed.
### Example
```
use shipyard::{AllStoragesViewMut, Component, World};
#[derive(Component, Debug, PartialEq, Eq)]
struct U32(u32);
#[derive(Component, Debug, PartialEq, Eq)]
struct USIZE(usize);
let world = World::new();
let mut all_storages = world.borrow::<AllStoragesViewMut>().unwrap();
let entity = all_storages.add_entity((USIZE(0), U32(1)));
let mut iter = all_storages.iter::<(&USIZE, &mut U32)>();
for (i, j) in &mut iter {
// <-- SNIP -->
}
```"]
#[cfg_attr(
feature = "thread_local",
doc = "[NonSend]: crate::NonSend
[NonSync]: crate::NonSync
[NonSendSync]: crate::NonSendSync"
)]
#[inline]
#[track_caller]
pub fn iter<T: IterComponent>(&self) -> IntoIterRef<'_, T> {
let current = self.get_current();
IntoIterRef {
all_storages: self,
all_borrow: None,
current,
phantom: PhantomData,
}
}
#[track_caller]
pub fn on_deletion(&self, f: impl FnMut(EntityId) + Send + Sync + 'static) {
let mut entities = self.borrow::<EntitiesViewMut<'_>>().unwrap();
entities.on_deletion(f);
}
pub fn is_entity_alive(&mut self, entity: EntityId) -> bool {
self.exclusive_storage_mut::<Entities>()
.unwrap()
.is_alive(entity)
}
#[track_caller]
pub fn move_entity(&mut self, other: &mut AllStorages, entity: EntityId) {
let current = self.get_current();
let other_current = other.get_current();
if !self
.exclusive_storage_mut::<Entities>()
.unwrap()
.delete_unchecked(entity)
{
panic!(
"Entity {:?} has to be alive to move it to another World.",
entity
);
};
assert!(
other
.exclusive_storage_mut::<Entities>()
.unwrap()
.spawn(entity),
"Other World already has an entity at {:?}'s index.",
entity
);
for storage in self.storages.get_mut().values_mut() {
unsafe { &mut *storage.0 }.get_mut().move_component_from(
other,
entity,
entity,
current,
other_current,
);
}
}
#[track_caller]
pub fn move_components(&mut self, other: &mut AllStorages, from: EntityId, to: EntityId) {
let current = self.get_current();
let other_current = other.get_current();
if !self
.exclusive_storage_mut::<Entities>()
.unwrap()
.is_alive(from)
{
panic!(
"Entity {:?} has to be alive to move its components to another World.",
from
);
};
if !other
.exclusive_storage_mut::<Entities>()
.unwrap()
.is_alive(to)
{
panic!(
"Entity {:?} has to be alive to receive components from another World.",
to
);
};
for storage in self.storages.get_mut().values_mut() {
unsafe { &mut *storage.0 }.get_mut().move_component_from(
other,
from,
to,
current,
other_current,
);
}
}
}
impl core::fmt::Debug for AllStorages {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut debug_struct = f.debug_struct("AllStorages");
let storages = self.storages.read();
debug_struct.field("storage_count", &storages.len());
debug_struct.field("storages", &storages.values());
debug_struct.finish()
}
}
impl core::fmt::Debug for AllStoragesMemoryUsage<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut borrowed_storages = 0;
let mut debug_struct = f.debug_list();
let storages = self.0.storages.read();
debug_struct.entries(storages.values().filter_map(|storage| {
match unsafe { &*(storage.0) }.borrow() {
Ok(storage) => storage.memory_usage(),
Err(_) => {
borrowed_storages += 1;
None
}
}
}));
if borrowed_storages != 0 {
debug_struct.entry(&format_args!(
"{} storages could not be borrored",
borrowed_storages
));
}
debug_struct.finish()
}
}