use alloc::{boxed::Box, vec::Vec};
use bevy_platform::sync::PoisonError;
use bevy_utils::TypeIdMap;
use core::any::Any;
use core::{any::TypeId, fmt::Debug, ops::Deref};
use crate::component::{enforce_no_required_components_recursion, RequiredComponentsRegistrator};
use crate::{
component::{
Component, ComponentDescriptor, ComponentId, Components, RequiredComponents, StorageType,
},
query::DebugCheckedUnwrap as _,
resource::Resource,
};
#[derive(Debug, Default)]
pub struct ComponentIds {
next: bevy_platform::sync::atomic::AtomicUsize,
}
impl ComponentIds {
pub fn peek(&self) -> ComponentId {
ComponentId(
self.next
.load(bevy_platform::sync::atomic::Ordering::Relaxed),
)
}
pub fn next(&self) -> ComponentId {
ComponentId(
self.next
.fetch_add(1, bevy_platform::sync::atomic::Ordering::Relaxed),
)
}
pub fn peek_mut(&mut self) -> ComponentId {
ComponentId(*self.next.get_mut())
}
pub fn next_mut(&mut self) -> ComponentId {
let id = self.next.get_mut();
let result = ComponentId(*id);
*id += 1;
result
}
pub fn len(&self) -> usize {
self.peek().0
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
pub struct ComponentsRegistrator<'w> {
pub(super) components: &'w mut Components,
pub(super) ids: &'w mut ComponentIds,
pub(super) recursion_check_stack: Vec<ComponentId>,
}
impl Deref for ComponentsRegistrator<'_> {
type Target = Components;
fn deref(&self) -> &Self::Target {
self.components
}
}
impl<'w> ComponentsRegistrator<'w> {
pub unsafe fn new(components: &'w mut Components, ids: &'w mut ComponentIds) -> Self {
Self {
components,
ids,
recursion_check_stack: Vec::new(),
}
}
pub fn as_queued(&self) -> ComponentsQueuedRegistrator<'_> {
unsafe { ComponentsQueuedRegistrator::new(self.components, self.ids) }
}
pub fn apply_queued_registrations(&mut self) {
if !self.any_queued_mut() {
return;
}
while let Some(registrator) = {
let queued = self
.components
.queued
.get_mut()
.unwrap_or_else(PoisonError::into_inner);
queued.components.keys().next().copied().map(|type_id| {
unsafe { queued.components.remove(&type_id).debug_checked_unwrap() }
})
} {
registrator.register(self);
}
while let Some(registrator) = {
let queued = self
.components
.queued
.get_mut()
.unwrap_or_else(PoisonError::into_inner);
queued.resources.keys().next().copied().map(|type_id| {
unsafe { queued.resources.remove(&type_id).debug_checked_unwrap() }
})
} {
registrator.register(self);
}
let queued = &mut self
.components
.queued
.get_mut()
.unwrap_or_else(PoisonError::into_inner);
if !queued.dynamic_registrations.is_empty() {
for registrator in core::mem::take(&mut queued.dynamic_registrations) {
registrator.register(self);
}
}
}
#[inline]
pub fn register_component<T: Component>(&mut self) -> ComponentId {
self.register_component_checked::<T>()
}
#[inline]
pub(super) fn register_component_checked<T: Component>(&mut self) -> ComponentId {
let type_id = TypeId::of::<T>();
if let Some(&id) = self.indices.get(&type_id) {
enforce_no_required_components_recursion(self, &self.recursion_check_stack, id);
return id;
}
if let Some(registrator) = self
.components
.queued
.get_mut()
.unwrap_or_else(PoisonError::into_inner)
.components
.remove(&type_id)
{
return registrator.register(self);
}
let id = self.ids.next_mut();
unsafe {
self.register_component_unchecked::<T>(id);
}
id
}
#[inline]
unsafe fn register_component_unchecked<T: Component>(&mut self, id: ComponentId) {
unsafe {
self.components
.register_component_inner(id, ComponentDescriptor::new::<T>());
}
let type_id = TypeId::of::<T>();
let prev = self.components.indices.insert(type_id, id);
debug_assert!(prev.is_none());
self.recursion_check_stack.push(id);
let mut required_components = RequiredComponents::default();
let mut required_components_registrator =
unsafe { RequiredComponentsRegistrator::new(self, &mut required_components) };
T::register_required_components(id, &mut required_components_registrator);
unsafe {
self.components
.register_required_by(id, &required_components);
}
self.recursion_check_stack.pop();
let info = unsafe {
&mut self
.components
.components
.get_mut(id.0)
.debug_checked_unwrap()
.as_mut()
.debug_checked_unwrap()
};
info.hooks.update_from_component::<T>();
info.required_components = required_components;
}
#[inline]
pub fn register_component_with_descriptor(
&mut self,
descriptor: ComponentDescriptor,
) -> ComponentId {
let id = self.ids.next_mut();
unsafe {
self.components.register_component_inner(id, descriptor);
}
id
}
#[inline]
pub fn register_resource<T: Resource>(&mut self) -> ComponentId {
unsafe {
self.register_resource_with(TypeId::of::<T>(), || {
ComponentDescriptor::new_resource::<T>()
})
}
}
#[inline]
pub fn register_non_send<T: Any>(&mut self) -> ComponentId {
unsafe {
self.register_resource_with(TypeId::of::<T>(), || {
ComponentDescriptor::new_non_send::<T>(StorageType::default())
})
}
}
#[inline]
unsafe fn register_resource_with(
&mut self,
type_id: TypeId,
descriptor: impl FnOnce() -> ComponentDescriptor,
) -> ComponentId {
if let Some(id) = self.resource_indices.get(&type_id) {
return *id;
}
if let Some(registrator) = self
.components
.queued
.get_mut()
.unwrap_or_else(PoisonError::into_inner)
.resources
.remove(&type_id)
{
return registrator.register(self);
}
let id = self.ids.next_mut();
unsafe {
self.components
.register_resource_unchecked(type_id, id, descriptor());
}
id
}
#[inline]
pub fn register_resource_with_descriptor(
&mut self,
descriptor: ComponentDescriptor,
) -> ComponentId {
let id = self.ids.next_mut();
unsafe {
self.components.register_component_inner(id, descriptor);
}
id
}
pub fn any_queued_mut(&mut self) -> bool {
self.components.any_queued_mut()
}
pub fn num_queued_mut(&mut self) -> usize {
self.components.num_queued_mut()
}
}
pub(super) struct QueuedRegistration {
pub(super) registrator:
Box<dyn FnOnce(&mut ComponentsRegistrator, ComponentId, ComponentDescriptor)>,
pub(super) id: ComponentId,
pub(super) descriptor: ComponentDescriptor,
}
impl QueuedRegistration {
unsafe fn new(
id: ComponentId,
descriptor: ComponentDescriptor,
func: impl FnOnce(&mut ComponentsRegistrator, ComponentId, ComponentDescriptor) + 'static,
) -> Self {
Self {
registrator: Box::new(func),
id,
descriptor,
}
}
pub(super) fn register(self, registrator: &mut ComponentsRegistrator) -> ComponentId {
(self.registrator)(registrator, self.id, self.descriptor);
self.id
}
}
#[derive(Default)]
pub struct QueuedComponents {
pub(super) components: TypeIdMap<QueuedRegistration>,
pub(super) resources: TypeIdMap<QueuedRegistration>,
pub(super) dynamic_registrations: Vec<QueuedRegistration>,
}
impl Debug for QueuedComponents {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let components = self
.components
.iter()
.map(|(type_id, queued)| (type_id, queued.id))
.collect::<Vec<_>>();
let resources = self
.resources
.iter()
.map(|(type_id, queued)| (type_id, queued.id))
.collect::<Vec<_>>();
let dynamic_registrations = self
.dynamic_registrations
.iter()
.map(|queued| queued.id)
.collect::<Vec<_>>();
write!(f, "components: {components:?}, resources: {resources:?}, dynamic_registrations: {dynamic_registrations:?}")
}
}
#[derive(Clone, Copy)]
pub struct ComponentsQueuedRegistrator<'w> {
components: &'w Components,
ids: &'w ComponentIds,
}
impl Deref for ComponentsQueuedRegistrator<'_> {
type Target = Components;
fn deref(&self) -> &Self::Target {
self.components
}
}
impl<'w> ComponentsQueuedRegistrator<'w> {
pub unsafe fn new(components: &'w Components, ids: &'w ComponentIds) -> Self {
Self { components, ids }
}
unsafe fn register_arbitrary_component(
&self,
type_id: TypeId,
descriptor: ComponentDescriptor,
func: impl FnOnce(&mut ComponentsRegistrator, ComponentId, ComponentDescriptor) + 'static,
) -> ComponentId {
self.components
.queued
.write()
.unwrap_or_else(PoisonError::into_inner)
.components
.entry(type_id)
.or_insert_with(|| {
unsafe { QueuedRegistration::new(self.ids.next(), descriptor, func) }
})
.id
}
unsafe fn register_arbitrary_resource(
&self,
type_id: TypeId,
descriptor: ComponentDescriptor,
func: impl FnOnce(&mut ComponentsRegistrator, ComponentId, ComponentDescriptor) + 'static,
) -> ComponentId {
self.components
.queued
.write()
.unwrap_or_else(PoisonError::into_inner)
.resources
.entry(type_id)
.or_insert_with(|| {
unsafe { QueuedRegistration::new(self.ids.next(), descriptor, func) }
})
.id
}
fn register_arbitrary_dynamic(
&self,
descriptor: ComponentDescriptor,
func: impl FnOnce(&mut ComponentsRegistrator, ComponentId, ComponentDescriptor) + 'static,
) -> ComponentId {
let id = self.ids.next();
self.components
.queued
.write()
.unwrap_or_else(PoisonError::into_inner)
.dynamic_registrations
.push(
unsafe { QueuedRegistration::new(id, descriptor, func) },
);
id
}
#[inline]
pub fn queue_register_component<T: Component>(&self) -> ComponentId {
self.component_id::<T>().unwrap_or_else(|| {
unsafe {
self.register_arbitrary_component(
TypeId::of::<T>(),
ComponentDescriptor::new::<T>(),
|registrator, id, _descriptor| {
#[expect(unused_unsafe, reason = "More precise to specify.")]
unsafe {
registrator.register_component_unchecked::<T>(id);
}
},
)
}
})
}
#[inline]
pub fn queue_register_component_with_descriptor(
&self,
descriptor: ComponentDescriptor,
) -> ComponentId {
self.register_arbitrary_dynamic(descriptor, |registrator, id, descriptor| {
unsafe {
registrator
.components
.register_component_inner(id, descriptor);
}
})
}
#[inline]
pub fn queue_register_resource<T: Resource>(&self) -> ComponentId {
let type_id = TypeId::of::<T>();
self.get_resource_id(type_id).unwrap_or_else(|| {
unsafe {
self.register_arbitrary_resource(
type_id,
ComponentDescriptor::new_resource::<T>(),
move |registrator, id, descriptor| {
#[expect(unused_unsafe, reason = "More precise to specify.")]
unsafe {
registrator
.components
.register_resource_unchecked(type_id, id, descriptor);
}
},
)
}
})
}
#[inline]
pub fn queue_register_non_send<T: Any>(&self) -> ComponentId {
let type_id = TypeId::of::<T>();
self.get_resource_id(type_id).unwrap_or_else(|| {
unsafe {
self.register_arbitrary_resource(
type_id,
ComponentDescriptor::new_non_send::<T>(StorageType::default()),
move |registrator, id, descriptor| {
#[expect(unused_unsafe, reason = "More precise to specify.")]
unsafe {
registrator
.components
.register_resource_unchecked(type_id, id, descriptor);
}
},
)
}
})
}
#[inline]
pub fn queue_register_resource_with_descriptor(
&self,
descriptor: ComponentDescriptor,
) -> ComponentId {
self.register_arbitrary_dynamic(descriptor, |registrator, id, descriptor| {
unsafe {
registrator
.components
.register_component_inner(id, descriptor);
}
})
}
}