Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

27
vendor/bevy_scene/src/components.rs vendored Normal file
View File

@@ -0,0 +1,27 @@
use bevy_asset::Handle;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{component::Component, prelude::ReflectComponent};
use bevy_reflect::{prelude::ReflectDefault, Reflect};
use bevy_transform::components::Transform;
use derive_more::derive::From;
#[cfg(feature = "bevy_render")]
use bevy_render::view::visibility::Visibility;
use crate::{DynamicScene, Scene};
/// Adding this component will spawn the scene as a child of that entity.
/// Once it's spawned, the entity will have a [`SceneInstance`](crate::SceneInstance) component.
#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect, PartialEq, Eq, From)]
#[reflect(Component, Default, Debug, PartialEq, Clone)]
#[require(Transform)]
#[cfg_attr(feature = "bevy_render", require(Visibility))]
pub struct SceneRoot(pub Handle<Scene>);
/// Adding this component will spawn the scene as a child of that entity.
/// Once it's spawned, the entity will have a [`SceneInstance`](crate::SceneInstance) component.
#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect, PartialEq, Eq, From)]
#[reflect(Component, Default, Debug, PartialEq, Clone)]
#[require(Transform)]
#[cfg_attr(feature = "bevy_render", require(Visibility))]
pub struct DynamicSceneRoot(pub Handle<DynamicScene>);

399
vendor/bevy_scene/src/dynamic_scene.rs vendored Normal file
View File

@@ -0,0 +1,399 @@
use crate::{DynamicSceneBuilder, Scene, SceneSpawnError};
use bevy_asset::Asset;
use bevy_ecs::reflect::{ReflectMapEntities, ReflectResource};
use bevy_ecs::{
entity::{Entity, EntityHashMap, SceneEntityMapper},
reflect::{AppTypeRegistry, ReflectComponent},
world::World,
};
use bevy_reflect::{PartialReflect, TypePath};
use crate::reflect_utils::clone_reflect_value;
use bevy_ecs::component::ComponentCloneBehavior;
use bevy_ecs::relationship::RelationshipHookMode;
#[cfg(feature = "serialize")]
use {
crate::{ron, serde::SceneSerializer},
bevy_reflect::TypeRegistry,
serde::Serialize,
};
/// A collection of serializable resources and dynamic entities.
///
/// Each dynamic entity in the collection contains its own run-time defined set of components.
/// To spawn a dynamic scene, you can use either:
/// * [`SceneSpawner::spawn_dynamic`](crate::SceneSpawner::spawn_dynamic)
/// * adding the [`DynamicSceneRoot`](crate::components::DynamicSceneRoot) component to an entity.
/// * using the [`DynamicSceneBuilder`] to construct a `DynamicScene` from `World`.
#[derive(Asset, TypePath, Default)]
pub struct DynamicScene {
/// Resources stored in the dynamic scene.
pub resources: Vec<Box<dyn PartialReflect>>,
/// Entities contained in the dynamic scene.
pub entities: Vec<DynamicEntity>,
}
/// A reflection-powered serializable representation of an entity and its components.
pub struct DynamicEntity {
/// The identifier of the entity, unique within a scene (and the world it may have been generated from).
///
/// Components that reference this entity must consistently use this identifier.
pub entity: Entity,
/// A vector of boxed components that belong to the given entity and
/// implement the [`PartialReflect`] trait.
pub components: Vec<Box<dyn PartialReflect>>,
}
impl DynamicScene {
/// Create a new dynamic scene from a given scene.
pub fn from_scene(scene: &Scene) -> Self {
Self::from_world(&scene.world)
}
/// Create a new dynamic scene from a given world.
pub fn from_world(world: &World) -> Self {
DynamicSceneBuilder::from_world(world)
.extract_entities(world.iter_entities().map(|entity| entity.id()))
.extract_resources()
.build()
}
/// Write the resources, the dynamic entities, and their corresponding components to the given world.
///
/// This method will return a [`SceneSpawnError`] if a type either is not registered
/// in the provided [`AppTypeRegistry`] resource, or doesn't reflect the
/// [`Component`](bevy_ecs::component::Component) or [`Resource`](bevy_ecs::prelude::Resource) trait.
pub fn write_to_world_with(
&self,
world: &mut World,
entity_map: &mut EntityHashMap<Entity>,
type_registry: &AppTypeRegistry,
) -> Result<(), SceneSpawnError> {
let type_registry = type_registry.read();
// First ensure that every entity in the scene has a corresponding world
// entity in the entity map.
for scene_entity in &self.entities {
// Fetch the entity with the given entity id from the `entity_map`
// or spawn a new entity with a transiently unique id if there is
// no corresponding entry.
entity_map
.entry(scene_entity.entity)
.or_insert_with(|| world.spawn_empty().id());
}
for scene_entity in &self.entities {
// Fetch the entity with the given entity id from the `entity_map`.
let entity = *entity_map
.get(&scene_entity.entity)
.expect("should have previously spawned an empty entity");
// Apply/ add each component to the given entity.
for component in &scene_entity.components {
let type_info = component.get_represented_type_info().ok_or_else(|| {
SceneSpawnError::NoRepresentedType {
type_path: component.reflect_type_path().to_string(),
}
})?;
let registration = type_registry.get(type_info.type_id()).ok_or_else(|| {
SceneSpawnError::UnregisteredButReflectedType {
type_path: type_info.type_path().to_string(),
}
})?;
let reflect_component =
registration.data::<ReflectComponent>().ok_or_else(|| {
SceneSpawnError::UnregisteredComponent {
type_path: type_info.type_path().to_string(),
}
})?;
{
let component_id = reflect_component.register_component(world);
// SAFETY: we registered the component above. the info exists
#[expect(unsafe_code, reason = "this is faster")]
let component_info =
unsafe { world.components().get_info_unchecked(component_id) };
if *component_info.clone_behavior() == ComponentCloneBehavior::Ignore {
continue;
}
}
SceneEntityMapper::world_scope(entity_map, world, |world, mapper| {
reflect_component.apply_or_insert_mapped(
&mut world.entity_mut(entity),
component.as_partial_reflect(),
&type_registry,
mapper,
RelationshipHookMode::Skip,
);
});
}
}
// Insert resources after all entities have been added to the world.
// This ensures the entities are available for the resources to reference during mapping.
for resource in &self.resources {
let type_info = resource.get_represented_type_info().ok_or_else(|| {
SceneSpawnError::NoRepresentedType {
type_path: resource.reflect_type_path().to_string(),
}
})?;
let registration = type_registry.get(type_info.type_id()).ok_or_else(|| {
SceneSpawnError::UnregisteredButReflectedType {
type_path: type_info.type_path().to_string(),
}
})?;
let reflect_resource = registration.data::<ReflectResource>().ok_or_else(|| {
SceneSpawnError::UnregisteredResource {
type_path: type_info.type_path().to_string(),
}
})?;
// If this component references entities in the scene, update
// them to the entities in the world.
let mut cloned_resource;
let partial_reflect_resource = if let Some(map_entities) =
registration.data::<ReflectMapEntities>()
{
cloned_resource = clone_reflect_value(resource.as_partial_reflect(), registration);
SceneEntityMapper::world_scope(entity_map, world, |_, mapper| {
map_entities.map_entities(cloned_resource.as_partial_reflect_mut(), mapper);
});
cloned_resource.as_partial_reflect()
} else {
resource.as_partial_reflect()
};
// If the world already contains an instance of the given resource
// just apply the (possibly) new value, otherwise insert the resource
reflect_resource.apply_or_insert(world, partial_reflect_resource, &type_registry);
}
Ok(())
}
/// Write the resources, the dynamic entities, and their corresponding components to the given world.
///
/// This method will return a [`SceneSpawnError`] if a type either is not registered
/// in the world's [`AppTypeRegistry`] resource, or doesn't reflect the
/// [`Component`](bevy_ecs::component::Component) trait.
pub fn write_to_world(
&self,
world: &mut World,
entity_map: &mut EntityHashMap<Entity>,
) -> Result<(), SceneSpawnError> {
let registry = world.resource::<AppTypeRegistry>().clone();
self.write_to_world_with(world, entity_map, &registry)
}
// TODO: move to AssetSaver when it is implemented
/// Serialize this dynamic scene into the official Bevy scene format (`.scn` / `.scn.ron`).
///
/// The Bevy scene format is based on [Rusty Object Notation (RON)]. It describes the scene
/// in a human-friendly format. To deserialize the scene, use the [`SceneLoader`].
///
/// [`SceneLoader`]: crate::SceneLoader
/// [Rusty Object Notation (RON)]: https://crates.io/crates/ron
#[cfg(feature = "serialize")]
pub fn serialize(&self, registry: &TypeRegistry) -> Result<String, ron::Error> {
serialize_ron(SceneSerializer::new(self, registry))
}
}
/// Serialize a given Rust data structure into rust object notation (ron).
#[cfg(feature = "serialize")]
pub fn serialize_ron<S>(serialize: S) -> Result<String, ron::Error>
where
S: Serialize,
{
let pretty_config = ron::ser::PrettyConfig::default()
.indentor(" ".to_string())
.new_line("\n".to_string());
ron::ser::to_string_pretty(&serialize, pretty_config)
}
#[cfg(test)]
mod tests {
use bevy_ecs::{
component::Component,
entity::{Entity, EntityHashMap, EntityMapper, MapEntities},
hierarchy::ChildOf,
reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities, ReflectResource},
resource::Resource,
world::World,
};
use bevy_reflect::Reflect;
use crate::dynamic_scene::DynamicScene;
use crate::dynamic_scene_builder::DynamicSceneBuilder;
#[derive(Resource, Reflect, MapEntities, Debug)]
#[reflect(Resource, MapEntities)]
struct TestResource {
#[entities]
entity_a: Entity,
#[entities]
entity_b: Entity,
}
#[test]
fn resource_entity_map_maps_entities() {
let type_registry = AppTypeRegistry::default();
type_registry.write().register::<TestResource>();
let mut source_world = World::new();
source_world.insert_resource(type_registry.clone());
let original_entity_a = source_world.spawn_empty().id();
let original_entity_b = source_world.spawn_empty().id();
source_world.insert_resource(TestResource {
entity_a: original_entity_a,
entity_b: original_entity_b,
});
// Write the scene.
let scene = DynamicSceneBuilder::from_world(&source_world)
.extract_resources()
.extract_entity(original_entity_a)
.extract_entity(original_entity_b)
.build();
let mut entity_map = EntityHashMap::default();
let mut destination_world = World::new();
destination_world.insert_resource(type_registry);
scene
.write_to_world(&mut destination_world, &mut entity_map)
.unwrap();
let &from_entity_a = entity_map.get(&original_entity_a).unwrap();
let &from_entity_b = entity_map.get(&original_entity_b).unwrap();
let test_resource = destination_world.get_resource::<TestResource>().unwrap();
assert_eq!(from_entity_a, test_resource.entity_a);
assert_eq!(from_entity_b, test_resource.entity_b);
}
#[test]
fn components_not_defined_in_scene_should_not_be_affected_by_scene_entity_map() {
// Testing that scene reloading applies EntityMap correctly to MapEntities components.
// First, we create a simple world with a parent and a child relationship
let mut world = World::new();
world.init_resource::<AppTypeRegistry>();
world
.resource_mut::<AppTypeRegistry>()
.write()
.register::<ChildOf>();
let original_parent_entity = world.spawn_empty().id();
let original_child_entity = world.spawn_empty().id();
world
.entity_mut(original_parent_entity)
.add_child(original_child_entity);
// We then write this relationship to a new scene, and then write that scene back to the
// world to create another parent and child relationship
let scene = DynamicSceneBuilder::from_world(&world)
.extract_entity(original_parent_entity)
.extract_entity(original_child_entity)
.build();
let mut entity_map = EntityHashMap::default();
scene.write_to_world(&mut world, &mut entity_map).unwrap();
let &from_scene_parent_entity = entity_map.get(&original_parent_entity).unwrap();
let &from_scene_child_entity = entity_map.get(&original_child_entity).unwrap();
// We then add the parent from the scene as a child of the original child
// Hierarchy should look like:
// Original Parent <- Original Child <- Scene Parent <- Scene Child
world
.entity_mut(original_child_entity)
.add_child(from_scene_parent_entity);
// We then reload the scene to make sure that from_scene_parent_entity's parent component
// isn't updated with the entity map, since this component isn't defined in the scene.
// With [`bevy_ecs::hierarchy`], this can cause serious errors and malformed hierarchies.
scene.write_to_world(&mut world, &mut entity_map).unwrap();
assert_eq!(
original_parent_entity,
world
.get_entity(original_child_entity)
.unwrap()
.get::<ChildOf>()
.unwrap()
.parent(),
"something about reloading the scene is touching entities with the same scene Ids"
);
assert_eq!(
original_child_entity,
world
.get_entity(from_scene_parent_entity)
.unwrap()
.get::<ChildOf>()
.unwrap()
.parent(),
"something about reloading the scene is touching components not defined in the scene but on entities defined in the scene"
);
assert_eq!(
from_scene_parent_entity,
world
.get_entity(from_scene_child_entity)
.unwrap()
.get::<ChildOf>()
.expect("something is wrong with this test, and the scene components don't have a parent/child relationship")
.parent(),
"something is wrong with this test or the code reloading scenes since the relationship between scene entities is broken"
);
}
// Regression test for https://github.com/bevyengine/bevy/issues/14300
// Fails before the fix in https://github.com/bevyengine/bevy/pull/15405
#[test]
fn no_panic_in_map_entities_after_pending_entity_in_hook() {
#[derive(Default, Component, Reflect)]
#[reflect(Component)]
struct A;
#[derive(Component, Reflect)]
#[reflect(Component)]
struct B(pub Entity);
impl MapEntities for B {
fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {
self.0 = entity_mapper.get_mapped(self.0);
}
}
let reg = AppTypeRegistry::default();
{
let mut reg_write = reg.write();
reg_write.register::<A>();
reg_write.register::<B>();
}
let mut scene_world = World::new();
scene_world.insert_resource(reg.clone());
scene_world.spawn((B(Entity::PLACEHOLDER), A));
let scene = DynamicScene::from_world(&scene_world);
let mut dst_world = World::new();
dst_world
.register_component_hooks::<A>()
.on_add(|mut world, _| {
world.commands().spawn_empty();
});
dst_world.insert_resource(reg.clone());
// Should not panic.
// Prior to fix, the `Entities::alloc` call in
// `EntityMapper::map_entity` would panic due to pending entities from the observer
// not having been flushed.
scene
.write_to_world(&mut dst_world, &mut Default::default())
.unwrap();
}
}

