Queued component registration (#18173)
# Objective This is an alternative to #17871 and #17701 for tracking issue #18155. This thanks to @maniwani for help with this design. The goal is to enable component ids to be reserved from multiple threads concurrently and with only `&World`. This contributes to assets as entities, read-only query and system parameter initialization, etc. ## What's wrong with #17871 ? In #17871, I used my proposed staging utilities to allow *fully* registering components from any thread concurrently with only `&Components`. However, if we want to pursue components as entities (which is desirable for a great many reasons. See [here](https://discord.com/channels/691052431525675048/692572690833473578/1346499196655505534) on discord), this staging isn't going to work. After all, if registering a component requires spawning an entity, and spawning an entity requires `&mut World`, it is impossible to register a component fully with only `&World`. ## Solution But what if we don't have to register it all the way? What if it's enough to just know the `ComponentId` it will have once it is registered and to queue it to be registered at a later time? Spoiler alert: That is all we need for these features. Here's the basic design: Queue a registration: 1. Check if it has already been registered. 2. Check if it has already been queued. 3. Reserve a `ComponentId`. 4. Queue the registration at that id. Direct (normal) registration: 1. Check if this registration has been queued. 2. If it has, use the queued registration instead. 3. Otherwise, proceed like normal. Appllying the queue: 1. Pop queued items off one by one. 2. Register them directly. One other change: The whole point of this design over #17871 is to facilitate coupling component registration with the World. To ensure that this would fully work with that, I went ahead and moved the `ComponentId` generator onto the world itself. That stemmed a couple of minor organizational changes (see migration guide). As we do components as entities, we will replace this generator with `Entities`, which lives on `World` too. Doing this move early let me verify the design and will reduce migration headaches in the future. If components as entities is as close as I think it is, I don't think splitting this up into different PRs is worth it. If it is not as close as it is, it might make sense to still do #17871 in the meantime (see the risks section). I'll leave it up to y'all what we end up doing though. ## Risks and Testing The biggest downside of this compared to #17871 is that now we have to deal with correct but invalid `ComponentId`s. They are invalid because the component still isn't registered, but they are correct because, once registered, the component will have exactly that id. However, the only time this becomes a problem is if some code violates safety rules by queuing a registration and using the returned id as if it was valid. As this is a new feature though, nothing in Bevy does this, so no new tests were added for it. When we do use it, I left detailed docs to help mitigate issues here, and we can test those usages. Ex: we will want some tests on using queries initialized from queued registrations. ## Migration Guide Component registration can now be queued with only `&World`. To facilitate this, a few APIs needed to be moved around. The following functions have moved from `Components` to `ComponentsRegistrator`: - `register_component` - `register_component_with_descriptor` - `register_resource_with_descriptor` - `register_non_send` - `register_resource` - `register_required_components_manual` Accordingly, functions in `Bundle` and `Component` now take `ComponentsRegistrator` instead of `Components`. You can obtain `ComponentsRegistrator` from the new `World::components_registrator`. You can obtain `ComponentsQueuedRegistrator` from the new `World::components_queue`, and use it to stage component registration if desired. # Open Question Can we verify that it is enough to queue registration with `&World`? I don't think it would be too difficult to package this up into a `Arc<MyComponentsManager>` type thing if we need to, but keeping this on `&World` certainly simplifies things. If we do need the `Arc`, we'll need to look into partitioning `Entities` for components as entities, so we can keep most of the allocation fast on `World` and only keep a smaller partition in the `Arc`. I'd love an SME on assets as entities to shed some light on this. --------- Co-authored-by: andriyDev <andriydzikh@gmail.com>
This commit is contained in:
parent
b46d9f0825
commit
246ce590e5
@ -272,7 +272,7 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
|
||||
type Mutability = #mutable_type;
|
||||
fn register_required_components(
|
||||
requiree: #bevy_ecs_path::component::ComponentId,
|
||||
components: &mut #bevy_ecs_path::component::Components,
|
||||
components: &mut #bevy_ecs_path::component::ComponentsRegistrator,
|
||||
required_components: &mut #bevy_ecs_path::component::RequiredComponents,
|
||||
inheritance_depth: u16,
|
||||
recursion_check_stack: &mut #bevy_ecs_path::__macro_exports::Vec<#bevy_ecs_path::component::ComponentId>
|
||||
|
@ -134,7 +134,7 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
|
||||
#[allow(deprecated)]
|
||||
unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name #ty_generics #where_clause {
|
||||
fn component_ids(
|
||||
components: &mut #ecs_path::component::Components,
|
||||
components: &mut #ecs_path::component::ComponentsRegistrator,
|
||||
ids: &mut impl FnMut(#ecs_path::component::ComponentId)
|
||||
){
|
||||
#(#field_component_ids)*
|
||||
@ -148,7 +148,7 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
|
||||
}
|
||||
|
||||
fn register_required_components(
|
||||
components: &mut #ecs_path::component::Components,
|
||||
components: &mut #ecs_path::component::ComponentsRegistrator,
|
||||
required_components: &mut #ecs_path::component::RequiredComponents
|
||||
){
|
||||
#(#field_required_components)*
|
||||
|
@ -11,8 +11,8 @@ use crate::{
|
||||
},
|
||||
change_detection::MaybeLocation,
|
||||
component::{
|
||||
Component, ComponentId, Components, RequiredComponentConstructor, RequiredComponents,
|
||||
StorageType, Tick,
|
||||
Component, ComponentId, Components, ComponentsRegistrator, RequiredComponentConstructor,
|
||||
RequiredComponents, StorageType, Tick,
|
||||
},
|
||||
entity::{Entities, Entity, EntityLocation},
|
||||
observer::Observers,
|
||||
@ -31,7 +31,7 @@ use variadics_please::all_tuples;
|
||||
|
||||
/// The `Bundle` trait enables insertion and removal of [`Component`]s from an entity.
|
||||
///
|
||||
/// Implementors of the `Bundle` trait are called 'bundles'.
|
||||
/// Implementers of the `Bundle` trait are called 'bundles'.
|
||||
///
|
||||
/// Each bundle represents a static set of [`Component`] types.
|
||||
/// Currently, bundles can only contain one of each [`Component`], and will
|
||||
@ -72,7 +72,7 @@ use variadics_please::all_tuples;
|
||||
/// That is, if the entity does not have all the components of the bundle, those
|
||||
/// which are present will be removed.
|
||||
///
|
||||
/// # Implementors
|
||||
/// # Implementers
|
||||
///
|
||||
/// Every type which implements [`Component`] also implements `Bundle`, since
|
||||
/// [`Component`] types can be added to or removed from an entity.
|
||||
@ -151,14 +151,14 @@ use variadics_please::all_tuples;
|
||||
pub unsafe trait Bundle: DynamicBundle + Send + Sync + 'static {
|
||||
/// Gets this [`Bundle`]'s component ids, in the order of this bundle's [`Component`]s
|
||||
#[doc(hidden)]
|
||||
fn component_ids(components: &mut Components, ids: &mut impl FnMut(ComponentId));
|
||||
fn component_ids(components: &mut ComponentsRegistrator, ids: &mut impl FnMut(ComponentId));
|
||||
|
||||
/// Gets this [`Bundle`]'s component ids. This will be [`None`] if the component has not been registered.
|
||||
fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option<ComponentId>));
|
||||
|
||||
/// Registers components that are required by the components in this [`Bundle`].
|
||||
fn register_required_components(
|
||||
_components: &mut Components,
|
||||
_components: &mut ComponentsRegistrator,
|
||||
_required_components: &mut RequiredComponents,
|
||||
);
|
||||
}
|
||||
@ -223,12 +223,12 @@ pub trait BundleEffect {
|
||||
// - `Bundle::component_ids` calls `ids` for C's component id (and nothing else)
|
||||
// - `Bundle::get_components` is called exactly once for C and passes the component's storage type based on its associated constant.
|
||||
unsafe impl<C: Component> Bundle for C {
|
||||
fn component_ids(components: &mut Components, ids: &mut impl FnMut(ComponentId)) {
|
||||
fn component_ids(components: &mut ComponentsRegistrator, ids: &mut impl FnMut(ComponentId)) {
|
||||
ids(components.register_component::<C>());
|
||||
}
|
||||
|
||||
fn register_required_components(
|
||||
components: &mut Components,
|
||||
components: &mut ComponentsRegistrator,
|
||||
required_components: &mut RequiredComponents,
|
||||
) {
|
||||
let component_id = components.register_component::<C>();
|
||||
@ -288,7 +288,7 @@ macro_rules! tuple_impl {
|
||||
// - `Bundle::get_components` is called exactly once for each member. Relies on the above implementation to pass the correct
|
||||
// `StorageType` into the callback.
|
||||
unsafe impl<$($name: Bundle),*> Bundle for ($($name,)*) {
|
||||
fn component_ids(components: &mut Components, ids: &mut impl FnMut(ComponentId)){
|
||||
fn component_ids(components: &mut ComponentsRegistrator, ids: &mut impl FnMut(ComponentId)){
|
||||
$(<$name as Bundle>::component_ids(components, ids);)*
|
||||
}
|
||||
|
||||
@ -297,7 +297,7 @@ macro_rules! tuple_impl {
|
||||
}
|
||||
|
||||
fn register_required_components(
|
||||
components: &mut Components,
|
||||
components: &mut ComponentsRegistrator,
|
||||
required_components: &mut RequiredComponents,
|
||||
) {
|
||||
$(<$name as Bundle>::register_required_components(components, required_components);)*
|
||||
@ -999,9 +999,12 @@ impl<'w> BundleInserter<'w> {
|
||||
archetype_id: ArchetypeId,
|
||||
change_tick: Tick,
|
||||
) -> Self {
|
||||
// SAFETY: These come from the same world. `world.components_registrator` can't be used since we borrow other fields too.
|
||||
let mut registrator =
|
||||
unsafe { ComponentsRegistrator::new(&mut world.components, &mut world.component_ids) };
|
||||
let bundle_id = world
|
||||
.bundles
|
||||
.register_info::<T>(&mut world.components, &mut world.storages);
|
||||
.register_info::<T>(&mut registrator, &mut world.storages);
|
||||
// SAFETY: We just ensured this bundle exists
|
||||
unsafe { Self::new_with_id(world, archetype_id, bundle_id, change_tick) }
|
||||
}
|
||||
@ -1369,9 +1372,12 @@ pub(crate) struct BundleSpawner<'w> {
|
||||
impl<'w> BundleSpawner<'w> {
|
||||
#[inline]
|
||||
pub fn new<T: Bundle>(world: &'w mut World, change_tick: Tick) -> Self {
|
||||
// SAFETY: These come from the same world. `world.components_registrator` can't be used since we borrow other fields too.
|
||||
let mut registrator =
|
||||
unsafe { ComponentsRegistrator::new(&mut world.components, &mut world.component_ids) };
|
||||
let bundle_id = world
|
||||
.bundles
|
||||
.register_info::<T>(&mut world.components, &mut world.storages);
|
||||
.register_info::<T>(&mut registrator, &mut world.storages);
|
||||
// SAFETY: we initialized this bundle_id in `init_info`
|
||||
unsafe { Self::new_with_id(world, bundle_id, change_tick) }
|
||||
}
|
||||
@ -1574,7 +1580,7 @@ impl Bundles {
|
||||
/// Also registers all the components in the bundle.
|
||||
pub(crate) fn register_info<T: Bundle>(
|
||||
&mut self,
|
||||
components: &mut Components,
|
||||
components: &mut ComponentsRegistrator,
|
||||
storages: &mut Storages,
|
||||
) -> BundleId {
|
||||
let bundle_infos = &mut self.bundle_infos;
|
||||
@ -1599,7 +1605,7 @@ impl Bundles {
|
||||
/// Also registers all the components in the bundle.
|
||||
pub(crate) fn register_contributed_bundle_info<T: Bundle>(
|
||||
&mut self,
|
||||
components: &mut Components,
|
||||
components: &mut ComponentsRegistrator,
|
||||
storages: &mut Storages,
|
||||
) -> BundleId {
|
||||
if let Some(id) = self.contributed_bundle_ids.get(&TypeId::of::<T>()).cloned() {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -229,7 +229,7 @@ mod tests {
|
||||
y: SparseStored,
|
||||
}
|
||||
let mut ids = Vec::new();
|
||||
<FooBundle as Bundle>::component_ids(&mut world.components, &mut |id| {
|
||||
<FooBundle as Bundle>::component_ids(&mut world.components_registrator(), &mut |id| {
|
||||
ids.push(id);
|
||||
});
|
||||
|
||||
@ -279,7 +279,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let mut ids = Vec::new();
|
||||
<NestedBundle as Bundle>::component_ids(&mut world.components, &mut |id| {
|
||||
<NestedBundle as Bundle>::component_ids(&mut world.components_registrator(), &mut |id| {
|
||||
ids.push(id);
|
||||
});
|
||||
|
||||
@ -331,9 +331,12 @@ mod tests {
|
||||
}
|
||||
|
||||
let mut ids = Vec::new();
|
||||
<BundleWithIgnored as Bundle>::component_ids(&mut world.components, &mut |id| {
|
||||
ids.push(id);
|
||||
});
|
||||
<BundleWithIgnored as Bundle>::component_ids(
|
||||
&mut world.components_registrator(),
|
||||
&mut |id| {
|
||||
ids.push(id);
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(ids, &[world.register_component::<C>(),]);
|
||||
|
||||
|
@ -435,7 +435,7 @@ fn hook_on_add<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
|
||||
world.commands().queue(move |world: &mut World| {
|
||||
let event_id = E::register_component_id(world);
|
||||
let mut components = Vec::new();
|
||||
B::component_ids(&mut world.components, &mut |id| {
|
||||
B::component_ids(&mut world.components_registrator(), &mut |id| {
|
||||
components.push(id);
|
||||
});
|
||||
let mut descriptor = ObserverDescriptor {
|
||||
|
@ -863,7 +863,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn init_state(world: &mut World) -> Self::State {
|
||||
world.components.register_resource::<R>()
|
||||
world.components_registrator().register_resource::<R>()
|
||||
}
|
||||
|
||||
fn get_state(components: &Components) -> Option<Self::State> {
|
||||
|
@ -146,7 +146,7 @@ impl Schedules {
|
||||
/// Ignore system order ambiguities caused by conflicts on [`Resource`]s of type `T`.
|
||||
pub fn allow_ambiguous_resource<T: Resource>(&mut self, world: &mut World) {
|
||||
self.ignored_scheduling_ambiguities
|
||||
.insert(world.components.register_resource::<T>());
|
||||
.insert(world.components_registrator().register_resource::<T>());
|
||||
}
|
||||
|
||||
/// Iterate through the [`ComponentId`]'s that will be ignored.
|
||||
|
@ -175,7 +175,7 @@ unsafe impl<R: Relationship, L: SpawnableList<R> + Send + Sync + 'static> Bundle
|
||||
for SpawnRelatedBundle<R, L>
|
||||
{
|
||||
fn component_ids(
|
||||
components: &mut crate::component::Components,
|
||||
components: &mut crate::component::ComponentsRegistrator,
|
||||
ids: &mut impl FnMut(crate::component::ComponentId),
|
||||
) {
|
||||
<R::RelationshipTarget as Bundle>::component_ids(components, ids);
|
||||
@ -189,7 +189,7 @@ unsafe impl<R: Relationship, L: SpawnableList<R> + Send + Sync + 'static> Bundle
|
||||
}
|
||||
|
||||
fn register_required_components(
|
||||
components: &mut crate::component::Components,
|
||||
components: &mut crate::component::ComponentsRegistrator,
|
||||
required_components: &mut crate::component::RequiredComponents,
|
||||
) {
|
||||
<R::RelationshipTarget as Bundle>::register_required_components(
|
||||
@ -244,7 +244,7 @@ impl<R: Relationship, B: Bundle> DynamicBundle for SpawnOneRelated<R, B> {
|
||||
// SAFETY: This internally relies on the RelationshipTarget's Bundle implementation, which is sound.
|
||||
unsafe impl<R: Relationship, B: Bundle> Bundle for SpawnOneRelated<R, B> {
|
||||
fn component_ids(
|
||||
components: &mut crate::component::Components,
|
||||
components: &mut crate::component::ComponentsRegistrator,
|
||||
ids: &mut impl FnMut(crate::component::ComponentId),
|
||||
) {
|
||||
<R::RelationshipTarget as Bundle>::component_ids(components, ids);
|
||||
@ -258,7 +258,7 @@ unsafe impl<R: Relationship, B: Bundle> Bundle for SpawnOneRelated<R, B> {
|
||||
}
|
||||
|
||||
fn register_required_components(
|
||||
components: &mut crate::component::Components,
|
||||
components: &mut crate::component::ComponentsRegistrator,
|
||||
required_components: &mut crate::component::RequiredComponents,
|
||||
) {
|
||||
<R::RelationshipTarget as Bundle>::register_required_components(
|
||||
|
@ -822,7 +822,7 @@ impl Drop for Table {
|
||||
mod tests {
|
||||
use crate::{
|
||||
change_detection::MaybeLocation,
|
||||
component::{Component, Components, Tick},
|
||||
component::{Component, ComponentIds, Components, ComponentsRegistrator, Tick},
|
||||
entity::Entity,
|
||||
ptr::OwningPtr,
|
||||
storage::{TableBuilder, TableId, TableRow, Tables},
|
||||
@ -847,7 +847,11 @@ mod tests {
|
||||
#[test]
|
||||
fn table() {
|
||||
let mut components = Components::default();
|
||||
let component_id = components.register_component::<W<TableRow>>();
|
||||
let mut componentids = ComponentIds::default();
|
||||
// SAFETY: They are both new.
|
||||
let mut registrator =
|
||||
unsafe { ComponentsRegistrator::new(&mut components, &mut componentids) };
|
||||
let component_id = registrator.register_component::<W<TableRow>>();
|
||||
let columns = &[component_id];
|
||||
let mut table = TableBuilder::with_capacity(0, columns.len())
|
||||
.add_column(components.get_info(component_id).unwrap())
|
||||
|
@ -817,7 +817,7 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> {
|
||||
type Item<'w, 's> = Res<'w, T>;
|
||||
|
||||
fn init_state(world: &mut World, system_meta: &mut SystemMeta) -> Self::State {
|
||||
let component_id = world.components.register_resource::<T>();
|
||||
let component_id = world.components_registrator().register_resource::<T>();
|
||||
let archetype_component_id = world.initialize_resource_internal(component_id).id();
|
||||
|
||||
let combined_access = system_meta.component_access_set.combined_access();
|
||||
@ -926,7 +926,7 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> {
|
||||
type Item<'w, 's> = ResMut<'w, T>;
|
||||
|
||||
fn init_state(world: &mut World, system_meta: &mut SystemMeta) -> Self::State {
|
||||
let component_id = world.components.register_resource::<T>();
|
||||
let component_id = world.components_registrator().register_resource::<T>();
|
||||
let archetype_component_id = world.initialize_resource_internal(component_id).id();
|
||||
|
||||
let combined_access = system_meta.component_access_set.combined_access();
|
||||
@ -1499,7 +1499,7 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> {
|
||||
fn init_state(world: &mut World, system_meta: &mut SystemMeta) -> Self::State {
|
||||
system_meta.set_non_send();
|
||||
|
||||
let component_id = world.components.register_non_send::<T>();
|
||||
let component_id = world.components_registrator().register_non_send::<T>();
|
||||
let archetype_component_id = world.initialize_non_send_internal(component_id).id();
|
||||
|
||||
let combined_access = system_meta.component_access_set.combined_access();
|
||||
@ -1605,7 +1605,7 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> {
|
||||
fn init_state(world: &mut World, system_meta: &mut SystemMeta) -> Self::State {
|
||||
system_meta.set_non_send();
|
||||
|
||||
let component_id = world.components.register_non_send::<T>();
|
||||
let component_id = world.components_registrator().register_non_send::<T>();
|
||||
let archetype_component_id = world.initialize_non_send_internal(component_id).id();
|
||||
|
||||
let combined_access = system_meta.component_access_set.combined_access();
|
||||
|
@ -5,7 +5,10 @@ use crate::{
|
||||
DynamicBundle, InsertMode,
|
||||
},
|
||||
change_detection::{MaybeLocation, MutUntyped},
|
||||
component::{Component, ComponentId, ComponentTicks, Components, Mutable, StorageType},
|
||||
component::{
|
||||
Component, ComponentId, ComponentTicks, Components, ComponentsRegistrator, Mutable,
|
||||
StorageType,
|
||||
},
|
||||
entity::{
|
||||
Entities, Entity, EntityBorrow, EntityCloner, EntityClonerBuilder, EntityLocation,
|
||||
TrustedEntityBorrow,
|
||||
@ -1769,8 +1772,10 @@ impl<'w> EntityWorldMut<'w> {
|
||||
self.assert_not_despawned();
|
||||
let world = &mut self.world;
|
||||
let storages = &mut world.storages;
|
||||
let components = &mut world.components;
|
||||
let bundle_id = world.bundles.register_info::<T>(components, storages);
|
||||
// SAFETY: These come from the same world.
|
||||
let mut registrator =
|
||||
unsafe { ComponentsRegistrator::new(&mut world.components, &mut world.component_ids) };
|
||||
let bundle_id = world.bundles.register_info::<T>(&mut registrator, storages);
|
||||
// SAFETY: We just ensured this bundle exists
|
||||
let bundle_info = unsafe { world.bundles.get_unchecked(bundle_id) };
|
||||
let old_location = self.location;
|
||||
@ -1780,7 +1785,7 @@ impl<'w> EntityWorldMut<'w> {
|
||||
bundle_info.remove_bundle_from_archetype(
|
||||
&mut world.archetypes,
|
||||
storages,
|
||||
components,
|
||||
®istrator,
|
||||
&world.observers,
|
||||
old_location.archetype_id,
|
||||
false,
|
||||
@ -2052,8 +2057,14 @@ impl<'w> EntityWorldMut<'w> {
|
||||
pub(crate) fn remove_with_caller<T: Bundle>(&mut self, caller: MaybeLocation) -> &mut Self {
|
||||
self.assert_not_despawned();
|
||||
let storages = &mut self.world.storages;
|
||||
let components = &mut self.world.components;
|
||||
let bundle_info = self.world.bundles.register_info::<T>(components, storages);
|
||||
// SAFETY: These come from the same world.
|
||||
let mut registrator = unsafe {
|
||||
ComponentsRegistrator::new(&mut self.world.components, &mut self.world.component_ids)
|
||||
};
|
||||
let bundle_info = self
|
||||
.world
|
||||
.bundles
|
||||
.register_info::<T>(&mut registrator, storages);
|
||||
|
||||
// SAFETY: the `BundleInfo` is initialized above
|
||||
self.location = unsafe { self.remove_bundle(bundle_info, caller) };
|
||||
@ -2078,10 +2089,13 @@ impl<'w> EntityWorldMut<'w> {
|
||||
) -> &mut Self {
|
||||
self.assert_not_despawned();
|
||||
let storages = &mut self.world.storages;
|
||||
let components = &mut self.world.components;
|
||||
// SAFETY: These come from the same world.
|
||||
let mut registrator = unsafe {
|
||||
ComponentsRegistrator::new(&mut self.world.components, &mut self.world.component_ids)
|
||||
};
|
||||
let bundles = &mut self.world.bundles;
|
||||
|
||||
let bundle_id = bundles.register_contributed_bundle_info::<T>(components, storages);
|
||||
let bundle_id = bundles.register_contributed_bundle_info::<T>(&mut registrator, storages);
|
||||
|
||||
// SAFETY: the dynamic `BundleInfo` is initialized above
|
||||
self.location = unsafe { self.remove_bundle(bundle_id, caller) };
|
||||
@ -2107,9 +2121,15 @@ impl<'w> EntityWorldMut<'w> {
|
||||
self.assert_not_despawned();
|
||||
let archetypes = &mut self.world.archetypes;
|
||||
let storages = &mut self.world.storages;
|
||||
let components = &mut self.world.components;
|
||||
// SAFETY: These come from the same world.
|
||||
let mut registrator = unsafe {
|
||||
ComponentsRegistrator::new(&mut self.world.components, &mut self.world.component_ids)
|
||||
};
|
||||
|
||||
let retained_bundle = self.world.bundles.register_info::<T>(components, storages);
|
||||
let retained_bundle = self
|
||||
.world
|
||||
.bundles
|
||||
.register_info::<T>(&mut registrator, storages);
|
||||
// SAFETY: `retained_bundle` exists as we just initialized it.
|
||||
let retained_bundle_info = unsafe { self.world.bundles.get_unchecked(retained_bundle) };
|
||||
let old_location = self.location;
|
||||
@ -2123,7 +2143,7 @@ impl<'w> EntityWorldMut<'w> {
|
||||
let remove_bundle =
|
||||
self.world
|
||||
.bundles
|
||||
.init_dynamic_info(&mut self.world.storages, components, to_remove);
|
||||
.init_dynamic_info(&mut self.world.storages, ®istrator, to_remove);
|
||||
|
||||
// SAFETY: the `BundleInfo` for the components to remove is initialized above
|
||||
self.location = unsafe { self.remove_bundle(remove_bundle, caller) };
|
||||
|
@ -542,7 +542,7 @@ impl<'w> FilteredResourcesBuilder<'w> {
|
||||
|
||||
/// Add accesses required to read the resource of the given type.
|
||||
pub fn add_read<R: Resource>(&mut self) -> &mut Self {
|
||||
let component_id = self.world.components.register_resource::<R>();
|
||||
let component_id = self.world.components_registrator().register_resource::<R>();
|
||||
self.add_read_by_id(component_id)
|
||||
}
|
||||
|
||||
@ -588,7 +588,7 @@ impl<'w> FilteredResourcesMutBuilder<'w> {
|
||||
|
||||
/// Add accesses required to read the resource of the given type.
|
||||
pub fn add_read<R: Resource>(&mut self) -> &mut Self {
|
||||
let component_id = self.world.components.register_resource::<R>();
|
||||
let component_id = self.world.components_registrator().register_resource::<R>();
|
||||
self.add_read_by_id(component_id)
|
||||
}
|
||||
|
||||
@ -606,7 +606,7 @@ impl<'w> FilteredResourcesMutBuilder<'w> {
|
||||
|
||||
/// Add accesses required to get mutable access to the resource of the given type.
|
||||
pub fn add_write<R: Resource>(&mut self) -> &mut Self {
|
||||
let component_id = self.world.components.register_resource::<R>();
|
||||
let component_id = self.world.components_registrator().register_resource::<R>();
|
||||
self.add_write_by_id(component_id)
|
||||
}
|
||||
|
||||
|
@ -42,8 +42,9 @@ use crate::{
|
||||
},
|
||||
change_detection::{MaybeLocation, MutUntyped, TicksMut},
|
||||
component::{
|
||||
Component, ComponentDescriptor, ComponentHooks, ComponentId, ComponentInfo, ComponentTicks,
|
||||
Components, Mutable, RequiredComponents, RequiredComponentsError, Tick,
|
||||
Component, ComponentDescriptor, ComponentHooks, ComponentId, ComponentIds, ComponentInfo,
|
||||
ComponentTicks, Components, ComponentsQueuedRegistrator, ComponentsRegistrator, Mutable,
|
||||
RequiredComponents, RequiredComponentsError, Tick,
|
||||
},
|
||||
entity::{
|
||||
AllocAtWithoutReplacement, Entities, Entity, EntityDoesNotExistError, EntityLocation,
|
||||
@ -94,6 +95,7 @@ pub struct World {
|
||||
id: WorldId,
|
||||
pub(crate) entities: Entities,
|
||||
pub(crate) components: Components,
|
||||
pub(crate) component_ids: ComponentIds,
|
||||
pub(crate) archetypes: Archetypes,
|
||||
pub(crate) storages: Storages,
|
||||
pub(crate) bundles: Bundles,
|
||||
@ -124,6 +126,7 @@ impl Default for World {
|
||||
last_check_tick: Tick::new(0),
|
||||
last_trigger_id: 0,
|
||||
command_queue: RawCommandQueue::new(),
|
||||
component_ids: ComponentIds::default(),
|
||||
};
|
||||
world.bootstrap();
|
||||
world
|
||||
@ -225,6 +228,22 @@ impl World {
|
||||
&self.components
|
||||
}
|
||||
|
||||
/// Prepares a [`ComponentsQueuedRegistrator`] for the world.
|
||||
/// **NOTE:** [`ComponentsQueuedRegistrator`] is easily misused.
|
||||
/// See its docs for important notes on when and how it should be used.
|
||||
#[inline]
|
||||
pub fn components_queue(&self) -> ComponentsQueuedRegistrator {
|
||||
// SAFETY: These are from the same world.
|
||||
unsafe { ComponentsQueuedRegistrator::new(&self.components, &self.component_ids) }
|
||||
}
|
||||
|
||||
/// Prepares a [`ComponentsRegistrator`] for the world.
|
||||
#[inline]
|
||||
pub fn components_registrator(&mut self) -> ComponentsRegistrator {
|
||||
// SAFETY: These are from the same world.
|
||||
unsafe { ComponentsRegistrator::new(&mut self.components, &mut self.component_ids) }
|
||||
}
|
||||
|
||||
/// Retrieves this world's [`Storages`] collection.
|
||||
#[inline]
|
||||
pub fn storages(&self) -> &Storages {
|
||||
@ -257,7 +276,7 @@ impl World {
|
||||
/// In most cases, you don't need to call this method directly since component registration
|
||||
/// happens automatically during system initialization.
|
||||
pub fn register_component<T: Component>(&mut self) -> ComponentId {
|
||||
self.components.register_component::<T>()
|
||||
self.components_registrator().register_component::<T>()
|
||||
}
|
||||
|
||||
/// Registers a component type as "disabling",
|
||||
@ -541,7 +560,7 @@ impl World {
|
||||
&mut self,
|
||||
descriptor: ComponentDescriptor,
|
||||
) -> ComponentId {
|
||||
self.components
|
||||
self.components_registrator()
|
||||
.register_component_with_descriptor(descriptor)
|
||||
}
|
||||
|
||||
@ -581,7 +600,7 @@ impl World {
|
||||
/// to insert the [`Resource`] in the [`World`], use [`World::init_resource`] or
|
||||
/// [`World::insert_resource`] instead.
|
||||
pub fn register_resource<R: Resource>(&mut self) -> ComponentId {
|
||||
self.components.register_resource::<R>()
|
||||
self.components_registrator().register_resource::<R>()
|
||||
}
|
||||
|
||||
/// Returns the [`ComponentId`] of the given [`Resource`] type `T`.
|
||||
@ -1578,7 +1597,7 @@ impl World {
|
||||
&mut self,
|
||||
descriptor: ComponentDescriptor,
|
||||
) -> ComponentId {
|
||||
self.components
|
||||
self.components_registrator()
|
||||
.register_resource_with_descriptor(descriptor)
|
||||
}
|
||||
|
||||
@ -1593,7 +1612,7 @@ impl World {
|
||||
#[track_caller]
|
||||
pub fn init_resource<R: Resource + FromWorld>(&mut self) -> ComponentId {
|
||||
let caller = MaybeLocation::caller();
|
||||
let component_id = self.components.register_resource::<R>();
|
||||
let component_id = self.components_registrator().register_resource::<R>();
|
||||
if self
|
||||
.storages
|
||||
.resources
|
||||
@ -1630,7 +1649,7 @@ impl World {
|
||||
value: R,
|
||||
caller: MaybeLocation,
|
||||
) {
|
||||
let component_id = self.components.register_resource::<R>();
|
||||
let component_id = self.components_registrator().register_resource::<R>();
|
||||
OwningPtr::make(value, |ptr| {
|
||||
// SAFETY: component_id was just initialized and corresponds to resource of type R.
|
||||
unsafe {
|
||||
@ -1654,7 +1673,7 @@ impl World {
|
||||
#[track_caller]
|
||||
pub fn init_non_send_resource<R: 'static + FromWorld>(&mut self) -> ComponentId {
|
||||
let caller = MaybeLocation::caller();
|
||||
let component_id = self.components.register_non_send::<R>();
|
||||
let component_id = self.components_registrator().register_non_send::<R>();
|
||||
if self
|
||||
.storages
|
||||
.non_send_resources
|
||||
@ -1685,7 +1704,7 @@ impl World {
|
||||
#[track_caller]
|
||||
pub fn insert_non_send_resource<R: 'static>(&mut self, value: R) {
|
||||
let caller = MaybeLocation::caller();
|
||||
let component_id = self.components.register_non_send::<R>();
|
||||
let component_id = self.components_registrator().register_non_send::<R>();
|
||||
OwningPtr::make(value, |ptr| {
|
||||
// SAFETY: component_id was just initialized and corresponds to resource of type R.
|
||||
unsafe {
|
||||
@ -1968,7 +1987,7 @@ impl World {
|
||||
let change_tick = self.change_tick();
|
||||
let last_change_tick = self.last_change_tick();
|
||||
|
||||
let component_id = self.components.register_resource::<R>();
|
||||
let component_id = self.components_registrator().register_resource::<R>();
|
||||
let data = self.initialize_resource_internal(component_id);
|
||||
if !data.is_present() {
|
||||
OwningPtr::make(func(), |ptr| {
|
||||
@ -2026,7 +2045,7 @@ impl World {
|
||||
let change_tick = self.change_tick();
|
||||
let last_change_tick = self.last_change_tick();
|
||||
|
||||
let component_id = self.components.register_resource::<R>();
|
||||
let component_id = self.components_registrator().register_resource::<R>();
|
||||
if self
|
||||
.storages
|
||||
.resources
|
||||
@ -2191,12 +2210,14 @@ impl World {
|
||||
B: Bundle<Effect: NoBundleEffect>,
|
||||
{
|
||||
self.flush();
|
||||
|
||||
let change_tick = self.change_tick();
|
||||
|
||||
// SAFETY: These come from the same world. `Self.components_registrator` can't be used since we borrow other fields too.
|
||||
let mut registrator =
|
||||
unsafe { ComponentsRegistrator::new(&mut self.components, &mut self.component_ids) };
|
||||
let bundle_id = self
|
||||
.bundles
|
||||
.register_info::<B>(&mut self.components, &mut self.storages);
|
||||
.register_info::<B>(&mut registrator, &mut self.storages);
|
||||
enum SpawnOrInsert<'w> {
|
||||
Spawn(BundleSpawner<'w>),
|
||||
Insert(BundleInserter<'w>, ArchetypeId),
|
||||
@ -2367,9 +2388,12 @@ impl World {
|
||||
|
||||
self.flush();
|
||||
let change_tick = self.change_tick();
|
||||
// SAFETY: These come from the same world. `Self.components_registrator` can't be used since we borrow other fields too.
|
||||
let mut registrator =
|
||||
unsafe { ComponentsRegistrator::new(&mut self.components, &mut self.component_ids) };
|
||||
let bundle_id = self
|
||||
.bundles
|
||||
.register_info::<B>(&mut self.components, &mut self.storages);
|
||||
.register_info::<B>(&mut registrator, &mut self.storages);
|
||||
|
||||
let mut batch_iter = batch.into_iter();
|
||||
|
||||
@ -2509,9 +2533,12 @@ impl World {
|
||||
|
||||
self.flush();
|
||||
let change_tick = self.change_tick();
|
||||
// SAFETY: These come from the same world. `Self.components_registrator` can't be used since we borrow other fields too.
|
||||
let mut registrator =
|
||||
unsafe { ComponentsRegistrator::new(&mut self.components, &mut self.component_ids) };
|
||||
let bundle_id = self
|
||||
.bundles
|
||||
.register_info::<B>(&mut self.components, &mut self.storages);
|
||||
.register_info::<B>(&mut registrator, &mut self.storages);
|
||||
|
||||
let mut invalid_entities = Vec::<Entity>::new();
|
||||
let mut batch_iter = batch.into_iter();
|
||||
@ -2832,12 +2859,22 @@ impl World {
|
||||
}
|
||||
}
|
||||
|
||||
/// Applies any queued component registration.
|
||||
/// For spawning vanilla rust component types and resources, this is not strictly necessary.
|
||||
/// However, flushing components can make information available more quickly, and can have performance benefits.
|
||||
/// Additionally, for components and resources registered dynamically through a raw descriptor or similar,
|
||||
/// this is the only way to complete their registration.
|
||||
pub(crate) fn flush_components(&mut self) {
|
||||
self.components_registrator().apply_queued_registrations();
|
||||
}
|
||||
|
||||
/// Flushes queued entities and commands.
|
||||
///
|
||||
/// Queued entities will be spawned, and then commands will be applied.
|
||||
#[inline]
|
||||
pub fn flush(&mut self) {
|
||||
self.flush_entities();
|
||||
self.flush_components();
|
||||
self.flush_commands();
|
||||
}
|
||||
|
||||
@ -3071,9 +3108,12 @@ impl World {
|
||||
/// component in the bundle.
|
||||
#[inline]
|
||||
pub fn register_bundle<B: Bundle>(&mut self) -> &BundleInfo {
|
||||
// SAFETY: These come from the same world. `Self.components_registrator` can't be used since we borrow other fields too.
|
||||
let mut registrator =
|
||||
unsafe { ComponentsRegistrator::new(&mut self.components, &mut self.component_ids) };
|
||||
let id = self
|
||||
.bundles
|
||||
.register_info::<B>(&mut self.components, &mut self.storages);
|
||||
.register_info::<B>(&mut registrator, &mut self.storages);
|
||||
// SAFETY: We just initialized the bundle so its id should definitely be valid.
|
||||
unsafe { self.bundles.get(id).debug_checked_unwrap() }
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user