Skip to content

Dynamic Queries #37

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed

Conversation

GarettCooper
Copy link

@GarettCooper GarettCooper commented Oct 20, 2021

RENDERED

Expands the query interface of World to support queries with parameters which are determined at runtime, not compile time, to enable future scripting and hot reloading.

I wrote this RFC as a way of learning how Bevy's ECS worked, so I had to implement most of it to make sure what I was proposing was possible. You can find my branch here.


A static query returns the various components it has retrieved in a tuple. This isn't an option for a Dynamic Query
however, since the number of components being returned isn't known at compile time. Instead, a Dynamic Query returns
a `DynamicQueryEntity` struct, which is both indexable and iterable to access the individual `DynamicItem`s it
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Allowing indexing and iteration over query items to static queries might be a nice bit of API consistency. I've seen users ask for an alternative to tuple unpacking before in favor of a more "named" API.

Using static typing, you would perform a query like this:

```rust
let query_state = world.query::<(Entity, &Velocity, &mut Position, Option<&Layer>), Without<Frozen>>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would really like to see this used in a standard system context as well; I think the ergonomics of it matter too. If it's not possible, explain why in this section.

}
```
The two inner types, `DynamicComponentReference` and `DynamicMutComponentReference` provide safe access to
`&`/`&mut` and change tracking functionality while also enabling users to have access to the underlying pointer for
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Presumably the underlying pointer API will have to be extremely unsafe?

result[2].unwrap_mut_component().downcast_mut::<Position>().unwrap() += result[1].unwrap_component().downcast_mut::<Velocity>.unwrap();
}
```

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO this really needs to show a more representative use case after the existing "basic usage" example.

Right now, all of the functionality demonstrated is simply less safe and slower than the existing tools :p

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. I'd like to see something like a function that lists all the values of an arbitrary component given a String component name, (which could actually be useful for debugging.)

Could DynamicQuery be serializable? This would make it possible to send queries from another process or machine.`

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HackerFoo I feel that a built in serialization method may be overly prescriptive. It would be convenient, but for use cases such as scripting interfaces it makes more sense for the plugin implementation to determine the best method of communicating the query across the interop boundary.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't need to be implemented, but it would be useful if it was designed to make that possible. Just using #[derive(Serialize, Deserialize)] from serde should be enough, though, which can then be serialized in any way that works with serde.

is inferior to the static interface in all but a very specific subset of cases. While this is a concern, it isn't
one which will have much of an impact as long as documentation guides users towards the standard static queries, and
makes it clear that the primary audience of dynamic queries is plugin authors. The interface has been designed with
ergonomic builders, but it's unlikely that the intended consumers of the dynamic query API will ever user it in this
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ergonomic builders, but it's unlikely that the intended consumers of the dynamic query API will ever user it in this
ergonomic builders, but it's unlikely that the intended consumers of the dynamic query API will ever use it in this

- Should dynamic queries be hidden behind a feature?
- Which traits should the `DynamicItem`/`DynamicItemSet` types implemented for maximum convenience?
- Do we even want maximum convenience?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do modding / scripting use cases access resources?

These questions remain unresolved:
- How much of the type's information should the `DynamicComponentReference` types carry with them (Layout?)?
- How can change tracking information be captured in dynamic queries?
- Should dynamic queries be hidden behind a feature?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that many of the other bits of special-case functionality (e.g. audio), I think it should be reasonable to do so.

If it is, this should not be a default feature. Ideally it's named in such a way that we can easily add other related bits of functionality to the same feature flag.

This would help reduce compile times slightly (I think), but more importantly, make it much more challenging for beginners to accidentally stumble across.

Dynamic queries lay the groundwork for future expansion of the ECS to fully support runtime behaviour changes.
Plugins could dynamically define systems at runtime based on configuration files or scripts, since they now have the
tools to interact with the Entity Component System freely. This addition of this single RFC would still leave Bevy a
long way from full dynamic support though. Future RFCs should set up implementations for inserting new systems and
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

@zicklag zicklag left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there! Thanks for pinging me on this @alice-i-cecile. :)

Unfortunately I don't have time to do a great review of this, but it looks like the right direction as far as I can tell roughly.

Also, as far as implementation goes, the bevy internals have changed so much I don't know anything much about them anymore. :)

Anyway, that's what I've got for what it's worth, but thanks for working on scripting support @GarettCooper!

@HackerFoo
Copy link

I thought of a potential use for dynamic queries.

I model relations as entities having a component that implements a Relation trait. The entities referred to in each Relation component have an index to all Relation entities to which they belong.

When despawning an entity belonging to a Relation, it would be nice to remove it from the relation, but that's difficult, because it can belong to multiple different component types, so I'd need to query every type that implements Relation. Ideally, I could store the list of types in each index, and automatically query each type.

There may be better ways to implement relations, but this is one potential use case for dynamic queries - implementing general cleanup systems.

@CGMossa
Copy link

CGMossa commented Oct 7, 2022

What's happened?

@GarettCooper
Copy link
Author

I haven't had the time to pursue this and it seems like there community has been happy to move on without it with a lot of great work being done in the dynamic/reflection space. I figured this was just adding noise to the open RFCs list and was better off closed.

@zicklag
Copy link
Member

zicklag commented Oct 7, 2022

If anybody is looking for dynamic query support, there is now the work-in-progress, but very promising, bevy_ecs_dynamic crate. It's being used to help implement a JavaScript scripting API in bevy_mod_js_scripting which is also being put to actual use in the Punchy and Jumpy.

It's all a little work-in-progress and under development, but so far it looks like it's going to turn out great.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants