1use bevy_app::Plugin;
2use bevy_derive::{Deref, DerefMut};
3use bevy_ecs::{
4 component::Component,
5 entity::{ContainsEntity, Entity, EntityEquivalent, EntityHash},
6 lifecycle::{Add, Remove},
7 observer::On,
8 query::With,
9 reflect::ReflectComponent,
10 resource::Resource,
11 system::{Local, Query, ResMut, SystemState},
12 world::{EntityWorldMut, Mut, World},
13};
14use bevy_platform::collections::{HashMap, HashSet};
15use bevy_reflect::{std_traits::ReflectDefault, Reflect};
16
17#[derive(Default)]
90pub struct SyncWorldPlugin;
91
92impl Plugin for SyncWorldPlugin {
93 fn build(&self, app: &mut bevy_app::App) {
94 app.init_resource::<PendingSyncEntity>();
95 app.add_observer(
96 |add: On<Add, SyncToRenderWorld>, mut pending: ResMut<PendingSyncEntity>| {
97 pending.push(EntityRecord::Added(add.entity));
98 },
99 );
100 app.add_observer(
101 |remove: On<Remove, SyncToRenderWorld>,
102 mut pending: ResMut<PendingSyncEntity>,
103 query: Query<&RenderEntity>| {
104 if let Ok(e) = query.get(remove.entity) {
105 pending.push(EntityRecord::Removed(*e));
106 };
107 },
108 );
109 }
110}
111#[derive(Component, Copy, Clone, Debug, Default, Reflect)]
122#[reflect(Component, Default, Clone)]
123#[component(storage = "SparseSet")]
124pub struct SyncToRenderWorld;
125
126#[derive(Component, Deref, Copy, Clone, Debug, Eq, Hash, PartialEq, Reflect)]
130#[component(clone_behavior = Ignore)]
131#[reflect(Component, Clone)]
132pub struct RenderEntity(Entity);
133impl RenderEntity {
134 #[inline]
135 pub fn id(&self) -> Entity {
136 self.0
137 }
138}
139
140impl From<Entity> for RenderEntity {
141 fn from(entity: Entity) -> Self {
142 RenderEntity(entity)
143 }
144}
145
146impl ContainsEntity for RenderEntity {
147 fn entity(&self) -> Entity {
148 self.id()
149 }
150}
151
152unsafe impl EntityEquivalent for RenderEntity {}
154
155#[derive(Component, Deref, Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Reflect)]
159#[reflect(Component, Clone)]
160pub struct MainEntity(Entity);
161impl MainEntity {
162 #[inline]
163 pub fn id(&self) -> Entity {
164 self.0
165 }
166}
167
168impl From<Entity> for MainEntity {
169 fn from(entity: Entity) -> Self {
170 MainEntity(entity)
171 }
172}
173
174impl ContainsEntity for MainEntity {
175 fn entity(&self) -> Entity {
176 self.id()
177 }
178}
179
180unsafe impl EntityEquivalent for MainEntity {}
182
183pub type MainEntityHashMap<V> = HashMap<MainEntity, V, EntityHash>;
185
186pub type MainEntityHashSet = HashSet<MainEntity, EntityHash>;
188
189#[derive(Component, Copy, Clone, Debug, Default, Reflect)]
191#[reflect(Component, Default, Clone)]
192pub struct TemporaryRenderEntity;
193
194#[derive(Debug)]
196pub(crate) enum EntityRecord {
197 Added(Entity),
200 Removed(RenderEntity),
203 ComponentRemoved(Entity, fn(EntityWorldMut<'_>)),
206}
207
208#[derive(Resource, Default, Deref, DerefMut)]
210pub(crate) struct PendingSyncEntity {
211 records: Vec<EntityRecord>,
212}
213
214pub(crate) fn entity_sync_system(main_world: &mut World, render_world: &mut World) {
215 main_world.resource_scope(|world, mut pending: Mut<PendingSyncEntity>| {
216 for record in pending.drain(..) {
218 match record {
219 EntityRecord::Added(e) => {
220 if let Ok(mut main_entity) = world.get_entity_mut(e) {
221 match main_entity.entry::<RenderEntity>() {
222 bevy_ecs::world::ComponentEntry::Occupied(_) => {
223 panic!("Attempting to synchronize an entity that has already been synchronized!");
224 }
225 bevy_ecs::world::ComponentEntry::Vacant(entry) => {
226 let id = render_world.spawn(MainEntity(e)).id();
227
228 entry.insert(RenderEntity(id));
229 }
230 };
231 }
232 }
233 EntityRecord::Removed(render_entity) => {
234 if let Ok(ec) = render_world.get_entity_mut(render_entity.id()) {
235 ec.despawn();
236 };
237 }
238 EntityRecord::ComponentRemoved(main_entity, removal_function) => {
239 let Some(render_entity) = world.get::<RenderEntity>(main_entity) else {
240 continue;
241 };
242 if let Ok(render_world_entity) = render_world.get_entity_mut(render_entity.id()) {
243 removal_function(render_world_entity);
244 }
245 },
246 }
247 }
248 });
249}
250
251pub(crate) fn despawn_temporary_render_entities(
252 world: &mut World,
253 state: &mut SystemState<Query<Entity, With<TemporaryRenderEntity>>>,
254 mut local: Local<Vec<Entity>>,
255) {
256 let query = state.get(world).unwrap();
257
258 local.extend(query.iter());
259
260 local.sort_unstable_by_key(|e| e.index());
262 for e in local.drain(..).rev() {
263 world.despawn(e);
264 }
265}
266
267mod render_entities_world_query_impls {
272 use super::{MainEntity, RenderEntity};
273
274 use bevy_ecs::{
275 archetype::Archetype,
276 change_detection::Tick,
277 component::{ComponentId, Components},
278 entity::Entity,
279 query::{
280 ArchetypeQueryData, FilteredAccess, IterQueryData, QueryData, ReadOnlyQueryData,
281 ReleaseStateQueryData, SingleEntityQueryData, WorldQuery,
282 },
283 storage::{Table, TableRow},
284 world::{unsafe_world_cell::UnsafeWorldCell, World},
285 };
286
287 unsafe impl WorldQuery for RenderEntity {
290 type Fetch<'w> = <&'static RenderEntity as WorldQuery>::Fetch<'w>;
291 type State = <&'static RenderEntity as WorldQuery>::State;
292
293 fn shrink_fetch<'wlong: 'wshort, 'wshort>(
294 fetch: Self::Fetch<'wlong>,
295 ) -> Self::Fetch<'wshort> {
296 fetch
297 }
298
299 #[inline]
300 unsafe fn init_fetch<'w, 's>(
301 world: UnsafeWorldCell<'w>,
302 component_id: &'s ComponentId,
303 last_run: Tick,
304 this_run: Tick,
305 ) -> Self::Fetch<'w> {
306 unsafe {
308 <&RenderEntity as WorldQuery>::init_fetch(world, component_id, last_run, this_run)
309 }
310 }
311
312 const IS_DENSE: bool = <&'static RenderEntity as WorldQuery>::IS_DENSE;
313
314 #[inline]
315 unsafe fn set_archetype<'w, 's>(
316 fetch: &mut Self::Fetch<'w>,
317 component_id: &'s ComponentId,
318 archetype: &'w Archetype,
319 table: &'w Table,
320 ) {
321 unsafe {
323 <&RenderEntity as WorldQuery>::set_archetype(fetch, component_id, archetype, table);
324 }
325 }
326
327 #[inline]
328 unsafe fn set_table<'w, 's>(
329 fetch: &mut Self::Fetch<'w>,
330 &component_id: &'s ComponentId,
331 table: &'w Table,
332 ) {
333 unsafe { <&RenderEntity as WorldQuery>::set_table(fetch, &component_id, table) }
335 }
336
337 fn update_component_access(&component_id: &ComponentId, access: &mut FilteredAccess) {
338 <&RenderEntity as WorldQuery>::update_component_access(&component_id, access);
339 }
340
341 fn init_state(world: &mut World) -> ComponentId {
342 <&RenderEntity as WorldQuery>::init_state(world)
343 }
344
345 fn get_state(components: &Components) -> Option<Self::State> {
346 <&RenderEntity as WorldQuery>::get_state(components)
347 }
348
349 fn matches_component_set(
350 &state: &ComponentId,
351 set_contains_id: &impl Fn(ComponentId) -> bool,
352 ) -> bool {
353 <&RenderEntity as WorldQuery>::matches_component_set(&state, set_contains_id)
354 }
355 }
356
357 unsafe impl QueryData for RenderEntity {
360 const IS_READ_ONLY: bool = true;
361 const IS_ARCHETYPAL: bool = <&MainEntity as QueryData>::IS_ARCHETYPAL;
362 type ReadOnly = RenderEntity;
363 type Item<'w, 's> = Entity;
364
365 fn shrink<'wlong: 'wshort, 'wshort, 's>(
366 item: Self::Item<'wlong, 's>,
367 ) -> Self::Item<'wshort, 's> {
368 item
369 }
370
371 #[inline(always)]
372 unsafe fn fetch<'w, 's>(
373 state: &'s Self::State,
374 fetch: &mut Self::Fetch<'w>,
375 entity: Entity,
376 table_row: TableRow,
377 ) -> Option<Self::Item<'w, 's>> {
378 let component =
380 unsafe { <&RenderEntity as QueryData>::fetch(state, fetch, entity, table_row) };
381 component.map(RenderEntity::id)
382 }
383
384 fn iter_access(
385 state: &Self::State,
386 ) -> impl Iterator<Item = bevy_ecs::query::EcsAccessType<'_>> {
387 <&RenderEntity as QueryData>::iter_access(state)
388 }
389 }
390
391 unsafe impl IterQueryData for RenderEntity {}
393
394 unsafe impl ReadOnlyQueryData for RenderEntity {}
396
397 unsafe impl SingleEntityQueryData for RenderEntity {}
399
400 impl ArchetypeQueryData for RenderEntity {}
401
402 impl ReleaseStateQueryData for RenderEntity {
403 fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> {
404 item
405 }
406 }
407
408 unsafe impl WorldQuery for MainEntity {
411 type Fetch<'w> = <&'static MainEntity as WorldQuery>::Fetch<'w>;
412 type State = <&'static MainEntity as WorldQuery>::State;
413
414 fn shrink_fetch<'wlong: 'wshort, 'wshort>(
415 fetch: Self::Fetch<'wlong>,
416 ) -> Self::Fetch<'wshort> {
417 fetch
418 }
419
420 #[inline]
421 unsafe fn init_fetch<'w, 's>(
422 world: UnsafeWorldCell<'w>,
423 component_id: &'s ComponentId,
424 last_run: Tick,
425 this_run: Tick,
426 ) -> Self::Fetch<'w> {
427 unsafe {
429 <&MainEntity as WorldQuery>::init_fetch(world, component_id, last_run, this_run)
430 }
431 }
432
433 const IS_DENSE: bool = <&'static MainEntity as WorldQuery>::IS_DENSE;
434
435 #[inline]
436 unsafe fn set_archetype<'w, 's>(
437 fetch: &mut Self::Fetch<'w>,
438 component_id: &ComponentId,
439 archetype: &'w Archetype,
440 table: &'w Table,
441 ) {
442 unsafe {
444 <&MainEntity as WorldQuery>::set_archetype(fetch, component_id, archetype, table);
445 }
446 }
447
448 #[inline]
449 unsafe fn set_table<'w, 's>(
450 fetch: &mut Self::Fetch<'w>,
451 &component_id: &'s ComponentId,
452 table: &'w Table,
453 ) {
454 unsafe { <&MainEntity as WorldQuery>::set_table(fetch, &component_id, table) }
456 }
457
458 fn update_component_access(&component_id: &ComponentId, access: &mut FilteredAccess) {
459 <&MainEntity as WorldQuery>::update_component_access(&component_id, access);
460 }
461
462 fn init_state(world: &mut World) -> ComponentId {
463 <&MainEntity as WorldQuery>::init_state(world)
464 }
465
466 fn get_state(components: &Components) -> Option<Self::State> {
467 <&MainEntity as WorldQuery>::get_state(components)
468 }
469
470 fn matches_component_set(
471 &state: &ComponentId,
472 set_contains_id: &impl Fn(ComponentId) -> bool,
473 ) -> bool {
474 <&MainEntity as WorldQuery>::matches_component_set(&state, set_contains_id)
475 }
476 }
477
478 unsafe impl QueryData for MainEntity {
481 const IS_READ_ONLY: bool = true;
482 const IS_ARCHETYPAL: bool = <&MainEntity as QueryData>::IS_ARCHETYPAL;
483 type ReadOnly = MainEntity;
484 type Item<'w, 's> = Entity;
485
486 fn shrink<'wlong: 'wshort, 'wshort, 's>(
487 item: Self::Item<'wlong, 's>,
488 ) -> Self::Item<'wshort, 's> {
489 item
490 }
491
492 #[inline(always)]
493 unsafe fn fetch<'w, 's>(
494 state: &'s Self::State,
495 fetch: &mut Self::Fetch<'w>,
496 entity: Entity,
497 table_row: TableRow,
498 ) -> Option<Self::Item<'w, 's>> {
499 let component =
501 unsafe { <&MainEntity as QueryData>::fetch(state, fetch, entity, table_row) };
502 component.map(MainEntity::id)
503 }
504
505 fn iter_access(
506 state: &Self::State,
507 ) -> impl Iterator<Item = bevy_ecs::query::EcsAccessType<'_>> {
508 <&MainEntity as QueryData>::iter_access(state)
509 }
510 }
511
512 unsafe impl IterQueryData for MainEntity {}
514
515 unsafe impl ReadOnlyQueryData for MainEntity {}
517
518 unsafe impl SingleEntityQueryData for MainEntity {}
520
521 impl ArchetypeQueryData for MainEntity {}
522
523 impl ReleaseStateQueryData for MainEntity {
524 fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> {
525 item
526 }
527 }
528}
529
530#[cfg(test)]
531mod tests {
532 use bevy_ecs::{
533 component::Component,
534 entity::Entity,
535 lifecycle::{Add, Remove},
536 observer::On,
537 query::With,
538 system::{Query, ResMut},
539 world::World,
540 };
541
542 use super::{
543 entity_sync_system, EntityRecord, MainEntity, PendingSyncEntity, RenderEntity,
544 SyncToRenderWorld,
545 };
546
547 #[derive(Component)]
548 struct RenderDataComponent;
549
550 #[test]
551 fn sync_world() {
552 let mut main_world = World::new();
553 let mut render_world = World::new();
554 main_world.init_resource::<PendingSyncEntity>();
555
556 main_world.add_observer(
557 |add: On<Add, SyncToRenderWorld>, mut pending: ResMut<PendingSyncEntity>| {
558 pending.push(EntityRecord::Added(add.entity));
559 },
560 );
561 main_world.add_observer(
562 |remove: On<Remove, SyncToRenderWorld>,
563 mut pending: ResMut<PendingSyncEntity>,
564 query: Query<&RenderEntity>| {
565 if let Ok(e) = query.get(remove.entity) {
566 pending.push(EntityRecord::Removed(*e));
567 };
568 },
569 );
570
571 for _ in 0..99 {
573 main_world.spawn_empty();
574 }
575
576 let main_entity = main_world
578 .spawn(RenderDataComponent)
579 .insert(SyncToRenderWorld)
581 .id();
582
583 entity_sync_system(&mut main_world, &mut render_world);
584
585 let mut q = render_world.query_filtered::<Entity, With<MainEntity>>();
586
587 assert!(q.iter(&render_world).count() == 1);
589
590 let render_entity = q.single(&render_world).unwrap();
591 let render_entity_component = main_world.get::<RenderEntity>(main_entity).unwrap();
592
593 assert!(render_entity_component.id() == render_entity);
594
595 let main_entity_component = render_world
596 .get::<MainEntity>(render_entity_component.id())
597 .unwrap();
598
599 assert!(main_entity_component.id() == main_entity);
600
601 main_world.despawn(main_entity);
603
604 entity_sync_system(&mut main_world, &mut render_world);
605
606 assert!(q.iter(&render_world).count() == 0);
608 }
609}