View File

@@ -0,0 +1,738 @@
use core::any::TypeId;
use crate::reflect_utils::clone_reflect_value;
use crate::{DynamicEntity, DynamicScene, SceneFilter};
use alloc::collections::BTreeMap;
use bevy_ecs::{
component::{Component, ComponentId},
entity_disabling::DefaultQueryFilters,
prelude::Entity,
reflect::{AppTypeRegistry, ReflectComponent, ReflectResource},
resource::Resource,
world::World,
};
use bevy_reflect::PartialReflect;
use bevy_utils::default;
/// A [`DynamicScene`] builder, used to build a scene from a [`World`] by extracting some entities and resources.
///
/// # Component Extraction
///
/// By default, all components registered with [`ReflectComponent`] type data in a world's [`AppTypeRegistry`] will be extracted.
/// (this type data is added automatically during registration if [`Reflect`] is derived with the `#[reflect(Component)]` attribute).
/// This can be changed by [specifying a filter](DynamicSceneBuilder::with_component_filter) or by explicitly
/// [allowing](DynamicSceneBuilder::allow_component)/[denying](DynamicSceneBuilder::deny_component) certain components.
///
/// Extraction happens immediately and uses the filter as it exists during the time of extraction.
///
/// # Resource Extraction
///
/// By default, all resources registered with [`ReflectResource`] type data in a world's [`AppTypeRegistry`] will be extracted.
/// (this type data is added automatically during registration if [`Reflect`] is derived with the `#[reflect(Resource)]` attribute).
/// This can be changed by [specifying a filter](DynamicSceneBuilder::with_resource_filter) or by explicitly
/// [allowing](DynamicSceneBuilder::allow_resource)/[denying](DynamicSceneBuilder::deny_resource) certain resources.
///
/// Extraction happens immediately and uses the filter as it exists during the time of extraction.
///
/// # Entity Order
///
/// Extracted entities will always be stored in ascending order based on their [index](Entity::index).
/// This means that inserting `Entity(1v0)` then `Entity(0v0)` will always result in the entities
/// being ordered as `[Entity(0v0), Entity(1v0)]`.
///
/// # Example
/// ```
/// # use bevy_scene::DynamicSceneBuilder;
/// # use bevy_ecs::reflect::AppTypeRegistry;
/// # use bevy_ecs::{
/// # component::Component, prelude::Entity, query::With, reflect::ReflectComponent, world::World,
/// # };
/// # use bevy_reflect::Reflect;
/// # #[derive(Component, Reflect, Default, Eq, PartialEq, Debug)]
/// # #[reflect(Component)]
/// # struct ComponentA;
/// # let mut world = World::default();
/// # world.init_resource::<AppTypeRegistry>();
/// # let entity = world.spawn(ComponentA).id();
/// let dynamic_scene = DynamicSceneBuilder::from_world(&world).extract_entity(entity).build();
/// ```
///
/// [`Reflect`]: bevy_reflect::Reflect
pub struct DynamicSceneBuilder<'w> {
extracted_resources: BTreeMap<ComponentId, Box<dyn PartialReflect>>,
extracted_scene: BTreeMap<Entity, DynamicEntity>,
component_filter: SceneFilter,
resource_filter: SceneFilter,
original_world: &'w World,
}
impl<'w> DynamicSceneBuilder<'w> {
/// Prepare a builder that will extract entities and their component from the given [`World`].
pub fn from_world(world: &'w World) -> Self {
Self {
extracted_resources: default(),
extracted_scene: default(),
component_filter: SceneFilter::default(),
resource_filter: SceneFilter::default(),
original_world: world,
}
}
/// Specify a custom component [`SceneFilter`] to be used with this builder.
#[must_use]
pub fn with_component_filter(mut self, filter: SceneFilter) -> Self {
self.component_filter = filter;
self
}
/// Specify a custom resource [`SceneFilter`] to be used with this builder.
#[must_use]
pub fn with_resource_filter(mut self, filter: SceneFilter) -> Self {
self.resource_filter = filter;
self
}
/// Updates the filter to allow all component and resource types.
///
/// This is useful for resetting the filter so that types may be selectively denied
/// with [`deny_component`](`Self::deny_component`) and [`deny_resource`](`Self::deny_resource`).
pub fn allow_all(mut self) -> Self {
self.component_filter = SceneFilter::allow_all();
self.resource_filter = SceneFilter::allow_all();
self
}
/// Updates the filter to deny all component and resource types.
///
/// This is useful for resetting the filter so that types may be selectively allowed
/// with [`allow_component`](`Self::allow_component`) and [`allow_resource`](`Self::allow_resource`).
pub fn deny_all(mut self) -> Self {
self.component_filter = SceneFilter::deny_all();
self.resource_filter = SceneFilter::deny_all();
self
}
/// Allows the given component type, `T`, to be included in the generated scene.
///
/// This method may be called multiple times for any number of components.
///
/// This is the inverse of [`deny_component`](Self::deny_component).
/// If `T` has already been denied, then it will be removed from the denylist.
#[must_use]
pub fn allow_component<T: Component>(mut self) -> Self {
self.component_filter = self.component_filter.allow::<T>();
self
}
/// Denies the given component type, `T`, from being included in the generated scene.
///
/// This method may be called multiple times for any number of components.
///
/// This is the inverse of [`allow_component`](Self::allow_component).
/// If `T` has already been allowed, then it will be removed from the allowlist.
#[must_use]
pub fn deny_component<T: Component>(mut self) -> Self {
self.component_filter = self.component_filter.deny::<T>();
self
}
/// Updates the filter to allow all component types.
///
/// This is useful for resetting the filter so that types may be selectively [denied].
///
/// [denied]: Self::deny_component
#[must_use]
pub fn allow_all_components(mut self) -> Self {
self.component_filter = SceneFilter::allow_all();
self
}
/// Updates the filter to deny all component types.
///
/// This is useful for resetting the filter so that types may be selectively [allowed].
///
/// [allowed]: Self::allow_component
#[must_use]
pub fn deny_all_components(mut self) -> Self {
self.component_filter = SceneFilter::deny_all();
self
}
/// Allows the given resource type, `T`, to be included in the generated scene.
///
/// This method may be called multiple times for any number of resources.
///
/// This is the inverse of [`deny_resource`](Self::deny_resource).
/// If `T` has already been denied, then it will be removed from the denylist.
#[must_use]
pub fn allow_resource<T: Resource>(mut self) -> Self {
self.resource_filter = self.resource_filter.allow::<T>();
self
}
/// Denies the given resource type, `T`, from being included in the generated scene.
///
/// This method may be called multiple times for any number of resources.
///
/// This is the inverse of [`allow_resource`](Self::allow_resource).
/// If `T` has already been allowed, then it will be removed from the allowlist.
#[must_use]
pub fn deny_resource<T: Resource>(mut self) -> Self {
self.resource_filter = self.resource_filter.deny::<T>();
self
}
/// Updates the filter to allow all resource types.
///
/// This is useful for resetting the filter so that types may be selectively [denied].
///
/// [denied]: Self::deny_resource
#[must_use]
pub fn allow_all_resources(mut self) -> Self {
self.resource_filter = SceneFilter::allow_all();
self
}
/// Updates the filter to deny all resource types.
///
/// This is useful for resetting the filter so that types may be selectively [allowed].
///
/// [allowed]: Self::allow_resource
#[must_use]
pub fn deny_all_resources(mut self) -> Self {
self.resource_filter = SceneFilter::deny_all();
self
}
/// Consume the builder, producing a [`DynamicScene`].
///
/// To make sure the dynamic scene doesn't contain entities without any components, call
/// [`Self::remove_empty_entities`] before building the scene.
#[must_use]
pub fn build(self) -> DynamicScene {
DynamicScene {
resources: self.extracted_resources.into_values().collect(),
entities: self.extracted_scene.into_values().collect(),
}
}
/// Extract one entity from the builder's [`World`].
///
/// Re-extracting an entity that was already extracted will have no effect.
#[must_use]
pub fn extract_entity(self, entity: Entity) -> Self {
self.extract_entities(core::iter::once(entity))
}
/// Despawns all entities with no components.
///
/// These were likely created because none of their components were present in the provided type registry upon extraction.
#[must_use]
pub fn remove_empty_entities(mut self) -> Self {
self.extracted_scene
.retain(|_, entity| !entity.components.is_empty());
self
}
/// Extract entities from the builder's [`World`].
///
/// Re-extracting an entity that was already extracted will have no effect.
///
/// To control which components are extracted, use the [`allow`] or
/// [`deny`] helper methods.
///
/// This method may be used to extract entities from a query:
/// ```
/// # use bevy_scene::DynamicSceneBuilder;
/// # use bevy_ecs::reflect::AppTypeRegistry;
/// # use bevy_ecs::{
/// # component::Component, prelude::Entity, query::With, reflect::ReflectComponent, world::World,
/// # };
/// # use bevy_reflect::Reflect;
/// #[derive(Component, Default, Reflect)]
/// #[reflect(Component)]
/// struct MyComponent;
///
/// # let mut world = World::default();
/// # world.init_resource::<AppTypeRegistry>();
/// # let _entity = world.spawn(MyComponent).id();
/// let mut query = world.query_filtered::<Entity, With<MyComponent>>();
///
/// let scene = DynamicSceneBuilder::from_world(&world)
/// .extract_entities(query.iter(&world))
/// .build();
/// ```
///
/// Note that components extracted from queried entities must still pass through the filter if one is set.
///
/// [`allow`]: Self::allow_component
/// [`deny`]: Self::deny_component
#[must_use]
pub fn extract_entities(mut self, entities: impl Iterator<Item = Entity>) -> Self {
let type_registry = self.original_world.resource::<AppTypeRegistry>().read();
for entity in entities {
if self.extracted_scene.contains_key(&entity) {
continue;
}
let mut entry = DynamicEntity {
entity,
components: Vec::new(),
};
let original_entity = self.original_world.entity(entity);
for component_id in original_entity.archetype().components() {
let mut extract_and_push = || {
let type_id = self
.original_world
.components()
.get_info(component_id)?
.type_id()?;
let is_denied = self.component_filter.is_denied_by_id(type_id);
if is_denied {
// Component is either in the denylist or _not_ in the allowlist
return None;
}
let type_registration = type_registry.get(type_id)?;
let component = type_registration
.data::<ReflectComponent>()?
.reflect(original_entity)?;
let component =
clone_reflect_value(component.as_partial_reflect(), type_registration);
entry.components.push(component);
Some(())
};
extract_and_push();
}
self.extracted_scene.insert(entity, entry);
}
self
}
/// Extract resources from the builder's [`World`].
///
/// Re-extracting a resource that was already extracted will have no effect.
///
/// To control which resources are extracted, use the [`allow_resource`] or
/// [`deny_resource`] helper methods.
///
/// ```
/// # use bevy_scene::DynamicSceneBuilder;
/// # use bevy_ecs::reflect::AppTypeRegistry;
/// # use bevy_ecs::prelude::{ReflectResource, Resource, World};
/// # use bevy_reflect::Reflect;
/// #[derive(Resource, Default, Reflect)]
/// #[reflect(Resource)]
/// struct MyResource;
///
/// # let mut world = World::default();
/// # world.init_resource::<AppTypeRegistry>();
/// world.insert_resource(MyResource);
///
/// let mut builder = DynamicSceneBuilder::from_world(&world).extract_resources();
/// let scene = builder.build();
/// ```
///
/// [`allow_resource`]: Self::allow_resource
/// [`deny_resource`]: Self::deny_resource
#[must_use]
pub fn extract_resources(mut self) -> Self {
// Don't extract the DefaultQueryFilters resource
let original_world_dqf_id = self
.original_world
.components()
.get_resource_id(TypeId::of::<DefaultQueryFilters>());
let type_registry = self.original_world.resource::<AppTypeRegistry>().read();
for (component_id, _) in self.original_world.storages().resources.iter() {
if Some(component_id) == original_world_dqf_id {
continue;
}
let mut extract_and_push = || {
let type_id = self
.original_world
.components()
.get_info(component_id)?
.type_id()?;
let is_denied = self.resource_filter.is_denied_by_id(type_id);
if is_denied {
// Resource is either in the denylist or _not_ in the allowlist
return None;
}
let type_registration = type_registry.get(type_id)?;
let resource = type_registration
.data::<ReflectResource>()?
.reflect(self.original_world)
.ok()?;
let resource =
clone_reflect_value(resource.as_partial_reflect(), type_registration);
self.extracted_resources.insert(component_id, resource);
Some(())
};
extract_and_push();
}
drop(type_registry);
self
}
}
#[cfg(test)]
mod tests {
use bevy_ecs::{
component::Component,
prelude::{Entity, Resource},
query::With,
reflect::{AppTypeRegistry, ReflectComponent, ReflectResource},
world::World,
};
use bevy_reflect::Reflect;
use super::DynamicSceneBuilder;
#[derive(Component, Reflect, Default, Eq, PartialEq, Debug)]
#[reflect(Component)]
struct ComponentA;
#[derive(Component, Reflect, Default, Eq, PartialEq, Debug)]
#[reflect(Component)]
struct ComponentB;
#[derive(Resource, Reflect, Default, Eq, PartialEq, Debug)]
#[reflect(Resource)]
struct ResourceA;
#[derive(Resource, Reflect, Default, Eq, PartialEq, Debug)]
#[reflect(Resource)]
struct ResourceB;
#[test]
fn extract_one_entity() {
let mut world = World::default();
let atr = AppTypeRegistry::default();
atr.write().register::<ComponentA>();
world.insert_resource(atr);
let entity = world.spawn((ComponentA, ComponentB)).id();
let scene = DynamicSceneBuilder::from_world(&world)
.extract_entity(entity)
.build();
assert_eq!(scene.entities.len(), 1);
assert_eq!(scene.entities[0].entity, entity);
assert_eq!(scene.entities[0].components.len(), 1);
assert!(scene.entities[0].components[0].represents::<ComponentA>());
}
#[test]
fn extract_one_entity_twice() {
let mut world = World::default();
let atr = AppTypeRegistry::default();
atr.write().register::<ComponentA>();
world.insert_resource(atr);
let entity = world.spawn((ComponentA, ComponentB)).id();
let scene = DynamicSceneBuilder::from_world(&world)
.extract_entity(entity)
.extract_entity(entity)
.build();
assert_eq!(scene.entities.len(), 1);
assert_eq!(scene.entities[0].entity, entity);
assert_eq!(scene.entities[0].components.len(), 1);
assert!(scene.entities[0].components[0].represents::<ComponentA>());
}
#[test]
fn extract_one_entity_two_components() {
let mut world = World::default();
let atr = AppTypeRegistry::default();
{
let mut register = atr.write();
register.register::<ComponentA>();
register.register::<ComponentB>();
}
world.insert_resource(atr);
let entity = world.spawn((ComponentA, ComponentB)).id();
let scene = DynamicSceneBuilder::from_world(&world)
.extract_entity(entity)
.build();
assert_eq!(scene.entities.len(), 1);
assert_eq!(scene.entities[0].entity, entity);
assert_eq!(scene.entities[0].components.len(), 2);
assert!(scene.entities[0].components[0].represents::<ComponentA>());
assert!(scene.entities[0].components[1].represents::<ComponentB>());
}
#[test]
fn extract_entity_order() {
let mut world = World::default();
world.init_resource::<AppTypeRegistry>();
// Spawn entities in order
let entity_a = world.spawn_empty().id();
let entity_b = world.spawn_empty().id();
let entity_c = world.spawn_empty().id();
let entity_d = world.spawn_empty().id();
// Insert entities out of order
let builder = DynamicSceneBuilder::from_world(&world)
.extract_entity(entity_b)
.extract_entities([entity_d, entity_a].into_iter())
.extract_entity(entity_c);
let mut entities = builder.build().entities.into_iter();
// Assert entities are ordered
assert_eq!(entity_a, entities.next().map(|e| e.entity).unwrap());
assert_eq!(entity_b, entities.next().map(|e| e.entity).unwrap());
assert_eq!(entity_c, entities.next().map(|e| e.entity).unwrap());
assert_eq!(entity_d, entities.next().map(|e| e.entity).unwrap());
}
#[test]
fn extract_query() {
let mut world = World::default();
let atr = AppTypeRegistry::default();
{
let mut register = atr.write();
register.register::<ComponentA>();
register.register::<ComponentB>();
}
world.insert_resource(atr);
let entity_a_b = world.spawn((ComponentA, ComponentB)).id();
let entity_a = world.spawn(ComponentA).id();
let _entity_b = world.spawn(ComponentB).id();
let mut query = world.query_filtered::<Entity, With<ComponentA>>();
let scene = DynamicSceneBuilder::from_world(&world)
.extract_entities(query.iter(&world))
.build();
assert_eq!(scene.entities.len(), 2);
let mut scene_entities = vec![scene.entities[0].entity, scene.entities[1].entity];
scene_entities.sort();
assert_eq!(scene_entities, [entity_a_b, entity_a]);
}
#[test]
fn remove_componentless_entity() {
let mut world = World::default();
let atr = AppTypeRegistry::default();
atr.write().register::<ComponentA>();
world.insert_resource(atr);
let entity_a = world.spawn(ComponentA).id();
let entity_b = world.spawn(ComponentB).id();
let scene = DynamicSceneBuilder::from_world(&world)
.extract_entities([entity_a, entity_b].into_iter())
.remove_empty_entities()
.build();
assert_eq!(scene.entities.len(), 1);
assert_eq!(scene.entities[0].entity, entity_a);
}
#[test]
fn extract_one_resource() {
let mut world = World::default();
let atr = AppTypeRegistry::default();
atr.write().register::<ResourceA>();
world.insert_resource(atr);
world.insert_resource(ResourceA);
let scene = DynamicSceneBuilder::from_world(&world)
.extract_resources()
.build();
assert_eq!(scene.resources.len(), 1);
assert!(scene.resources[0].represents::<ResourceA>());
}
#[test]
fn extract_one_resource_twice() {
let mut world = World::default();
let atr = AppTypeRegistry::default();
atr.write().register::<ResourceA>();
world.insert_resource(atr);
world.insert_resource(ResourceA);
let scene = DynamicSceneBuilder::from_world(&world)
.extract_resources()
.extract_resources()
.build();
assert_eq!(scene.resources.len(), 1);
assert!(scene.resources[0].represents::<ResourceA>());
}
#[test]
fn should_extract_allowed_components() {
let mut world = World::default();
let atr = AppTypeRegistry::default();
{
let mut register = atr.write();
register.register::<ComponentA>();
register.register::<ComponentB>();
}
world.insert_resource(atr);
let entity_a_b = world.spawn((ComponentA, ComponentB)).id();
let entity_a = world.spawn(ComponentA).id();
let entity_b = world.spawn(ComponentB).id();
let scene = DynamicSceneBuilder::from_world(&world)
.allow_component::<ComponentA>()
.extract_entities([entity_a_b, entity_a, entity_b].into_iter())
.build();
assert_eq!(scene.entities.len(), 3);
assert!(scene.entities[0].components[0].represents::<ComponentA>());
assert!(scene.entities[1].components[0].represents::<ComponentA>());
assert_eq!(scene.entities[2].components.len(), 0);
}
#[test]
fn should_not_extract_denied_components() {
let mut world = World::default();
let atr = AppTypeRegistry::default();
{
let mut register = atr.write();
register.register::<ComponentA>();
register.register::<ComponentB>();
}
world.insert_resource(atr);
let entity_a_b = world.spawn((ComponentA, ComponentB)).id();
let entity_a = world.spawn(ComponentA).id();
let entity_b = world.spawn(ComponentB).id();
let scene = DynamicSceneBuilder::from_world(&world)
.deny_component::<ComponentA>()
.extract_entities([entity_a_b, entity_a, entity_b].into_iter())
.build();
assert_eq!(scene.entities.len(), 3);
assert!(scene.entities[0].components[0].represents::<ComponentB>());
assert_eq!(scene.entities[1].components.len(), 0);
assert!(scene.entities[2].components[0].represents::<ComponentB>());
}
#[test]
fn should_extract_allowed_resources() {
let mut world = World::default();
let atr = AppTypeRegistry::default();
{
let mut register = atr.write();
register.register::<ResourceA>();
register.register::<ResourceB>();
}
world.insert_resource(atr);
world.insert_resource(ResourceA);
world.insert_resource(ResourceB);
let scene = DynamicSceneBuilder::from_world(&world)
.allow_resource::<ResourceA>()
.extract_resources()
.build();
assert_eq!(scene.resources.len(), 1);
assert!(scene.resources[0].represents::<ResourceA>());
}
#[test]
fn should_not_extract_denied_resources() {
let mut world = World::default();
let atr = AppTypeRegistry::default();
{
let mut register = atr.write();
register.register::<ResourceA>();
register.register::<ResourceB>();
}
world.insert_resource(atr);
world.insert_resource(ResourceA);
world.insert_resource(ResourceB);
let scene = DynamicSceneBuilder::from_world(&world)
.deny_resource::<ResourceA>()
.extract_resources()
.build();
assert_eq!(scene.resources.len(), 1);
assert!(scene.resources[0].represents::<ResourceB>());
}
#[test]
fn should_use_from_reflect() {
#[derive(Resource, Component, Reflect)]
#[reflect(Resource, Component)]
struct SomeType(i32);
let mut world = World::default();
let atr = AppTypeRegistry::default();
{
let mut register = atr.write();
register.register::<SomeType>();
}
world.insert_resource(atr);
world.insert_resource(SomeType(123));
let entity = world.spawn(SomeType(123)).id();
let scene = DynamicSceneBuilder::from_world(&world)
.extract_resources()
.extract_entities(vec![entity].into_iter())
.build();
let component = &scene.entities[0].components[0];
assert!(component
.try_as_reflect()
.expect("component should be concrete due to `FromReflect`")
.is::<SomeType>());
let resource = &scene.resources[0];
assert!(resource
.try_as_reflect()
.expect("resource should be concrete due to `FromReflect`")
.is::<SomeType>());
}
}

