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}