1use core::any::TypeId;
2
3use crate::reflect_utils::clone_reflect_value;
4use crate::{DynamicScene, SceneSpawnError};
5use bevy_asset::Asset;
6use bevy_ecs::{
7 component::ComponentCloneBehavior,
8 entity::{Entity, EntityHashMap, SceneEntityMapper},
9 entity_disabling::DefaultQueryFilters,
10 reflect::{AppTypeRegistry, ReflectComponent, ReflectResource},
11 relationship::RelationshipHookMode,
12 world::World,
13};
14use bevy_reflect::TypePath;
15
16#[derive(Asset, TypePath, Debug)]
22pub struct Scene {
23 pub world: World,
25}
26
27impl Scene {
28 pub fn new(world: World) -> Self {
30 Self { world }
31 }
32
33 pub fn from_dynamic_scene(
35 dynamic_scene: &DynamicScene,
36 type_registry: &AppTypeRegistry,
37 ) -> Result<Scene, SceneSpawnError> {
38 let mut world = World::new();
39 let mut entity_map = EntityHashMap::default();
40 dynamic_scene.write_to_world_with(&mut world, &mut entity_map, type_registry)?;
41
42 Ok(Self { world })
43 }
44
45 pub fn clone_with(&self, type_registry: &AppTypeRegistry) -> Result<Scene, SceneSpawnError> {
50 let mut new_world = World::new();
51 let mut entity_map = EntityHashMap::default();
52 self.write_to_world_with(&mut new_world, &mut entity_map, type_registry)?;
53 Ok(Self { world: new_world })
54 }
55
56 pub fn write_to_world_with(
61 &self,
62 world: &mut World,
63 entity_map: &mut EntityHashMap<Entity>,
64 type_registry: &AppTypeRegistry,
65 ) -> Result<(), SceneSpawnError> {
66 let type_registry = type_registry.read();
67
68 let self_dqf_id = self
69 .world
70 .components()
71 .get_resource_id(TypeId::of::<DefaultQueryFilters>());
72
73 for (component_id, resource_data) in self.world.storages().resources.iter() {
75 if Some(component_id) == self_dqf_id {
76 continue;
77 }
78 if !resource_data.is_present() {
79 continue;
80 }
81
82 let component_info = self
83 .world
84 .components()
85 .get_info(component_id)
86 .expect("component_ids in archetypes should have ComponentInfo");
87
88 let type_id = component_info
89 .type_id()
90 .expect("reflected resources must have a type_id");
91
92 let registration =
93 type_registry
94 .get(type_id)
95 .ok_or_else(|| SceneSpawnError::UnregisteredType {
96 std_type_name: component_info.name().to_string(),
97 })?;
98 let reflect_resource = registration.data::<ReflectResource>().ok_or_else(|| {
99 SceneSpawnError::UnregisteredResource {
100 type_path: registration.type_info().type_path().to_string(),
101 }
102 })?;
103 reflect_resource.copy(&self.world, world, &type_registry);
104 }
105
106 for archetype in self.world.archetypes().iter() {
109 for scene_entity in archetype.entities() {
110 entity_map
111 .entry(scene_entity.id())
112 .or_insert_with(|| world.spawn_empty().id());
113 }
114 }
115
116 for archetype in self.world.archetypes().iter() {
117 for scene_entity in archetype.entities() {
118 let entity = *entity_map
119 .get(&scene_entity.id())
120 .expect("should have previously spawned an entity");
121
122 for component_id in archetype.components() {
123 let component_info = self
124 .world
125 .components()
126 .get_info(component_id)
127 .expect("component_ids in archetypes should have ComponentInfo");
128
129 if *component_info.clone_behavior() == ComponentCloneBehavior::Ignore {
130 continue;
131 }
132
133 let registration = type_registry
134 .get(component_info.type_id().unwrap())
135 .ok_or_else(|| SceneSpawnError::UnregisteredType {
136 std_type_name: component_info.name().to_string(),
137 })?;
138 let reflect_component =
139 registration.data::<ReflectComponent>().ok_or_else(|| {
140 SceneSpawnError::UnregisteredComponent {
141 type_path: registration.type_info().type_path().to_string(),
142 }
143 })?;
144
145 let Some(component) = reflect_component
146 .reflect(self.world.entity(scene_entity.id()))
147 .map(|component| {
148 clone_reflect_value(component.as_partial_reflect(), registration)
149 })
150 else {
151 continue;
152 };
153
154 SceneEntityMapper::world_scope(entity_map, world, |world, mapper| {
157 reflect_component.apply_or_insert_mapped(
158 &mut world.entity_mut(entity),
159 component.as_partial_reflect(),
160 &type_registry,
161 mapper,
162 RelationshipHookMode::Skip,
163 );
164 });
165 }
166 }
167 }
168
169 Ok(())
170 }
171}