109
vendor/bevy_scene/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,109 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
)]
//! Provides scene definition, instantiation and serialization/deserialization.
//!
//! Scenes are collections of entities and their associated components that can be
//! instantiated or removed from a world to allow composition. Scenes can be serialized/deserialized,
//! for example to save part of the world state to a file.
extern crate alloc;
mod components;
mod dynamic_scene;
mod dynamic_scene_builder;
mod reflect_utils;
mod scene;
mod scene_filter;
mod scene_loader;
mod scene_spawner;
#[cfg(feature = "serialize")]
pub mod serde;
/// Rusty Object Notation, a crate used to serialize and deserialize bevy scenes.
pub use bevy_asset::ron;
pub use components::*;
pub use dynamic_scene::*;
pub use dynamic_scene_builder::*;
pub use scene::*;
pub use scene_filter::*;
pub use scene_loader::*;
pub use scene_spawner::*;
/// The scene prelude.
///
/// This includes the most common types in this crate, re-exported for your convenience.
pub mod prelude {
#[doc(hidden)]
pub use crate::{
DynamicScene, DynamicSceneBuilder, DynamicSceneRoot, Scene, SceneFilter, SceneRoot,
SceneSpawner,
};
}
use bevy_app::prelude::*;
#[cfg(feature = "serialize")]
use {bevy_asset::AssetApp, bevy_ecs::schedule::IntoScheduleConfigs};
/// Plugin that provides scene functionality to an [`App`].
#[derive(Default)]
pub struct ScenePlugin;
#[cfg(feature = "serialize")]
impl Plugin for ScenePlugin {
fn build(&self, app: &mut App) {
app.init_asset::<DynamicScene>()
.init_asset::<Scene>()
.init_asset_loader::<SceneLoader>()
.init_resource::<SceneSpawner>()
.register_type::<SceneRoot>()
.register_type::<DynamicSceneRoot>()
.add_systems(SpawnScene, (scene_spawner, scene_spawner_system).chain());
// Register component hooks for DynamicSceneRoot
app.world_mut()
.register_component_hooks::<DynamicSceneRoot>()
.on_remove(|mut world, context| {
let Some(handle) = world.get::<DynamicSceneRoot>(context.entity) else {
return;
};
let id = handle.id();
if let Some(&SceneInstance(scene_instance)) =
world.get::<SceneInstance>(context.entity)
{
let Some(mut scene_spawner) = world.get_resource_mut::<SceneSpawner>() else {
return;
};
if let Some(instance_ids) = scene_spawner.spawned_dynamic_scenes.get_mut(&id) {
instance_ids.remove(&scene_instance);
}
scene_spawner.unregister_instance(scene_instance);
}
});
// Register component hooks for SceneRoot
app.world_mut()
.register_component_hooks::<SceneRoot>()
.on_remove(|mut world, context| {
if let Some(&SceneInstance(scene_instance)) =
world.get::<SceneInstance>(context.entity)
{
let Some(mut scene_spawner) = world.get_resource_mut::<SceneSpawner>() else {
return;
};
scene_spawner.unregister_instance(scene_instance);
}
});
}
}
#[cfg(not(feature = "serialize"))]
impl Plugin for ScenePlugin {
fn build(&self, _: &mut App) {}
}

