Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 14 additions & 50 deletions crates/bevy_scene/src/dynamic_scene.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
use crate::{DynamicSceneBuilder, Scene, SceneSpawnError};
use bevy_asset::Asset;
use bevy_ecs::reflect::ReflectResource;
use bevy_ecs::{
entity::{Entity, EntityHashMap, SceneEntityMapper},
reflect::{AppTypeRegistry, ReflectComponent},
world::World,
};
use bevy_reflect::{PartialReflect, TypePath};
use bevy_reflect::{PartialReflect, Reflect, TypePath};

use bevy_ecs::component::ComponentCloneBehavior;
use bevy_ecs::relationship::RelationshipHookMode;

#[cfg(feature = "serialize")]
use {crate::serde::SceneSerializer, bevy_reflect::TypeRegistry, serde::Serialize};

/// A collection of serializable resources and dynamic entities.
/// A collection of serializable 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:
Expand All @@ -23,8 +22,6 @@ use {crate::serde::SceneSerializer, bevy_reflect::TypeRegistry, serde::Serialize
/// * 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>,
}
Expand All @@ -40,6 +37,18 @@ pub struct DynamicEntity {
pub components: Vec<Box<dyn PartialReflect>>,
}

impl DynamicEntity {
/// Returns true if a dynamic entity has a particular component
pub fn has<T: Reflect + TypePath>(&self) -> bool {
for component in &self.components {
if component.represents::<T>() {
return true;
}
}
false
}
}

impl DynamicScene {
/// Create a new dynamic scene from a given scene.
pub fn from_scene(scene: &Scene) -> Self {
Expand All @@ -58,7 +67,6 @@ impl DynamicScene {
.flat_map(bevy_ecs::archetype::Archetype::entities)
.map(bevy_ecs::archetype::ArchetypeEntity::id),
)
.extract_resources()
.build()
}

Expand Down Expand Up @@ -136,50 +144,6 @@ impl DynamicScene {
});
}
}

// 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(),
}
})?;
registration.data::<ReflectResource>().ok_or_else(|| {
SceneSpawnError::UnregisteredResource {
type_path: type_info.type_path().to_string(),
}
})?;
// reflect_resource existing, implies that reflect_component also exists
let reflect_component = registration
.data::<ReflectComponent>()
.expect("ReflectComponent is depended on ReflectResource");

let resource_id = reflect_component.register_component(world);

// check if the resource already exists, if not spawn it, otherwise override the value
let entity = if let Some(entity) = world.resource_entities().get(resource_id) {
*entity
} else {
world.spawn_empty().id()
};

SceneEntityMapper::world_scope(entity_map, world, |world, mapper| {
reflect_component.apply_or_insert_mapped(
&mut world.entity_mut(entity),
resource.as_partial_reflect(),
&type_registry,
mapper,
RelationshipHookMode::Skip,
);
});
}

Ok(())
}

Expand Down
154 changes: 77 additions & 77 deletions crates/bevy_scene/src/dynamic_scene_builder.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
use core::any::TypeId;

