diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 9dbe253e04c42..bf8717dbe209f 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -1420,8 +1420,8 @@ mod tests { let mut expected = FilteredAccess::::default(); let a_id = world.components.get_id(TypeId::of::()).unwrap(); let b_id = world.components.get_id(TypeId::of::()).unwrap(); - expected.add_write(a_id); - expected.add_read(b_id); + expected.add_component_write(a_id); + expected.add_component_read(b_id); assert!( query.component_access.eq(&expected), "ComponentId access from query fetch and query filter should be combined" diff --git a/crates/bevy_ecs/src/query/access.rs b/crates/bevy_ecs/src/query/access.rs index 54a70690a126f..07fb201a5b979 100644 --- a/crates/bevy_ecs/src/query/access.rs +++ b/crates/bevy_ecs/src/query/access.rs @@ -49,17 +49,27 @@ impl<'a, T: SparseSetIndex + fmt::Debug> fmt::Debug for FormattedBitSet<'a, T> { /// See the [`is_compatible`](Access::is_compatible) and [`get_conflicts`](Access::get_conflicts) functions. #[derive(Eq, PartialEq)] pub struct Access { - /// All accessed elements. - reads_and_writes: FixedBitSet, - /// The exclusively-accessed elements. - writes: FixedBitSet, - /// Is `true` if this has access to all elements in the collection. + /// All accessed components. + component_read_and_writes: FixedBitSet, + /// The exclusively-accessed components. + component_writes: FixedBitSet, + /// All accessed resources. + resource_read_and_writes: FixedBitSet, + /// The exclusively-accessed resources. + resource_writes: FixedBitSet, + /// Is `true` if this has access to all components. + /// (Note that this does not include `Resources`) + reads_all_components: bool, + /// Is `true` if this has mutable access to all components. + /// (Note that this does not include `Resources`) + writes_all_components: bool, + /// Is `true` if this has access to all resources. /// This field is a performance optimization for `&World` (also harder to mess up for soundness). - reads_all: bool, - /// Is `true` if this has mutable access to all elements in the collection. + reads_all_resources: bool, + /// Is `true` if this has mutable access to all resources. /// If this is true, then `reads_all` must also be true. - writes_all: bool, - // Elements that are not accessed, but whose presence in an archetype affect query results. + writes_all_resources: bool, + // Components that are not accessed, but whose presence in an archetype affect query results. archetypal: FixedBitSet, marker: PhantomData, } @@ -68,20 +78,30 @@ pub struct Access { impl Clone for Access { fn clone(&self) -> Self { Self { - reads_and_writes: self.reads_and_writes.clone(), - writes: self.writes.clone(), - reads_all: self.reads_all, - writes_all: self.writes_all, + component_read_and_writes: self.component_read_and_writes.clone(), + component_writes: self.component_writes.clone(), + resource_read_and_writes: self.resource_read_and_writes.clone(), + resource_writes: self.resource_writes.clone(), + reads_all_components: self.reads_all_components, + writes_all_components: self.writes_all_components, + reads_all_resources: self.reads_all_resources, + writes_all_resources: self.writes_all_resources, archetypal: self.archetypal.clone(), marker: PhantomData, } } fn clone_from(&mut self, source: &Self) { - self.reads_and_writes.clone_from(&source.reads_and_writes); - self.writes.clone_from(&source.writes); - self.reads_all = source.reads_all; - self.writes_all = source.writes_all; + self.component_read_and_writes + .clone_from(&source.component_read_and_writes); + self.component_writes.clone_from(&source.component_writes); + self.resource_read_and_writes + .clone_from(&source.resource_read_and_writes); + self.resource_writes.clone_from(&source.resource_writes); + self.reads_all_components = source.reads_all_components; + self.writes_all_components = source.writes_all_components; + self.reads_all_resources = source.reads_all_resources; + self.writes_all_resources = source.writes_all_resources; self.archetypal.clone_from(&source.archetypal); } } @@ -90,12 +110,25 @@ impl fmt::Debug for Access { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Access") .field( - "read_and_writes", - &FormattedBitSet::::new(&self.reads_and_writes), + "component_read_and_writes", + &FormattedBitSet::::new(&self.component_read_and_writes), ) - .field("writes", &FormattedBitSet::::new(&self.writes)) - .field("reads_all", &self.reads_all) - .field("writes_all", &self.writes_all) + .field( + "component_writes", + &FormattedBitSet::::new(&self.component_writes), + ) + .field( + "resource_read_and_writes", + &FormattedBitSet::::new(&self.resource_read_and_writes), + ) + .field( + "resource_writes", + &FormattedBitSet::::new(&self.resource_writes), + ) + .field("reads_all_components", &self.reads_all_components) + .field("writes_all_components", &self.writes_all_components) + .field("reads_all_resources", &self.reads_all_resources) + .field("writes_all_resources", &self.writes_all_resources) .field("archetypal", &FormattedBitSet::::new(&self.archetypal)) .finish() } @@ -111,31 +144,50 @@ impl Access { /// Creates an empty [`Access`] collection. pub const fn new() -> Self { Self { - reads_all: false, - writes_all: false, - reads_and_writes: FixedBitSet::new(), - writes: FixedBitSet::new(), + reads_all_resources: false, + writes_all_resources: false, + reads_all_components: false, + writes_all_components: false, + component_read_and_writes: FixedBitSet::new(), + component_writes: FixedBitSet::new(), + resource_read_and_writes: FixedBitSet::new(), + resource_writes: FixedBitSet::new(), archetypal: FixedBitSet::new(), marker: PhantomData, } } - /// Adds access to the element given by `index`. - pub fn add_read(&mut self, index: T) { - self.reads_and_writes + /// Adds access to the component given by `index`. + pub fn add_component_read(&mut self, index: T) { + self.component_read_and_writes + .grow_and_insert(index.sparse_set_index()); + } + + /// Adds exclusive access to the component given by `index`. + pub fn add_component_write(&mut self, index: T) { + self.component_read_and_writes + .grow_and_insert(index.sparse_set_index()); + self.component_writes .grow_and_insert(index.sparse_set_index()); } - /// Adds exclusive access to the element given by `index`. - pub fn add_write(&mut self, index: T) { - self.reads_and_writes + /// Adds access to the resource given by `index`. + pub fn add_resource_read(&mut self, index: T) { + self.resource_read_and_writes + .grow_and_insert(index.sparse_set_index()); + } + + /// Adds exclusive access to the resource given by `index`. + pub fn add_resource_write(&mut self, index: T) { + self.resource_read_and_writes + .grow_and_insert(index.sparse_set_index()); + self.resource_writes .grow_and_insert(index.sparse_set_index()); - self.writes.grow_and_insert(index.sparse_set_index()); } - /// Adds an archetypal (indirect) access to the element given by `index`. + /// Adds an archetypal (indirect) access to the component given by `index`. /// - /// This is for elements whose values are not accessed (and thus will never cause conflicts), + /// This is for components whose values are not accessed (and thus will never cause conflicts), /// but whose presence in an archetype may affect query results. /// /// Currently, this is only used for [`Has`]. @@ -145,29 +197,55 @@ impl Access { self.archetypal.grow_and_insert(index.sparse_set_index()); } - /// Returns `true` if this can access the element given by `index`. - pub fn has_read(&self, index: T) -> bool { - self.reads_all || self.reads_and_writes.contains(index.sparse_set_index()) + /// Returns `true` if this can access the component given by `index`. + pub fn has_component_read(&self, index: T) -> bool { + self.reads_all_components + || self + .component_read_and_writes + .contains(index.sparse_set_index()) } - /// Returns `true` if this can access anything. - pub fn has_any_read(&self) -> bool { - self.reads_all || !self.reads_and_writes.is_clear() + /// Returns `true` if this can access any component. + pub fn has_any_component_read(&self) -> bool { + self.reads_all_components || !self.component_read_and_writes.is_clear() } - /// Returns `true` if this can exclusively access the element given by `index`. - pub fn has_write(&self, index: T) -> bool { - self.writes_all || self.writes.contains(index.sparse_set_index()) + /// Returns `true` if this can exclusively access the component given by `index`. + pub fn has_component_write(&self, index: T) -> bool { + self.writes_all_components || self.component_writes.contains(index.sparse_set_index()) } - /// Returns `true` if this accesses anything mutably. - pub fn has_any_write(&self) -> bool { - self.writes_all || !self.writes.is_clear() + /// Returns `true` if this accesses any component mutably. + pub fn has_any_component_write(&self) -> bool { + self.writes_all_components || !self.component_writes.is_clear() } - /// Returns true if this has an archetypal (indirect) access to the element given by `index`. + /// Returns `true` if this can access the resource given by `index`. + pub fn has_resource_read(&self, index: T) -> bool { + self.reads_all_resources + || self + .resource_read_and_writes + .contains(index.sparse_set_index()) + } + + /// Returns `true` if this can access any resource. + pub fn has_any_resource_read(&self) -> bool { + self.reads_all_resources || !self.resource_read_and_writes.is_clear() + } + + /// Returns `true` if this can exclusively access the resource given by `index`. + pub fn has_resource_write(&self, index: T) -> bool { + self.writes_all_resources || self.resource_writes.contains(index.sparse_set_index()) + } + + /// Returns `true` if this accesses any resource mutably. + pub fn has_any_resource_write(&self) -> bool { + self.writes_all_resources || !self.resource_writes.is_clear() + } + + /// Returns true if this has an archetypal (indirect) access to the component given by `index`. /// - /// This is an element whose value is not accessed (and thus will never cause conflicts), + /// This is a component whose value is not accessed (and thus will never cause conflicts), /// but whose presence in an archetype may affect query results. /// /// Currently, this is only used for [`Has`]. @@ -177,47 +255,170 @@ impl Access { self.archetypal.contains(index.sparse_set_index()) } + /// Sets this as having access to all components (i.e. `EntityRef`). + #[inline] + pub fn read_all_components(&mut self) { + self.reads_all_components = true; + } + + /// Sets this as having mutable access to all components (i.e. `EntityMut`). + #[inline] + pub fn write_all_components(&mut self) { + self.reads_all_components = true; + self.writes_all_components = true; + } + + /// Sets this as having access to all resources (i.e. `&World`). + #[inline] + pub fn read_all_resources(&mut self) { + self.reads_all_resources = true; + } + + /// Sets this as having mutable access to all resources (i.e. `&mut World`). + #[inline] + pub fn write_all_resources(&mut self) { + self.reads_all_resources = true; + self.writes_all_resources = true; + } + /// Sets this as having access to all indexed elements (i.e. `&World`). + #[inline] pub fn read_all(&mut self) { - self.reads_all = true; + self.read_all_components(); + self.read_all_resources(); } - /// Sets this as having mutable access to all indexed elements (i.e. `EntityMut`). + /// Sets this as having mutable access to all indexed elements (i.e. `&mut World`). + #[inline] pub fn write_all(&mut self) { - self.reads_all = true; - self.writes_all = true; + self.write_all_components(); + self.write_all_resources(); + } + + /// Returns `true` if this has access to all components (i.e. `EntityRef`). + #[inline] + pub fn has_read_all_components(&self) -> bool { + self.reads_all_components + } + + /// Returns `true` if this has write access to all components (i.e. `EntityMut`). + #[inline] + pub fn has_write_all_components(&self) -> bool { + self.writes_all_components + } + + /// Returns `true` if this has access to all resources (i.e. `EntityRef`). + #[inline] + pub fn has_read_all_resources(&self) -> bool { + self.reads_all_resources + } + + /// Returns `true` if this has write access to all resources (i.e. `EntityMut`). + #[inline] + pub fn has_write_all_resources(&self) -> bool { + self.writes_all_resources } /// Returns `true` if this has access to all indexed elements (i.e. `&World`). pub fn has_read_all(&self) -> bool { - self.reads_all + self.has_read_all_components() && self.has_read_all_resources() } - /// Returns `true` if this has write access to all indexed elements (i.e. `EntityMut`). + /// Returns `true` if this has write access to all indexed elements (i.e. `&mut World`). pub fn has_write_all(&self) -> bool { - self.writes_all + self.has_write_all_components() && self.has_write_all_resources() } /// Removes all writes. pub fn clear_writes(&mut self) { - self.writes_all = false; - self.writes.clear(); + self.writes_all_resources = false; + self.writes_all_components = false; + self.component_writes.clear(); + self.resource_writes.clear(); } /// Removes all accesses. pub fn clear(&mut self) { - self.reads_all = false; - self.writes_all = false; - self.reads_and_writes.clear(); - self.writes.clear(); + self.reads_all_resources = false; + self.writes_all_resources = false; + self.reads_all_components = false; + self.writes_all_components = false; + self.component_read_and_writes.clear(); + self.component_writes.clear(); + self.resource_read_and_writes.clear(); + self.resource_writes.clear(); } /// Adds all access from `other`. pub fn extend(&mut self, other: &Access) { - self.reads_all = self.reads_all || other.reads_all; - self.writes_all = self.writes_all || other.writes_all; - self.reads_and_writes.union_with(&other.reads_and_writes); - self.writes.union_with(&other.writes); + self.reads_all_resources = self.reads_all_resources || other.reads_all_resources; + self.writes_all_resources = self.writes_all_resources || other.writes_all_resources; + self.reads_all_components = self.reads_all_components || other.reads_all_components; + self.writes_all_components = self.writes_all_components || other.writes_all_components; + self.component_read_and_writes + .union_with(&other.component_read_and_writes); + self.component_writes.union_with(&other.component_writes); + self.resource_read_and_writes + .union_with(&other.resource_read_and_writes); + self.resource_writes.union_with(&other.resource_writes); + } + + /// Returns `true` if the access and `other` can be active at the same time, + /// only looking at their component access. + /// + /// [`Access`] instances are incompatible if one can write + /// an element that the other can read or write. + pub fn is_components_compatible(&self, other: &Access) -> bool { + if self.writes_all_components { + return !other.has_any_component_read(); + } + + if other.writes_all_components { + return !self.has_any_component_read(); + } + + if self.reads_all_components { + return !other.has_any_component_write(); + } + + if other.reads_all_components { + return !self.has_any_component_write(); + } + + self.component_writes + .is_disjoint(&other.component_read_and_writes) + && other + .component_writes + .is_disjoint(&self.component_read_and_writes) + } + + /// Returns `true` if the access and `other` can be active at the same time, + /// only looking at their resource access. + /// + /// [`Access`] instances are incompatible if one can write + /// an element that the other can read or write. + pub fn is_resources_compatible(&self, other: &Access) -> bool { + if self.writes_all_resources { + return !other.has_any_resource_read(); + } + + if other.writes_all_resources { + return !self.has_any_resource_read(); + } + + if self.reads_all_resources { + return !other.has_any_resource_write(); + } + + if other.reads_all_resources { + return !self.has_any_resource_write(); + } + + self.resource_writes + .is_disjoint(&other.resource_read_and_writes) + && other + .resource_writes + .is_disjoint(&self.resource_read_and_writes) } /// Returns `true` if the access and `other` can be active at the same time. @@ -225,98 +426,145 @@ impl Access { /// [`Access`] instances are incompatible if one can write /// an element that the other can read or write. pub fn is_compatible(&self, other: &Access) -> bool { - if self.writes_all { - return !other.has_any_read(); + self.is_components_compatible(other) && self.is_resources_compatible(other) + } + + /// Returns `true` if the set's component access is a subset of another, i.e. `other`'s component access + /// contains at least all the values in `self`. + pub fn is_subset_components(&self, other: &Access) -> bool { + if self.writes_all_components { + return other.writes_all_components; } - if other.writes_all { - return !self.has_any_read(); + if other.writes_all_components { + return true; } - if self.reads_all { - return !other.has_any_write(); + if self.reads_all_components { + return other.reads_all_components; } - if other.reads_all { - return !self.has_any_write(); + if other.reads_all_components { + return self.component_writes.is_subset(&other.component_writes); } - self.writes.is_disjoint(&other.reads_and_writes) - && other.writes.is_disjoint(&self.reads_and_writes) + self.component_read_and_writes + .is_subset(&other.component_read_and_writes) + && self.component_writes.is_subset(&other.component_writes) } - /// Returns `true` if the set is a subset of another, i.e. `other` contains - /// at least all the values in `self`. - pub fn is_subset(&self, other: &Access) -> bool { - if self.writes_all { - return other.writes_all; + /// Returns `true` if the set's resource access is a subset of another, i.e. `other`'s resource access + /// contains at least all the values in `self`. + pub fn is_subset_resources(&self, other: &Access) -> bool { + if self.writes_all_resources { + return other.writes_all_resources; } - if other.writes_all { + if other.writes_all_resources { return true; } - if self.reads_all { - return other.reads_all; + if self.reads_all_resources { + return other.reads_all_resources; } - if other.reads_all { - return self.writes.is_subset(&other.writes); + if other.reads_all_resources { + return self.resource_writes.is_subset(&other.resource_writes); } - self.reads_and_writes.is_subset(&other.reads_and_writes) - && self.writes.is_subset(&other.writes) + self.resource_read_and_writes + .is_subset(&other.resource_read_and_writes) + && self.resource_writes.is_subset(&other.resource_writes) + } + + /// Returns `true` if the set is a subset of another, i.e. `other` contains + /// at least all the values in `self`. + pub fn is_subset(&self, other: &Access) -> bool { + self.is_subset_components(other) && self.is_subset_resources(other) } /// Returns a vector of elements that the access and `other` cannot access at the same time. pub fn get_conflicts(&self, other: &Access) -> Vec { let mut conflicts = FixedBitSet::default(); - if self.reads_all { + if self.reads_all_components { // QUESTION: How to handle `other.writes_all`? - conflicts.extend(other.writes.ones()); + conflicts.extend(other.component_writes.ones()); } - if other.reads_all { + if other.reads_all_components { // QUESTION: How to handle `self.writes_all`. - conflicts.extend(self.writes.ones()); + conflicts.extend(self.component_writes.ones()); } - if self.writes_all { - conflicts.extend(other.reads_and_writes.ones()); + if self.writes_all_components { + conflicts.extend(other.component_read_and_writes.ones()); } - if other.writes_all { - conflicts.extend(self.reads_and_writes.ones()); + if other.writes_all_components { + conflicts.extend(self.component_read_and_writes.ones()); + } + if self.reads_all_resources { + // QUESTION: How to handle `other.writes_all`? + conflicts.extend(other.resource_writes.ones()); } - conflicts.extend(self.writes.intersection(&other.reads_and_writes)); - conflicts.extend(self.reads_and_writes.intersection(&other.writes)); + if other.reads_all_resources { + // QUESTION: How to handle `self.writes_all`. + conflicts.extend(self.resource_writes.ones()); + } + + if self.writes_all_resources { + conflicts.extend(other.resource_read_and_writes.ones()); + } + + if other.writes_all_resources { + conflicts.extend(self.resource_read_and_writes.ones()); + } + + conflicts.extend( + self.component_writes + .intersection(&other.component_read_and_writes), + ); + conflicts.extend( + self.component_read_and_writes + .intersection(&other.component_writes), + ); + conflicts.extend( + self.resource_writes + .intersection(&other.resource_read_and_writes), + ); + conflicts.extend( + self.resource_read_and_writes + .intersection(&other.resource_writes), + ); conflicts .ones() .map(SparseSetIndex::get_sparse_set_index) .collect() } - /// Returns the indices of the elements this has access to. - pub fn reads_and_writes(&self) -> impl Iterator + '_ { - self.reads_and_writes.ones().map(T::get_sparse_set_index) + /// Returns the indices of the components this has access to. + pub fn component_reads_and_writes(&self) -> impl Iterator + '_ { + self.component_read_and_writes + .ones() + .map(T::get_sparse_set_index) } - /// Returns the indices of the elements this has non-exclusive access to. - pub fn reads(&self) -> impl Iterator + '_ { - self.reads_and_writes - .difference(&self.writes) + /// Returns the indices of the components this has non-exclusive access to. + pub fn component_reads(&self) -> impl Iterator + '_ { + self.component_read_and_writes + .difference(&self.component_writes) .map(T::get_sparse_set_index) } - /// Returns the indices of the elements this has exclusive access to. - pub fn writes(&self) -> impl Iterator + '_ { - self.writes.ones().map(T::get_sparse_set_index) + /// Returns the indices of the components this has exclusive access to. + pub fn component_writes(&self) -> impl Iterator + '_ { + self.component_writes.ones().map(T::get_sparse_set_index) } - /// Returns the indices of the elements that this has an archetypal access to. + /// Returns the indices of the components that this has an archetypal access to. /// - /// These are elements whose values are not accessed (and thus will never cause conflicts), + /// These are components whose values are not accessed (and thus will never cause conflicts), /// but whose presence in an archetype may affect query results. /// /// Currently, this is only used for [`Has`]. @@ -420,20 +668,30 @@ impl FilteredAccess { &mut self.access } - /// Adds access to the element given by `index`. - pub fn add_read(&mut self, index: T) { - self.access.add_read(index.clone()); + /// Adds access to the component given by `index`. + pub fn add_component_read(&mut self, index: T) { + self.access.add_component_read(index.clone()); self.add_required(index.clone()); self.and_with(index); } - /// Adds exclusive access to the element given by `index`. - pub fn add_write(&mut self, index: T) { - self.access.add_write(index.clone()); + /// Adds exclusive access to the component given by `index`. + pub fn add_component_write(&mut self, index: T) { + self.access.add_component_write(index.clone()); self.add_required(index.clone()); self.and_with(index); } + /// Adds access to the resource given by `index`. + pub fn add_resource_read(&mut self, index: T) { + self.access.add_resource_read(index.clone()); + } + + /// Adds exclusive access to the resource given by `index`. + pub fn add_resource_write(&mut self, index: T) { + self.access.add_resource_write(index.clone()); + } + fn add_required(&mut self, index: T) { self.required.grow_and_insert(index.sparse_set_index()); } @@ -544,6 +802,16 @@ impl FilteredAccess { self.access.write_all(); } + /// Sets the underlying unfiltered access as having access to all components. + pub fn read_all_components(&mut self) { + self.access.read_all_components(); + } + + /// Sets the underlying unfiltered access as having mutable access to all components. + pub fn write_all_components(&mut self) { + self.access.write_all_components(); + } + /// Returns `true` if the set is a subset of another, i.e. `other` contains /// at least all the values in `self`. pub fn is_subset(&self, other: &FilteredAccess) -> bool { @@ -711,17 +979,17 @@ impl FilteredAccessSet { self.filtered_accesses.push(filtered_access); } - /// Adds a read access without filters to the set. - pub(crate) fn add_unfiltered_read(&mut self, index: T) { + /// Adds a read access to a resource to the set. + pub(crate) fn add_unfiltered_resource_read(&mut self, index: T) { let mut filter = FilteredAccess::default(); - filter.add_read(index); + filter.add_resource_read(index); self.add(filter); } - /// Adds a write access without filters to the set. - pub(crate) fn add_unfiltered_write(&mut self, index: T) { + /// Adds a write access to a resource to the set. + pub(crate) fn add_unfiltered_resource_write(&mut self, index: T) { let mut filter = FilteredAccess::default(); - filter.add_write(index); + filter.add_resource_write(index); self.add(filter); } @@ -769,9 +1037,9 @@ mod tests { fn create_sample_access() -> Access { let mut access = Access::::default(); - access.add_read(1); - access.add_read(2); - access.add_write(3); + access.add_component_read(1); + access.add_component_read(2); + access.add_component_write(3); access.add_archetypal(5); access.read_all(); @@ -781,8 +1049,8 @@ mod tests { fn create_sample_filtered_access() -> FilteredAccess { let mut filtered_access = FilteredAccess::::default(); - filtered_access.add_write(1); - filtered_access.add_read(2); + filtered_access.add_component_write(1); + filtered_access.add_component_read(2); filtered_access.add_required(3); filtered_access.and_with(4); @@ -801,8 +1069,8 @@ mod tests { fn create_sample_filtered_access_set() -> FilteredAccessSet { let mut filtered_access_set = FilteredAccessSet::::default(); - filtered_access_set.add_unfiltered_read(2); - filtered_access_set.add_unfiltered_write(4); + filtered_access_set.add_unfiltered_resource_read(2); + filtered_access_set.add_unfiltered_resource_write(4); filtered_access_set.read_all(); filtered_access_set @@ -821,8 +1089,8 @@ mod tests { let original: Access = create_sample_access(); let mut cloned = Access::::default(); - cloned.add_write(7); - cloned.add_read(4); + cloned.add_component_write(7); + cloned.add_component_read(4); cloned.add_archetypal(8); cloned.write_all(); @@ -844,8 +1112,8 @@ mod tests { let original: FilteredAccess = create_sample_filtered_access(); let mut cloned = FilteredAccess::::default(); - cloned.add_write(7); - cloned.add_read(4); + cloned.add_component_write(7); + cloned.add_component_read(4); cloned.append_or(&FilteredAccess::default()); cloned.clone_from(&original); @@ -887,8 +1155,8 @@ mod tests { let original: FilteredAccessSet = create_sample_filtered_access_set(); let mut cloned = FilteredAccessSet::::default(); - cloned.add_unfiltered_read(7); - cloned.add_unfiltered_write(9); + cloned.add_unfiltered_resource_read(7); + cloned.add_unfiltered_resource_write(9); cloned.write_all(); cloned.clone_from(&original); @@ -900,7 +1168,7 @@ mod tests { fn read_all_access_conflicts() { // read_all / single write let mut access_a = Access::::default(); - access_a.add_write(0); + access_a.add_component_write(0); let mut access_b = Access::::default(); access_b.read_all(); @@ -920,24 +1188,24 @@ mod tests { #[test] fn access_get_conflicts() { let mut access_a = Access::::default(); - access_a.add_read(0); - access_a.add_read(1); + access_a.add_component_read(0); + access_a.add_component_read(1); let mut access_b = Access::::default(); - access_b.add_read(0); - access_b.add_write(1); + access_b.add_component_read(0); + access_b.add_component_write(1); assert_eq!(access_a.get_conflicts(&access_b), vec![1]); let mut access_c = Access::::default(); - access_c.add_write(0); - access_c.add_write(1); + access_c.add_component_write(0); + access_c.add_component_write(1); assert_eq!(access_a.get_conflicts(&access_c), vec![0, 1]); assert_eq!(access_b.get_conflicts(&access_c), vec![0, 1]); let mut access_d = Access::::default(); - access_d.add_read(0); + access_d.add_component_read(0); assert_eq!(access_d.get_conflicts(&access_a), vec![]); assert_eq!(access_d.get_conflicts(&access_b), vec![]); @@ -947,10 +1215,10 @@ mod tests { #[test] fn filtered_combined_access() { let mut access_a = FilteredAccessSet::::default(); - access_a.add_unfiltered_read(1); + access_a.add_unfiltered_resource_read(1); let mut filter_b = FilteredAccess::::default(); - filter_b.add_write(1); + filter_b.add_resource_write(1); let conflicts = access_a.get_conflicts_single(&filter_b); assert_eq!( @@ -963,22 +1231,22 @@ mod tests { #[test] fn filtered_access_extend() { let mut access_a = FilteredAccess::::default(); - access_a.add_read(0); - access_a.add_read(1); + access_a.add_component_read(0); + access_a.add_component_read(1); access_a.and_with(2); let mut access_b = FilteredAccess::::default(); - access_b.add_read(0); - access_b.add_write(3); + access_b.add_component_read(0); + access_b.add_component_write(3); access_b.and_without(4); access_a.extend(&access_b); let mut expected = FilteredAccess::::default(); - expected.add_read(0); - expected.add_read(1); + expected.add_component_read(0); + expected.add_component_read(1); expected.and_with(2); - expected.add_write(3); + expected.add_component_write(3); expected.and_without(4); assert!(access_a.eq(&expected)); @@ -988,8 +1256,8 @@ mod tests { fn filtered_access_extend_or() { let mut access_a = FilteredAccess::::default(); // Exclusive access to `(&mut A, &mut B)`. - access_a.add_write(0); - access_a.add_write(1); + access_a.add_component_write(0); + access_a.add_component_write(1); // Filter by `With`. let mut access_b = FilteredAccess::::default(); @@ -1010,8 +1278,8 @@ mod tests { // The intention here is to test that exclusive access implied by `add_write` // forms correct normalized access structs when extended with `Or` filters. let mut expected = FilteredAccess::::default(); - expected.add_write(0); - expected.add_write(1); + expected.add_component_write(0); + expected.add_component_write(1); // The resulted access is expected to represent `Or<((With, With, With), (With, With, With, Without))>`. expected.filter_sets = vec![ AccessFilters { diff --git a/crates/bevy_ecs/src/query/builder.rs b/crates/bevy_ecs/src/query/builder.rs index 101371d00400f..3076b5d54e090 100644 --- a/crates/bevy_ecs/src/query/builder.rs +++ b/crates/bevy_ecs/src/query/builder.rs @@ -142,14 +142,14 @@ impl<'w, D: QueryData, F: QueryFilter> QueryBuilder<'w, D, F> { /// Adds `&T` to the [`FilteredAccess`] of self. pub fn ref_id(&mut self, id: ComponentId) -> &mut Self { self.with_id(id); - self.access.add_read(id); + self.access.add_component_read(id); self } /// Adds `&mut T` to the [`FilteredAccess`] of self. pub fn mut_id(&mut self, id: ComponentId) -> &mut Self { self.with_id(id); - self.access.add_write(id); + self.access.add_component_write(id); self } diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index e3d51405771bc..7e9dd2f9c4f02 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -480,10 +480,10 @@ unsafe impl<'a> WorldQuery for EntityRef<'a> { fn update_component_access(_state: &Self::State, access: &mut FilteredAccess) { assert!( - !access.access().has_any_write(), + !access.access().has_any_component_write(), "EntityRef conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", ); - access.read_all(); + access.read_all_components(); } fn init_state(_world: &mut World) {} @@ -556,10 +556,10 @@ unsafe impl<'a> WorldQuery for EntityMut<'a> { fn update_component_access(_state: &Self::State, access: &mut FilteredAccess) { assert!( - !access.access().has_any_read(), + !access.access().has_any_component_read(), "EntityMut conflicts with a previous access in this query. Exclusive access cannot coincide with any other accesses.", ); - access.write_all(); + access.write_all_components(); } fn init_state(_world: &mut World) {} @@ -600,7 +600,7 @@ unsafe impl<'a> WorldQuery for FilteredEntityRef<'a> { _this_run: Tick, ) -> Self::Fetch<'w> { let mut access = Access::default(); - access.read_all(); + access.read_all_components(); (world, access) } @@ -612,9 +612,9 @@ unsafe impl<'a> WorldQuery for FilteredEntityRef<'a> { _table: &Table, ) { let mut access = Access::default(); - state.access.reads().for_each(|id| { + state.access.component_reads().for_each(|id| { if archetype.contains(id) { - access.add_read(id); + access.add_component_read(id); } }); fetch.1 = access; @@ -623,9 +623,9 @@ unsafe impl<'a> WorldQuery for FilteredEntityRef<'a> { #[inline] unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) { let mut access = Access::default(); - state.access.reads().for_each(|id| { + state.access.component_reads().for_each(|id| { if table.has_column(id) { - access.add_read(id); + access.add_component_read(id); } }); fetch.1 = access; @@ -703,7 +703,7 @@ unsafe impl<'a> WorldQuery for FilteredEntityMut<'a> { _this_run: Tick, ) -> Self::Fetch<'w> { let mut access = Access::default(); - access.write_all(); + access.write_all_components(); (world, access) } @@ -715,14 +715,14 @@ unsafe impl<'a> WorldQuery for FilteredEntityMut<'a> { _table: &Table, ) { let mut access = Access::default(); - state.access.reads().for_each(|id| { + state.access.component_reads().for_each(|id| { if archetype.contains(id) { - access.add_read(id); + access.add_component_read(id); } }); - state.access.writes().for_each(|id| { + state.access.component_writes().for_each(|id| { if archetype.contains(id) { - access.add_write(id); + access.add_component_write(id); } }); fetch.1 = access; @@ -731,14 +731,14 @@ unsafe impl<'a> WorldQuery for FilteredEntityMut<'a> { #[inline] unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) { let mut access = Access::default(); - state.access.reads().for_each(|id| { + state.access.component_reads().for_each(|id| { if table.has_column(id) { - access.add_read(id); + access.add_component_read(id); } }); - state.access.writes().for_each(|id| { + state.access.component_writes().for_each(|id| { if table.has_column(id) { - access.add_write(id); + access.add_component_write(id); } }); fetch.1 = access; @@ -988,11 +988,11 @@ unsafe impl WorldQuery for &T { access: &mut FilteredAccess, ) { assert!( - !access.access().has_write(component_id), + !access.access().has_component_write(component_id), "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", - std::any::type_name::(), + std::any::type_name::(), ); - access.add_read(component_id); + access.add_component_read(component_id); } fn init_state(world: &mut World) -> ComponentId { @@ -1183,11 +1183,11 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { access: &mut FilteredAccess, ) { assert!( - !access.access().has_write(component_id), + !access.access().has_component_write(component_id), "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", - std::any::type_name::(), + std::any::type_name::(), ); - access.add_read(component_id); + access.add_component_read(component_id); } fn init_state(world: &mut World) -> ComponentId { @@ -1378,11 +1378,11 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { access: &mut FilteredAccess, ) { assert!( - !access.access().has_read(component_id), + !access.access().has_component_read(component_id), "&mut {} conflicts with a previous access in this query. Mutable component access must be unique.", - std::any::type_name::(), + std::any::type_name::(), ); - access.add_write(component_id); + access.add_component_write(component_id); } fn init_state(world: &mut World) -> ComponentId { @@ -1476,11 +1476,11 @@ unsafe impl<'__w, T: Component> WorldQuery for Mut<'__w, T> { // Update component access here instead of in `<&mut T as WorldQuery>` to avoid erroneously referencing // `&mut T` in error message. assert!( - !access.access().has_read(component_id), + !access.access().has_component_read(component_id), "Mut<{}> conflicts with a previous access in this query. Mutable component access mut be unique.", - std::any::type_name::(), + std::any::type_name::(), ); - access.add_write(component_id); + access.add_component_write(component_id); } // Forwarded to `&mut T` diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 0e4d9cc405097..f7f9617d51434 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -688,10 +688,10 @@ unsafe impl WorldQuery for Added { #[inline] fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess) { - if access.access().has_write(id) { + if access.access().has_component_write(id) { panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",std::any::type_name::()); } - access.add_read(id); + access.add_component_read(id); } fn init_state(world: &mut World) -> ComponentId { @@ -899,10 +899,10 @@ unsafe impl WorldQuery for Changed { #[inline] fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess) { - if access.access().has_write(id) { + if access.access().has_component_write(id) { panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",std::any::type_name::()); } - access.add_read(id); + access.add_component_read(id); } fn init_state(world: &mut World) -> ComponentId { diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 2d3c454201c9c..2d2a85e014b3a 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -495,16 +495,22 @@ impl QueryState { archetype: &Archetype, access: &mut Access, ) { - self.component_access.access.reads().for_each(|id| { - if let Some(id) = archetype.get_archetype_component_id(id) { - access.add_read(id); - } - }); - self.component_access.access.writes().for_each(|id| { - if let Some(id) = archetype.get_archetype_component_id(id) { - access.add_write(id); - } - }); + self.component_access + .access + .component_reads() + .for_each(|id| { + if let Some(id) = archetype.get_archetype_component_id(id) { + access.add_component_read(id); + } + }); + self.component_access + .access + .component_writes() + .for_each(|id| { + if let Some(id) = archetype.get_archetype_component_id(id) { + access.add_component_write(id); + } + }); } /// Use this to transform a [`QueryState`] into a more generic [`QueryState`]. diff --git a/crates/bevy_ecs/src/schedule/mod.rs b/crates/bevy_ecs/src/schedule/mod.rs index 184bef250a6b5..28aa65c59d2b9 100644 --- a/crates/bevy_ecs/src/schedule/mod.rs +++ b/crates/bevy_ecs/src/schedule/mod.rs @@ -737,6 +737,9 @@ mod tests { #[derive(Event)] struct E; + #[derive(Resource, Component)] + struct RC; + fn empty_system() {} fn res_system(_res: Res) {} fn resmut_system(_res: ResMut) {} @@ -746,6 +749,8 @@ mod tests { fn write_component_system(_query: Query<&mut A>) {} fn with_filtered_component_system(_query: Query<&mut A, With>) {} fn without_filtered_component_system(_query: Query<&mut A, Without>) {} + fn entity_ref_system(_query: Query) {} + fn entity_mut_system(_query: Query) {} fn event_reader_system(_reader: EventReader) {} fn event_writer_system(_writer: EventWriter) {} fn event_resource_system(_events: ResMut>) {} @@ -788,6 +793,8 @@ mod tests { nonsend_system, read_component_system, read_component_system, + entity_ref_system, + entity_ref_system, event_reader_system, event_reader_system, read_world_system, @@ -893,6 +900,73 @@ mod tests { assert_eq!(schedule.graph().conflicting_systems().len(), 3); } + /// Test that when a struct is both a Resource and a Component, they do not + /// conflict with each other. + #[test] + fn shared_resource_mut_component() { + let mut world = World::new(); + world.insert_resource(RC); + + let mut schedule = Schedule::default(); + schedule.add_systems((|_: ResMut| {}, |_: Query<&mut RC>| {})); + + let _ = schedule.initialize(&mut world); + + assert_eq!(schedule.graph().conflicting_systems().len(), 0); + } + + #[test] + fn resource_mut_and_entity_ref() { + let mut world = World::new(); + world.insert_resource(R); + + let mut schedule = Schedule::default(); + schedule.add_systems((resmut_system, entity_ref_system)); + + let _ = schedule.initialize(&mut world); + + assert_eq!(schedule.graph().conflicting_systems().len(), 0); + } + + #[test] + fn resource_and_entity_mut() { + let mut world = World::new(); + world.insert_resource(R); + + let mut schedule = Schedule::default(); + schedule.add_systems((res_system, nonsend_system, entity_mut_system)); + + let _ = schedule.initialize(&mut world); + + assert_eq!(schedule.graph().conflicting_systems().len(), 0); + } + + #[test] + fn write_component_and_entity_ref() { + let mut world = World::new(); + world.insert_resource(R); + + let mut schedule = Schedule::default(); + schedule.add_systems((write_component_system, entity_ref_system)); + + let _ = schedule.initialize(&mut world); + + assert_eq!(schedule.graph().conflicting_systems().len(), 1); + } + + #[test] + fn read_component_and_entity_mut() { + let mut world = World::new(); + world.insert_resource(R); + + let mut schedule = Schedule::default(); + schedule.add_systems((read_component_system, entity_mut_system)); + + let _ = schedule.initialize(&mut world); + + assert_eq!(schedule.graph().conflicting_systems().len(), 1); + } + #[test] fn exclusive() { let mut world = World::new(); diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 071e02f9d3ca4..88faa30cdf4b8 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -1495,7 +1495,7 @@ mod tests { assert_eq!( system .archetype_component_access() - .reads() + .component_reads() .collect::>(), expected_ids ); @@ -1525,7 +1525,7 @@ mod tests { assert_eq!( system .archetype_component_access() - .reads() + .component_reads() .collect::>(), expected_ids ); @@ -1543,7 +1543,7 @@ mod tests { assert_eq!( system .archetype_component_access() - .reads() + .component_reads() .collect::>(), expected_ids ); diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 8898e34d092e1..1ae9f9a0ae00b 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -504,21 +504,21 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { let combined_access = system_meta.component_access_set.combined_access(); assert!( - !combined_access.has_write(component_id), + !combined_access.has_resource_write(component_id), "error[B0002]: Res<{}> in system {} conflicts with a previous ResMut<{0}> access. Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002", std::any::type_name::(), system_meta.name, ); system_meta .component_access_set - .add_unfiltered_read(component_id); + .add_unfiltered_resource_read(component_id); let archetype_component_id = world .get_resource_archetype_component_id(component_id) .unwrap(); system_meta .archetype_component_access - .add_read(archetype_component_id); + .add_resource_read(archetype_component_id); component_id } @@ -600,25 +600,25 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { world.initialize_resource_internal(component_id); let combined_access = system_meta.component_access_set.combined_access(); - if combined_access.has_write(component_id) { + if combined_access.has_resource_write(component_id) { panic!( "error[B0002]: ResMut<{}> in system {} conflicts with a previous ResMut<{0}> access. Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002", std::any::type_name::(), system_meta.name); - } else if combined_access.has_read(component_id) { + } else if combined_access.has_resource_read(component_id) { panic!( "error[B0002]: ResMut<{}> in system {} conflicts with a previous Res<{0}> access. Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002", std::any::type_name::(), system_meta.name); } system_meta .component_access_set - .add_unfiltered_write(component_id); + .add_unfiltered_resource_write(component_id); let archetype_component_id = world .get_resource_archetype_component_id(component_id) .unwrap(); system_meta .archetype_component_access - .add_write(archetype_component_id); + .add_resource_write(archetype_component_id); component_id } @@ -1153,21 +1153,21 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> { let combined_access = system_meta.component_access_set.combined_access(); assert!( - !combined_access.has_write(component_id), + !combined_access.has_resource_write(component_id), "error[B0002]: NonSend<{}> in system {} conflicts with a previous mutable resource access ({0}). Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002", std::any::type_name::(), system_meta.name, ); system_meta .component_access_set - .add_unfiltered_read(component_id); + .add_unfiltered_resource_read(component_id); let archetype_component_id = world .get_non_send_archetype_component_id(component_id) .unwrap(); system_meta .archetype_component_access - .add_read(archetype_component_id); + .add_resource_read(archetype_component_id); component_id } @@ -1246,25 +1246,25 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> { world.initialize_non_send_internal(component_id); let combined_access = system_meta.component_access_set.combined_access(); - if combined_access.has_write(component_id) { + if combined_access.has_component_write(component_id) { panic!( "error[B0002]: NonSendMut<{}> in system {} conflicts with a previous mutable resource access ({0}). Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002", std::any::type_name::(), system_meta.name); - } else if combined_access.has_read(component_id) { + } else if combined_access.has_component_read(component_id) { panic!( "error[B0002]: NonSendMut<{}> in system {} conflicts with a previous immutable resource access ({0}). Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002", std::any::type_name::(), system_meta.name); } system_meta .component_access_set - .add_unfiltered_write(component_id); + .add_unfiltered_resource_write(component_id); let archetype_component_id = world .get_non_send_archetype_component_id(component_id) .unwrap(); system_meta .archetype_component_access - .add_write(archetype_component_id); + .add_resource_write(archetype_component_id); component_id } diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 1dd522c17c802..34dc767341ba6 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -1860,7 +1860,7 @@ impl<'w> FilteredEntityRef<'w> { /// Returns an iterator over the component ids that are accessed by self. #[inline] pub fn components(&self) -> impl Iterator + '_ { - self.access.reads_and_writes() + self.access.component_reads_and_writes() } /// Returns a reference to the underlying [`Access`]. @@ -1912,7 +1912,7 @@ impl<'w> FilteredEntityRef<'w> { pub fn get(&self) -> Option<&'w T> { let id = self.entity.world().components().get_id(TypeId::of::())?; self.access - .has_read(id) + .has_component_read(id) // SAFETY: We have read access .then(|| unsafe { self.entity.get() }) .flatten() @@ -1926,7 +1926,7 @@ impl<'w> FilteredEntityRef<'w> { pub fn get_ref(&self) -> Option> { let id = self.entity.world().components().get_id(TypeId::of::())?; self.access - .has_read(id) + .has_component_read(id) // SAFETY: We have read access .then(|| unsafe { self.entity.get_ref() }) .flatten() @@ -1938,7 +1938,7 @@ impl<'w> FilteredEntityRef<'w> { pub fn get_change_ticks(&self) -> Option { let id = self.entity.world().components().get_id(TypeId::of::())?; self.access - .has_read(id) + .has_component_read(id) // SAFETY: We have read access .then(|| unsafe { self.entity.get_change_ticks::() }) .flatten() @@ -1953,7 +1953,7 @@ impl<'w> FilteredEntityRef<'w> { #[inline] pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option { self.access - .has_read(component_id) + .has_component_read(component_id) // SAFETY: We have read access .then(|| unsafe { self.entity.get_change_ticks_by_id(component_id) }) .flatten() @@ -1970,7 +1970,7 @@ impl<'w> FilteredEntityRef<'w> { #[inline] pub fn get_by_id(&self, component_id: ComponentId) -> Option> { self.access - .has_read(component_id) + .has_component_read(component_id) // SAFETY: We have read access .then(|| unsafe { self.entity.get_by_id(component_id) }) .flatten() @@ -2117,7 +2117,7 @@ impl<'w> FilteredEntityMut<'w> { /// Returns an iterator over the component ids that are accessed by self. #[inline] pub fn components(&self) -> impl Iterator + '_ { - self.access.reads_and_writes() + self.access.component_reads_and_writes() } /// Returns a reference to the underlying [`Access`]. @@ -2185,7 +2185,7 @@ impl<'w> FilteredEntityMut<'w> { pub fn get_mut(&mut self) -> Option> { let id = self.entity.world().components().get_id(TypeId::of::())?; self.access - .has_write(id) + .has_component_write(id) // SAFETY: We have write access .then(|| unsafe { self.entity.get_mut() }) .flatten() @@ -2198,7 +2198,7 @@ impl<'w> FilteredEntityMut<'w> { pub fn into_mut(self) -> Option> { let id = self.entity.world().components().get_id(TypeId::of::())?; self.access - .has_write(id) + .has_component_write(id) // SAFETY: We have write access .then(|| unsafe { self.entity.get_mut() }) .flatten() @@ -2246,7 +2246,7 @@ impl<'w> FilteredEntityMut<'w> { #[inline] pub fn get_mut_by_id(&mut self, component_id: ComponentId) -> Option> { self.access - .has_write(component_id) + .has_component_write(component_id) // SAFETY: We have write access .then(|| unsafe { self.entity.get_mut_by_id(component_id) }) .flatten() diff --git a/examples/ecs/dynamic.rs b/examples/ecs/dynamic.rs index 14a9d35ce3023..f13f3751740c8 100644 --- a/examples/ecs/dynamic.rs +++ b/examples/ecs/dynamic.rs @@ -168,7 +168,7 @@ fn main() { }; // If we have write access, increment each value once - if filtered_entity.access().has_write(id) { + if filtered_entity.access().has_component_write(id) { data.iter_mut().for_each(|data| { *data += 1; });