1use crate::{DynamicSceneBuilder, Scene, SceneSpawnError};
2use bevy_asset::Asset;
3use bevy_ecs::reflect::{ReflectMapEntities, ReflectResource};
4use bevy_ecs::{
5 entity::{Entity, EntityHashMap, SceneEntityMapper},
6 reflect::{AppTypeRegistry, ReflectComponent},
7 world::World,
8};
9use bevy_reflect::{PartialReflect, TypePath};
10
11use crate::reflect_utils::clone_reflect_value;
12use bevy_ecs::component::ComponentCloneBehavior;
13use bevy_ecs::relationship::RelationshipHookMode;
14
15#[cfg(feature = "serialize")]
16use {crate::serde::SceneSerializer, bevy_reflect::TypeRegistry, serde::Serialize};
17
18#[derive(Asset, TypePath, Default)]
26pub struct DynamicScene {
27 pub resources: Vec<Box<dyn PartialReflect>>,
29 pub entities: Vec<DynamicEntity>,
31}
32
33pub struct DynamicEntity {
35 pub entity: Entity,
39 pub components: Vec<Box<dyn PartialReflect>>,
42}
43
44impl DynamicScene {
45 pub fn from_scene(scene: &Scene) -> Self {
47 Self::from_world(&scene.world)
48 }
49
50 pub fn from_world(world: &World) -> Self {
52 DynamicSceneBuilder::from_world(world)
53 .extract_entities(
54 world
57 .archetypes()
58 .iter()
59 .flat_map(bevy_ecs::archetype::Archetype::entities)
60 .map(bevy_ecs::archetype::ArchetypeEntity::id),
61 )
62 .extract_resources()
63 .build()
64 }
65
66 pub fn write_to_world_with(
72 &self,
73 world: &mut World,
74 entity_map: &mut EntityHashMap<Entity>,
75 type_registry: &AppTypeRegistry,
76 ) -> Result<(), SceneSpawnError> {
77 let type_registry = type_registry.read();
78
79 for scene_entity in &self.entities {
82 entity_map
86 .entry(scene_entity.entity)
87 .or_insert_with(|| world.spawn_empty().id());
88 }
89
90 for scene_entity in &self.entities {
91 let entity = *entity_map
93 .get(&scene_entity.entity)
94 .expect("should have previously spawned an empty entity");
95
96 for component in &scene_entity.components {
98 let type_info = component.get_represented_type_info().ok_or_else(|| {
99 SceneSpawnError::NoRepresentedType {
100 type_path: component.reflect_type_path().to_string(),
101 }
102 })?;
103 let registration = type_registry.get(type_info.type_id()).ok_or_else(|| {
104 SceneSpawnError::UnregisteredButReflectedType {
105 type_path: type_info.type_path().to_string(),
106 }
107 })?;
108 let reflect_component =
109 registration.data::<ReflectComponent>().ok_or_else(|| {
110 SceneSpawnError::UnregisteredComponent {
111 type_path: type_info.type_path().to_string(),
112 }
113 })?;
114
115 {
116 let component_id = reflect_component.register_component(world);
117 #[expect(unsafe_code, reason = "this is faster")]
119 let component_info =
120 unsafe { world.components().get_info_unchecked(component_id) };
121 if matches!(
122 *component_info.clone_behavior(),
123 ComponentCloneBehavior::Ignore
124 ) {
125 continue;
126 }
127 }
128
129 SceneEntityMapper::world_scope(entity_map, world, |world, mapper| {
130 reflect_component.apply_or_insert_mapped(
131 &mut world.entity_mut(entity),
132 component.as_partial_reflect(),
133 &type_registry,
134 mapper,
135 RelationshipHookMode::Skip,
136 );
137 });
138 }
139 }
140
141 for resource in &self.resources {
144 let type_info = resource.get_represented_type_info().ok_or_else(|| {
145 SceneSpawnError::NoRepresentedType {
146 type_path: resource.reflect_type_path().to_string(),
147 }
148 })?;
149 let registration = type_registry.get(type_info.type_id()).ok_or_else(|| {
150 SceneSpawnError::UnregisteredButReflectedType {
151 type_path: type_info.type_path().to_string(),
152 }
153 })?;
154 let reflect_resource = registration.data::<ReflectResource>().ok_or_else(|| {
155 SceneSpawnError::UnregisteredResource {
156 type_path: type_info.type_path().to_string(),
157 }
158 })?;
159
160 let mut cloned_resource;
163 let partial_reflect_resource = if let Some(map_entities) =
164 registration.data::<ReflectMapEntities>()
165 {
166 cloned_resource = clone_reflect_value(resource.as_partial_reflect(), registration);
167 SceneEntityMapper::world_scope(entity_map, world, |_, mapper| {
168 map_entities.map_entities(cloned_resource.as_partial_reflect_mut(), mapper);
169 });
170 cloned_resource.as_partial_reflect()
171 } else {
172 resource.as_partial_reflect()
173 };
174
175 reflect_resource.apply_or_insert(world, partial_reflect_resource, &type_registry);
178 }
179
180 Ok(())
181 }
182
183 pub fn write_to_world(
189 &self,
190 world: &mut World,
191 entity_map: &mut EntityHashMap<Entity>,
192 ) -> Result<(), SceneSpawnError> {
193 let registry = world.resource::<AppTypeRegistry>().clone();
194 self.write_to_world_with(world, entity_map, ®istry)
195 }
196
197 #[cfg(feature = "serialize")]
206 pub fn serialize(&self, registry: &TypeRegistry) -> Result<String, ron::Error> {
207 serialize_ron(SceneSerializer::new(self, registry))
208 }
209}
210
211#[cfg(feature = "serialize")]
213pub fn serialize_ron<S>(serialize: S) -> Result<String, ron::Error>
214where
215 S: Serialize,
216{
217 let pretty_config = ron::ser::PrettyConfig::default()
218 .indentor(" ".to_string())
219 .new_line("\n".to_string());
220 ron::ser::to_string_pretty(&serialize, pretty_config)
221}
222
223#[cfg(test)]
224mod tests {
225 use bevy_ecs::{
226 component::Component,
227 entity::{Entity, EntityHashMap, EntityMapper, MapEntities},
228 hierarchy::ChildOf,
229 reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities, ReflectResource},
230 resource::Resource,
231 world::World,
232 };
233
234 use bevy_reflect::Reflect;
235
236 use crate::dynamic_scene::DynamicScene;
237 use crate::dynamic_scene_builder::DynamicSceneBuilder;
238
239 #[derive(Resource, Reflect, MapEntities, Debug)]
240 #[reflect(Resource, MapEntities)]
241 struct TestResource {
242 #[entities]
243 entity_a: Entity,
244 #[entities]
245 entity_b: Entity,
246 }
247
248 #[test]
249 fn resource_entity_map_maps_entities() {
250 let type_registry = AppTypeRegistry::default();
251 type_registry.write().register::<TestResource>();
252
253 let mut source_world = World::new();
254 source_world.insert_resource(type_registry.clone());
255
256 let original_entity_a = source_world.spawn_empty().id();
257 let original_entity_b = source_world.spawn_empty().id();
258
259 source_world.insert_resource(TestResource {
260 entity_a: original_entity_a,
261 entity_b: original_entity_b,
262 });
263
264 let scene = DynamicSceneBuilder::from_world(&source_world)
266 .extract_resources()
267 .extract_entity(original_entity_a)
268 .extract_entity(original_entity_b)
269 .build();
270
271 let mut entity_map = EntityHashMap::default();
272 let mut destination_world = World::new();
273 destination_world.insert_resource(type_registry);
274
275 scene
276 .write_to_world(&mut destination_world, &mut entity_map)
277 .unwrap();
278
279 let &from_entity_a = entity_map.get(&original_entity_a).unwrap();
280 let &from_entity_b = entity_map.get(&original_entity_b).unwrap();
281
282 let test_resource = destination_world.get_resource::<TestResource>().unwrap();
283 assert_eq!(from_entity_a, test_resource.entity_a);
284 assert_eq!(from_entity_b, test_resource.entity_b);
285 }
286
287 #[test]
288 fn components_not_defined_in_scene_should_not_be_affected_by_scene_entity_map() {
289 let mut world = World::new();
293 world.init_resource::<AppTypeRegistry>();
294 world
295 .resource_mut::<AppTypeRegistry>()
296 .write()
297 .register::<ChildOf>();
298 let original_parent_entity = world.spawn_empty().id();
299 let original_child_entity = world.spawn_empty().id();
300 world
301 .entity_mut(original_parent_entity)
302 .add_child(original_child_entity);
303
304 let scene = DynamicSceneBuilder::from_world(&world)
307 .extract_entity(original_parent_entity)
308 .extract_entity(original_child_entity)
309 .build();
310 let mut entity_map = EntityHashMap::default();
311 scene.write_to_world(&mut world, &mut entity_map).unwrap();
312
313 let &from_scene_parent_entity = entity_map.get(&original_parent_entity).unwrap();
314 let &from_scene_child_entity = entity_map.get(&original_child_entity).unwrap();
315
316 world
320 .entity_mut(original_child_entity)
321 .add_child(from_scene_parent_entity);
322
323 scene.write_to_world(&mut world, &mut entity_map).unwrap();
327
328 assert_eq!(
329 original_parent_entity,
330 world
331 .get_entity(original_child_entity)
332 .unwrap()
333 .get::<ChildOf>()
334 .unwrap()
335 .parent(),
336 "something about reloading the scene is touching entities with the same scene Ids"
337 );
338 assert_eq!(
339 original_child_entity,
340 world
341 .get_entity(from_scene_parent_entity)
342 .unwrap()
343 .get::<ChildOf>()
344 .unwrap()
345 .parent(),
346 "something about reloading the scene is touching components not defined in the scene but on entities defined in the scene"
347 );
348 assert_eq!(
349 from_scene_parent_entity,
350 world
351 .get_entity(from_scene_child_entity)
352 .unwrap()
353 .get::<ChildOf>()
354 .expect("something is wrong with this test, and the scene components don't have a parent/child relationship")
355 .parent(),
356 "something is wrong with this test or the code reloading scenes since the relationship between scene entities is broken"
357 );
358 }
359
360 #[test]
363 fn no_panic_in_map_entities_after_pending_entity_in_hook() {
364 #[derive(Default, Component, Reflect)]
365 #[reflect(Component)]
366 struct A;
367
368 #[derive(Component, Reflect)]
369 #[reflect(Component)]
370 struct B(pub Entity);
371
372 impl MapEntities for B {
373 fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {
374 self.0 = entity_mapper.get_mapped(self.0);
375 }
376 }
377
378 let reg = AppTypeRegistry::default();
379 {
380 let mut reg_write = reg.write();
381 reg_write.register::<A>();
382 reg_write.register::<B>();
383 }
384
385 let mut scene_world = World::new();
386 scene_world.insert_resource(reg.clone());
387 scene_world.spawn((B(Entity::PLACEHOLDER), A));
388 let scene = DynamicScene::from_world(&scene_world);
389
390 let mut dst_world = World::new();
391 dst_world
392 .register_component_hooks::<A>()
393 .on_add(|mut world, _| {
394 world.commands().spawn_empty();
395 });
396 dst_world.insert_resource(reg.clone());
397
398 scene
403 .write_to_world(&mut dst_world, &mut Default::default())
404 .unwrap();
405 }
406}