use crate::reflect_utils::clone_reflect_value;
use crate::{DynamicEntity, DynamicScene, SceneFilter};
use alloc::collections::BTreeMap;
use bevy_ecs::resource::IS_RESOURCE;
use bevy_ecs::entity_disabling::DefaultQueryFilters;
use bevy_ecs::{
component::{Component, ComponentId},
entity_disabling::DefaultQueryFilters,
component::Component,
prelude::Entity,
reflect::{AppTypeRegistry, ReflectComponent, ReflectResource},
reflect::{AppTypeRegistry, ReflectComponent},
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.
Expand Down Expand Up @@ -60,7 +56,6 @@ use bevy_utils::default;
///
/// [`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,
Expand All @@ -71,7 +66,6 @@ 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(),
Expand Down Expand Up @@ -121,7 +115,9 @@ impl<'w> DynamicSceneBuilder<'w> {
/// 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>();
if let Some(component_id) = self.original_world.component_id::<T>() {
self.component_filter = self.component_filter.allow(component_id);
}
self
}

Expand All @@ -133,7 +129,9 @@ impl<'w> DynamicSceneBuilder<'w> {
/// 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>();
if let Some(component_id) = self.original_world.component_id::<T>() {
self.component_filter = self.component_filter.deny(component_id);
}
self
}

Expand Down Expand Up @@ -167,7 +165,9 @@ impl<'w> DynamicSceneBuilder<'w> {
/// 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>();
if let Some(component_id) = self.original_world.component_id::<T>() {
self.resource_filter = self.resource_filter.allow(component_id);
}
self
}

Expand All @@ -179,7 +179,9 @@ impl<'w> DynamicSceneBuilder<'w> {
/// 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>();
if let Some(component_id) = self.original_world.component_id::<T>() {
self.resource_filter = self.resource_filter.deny(component_id);
}
self
}

Expand Down Expand Up @@ -212,7 +214,6 @@ impl<'w> DynamicSceneBuilder<'w> {
#[must_use]
pub fn build(self) -> DynamicScene {
DynamicScene {
resources: self.extracted_resources.into_values().collect(),
entities: self.extracted_scene.into_values().collect(),
}
}
Expand Down Expand Up @@ -284,25 +285,19 @@ impl<'w> DynamicSceneBuilder<'w> {
};

let original_entity = self.original_world.entity(entity);
if original_entity.contains_id(IS_RESOURCE) {
continue;
}

for &component_id in original_entity.archetype().components().iter() {
if self.component_filter.is_denied(component_id) {
continue;
}

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
Expand Down Expand Up @@ -349,52 +344,56 @@ impl<'w> DynamicSceneBuilder<'w> {
///
/// [`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_valid_resource_id(TypeId::of::<DefaultQueryFilters>());

let type_registry = self.original_world.resource::<AppTypeRegistry>().read();

for (component_id, entity) in self.original_world.resource_entities().iter() {
if Some(*component_id) == original_world_dqf_id {
continue;
pub fn extract_resources(self) -> Self {
let dqf_id = self.original_world.resource_id::<DefaultQueryFilters>();
let atr_id = self.original_world.resource_id::<AppTypeRegistry>();
let entities = match self.resource_filter {
SceneFilter::Unset => {
// extract all resources, excluding DefaultQueryFilters and AppTypeRegistry
let mut entities = vec![];
for (component_id, entity) in self.original_world.resource_entities().iter() {
if dqf_id.is_some_and(|id| *component_id == id)
|| atr_id.is_some_and(|id| *component_id == id)
{
continue;
}
entities.push(*entity);
}
entities
}
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;
SceneFilter::Allowlist(ref list) => {
// if DefaultQueryFilters or AppTypeRegistry has specifically been allowed to be added, we don't change that
let mut entities = vec![];
for component_id in list {
if let Some(entity) = self.original_world.resource_entities().get(*component_id)
{
entities.push(*entity);
}
}
entities
}
SceneFilter::Denylist(ref list) => {
// also deny DefaultQueryFilters and AppTypeRegistry
let mut deny_component_id = list.clone();
if let Some(id) = dqf_id {
deny_component_id.insert(id);
}
if let Some(id) = atr_id {
deny_component_id.insert(id);
}
let mut entities = vec![];
for (component_id, entity) in self.original_world.resource_entities().iter() {
if deny_component_id.contains(component_id) {
continue;
} else {
entities.push(*entity);
}
}
entities
}
};

let type_registration = type_registry.get(type_id)?;

type_registration.data::<ReflectResource>()?;
let component = type_registration
.data::<ReflectComponent>()?
.reflect(self.original_world.entity(*entity))?;

let component =
clone_reflect_value(component.as_partial_reflect(), type_registration);

self.extracted_resources.insert(*component_id, component);
Some(())
};
extract_and_push();
}

drop(type_registry);
self
self.extract_entities(entities.into_iter())
}
}

Expand Down Expand Up @@ -581,8 +580,8 @@ mod tests {
.extract_resources()
.build();

assert_eq!(scene.resources.len(), 1);
assert!(scene.resources[0].represents::<ResourceA>());
assert_eq!(scene.entities.len(), 1);
assert!(scene.entities[0].has::<ResourceA>());
}

#[test]
Expand All @@ -600,8 +599,8 @@ mod tests {
.extract_resources()
.build();

assert_eq!(scene.resources.len(), 1);
assert!(scene.resources[0].represents::<ResourceA>());
assert_eq!(scene.entities.len(), 1);
assert!(scene.entities[0].has::<ResourceA>());
}

#[test]
Expand Down Expand Up @@ -678,8 +677,9 @@ mod tests {
.extract_resources()
.build();

assert_eq!(scene.resources.len(), 1);
assert!(scene.resources[0].represents::<ResourceA>());
assert_eq!(scene.entities.len(), 2);
assert!(scene.entities[0].has::<ResourceB>());
assert!(scene.entities[1].has::<ResourceA>())
}

#[test]
Expand All @@ -702,8 +702,8 @@ mod tests {
.extract_resources()
.build();

assert_eq!(scene.resources.len(), 1);
assert!(scene.resources[0].represents::<ResourceB>());
assert_eq!(scene.entities.len(), 1);
assert!(scene.entities[0].has::<ResourceB>());
}

#[test]
Expand Down Expand Up @@ -739,8 +739,8 @@ mod tests {
.expect("component should be concrete due to `FromReflect`")
.is::<SomeType>());

let resource = &scene.resources[0];
assert!(resource
let resource = &scene.entities[1];
assert!(resource.components[0]
.try_as_reflect()
.expect("resource should be concrete due to `FromReflect`")
.is::<SomeResource>());
Expand Down
Loading