#![doc(hidden)]
use core::ffi::c_char;
use core::ffi::c_void;
use crate::core::*;
use crate::sys;
struct ScopeWithGuard {
world: *mut sys::ecs_world_t,
prev_scope: Option<sys::ecs_entity_t>,
prev_with: Option<sys::ecs_id_t>,
}
impl ScopeWithGuard {
#[inline(always)]
fn new(world: *mut sys::ecs_world_t, disable_component_scoping: bool) -> Self {
if !disable_component_scoping {
return Self {
world,
prev_scope: None,
prev_with: None,
};
}
let prev_scope = unsafe {
let current_scope = sys::ecs_get_scope(world);
if current_scope != 0 {
sys::ecs_set_scope(world, 0);
Some(current_scope)
} else {
None
}
};
let prev_with = unsafe {
let previous_with = sys::ecs_set_with(world, 0);
if previous_with != 0 {
Some(previous_with)
} else {
None
}
};
Self {
world,
prev_scope,
prev_with,
}
}
}
impl Drop for ScopeWithGuard {
fn drop(&mut self) {
if let Some(prev_with) = self.prev_with {
unsafe { sys::ecs_set_with(self.world, prev_with) };
}
if let Some(prev_scope) = self.prev_scope {
unsafe { sys::ecs_set_scope(self.world, prev_scope) };
}
}
}
#[doc(hidden)]
pub fn internal_register_component<
'a,
const IS_NAMED: bool,
const COMPONENT_REGISTRATION: bool,
T,
>(
world: impl WorldProvider<'a>,
name: *const c_char,
) -> u64
where
T: ComponentId,
{
let world = world.world();
let world_ptr = world.world_ptr_mut();
let id = if IS_NAMED {
register_component_data_named::<COMPONENT_REGISTRATION, T>(world, name)
} else {
register_component_data::<COMPONENT_REGISTRATION, T>(world)
};
if T::IS_ENUM {
let underlying_enum_type_id = world.component_id::<T::UnderlyingTypeOfEnum>();
register_enum_data::<T>(world_ptr, id, *underlying_enum_type_id);
}
id
}
#[doc(hidden)]
pub(crate) fn external_register_component<'a, const COMPONENT_REGISTRATION: bool, T>(
world: impl WorldProvider<'a>,
name: *const c_char,
) -> u64
where
T: 'static,
{
let world = world.world();
external_register_component_data::<COMPONENT_REGISTRATION, T>(world, name) as u64
}
#[inline(always)]
pub(crate) fn try_register_component<'a, const COMPONENT_REGISTRATION: bool, T>(
world: impl WorldProvider<'a>,
) -> sys::ecs_entity_t
where
T: ComponentId,
{
const NAMED: bool = false;
internal_register_component::<NAMED, COMPONENT_REGISTRATION, T>(world, core::ptr::null())
}
#[inline(always)]
pub(crate) fn try_register_component_named<'a, const COMPONENT_REGISTRATION: bool, T>(
world: impl WorldProvider<'a>,
name: &str,
) -> sys::ecs_entity_t
where
T: ComponentId,
{
let name = compact_str::format_compact!("{}\0", name);
const NAMED: bool = true;
internal_register_component::<NAMED, COMPONENT_REGISTRATION, T>(
world,
name.as_ptr() as *const c_char,
)
}
pub(crate) fn register_enum_data<T>(
world: *mut sys::ecs_world_t,
id: sys::ecs_entity_t,
underlying_type_id: sys::ecs_entity_t,
) where
T: ComponentId,
{
unsafe { sys::ecs_cpp_enum_init(world, id, underlying_type_id) };
let enum_array_ptr = T::UnderlyingEnumType::__enum_data_mut();
let enum_size = const { core::mem::size_of::<T::UnderlyingTypeOfEnum>() };
for enum_item in T::UnderlyingEnumType::iter() {
let name = enum_item.name_cstr();
let enum_index = enum_item.enum_index();
let mut array_index = enum_index;
let entity_id: sys::ecs_entity_t = unsafe {
sys::ecs_cpp_enum_constant_register(
world,
id,
T::UnderlyingEnumType::id_variant_of_index_unchecked(enum_index),
name.as_ptr(),
&mut array_index as *mut usize as *mut c_void,
underlying_type_id,
enum_size,
)
};
store_enum_entity_if_needed::<T>(enum_array_ptr, array_index, entity_id);
}
}
#[inline(always)]
fn store_enum_entity_if_needed<T: ComponentId>(
enum_array_ptr: *mut sys::ecs_entity_t,
index: usize,
entity_id: sys::ecs_entity_t,
) {
if !T::UnderlyingEnumType::is_index_registered_as_entity(index) {
unsafe { *enum_array_ptr.add(index) = entity_id };
}
}
pub(crate) fn register_component_data_named<const COMPONENT_REGISTRATION: bool, T>(
world: WorldRef<'_>,
name: *const c_char,
) -> sys::ecs_entity_t
where
T: ComponentId,
{
let world_ptr = world.world_ptr_mut();
let _scope = ScopeWithGuard::new(world_ptr, !COMPONENT_REGISTRATION);
register_component_data_explicit::<T, false>(world, name)
}
pub(crate) fn register_component_data<const COMPONENT_REGISTRATION: bool, T>(
world: WorldRef<'_>,
) -> sys::ecs_entity_t
where
T: ComponentId,
{
let world_ptr = world.world_ptr_mut();
let _scope = ScopeWithGuard::new(world_ptr, !COMPONENT_REGISTRATION);
register_component_data_explicit::<T, false>(world, core::ptr::null())
}
pub(crate) fn external_register_component_data<const COMPONENT_REGISTRATION: bool, T>(
world: WorldRef<'_>,
name: *const c_char,
) -> sys::ecs_entity_t
where
T: 'static,
{
let world_ptr = world.world_ptr_mut();
let _scope = ScopeWithGuard::new(world_ptr, !COMPONENT_REGISTRATION);
external_register_component_data_explicit::<T>(world, name)
}
pub(crate) fn register_component_data_explicit<T, const ALLOCATE_TAG: bool>(
world: WorldRef<'_>,
name: *const c_char,
) -> sys::ecs_entity_t
where
T: ComponentId,
{
let worldref = world;
let world = worldref.world_ptr_mut();
let arr = worldref.components_array();
let index = T::index() as usize;
let c = if index < arr.len() { arr[index] } else { 0 };
if c != 0 && unsafe { sys::ecs_is_alive(world, c) } {
return c;
}
let type_name_without_scope = if T::IS_GENERIC {
crate::core::get_type_name_without_scope_generic::<T>()
} else {
crate::core::get_type_name_without_scope::<T>()
};
let type_name_without_scope =
compact_str::format_compact!("{}\0", type_name_without_scope.as_str());
let type_name = crate::core::type_name_cstring::<T>();
let type_name_ptr = type_name.as_ptr();
let mut user_name = name;
let mut implicit_name = false;
if user_name.is_null() {
user_name = type_name_ptr;
implicit_name = true;
}
let module = unsafe { sys::ecs_get_scope(world) };
if module != 0 && implicit_name {
user_name = type_name_without_scope.as_ptr() as *const c_char;
}
let id = if name.is_null() {
let prev_scope = unsafe { sys::ecs_set_scope(world, 0) };
let id = unsafe { sys::ecs_lookup_symbol(world, type_name_ptr as *const _, false, false) };
unsafe { sys::ecs_set_scope(world, prev_scope) };
id
} else {
0
};
if id != 0 {
return id;
}
let name = user_name;
let entity_desc_name = if type_name_without_scope.starts_with("Ecs") {
type_name_without_scope.as_ptr() as *const c_char
} else {
type_name_ptr
};
let type_info = create_type_info::<T, ALLOCATE_TAG>();
finalize_component_registration(world, name, entity_desc_name, type_info)
}
pub(crate) fn external_register_component_data_explicit<T>(
world: WorldRef<'_>,
name: *const c_char,
) -> sys::ecs_entity_t
where
T: 'static,
{
let world_ptr = world.world_ptr_mut();
let type_name_without_scope = crate::core::get_type_name_without_scope_generic::<T>();
let type_name_without_scope =
compact_str::format_compact!("{}\0", type_name_without_scope.as_str());
let id = if name.is_null() {
let prev_scope = unsafe { sys::ecs_set_scope(world_ptr, 0) };
let id = unsafe {
sys::ecs_lookup_symbol(
world_ptr,
type_name_without_scope.as_ptr() as *const _,
false,
false,
)
};
unsafe { sys::ecs_set_scope(world_ptr, prev_scope) };
id
} else {
0
};
if id != 0 {
return id;
}
let type_name = crate::core::type_name_cstring::<T>();
let type_name_ptr = type_name.as_ptr();
let name = if name.is_null() { type_name_ptr } else { name };
let type_info = external_create_type_info::<T>();
finalize_component_registration(world_ptr, name, type_name_ptr, type_info)
}
fn finalize_component_registration(
world: *mut sys::ecs_world_t,
name: *const c_char,
entity_desc_name: *const c_char,
type_info: sys::ecs_type_info_t,
) -> sys::ecs_entity_t {
let entity_desc = create_entity_desc(name, entity_desc_name);
let entity = unsafe { flecs_ecs_sys::ecs_entity_init(world, &entity_desc) };
let component_desc = create_component_desc(entity, type_info);
#[cfg(any(debug_assertions, feature = "flecs_force_enable_ecs_asserts"))]
{
let entity = unsafe { flecs_ecs_sys::ecs_component_init(world, &component_desc) };
ecs_assert!(
entity != 0 && unsafe { sys::ecs_exists(world, entity) },
FlecsErrorCode::InternalError
);
entity
}
#[cfg(not(any(debug_assertions, feature = "flecs_force_enable_ecs_asserts")))]
{
unsafe { flecs_ecs_sys::ecs_component_init(world, &component_desc) }
}
}