Skip to content

feat: add .fetch #249

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 151 additions & 4 deletions flecs_ecs/src/core/entity_view/entity_view_const.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
use core::{
ffi::{CStr, c_void},
ops::{Deref, DerefMut},
Expand All @@ -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.
///
Expand Down Expand Up @@ -1444,6 +1444,10 @@ fn get_rw_lock<T: GetTuple, Return>(
ret
}

pub trait EntityViewFetch<'a>: WorldProvider<'a> + Sized {
fn fetch<T: GetTuple>(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.
Expand Down Expand Up @@ -1660,6 +1664,149 @@ impl<'a, Return> EntityViewGet<'a, Return> for EntityView<'a> {
}
}

impl<'a> EntityViewFetch<'a> for EntityView<'a> {
fn fetch<T: GetTuple>(self) -> TableLock<'a, T> {
init_locked::<T>(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<T: GetTuple> 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<T: GetTuple>(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::<true>(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<T: GetTuple> 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.
Expand Down
1 change: 1 addition & 0 deletions flecs_ecs/src/core/entity_view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
1 change: 1 addition & 0 deletions flecs_ecs/src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down
12 changes: 12 additions & 0 deletions flecs_ecs/tests/flecs/fetch_test.rs
Original file line number Diff line number Diff line change
@@ -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:?}");
}
1 change: 1 addition & 0 deletions flecs_ecs/tests/flecs/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading