diff --git a/flecs_ecs/src/core/entity_view/entity_view_const.rs b/flecs_ecs/src/core/entity_view/entity_view_const.rs index d0ea9ae8..c0595df6 100644 --- a/flecs_ecs/src/core/entity_view/entity_view_const.rs +++ b/flecs_ecs/src/core/entity_view/entity_view_const.rs @@ -1,3 +1,6 @@ +extern crate alloc; +#[cfg(feature = "std")] +extern crate std; use core::{ ffi::{CStr, c_void}, ops::{Deref, DerefMut}, @@ -8,12 +11,9 @@ use crate::sys; use flecs_ecs::core::*; use sys::ecs_get_with; -#[cfg(feature = "std")] -extern crate std; - -extern crate alloc; #[allow(unused_imports)] //meant for no_std, not ready yet use alloc::{borrow::ToOwned, boxed::Box, format, string::String, string::ToString, vec, vec::Vec}; +use flecs_ecs_sys::ecs_table_t; /// A view into an entity in the world that provides both read and write access to components and relationships. /// @@ -1444,6 +1444,10 @@ fn get_rw_lock( ret } +pub trait EntityViewFetch<'a>: WorldProvider<'a> + Sized { + fn fetch(self) -> TableLock<'a, T>; +} + pub trait EntityViewGet<'a, Return>: WorldProvider<'a> + Sized { /// gets mutable or immutable component(s) and/or relationship(s) from an entity in a callback and return a value. /// each component type must be marked `&` or `&mut` to indicate if it is mutable or not. @@ -1660,6 +1664,149 @@ impl<'a, Return> EntityViewGet<'a, Return> for EntityView<'a> { } } +impl<'a> EntityViewFetch<'a> for EntityView<'a> { + fn fetch(self) -> TableLock<'a, T> { + init_locked::(self) + } +} + +pub struct TableLock<'a, T: GetTuple> { + world: WorldRef<'a>, + arr_component_info: T::ArrayColumnIndex, + data: T::TupleType<'a>, + table: *mut ecs_table_t, + stage_id: i32, +} + +impl<'a, T: GetTuple> Deref for TableLock<'a, T> { + type Target = T::TupleType<'a>; + + fn deref(&self) -> &Self::Target { + &self.data + } +} + +impl core::fmt::Debug for TableLock<'_, T> +where + T::TupleType<'_>: core::fmt::Debug, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("TableLock") + .field("data", &self.data) + .finish() + } +} + +impl<'a, T: GetTuple> DerefMut for TableLock<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.data + } +} + +fn init_locked(view: EntityView<'_>) -> TableLock<'_, T> { + let record = unsafe { sys::ecs_record_find(view.world.world_ptr(), *view.id) }; + let table = unsafe { (*record).table }; + + if unsafe { (*record).table.is_null() } { + panic!("Entity does not have any components"); + } + + let tuple_data = T::create_ptrs::(view.world, view.id, record); + let tuple = tuple_data.get_tuple(); + + let ids = tuple_data.read_write_ids(); + let world = view.world.real_world(); + let world_ptr = world.world_ptr(); + let stage_id = world.stage_id(); + let mut arr_component_info: T::ArrayColumnIndex = T::ArrayColumnIndex::init(); + let component_info_arr = arr_component_info.column_indices_mut(); + let mut i = 0; + for id in ids { + let raw_id = **id; + let idr = unsafe { sys::ecs_id_record_get(world_ptr, raw_id) }; + if unsafe { sys::ecs_rust_is_sparse_idr(idr) } { + //TODO this does locking regardless if the entity has the component or not + component_info_arr[i] = ComponentTypeRWLock::Sparse((idr, *id)); + match id { + ReadWriteId::Read(_) => { + sparse_id_record_lock_read_begin(&world, idr); + } + ReadWriteId::Write(_) => { + sparse_id_record_lock_write_begin(&world, idr); + } + } + i += 1; + continue; + } + let column_index = unsafe { + sys::ecs_table_get_column_index_w_idr(world_ptr, (*record).table, raw_id, idr) + }; + + component_info_arr[i] = ComponentTypeRWLock::Dense((column_index as i16, *id)); + i += 1; + + //does not have the component + if column_index == -1 { + continue; + } + match id { + ReadWriteId::Read(_) => { + table_column_lock_read_begin(&world, table, column_index as i16, stage_id); + } + ReadWriteId::Write(_) => { + table_column_lock_write_begin(&world, table, column_index as i16, stage_id); + } + } + } + world.defer_begin(); + + // let ret = callback(tuple); + + TableLock { + world, + arr_component_info, + data: tuple, + table, + stage_id, + } +} + +impl Drop for TableLock<'_, T> { + fn drop(&mut self) { + self.world.defer_end(); + + let component_info_arr = self.arr_component_info.column_indices_mut(); + for id_info in component_info_arr.iter() { + match id_info { + ComponentTypeRWLock::Sparse((idr, id)) => match id { + ReadWriteId::Read(_) => { + sparse_id_record_lock_read_end(*idr); + } + ReadWriteId::Write(_) => { + sparse_id_record_lock_write_end(*idr); + } + }, + ComponentTypeRWLock::Dense((column_index, id)) => match id { + ReadWriteId::Read(_) => { + //does not have the component + if *column_index == -1 { + continue; + } + table_column_lock_read_end(self.table, *column_index, self.stage_id); + } + ReadWriteId::Write(_) => { + //does not have the component + if *column_index == -1 { + continue; + } + table_column_lock_write_end(self.table, *column_index, self.stage_id); + } + }, + } + } + } +} + impl<'a> EntityView<'a> { /// Clones components and/or relationship(s) from an entity and returns it. /// each component type must be marked `&`. This helps Rust type checker to determine if it's a relationship. diff --git a/flecs_ecs/src/core/entity_view/mod.rs b/flecs_ecs/src/core/entity_view/mod.rs index beae789d..5f6af837 100644 --- a/flecs_ecs/src/core/entity_view/mod.rs +++ b/flecs_ecs/src/core/entity_view/mod.rs @@ -7,4 +7,5 @@ mod entity_view_mut; mod macros; pub use entity_view_const::EntityView; +pub use entity_view_const::EntityViewFetch; pub use entity_view_const::EntityViewGet; diff --git a/flecs_ecs/src/core/mod.rs b/flecs_ecs/src/core/mod.rs index 5400d946..273884e1 100644 --- a/flecs_ecs/src/core/mod.rs +++ b/flecs_ecs/src/core/mod.rs @@ -38,6 +38,7 @@ pub use component_registration::*; pub use components::*; pub use entity::Entity; pub use entity_view::EntityView; +pub use entity_view::EntityViewFetch; pub use entity_view::EntityViewGet; pub use event::EventBuilder; pub(crate) use get_tuple::*; diff --git a/flecs_ecs/tests/flecs/fetch_test.rs b/flecs_ecs/tests/flecs/fetch_test.rs new file mode 100644 index 00000000..b535117f --- /dev/null +++ b/flecs_ecs/tests/flecs/fetch_test.rs @@ -0,0 +1,12 @@ +#![allow(dead_code)] +use crate::common_test::*; + +#[test] +fn table_eq_test() { + let world = World::new(); + + let ent1 = world.entity().set(Position { x: 10, y: 20 }); + + let x = ent1.fetch::<&Position>(); + println!("position: {x:?}"); +} diff --git a/flecs_ecs/tests/flecs/main.rs b/flecs_ecs/tests/flecs/main.rs index a0843cd1..c24302c7 100644 --- a/flecs_ecs/tests/flecs/main.rs +++ b/flecs_ecs/tests/flecs/main.rs @@ -13,6 +13,7 @@ mod entity_rust_test; mod entity_test; mod enum_test; mod eq_test; +mod fetch_test; mod flecs_docs_test; mod is_ref_test; mod meta_macro_test;