Merge 0e4643c2b9
into 877d278785
This commit is contained in:
commit
43ffe756fe
@ -10,7 +10,7 @@ use crate::{
|
|||||||
change_detection::MaybeLocation,
|
change_detection::MaybeLocation,
|
||||||
component::{
|
component::{
|
||||||
ComponentId, Components, ComponentsRegistrator, RequiredComponentConstructor,
|
ComponentId, Components, ComponentsRegistrator, RequiredComponentConstructor,
|
||||||
RequiredComponents, StorageType, Tick,
|
RequiredComponents, RequiredComponentsError, StorageType, Tick,
|
||||||
},
|
},
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
query::DebugCheckedUnwrap as _,
|
query::DebugCheckedUnwrap as _,
|
||||||
@ -75,6 +75,9 @@ pub struct BundleInfo {
|
|||||||
impl BundleInfo {
|
impl BundleInfo {
|
||||||
/// Create a new [`BundleInfo`].
|
/// Create a new [`BundleInfo`].
|
||||||
///
|
///
|
||||||
|
/// The `component_to_containing_bundles` parameter comes from [`Bundles::component_to_containing_bundles`],
|
||||||
|
/// see its documentation for more information.
|
||||||
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Every ID in `component_ids` must be valid within the World that owns the `BundleInfo`
|
/// Every ID in `component_ids` must be valid within the World that owns the `BundleInfo`
|
||||||
@ -83,7 +86,8 @@ impl BundleInfo {
|
|||||||
bundle_type_name: &'static str,
|
bundle_type_name: &'static str,
|
||||||
storages: &mut Storages,
|
storages: &mut Storages,
|
||||||
components: &Components,
|
components: &Components,
|
||||||
mut component_ids: Vec<ComponentId>,
|
component_to_containing_bundles: &mut Vec<Vec<BundleId>>,
|
||||||
|
component_ids: Vec<ComponentId>,
|
||||||
id: BundleId,
|
id: BundleId,
|
||||||
) -> BundleInfo {
|
) -> BundleInfo {
|
||||||
// check for duplicates
|
// check for duplicates
|
||||||
@ -111,41 +115,115 @@ impl BundleInfo {
|
|||||||
panic!("Bundle {bundle_type_name} has duplicate components: {names:?}");
|
panic!("Bundle {bundle_type_name} has duplicate components: {names:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle explicit components
|
|
||||||
let explicit_components_len = component_ids.len();
|
let explicit_components_len = component_ids.len();
|
||||||
let mut required_components = RequiredComponents::default();
|
|
||||||
for component_id in component_ids.iter().copied() {
|
|
||||||
// SAFETY: caller has verified that all ids are valid
|
|
||||||
let info = unsafe { components.get_info_unchecked(component_id) };
|
|
||||||
required_components.merge(info.required_components());
|
|
||||||
storages.prepare_component(info);
|
|
||||||
}
|
|
||||||
required_components.remove_explicit_components(&component_ids);
|
|
||||||
|
|
||||||
// handle required components
|
|
||||||
let required_components = required_components
|
|
||||||
.0
|
|
||||||
.into_iter()
|
|
||||||
.map(|(component_id, v)| {
|
|
||||||
// Safety: These ids came out of the passed `components`, so they must be valid.
|
|
||||||
let info = unsafe { components.get_info_unchecked(component_id) };
|
|
||||||
storages.prepare_component(info);
|
|
||||||
// This adds required components to the component_ids list _after_ using that list to remove explicitly provided
|
|
||||||
// components. This ordering is important!
|
|
||||||
component_ids.push(component_id);
|
|
||||||
v.constructor
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// SAFETY: The caller ensures that component_ids:
|
// SAFETY: The caller ensures that component_ids:
|
||||||
// - is valid for the associated world
|
// - is valid for the associated world
|
||||||
// - has had its storage initialized
|
// - has had its storage initialized
|
||||||
// - is in the same order as the source bundle type
|
// - is in the same order as the source bundle type
|
||||||
BundleInfo {
|
let mut info = BundleInfo {
|
||||||
id,
|
id,
|
||||||
component_ids,
|
component_ids,
|
||||||
required_components,
|
required_components: Vec::new(),
|
||||||
explicit_components_len,
|
explicit_components_len,
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY: first call with the same `storages` and `components`.
|
||||||
|
unsafe {
|
||||||
|
info.set_required_components(
|
||||||
|
storages,
|
||||||
|
components,
|
||||||
|
component_to_containing_bundles,
|
||||||
|
|_| true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
info
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update [`Self::component_ids`] from containing only the explicit components to additionally contain all required
|
||||||
|
/// components as well and update [`Self::required_components`] alongside.
|
||||||
|
///
|
||||||
|
/// The `component_to_containing_bundles` parameter comes from [`Bundles::component_to_containing_bundles`],
|
||||||
|
/// see its documentation for more information.
|
||||||
|
///
|
||||||
|
/// The filter `component_to_containing_bundles_filter` determines for which components [`Self::id`] is added to `component_to_containing_bundles`.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `Self` must be constructed from [`Self::new`] with the same `storages` and `components`.
|
||||||
|
unsafe fn set_required_components(
|
||||||
|
&mut self,
|
||||||
|
storages: &mut Storages,
|
||||||
|
components: &Components,
|
||||||
|
component_to_containing_bundles: &mut Vec<Vec<BundleId>>,
|
||||||
|
component_to_containing_bundles_filter: impl Fn(ComponentId) -> bool,
|
||||||
|
) {
|
||||||
|
let mut component_to_containing_bundles_push = |component: ComponentId| {
|
||||||
|
if !component_to_containing_bundles_filter(component) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
match component_to_containing_bundles.get_mut(component.index()) {
|
||||||
|
Some(bundles) => bundles.push(self.id),
|
||||||
|
None => {
|
||||||
|
component_to_containing_bundles.resize_with(component.index() + 1, Vec::new);
|
||||||
|
*component_to_containing_bundles.last_mut().unwrap() = vec![self.id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// handle explicit components
|
||||||
|
let mut required_components = RequiredComponents::default();
|
||||||
|
for component_id in self.component_ids.iter().copied() {
|
||||||
|
// SAFETY: caller of initial constructor has verified that all ids are valid
|
||||||
|
let info = unsafe { components.get_info_unchecked(component_id) };
|
||||||
|
required_components.merge(info.required_components());
|
||||||
|
storages.prepare_component(info);
|
||||||
|
component_to_containing_bundles_push(component_id);
|
||||||
|
}
|
||||||
|
required_components.remove_explicit_components(&self.component_ids);
|
||||||
|
|
||||||
|
// handle required components, clear first in case this gets called again for this bundle
|
||||||
|
self.required_components.clear();
|
||||||
|
self.required_components
|
||||||
|
.extend(required_components.0.into_iter().map(|(component_id, v)| {
|
||||||
|
// Safety: These ids came out of the passed `components`, so they must be valid.
|
||||||
|
let info = unsafe { components.get_info_unchecked(component_id) };
|
||||||
|
storages.prepare_component(info);
|
||||||
|
// This adds required components to the component_ids list _after_ using that list to remove explicitly provided
|
||||||
|
// components. This ordering is important!
|
||||||
|
self.component_ids.push(component_id);
|
||||||
|
component_to_containing_bundles_push(component_id);
|
||||||
|
v.constructor
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates required components for new runtime-added components.
|
||||||
|
///
|
||||||
|
/// The `component_to_containing_bundles` parameter comes from [`Bundles::component_to_containing_bundles`],
|
||||||
|
/// see its documentation for more information.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `Self` must be constructed from [`Self::new`] with the same `storages` and `components`.
|
||||||
|
unsafe fn reset_required_components(
|
||||||
|
&mut self,
|
||||||
|
storages: &mut Storages,
|
||||||
|
components: &Components,
|
||||||
|
component_to_containing_bundles: &mut Vec<Vec<BundleId>>,
|
||||||
|
) {
|
||||||
|
let registered_component_to_containing_bundles: HashSet<_> =
|
||||||
|
self.component_ids.iter().copied().collect();
|
||||||
|
self.component_ids.truncate(self.explicit_components_len);
|
||||||
|
|
||||||
|
// SAFETY: caller ensured the same contract is fulfilled
|
||||||
|
unsafe {
|
||||||
|
self.set_required_components(
|
||||||
|
storages,
|
||||||
|
components,
|
||||||
|
component_to_containing_bundles,
|
||||||
|
|component| !registered_component_to_containing_bundles.contains(&component),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,6 +446,9 @@ pub struct Bundles {
|
|||||||
/// Cache optimized dynamic [`BundleId`] with single component
|
/// Cache optimized dynamic [`BundleId`] with single component
|
||||||
dynamic_component_bundle_ids: HashMap<ComponentId, BundleId>,
|
dynamic_component_bundle_ids: HashMap<ComponentId, BundleId>,
|
||||||
dynamic_component_storages: HashMap<BundleId, StorageType>,
|
dynamic_component_storages: HashMap<BundleId, StorageType>,
|
||||||
|
/// Cache [`BundleId`]s that contain a certain [`ComponentId`], which uses
|
||||||
|
/// [`ComponentId::index`] as the index of the outer vector into the inner
|
||||||
|
component_to_containing_bundles: Vec<Vec<BundleId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bundles {
|
impl Bundles {
|
||||||
@ -419,7 +500,7 @@ impl Bundles {
|
|||||||
// - its info was created
|
// - its info was created
|
||||||
// - appropriate storage for it has been initialized.
|
// - appropriate storage for it has been initialized.
|
||||||
// - it was created in the same order as the components in T
|
// - it was created in the same order as the components in T
|
||||||
unsafe { BundleInfo::new(core::any::type_name::<T>(), storages, components, component_ids, id) };
|
unsafe { BundleInfo::new(core::any::type_name::<T>(), storages, components, &mut self.component_to_containing_bundles, component_ids, id) };
|
||||||
bundle_infos.push(bundle_info);
|
bundle_infos.push(bundle_info);
|
||||||
id
|
id
|
||||||
})
|
})
|
||||||
@ -428,6 +509,8 @@ impl Bundles {
|
|||||||
/// Registers a new [`BundleInfo`], which contains both explicit and required components for a statically known type.
|
/// Registers a new [`BundleInfo`], which contains both explicit and required components for a statically known type.
|
||||||
///
|
///
|
||||||
/// Also registers all the components in the bundle.
|
/// Also registers all the components in the bundle.
|
||||||
|
///
|
||||||
|
/// Using this forbids components in this bundle to add more required components via [`Self::refresh_required_components`].
|
||||||
pub(crate) fn register_contributed_bundle_info<T: Bundle>(
|
pub(crate) fn register_contributed_bundle_info<T: Bundle>(
|
||||||
&mut self,
|
&mut self,
|
||||||
components: &mut ComponentsRegistrator,
|
components: &mut ComponentsRegistrator,
|
||||||
@ -436,7 +519,7 @@ impl Bundles {
|
|||||||
if let Some(id) = self.contributed_bundle_ids.get(&TypeId::of::<T>()).cloned() {
|
if let Some(id) = self.contributed_bundle_ids.get(&TypeId::of::<T>()).cloned() {
|
||||||
id
|
id
|
||||||
} else {
|
} else {
|
||||||
let explicit_bundle_id = self.register_info::<T>(components, storages);
|
let explicit_bundle_id: BundleId = self.register_info::<T>(components, storages);
|
||||||
// SAFETY: reading from `explicit_bundle_id` and creating new bundle in same time. Its valid because bundle hashmap allow this
|
// SAFETY: reading from `explicit_bundle_id` and creating new bundle in same time. Its valid because bundle hashmap allow this
|
||||||
let id = unsafe {
|
let id = unsafe {
|
||||||
let (ptr, len) = {
|
let (ptr, len) = {
|
||||||
@ -502,6 +585,7 @@ impl Bundles {
|
|||||||
.or_insert_with(|| {
|
.or_insert_with(|| {
|
||||||
let (id, storages) = initialize_dynamic_bundle(
|
let (id, storages) = initialize_dynamic_bundle(
|
||||||
bundle_infos,
|
bundle_infos,
|
||||||
|
&mut self.component_to_containing_bundles,
|
||||||
storages,
|
storages,
|
||||||
components,
|
components,
|
||||||
Vec::from(component_ids),
|
Vec::from(component_ids),
|
||||||
@ -534,6 +618,7 @@ impl Bundles {
|
|||||||
.or_insert_with(|| {
|
.or_insert_with(|| {
|
||||||
let (id, storage_type) = initialize_dynamic_bundle(
|
let (id, storage_type) = initialize_dynamic_bundle(
|
||||||
bundle_infos,
|
bundle_infos,
|
||||||
|
&mut self.component_to_containing_bundles,
|
||||||
storages,
|
storages,
|
||||||
components,
|
components,
|
||||||
vec![component_id],
|
vec![component_id],
|
||||||
@ -543,12 +628,111 @@ impl Bundles {
|
|||||||
});
|
});
|
||||||
*bundle_id
|
*bundle_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if the bundles containing `requiree` can have their required components be updated,
|
||||||
|
/// in which case this returns `Ok(true)`.
|
||||||
|
///
|
||||||
|
/// Returns `Ok(false)` if there are no known bundles with this component.
|
||||||
|
///
|
||||||
|
/// Returns the [`RequiredComponentsError::RemovedFromArchetype`] error if bundles cannot be updated
|
||||||
|
/// in which case the registration of any new required component must be refused.
|
||||||
|
/// This does not check for [`RequiredComponentsError::ArchetypeExists`] as that is done by
|
||||||
|
/// [`Components::register_required_components`] that needs to be called before
|
||||||
|
/// [`Self::refresh_required_components`] anyway.
|
||||||
|
pub(crate) fn verify_to_refresh_required_components(
|
||||||
|
&self,
|
||||||
|
requiree: ComponentId,
|
||||||
|
) -> Result<bool, RequiredComponentsError> {
|
||||||
|
let Some(bundles_with_requiree) = self
|
||||||
|
.component_to_containing_bundles
|
||||||
|
.get(requiree.index())
|
||||||
|
.filter(|bundles| !bundles.is_empty())
|
||||||
|
else {
|
||||||
|
// no bundle with `requiree` exists
|
||||||
|
return Ok(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
// `EntityWorldMut::remove_with_requires` generate edges between archetypes where the required
|
||||||
|
// components matter for the the target archetype. These bundles cannot receive new required
|
||||||
|
// components as that would invalidate these edges.
|
||||||
|
// The mechanism is using `Self::contributed_bundle_ids` to track the bundles so we check here if it
|
||||||
|
// has any overlapping `BundleId`s with `bundles_with_requiree`.
|
||||||
|
// It would also be an error if the bundle is part of any archetype (edge) involving inserts,
|
||||||
|
// but that error is checked on by `Components::register_required_components` so doing that here
|
||||||
|
// would be redundant.
|
||||||
|
// TODO: Remove this error and update archetype edges accordingly when required components are added
|
||||||
|
if bundles_with_requiree.iter().any(|bundles_with_requiree| {
|
||||||
|
self.contributed_bundle_ids
|
||||||
|
.values()
|
||||||
|
.any(|bundle: &BundleId| bundles_with_requiree == bundle)
|
||||||
|
}) {
|
||||||
|
return Err(RequiredComponentsError::RemovedFromArchetype(requiree));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the required components of bundles that contain `requiree`.
|
||||||
|
///
|
||||||
|
/// This assumes that `requiree` cannot have been added itself as a new required component.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller assures that immediately prior calling this, ...
|
||||||
|
/// - [`Self::verify_to_refresh_required_components`] returned `Ok(true)` for `requiree`
|
||||||
|
/// - [`Components::register_required_components`] did not return `Err` for `requiree`
|
||||||
|
/// - and `storages` and `components` must be the same that were used to create the stored bundles
|
||||||
|
pub(crate) unsafe fn refresh_required_components(
|
||||||
|
&mut self,
|
||||||
|
storages: &mut Storages,
|
||||||
|
components: &Components,
|
||||||
|
requiree: ComponentId,
|
||||||
|
) {
|
||||||
|
// take list of bundles to update `Self::bundles_with_requiree` while iterating this
|
||||||
|
let taken_bundles_with_requiree = self
|
||||||
|
.component_to_containing_bundles
|
||||||
|
.get_mut(requiree.index())
|
||||||
|
.filter(|bundles| !bundles.is_empty())
|
||||||
|
.map(core::mem::take)
|
||||||
|
.expect("verify_to_refresh_required_components confirmed existing bundles to refresh");
|
||||||
|
|
||||||
|
for bundle_id in taken_bundles_with_requiree.iter() {
|
||||||
|
let bundle_info = self.bundle_infos.get_mut(bundle_id.index());
|
||||||
|
|
||||||
|
// SAFETY: BundleIds in Self::component_to_containing_bundles are valid ids of Self::bundle_infos
|
||||||
|
let bundle_info = unsafe { bundle_info.debug_checked_unwrap() };
|
||||||
|
|
||||||
|
// SAFETY: Caller ensured `storages` and `components` match those used to create this bundle
|
||||||
|
unsafe {
|
||||||
|
bundle_info.reset_required_components(
|
||||||
|
storages,
|
||||||
|
components,
|
||||||
|
&mut self.component_to_containing_bundles,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// put the list of bundles with `requiree` back
|
||||||
|
let replaced = self
|
||||||
|
.component_to_containing_bundles
|
||||||
|
.get_mut(requiree.index());
|
||||||
|
|
||||||
|
// SAFETY: this list has to exist at this index to be taken in the first place
|
||||||
|
let replaced = unsafe { replaced.debug_checked_unwrap() };
|
||||||
|
|
||||||
|
// put the bundles back, the placeholder that was put in its place earlier cannot have been updated
|
||||||
|
// because only the lists of the newly required components get new bundles added.
|
||||||
|
// if that happened for this list, that would mean `requiree` would be its own new required component,
|
||||||
|
// which is invalid.
|
||||||
|
*replaced = taken_bundles_with_requiree;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asserts that all components are part of [`Components`]
|
/// Asserts that all components are part of [`Components`]
|
||||||
/// and initializes a [`BundleInfo`].
|
/// and initializes a [`BundleInfo`].
|
||||||
fn initialize_dynamic_bundle(
|
fn initialize_dynamic_bundle(
|
||||||
bundle_infos: &mut Vec<BundleInfo>,
|
bundle_infos: &mut Vec<BundleInfo>,
|
||||||
|
component_to_containing_bundles: &mut Vec<Vec<BundleId>>,
|
||||||
storages: &mut Storages,
|
storages: &mut Storages,
|
||||||
components: &Components,
|
components: &Components,
|
||||||
component_ids: Vec<ComponentId>,
|
component_ids: Vec<ComponentId>,
|
||||||
@ -565,7 +749,7 @@ fn initialize_dynamic_bundle(
|
|||||||
let id = BundleId(bundle_infos.len());
|
let id = BundleId(bundle_infos.len());
|
||||||
let bundle_info =
|
let bundle_info =
|
||||||
// SAFETY: `component_ids` are valid as they were just checked
|
// SAFETY: `component_ids` are valid as they were just checked
|
||||||
unsafe { BundleInfo::new("<dynamic bundle>", storages, components, component_ids, id) };
|
unsafe { BundleInfo::new("<dynamic bundle>", storages, components, component_to_containing_bundles, component_ids, id) };
|
||||||
bundle_infos.push(bundle_info);
|
bundle_infos.push(bundle_info);
|
||||||
|
|
||||||
(id, storage_types)
|
(id, storage_types)
|
||||||
|
@ -24,7 +24,8 @@ impl Components {
|
|||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// The given component IDs `required` and `requiree` must be valid.
|
/// - The given component IDs `required` and `requiree` must be valid
|
||||||
|
/// - [`crate::bundle::Bundles::verify_to_refresh_required_components`] returned `Ok` for `requiree`
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
@ -271,9 +272,12 @@ pub enum RequiredComponentsError {
|
|||||||
/// The component is already a directly required component for the requiree.
|
/// The component is already a directly required component for the requiree.
|
||||||
#[error("Component {0:?} already directly requires component {1:?}")]
|
#[error("Component {0:?} already directly requires component {1:?}")]
|
||||||
DuplicateRegistration(ComponentId, ComponentId),
|
DuplicateRegistration(ComponentId, ComponentId),
|
||||||
/// An archetype with the component that requires other components already exists
|
/// An archetype with the component that requires other components already exists.
|
||||||
#[error("An archetype with the component {0:?} that requires other components already exists")]
|
#[error("An archetype with the component {0:?} that requires other components already exists")]
|
||||||
ArchetypeExists(ComponentId),
|
ArchetypeExists(ComponentId),
|
||||||
|
/// A bundle with the component that requires other components was removed via [`crate::world::EntityWorldMut::remove_with_requires`].
|
||||||
|
#[error("A bundle with the component {0:?} that requires other components was removed including required components previously")]
|
||||||
|
RemovedFromArchetype(ComponentId),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Required Component constructor. See [`Component`] for details.
|
/// A Required Component constructor. See [`Component`] for details.
|
||||||
|
@ -2473,6 +2473,26 @@ mod tests {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn runtime_required_components_of_remove_with_requires_bundle() {
|
||||||
|
#[derive(Component)]
|
||||||
|
struct A;
|
||||||
|
|
||||||
|
#[derive(Component, Default)]
|
||||||
|
struct B;
|
||||||
|
|
||||||
|
let mut world = World::new();
|
||||||
|
|
||||||
|
// This should fail, removing A from an archetype with only B was previously noop
|
||||||
|
// and register_required_components cannot trivially update the edge to now cause
|
||||||
|
// a move to an archetype without A
|
||||||
|
world.spawn(B).remove_with_requires::<A>();
|
||||||
|
assert!(matches!(
|
||||||
|
world.try_register_required_components::<A, B>(),
|
||||||
|
Err(RequiredComponentsError::RemovedFromArchetype(_))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn required_components_inheritance_depth() {
|
fn required_components_inheritance_depth() {
|
||||||
// Test that inheritance depths are computed correctly for requirements.
|
// Test that inheritance depths are computed correctly for requirements.
|
||||||
@ -2556,6 +2576,66 @@ mod tests {
|
|||||||
assert_eq!(to_vec(required_z), vec![(b, 0), (c, 1)]);
|
assert_eq!(to_vec(required_z), vec![(b, 0), (c, 1)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn update_required_components_in_bundle() {
|
||||||
|
#[derive(Component)]
|
||||||
|
#[require(B)]
|
||||||
|
struct A;
|
||||||
|
|
||||||
|
#[derive(Component, Default)]
|
||||||
|
struct B;
|
||||||
|
|
||||||
|
#[derive(Component, Default)]
|
||||||
|
#[require(D)]
|
||||||
|
struct C;
|
||||||
|
|
||||||
|
#[derive(Component, Default)]
|
||||||
|
struct D;
|
||||||
|
|
||||||
|
#[derive(Component, Default)]
|
||||||
|
struct E;
|
||||||
|
|
||||||
|
let mut world = World::new();
|
||||||
|
|
||||||
|
let a_id = world.register_component::<A>();
|
||||||
|
let b_id = world.register_component::<B>();
|
||||||
|
let c_id = world.register_component::<C>();
|
||||||
|
let d_id = world.register_component::<D>();
|
||||||
|
let e_id = world.register_component::<E>();
|
||||||
|
|
||||||
|
let bundle = world.register_bundle::<A>();
|
||||||
|
let bundle_id = bundle.id();
|
||||||
|
let contributed: HashSet<_> = bundle.contributed_components().iter().copied().collect();
|
||||||
|
|
||||||
|
assert!(contributed.contains(&a_id));
|
||||||
|
assert!(contributed.contains(&b_id));
|
||||||
|
assert!(!contributed.contains(&c_id));
|
||||||
|
assert!(!contributed.contains(&d_id));
|
||||||
|
assert!(!contributed.contains(&e_id));
|
||||||
|
|
||||||
|
// check if registration succeeds
|
||||||
|
world.register_required_components::<B, C>();
|
||||||
|
let bundle = world.bundles().get(bundle_id).unwrap();
|
||||||
|
let contributed: HashSet<_> = bundle.contributed_components().iter().copied().collect();
|
||||||
|
|
||||||
|
assert!(contributed.contains(&a_id));
|
||||||
|
assert!(contributed.contains(&b_id));
|
||||||
|
assert!(contributed.contains(&c_id));
|
||||||
|
assert!(contributed.contains(&d_id));
|
||||||
|
assert!(!contributed.contains(&e_id));
|
||||||
|
|
||||||
|
// check if another registration can be associated to the bundle using the previously registered component
|
||||||
|
world.register_required_components::<D, E>();
|
||||||
|
let bundle = world.bundles().get(bundle_id).unwrap();
|
||||||
|
let contributed: HashSet<_> = bundle.contributed_components().iter().copied().collect();
|
||||||
|
|
||||||
|
assert!(contributed.contains(&a_id));
|
||||||
|
assert!(contributed.contains(&b_id));
|
||||||
|
assert!(contributed.contains(&c_id));
|
||||||
|
assert!(contributed.contains(&d_id));
|
||||||
|
assert!(contributed.contains(&e_id));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn required_components_inheritance_depth_bias() {
|
fn required_components_inheritance_depth_bias() {
|
||||||
#[derive(Component, PartialEq, Eq, Clone, Copy, Debug)]
|
#[derive(Component, PartialEq, Eq, Clone, Copy, Debug)]
|
||||||
|
@ -2144,6 +2144,8 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// If the entity has been despawned while this `EntityWorldMut` is still alive.
|
/// If the entity has been despawned while this `EntityWorldMut` is still alive.
|
||||||
|
// Implementation note: Using this forbids components in this bundle to add more required components
|
||||||
|
// See `Bundles::verify_to_refresh_required_components`
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn remove_with_requires<T: Bundle>(&mut self) -> &mut Self {
|
pub fn remove_with_requires<T: Bundle>(&mut self) -> &mut Self {
|
||||||
self.remove_with_requires_with_caller::<T>(MaybeLocation::caller())
|
self.remove_with_requires_with_caller::<T>(MaybeLocation::caller())
|
||||||
|
@ -535,18 +535,35 @@ impl World {
|
|||||||
) -> Result<(), RequiredComponentsError> {
|
) -> Result<(), RequiredComponentsError> {
|
||||||
let requiree = self.register_component::<T>();
|
let requiree = self.register_component::<T>();
|
||||||
|
|
||||||
// TODO: Remove this panic and update archetype edges accordingly when required components are added
|
// TODO: Remove this error and update archetype edges accordingly when required components are added
|
||||||
if self.archetypes().component_index().contains_key(&requiree) {
|
if self.archetypes().component_index().contains_key(&requiree) {
|
||||||
return Err(RequiredComponentsError::ArchetypeExists(requiree));
|
return Err(RequiredComponentsError::ArchetypeExists(requiree));
|
||||||
}
|
}
|
||||||
|
|
||||||
let required = self.register_component::<R>();
|
let required = self.register_component::<R>();
|
||||||
|
|
||||||
|
let update_bundles = self
|
||||||
|
.bundles
|
||||||
|
.verify_to_refresh_required_components(requiree)?;
|
||||||
|
|
||||||
// SAFETY: We just created the `required` and `requiree` components.
|
// SAFETY: We just created the `required` and `requiree` components.
|
||||||
unsafe {
|
unsafe {
|
||||||
self.components
|
self.components
|
||||||
.register_required_components::<R>(requiree, required, constructor)
|
.register_required_components::<R>(requiree, required, constructor)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if update_bundles {
|
||||||
|
// SAFETY: all bundles are created with Self::storages and Self::components
|
||||||
|
unsafe {
|
||||||
|
self.bundles.refresh_required_components(
|
||||||
|
&mut self.storages,
|
||||||
|
&self.components,
|
||||||
|
requiree,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves the [required components](RequiredComponents) for the given component type, if it exists.
|
/// Retrieves the [required components](RequiredComponents) for the given component type, if it exists.
|
||||||
|
Loading…
Reference in New Issue
Block a user