ecs: rename ComMut<T> to Track<T> and fix nested change queries
This commit is contained in:
parent
23b96a48a6
commit
fbcf3f89d0
@ -203,6 +203,10 @@ impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch<T> {
|
|||||||
unsafe fn next(&mut self) -> Option<T::Item> {
|
unsafe fn next(&mut self) -> Option<T::Item> {
|
||||||
Some(self.0.as_mut()?.next())
|
Some(self.0.as_mut()?.next())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn should_skip(&self) -> bool {
|
||||||
|
self.0.as_ref().map_or(false, |fetch| fetch.should_skip())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Query transformer skipping entities that have a `T` component
|
/// Query transformer skipping entities that have a `T` component
|
||||||
@ -258,6 +262,10 @@ impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWithout<T, F> {
|
|||||||
unsafe fn next(&mut self) -> F::Item {
|
unsafe fn next(&mut self) -> F::Item {
|
||||||
self.0.next()
|
self.0.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn should_skip(&self) -> bool {
|
||||||
|
self.0.should_skip()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Query transformer skipping entities that do not have a `T` component
|
/// Query transformer skipping entities that do not have a `T` component
|
||||||
@ -315,6 +323,10 @@ impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWith<T, F> {
|
|||||||
unsafe fn next(&mut self) -> F::Item {
|
unsafe fn next(&mut self) -> F::Item {
|
||||||
self.0.next()
|
self.0.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn should_skip(&self) -> bool {
|
||||||
|
self.0.should_skip()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A borrow of a `World` sufficient to execute the query `Q`
|
/// A borrow of a `World` sufficient to execute the query `Q`
|
||||||
@ -536,7 +548,7 @@ impl<Q: Query> ChunkIter<Q> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
break Some(self.fetch.next())
|
break Some(self.fetch.next());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -636,6 +648,10 @@ macro_rules! tuple_impl {
|
|||||||
let ($($name,)*) = self;
|
let ($($name,)*) = self;
|
||||||
($($name.next(),)*)
|
($($name.next(),)*)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn should_skip(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<$($name: Query),*> Query for ($($name,)*) {
|
impl<$($name: Query),*> Query for ($($name,)*) {
|
||||||
|
|||||||
@ -15,7 +15,7 @@ pub mod prelude {
|
|||||||
system::{
|
system::{
|
||||||
Commands, IntoForEachSystem, IntoQuerySystem, IntoThreadLocalSystem, Query, System,
|
Commands, IntoForEachSystem, IntoQuerySystem, IntoThreadLocalSystem, Query, System,
|
||||||
},
|
},
|
||||||
world::{WorldBuilderSource, ComMut},
|
world::{WorldBuilderSource, Track},
|
||||||
Bundle, Component, Entity, Ref, RefMut, With, Without, World,
|
Bundle, Component, Entity, Ref, RefMut, With, Without, World,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,36 +7,36 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Unique borrow of an entity's component
|
/// Unique borrow of an entity's component
|
||||||
pub struct ComMut<'a, T: Component> {
|
pub struct Track<'a, T: Component> {
|
||||||
value: &'a mut T,
|
value: &'a mut T,
|
||||||
modified: &'a mut bool,
|
modified: &'a mut bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<T: Component> Send for ComMut<'_, T> {}
|
unsafe impl<T: Component> Send for Track<'_, T> {}
|
||||||
unsafe impl<T: Component> Sync for ComMut<'_, T> {}
|
unsafe impl<T: Component> Sync for Track<'_, T> {}
|
||||||
|
|
||||||
impl<'a, T: Component> Deref for ComMut<'a, T> {
|
impl<'a, T: Component> Deref for Track<'a, T> {
|
||||||
type Target = T;
|
type Target = T;
|
||||||
fn deref(&self) -> &T {
|
fn deref(&self) -> &T {
|
||||||
self.value
|
self.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: Component> DerefMut for ComMut<'a, T> {
|
impl<'a, T: Component> DerefMut for Track<'a, T> {
|
||||||
fn deref_mut(&mut self) -> &mut T {
|
fn deref_mut(&mut self) -> &mut T {
|
||||||
*self.modified = true;
|
*self.modified = true;
|
||||||
self.value
|
self.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: Component> HecsQuery for ComMut<'a, T> {
|
impl<'a, T: Component> HecsQuery for Track<'a, T> {
|
||||||
type Fetch = FetchComMut<T>;
|
type Fetch = FetchTrack<T>;
|
||||||
}
|
}
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub struct FetchComMut<T>(NonNull<T>, NonNull<bool>);
|
pub struct FetchTrack<T>(NonNull<T>, NonNull<bool>);
|
||||||
|
|
||||||
impl<'a, T: Component> Fetch<'a> for FetchComMut<T> {
|
impl<'a, T: Component> Fetch<'a> for FetchTrack<T> {
|
||||||
type Item = ComMut<'a, T>;
|
type Item = Track<'a, T>;
|
||||||
|
|
||||||
fn access(archetype: &Archetype) -> Option<Access> {
|
fn access(archetype: &Archetype) -> Option<Access> {
|
||||||
if archetype.has::<T>() {
|
if archetype.has::<T>() {
|
||||||
@ -63,12 +63,12 @@ impl<'a, T: Component> Fetch<'a> for FetchComMut<T> {
|
|||||||
archetype.release_mut::<T>();
|
archetype.release_mut::<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn next(&mut self) -> ComMut<'a, T> {
|
unsafe fn next(&mut self) -> Track<'a, T> {
|
||||||
let component = self.0.as_ptr();
|
let component = self.0.as_ptr();
|
||||||
let modified = self.1.as_ptr();
|
let modified = self.1.as_ptr();
|
||||||
self.0 = NonNull::new_unchecked(component.add(1));
|
self.0 = NonNull::new_unchecked(component.add(1));
|
||||||
self.1 = NonNull::new_unchecked(modified.add(1));
|
self.1 = NonNull::new_unchecked(modified.add(1));
|
||||||
ComMut {
|
Track {
|
||||||
value: &mut *component,
|
value: &mut *component,
|
||||||
modified: &mut *modified,
|
modified: &mut *modified,
|
||||||
}
|
}
|
||||||
@ -114,7 +114,7 @@ impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchChanged<T, F> {
|
|||||||
|
|
||||||
unsafe fn should_skip(&self) -> bool {
|
unsafe fn should_skip(&self) -> bool {
|
||||||
// skip if the current item wasn't changed
|
// skip if the current item wasn't changed
|
||||||
!*self.2.as_ref()
|
!*self.2.as_ref() || self.0.should_skip()
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn next(&mut self) -> F::Item {
|
unsafe fn next(&mut self) -> F::Item {
|
||||||
@ -125,52 +125,64 @@ impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchChanged<T, F> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{Changed, ComMut};
|
use crate::{Changed, Track};
|
||||||
use hecs::{Entity, World};
|
use hecs::{Entity, World};
|
||||||
|
|
||||||
struct A(usize);
|
struct A(usize);
|
||||||
struct B;
|
struct B(usize);
|
||||||
struct C;
|
struct C;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn modified_trackers() {
|
fn modified_trackers() {
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
let e1 = world.spawn((A(0), B));
|
let e1 = world.spawn((A(0), B(0)));
|
||||||
let e2 = world.spawn((A(0), B));
|
let e2 = world.spawn((A(0), B(0)));
|
||||||
let e3 = world.spawn((A(0), B));
|
let e3 = world.spawn((A(0), B(0)));
|
||||||
world.spawn((A(0), B));
|
world.spawn((A(0), B));
|
||||||
|
|
||||||
for (i, mut a) in world.query::<ComMut<A>>().iter().enumerate() {
|
for (i, mut a) in world.query::<Track<A>>().iter().enumerate() {
|
||||||
if i % 2 == 0 {
|
if i % 2 == 0 {
|
||||||
a.0 += 1;
|
a.0 += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_changed(world: &World) -> Vec<Entity> {
|
fn get_changed_a(world: &World) -> Vec<Entity> {
|
||||||
world
|
world
|
||||||
.query::<Changed<A, Entity>>()
|
.query::<Changed<A, Entity>>()
|
||||||
.iter()
|
.iter()
|
||||||
.collect::<Vec<Entity>>()
|
.collect::<Vec<Entity>>()
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(get_changed(&world), vec![e1, e3]);
|
assert_eq!(get_changed_a(&world), vec![e1, e3]);
|
||||||
|
|
||||||
// ensure changing an entity's archetypes also moves its modified state
|
// ensure changing an entity's archetypes also moves its modified state
|
||||||
world.insert(e1, (C,)).unwrap();
|
world.insert(e1, (C,)).unwrap();
|
||||||
|
|
||||||
assert_eq!(get_changed(&world), vec![e3, e1], "changed entities list should not change (although the order will due to archetype moves)");
|
assert_eq!(get_changed_a(&world), vec![e3, e1], "changed entities list should not change (although the order will due to archetype moves)");
|
||||||
|
|
||||||
// spawning a new A entity should not change existing modified state
|
// spawning a new A entity should not change existing modified state
|
||||||
world.insert(e1, (A(0), B)).unwrap();
|
world.insert(e1, (A(0), B)).unwrap();
|
||||||
assert_eq!(get_changed(&world), vec![e3, e1], "changed entities list should not change");
|
assert_eq!(
|
||||||
|
get_changed_a(&world),
|
||||||
|
vec![e3, e1],
|
||||||
|
"changed entities list should not change"
|
||||||
|
);
|
||||||
|
|
||||||
// removing an unchanged entity should not change modified state
|
// removing an unchanged entity should not change modified state
|
||||||
world.despawn(e2).unwrap();
|
world.despawn(e2).unwrap();
|
||||||
assert_eq!(get_changed(&world), vec![e3, e1], "changed entities list should not change");
|
assert_eq!(
|
||||||
|
get_changed_a(&world),
|
||||||
|
vec![e3, e1],
|
||||||
|
"changed entities list should not change"
|
||||||
|
);
|
||||||
|
|
||||||
// removing a changed entity should remove it from enumeration
|
// removing a changed entity should remove it from enumeration
|
||||||
world.despawn(e1).unwrap();
|
world.despawn(e1).unwrap();
|
||||||
assert_eq!(get_changed(&world), vec![e3], "e1 should no longer be returned");
|
assert_eq!(
|
||||||
|
get_changed_a(&world),
|
||||||
|
vec![e3],
|
||||||
|
"e1 should no longer be returned"
|
||||||
|
);
|
||||||
|
|
||||||
world.clear_trackers();
|
world.clear_trackers();
|
||||||
|
|
||||||
@ -180,4 +192,26 @@ mod tests {
|
|||||||
.collect::<Vec<Entity>>()
|
.collect::<Vec<Entity>>()
|
||||||
.is_empty());
|
.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nested_changed_query() {
|
||||||
|
let mut world = World::default();
|
||||||
|
world.spawn((A(0), B(0)));
|
||||||
|
let e2 = world.spawn((A(0), B(0)));
|
||||||
|
world.spawn((A(0), B(0)));
|
||||||
|
|
||||||
|
for mut a in world.query::<Track<A>>().iter() {
|
||||||
|
a.0 += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for mut b in world.query::<Track<B>>().iter().skip(1).take(1) {
|
||||||
|
b.0 += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let a_b_changed = world
|
||||||
|
.query::<Changed<A, Changed<B, Entity>>>()
|
||||||
|
.iter()
|
||||||
|
.collect::<Vec<Entity>>();
|
||||||
|
assert_eq!(a_b_changed, vec![e2]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user