wde_renderer/sync/
sync_worlds.rs

1use bevy::prelude::*;
2
3use crate::sync::{MainEntity, RenderEntity};
4
5/// Marker component that indicates that its entity needs to be synchronized to the render world.
6/// 
7/// It is added automatically to entities that have components that implement [`SyncComponent`](crate::sync::sync_component::SyncComponent) (see [`SyncComponentPlugin`](crate::sync::sync_component::SyncComponentPlugin)), and can be added manually to entities that need to be synchronized without any specific component requirements.
8/// See [`sync`](crate::sync) for more details.
9#[derive(Component, Copy, Clone, Debug, Default, Reflect)]
10#[reflect(Component, Default, Clone)]
11#[component(storage = "SparseSet")]
12pub struct SyncToRenderWorld;
13
14pub(crate) struct SyncWorldPlugin;
15impl Plugin for SyncWorldPlugin {
16    fn build(&self, app: &mut App) {
17        app.init_resource::<PendingSyncEntity>()
18            .add_observer(
19                |add: On<Add, SyncToRenderWorld>, mut pending: ResMut<PendingSyncEntity>| {
20                    pending.push(EntityRecord::Added(add.entity)); // Push the added main world entity to the pending sync list
21                },
22            )
23            .add_observer(
24                |remove: On<Remove, SyncToRenderWorld>,
25                 mut pending: ResMut<PendingSyncEntity>,
26                 query: Query<&RenderEntity>| {
27                    if let Ok(e) = query.get(remove.entity) {
28                        pending.push(EntityRecord::Removed(*e)); // Push the removed main world entity to the pending sync list
29                    };
30                },
31            );
32    }
33}
34
35/// A record enum to what entities with [`SyncToRenderWorld`] have been added or removed.
36#[derive(Debug)]
37pub(crate) enum EntityRecord {
38    /// Entity spawned on the main world.
39    Added(Entity),
40    /// Entity despawned on the main world.
41    Removed(RenderEntity),
42    /// Component removed on an entity in the main world.
43    ComponentRemoved(Entity, fn(EntityWorldMut<'_>)),
44}
45
46// Entity Record in Main World pending to Sync to the Render World.
47#[derive(Resource, Default, Deref, DerefMut)]
48pub(crate) struct PendingSyncEntity {
49    records: Vec<EntityRecord>,
50}
51
52pub(crate) fn sync_worlds(main_world: &mut World, render_world: &mut World) {
53    main_world.resource_scope(|world, mut pending: Mut<PendingSyncEntity>| {
54        for record in pending.drain(..) {
55            match record {
56                EntityRecord::Added(e) => {
57                    if let Ok(mut main_entity) = world.get_entity_mut(e) {
58                        match main_entity.entry::<RenderEntity>() {
59                            bevy::ecs::world::ComponentEntry::Occupied(_) => {
60                                panic!("Attempting to synchronize an entity that has already been synchronized!");
61                            }
62                            bevy::ecs::world::ComponentEntry::Vacant(entry) => {
63                                let id = render_world.spawn(MainEntity(e)).id();
64
65                                entry.insert(RenderEntity(id));
66                            }
67                        };
68                    }
69                }
70                EntityRecord::Removed(render_entity) => {
71                    if let Ok(ec) = render_world.get_entity_mut(render_entity.id()) {
72                        ec.despawn();
73                    };
74                }
75                EntityRecord::ComponentRemoved(main_entity, removal_function) => {
76                    let Some(render_entity) = world.get::<RenderEntity>(main_entity) else {
77                        continue;
78                    };
79                    if let Ok(render_world_entity) = render_world.get_entity_mut(render_entity.id()) {
80                        removal_function(render_world_entity);
81                    }
82                },
83            }
84        }
85    });
86}