diff --git a/src/storage/deref_flagged.rs b/src/storage/deref_flagged.rs index 5c9f8daf8..3941db4e6 100644 --- a/src/storage/deref_flagged.rs +++ b/src/storage/deref_flagged.rs @@ -7,7 +7,7 @@ use hibitset::BitSetLike; use crate::{ storage::{ComponentEvent, DenseVecStorage, Tracked, TryDefault, UnprotectedStorage}, - world::{Component, Index}, + world::{Component, HasIndex, Index}, }; use shrev::EventChannel; @@ -55,7 +55,12 @@ where } impl> UnprotectedStorage for DerefFlaggedStorage { - type AccessMut<'a> where T: 'a = FlaggedAccessMut<'a, >::AccessMut<'a>, C>; + type AccessMut<'a> + where + T: 'a, + = FlaggedAccessMut<'a, >::AccessMut<'a>, C>; + + type MutIndex = >::MutIndex; unsafe fn clean(&mut self, has: B) where @@ -68,27 +73,27 @@ impl> UnprotectedStorage for DerefFlag self.storage.get(id) } - unsafe fn get_mut(&mut self, id: Index) -> Self::AccessMut<'_> { + unsafe fn get_mut(&mut self, id: Self::MutIndex) -> Self::AccessMut<'_> { let emit = self.emit_event(); FlaggedAccessMut { channel: &mut self.channel, emit, - id, + id: id.id(), access: self.storage.get_mut(id), phantom: PhantomData, } } - unsafe fn insert(&mut self, id: Index, comp: C) { + unsafe fn insert(&mut self, id: Self::MutIndex, comp: C) { if self.emit_event() { - self.channel.single_write(ComponentEvent::Inserted(id)); + self.channel.single_write(ComponentEvent::Inserted(id.id())); } self.storage.insert(id, comp); } - unsafe fn remove(&mut self, id: Index) -> C { + unsafe fn remove(&mut self, id: Self::MutIndex) -> C { if self.emit_event() { - self.channel.single_write(ComponentEvent::Removed(id)); + self.channel.single_write(ComponentEvent::Removed(id.id())); } self.storage.remove(id) } @@ -123,14 +128,18 @@ pub struct FlaggedAccessMut<'a, A, C> { } impl<'a, A, C> Deref for FlaggedAccessMut<'a, A, C> - where A: Deref +where + A: Deref, { type Target = C; - fn deref(&self) -> &Self::Target { self.access.deref() } + fn deref(&self) -> &Self::Target { + self.access.deref() + } } impl<'a, A, C> DerefMut for FlaggedAccessMut<'a, A, C> - where A: DerefMut +where + A: DerefMut, { fn deref_mut(&mut self) -> &mut Self::Target { if self.emit { diff --git a/src/storage/deref_flagged_gen.rs b/src/storage/deref_flagged_gen.rs new file mode 100644 index 000000000..7d3c2a2c7 --- /dev/null +++ b/src/storage/deref_flagged_gen.rs @@ -0,0 +1,155 @@ +use std::{ + marker::PhantomData, + ops::{Deref, DerefMut}, +}; + +use hibitset::BitSetLike; + +use crate::{ + storage::{ComponentEvent, DenseVecStorage, Tracked, TryDefault, UnprotectedStorage}, + world::{Component, HasIndex, Index}, + Entity, +}; + +use shrev::EventChannel; + +/// Wrapper storage that tracks modifications, insertions, and removals of +/// components through an `EventChannel`, in a similar manner to `FlaggedStorage`. +/// +/// Unlike `FlaggedStorage`, this storage uses a wrapper type for mutable +/// accesses that only emits modification events when the component is actually +/// used mutably. This means that simply performing a mutable join or calling +/// `WriteStorage::get_mut` will not, by itself, trigger a modification event. +pub struct DerefFlaggedGenStorage> { + channel: EventChannel>, + storage: T, + #[cfg(feature = "storage-event-control")] + event_emission: bool, + phantom: PhantomData, +} + +impl DerefFlaggedGenStorage { + #[cfg(feature = "storage-event-control")] + fn emit_event(&self) -> bool { + self.event_emission + } + + #[cfg(not(feature = "storage-event-control"))] + fn emit_event(&self) -> bool { + true + } +} + +impl Default for DerefFlaggedGenStorage +where + T: TryDefault, +{ + fn default() -> Self { + Self { + channel: EventChannel::>::default(), + storage: T::unwrap_default(), + #[cfg(feature = "storage-event-control")] + event_emission: true, + phantom: PhantomData, + } + } +} + +impl> UnprotectedStorage + for DerefFlaggedGenStorage +{ + type AccessMut<'a> + where + T: 'a, + = FlaggedAccessMut<'a, >::AccessMut<'a>, C>; + + type MutIndex = Entity; + + unsafe fn clean(&mut self, has: B) + where + B: BitSetLike, + { + self.storage.clean(has); + } + + unsafe fn get(&self, id: Index) -> &C { + self.storage.get(id) + } + + unsafe fn get_mut(&mut self, id: Self::MutIndex) -> Self::AccessMut<'_> { + let emit = self.emit_event(); + FlaggedAccessMut { + channel: &mut self.channel, + emit, + id, + access: self.storage.get_mut(HasIndex::from_entity(id)), + phantom: PhantomData, + } + } + + unsafe fn insert(&mut self, id: Self::MutIndex, comp: C) { + if self.emit_event() { + self.channel.single_write(ComponentEvent::Inserted(id)); + } + self.storage.insert(HasIndex::from_entity(id), comp); + } + + unsafe fn remove(&mut self, id: Self::MutIndex) -> C { + if self.emit_event() { + self.channel.single_write(ComponentEvent::Removed(id)); + } + self.storage.remove(HasIndex::from_entity(id)) + } +} + +impl Tracked for DerefFlaggedGenStorage { + type Entity = Entity; + + fn channel(&self) -> &EventChannel> { + &self.channel + } + + fn channel_mut(&mut self) -> &mut EventChannel> { + &mut self.channel + } + + #[cfg(feature = "storage-event-control")] + fn set_event_emission(&mut self, emit: bool) { + self.event_emission = emit; + } + + #[cfg(feature = "storage-event-control")] + fn event_emission(&self) -> bool { + self.event_emission + } +} + +pub struct FlaggedAccessMut<'a, A, C> { + channel: &'a mut EventChannel>, + emit: bool, + id: Entity, + access: A, + phantom: PhantomData, +} + +impl<'a, A, C> Deref for FlaggedAccessMut<'a, A, C> +where + A: Deref, +{ + type Target = C; + fn deref(&self) -> &Self::Target { + self.access.deref() + } +} + +impl<'a, A, C> DerefMut for FlaggedAccessMut<'a, A, C> +where + A: DerefMut, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + if self.emit { + self.channel.single_write(ComponentEvent::Modified(self.id)); + } + self.access.deref_mut() + } +} diff --git a/src/storage/drain.rs b/src/storage/drain.rs index e86b52a5d..86e370e10 100644 --- a/src/storage/drain.rs +++ b/src/storage/drain.rs @@ -6,11 +6,17 @@ use crate::{ world::{Component, Index}, }; +#[cfg(feature = "nightly")] +use crate::world::{EntitiesRes, HasIndex}; + /// A draining storage wrapper which has a `Join` implementation /// that removes the components. pub struct Drain<'a, T: Component> { /// The masked storage pub data: &'a mut MaskedStorage, + /// Entities to get the generation for component events + #[cfg(feature = "nightly")] + pub entities: &'a EntitiesRes, } impl<'a, T> Join for Drain<'a, T> @@ -19,15 +25,32 @@ where { type Mask = BitSet; type Type = T; + #[cfg(feature = "nightly")] + type Value = (&'a mut MaskedStorage, &'a EntitiesRes); + #[cfg(not(feature = "nightly"))] type Value = &'a mut MaskedStorage; // SAFETY: No invariants to meet and no unsafe code. unsafe fn open(self) -> (Self::Mask, Self::Value) { let mask = self.data.mask.clone(); - (mask, self.data) + #[cfg(feature = "nightly")] + let t = (mask, (self.data, self.entities)); + #[cfg(not(feature = "nightly"))] + let t = (mask, self.data); + + t + } + + // SAFETY: No invariants to meet and no unsafe code. + #[cfg(feature = "nightly")] + unsafe fn get((storage, entities): &mut Self::Value, id: Index) -> T { + storage + .remove(HasIndex::from_index(id, entities)) + .expect("Tried to access same index twice") } + #[cfg(not(feature = "nightly"))] // SAFETY: No invariants to meet and no unsafe code. unsafe fn get(value: &mut Self::Value, id: Index) -> T { value.remove(id).expect("Tried to access same index twice") diff --git a/src/storage/entry.rs b/src/storage/entry.rs index 4c113c7b4..a5789bff2 100644 --- a/src/storage/entry.rs +++ b/src/storage/entry.rs @@ -1,6 +1,6 @@ use hibitset::BitSetAll; -use super::*; +use super::*; // TODO: remove glob use crate::join::Join; impl<'e, T, D> Storage<'e, T, D> @@ -194,7 +194,12 @@ where pub fn get_mut(&mut self) -> AccessMutReturn<'_, T> { // SAFETY: This is safe since `OccupiedEntry` is only constructed // after checking the mask. - unsafe { self.storage.data.inner.get_mut(self.id) } + unsafe { + self.storage + .data + .inner + .get_mut(HasIndex::from_index(self.id, &self.storage.entities)) + } } /// Converts the `OccupiedEntry` into a mutable reference bounded by @@ -202,7 +207,12 @@ where pub fn into_mut(self) -> AccessMutReturn<'a, T> { // SAFETY: This is safe since `OccupiedEntry` is only constructed // after checking the mask. - unsafe { self.storage.data.inner.get_mut(self.id) } + unsafe { + self.storage + .data + .inner + .get_mut(HasIndex::from_index(self.id, &self.storage.entities)) + } } /// Inserts a value into the storage and returns the old one. @@ -213,7 +223,10 @@ where /// Removes the component from the storage and returns it. pub fn remove(self) -> T { - self.storage.data.remove(self.id).unwrap() + self.storage + .data + .remove(HasIndex::from_index(self.id, &self.storage.entities)) + .unwrap() } } @@ -232,10 +245,11 @@ where /// Inserts a value into the storage. pub fn insert(self, component: T) -> AccessMutReturn<'a, T> { self.storage.data.mask.add(self.id); + let has_index = HasIndex::from_index(self.id, &self.storage.entities); // SAFETY: This is safe since we added `self.id` to the mask. unsafe { - self.storage.data.inner.insert(self.id, component); - self.storage.data.inner.get_mut(self.id) + self.storage.data.inner.insert(has_index, component); + self.storage.data.inner.get_mut(has_index) } } } diff --git a/src/storage/flagged.rs b/src/storage/flagged.rs index 3d2fe53aa..fe13d8b16 100644 --- a/src/storage/flagged.rs +++ b/src/storage/flagged.rs @@ -7,6 +7,9 @@ use crate::{ world::{Component, Index}, }; +#[cfg(feature = "nightly")] +use crate::world::HasIndex; + use shrev::EventChannel; /// Wrapper storage that tracks modifications, insertions, and removals of @@ -201,7 +204,13 @@ where impl> UnprotectedStorage for FlaggedStorage { #[cfg(feature = "nightly")] - type AccessMut<'a> where T: 'a = >::AccessMut<'a>; + type AccessMut<'a> + where + T: 'a, + = >::AccessMut<'a>; + + #[cfg(feature = "nightly")] + type MutIndex = >::MutIndex; unsafe fn clean(&mut self, has: B) where @@ -215,9 +224,12 @@ impl> UnprotectedStorage for FlaggedSt } #[cfg(feature = "nightly")] - unsafe fn get_mut(&mut self, id: Index) -> >::AccessMut<'_> { + unsafe fn get_mut( + &mut self, + id: Self::MutIndex, + ) -> >::AccessMut<'_> { if self.emit_event() { - self.channel.single_write(ComponentEvent::Modified(id)); + self.channel.single_write(ComponentEvent::Modified(id.id())); } self.storage.get_mut(id) } @@ -230,6 +242,15 @@ impl> UnprotectedStorage for FlaggedSt self.storage.get_mut(id) } + #[cfg(feature = "nightly")] + unsafe fn insert(&mut self, id: Self::MutIndex, comp: C) { + if self.emit_event() { + self.channel.single_write(ComponentEvent::Inserted(id.id())); + } + self.storage.insert(id, comp); + } + + #[cfg(not(feature = "nightly"))] unsafe fn insert(&mut self, id: Index, comp: C) { if self.emit_event() { self.channel.single_write(ComponentEvent::Inserted(id)); @@ -237,6 +258,15 @@ impl> UnprotectedStorage for FlaggedSt self.storage.insert(id, comp); } + #[cfg(feature = "nightly")] + unsafe fn remove(&mut self, id: Self::MutIndex) -> C { + if self.emit_event() { + self.channel.single_write(ComponentEvent::Removed(id.id())); + } + self.storage.remove(id) + } + + #[cfg(not(feature = "nightly"))] unsafe fn remove(&mut self, id: Index) -> C { if self.emit_event() { self.channel.single_write(ComponentEvent::Removed(id)); diff --git a/src/storage/mod.rs b/src/storage/mod.rs index fb5ebe637..862b03684 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -6,8 +6,8 @@ pub use self::{ flagged::FlaggedStorage, generic::{GenericReadStorage, GenericWriteStorage}, restrict::{ - ImmutableParallelRestriction, MutableParallelRestriction, RestrictedStorage, - SequentialRestriction, PairedStorage + ImmutableParallelRestriction, MutableParallelRestriction, PairedStorage, RestrictedStorage, + SequentialRestriction, }, storages::{ BTreeStorage, DefaultVecStorage, DenseVecStorage, HashMapStorage, NullStorage, VecStorage, @@ -15,7 +15,7 @@ pub use self::{ track::{ComponentEvent, Tracked}, }; #[cfg(feature = "nightly")] -pub use self::deref_flagged::DerefFlaggedStorage; +pub use self::{deref_flagged::DerefFlaggedStorage, deref_flagged_gen::DerefFlaggedGenStorage}; use self::storages::SliceAccess; @@ -33,17 +33,19 @@ use crate::join::ParJoin; use crate::{ error::{Error, WrongGeneration}, join::Join, - world::{Component, EntitiesRes, Entity, Generation, Index}, + world::{Component, EntitiesRes, Entity, Generation, HasIndex, Index}, }; use self::drain::Drain; mod data; +#[cfg(feature = "nightly")] +mod deref_flagged; +#[cfg(feature = "nightly")] +mod deref_flagged_gen; mod drain; mod entry; mod flagged; -#[cfg(feature = "nightly")] -mod deref_flagged; mod generic; mod restrict; mod storages; @@ -106,7 +108,7 @@ where { fn drop(&mut self, entities: &[Entity]) { for entity in entities { - MaskedStorage::drop(self, entity.id()); + MaskedStorage::drop(self, HasIndex::from_entity(*entity)); } } } @@ -179,7 +181,41 @@ impl MaskedStorage { } self.mask.clear(); } +} + +#[cfg(feature = "nightly")] +impl MaskedStorage +where + T: Component, + T::Storage: UnprotectedStorage, + I: HasIndex, +{ + /// Remove an element by a given index. + pub fn remove(&mut self, id: I) -> Option { + if self.mask.remove(id.id()) { + // SAFETY: We checked the mask (`remove` returned `true`) + Some(unsafe { self.inner.remove(id) }) + } else { + None + } + } + + /// Drop an element by a given index. + pub fn drop(&mut self, id: I) { + if self.mask.remove(id.id()) { + // SAFETY: We checked the mask (`eemove` returned `true`) + unsafe { + self.inner.drop(id); + } + } + } +} +#[cfg(not(feature = "nightly"))] +impl MaskedStorage +where + T: Component, +{ /// Remove an element by a given index. pub fn remove(&mut self, id: Index) -> Option { if self.mask.remove(id) { @@ -193,7 +229,7 @@ impl MaskedStorage { /// Drop an element by a given index. pub fn drop(&mut self, id: Index) { if self.mask.remove(id) { - // SAFETY: We checked the mask (`remove` returned `true`) + // SAFETY: We checked the mask (`eemove` returned `true`) unsafe { self.inner.drop(id); } @@ -201,12 +237,6 @@ impl MaskedStorage { } } -impl Drop for MaskedStorage { - fn drop(&mut self) { - self.clear(); - } -} - /// A wrapper around the masked storage and the generations vector. /// Can be used for safe lookup of components, insertions and removes. /// This is what `World::read/write` fetches for the user. @@ -328,10 +358,10 @@ where } /// Tries to mutate the data associated with an `Entity`. - pub fn get_mut(&mut self, e: Entity) -> Option > { + pub fn get_mut(&mut self, e: Entity) -> Option> { if self.data.mask.contains(e.id()) && self.entities.is_alive(e) { // SAFETY: We checked the mask, so all invariants are met. - Some(unsafe { self.data.inner.get_mut(e.id()) }) + Some(unsafe { self.data.inner.get_mut(HasIndex::from_entity(e)) }) } else { None } @@ -348,12 +378,17 @@ where let id = e.id(); if self.data.mask.contains(id) { // SAFETY: We checked the mask, so all invariants are met. - std::mem::swap(&mut v, unsafe { self.data.inner.get_mut(id).deref_mut() }); + std::mem::swap(&mut v, unsafe { + self.data + .inner + .get_mut(HasIndex::from_entity(e)) + .deref_mut() + }); Ok(Some(v)) } else { self.data.mask.add(id); // SAFETY: The mask was previously empty, so it is safe to insert. - unsafe { self.data.inner.insert(id, v) }; + unsafe { self.data.inner.insert(HasIndex::from_entity(e), v) }; Ok(None) } } else { @@ -368,7 +403,7 @@ where /// Removes the data associated with an `Entity`. pub fn remove(&mut self, e: Entity) -> Option { if self.entities.is_alive(e) { - self.data.remove(e.id()) + self.data.remove(HasIndex::from_entity(e)) } else { None } @@ -384,6 +419,8 @@ where pub fn drain(&mut self) -> Drain { Drain { data: &mut self.data, + #[cfg(feature = "nightly")] + entities: &self.entities, } } } @@ -452,14 +489,35 @@ where { type Mask = &'a BitSet; type Type = AccessMutReturn<'a, T>; + // TODO: use HasIndex trait to optionally include EntitiesRes here + #[cfg(feature = "nightly")] + type Value = (&'a mut T::Storage, &'a EntitiesRes); + #[cfg(not(feature = "nightly"))] type Value = &'a mut T::Storage; // SAFETY: No unsafe code and no invariants to fulfill. unsafe fn open(self) -> (Self::Mask, Self::Value) { + #[cfg(feature = "nightly")] + { + let (mask, storage) = self.data.open_mut(); + (mask, (storage, &self.entities)) + } + #[cfg(not(feature = "nightly"))] self.data.open_mut() } // TODO: audit unsafe + #[cfg(feature = "nightly")] + unsafe fn get((storage, entities): &mut Self::Value, i: Index) -> Self::Type { + // This is horribly unsafe. Unfortunately, Rust doesn't provide a way + // to abstract mutable/immutable state at the moment, so we have to hack + // our way through it. + let storage: *mut T::Storage = *storage as *mut T::Storage; + (*storage).get_mut(HasIndex::from_index(i, entities)) + } + + // TODO: audit unsafe + #[cfg(not(feature = "nightly"))] unsafe fn get(v: &mut Self::Value, i: Index) -> Self::Type { // This is horribly unsafe. Unfortunately, Rust doesn't provide a way // to abstract mutable/immutable state at the moment, so we have to hack @@ -507,7 +565,15 @@ where pub trait UnprotectedStorage: TryDefault { /// The wrapper through with mutable access of a component is performed. #[cfg(feature = "nightly")] - type AccessMut<'a>: DerefMut where Self: 'a; + type AccessMut<'a>: DerefMut + where + Self: 'a; + + /// The index type used for mutable access of a component. + /// Useful to allow flagged storages to require the entity generation be supplied along with + /// the ID + #[cfg(feature = "nightly")] + type MutIndex: HasIndex = Index; /// Clean the storage given a bitset with bits set for valid indices. /// Allows us to safely drop the storage. @@ -545,7 +611,7 @@ pub trait UnprotectedStorage: TryDefault { /// A mask should keep track of those states, and an `id` being contained /// in the tracking mask is sufficient to call this method. #[cfg(feature = "nightly")] - unsafe fn get_mut(&mut self, id: Index) -> Self::AccessMut<'_>; + unsafe fn get_mut(&mut self, id: Self::MutIndex) -> Self::AccessMut<'_>; /// Tries mutating the data associated with an `Index`. /// This is unsafe because the external set used @@ -570,6 +636,19 @@ pub trait UnprotectedStorage: TryDefault { /// /// A mask should keep track of those states, and an `id` missing from the /// mask is sufficient to call `insert`. + #[cfg(feature = "nightly")] + unsafe fn insert(&mut self, id: Self::MutIndex, value: T); + + /// Inserts new data for a given `Index`. + /// + /// # Safety + /// + /// May only be called if `insert` was not called with `id` before, or + /// was reverted by a call to `remove` with `id. + /// + /// A mask should keep track of those states, and an `id` missing from the + /// mask is sufficient to call `insert`. + #[cfg(not(feature = "nightly"))] unsafe fn insert(&mut self, id: Index, value: T); /// Removes the data associated with an `Index`. @@ -578,6 +657,17 @@ pub trait UnprotectedStorage: TryDefault { /// /// May only be called if an element with `id` was `insert`ed and not yet /// removed / dropped. + /// mask is sufficient to call `insert`. + #[cfg(feature = "nightly")] + unsafe fn remove(&mut self, id: Self::MutIndex) -> T; + + /// Removes the data associated with an `Index`. + /// + /// # Safety + /// + /// May only be called if an element with `id` was `insert`ed and not yet + /// removed / dropped. + #[cfg(not(feature = "nightly"))] unsafe fn remove(&mut self, id: Index) -> T; /// Drops the data associated with an `Index`. @@ -589,6 +679,21 @@ pub trait UnprotectedStorage: TryDefault { /// /// May only be called if an element with `id` was `insert`ed and not yet /// removed / dropped. + #[cfg(feature = "nightly")] + unsafe fn drop(&mut self, id: Self::MutIndex) { + self.remove(id); + } + + /// Drops the data associated with an `Index`. + /// This could be used when a more efficient implementation for it exists than `remove` when the data + /// is no longer needed. + /// Defaults to simply calling `remove`. + /// + /// # Safety + /// + /// May only be called if an element with `id` was `insert`ed and not yet + /// removed / dropped. + #[cfg(not(feature = "nightly"))] unsafe fn drop(&mut self, id: Index) { self.remove(id); } diff --git a/src/storage/restrict.rs b/src/storage/restrict.rs index 99e4ccca5..e848c3b1b 100644 --- a/src/storage/restrict.rs +++ b/src/storage/restrict.rs @@ -12,8 +12,8 @@ use crate::join::Join; #[cfg(feature = "parallel")] use crate::join::ParJoin; use crate::{ - storage::{MaskedStorage, Storage, UnprotectedStorage, AccessMutReturn}, - world::{Component, EntitiesRes, Entity, Index}, + storage::{AccessMutReturn, MaskedStorage, Storage, UnprotectedStorage}, + world::{Component, EntitiesRes, Entity, HasIndex, Index}, }; /// Specifies that the `RestrictedStorage` cannot run in parallel. @@ -248,8 +248,12 @@ where { /// Gets the component related to the current entry without checking whether /// the storage has it or not. - pub fn get_mut_unchecked(&mut self) -> AccessMutReturn<'_, C> { - unsafe { self.storage.borrow_mut().get_mut(self.index) } + pub fn get_mut_unchecked(&mut self) -> AccessMutReturn<'_, C> { + unsafe { + self.storage + .borrow_mut() + .get_mut(HasIndex::from_index(self.index, &self.entities)) + } } } @@ -291,7 +295,11 @@ where /// threads. pub fn get_mut(&mut self, entity: Entity) -> Option> { if self.bitset.borrow().contains(entity.id()) && self.entities.is_alive(entity) { - Some(unsafe { self.storage.borrow_mut().get_mut(entity.id()) }) + Some(unsafe { + self.storage + .borrow_mut() + .get_mut(HasIndex::from_entity(entity)) + }) } else { None } diff --git a/src/storage/track.rs b/src/storage/track.rs index 98c7e0185..3622203e4 100644 --- a/src/storage/track.rs +++ b/src/storage/track.rs @@ -11,9 +11,26 @@ use crate::{ /// `UnprotectedStorage`s that track modifications, insertions, and /// removals of components. pub trait Tracked { + /// The type used to refer to the entity, is typically either `Index` which doesn't include the + /// generation or `Entity` which does. Using `Entity` allows determining whether the emitted + /// events are associated with an entity that is still alive. + #[cfg(feature = "nightly")] + type Entity: shrev::Event = Index; + + /// Event channel tracking modified/inserted/removed components. + #[cfg(feature = "nightly")] + fn channel(&self) -> &EventChannel>; + /// Event channel tracking modified/inserted/removed components. + #[cfg(not(feature = "nightly"))] fn channel(&self) -> &EventChannel; + + /// Mutable event channel tracking modified/inserted/removed components. + #[cfg(feature = "nightly")] + fn channel_mut(&mut self) -> &mut EventChannel>; + /// Mutable event channel tracking modified/inserted/removed components. + #[cfg(not(feature = "nightly"))] fn channel_mut(&mut self) -> &mut EventChannel; /// Controls the events signal emission. @@ -30,16 +47,16 @@ pub trait Tracked { #[derive(Clone, Copy, Debug, Eq, PartialEq)] /// Component storage events received from a `FlaggedStorage` or any storage /// that implements `Tracked`. -pub enum ComponentEvent { +pub enum ComponentEvent { /// An insertion event, note that a modification event will be triggered if /// the entity already had a component and had a new one inserted. - Inserted(Index), + Inserted(E), /// A modification event, this will be sent any time a component is accessed /// mutably so be careful with joins over `&mut storages` as it could /// potentially flag all of them. - Modified(Index), + Modified(E), /// A removal event. - Removed(Index), + Removed(E), } impl<'e, T, D> Storage<'e, T, D> @@ -49,6 +66,15 @@ where D: Deref>, { /// Returns the event channel tracking modified components. + #[cfg(feature = "nightly")] + pub fn channel( + &self, + ) -> &EventChannel::Storage as Tracked>::Entity>> { + unsafe { self.open() }.1.channel() + } + + /// Returns the event channel tracking modified components. + #[cfg(not(feature = "nightly"))] pub fn channel(&self) -> &EventChannel { unsafe { self.open() }.1.channel() } @@ -68,6 +94,16 @@ where { /// Returns the event channel for insertions/removals/modifications of this /// storage's components. + #[cfg(feature = "nightly")] + pub fn channel_mut( + &mut self, + ) -> &mut EventChannel::Storage as Tracked>::Entity>> { + unsafe { self.open() }.1 .0.channel_mut() + } + + /// Returns the event channel for insertions/removals/modifications of this + /// storage's components. + #[cfg(not(feature = "nightly"))] pub fn channel_mut(&mut self) -> &mut EventChannel { unsafe { self.open() }.1.channel_mut() } @@ -75,11 +111,29 @@ where /// Starts tracking component events. Note that this reader id should be /// used every frame, otherwise events will pile up and memory use by /// the event channel will grow waiting for this reader. + #[cfg(feature = "nightly")] + pub fn register_reader( + &mut self, + ) -> ReaderId::Storage as Tracked>::Entity>> { + self.channel_mut().register_reader() + } + + /// Starts tracking component events. Note that this reader id should be + /// used every frame, otherwise events will pile up and memory use by + /// the event channel will grow waiting for this reader. + #[cfg(not(feature = "nightly"))] pub fn register_reader(&mut self) -> ReaderId { self.channel_mut().register_reader() } /// Flags an index with a `ComponentEvent`. + #[cfg(feature = "nightly")] + pub fn flag(&mut self, event: ComponentEvent<<::Storage as Tracked>::Entity>) { + self.channel_mut().single_write(event); + } + + /// Flags an index with a `ComponentEvent`. + #[cfg(not(feature = "nightly"))] pub fn flag(&mut self, event: ComponentEvent) { self.channel_mut().single_write(event); } diff --git a/src/world/entity.rs b/src/world/entity.rs index 2c18cc09f..f9670ff86 100644 --- a/src/world/entity.rs +++ b/src/world/entity.rs @@ -242,6 +242,54 @@ impl Entity { } } +/// Trait to interoperate between Entity and Index +pub trait HasIndex: Copy { + /// Extract the Index + fn id(self) -> Index; + + /// Convert from an Entity + /// Does nothing if `Self` is `Entity` + fn from_entity(entity: Entity) -> Self; + + /// Convert from an `Index` + /// Requires a lookup if `Self` is `Entity` + fn from_index(id: Index, entities: &EntitiesRes) -> Self; +} + +impl HasIndex for Index { + #[inline] + fn id(self) -> Index { + self + } + + #[inline] + fn from_entity(entity: Entity) -> Self { + entity.id() + } + + #[inline] + fn from_index(id: Index, _entities: &EntitiesRes) -> Self { + id + } +} + +impl HasIndex for Entity { + #[inline] + fn id(self) -> Index { + self.id() + } + + #[inline] + fn from_entity(entity: Entity) -> Self { + entity + } + + #[inline] + fn from_index(id: Index, entities: &EntitiesRes) -> Self { + entities.entity(id) + } +} + /// The entities of this ECS. This is a resource, stored in the `World`. /// If you just want to access it in your system, you can also use the /// `Entities` type def. diff --git a/src/world/mod.rs b/src/world/mod.rs index aa74b50a0..e09b4b097 100644 --- a/src/world/mod.rs +++ b/src/world/mod.rs @@ -5,7 +5,8 @@ pub use shred::World; pub use self::{ comp::Component, entity::{ - CreateIterAtomic, Entities, EntitiesRes, Entity, EntityResBuilder, Generation, Index, + CreateIterAtomic, Entities, EntitiesRes, Entity, EntityResBuilder, Generation, HasIndex, + Index, }, lazy::{LazyBuilder, LazyUpdate}, world_ext::WorldExt,