bevy_trait_query_0_14_0/lib.rs
1//! Let's say you have a trait that you want to implement for some of your components.
2//!
3//! ```
4//! # use bevy::prelude::*;
5//! #
6//! /// Components that display a message when hovered.
7//! pub trait Tooltip {
8//! /// Text displayed when hovering over an entity with this trait.
9//! fn tooltip(&self) -> &str;
10//! }
11//! ```
12//!
13//! In order to be useful within bevy, you'll want to be able to query for this trait.
14//!
15//! ```
16//! # use bevy::prelude::*;
17//! # // Required to make the macro work, because cargo thinks
18//! # // we are in `bevy_trait_query` when compiling this example.
19//! # use bevy_trait_query_0_14_0::*;
20//!
21//! // Just add this attribute...
22//! #[bevy_trait_query_0_14_0::queryable]
23//! pub trait Tooltip {
24//! fn tooltip(&self) -> &str;
25//! }
26//!
27//! // ...and now you can use your trait in queries.
28//! fn show_tooltips_system(
29//! tooltips: Query<&dyn Tooltip>,
30//! // ...
31//! ) {
32//! // ...
33//! }
34//! # bevy_ecs::system::assert_is_system(show_tooltips_system);
35//! ```
36//!
37//! Since Rust unfortunately lacks any kind of reflection, it is necessary to register each
38//! component with the trait when the app gets built.
39//!
40//! ```
41//! # use bevy::prelude::*;
42//! # use bevy_trait_query_0_14_0::*;
43//! #
44//! # #[bevy_trait_query_0_14_0::queryable]
45//! # pub trait Tooltip {
46//! # fn tooltip(&self) -> &str;
47//! # }
48//! #
49//! #[derive(Component)]
50//! struct Player(String);
51//!
52//! #[derive(Component)]
53//! enum Villager {
54//! Farmer,
55//! // ...
56//! }
57//!
58//! #[derive(Component)]
59//! struct Monster;
60//!
61//! /* ...trait implementations omitted for brevity... */
62//!
63//! # impl Tooltip for Player {
64//! # fn tooltip(&self) -> &str {
65//! # &self.0
66//! # }
67//! # }
68//! #
69//! # impl Tooltip for Villager {
70//! # fn tooltip(&self) -> &str {
71//! # "Villager"
72//! # }
73//! # }
74//! #
75//! # impl Tooltip for Monster {
76//! # fn tooltip(&self) -> &str {
77//! # "Run!"
78//! # }
79//! # }
80//! #
81//! struct TooltipPlugin;
82//!
83//! impl Plugin for TooltipPlugin {
84//! fn build(&self, app: &mut App) {
85//! // We must import this trait in order to register our components.
86//! // If we don't register them, they will be invisible to the game engine.
87//! use bevy_trait_query_0_14_0::RegisterExt;
88//!
89//! app
90//! .register_component_as::<dyn Tooltip, Player>()
91//! .register_component_as::<dyn Tooltip, Villager>()
92//! .register_component_as::<dyn Tooltip, Monster>()
93//! .add_systems(Update, show_tooltips);
94//! }
95//! }
96//! # fn show_tooltips() {}
97//! #
98//! # fn main() {
99//! # App::new().add_plugins((DefaultPlugins, TooltipPlugin)).update();
100//! # }
101//! ```
102//!
103//! Unlike queries for concrete types, it's possible for an entity to have multiple components
104//! that match a trait query.
105//!
106//! ```
107//! # use bevy::prelude::*;
108//! # use bevy_trait_query_0_14_0::*;
109//! #
110//! # #[bevy_trait_query_0_14_0::queryable]
111//! # pub trait Tooltip {
112//! # fn tooltip(&self) -> &str;
113//! # }
114//! #
115//! # #[derive(Component)]
116//! # struct Player(String);
117//! #
118//! # #[derive(Component)]
119//! # struct Monster;
120//! #
121//! # impl Tooltip for Player {
122//! # fn tooltip(&self) -> &str {
123//! # &self.0
124//! # }
125//! # }
126//! #
127//! # impl Tooltip for Monster {
128//! # fn tooltip(&self) -> &str {
129//! # "Run!"
130//! # }
131//! # }
132//! #
133//! # fn main() {
134//! # App::new()
135//! # .add_plugins(DefaultPlugins)
136//! # .register_component_as::<dyn Tooltip, Player>()
137//! # .register_component_as::<dyn Tooltip, Monster>()
138//! # .add_systems(Startup, setup)
139//! # .update();
140//! # }
141//! #
142//! # fn setup(mut commands: Commands) {
143//! # commands.spawn(Player("Fourier".to_owned()));
144//! # commands.spawn(Monster);
145//! # }
146//!
147//! fn show_tooltips(
148//! tooltips: Query<&dyn Tooltip>,
149//! // ...
150//! ) {
151//! // Iterate over each entity that has tooltips.
152//! for entity_tooltips in &tooltips {
153//! // Iterate over each component implementing `Tooltip` for the current entity.
154//! for tooltip in entity_tooltips {
155//! println!("Tooltip: {}", tooltip.tooltip());
156//! }
157//! }
158//!
159//! // If you instead just want to iterate over all tooltips, you can do:
160//! for tooltip in tooltips.iter().flatten() {
161//! println!("Tooltip: {}", tooltip.tooltip());
162//! }
163//! }
164//! ```
165//!
166//! Alternatively, if you expect to only have component implementing the trait for each entity,
167//! you can use the filter [`One`](crate::one::One). This has significantly better performance than iterating
168//! over all trait impls.
169//!
170//! ```
171//! # use bevy::prelude::*;
172//! # use bevy_trait_query_0_14_0::*;
173//! #
174//! # #[bevy_trait_query_0_14_0::queryable]
175//! # pub trait Tooltip {
176//! # fn tooltip(&self) -> &str;
177//! # }
178//! #
179//! use bevy_trait_query_0_14_0::One;
180//!
181//! fn show_tooltips(
182//! tooltips: Query<One<&dyn Tooltip>>,
183//! // ...
184//! ) {
185//! for tooltip in &tooltips {
186//! println!("Tooltip: {}", tooltip.tooltip());
187//! }
188//! }
189//! # bevy_ecs::system::assert_is_system(show_tooltips);
190//! ```
191//!
192//! Trait queries support basic change detection filtration. So to get all the components that
193//! implement the target trait, and have also changed in some way since the last tick, you can:
194//! ```no_run
195//! # use bevy::prelude::*;
196//! # use bevy_trait_query_0_14_0::*;
197//! #
198//! # #[bevy_trait_query_0_14_0::queryable]
199//! # pub trait Tooltip {
200//! # fn tooltip(&self) -> &str;
201//! # }
202//! #
203//! fn show_tooltips(
204//! tooltips_query: Query<All<&dyn Tooltip>>
205//! // ...
206//! ) {
207//! // Iterate over all entities with at least one component implementing `Tooltip`
208//! for entity_tooltips in &tooltips_query {
209//! // Iterate over each component for the current entity that changed since the last time the system was run.
210//! for tooltip in entity_tooltips.iter_changed() {
211//! println!("Changed Tooltip: {}", tooltip.tooltip());
212//! }
213//! }
214//! }
215//! ```
216//!
217//! Similar to [`iter_changed`](crate::all::All::iter_changed), we have [`iter_added`](crate::all::All::iter_added)
218//! to detect entities which have had a trait-implementing component added since the last tick.
219//!
220//! If you know you have only one component that implements the target trait,
221//! you can use `OneAdded` or `OneChanged` which behave more like the typical
222//! `bevy` `Added/Changed` filters:
223//! ```no_run
224//! # use bevy::prelude::*;
225//! # use bevy_trait_query_0_14_0::*;
226//! #
227//! # #[bevy_trait_query_0_14_0::queryable]
228//! # pub trait Tooltip {
229//! # fn tooltip(&self) -> &str;
230//! # }
231//! #
232//! fn show_tooltips(
233//! tooltips_query: Query<One<&dyn Tooltip>, OneChanged<dyn Tooltip>>
234//! // ...
235//! ) {
236//! // Iterate over each entity that has one tooltip implementing component that has also changed
237//! for tooltip in &tooltips_query {
238//! println!("Changed Tooltip: {}", tooltip.tooltip());
239//! }
240//! }
241//! ```
242//! Note in the above example how `OneChanged` does *not* take a reference to the trait object!
243//!
244//! # Performance
245//!
246//! The performance of trait queries is quite competitive. Here are some benchmarks for simple cases:
247//!
248//! | | Concrete type | One<dyn Trait> | All<dyn Trait> |
249//! |-------------------|----------------|-------------------|-----------------|
250//! | 1 match | 16.135 µs | 31.441 µs | 63.273 µs |
251//! | 2 matches | 17.501 µs | - | 102.83 µs |
252//! | 1-2 matches | - | 16.959 µs | 82.179 µs |
253//!
254
255use bevy_ecs::{
256 component::{ComponentId, StorageType},
257 prelude::{Component, Resource, World},
258 ptr::{Ptr, PtrMut},
259};
260
261#[cfg(test)]
262mod tests;
263
264pub mod all;
265pub mod one;
266
267pub use all::*;
268pub use one::*;
269
270/// Marker for traits that can be used in queries.
271pub trait TraitQuery: 'static {}
272
273pub use bevy_trait_query_impl_0_14_0::queryable;
274
275#[doc(hidden)]
276pub trait TraitQueryMarker<Trait: ?Sized + TraitQuery> {
277 type Covered: Component;
278 /// Casts an untyped pointer to a trait object pointer,
279 /// with a vtable corresponding to `Self::Covered`.
280 fn cast(_: *mut u8) -> *mut Trait;
281}
282
283/// Extension methods for registering components with trait queries.
284pub trait RegisterExt {
285 /// Allows a component to be used in trait queries.
286 /// Calling this multiple times with the same arguments will do nothing on subsequent calls.
287 ///
288 /// # Panics
289 /// If this function is called after the simulation starts for a given [`World`].
290 /// Due to engine limitations, registering new trait impls after the game starts cannot be supported.
291 fn register_component_as<Trait: ?Sized + TraitQuery, C: Component>(&mut self) -> &mut Self
292 where
293 (C,): TraitQueryMarker<Trait, Covered = C>;
294}
295
296impl RegisterExt for World {
297 fn register_component_as<Trait: ?Sized + TraitQuery, C: Component>(&mut self) -> &mut Self
298 where
299 (C,): TraitQueryMarker<Trait, Covered = C>,
300 {
301 let component_id = self.init_component::<C>();
302 let registry = self
303 .get_resource_or_insert_with::<TraitImplRegistry<Trait>>(Default::default)
304 .into_inner();
305 let meta = TraitImplMeta {
306 size_bytes: std::mem::size_of::<C>(),
307 dyn_ctor: DynCtor { cast: <(C,)>::cast },
308 };
309 registry.register::<C>(component_id, meta);
310 self
311 }
312}
313
314#[cfg(feature = "bevy_app")]
315impl RegisterExt for bevy_app::App {
316 fn register_component_as<Trait: ?Sized + TraitQuery, C: Component>(&mut self) -> &mut Self
317 where
318 (C,): TraitQueryMarker<Trait, Covered = C>,
319 {
320 self.world_mut().register_component_as::<Trait, C>();
321 self
322 }
323}
324
325#[derive(Resource)]
326struct TraitImplRegistry<Trait: ?Sized> {
327 // Component IDs are stored contiguously so that we can search them quickly.
328 components: Vec<ComponentId>,
329 meta: Vec<TraitImplMeta<Trait>>,
330
331 table_components: Vec<ComponentId>,
332 table_meta: Vec<TraitImplMeta<Trait>>,
333
334 sparse_components: Vec<ComponentId>,
335 sparse_meta: Vec<TraitImplMeta<Trait>>,
336
337 sealed: bool,
338}
339
340impl<T: ?Sized> Default for TraitImplRegistry<T> {
341 #[inline]
342 fn default() -> Self {
343 Self {
344 components: vec![],
345 meta: vec![],
346 table_components: vec![],
347 table_meta: vec![],
348 sparse_components: vec![],
349 sparse_meta: vec![],
350 sealed: false,
351 }
352 }
353}
354
355impl<Trait: ?Sized + TraitQuery> TraitImplRegistry<Trait> {
356 fn register<C: Component>(&mut self, component: ComponentId, meta: TraitImplMeta<Trait>) {
357 // Don't register the same component multiple times.
358 if self.components.contains(&component) {
359 return;
360 }
361
362 if self.sealed {
363 // It is not possible to update the `FetchState` for a given system after the game has started,
364 // so for explicitness, let's panic instead of having a trait impl silently get forgotten.
365 panic!("Cannot register new trait impls after the game has started");
366 }
367
368 self.components.push(component);
369 self.meta.push(meta);
370
371 match <C as Component>::STORAGE_TYPE {
372 StorageType::Table => {
373 self.table_components.push(component);
374 self.table_meta.push(meta);
375 }
376 StorageType::SparseSet => {
377 self.sparse_components.push(component);
378 self.sparse_meta.push(meta);
379 }
380 }
381 }
382
383 fn seal(&mut self) {
384 self.sealed = true;
385 }
386}
387
388/// Stores data about an impl of a trait
389struct TraitImplMeta<Trait: ?Sized> {
390 size_bytes: usize,
391 dyn_ctor: DynCtor<Trait>,
392}
393
394impl<T: ?Sized> Copy for TraitImplMeta<T> {}
395impl<T: ?Sized> Clone for TraitImplMeta<T> {
396 fn clone(&self) -> Self {
397 *self
398 }
399}
400
401#[doc(hidden)]
402pub mod imports {
403 pub use bevy_ecs::{
404 archetype::{Archetype, ArchetypeComponentId},
405 component::Tick,
406 component::{Component, ComponentId, Components},
407 entity::Entity,
408 query::{
409 Access, Added, Changed, FilteredAccess, QueryData, QueryFilter, QueryItem,
410 ReadOnlyQueryData, WorldQuery,
411 },
412 storage::{Table, TableRow},
413 world::{unsafe_world_cell::UnsafeWorldCell, World},
414 };
415}
416
417#[doc(hidden)]
418pub struct TraitQueryState<Trait: ?Sized> {
419 components: Box<[ComponentId]>,
420 meta: Box<[TraitImplMeta<Trait>]>,
421}
422
423impl<Trait: ?Sized + TraitQuery> TraitQueryState<Trait> {
424 fn init(world: &mut World) -> Self {
425 #[cold]
426 fn missing_registry<T: ?Sized + 'static>() -> TraitImplRegistry<T> {
427 tracing::warn!(
428 "no components found matching `{}`, did you forget to register them?",
429 std::any::type_name::<T>()
430 );
431 TraitImplRegistry::<T>::default()
432 }
433
434 let mut registry = world.get_resource_or_insert_with(missing_registry);
435 registry.seal();
436 Self {
437 components: registry.components.clone().into_boxed_slice(),
438 meta: registry.meta.clone().into_boxed_slice(),
439 }
440 }
441
442 // // REVIEW: inline?
443 // // REVIEW: does it make sense to use the optional return type here? The call sites would be
444 // // happy with this so I just made it use `Option`
445 // fn get(world: &World) -> Option<Self> {
446 // // REVIEW: is it ok to use the optional version here?
447 // let registry = world.get_resource::<TraitImplRegistry<Trait>>()?;
448 // // REVIEW: do we really need to clone here on get calls?
449 // Some(Self {
450 // components: registry.components.clone().into_boxed_slice(),
451 // meta: registry.meta.clone().into_boxed_slice(),
452 // })
453 // }
454
455 #[inline]
456 fn matches_component_set_any(&self, set_contains_id: &impl Fn(ComponentId) -> bool) -> bool {
457 self.components.iter().copied().any(set_contains_id)
458 }
459
460 #[inline]
461 fn matches_component_set_one(&self, set_contains_id: &impl Fn(ComponentId) -> bool) -> bool {
462 let match_count = self
463 .components
464 .iter()
465 .filter(|&&c| set_contains_id(c))
466 .count();
467 match_count == 1
468 }
469}
470
471/// Turns an untyped pointer into a trait object pointer,
472/// for a specific erased concrete type.
473struct DynCtor<Trait: ?Sized> {
474 cast: unsafe fn(*mut u8) -> *mut Trait,
475}
476
477impl<T: ?Sized> Copy for DynCtor<T> {}
478impl<T: ?Sized> Clone for DynCtor<T> {
479 fn clone(&self) -> Self {
480 *self
481 }
482}
483
484impl<Trait: ?Sized> DynCtor<Trait> {
485 #[inline]
486 unsafe fn cast(self, ptr: Ptr) -> &Trait {
487 &*(self.cast)(ptr.as_ptr())
488 }
489 #[inline]
490 unsafe fn cast_mut(self, ptr: PtrMut) -> &mut Trait {
491 &mut *(self.cast)(ptr.as_ptr())
492 }
493}
494
495struct ZipExact<A, B> {
496 a: A,
497 b: B,
498}
499
500impl<A: Iterator, B: Iterator> Iterator for ZipExact<A, B> {
501 type Item = (A::Item, B::Item);
502 #[inline]
503 fn next(&mut self) -> Option<Self::Item> {
504 let a = self.a.next()?;
505 let b = self
506 .b
507 .next()
508 // SAFETY: `a` returned a valid value, and the caller of `zip_exact`
509 // guaranteed that `b` will return a value as long as `a` does.
510 .unwrap_or_else(|| unsafe { debug_unreachable() });
511 Some((a, b))
512 }
513}
514
515/// SAFETY: `b` must yield at least as many items as `a`.
516#[inline]
517unsafe fn zip_exact<A: IntoIterator, B: IntoIterator>(
518 a: A,
519 b: B,
520) -> ZipExact<A::IntoIter, B::IntoIter>
521where
522 A::IntoIter: ExactSizeIterator,
523 B::IntoIter: ExactSizeIterator,
524{
525 let a = a.into_iter();
526 let b = b.into_iter();
527 debug_assert_eq!(a.len(), b.len());
528 ZipExact { a, b }
529}
530
531#[track_caller]
532#[inline(always)]
533unsafe fn debug_unreachable() -> ! {
534 #[cfg(debug_assertions)]
535 unreachable!();
536
537 #[cfg(not(debug_assertions))]
538 std::hint::unreachable_unchecked();
539}
540
541#[inline(never)]
542#[cold]
543fn trait_registry_error() -> ! {
544 panic!("The trait query registry has not been initialized; did you forget to register your traits with the world?")
545}