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(),
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.iter_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 matches!(
130 *component_info.clone_behavior(),
131 ComponentCloneBehavior::Ignore
132 ) {
133 continue;
134 }
135
136 let registration = type_registry
137 .get(component_info.type_id().unwrap())
138 .ok_or_else(|| SceneSpawnError::UnregisteredType {
139 std_type_name: component_info.name(),
140 })?;
141 let reflect_component =
142 registration.data::<ReflectComponent>().ok_or_else(|| {
143 SceneSpawnError::UnregisteredComponent {
144 type_path: registration.type_info().type_path().to_string(),
145 }
146 })?;
147
148 let Some(component) = reflect_component
149 .reflect(self.world.entity(scene_entity.id()))
150 .map(|component| {
151 clone_reflect_value(component.as_partial_reflect(), registration)
152 })
153 else {
154 continue;
155 };
156
157 SceneEntityMapper::world_scope(entity_map, world, |world, mapper| {
160 reflect_component.apply_or_insert_mapped(
161 &mut world.entity_mut(entity),
162 component.as_partial_reflect(),
163 &type_registry,
164 mapper,
165 RelationshipHookMode::Skip,
166 );
167 });
168 }
169 }
170 }
171
172 Ok(())
173 }
174}