25
vendor/bevy_scene/src/reflect_utils.rs vendored Normal file
View File

@@ -0,0 +1,25 @@
use bevy_reflect::{PartialReflect, ReflectFromReflect, TypeRegistration};
/// Attempts to clone a [`PartialReflect`] value using various methods.
///
/// This first attempts to clone via [`PartialReflect::reflect_clone`].
/// then falls back to [`ReflectFromReflect::from_reflect`],
/// and finally [`PartialReflect::to_dynamic`] if the first two methods fail.
///
/// This helps ensure that the original type and type data is retained,
/// and only returning a dynamic type if all other methods fail.
pub(super) fn clone_reflect_value(
value: &dyn PartialReflect,
type_registration: &TypeRegistration,
) -> Box<dyn PartialReflect> {
value
.reflect_clone()
.map(PartialReflect::into_partial_reflect)
.unwrap_or_else(|_| {
type_registration
.data::<ReflectFromReflect>()
.and_then(|fr| fr.from_reflect(value.as_partial_reflect()))
.map(PartialReflect::into_partial_reflect)
.unwrap_or_else(|| value.to_dynamic())
})
}

171
vendor/bevy_scene/src/scene.rs vendored Normal file
View File

@@ -0,0 +1,171 @@
use core::any::TypeId;
use crate::reflect_utils::clone_reflect_value;
use crate::{DynamicScene, SceneSpawnError};
use bevy_asset::Asset;
use bevy_ecs::{
component::ComponentCloneBehavior,
entity::{Entity, EntityHashMap, SceneEntityMapper},
entity_disabling::DefaultQueryFilters,
reflect::{AppTypeRegistry, ReflectComponent, ReflectResource},
relationship::RelationshipHookMode,
world::World,
};
use bevy_reflect::TypePath;
/// A composition of [`World`] objects.
///
/// To spawn a scene, you can use either:
/// * [`SceneSpawner::spawn`](crate::SceneSpawner::spawn)
/// * adding the [`SceneRoot`](crate::components::SceneRoot) component to an entity.
#[derive(Asset, TypePath, Debug)]
pub struct Scene {
/// The world of the scene, containing its entities and resources.
pub world: World,
}
impl Scene {
/// Creates a new scene with the given world.
pub fn new(world: World) -> Self {
Self { world }
}
/// Create a new scene from a given dynamic scene.
pub fn from_dynamic_scene(
dynamic_scene: &DynamicScene,
type_registry: &AppTypeRegistry,
) -> Result<Scene, SceneSpawnError> {
let mut world = World::new();
let mut entity_map = EntityHashMap::default();
dynamic_scene.write_to_world_with(&mut world, &mut entity_map, type_registry)?;
Ok(Self { world })
}
/// Clone the scene.
///
/// This method will return a [`SceneSpawnError`] if a type either is not registered in the
/// provided [`AppTypeRegistry`] or doesn't reflect the [`Component`](bevy_ecs::component::Component) trait.
pub fn clone_with(&self, type_registry: &AppTypeRegistry) -> Result<Scene, SceneSpawnError> {
let mut new_world = World::new();
let mut entity_map = EntityHashMap::default();
self.write_to_world_with(&mut new_world, &mut entity_map, type_registry)?;
Ok(Self { world: new_world })
}
/// Write the entities and their corresponding components to the given world.
///
/// This method will return a [`SceneSpawnError`] if a type either is not registered in the
/// provided [`AppTypeRegistry`] or doesn't reflect the [`Component`](bevy_ecs::component::Component) trait.
pub fn write_to_world_with(
&self,
world: &mut World,
entity_map: &mut EntityHashMap<Entity>,
type_registry: &AppTypeRegistry,
) -> Result<(), SceneSpawnError> {
let type_registry = type_registry.read();
let self_dqf_id = self
.world
.components()
.get_resource_id(TypeId::of::<DefaultQueryFilters>());
// Resources archetype
for (component_id, resource_data) in self.world.storages().resources.iter() {
if Some(component_id) == self_dqf_id {
continue;
}
if !resource_data.is_present() {
continue;
}
let component_info = self
.world
.components()
.get_info(component_id)
.expect("component_ids in archetypes should have ComponentInfo");
let type_id = component_info
.type_id()
.expect("reflected resources must have a type_id");
let registration =
type_registry
.get(type_id)
.ok_or_else(|| SceneSpawnError::UnregisteredType {
std_type_name: component_info.name().to_string(),
})?;
let reflect_resource = registration.data::<ReflectResource>().ok_or_else(|| {
SceneSpawnError::UnregisteredResource {
type_path: registration.type_info().type_path().to_string(),
}
})?;
reflect_resource.copy(&self.world, world, &type_registry);
}
// Ensure that all scene entities have been allocated in the destination
// world before handling components that may contain references that need mapping.
for archetype in self.world.archetypes().iter() {
for scene_entity in archetype.entities() {
entity_map
.entry(scene_entity.id())
.or_insert_with(|| world.spawn_empty().id());
}
}
for archetype in self.world.archetypes().iter() {
for scene_entity in archetype.entities() {
let entity = *entity_map
.get(&scene_entity.id())
.expect("should have previously spawned an entity");
for component_id in archetype.components() {
let component_info = self
.world
.components()
.get_info(component_id)
.expect("component_ids in archetypes should have ComponentInfo");
if *component_info.clone_behavior() == ComponentCloneBehavior::Ignore {
continue;
}
let registration = type_registry
.get(component_info.type_id().unwrap())
.ok_or_else(|| SceneSpawnError::UnregisteredType {
std_type_name: component_info.name().to_string(),
})?;
let reflect_component =
registration.data::<ReflectComponent>().ok_or_else(|| {
SceneSpawnError::UnregisteredComponent {
type_path: registration.type_info().type_path().to_string(),
}
})?;
let Some(component) = reflect_component
.reflect(self.world.entity(scene_entity.id()))
.map(|component| {
clone_reflect_value(component.as_partial_reflect(), registration)
})
else {
continue;
};
// If this component references entities in the scene,
// update them to the entities in the world.
SceneEntityMapper::world_scope(entity_map, world, |world, mapper| {
reflect_component.apply_or_insert_mapped(
&mut world.entity_mut(entity),
component.as_partial_reflect(),
&type_registry,
mapper,
RelationshipHookMode::Skip,
);
});
}
}
}
Ok(())
}
}

