Skip to main content

shipyard/views/
view_mut.rs

1use crate::all_storages::AllStorages;
2use crate::atomic_refcell::{ARef, ARefMut, ExclusiveBorrow, SharedBorrow};
3use crate::component::Component;
4use crate::entity_id::EntityId;
5use crate::error;
6use crate::get::Get;
7use crate::r#mut::Mut;
8use crate::sparse_set::{SparseSet, SparseSetDrain};
9use crate::storage::StorageId;
10use crate::track;
11use crate::tracking::{
12    DeletionTracking, Inserted, InsertedOrModified, InsertionTracking, ModificationTracking,
13    Modified, RemovalOrDeletionTracking, RemovalTracking, Tracking, TrackingTimestamp,
14};
15use crate::views::view::View;
16use core::fmt;
17use core::marker::PhantomData;
18use core::ops::{Deref, DerefMut};
19
20/// Exclusive view over a component storage.
21pub struct ViewMut<'a, T: Component, Track = <T as Component>::Tracking> {
22    pub(crate) sparse_set: &'a mut SparseSet<T>,
23    pub(crate) all_borrow: Option<SharedBorrow<'a>>,
24    pub(crate) borrow: ExclusiveBorrow<'a>,
25    pub(crate) last_insertion: TrackingTimestamp,
26    pub(crate) last_modification: TrackingTimestamp,
27    pub(crate) last_removal_or_deletion: TrackingTimestamp,
28    pub(crate) current: TrackingTimestamp,
29    pub(crate) phantom: PhantomData<Track>,
30}
31
32impl<'a, T: Component, Track> ViewMut<'a, T, Track>
33where
34    Track: Tracking,
35{
36    /// Returns a `View` reborrowing from `ViewMut`.
37    ///
38    /// There are often alternatives to calling this method, like reborrow.\
39    /// One case where this method shines is for calling a system in another system.\
40    /// `sys_b` cannot use a reference or it wouldn't work as a system anymore.
41    /// ```rust
42    /// # use shipyard::{track ,Component, View, ViewMut, World};
43    /// # let mut world = World::new();
44    /// # struct A;
45    /// # impl Component for A { type Tracking = track::Untracked; };
46    /// # struct B;
47    /// # impl Component for B { type Tracking = track::Untracked; };
48    ///
49    /// fn sys_a(vm_compA: ViewMut<A>) {
50    ///     // -- SNIP --
51    ///
52    ///     sys_b(vm_compA.as_view());
53    /// }
54    ///
55    /// fn sys_b(v_compA: View<A>) {}
56    ///
57    /// world.run(sys_a);
58    /// world.run(sys_b);
59    /// ```
60    pub fn as_view(&self) -> View<'_, T, Track> {
61        View {
62            sparse_set: self.sparse_set,
63            all_borrow: self.all_borrow.as_ref().cloned(),
64            borrow: self.borrow.shared_reborrow(),
65            last_insertion: self.last_insertion,
66            last_modification: self.last_modification,
67            last_removal_or_deletion: self.last_removal_or_deletion,
68            current: self.current,
69            phantom: PhantomData,
70        }
71    }
72
73    /// Replaces the timestamp starting the tracking time window for insertions.
74    ///
75    /// Tracking works based on a time window. From the last time the system ran (in workloads)
76    /// or since the last clear.
77    ///
78    /// Sometimes this automatic time window isn't what you need.
79    /// This can happen when you want to keep the same tracking information for multiple runs
80    /// of the same system.
81    ///
82    /// For example if you interpolate movement between frames, you might run an interpolation workload
83    /// multiple times but not change the [`World`](crate::World) during its execution.\
84    /// In this case you want the same tracking information for all runs of this workload
85    /// which would have disappeared using the automatic window.
86    pub fn override_last_insertion(
87        &mut self,
88        new_timestamp: TrackingTimestamp,
89    ) -> TrackingTimestamp {
90        core::mem::replace(&mut self.last_insertion, new_timestamp)
91    }
92
93    /// Replaces the timestamp starting the tracking time window for modifications.
94    ///
95    /// Tracking works based on a time window. From the last time the system ran (in workloads)
96    /// or since the last clear.
97    ///
98    /// Sometimes this automatic time window isn't what you need.
99    /// This can happen when you want to keep the same tracking information for multiple runs
100    /// of the same system.
101    ///
102    /// For example if you interpolate movement between frames, you might run an interpolation workload
103    /// multiple times but not change the [`World`](crate::World) during its execution.\
104    /// In this case you want the same tracking information for all runs of this workload
105    /// which would have disappeared using the automatic window.
106    pub fn override_last_modification(
107        &mut self,
108        new_timestamp: TrackingTimestamp,
109    ) -> TrackingTimestamp {
110        core::mem::replace(&mut self.last_modification, new_timestamp)
111    }
112
113    /// Replaces the timestamp starting the tracking time window for removals and deletions.
114    ///
115    /// Tracking works based on a time window. From the last time the system ran (in workloads)
116    /// or since the last clear.
117    ///
118    /// Sometimes this automatic time window isn't what you need.
119    /// This can happen when you want to keep the same tracking information for multiple runs
120    /// of the same system.
121    ///
122    /// For example if you interpolate movement between frames, you might run an interpolation workload
123    /// multiple times but not change the [`World`](crate::World) during its execution.\
124    /// In this case you want the same tracking information for all runs of this workload
125    /// which would have disappeared using the automatic window.
126    pub fn override_last_removal_or_deletion(
127        &mut self,
128        new_timestamp: TrackingTimestamp,
129    ) -> TrackingTimestamp {
130        core::mem::replace(&mut self.last_removal_or_deletion, new_timestamp)
131    }
132}
133
134impl<'a, T: Component> ViewMut<'a, T, track::Untracked> {
135    /// Creates a new [`ViewMut`] for custom [`SparseSet`] storage.
136    ///
137    /// ```
138    /// use shipyard::{track, advanced::StorageId, Component, sparse_set::SparseSet, ViewMut, World};
139    ///
140    /// struct ScriptingComponent(Vec<u8>);
141    /// impl Component for ScriptingComponent {
142    ///     type Tracking = track::Untracked;
143    /// }
144    ///
145    /// let world = World::new();
146    ///
147    /// world.add_custom_storage(
148    ///     StorageId::Custom(0),
149    ///     SparseSet::<ScriptingComponent>::new_custom_storage(),
150    /// ).unwrap();
151    ///
152    /// let all_storages = world.all_storages().unwrap();
153    /// let scripting_storage =
154    ///     ViewMut::<ScriptingComponent>::new_for_custom_storage(StorageId::Custom(0), all_storages)
155    ///         .unwrap();
156    /// ```
157    pub fn new_for_custom_storage(
158        storage_id: StorageId,
159        all_storages: ARef<'a, &'a AllStorages>,
160    ) -> Result<Self, error::CustomStorageView> {
161        use crate::all_storages::CustomStorageAccess;
162
163        let (all_storages, all_borrow) = unsafe { ARef::destructure(all_storages) };
164
165        let storage = all_storages.custom_storage_mut_by_id(storage_id)?;
166        let (storage, borrow) = unsafe { ARefMut::destructure(storage) };
167
168        let name = storage.name();
169
170        if let Some(sparse_set) = storage.any_mut().downcast_mut() {
171            Ok(ViewMut {
172                sparse_set,
173                all_borrow: Some(all_borrow),
174                borrow,
175                last_insertion: TrackingTimestamp::new(0),
176                last_modification: TrackingTimestamp::new(0),
177                last_removal_or_deletion: TrackingTimestamp::new(0),
178                current: TrackingTimestamp::new(0),
179                phantom: PhantomData,
180            })
181        } else {
182            Err(error::CustomStorageView::WrongType(name))
183        }
184    }
185}
186
187impl<'a, T: Component, Track> ViewMut<'a, T, Track>
188where
189    Track: Tracking,
190{
191    /// Deletes all components in this storage.
192    pub fn clear(&mut self) {
193        self.sparse_set.private_clear(self.current);
194    }
195    /// Creates a draining iterator that empties the storage and yields the removed items.
196    pub fn drain(&mut self) -> SparseSetDrain<'_, T> {
197        self.sparse_set.private_drain(self.current)
198    }
199    /// Applies the given function `f` to the entities `a` and `b`.\
200    /// The two entities shouldn't point to the same component.  
201    ///
202    /// ### Panics
203    ///
204    /// - MissingComponent - if one of the entity doesn't have any component in the storage.
205    /// - IdenticalIds - if the two entities point to the same component.
206    #[track_caller]
207    pub fn apply<R, F: FnOnce(&mut T, &T) -> R>(&mut self, a: EntityId, b: EntityId, f: F) -> R {
208        self.sparse_set.private_apply(a, b, f, self.current)
209    }
210    /// Applies the given function `f` to the entities `a` and `b`.\
211    /// The two entities shouldn't point to the same component.  
212    ///
213    /// ### Panics
214    ///
215    /// - MissingComponent - if one of the entity doesn't have any component in the storage.
216    /// - IdenticalIds - if the two entities point to the same component.
217    #[track_caller]
218    pub fn apply_mut<R, F: FnOnce(&mut T, &mut T) -> R>(
219        &mut self,
220        a: EntityId,
221        b: EntityId,
222        f: F,
223    ) -> R {
224        self.sparse_set.private_apply_mut(a, b, f, self.current)
225    }
226
227    /// Deletes all components for which `f(id, &component)` returns `false`.
228    pub fn retain<F: FnMut(EntityId, &T) -> bool>(&mut self, f: F) {
229        self.sparse_set.private_retain(self.current, f);
230    }
231
232    /// Deletes all components for which `f(id, Mut<component>)` returns `false`.
233    pub fn retain_mut<F: FnMut(EntityId, Mut<'_, T>) -> bool>(&mut self, f: F) {
234        self.sparse_set.private_retain_mut(self.current, f);
235    }
236}
237
238impl<'v, Track, T: Component + Default> ViewMut<'v, T, Track>
239where
240    for<'a> &'a mut ViewMut<'v, T, Track>: Get,
241{
242    /// Retrieve `entity` component.
243    ///
244    /// If the entity doesn't have the component, insert its `Default` value.
245    ///
246    /// ### Errors
247    ///
248    /// Returns `None` when `entity` is dead and a component is already present for an entity with the same index.
249    #[inline]
250    pub fn get_or_default<'a>(
251        &'a mut self,
252        entity: EntityId,
253    ) -> Option<<&'a mut ViewMut<'v, T, Track> as Get>::Out> {
254        self.get_or_insert_with(entity, T::default)
255    }
256}
257
258impl<'v, Track, T: Component> ViewMut<'v, T, Track>
259where
260    for<'a> &'a mut ViewMut<'v, T, Track>: Get,
261{
262    /// Retrieve `entity` component.
263    ///
264    /// If the entity doesn't have the component, insert `component`.
265    ///
266    /// ### Errors
267    ///
268    /// Returns `None` when `entity` is dead and a component is already present for an entity with the same index.
269    #[inline]
270    pub fn get_or_insert<'a>(
271        &'a mut self,
272        entity: EntityId,
273        component: T,
274    ) -> Option<<&'a mut ViewMut<'v, T, Track> as Get>::Out> {
275        self.get_or_insert_with(entity, || component)
276    }
277    /// Retrieve `entity` component.
278    ///
279    /// If the entity doesn't have the component, insert the result of `f`.
280    ///
281    /// ### Errors
282    ///
283    /// Returns `None` when `entity` is dead and a component is already present for an entity with the same index.
284    #[inline]
285    pub fn get_or_insert_with<'a, F: FnOnce() -> T>(
286        &'a mut self,
287        entity: EntityId,
288        f: F,
289    ) -> Option<<&'a mut ViewMut<'v, T, Track> as Get>::Out> {
290        if !self.sparse_set.contains(entity) {
291            let was_inserted = self
292                .sparse_set
293                .insert(entity, f(), self.current)
294                .was_inserted();
295
296            if !was_inserted {
297                return None;
298            }
299        }
300
301        // At this point, it is not possible for the entity to not have a component of this type.
302        let component = unsafe { Get::get(self, entity).unwrap_unchecked() };
303
304        Some(component)
305    }
306}
307
308impl<Track, T: Component> ViewMut<'_, T, Track>
309where
310    Track: InsertionTracking,
311{
312    /// Inside a workload returns `true` if `entity`'s component was inserted since the last run of this system.\
313    /// Outside workloads returns `true` if `entity`'s component was inserted since the last call to [`clear_all_inserted`](ViewMut::clear_all_inserted).\
314    /// Returns `false` if `entity` does not have a component in this storage.
315    #[inline]
316    pub fn is_inserted(&self, entity: EntityId) -> bool {
317        Track::is_inserted(self.sparse_set, entity, self.last_insertion, self.current)
318    }
319    /// Wraps this view to be able to iterate *inserted* components.
320    #[inline]
321    pub fn inserted(&self) -> Inserted<&Self> {
322        Inserted(self)
323    }
324    /// Wraps this view to be able to iterate *inserted* components.
325    #[inline]
326    pub fn inserted_mut(&mut self) -> Inserted<&mut Self> {
327        Inserted(self)
328    }
329    /// Removes the *inserted* flag on all components of this storage.
330    #[inline]
331    pub fn clear_all_inserted(self) {
332        self.sparse_set.private_clear_all_inserted(self.current);
333    }
334}
335
336impl<Track, T: Component> ViewMut<'_, T, Track>
337where
338    Track: ModificationTracking,
339{
340    /// Inside a workload returns `true` if `entity`'s component was modified since the last run of this system.\
341    /// Outside workloads returns `true` if `entity`'s component was modified since the last call to [`clear_all_modified`](ViewMut::clear_all_modified).\
342    /// Returns `false` if `entity` does not have a component in this storage.
343    #[inline]
344    pub fn is_modified(&self, entity: EntityId) -> bool {
345        Track::is_modified(
346            self.sparse_set,
347            entity,
348            self.last_modification,
349            self.current,
350        )
351    }
352    /// Wraps this view to be able to iterate *modified* components.
353    #[inline]
354    pub fn modified(&self) -> Modified<&Self> {
355        Modified(self)
356    }
357    /// Wraps this view to be able to iterate *modified* components.
358    #[inline]
359    pub fn modified_mut(&mut self) -> Modified<&mut Self> {
360        Modified(self)
361    }
362    /// Removes the *modified* flag on all components of this storage.
363    #[inline]
364    pub fn clear_all_modified(self) {
365        self.sparse_set.private_clear_all_modified(self.current);
366    }
367}
368
369impl<Track, T: Component> ViewMut<'_, T, Track>
370where
371    Track: InsertionTracking + ModificationTracking,
372{
373    /// Inside a workload returns `true` if `entity`'s component was inserted or modified since the last run of this system.\
374    /// Outside workloads returns `true` if `entity`'s component was inserted or modified since the last call to [`clear_all_inserted`](ViewMut::clear_all_inserted).\
375    /// Returns `false` if `entity` does not have a component in this storage.
376    #[inline]
377    pub fn is_inserted_or_modified(&self, entity: EntityId) -> bool {
378        self.is_inserted(entity) || self.is_modified(entity)
379    }
380    /// Wraps this view to be able to iterate *inserted* and *modified* components.
381    #[inline]
382    pub fn inserted_or_modified(&self) -> InsertedOrModified<&Self> {
383        InsertedOrModified(self)
384    }
385    /// Wraps this view to be able to iterate *inserted* and *modified* components.
386    #[inline]
387    pub fn inserted_or_modified_mut(&mut self) -> InsertedOrModified<&mut Self> {
388        InsertedOrModified(self)
389    }
390    /// Removes the *inserted* and *modified* flags on all components of this storage.
391    #[inline]
392    pub fn clear_all_inserted_and_modified(self) {
393        self.sparse_set
394            .private_clear_all_inserted_and_modified(self.current);
395    }
396}
397
398impl<Track, T: Component> ViewMut<'_, T, Track>
399where
400    Track: DeletionTracking,
401{
402    /// Inside a workload returns `true` if `entity`'s component was deleted since the last run of this system.\
403    /// Outside workloads returns `true` if `entity`'s component was deleted since the last call to [`clear_all_deleted`](SparseSet::clear_all_deleted).\
404    /// Returns `false` if `entity` does not have a component in this storage.
405    #[inline]
406    pub fn is_deleted(&self, entity: EntityId) -> bool {
407        Track::is_deleted(self, entity, self.last_removal_or_deletion, self.current)
408    }
409    /// Returns the *deleted* components of a storage tracking deletion.
410    pub fn deleted(&self) -> impl Iterator<Item = (EntityId, &T)> + '_ {
411        self.sparse_set
412            .deletion_data
413            .iter()
414            .filter_map(move |(entity, timestamp, component)| {
415                if timestamp.is_within(self.last_removal_or_deletion, self.current) {
416                    Some((*entity, component))
417                } else {
418                    None
419                }
420            })
421    }
422}
423
424impl<Track, T: Component> ViewMut<'_, T, Track>
425where
426    Track: RemovalTracking,
427{
428    /// Inside a workload returns `true` if `entity`'s component was removed since the last run of this system.\
429    /// Outside workloads returns `true` if `entity`'s component was removed since the last call to [`clear_all_removed`](SparseSet::clear_all_removed).\
430    /// Returns `false` if `entity` does not have a component in this storage.
431    #[inline]
432    pub fn is_removed(&self, entity: EntityId) -> bool {
433        Track::is_removed(self, entity, self.last_removal_or_deletion, self.current)
434    }
435    /// Returns the ids of *removed* components of a storage tracking removal.
436    pub fn removed(&self) -> impl Iterator<Item = EntityId> + '_ {
437        self.sparse_set
438            .removal_data
439            .iter()
440            .filter_map(move |(entity, timestamp)| {
441                if timestamp.is_within(self.last_removal_or_deletion, self.current) {
442                    Some(*entity)
443                } else {
444                    None
445                }
446            })
447    }
448}
449
450impl<Track, T: Component> ViewMut<'_, T, Track>
451where
452    Track: RemovalOrDeletionTracking,
453{
454    /// Inside a workload returns `true` if `entity`'s component was deleted or removed since the last run of this system.\
455    /// Outside workloads returns `true` if `entity`'s component was deleted or removed since the last clear call.\
456    /// Returns `false` if `entity` does not have a component in this storage.
457    #[inline]
458    pub fn is_removed_or_deleted(&self, entity: EntityId) -> bool {
459        Track::is_removed(self, entity, self.last_removal_or_deletion, self.current)
460            || Track::is_deleted(self, entity, self.last_removal_or_deletion, self.current)
461    }
462    /// Returns the ids of *removed* or *deleted* components of a storage tracking removal and/or deletion.
463    pub fn removed_or_deleted(&self) -> impl Iterator<Item = EntityId> + '_ {
464        Track::removed_or_deleted(self.sparse_set).filter_map(move |(entity, timestamp)| {
465            if timestamp.is_within(self.last_removal_or_deletion, self.current) {
466                Some(entity)
467            } else {
468                None
469            }
470        })
471    }
472}
473
474impl<T: Component, Track> Deref for ViewMut<'_, T, Track> {
475    type Target = SparseSet<T>;
476
477    #[inline]
478    fn deref(&self) -> &Self::Target {
479        self.sparse_set
480    }
481}
482
483impl<T: Component, Track> DerefMut for ViewMut<'_, T, Track> {
484    #[inline]
485    fn deref_mut(&mut self) -> &mut Self::Target {
486        self.sparse_set
487    }
488}
489
490impl<'a, T: Component, Track> AsRef<SparseSet<T>> for ViewMut<'a, T, Track> {
491    #[inline]
492    fn as_ref(&self) -> &SparseSet<T> {
493        self.sparse_set
494    }
495}
496
497impl<'a, T: Component, Track> AsMut<SparseSet<T>> for ViewMut<'a, T, Track> {
498    #[inline]
499    fn as_mut(&mut self) -> &mut SparseSet<T> {
500        self.sparse_set
501    }
502}
503
504impl<'a, T: Component, Track> AsMut<Self> for ViewMut<'a, T, Track> {
505    #[inline]
506    fn as_mut(&mut self) -> &mut Self {
507        self
508    }
509}
510
511impl<T: fmt::Debug + Component, Track> fmt::Debug for ViewMut<'_, T, Track> {
512    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
513        self.sparse_set.fmt(f)
514    }
515}
516
517impl<'a, T: Component, Track> core::ops::Index<EntityId> for ViewMut<'a, T, Track> {
518    type Output = T;
519    #[inline]
520    fn index(&self, entity: EntityId) -> &Self::Output {
521        self.get(entity).unwrap()
522    }
523}
524
525impl<'a, T: Component, Track> core::ops::IndexMut<EntityId> for ViewMut<'a, T, Track> {
526    #[inline]
527    fn index_mut(&mut self, entity: EntityId) -> &mut Self::Output {
528        let index = self
529            .index_of(entity)
530            .ok_or_else(|| error::MissingComponent {
531                id: entity,
532                name: core::any::type_name::<T>(),
533            })
534            .unwrap();
535
536        let SparseSet {
537            data,
538            modification_data,
539            is_tracking_modification,
540            ..
541        } = self.sparse_set;
542
543        if *is_tracking_modification {
544            unsafe {
545                *modification_data.get_unchecked_mut(index) = self.current;
546            };
547        }
548
549        unsafe { data.get_unchecked_mut(index) }
550    }
551}