Skip to main content

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}