275
vendor/bevy_scene/src/scene_filter.rs vendored Normal file
View File

@@ -0,0 +1,275 @@
use bevy_platform::collections::{hash_set::IntoIter, HashSet};
use core::any::{Any, TypeId};
/// A filter used to control which types can be added to a [`DynamicScene`].
///
/// This scene filter _can_ be used more generically to represent a filter for any given type;
/// however, note that its intended usage with `DynamicScene` only considers [components] and [resources].
/// Adding types that are not a component or resource will have no effect when used with `DynamicScene`.
///
/// [`DynamicScene`]: crate::DynamicScene
/// [components]: bevy_ecs::prelude::Component
/// [resources]: bevy_ecs::prelude::Resource
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub enum SceneFilter {
/// Represents an unset filter.
///
/// This is the equivalent of an empty [`Denylist`] or an [`Allowlist`] containing every type—
/// essentially, all types are permissible.
///
/// [Allowing] a type will convert this filter to an `Allowlist`.
/// Similarly, [denying] a type will convert this filter to a `Denylist`.
///
/// [`Denylist`]: SceneFilter::Denylist
/// [`Allowlist`]: SceneFilter::Allowlist
/// [Allowing]: SceneFilter::allow
/// [denying]: SceneFilter::deny
#[default]
Unset,
/// Contains the set of permitted types by their [`TypeId`].
///
/// Types not contained within this set should not be allowed to be saved to an associated [`DynamicScene`].
///
/// [`DynamicScene`]: crate::DynamicScene
Allowlist(HashSet<TypeId>),
/// Contains the set of prohibited types by their [`TypeId`].
///
/// Types contained within this set should not be allowed to be saved to an associated [`DynamicScene`].
///
/// [`DynamicScene`]: crate::DynamicScene
Denylist(HashSet<TypeId>),
}
impl SceneFilter {
/// Creates a filter where all types are allowed.
///
/// This is the equivalent of creating an empty [`Denylist`].
///
/// [`Denylist`]: SceneFilter::Denylist
pub fn allow_all() -> Self {
Self::Denylist(HashSet::default())
}
/// Creates a filter where all types are denied.
///
/// This is the equivalent of creating an empty [`Allowlist`].
///
/// [`Allowlist`]: SceneFilter::Allowlist
pub fn deny_all() -> Self {
Self::Allowlist(HashSet::default())
}
/// Allow the given type, `T`.
///
/// If this filter is already set as a [`Denylist`],
/// then the given type will be removed from the denied set.
///
/// If this filter is [`Unset`], then it will be completely replaced by a new [`Allowlist`].
///
/// [`Denylist`]: SceneFilter::Denylist
/// [`Unset`]: SceneFilter::Unset
/// [`Allowlist`]: SceneFilter::Allowlist
#[must_use]
pub fn allow<T: Any>(self) -> Self {
self.allow_by_id(TypeId::of::<T>())
}
/// Allow the given type.
///
/// If this filter is already set as a [`Denylist`],
/// then the given type will be removed from the denied set.
///
/// If this filter is [`Unset`], then it will be completely replaced by a new [`Allowlist`].
///
/// [`Denylist`]: SceneFilter::Denylist
/// [`Unset`]: SceneFilter::Unset
/// [`Allowlist`]: SceneFilter::Allowlist
#[must_use]
pub fn allow_by_id(mut self, type_id: TypeId) -> Self {
match &mut self {
Self::Unset => {
self = Self::Allowlist([type_id].into_iter().collect());
}
Self::Allowlist(list) => {
list.insert(type_id);
}
Self::Denylist(list) => {
list.remove(&type_id);
}
}
self
}
/// Deny the given type, `T`.
///
/// If this filter is already set as an [`Allowlist`],
/// then the given type will be removed from the allowed set.
///
/// If this filter is [`Unset`], then it will be completely replaced by a new [`Denylist`].
///
/// [`Allowlist`]: SceneFilter::Allowlist
/// [`Unset`]: SceneFilter::Unset
/// [`Denylist`]: SceneFilter::Denylist
#[must_use]
pub fn deny<T: Any>(self) -> Self {
self.deny_by_id(TypeId::of::<T>())
}
/// Deny the given type.
///
/// If this filter is already set as an [`Allowlist`],
/// then the given type will be removed from the allowed set.
///
/// If this filter is [`Unset`], then it will be completely replaced by a new [`Denylist`].
///
/// [`Allowlist`]: SceneFilter::Allowlist
/// [`Unset`]: SceneFilter::Unset
/// [`Denylist`]: SceneFilter::Denylist
#[must_use]
pub fn deny_by_id(mut self, type_id: TypeId) -> Self {
match &mut self {
Self::Unset => self = Self::Denylist([type_id].into_iter().collect()),
Self::Allowlist(list) => {
list.remove(&type_id);
}
Self::Denylist(list) => {
list.insert(type_id);
}
}
self
}
/// Returns true if the given type, `T`, is allowed by the filter.
///
/// If the filter is [`Unset`], this will always return `true`.
///
/// [`Unset`]: SceneFilter::Unset
pub fn is_allowed<T: Any>(&self) -> bool {
self.is_allowed_by_id(TypeId::of::<T>())
}
/// Returns true if the given type is allowed by the filter.
///
/// If the filter is [`Unset`], this will always return `true`.
///
/// [`Unset`]: SceneFilter::Unset
pub fn is_allowed_by_id(&self, type_id: TypeId) -> bool {
match self {
Self::Unset => true,
Self::Allowlist(list) => list.contains(&type_id),
Self::Denylist(list) => !list.contains(&type_id),
}
}
/// Returns true if the given type, `T`, is denied by the filter.
///
/// If the filter is [`Unset`], this will always return `false`.
///
/// [`Unset`]: SceneFilter::Unset
pub fn is_denied<T: Any>(&self) -> bool {
self.is_denied_by_id(TypeId::of::<T>())
}
/// Returns true if the given type is denied by the filter.
///
/// If the filter is [`Unset`], this will always return `false`.
///
/// [`Unset`]: SceneFilter::Unset
pub fn is_denied_by_id(&self, type_id: TypeId) -> bool {
!self.is_allowed_by_id(type_id)
}
/// Returns an iterator over the items in the filter.
///
/// If the filter is [`Unset`], this will return an empty iterator.
///
/// [`Unset`]: SceneFilter::Unset
pub fn iter(&self) -> Box<dyn ExactSizeIterator<Item = &TypeId> + '_> {
match self {
Self::Unset => Box::new(core::iter::empty()),
Self::Allowlist(list) | Self::Denylist(list) => Box::new(list.iter()),
}
}
/// Returns the number of items in the filter.
///
/// If the filter is [`Unset`], this will always return a length of zero.
///
/// [`Unset`]: SceneFilter::Unset
pub fn len(&self) -> usize {
match self {
Self::Unset => 0,
Self::Allowlist(list) | Self::Denylist(list) => list.len(),
}
}
/// Returns true if there are zero items in the filter.
///
/// If the filter is [`Unset`], this will always return `true`.
///
/// [`Unset`]: SceneFilter::Unset
pub fn is_empty(&self) -> bool {
match self {
Self::Unset => true,
Self::Allowlist(list) | Self::Denylist(list) => list.is_empty(),
}
}
}
impl IntoIterator for SceneFilter {
type Item = TypeId;
type IntoIter = IntoIter<TypeId>;
fn into_iter(self) -> Self::IntoIter {
match self {
Self::Unset => Default::default(),
Self::Allowlist(list) | Self::Denylist(list) => list.into_iter(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_set_list_type_if_none() {
let filter = SceneFilter::Unset.allow::<i32>();
assert!(matches!(filter, SceneFilter::Allowlist(_)));
let filter = SceneFilter::Unset.deny::<i32>();
assert!(matches!(filter, SceneFilter::Denylist(_)));
}
#[test]
fn should_add_to_list() {
let filter = SceneFilter::default().allow::<i16>().allow::<i32>();
assert_eq!(2, filter.len());
assert!(filter.is_allowed::<i16>());
assert!(filter.is_allowed::<i32>());
let filter = SceneFilter::default().deny::<i16>().deny::<i32>();
assert_eq!(2, filter.len());
assert!(filter.is_denied::<i16>());
assert!(filter.is_denied::<i32>());
}
#[test]
fn should_remove_from_list() {
let filter = SceneFilter::default()
.allow::<i16>()
.allow::<i32>()
.deny::<i32>();
assert_eq!(1, filter.len());
assert!(filter.is_allowed::<i16>());
assert!(!filter.is_allowed::<i32>());
let filter = SceneFilter::default()
.deny::<i16>()
.deny::<i32>()
.allow::<i32>();
assert_eq!(1, filter.len());
assert!(filter.is_denied::<i16>());
assert!(!filter.is_denied::<i32>());
}
}

75
vendor/bevy_scene/src/scene_loader.rs vendored Normal file
View File

@@ -0,0 +1,75 @@
use crate::ron;
use bevy_ecs::{
reflect::AppTypeRegistry,
world::{FromWorld, World},
};
use bevy_reflect::TypeRegistryArc;
use thiserror::Error;
#[cfg(feature = "serialize")]
use {
crate::{serde::SceneDeserializer, DynamicScene},
bevy_asset::{io::Reader, AssetLoader, LoadContext},
serde::de::DeserializeSeed,
};
/// Asset loader for a Bevy dynamic scene (`.scn` / `.scn.ron`).
///
/// The loader handles assets serialized with [`DynamicScene::serialize`].
#[derive(Debug)]
pub struct SceneLoader {
#[cfg_attr(
not(feature = "serialize"),
expect(dead_code, reason = "only used with `serialize` feature")
)]
type_registry: TypeRegistryArc,
}
impl FromWorld for SceneLoader {
fn from_world(world: &mut World) -> Self {
let type_registry = world.resource::<AppTypeRegistry>();
SceneLoader {
type_registry: type_registry.0.clone(),
}
}
}
/// Possible errors that can be produced by [`SceneLoader`]
#[non_exhaustive]
#[derive(Debug, Error)]
pub enum SceneLoaderError {
/// An [IO Error](std::io::Error)
#[error("Error while trying to read the scene file: {0}")]
Io(#[from] std::io::Error),
/// A [RON Error](ron::error::SpannedError)
#[error("Could not parse RON: {0}")]
RonSpannedError(#[from] ron::error::SpannedError),
}
#[cfg(feature = "serialize")]
impl AssetLoader for SceneLoader {
type Asset = DynamicScene;
type Settings = ();
type Error = SceneLoaderError;
async fn load(
&self,
reader: &mut dyn Reader,
_settings: &(),
_load_context: &mut LoadContext<'_>,
) -> Result<Self::Asset, Self::Error> {
let mut bytes = Vec::new();
reader.read_to_end(&mut bytes).await?;
let mut deserializer = ron::de::Deserializer::from_bytes(&bytes)?;
let scene_deserializer = SceneDeserializer {
type_registry: &self.type_registry.read(),
};
Ok(scene_deserializer
.deserialize(&mut deserializer)
.map_err(|e| deserializer.span_error(e))?)
}
fn extensions(&self) -> &[&str] {
&["scn", "scn.ron"]
}
}

945
vendor/bevy_scene/src/scene_spawner.rs vendored Normal file
View File

@@ -0,0 +1,945 @@
use crate::{DynamicScene, Scene};
use bevy_asset::{AssetEvent, AssetId, Assets, Handle};
use bevy_ecs::{
entity::{Entity, EntityHashMap},
event::{Event, EventCursor, Events},
hierarchy::ChildOf,
reflect::AppTypeRegistry,
resource::Resource,
world::{Mut, World},
};
use bevy_platform::collections::{HashMap, HashSet};
use bevy_reflect::Reflect;
use thiserror::Error;
use uuid::Uuid;
use crate::{DynamicSceneRoot, SceneRoot};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
change_detection::ResMut,
prelude::{Changed, Component, Without},
system::{Commands, Query},
};
/// Triggered on a scene's parent entity when [`crate::SceneInstance`] becomes ready to use.
///
/// See also [`Trigger`], [`SceneSpawner::instance_is_ready`].
///
/// [`Trigger`]: bevy_ecs::observer::Trigger
#[derive(Clone, Copy, Debug, Eq, PartialEq, Event, Reflect)]
#[reflect(Debug, PartialEq, Clone)]
pub struct SceneInstanceReady {
/// Instance which has been spawned.
pub instance_id: InstanceId,
}
/// Information about a scene instance.
#[derive(Debug)]
pub struct InstanceInfo {
/// Mapping of entities from the scene world to the instance world.
pub entity_map: EntityHashMap<Entity>,
}
/// Unique id identifying a scene instance.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Reflect)]
#[reflect(Debug, PartialEq, Hash, Clone)]
pub struct InstanceId(Uuid);
impl InstanceId {
fn new() -> Self {
InstanceId(Uuid::new_v4())
}
}
/// Handles spawning and despawning scenes in the world, either synchronously or batched through the [`scene_spawner_system`].
///
/// Synchronous methods: (Scene operations will take effect immediately)
/// - [`spawn_dynamic_sync`](Self::spawn_dynamic_sync)
/// - [`spawn_sync`](Self::spawn_sync)
/// - [`despawn_sync`](Self::despawn_sync)
/// - [`despawn_instance_sync`](Self::despawn_instance_sync)
/// - [`update_spawned_scenes`](Self::update_spawned_scenes)
/// - [`spawn_queued_scenes`](Self::spawn_queued_scenes)
/// - [`despawn_queued_scenes`](Self::despawn_queued_scenes)
/// - [`despawn_queued_instances`](Self::despawn_queued_instances)
///
/// Deferred methods: (Scene operations will be processed when the [`scene_spawner_system`] is run)
/// - [`spawn_dynamic`](Self::spawn_dynamic)
/// - [`spawn_dynamic_as_child`](Self::spawn_dynamic_as_child)
/// - [`spawn`](Self::spawn)
/// - [`spawn_as_child`](Self::spawn_as_child)
/// - [`despawn`](Self::despawn)
/// - [`despawn_instance`](Self::despawn_instance)
#[derive(Default, Resource)]
pub struct SceneSpawner {
pub(crate) spawned_dynamic_scenes: HashMap<AssetId<DynamicScene>, HashSet<InstanceId>>,
pub(crate) spawned_instances: HashMap<InstanceId, InstanceInfo>,
scene_asset_event_reader: EventCursor<AssetEvent<DynamicScene>>,
dynamic_scenes_to_spawn: Vec<(Handle<DynamicScene>, InstanceId, Option<Entity>)>,
scenes_to_spawn: Vec<(Handle<Scene>, InstanceId, Option<Entity>)>,
scenes_to_despawn: Vec<AssetId<DynamicScene>>,
instances_to_despawn: Vec<InstanceId>,
scenes_with_parent: Vec<(InstanceId, Entity)>,
instances_ready: Vec<(InstanceId, Option<Entity>)>,
}
/// Errors that can occur when spawning a scene.
#[derive(Error, Debug)]
pub enum SceneSpawnError {
/// Scene contains an unregistered component type.
#[error("scene contains the unregistered component `{type_path}`. consider adding `#[reflect(Component)]` to your type")]
UnregisteredComponent {
/// Type of the unregistered component.
type_path: String,
},
/// Scene contains an unregistered resource type.
#[error("scene contains the unregistered resource `{type_path}`. consider adding `#[reflect(Resource)]` to your type")]
UnregisteredResource {
/// Type of the unregistered resource.
type_path: String,
},
/// Scene contains an unregistered type.
#[error(
"scene contains the unregistered type `{std_type_name}`. \
consider reflecting it with `#[derive(Reflect)]` \
and registering the type using `app.register_type::<T>()`"
)]
UnregisteredType {
/// The [type name](std::any::type_name) for the unregistered type.
std_type_name: String,
},
/// Scene contains an unregistered type which has a `TypePath`.
#[error(
"scene contains the reflected type `{type_path}` but it was not found in the type registry. \
consider registering the type using `app.register_type::<T>()``"
)]
UnregisteredButReflectedType {
/// The unregistered type.
type_path: String,
},
/// Scene contains a proxy without a represented type.
#[error("scene contains dynamic type `{type_path}` without a represented type. consider changing this using `set_represented_type`.")]
NoRepresentedType {
/// The dynamic instance type.
type_path: String,
},
/// Dynamic scene with the given id does not exist.
#[error("scene does not exist")]
NonExistentScene {
/// Id of the non-existent dynamic scene.
id: AssetId<DynamicScene>,
},
/// Scene with the given id does not exist.
#[error("scene does not exist")]
NonExistentRealScene {
/// Id of the non-existent scene.
id: AssetId<Scene>,
},
}
impl SceneSpawner {
/// Schedule the spawn of a new instance of the provided dynamic scene.
pub fn spawn_dynamic(&mut self, id: impl Into<Handle<DynamicScene>>) -> InstanceId {
let instance_id = InstanceId::new();
self.dynamic_scenes_to_spawn
.push((id.into(), instance_id, None));
instance_id
}
/// Schedule the spawn of a new instance of the provided dynamic scene as a child of `parent`.
pub fn spawn_dynamic_as_child(
&mut self,
id: impl Into<Handle<DynamicScene>>,
parent: Entity,
) -> InstanceId {
let instance_id = InstanceId::new();
self.dynamic_scenes_to_spawn
.push((id.into(), instance_id, Some(parent)));
self.scenes_with_parent.push((instance_id, parent));
instance_id
}
/// Schedule the spawn of a new instance of the provided scene.
pub fn spawn(&mut self, id: impl Into<Handle<Scene>>) -> InstanceId {
let instance_id = InstanceId::new();
self.scenes_to_spawn.push((id.into(), instance_id, None));
instance_id
}
/// Schedule the spawn of a new instance of the provided scene as a child of `parent`.
pub fn spawn_as_child(&mut self, id: impl Into<Handle<Scene>>, parent: Entity) -> InstanceId {
let instance_id = InstanceId::new();
self.scenes_to_spawn
.push((id.into(), instance_id, Some(parent)));
self.scenes_with_parent.push((instance_id, parent));
instance_id
}
/// Schedule the despawn of all instances of the provided dynamic scene.
pub fn despawn(&mut self, id: impl Into<AssetId<DynamicScene>>) {
self.scenes_to_despawn.push(id.into());
}
/// Schedule the despawn of a scene instance, removing all its entities from the world.
///
/// Note: this will despawn _all_ entities associated with this instance, including those
/// that have been removed from the scene hierarchy. To despawn _only_ entities still in the hierarchy,
/// despawn the relevant root entity directly.
pub fn despawn_instance(&mut self, instance_id: InstanceId) {
self.instances_to_despawn.push(instance_id);
}
/// This will remove all records of this instance, without despawning any entities.
pub fn unregister_instance(&mut self, instance_id: InstanceId) {
self.spawned_instances.remove(&instance_id);
}
/// Immediately despawns all instances of a dynamic scene.
pub fn despawn_sync(
&mut self,
world: &mut World,
id: impl Into<AssetId<DynamicScene>>,
) -> Result<(), SceneSpawnError> {
if let Some(instance_ids) = self.spawned_dynamic_scenes.remove(&id.into()) {
for instance_id in instance_ids {
self.despawn_instance_sync(world, &instance_id);
}
}
Ok(())
}
/// Immediately despawns a scene instance, removing all its entities from the world.
pub fn despawn_instance_sync(&mut self, world: &mut World, instance_id: &InstanceId) {
if let Some(instance) = self.spawned_instances.remove(instance_id) {
for &entity in instance.entity_map.values() {
if let Ok(entity_mut) = world.get_entity_mut(entity) {
entity_mut.despawn();
};
}
}
}
/// Immediately spawns a new instance of the provided dynamic scene.
pub fn spawn_dynamic_sync(
&mut self,
world: &mut World,
id: impl Into<AssetId<DynamicScene>>,
) -> Result<InstanceId, SceneSpawnError> {
let mut entity_map = EntityHashMap::default();
let id = id.into();
Self::spawn_dynamic_internal(world, id, &mut entity_map)?;
let instance_id = InstanceId::new();
self.spawned_instances
.insert(instance_id, InstanceInfo { entity_map });
let spawned = self.spawned_dynamic_scenes.entry(id).or_default();
spawned.insert(instance_id);
Ok(instance_id)
}
fn spawn_dynamic_internal(
world: &mut World,
id: AssetId<DynamicScene>,
entity_map: &mut EntityHashMap<Entity>,
) -> Result<(), SceneSpawnError> {
world.resource_scope(|world, scenes: Mut<Assets<DynamicScene>>| {
let scene = scenes
.get(id)
.ok_or(SceneSpawnError::NonExistentScene { id })?;
scene.write_to_world(world, entity_map)
})
}
/// Immediately spawns a new instance of the provided scene.
pub fn spawn_sync(
&mut self,
world: &mut World,
id: impl Into<AssetId<Scene>>,
) -> Result<InstanceId, SceneSpawnError> {
let mut entity_map = EntityHashMap::default();
let id = id.into();
Self::spawn_sync_internal(world, id, &mut entity_map)?;
let instance_id = InstanceId::new();
self.spawned_instances
.insert(instance_id, InstanceInfo { entity_map });
Ok(instance_id)
}
fn spawn_sync_internal(
world: &mut World,
id: AssetId<Scene>,
entity_map: &mut EntityHashMap<Entity>,
) -> Result<(), SceneSpawnError> {
world.resource_scope(|world, scenes: Mut<Assets<Scene>>| {
let scene = scenes
.get(id)
.ok_or(SceneSpawnError::NonExistentRealScene { id })?;
scene.write_to_world_with(
world,
entity_map,
&world.resource::<AppTypeRegistry>().clone(),
)
})
}
/// Iterate through all instances of the provided scenes and update those immediately.
///
/// Useful for updating already spawned scene instances after their corresponding scene has been modified.
pub fn update_spawned_scenes(
&mut self,
world: &mut World,
scene_ids: &[AssetId<DynamicScene>],
) -> Result<(), SceneSpawnError> {
for id in scene_ids {
if let Some(spawned_instances) = self.spawned_dynamic_scenes.get(id) {
for instance_id in spawned_instances {
if let Some(instance_info) = self.spawned_instances.get_mut(instance_id) {
Self::spawn_dynamic_internal(world, *id, &mut instance_info.entity_map)?;
}
}
}
}
Ok(())
}
/// Immediately despawns all scenes scheduled for despawn by despawning their instances.
pub fn despawn_queued_scenes(&mut self, world: &mut World) -> Result<(), SceneSpawnError> {
let scenes_to_despawn = core::mem::take(&mut self.scenes_to_despawn);
for scene_handle in scenes_to_despawn {
self.despawn_sync(world, scene_handle)?;
}
Ok(())
}
/// Immediately despawns all scene instances scheduled for despawn.
pub fn despawn_queued_instances(&mut self, world: &mut World) {
let instances_to_despawn = core::mem::take(&mut self.instances_to_despawn);
for instance_id in instances_to_despawn {
self.despawn_instance_sync(world, &instance_id);
}
}
/// Immediately spawns all scenes scheduled for spawn.
pub fn spawn_queued_scenes(&mut self, world: &mut World) -> Result<(), SceneSpawnError> {
let scenes_to_spawn = core::mem::take(&mut self.dynamic_scenes_to_spawn);
for (handle, instance_id, parent) in scenes_to_spawn {
let mut entity_map = EntityHashMap::default();
match Self::spawn_dynamic_internal(world, handle.id(), &mut entity_map) {
Ok(_) => {
self.spawned_instances
.insert(instance_id, InstanceInfo { entity_map });
let spawned = self.spawned_dynamic_scenes.entry(handle.id()).or_default();
spawned.insert(instance_id);
// Scenes with parents need more setup before they are ready.
// See `set_scene_instance_parent_sync()`.
if parent.is_none() {
// We trigger `SceneInstanceReady` events after processing all scenes
// SceneSpawner may not be available in the observer.
self.instances_ready.push((instance_id, None));
}
}
Err(SceneSpawnError::NonExistentScene { .. }) => {
self.dynamic_scenes_to_spawn
.push((handle, instance_id, parent));
}
Err(err) => return Err(err),
}
}
let scenes_to_spawn = core::mem::take(&mut self.scenes_to_spawn);
for (scene_handle, instance_id, parent) in scenes_to_spawn {
let mut entity_map = EntityHashMap::default();
match Self::spawn_sync_internal(world, scene_handle.id(), &mut entity_map) {
Ok(_) => {
self.spawned_instances
.insert(instance_id, InstanceInfo { entity_map });
// Scenes with parents need more setup before they are ready.
// See `set_scene_instance_parent_sync()`.
if parent.is_none() {
// We trigger `SceneInstanceReady` events after processing all scenes
// SceneSpawner may not be available in the observer.
self.instances_ready.push((instance_id, None));
}
}
Err(SceneSpawnError::NonExistentRealScene { .. }) => {
self.scenes_to_spawn
.push((scene_handle, instance_id, parent));
}
Err(err) => return Err(err),
}
}
Ok(())
}
pub(crate) fn set_scene_instance_parent_sync(&mut self, world: &mut World) {
let scenes_with_parent = core::mem::take(&mut self.scenes_with_parent);
for (instance_id, parent) in scenes_with_parent {
if let Some(instance) = self.spawned_instances.get(&instance_id) {
for &entity in instance.entity_map.values() {
// Add the `ChildOf` component to the scene root, and update the `Children` component of
// the scene parent
if !world
.get_entity(entity)
.ok()
// This will filter only the scene root entity, as all other from the
// scene have a parent
// Entities that wouldn't exist anymore are also skipped
// this case shouldn't happen anyway
.is_none_or(|entity| entity.contains::<ChildOf>())
{
world.entity_mut(parent).add_child(entity);
}
}
// We trigger `SceneInstanceReady` events after processing all scenes
// SceneSpawner may not be available in the observer.
self.instances_ready.push((instance_id, Some(parent)));
} else {
self.scenes_with_parent.push((instance_id, parent));
}
}
}
fn trigger_scene_ready_events(&mut self, world: &mut World) {
for (instance_id, parent) in self.instances_ready.drain(..) {
if let Some(parent) = parent {
// Defer via commands otherwise SceneSpawner is not available in the observer.
world
.commands()
.trigger_targets(SceneInstanceReady { instance_id }, parent);
} else {
// Defer via commands otherwise SceneSpawner is not available in the observer.
world.commands().trigger(SceneInstanceReady { instance_id });
}
}
}
/// Check that a scene instance spawned previously is ready to use
pub fn instance_is_ready(&self, instance_id: InstanceId) -> bool {
self.spawned_instances.contains_key(&instance_id)
}
/// Get an iterator over the entities in an instance, once it's spawned.
///
/// Before the scene is spawned, the iterator will be empty. Use [`Self::instance_is_ready`]
/// to check if the instance is ready.
pub fn iter_instance_entities(
&'_ self,
instance_id: InstanceId,
) -> impl Iterator<Item = Entity> + '_ {
self.spawned_instances
.get(&instance_id)
.map(|instance| instance.entity_map.values())
.into_iter()
.flatten()
.copied()
}
}
/// System that handles scheduled scene instance spawning and despawning through a [`SceneSpawner`].
pub fn scene_spawner_system(world: &mut World) {
world.resource_scope(|world, mut scene_spawner: Mut<SceneSpawner>| {
// remove any loading instances where parent is deleted
let mut dead_instances = <HashSet<_>>::default();
scene_spawner
.scenes_with_parent
.retain(|(instance, parent)| {
let retain = world.get_entity(*parent).is_ok();
if !retain {
dead_instances.insert(*instance);
}
retain
});
scene_spawner
.dynamic_scenes_to_spawn
.retain(|(_, instance, _)| !dead_instances.contains(instance));
scene_spawner
.scenes_to_spawn
.retain(|(_, instance, _)| !dead_instances.contains(instance));
let scene_asset_events = world.resource::<Events<AssetEvent<DynamicScene>>>();
let mut updated_spawned_scenes = Vec::new();
let scene_spawner = &mut *scene_spawner;
for event in scene_spawner
.scene_asset_event_reader
.read(scene_asset_events)
{
if let AssetEvent::Modified { id } = event {
if scene_spawner.spawned_dynamic_scenes.contains_key(id) {
updated_spawned_scenes.push(*id);
}
}
}
scene_spawner.despawn_queued_scenes(world).unwrap();
scene_spawner.despawn_queued_instances(world);
scene_spawner
.spawn_queued_scenes(world)
.unwrap_or_else(|err| panic!("{}", err));
scene_spawner
.update_spawned_scenes(world, &updated_spawned_scenes)
.unwrap();
scene_spawner.set_scene_instance_parent_sync(world);
scene_spawner.trigger_scene_ready_events(world);
});
}
/// [`InstanceId`] of a spawned scene. It can be used with the [`SceneSpawner`] to
/// interact with the spawned scene.
#[derive(Component, Deref, DerefMut)]
pub struct SceneInstance(pub(crate) InstanceId);
/// System that will spawn scenes from the [`SceneRoot`] and [`DynamicSceneRoot`] components.
pub fn scene_spawner(
mut commands: Commands,
mut scene_to_spawn: Query<
(Entity, &SceneRoot, Option<&mut SceneInstance>),
(Changed<SceneRoot>, Without<DynamicSceneRoot>),
>,
mut dynamic_scene_to_spawn: Query<
(Entity, &DynamicSceneRoot, Option<&mut SceneInstance>),
(Changed<DynamicSceneRoot>, Without<SceneRoot>),
>,
mut scene_spawner: ResMut<SceneSpawner>,
) {
for (entity, scene, instance) in &mut scene_to_spawn {
let new_instance = scene_spawner.spawn_as_child(scene.0.clone(), entity);
if let Some(mut old_instance) = instance {
scene_spawner.despawn_instance(**old_instance);
*old_instance = SceneInstance(new_instance);
} else {
commands.entity(entity).insert(SceneInstance(new_instance));
}
}
for (entity, dynamic_scene, instance) in &mut dynamic_scene_to_spawn {
let new_instance = scene_spawner.spawn_dynamic_as_child(dynamic_scene.0.clone(), entity);
if let Some(mut old_instance) = instance {
scene_spawner.despawn_instance(**old_instance);
*old_instance = SceneInstance(new_instance);
} else {
commands.entity(entity).insert(SceneInstance(new_instance));
}
}
}
#[cfg(test)]
mod tests {
use bevy_app::App;
use bevy_asset::{AssetPlugin, AssetServer, Handle};
use bevy_ecs::{
component::Component,
hierarchy::Children,
observer::Trigger,
prelude::ReflectComponent,
query::With,
system::{Commands, Query, Res, ResMut, RunSystemOnce},
};
use bevy_reflect::Reflect;
use crate::{DynamicSceneBuilder, DynamicSceneRoot, ScenePlugin};
use super::*;
use crate::{DynamicScene, SceneSpawner};
use bevy_app::ScheduleRunnerPlugin;
use bevy_asset::Assets;
use bevy_ecs::{
entity::Entity,
prelude::{AppTypeRegistry, World},
};
#[derive(Component, Reflect, Default)]
#[reflect(Component)]
struct ComponentA {
pub x: f32,
pub y: f32,
}
#[test]
fn spawn_and_delete() {
let mut app = App::new();
app.add_plugins(ScheduleRunnerPlugin::default())
.add_plugins(AssetPlugin::default())
.add_plugins(ScenePlugin)
.register_type::<ComponentA>();
app.update();
let mut scene_world = World::new();
// create a new DynamicScene manually
let type_registry = app.world().resource::<AppTypeRegistry>().clone();
scene_world.insert_resource(type_registry);
scene_world.spawn(ComponentA { x: 3.0, y: 4.0 });
let scene = DynamicScene::from_world(&scene_world);
let scene_handle = app
.world_mut()
.resource_mut::<Assets<DynamicScene>>()
.add(scene);
// spawn the scene as a child of `entity` using `DynamicSceneRoot`
let entity = app
.world_mut()
.spawn(DynamicSceneRoot(scene_handle.clone()))
.id();
// run the app's schedule once, so that the scene gets spawned
app.update();
// make sure that the scene was added as a child of the root entity
let (scene_entity, scene_component_a) = app
.world_mut()
.query::<(Entity, &ComponentA)>()
.single(app.world())
.unwrap();
assert_eq!(scene_component_a.x, 3.0);
assert_eq!(scene_component_a.y, 4.0);
assert_eq!(
app.world().entity(entity).get::<Children>().unwrap().len(),
1
);
// let's try to delete the scene
let mut scene_spawner = app.world_mut().resource_mut::<SceneSpawner>();
scene_spawner.despawn(&scene_handle);
// run the scene spawner system to despawn the scene
app.update();
// the scene entity does not exist anymore
assert!(app.world().get_entity(scene_entity).is_err());
// the root entity does not have any children anymore
assert!(app.world().entity(entity).get::<Children>().is_none());
}
#[derive(Reflect, Component, Debug, PartialEq, Eq, Clone, Copy, Default)]
#[reflect(Component)]
struct A(usize);
#[test]
fn clone_dynamic_entities() {
let mut world = World::default();
// setup
let atr = AppTypeRegistry::default();
atr.write().register::<A>();
world.insert_resource(atr);
world.insert_resource(Assets::<DynamicScene>::default());
// start test
world.spawn(A(42));
assert_eq!(world.query::<&A>().iter(&world).len(), 1);
// clone only existing entity
let mut scene_spawner = SceneSpawner::default();
let entity = world
.query_filtered::<Entity, With<A>>()
.single(&world)
.unwrap();
let scene = DynamicSceneBuilder::from_world(&world)
.extract_entity(entity)
.build();
let scene_id = world.resource_mut::<Assets<DynamicScene>>().add(scene);
let instance_id = scene_spawner
.spawn_dynamic_sync(&mut world, &scene_id)
.unwrap();
// verify we spawned exactly one new entity with our expected component
assert_eq!(world.query::<&A>().iter(&world).len(), 2);
// verify that we can get this newly-spawned entity by the instance ID
let new_entity = scene_spawner
.iter_instance_entities(instance_id)
.next()
.unwrap();
// verify this is not the original entity
assert_ne!(entity, new_entity);
// verify this new entity contains the same data as the original entity
let [old_a, new_a] = world
.query::<&A>()
.get_many(&world, [entity, new_entity])
.unwrap();
assert_eq!(old_a, new_a);
}
#[derive(Component, Reflect, Default)]
#[reflect(Component)]
struct ComponentF;
#[derive(Resource, Default)]
struct TriggerCount(u32);
fn setup() -> App {
let mut app = App::new();
app.add_plugins((AssetPlugin::default(), ScenePlugin));
app.init_resource::<TriggerCount>();
app.register_type::<ComponentF>();
app.world_mut().spawn(ComponentF);
app.world_mut().spawn(ComponentF);
app
}
fn build_scene(app: &mut App) -> Handle<Scene> {
app.world_mut()
.run_system_once(
|world: &World,
type_registry: Res<'_, AppTypeRegistry>,
asset_server: Res<'_, AssetServer>| {
asset_server.add(
Scene::from_dynamic_scene(&DynamicScene::from_world(world), &type_registry)
.unwrap(),
)
},
)
.expect("Failed to run scene builder system.")
}
fn build_dynamic_scene(app: &mut App) -> Handle<DynamicScene> {
app.world_mut()
.run_system_once(|world: &World, asset_server: Res<'_, AssetServer>| {
asset_server.add(DynamicScene::from_world(world))
})
.expect("Failed to run dynamic scene builder system.")
}
fn observe_trigger(app: &mut App, scene_id: InstanceId, scene_entity: Entity) {
// Add observer
app.world_mut().add_observer(
move |trigger: Trigger<SceneInstanceReady>,
scene_spawner: Res<SceneSpawner>,
mut trigger_count: ResMut<TriggerCount>| {
assert_eq!(
trigger.event().instance_id,
scene_id,
"`SceneInstanceReady` contains the wrong `InstanceId`"
);
assert_eq!(
trigger.target(),
scene_entity,
"`SceneInstanceReady` triggered on the wrong parent entity"
);
assert!(
scene_spawner.instance_is_ready(trigger.event().instance_id),
"`InstanceId` is not ready"
);
trigger_count.0 += 1;
},
);
// Check observer is triggered once.
app.update();
app.world_mut()
.run_system_once(|trigger_count: Res<TriggerCount>| {
assert_eq!(
trigger_count.0, 1,
"wrong number of `SceneInstanceReady` triggers"
);
})
.unwrap();
}
#[test]
fn observe_scene() {
let mut app = setup();
// Build scene.
let scene = build_scene(&mut app);
// Spawn scene.
let scene_id = app
.world_mut()
.run_system_once(move |mut scene_spawner: ResMut<'_, SceneSpawner>| {
scene_spawner.spawn(scene.clone())
})
.unwrap();
// Check trigger.
observe_trigger(&mut app, scene_id, Entity::PLACEHOLDER);
}
#[test]
fn observe_dynamic_scene() {
let mut app = setup();
// Build scene.
let scene = build_dynamic_scene(&mut app);
// Spawn scene.
let scene_id = app
.world_mut()
.run_system_once(move |mut scene_spawner: ResMut<'_, SceneSpawner>| {
scene_spawner.spawn_dynamic(scene.clone())
})
.unwrap();
// Check trigger.
observe_trigger(&mut app, scene_id, Entity::PLACEHOLDER);
}
#[test]
fn observe_scene_as_child() {
let mut app = setup();
// Build scene.
let scene = build_scene(&mut app);
// Spawn scene as child.
let (scene_id, scene_entity) = app
.world_mut()
.run_system_once(
move |mut commands: Commands<'_, '_>,
mut scene_spawner: ResMut<'_, SceneSpawner>| {
let entity = commands.spawn_empty().id();
let id = scene_spawner.spawn_as_child(scene.clone(), entity);
(id, entity)
},
)
.unwrap();
// Check trigger.
observe_trigger(&mut app, scene_id, scene_entity);
}
#[test]
fn observe_dynamic_scene_as_child() {
let mut app = setup();
// Build scene.
let scene = build_dynamic_scene(&mut app);
// Spawn scene as child.
let (scene_id, scene_entity) = app
.world_mut()
.run_system_once(
move |mut commands: Commands<'_, '_>,
mut scene_spawner: ResMut<'_, SceneSpawner>| {
let entity = commands.spawn_empty().id();
let id = scene_spawner.spawn_dynamic_as_child(scene.clone(), entity);
(id, entity)
},
)
.unwrap();
// Check trigger.
observe_trigger(&mut app, scene_id, scene_entity);
}
#[test]
fn despawn_scene() {
let mut app = App::new();
app.add_plugins((AssetPlugin::default(), ScenePlugin));
app.register_type::<ComponentF>();
let asset_server = app.world().resource::<AssetServer>();
// Build scene.
let scene = asset_server.add(DynamicScene::default());
let count = 10;
// Checks the number of scene instances stored in `SceneSpawner`.
let check = |world: &mut World, expected_count: usize| {
let scene_spawner = world.resource::<SceneSpawner>();
assert_eq!(
scene_spawner.spawned_dynamic_scenes[&scene.id()].len(),
expected_count
);
assert_eq!(scene_spawner.spawned_instances.len(), expected_count);
};
// Spawn scene.
for _ in 0..count {
app.world_mut()
.spawn((ComponentF, DynamicSceneRoot(scene.clone())));
}
app.update();
check(app.world_mut(), count);
// Despawn scene.
app.world_mut()
.run_system_once(
|mut commands: Commands, query: Query<Entity, With<ComponentF>>| {
for entity in query.iter() {
commands.entity(entity).despawn();
}
},
)
.unwrap();
app.update();
check(app.world_mut(), 0);
}
#[test]
fn scene_child_order_preserved_when_archetype_order_mismatched() {
let mut app = App::new();
app.add_plugins(ScheduleRunnerPlugin::default())
.add_plugins(AssetPlugin::default())
.add_plugins(ScenePlugin)
.register_type::<ComponentA>()
.register_type::<ComponentF>();
app.update();
let mut scene_world = World::new();
let root = scene_world.spawn_empty().id();
let temporary_root = scene_world.spawn_empty().id();
// Spawn entities with different parent first before parenting them to the actual root, allowing us
// to decouple child order from archetype-creation-order
let child1 = scene_world
.spawn((ChildOf(temporary_root), ComponentA { x: 1.0, y: 1.0 }))
.id();
let child2 = scene_world
.spawn((ChildOf(temporary_root), ComponentA { x: 2.0, y: 2.0 }))
.id();
// the "first" child is intentionally spawned with a different component to force it into a "newer" archetype,
// meaning it will be iterated later in the spawn code.
let child0 = scene_world
.spawn((ChildOf(temporary_root), ComponentF))
.id();
scene_world
.entity_mut(root)
.add_children(&[child0, child1, child2]);
let scene = Scene::new(scene_world);
let scene_handle = app.world_mut().resource_mut::<Assets<Scene>>().add(scene);
let spawned = app.world_mut().spawn(SceneRoot(scene_handle.clone())).id();
app.update();
let world = app.world_mut();
let spawned_root = world.entity(spawned).get::<Children>().unwrap()[0];
let children = world.entity(spawned_root).get::<Children>().unwrap();
assert_eq!(children.len(), 3);
assert!(world.entity(children[0]).get::<ComponentF>().is_some());
assert_eq!(
world.entity(children[1]).get::<ComponentA>().unwrap().x,
1.0
);
assert_eq!(
world.entity(children[2]).get::<ComponentA>().unwrap().x,
2.0
);
}
}

1035
vendor/bevy_scene/src/serde.rs vendored Normal file

File diff suppressed because it is too large Load Diff