Skip to main content

bevy_render/
sync_component.rs

1use core::marker::PhantomData;
2
3use bevy_app::{App, Plugin};
4use bevy_ecs::{
5    bundle::{Bundle, NoBundleEffect},
6    component::Component,
7};
8
9use crate::sync_world::{EntityRecord, PendingSyncEntity, SyncToRenderWorld};
10
11/// Plugin that registers a component for automatic sync to the render world. See [`SyncWorldPlugin`] for more information.
12///
13/// This plugin is automatically added by [`ExtractComponentPlugin`], and only needs to be added for manual extraction implementations.
14///
15/// The marker type `F` is only used as a way to bypass the orphan rules. To
16/// implement the trait for a foreign type you can use a local type as the
17/// marker, e.g. the type of the plugin that calls [`SyncComponentPlugin`].
18///
19/// # Implementation details
20///
21/// It adds [`SyncToRenderWorld`] as a required component to make the [`SyncWorldPlugin`] aware of the component, and
22/// handles cleanup of the component in the render world when it is removed from an entity.
23///
24/// [`ExtractComponentPlugin`]: crate::extract_component::ExtractComponentPlugin
25/// [`SyncWorldPlugin`]: crate::sync_world::SyncWorldPlugin
26pub struct SyncComponentPlugin<C, F = ()>(PhantomData<(C, F)>);
27
28impl<C: SyncComponent<F>, F> Default for SyncComponentPlugin<C, F> {
29    fn default() -> Self {
30        Self(PhantomData)
31    }
32}
33
34/// Trait that links components from the main world with output components in
35/// the render world. It is used by [`SyncComponentPlugin`].
36///
37/// The marker type `F` is only used as a way to bypass the orphan rules. To
38/// implement the trait for a foreign type you can use a local type as the
39/// marker, e.g. the type of the plugin that calls [`SyncComponentPlugin`].
40pub trait SyncComponent<F = ()>: Component {
41    /// Describes what components should be removed from the render world if the
42    /// implementing component is removed.
43    type Target: Bundle<Effect: NoBundleEffect>;
44    // TODO: https://github.com/rust-lang/rust/issues/29661
45    // type Target: Bundle<Effect: NoBundleEffect> = Self;
46}
47
48impl<C: SyncComponent<F>, F: Send + Sync + 'static> Plugin for SyncComponentPlugin<C, F> {
49    fn build(&self, app: &mut App) {
50        app.register_required_components::<C, SyncToRenderWorld>();
51
52        app.world_mut()
53            .register_component_hooks::<C>()
54            .on_remove(|mut world, context| {
55                let mut pending = world.resource_mut::<PendingSyncEntity>();
56                pending.push(EntityRecord::ComponentRemoved(
57                    context.entity,
58                    |mut entity| {
59                        entity.remove::<C::Target>();
60                    },
61                ));
62            });
63    }
64}