edict icon indicating copy to clipboard operation
edict copied to clipboard

[Discussion] New filter API

Open akhilman opened this issue 2 years ago • 4 comments

I have two complains about the current edict API:

  • There is no way to get entities if one or another component is modified. The current query::<(Modified<&A>, Modified<&B>)>() means that both components are modified;
  • The tracked query without Modified like query::<(&A, &B)>.tracked(&mut tracks) is meaningless;
  • Mutation of tracks by query is not intuitive. I have to clone tracks to pass it to multiple queries.

I propose this changes:

  • Remove Modified query;
  • Remove methods like World::for_each_tracked and QueryRef::tracked_iter;
  • Move skip_chunk and skip_item from Query to Filter;
  • Add Modified<T> which owns it's tracks and ModifiedSince<T> which refers the tracks from outside.
  • Add Or and And filters;
  • Add constructors with::<T>(), without::<T>(), modified_since::<T>(tracks) and modified::<T>();
  • Add .or(filter) and .and(filter) methods to Filter trait;
  • Add methods like World::for_each_filtered and QueryRef::filter or event World::query_filtered like bevy_ecs does.

Query with this changes will look like:

// the stateful filter is created outside the loop
let mut my_filter = with::<Foo>().or(with::<Bar>()).and(modified::<Baz>());
loop {
    for _, (bar, baz) in world.query_filtered<(&Bar, &Baz)>(&mut my_filter) {
        // do something
    }
}

and with passing the tracks manually:

let mut tracks = world.tracks_now()
loop {
    world.for_each_filtered::<(&Bar, &Baz)>(
        &mut with::<Foo>()
            .or(with::<Bar>())
            .and(modified_since::<Baz>(tracks)),
         |_, (bar. baz)| { ... } );
    tracks = world.tracks_now();  // tracks is updated explicitly 
}

Solves #10

akhilman avatar May 14 '22 05:05 akhilman

We also can implement operators, so creating a filter would look like this:

let mut my_filter = !with::<Foo>() && (modified::<Bar>() || modified::<Baz>());

akhilman avatar May 14 '22 07:05 akhilman

In this case one would need an instance of Tracks for each modified filter (implicit or explicit). Which may or may not be a bad thing. But to keep this in mind while we deciding on the API

zakarumych avatar May 14 '22 21:05 zakarumych

The problem with putting modification filtering into, well, filters, is that it would require to lock the version arrays immutably, which would prevent locking them mutably for mutable component query.

And if filter won't lock version arrays immutably, parallel query would be able to lock them mutably and cause a minor case of nasal demons.

zakarumych avatar May 19 '22 09:05 zakarumych

Theoretically we can lock components for complex query at once and then distribute it to sub-queries and filters and allow trackers to look at versions of mutably borrowed component. But this can make code too complex and possibly slow.

zakarumych avatar May 19 '22 09:05 zakarumych