Improve Lua ECS #30
|
@ -93,15 +93,23 @@ function on_update()
|
|||
---@type number
|
||||
local dt = world:resource(DeltaTime)
|
||||
|
||||
world:view(
|
||||
local view = View.new(Transform, WorldTransform)
|
||||
local res = world:view_query(view)
|
||||
|
||||
for entity, transform, world_tran in res:iter() do
|
||||
print("Entity is at: " .. tostring(world_tran))
|
||||
|
||||
transform:translate(0, 0.15 * dt, 0)
|
||||
entity:update(transform)
|
||||
end
|
||||
|
||||
--[[ world:view(
|
||||
---@param t Transform
|
||||
---@param wt WorldTransform
|
||||
function (t, wt)
|
||||
print("Entity is at: " .. tostring(wt))
|
||||
function (t)
|
||||
t:translate(0, 0.15 * dt, 0)
|
||||
return t
|
||||
end, Transform, WorldTransform
|
||||
)
|
||||
end, Transform
|
||||
) ]]
|
||||
|
||||
--[[ world:view(
|
||||
---@param c Camera
|
||||
|
|
|
@ -1,6 +1,16 @@
|
|||
use std::{ptr::{NonNull, self}, alloc::{self, Layout, alloc, dealloc}, mem, collections::HashMap, cell::{RefCell, Ref, RefMut, BorrowError, BorrowMutError}, ops::DerefMut};
|
||||
use std::{
|
||||
alloc::{self, alloc, dealloc, Layout},
|
||||
cell::{BorrowError, BorrowMutError, Ref, RefCell, RefMut},
|
||||
collections::HashMap,
|
||||
mem,
|
||||
ops::DerefMut,
|
||||
ptr::{self, NonNull},
|
||||
};
|
||||
|
||||
use crate::{bundle::Bundle, component_info::ComponentInfo, world::ArchetypeEntityId, DynTypeId, Entity, Tick};
|
||||
use crate::{
|
||||
bundle::Bundle, component_info::ComponentInfo, world::ArchetypeEntityId, DynTypeId, Entity,
|
||||
Tick,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ComponentColumn {
|
||||
|
@ -32,8 +42,9 @@ impl ComponentColumn {
|
|||
pub unsafe fn alloc(component_layout: Layout, capacity: usize) -> NonNull<u8> {
|
||||
let new_layout = Layout::from_size_align(
|
||||
component_layout.size().checked_mul(capacity).unwrap(),
|
||||
component_layout.align()
|
||||
).unwrap();
|
||||
component_layout.align(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if let Some(data) = NonNull::new(alloc(new_layout)) {
|
||||
data
|
||||
|
@ -44,7 +55,7 @@ impl ComponentColumn {
|
|||
|
||||
pub unsafe fn new(info: ComponentInfo, capacity: usize) -> Self {
|
||||
let data = ComponentColumn::alloc(info.layout(), capacity);
|
||||
|
||||
|
||||
Self {
|
||||
data: RefCell::new(data),
|
||||
capacity,
|
||||
|
@ -55,7 +66,7 @@ impl ComponentColumn {
|
|||
}
|
||||
|
||||
/// Set a component from pointer at an entity index.
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
/// This column must have space to fit the component, if it does not have room it will panic.
|
||||
pub unsafe fn set_at(&mut self, entity_index: usize, comp_src: NonNull<u8>, tick: Tick) {
|
||||
|
@ -63,7 +74,7 @@ impl ComponentColumn {
|
|||
|
||||
let mut data = self.data.borrow_mut();
|
||||
let data = data.deref_mut();
|
||||
|
||||
|
||||
let size = self.info.layout().size();
|
||||
let dest = NonNull::new_unchecked(data.as_ptr().add(entity_index * size));
|
||||
ptr::copy_nonoverlapping(comp_src.as_ptr(), dest.as_ptr(), size);
|
||||
|
@ -83,7 +94,7 @@ impl ComponentColumn {
|
|||
|
||||
let mut data = self.data.borrow_mut();
|
||||
let data = data.deref_mut();
|
||||
|
||||
|
||||
let size = self.info.layout().size();
|
||||
let dest = NonNull::new_unchecked(data.as_ptr().add(entity_index * size));
|
||||
ptr::copy_nonoverlapping(comp_src.as_ptr(), dest.as_ptr(), size);
|
||||
|
@ -95,25 +106,25 @@ impl ComponentColumn {
|
|||
}
|
||||
|
||||
/// Get a component at an entities index.
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
///
|
||||
/// This column MUST have the entity. If it does not, it WILL NOT panic and will cause UB.
|
||||
pub unsafe fn get<T>(&self, entity_index: usize) -> Ref<T> {
|
||||
let data = self.data.borrow();
|
||||
|
||||
Ref::map(data, |data| {
|
||||
let ptr = NonNull::new_unchecked(data.as_ptr()
|
||||
.add(entity_index * self.info.layout().size()))
|
||||
.cast();
|
||||
let ptr =
|
||||
NonNull::new_unchecked(data.as_ptr().add(entity_index * self.info.layout().size()))
|
||||
.cast();
|
||||
&*ptr.as_ptr()
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a mutable borrow to the component at an entities index, ticking the entity.
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
///
|
||||
/// This column must have the entity.
|
||||
pub unsafe fn get_mut<T>(&mut self, entity_index: usize, tick: &Tick) -> RefMut<T> {
|
||||
self.entity_ticks[entity_index].tick_to(tick);
|
||||
|
@ -121,22 +132,22 @@ impl ComponentColumn {
|
|||
let data = self.data.borrow_mut();
|
||||
|
||||
RefMut::map(data, |data| {
|
||||
let ptr = NonNull::new_unchecked(data.as_ptr()
|
||||
.add(entity_index * self.info.layout().size()))
|
||||
.cast();
|
||||
let ptr =
|
||||
NonNull::new_unchecked(data.as_ptr().add(entity_index * self.info.layout().size()))
|
||||
.cast();
|
||||
&mut *ptr.as_ptr()
|
||||
})
|
||||
}
|
||||
|
||||
/// Grow the column to fit `new_capacity` amount of components.
|
||||
///
|
||||
///
|
||||
/// Parameters:
|
||||
/// * `new_capacity` - The new capacity of components that can fit in this column.
|
||||
///
|
||||
///
|
||||
/// Note: This does not modify the Tick of this column, since no components were actually modified.
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
///
|
||||
/// Will panic if `new_capacity` is less than the current capacity of the column.
|
||||
pub unsafe fn grow(&mut self, new_capacity: usize) {
|
||||
assert!(new_capacity > self.capacity);
|
||||
|
@ -155,7 +166,7 @@ impl ComponentColumn {
|
|||
// create a layout with the same alignment, but expand the size of the buffer.
|
||||
let old_layout = Layout::from_size_align_unchecked(
|
||||
layout.size().checked_mul(self.capacity).unwrap(),
|
||||
layout.align()
|
||||
layout.align(),
|
||||
);
|
||||
|
||||
mem::swap(data.deref_mut(), &mut new_ptr);
|
||||
|
@ -163,7 +174,7 @@ impl ComponentColumn {
|
|||
} else {
|
||||
*data = new_ptr;
|
||||
}
|
||||
|
||||
|
||||
self.capacity = new_capacity;
|
||||
}
|
||||
|
||||
|
@ -171,19 +182,20 @@ impl ComponentColumn {
|
|||
pub unsafe fn remove_component(&mut self, entity_index: usize, tick: &Tick) -> Option<usize> {
|
||||
let _ = tick; // may be used at some point
|
||||
|
||||
debug_assert!(self.len > 0, "There are no entities in the Archetype to remove from!");
|
||||
|
||||
debug_assert!(
|
||||
self.len > 0,
|
||||
"There are no entities in the Archetype to remove from!"
|
||||
);
|
||||
|
||||
let mut data = self.data.borrow_mut();
|
||||
let data = data.deref_mut();
|
||||
|
||||
let size = self.info.layout().size();
|
||||
let mut old_comp_ptr = NonNull::new_unchecked(data.as_ptr()
|
||||
.add(entity_index * size));
|
||||
let mut old_comp_ptr = NonNull::new_unchecked(data.as_ptr().add(entity_index * size));
|
||||
|
||||
let moved_index = if entity_index != self.len - 1 {
|
||||
let moved_index = self.len - 1;
|
||||
let mut new_comp_ptr = NonNull::new_unchecked(data.as_ptr()
|
||||
.add(moved_index * size));
|
||||
let mut new_comp_ptr = NonNull::new_unchecked(data.as_ptr().add(moved_index * size));
|
||||
|
||||
ptr::copy_nonoverlapping(new_comp_ptr.as_ptr(), old_comp_ptr.as_ptr(), size);
|
||||
|
||||
|
@ -193,13 +205,25 @@ impl ComponentColumn {
|
|||
self.entity_ticks.swap_remove(entity_index);
|
||||
|
||||
Some(moved_index)
|
||||
} else { None };
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
self.len -= 1;
|
||||
|
||||
moved_index
|
||||
}
|
||||
|
||||
/// Get the pointer of the component for the entity.
|
||||
///
|
||||
/// It is assumed that the component will be mutated, meaning the component's tick will be
|
||||
/// updated.
|
||||
pub fn get_entity_ptr(&mut self, entity_index: usize, tick: &Tick) -> NonNull<u8> {
|
||||
self.entity_ticks.insert(entity_index, *tick);
|
||||
let size = self.info.layout().size();
|
||||
unsafe { NonNull::new_unchecked(self.borrow_ptr().as_ptr().add(entity_index * size)) }
|
||||
}
|
||||
|
||||
pub fn borrow_ptr(&self) -> Ref<NonNull<u8>> {
|
||||
self.data.borrow()
|
||||
}
|
||||
|
@ -226,13 +250,13 @@ impl ArchetypeId {
|
|||
pub(crate) fn increment(&mut self) -> Self {
|
||||
let v = self.0;
|
||||
self.0 += 1;
|
||||
|
||||
|
||||
ArchetypeId(v)
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores a group of entities with matching components.
|
||||
///
|
||||
///
|
||||
/// An Archetype can be thought of as a table, with entities as the rows and the entity's
|
||||
/// components as each column. This means you can have tightly packed components of entities and
|
||||
/// quickly iterate through entities with the same components.
|
||||
|
@ -267,9 +291,10 @@ impl Archetype {
|
|||
}
|
||||
|
||||
pub fn from_bundle_info(new_id: ArchetypeId, bundle_info: Vec<ComponentInfo>) -> Archetype {
|
||||
let columns = bundle_info.into_iter().map(|i| {
|
||||
unsafe { ComponentColumn::new(i, DEFAULT_CAPACITY) }
|
||||
}).collect();
|
||||
let columns = bundle_info
|
||||
.into_iter()
|
||||
.map(|i| unsafe { ComponentColumn::new(i, DEFAULT_CAPACITY) })
|
||||
.collect();
|
||||
|
||||
Archetype {
|
||||
id: new_id,
|
||||
|
@ -281,13 +306,18 @@ impl Archetype {
|
|||
}
|
||||
|
||||
/// Add an entity and its component bundle to the Archetype
|
||||
///
|
||||
///
|
||||
/// # Safety:
|
||||
///
|
||||
///
|
||||
/// Archetype must contain all of the components
|
||||
pub(crate) fn add_entity<B>(&mut self, entity: Entity, bundle: B, tick: &Tick) -> ArchetypeEntityId
|
||||
pub(crate) fn add_entity<B>(
|
||||
&mut self,
|
||||
entity: Entity,
|
||||
bundle: B,
|
||||
tick: &Tick,
|
||||
) -> ArchetypeEntityId
|
||||
where
|
||||
B: Bundle
|
||||
B: Bundle,
|
||||
{
|
||||
if self.capacity == self.entity_ids.len() {
|
||||
let new_cap = self.capacity * 2;
|
||||
|
@ -301,28 +331,49 @@ impl Archetype {
|
|||
self.entities.push(entity);
|
||||
|
||||
bundle.take(|data, type_id, info| {
|
||||
self.put_component_at(tick, data, type_id, info.layout().size(), entity_index.0 as _);
|
||||
self.put_component_at(
|
||||
tick,
|
||||
data,
|
||||
type_id,
|
||||
info.layout().size(),
|
||||
entity_index.0 as _,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
entity_index
|
||||
}
|
||||
|
||||
pub(crate) fn put_component_at(&mut self, tick: &Tick, ptr: NonNull<u8>, type_id: DynTypeId, size: usize, index: usize) {
|
||||
pub(crate) fn put_component_at(
|
||||
&mut self,
|
||||
tick: &Tick,
|
||||
ptr: NonNull<u8>,
|
||||
type_id: DynTypeId,
|
||||
size: usize,
|
||||
index: usize,
|
||||
) {
|
||||
let _ = size;
|
||||
|
||||
let col = self.get_column_mut(type_id).unwrap();
|
||||
//unsafe { col.set_at(index, ptr, *tick) };
|
||||
unsafe { col.insert_entity(index, ptr, *tick); }
|
||||
unsafe {
|
||||
col.insert_entity(index, ptr, *tick);
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes an entity from the Archetype and frees its components.
|
||||
///
|
||||
///
|
||||
/// Inside the component columns, the entities are swap-removed. Meaning that the last
|
||||
/// entity in the column is moved in the position of the entity that was removed.
|
||||
/// If there was an entity that was swapped, this function returns the entity, and its
|
||||
/// new index in the archetype that was put in place of the removed entity.
|
||||
pub(crate) fn remove_entity(&mut self, entity: Entity, tick: &Tick) -> Option<(Entity, ArchetypeEntityId)> {
|
||||
let entity_index = self.entity_ids.remove(&entity)
|
||||
pub(crate) fn remove_entity(
|
||||
&mut self,
|
||||
entity: Entity,
|
||||
tick: &Tick,
|
||||
) -> Option<(Entity, ArchetypeEntityId)> {
|
||||
let entity_index = self
|
||||
.entity_ids
|
||||
.remove(&entity)
|
||||
.expect("The entity is not in this Archetype!");
|
||||
let mut removed_entity: Option<(Entity, ArchetypeEntityId)> = None;
|
||||
|
||||
|
@ -339,19 +390,19 @@ impl Archetype {
|
|||
removed_entity = Some((just_removed, ArchetypeEntityId(moved_idx as u64)));
|
||||
}
|
||||
} else {
|
||||
// If there wasn't a moved entity, make sure no other columns moved something.
|
||||
// If there wasn't a moved entity, make sure no other columns moved something.
|
||||
assert!(removed_entity.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
// safe from the .expect at the start of this method.
|
||||
//self.entity_ids.remove(&entity).unwrap();
|
||||
|
||||
|
||||
// update the archetype index of the moved entity
|
||||
if let Some((moved, _old_idx)) = removed_entity {
|
||||
self.entity_ids.insert(moved, entity_index);
|
||||
}
|
||||
|
||||
|
||||
let removed = self.entities.swap_remove(entity_index.0 as _);
|
||||
assert_eq!(removed, entity);
|
||||
|
||||
|
@ -362,8 +413,12 @@ impl Archetype {
|
|||
/// Returns a boolean indicating whether this archetype can store the TypeIds given
|
||||
pub fn is_archetype_for(&self, types: &Vec<DynTypeId>) -> bool {
|
||||
if types.len() == self.columns.len() {
|
||||
self.columns.iter().all(|c| types.contains(&c.info.type_id()))
|
||||
} else { false }
|
||||
self.columns
|
||||
.iter()
|
||||
.all(|c| types.contains(&c.info.type_id()))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a boolean indicating whether this archetype has a column for `comp_type`
|
||||
|
@ -383,18 +438,20 @@ impl Archetype {
|
|||
}
|
||||
|
||||
/// Grows columns in the archetype
|
||||
///
|
||||
///
|
||||
/// Parameters:
|
||||
/// * `new_capacity` - The new capacity of components that can fit in this column.
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
///
|
||||
/// Will panic if new_capacity is less than the current capacity
|
||||
fn grow_columns(&mut self, new_capacity: usize) {
|
||||
assert!(new_capacity > self.capacity);
|
||||
|
||||
for c in self.columns.iter_mut() {
|
||||
unsafe { c.grow(new_capacity); }
|
||||
unsafe {
|
||||
c.grow(new_capacity);
|
||||
}
|
||||
}
|
||||
|
||||
self.capacity = new_capacity;
|
||||
|
@ -412,11 +469,16 @@ impl Archetype {
|
|||
}
|
||||
|
||||
/// Returns a mutable borrow to a component column for `type_id`.
|
||||
///
|
||||
///
|
||||
/// Note: This does not modify the tick for the column!
|
||||
pub fn get_column_mut<I: Into<DynTypeId>>(&mut self, type_id: I) -> Option<&mut ComponentColumn> {
|
||||
pub fn get_column_mut<I: Into<DynTypeId>>(
|
||||
&mut self,
|
||||
type_id: I,
|
||||
) -> Option<&mut ComponentColumn> {
|
||||
let type_id = type_id.into();
|
||||
self.columns.iter_mut().find(|c| c.info.type_id() == type_id)
|
||||
self.columns
|
||||
.iter_mut()
|
||||
.find(|c| c.info.type_id() == type_id)
|
||||
}
|
||||
|
||||
/// Reserves a slot in the columns for an entity and returns the index of that reserved spot
|
||||
|
@ -427,8 +489,11 @@ impl Archetype {
|
|||
self.capacity = new_cap;
|
||||
}
|
||||
|
||||
debug_assert_eq!(self.entity_ids.len(), self.entities.len(),
|
||||
"Somehow the Archetype's entity storage got unsynced");
|
||||
debug_assert_eq!(
|
||||
self.entity_ids.len(),
|
||||
self.entities.len(),
|
||||
"Somehow the Archetype's entity storage got unsynced"
|
||||
);
|
||||
let entity_index = ArchetypeEntityId(self.entity_ids.len() as u64);
|
||||
self.entity_ids.insert(entity, entity_index);
|
||||
self.entities.push(entity);
|
||||
|
@ -442,12 +507,15 @@ impl Archetype {
|
|||
|
||||
/// Ensure that the internal entity lists are synced in length
|
||||
pub(crate) fn ensure_synced(&self) {
|
||||
debug_assert_eq!(self.entity_ids.len(), self.entities.len(),
|
||||
"Somehow the Archetype's entity storage got unsynced");
|
||||
debug_assert_eq!(
|
||||
self.entity_ids.len(),
|
||||
self.entities.len(),
|
||||
"Somehow the Archetype's entity storage got unsynced"
|
||||
);
|
||||
}
|
||||
|
||||
/// Moves the entity from this archetype into another one.
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
/// The entity IS NOT removed from the old archetype. You must manually call [`Archetype::remove_entity`](crate::Archetype).
|
||||
/// It was done this way because I had some borrow check issues when writing [`World::insert`](crate::World)
|
||||
|
@ -463,7 +531,7 @@ impl Archetype {
|
|||
// move the existing components into the new archetype
|
||||
for col in self.columns.iter() {
|
||||
let into_col = into_arch.get_column_mut(col.info.type_id()).unwrap();
|
||||
|
||||
|
||||
// copy from the old column into the new column, then remove it from the old one
|
||||
unsafe {
|
||||
let ptr = col.borrow_ptr();
|
||||
|
@ -514,19 +582,20 @@ impl Archetype {
|
|||
}
|
||||
|
||||
/// Extend the Archetype by adding more columns.
|
||||
///
|
||||
///
|
||||
/// In order to extend the Archetype, the archetype needs the components for the entities
|
||||
/// it already has. These are provided through the `new_columns` parameter. **If the Vec
|
||||
/// does not have the same amount of bundles in it as the amount of entities in the
|
||||
/// Archetype, it will panic!**
|
||||
pub fn extend<B: Bundle>(&mut self, tick: &Tick, new_columns: Vec<B>) {
|
||||
debug_assert_eq!(new_columns.len(), self.len(), "The amount of provided column does not \
|
||||
match the amount of entities");
|
||||
|
||||
let column_info = new_columns.iter()
|
||||
.next()
|
||||
.unwrap()
|
||||
.info();
|
||||
debug_assert_eq!(
|
||||
new_columns.len(),
|
||||
self.len(),
|
||||
"The amount of provided column does not \
|
||||
match the amount of entities"
|
||||
);
|
||||
|
||||
let column_info = new_columns.iter().next().unwrap().info();
|
||||
|
||||
for coli in column_info.into_iter() {
|
||||
let col = unsafe { ComponentColumn::new(coli, self.capacity) };
|
||||
|
@ -534,11 +603,9 @@ impl Archetype {
|
|||
}
|
||||
|
||||
for (eid, bundle) in new_columns.into_iter().enumerate() {
|
||||
bundle.take(|ptr, tyid, _size| {
|
||||
unsafe {
|
||||
let col = self.get_column_mut(tyid).unwrap();
|
||||
col.insert_entity(eid, ptr, tick.clone());
|
||||
}
|
||||
bundle.take(|ptr, tyid, _size| unsafe {
|
||||
let col = self.get_column_mut(tyid).unwrap();
|
||||
col.insert_entity(eid, ptr, tick.clone());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -550,7 +617,11 @@ mod tests {
|
|||
|
||||
use rand::Rng;
|
||||
|
||||
use crate::{bundle::Bundle, tests::{Vec2, Vec3}, ComponentInfo, DynTypeId, DynamicBundle, Entity, EntityId, Tick};
|
||||
use crate::{
|
||||
bundle::Bundle,
|
||||
tests::{Vec2, Vec3},
|
||||
ComponentInfo, DynTypeId, DynamicBundle, Entity, EntityId, Tick,
|
||||
};
|
||||
|
||||
use super::Archetype;
|
||||
|
||||
|
@ -559,7 +630,7 @@ mod tests {
|
|||
let bundle = (Vec2::new(10.0, 20.0),);
|
||||
let entity = Entity {
|
||||
id: EntityId(0),
|
||||
generation: 0
|
||||
generation: 0,
|
||||
};
|
||||
|
||||
let mut a = Archetype::from_bundle_info(super::ArchetypeId(0), bundle.info());
|
||||
|
@ -572,10 +643,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn one_entity_two_component() {
|
||||
let bundle = (Vec2::new(10.0, 20.0),Vec3::new(15.0, 54.0, 84.0));
|
||||
let bundle = (Vec2::new(10.0, 20.0), Vec3::new(15.0, 54.0, 84.0));
|
||||
let entity = Entity {
|
||||
id: EntityId(0),
|
||||
generation: 0
|
||||
generation: 0,
|
||||
};
|
||||
|
||||
let mut a = Archetype::from_bundle_info(super::ArchetypeId(0), bundle.info());
|
||||
|
@ -595,12 +666,12 @@ mod tests {
|
|||
let b1 = (Vec2::new(10.0, 20.0),);
|
||||
let e1 = Entity {
|
||||
id: EntityId(0),
|
||||
generation: 0
|
||||
generation: 0,
|
||||
};
|
||||
let b2 = (Vec2::new(19.0, 43.0),);
|
||||
let e2 = Entity {
|
||||
id: EntityId(1),
|
||||
generation: 0
|
||||
generation: 0,
|
||||
};
|
||||
|
||||
let mut a = Archetype::from_bundle_info(super::ArchetypeId(0), b1.info());
|
||||
|
@ -619,12 +690,12 @@ mod tests {
|
|||
let b1 = (Vec2::new(10.0, 20.0), Vec3::new(84.0, 283.0, 28.0));
|
||||
let e1 = Entity {
|
||||
id: EntityId(0),
|
||||
generation: 0
|
||||
generation: 0,
|
||||
};
|
||||
let b2 = (Vec2::new(19.0, 43.0), Vec3::new(74.0, 28.0, 93.0));
|
||||
let e2 = Entity {
|
||||
id: EntityId(1),
|
||||
generation: 0
|
||||
generation: 0,
|
||||
};
|
||||
|
||||
let mut a = Archetype::from_bundle_info(super::ArchetypeId(0), b1.info());
|
||||
|
@ -659,7 +730,7 @@ mod tests {
|
|||
fn auto_archetype_growth() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let bundle_count = rng.gen_range(50..150);
|
||||
|
||||
|
||||
let mut bundles: Vec<(Vec2,)> = vec![];
|
||||
bundles.reserve(bundle_count);
|
||||
|
||||
|
@ -669,14 +740,14 @@ mod tests {
|
|||
for i in 0..bundle_count {
|
||||
let c = (Vec2::rand(),);
|
||||
bundles.push(c);
|
||||
|
||||
|
||||
a.add_entity(
|
||||
Entity {
|
||||
id: EntityId(i as u64),
|
||||
generation: 0
|
||||
generation: 0,
|
||||
},
|
||||
c,
|
||||
&Tick::default()
|
||||
&Tick::default(),
|
||||
);
|
||||
}
|
||||
println!("Inserted {} entities", bundle_count);
|
||||
|
@ -701,25 +772,27 @@ mod tests {
|
|||
a.add_entity(
|
||||
Entity {
|
||||
id: EntityId(i as u64),
|
||||
generation: 0
|
||||
generation: 0,
|
||||
},
|
||||
(bundles[i],),
|
||||
&Tick::default()
|
||||
&Tick::default(),
|
||||
);
|
||||
}
|
||||
|
||||
// Remove the 'middle' entity in the column
|
||||
let moved_entity = a.remove_entity(
|
||||
Entity {
|
||||
id: EntityId(1u64),
|
||||
generation: 0,
|
||||
},
|
||||
&Tick::default()
|
||||
).expect("No entity was moved");
|
||||
let moved_entity = a
|
||||
.remove_entity(
|
||||
Entity {
|
||||
id: EntityId(1u64),
|
||||
generation: 0,
|
||||
},
|
||||
&Tick::default(),
|
||||
)
|
||||
.expect("No entity was moved");
|
||||
|
||||
// The last entity in the column should have been moved
|
||||
assert!(moved_entity.0.id.0 == 2);
|
||||
assert!(moved_entity.1.0 == 1);
|
||||
assert!(moved_entity.1 .0 == 1);
|
||||
|
||||
// make sure that the entities' component was actually moved in the column
|
||||
let col = &a.columns[0];
|
||||
|
@ -731,7 +804,8 @@ mod tests {
|
|||
#[test]
|
||||
fn dynamic_archetype() {
|
||||
let layout = Layout::new::<u32>();
|
||||
let info = ComponentInfo::new_unknown(Some("u32".to_string()), DynTypeId::Unknown(100), layout);
|
||||
let info =
|
||||
ComponentInfo::new_unknown(Some("u32".to_string()), DynTypeId::Unknown(100), layout);
|
||||
let infos = vec![info.clone()];
|
||||
|
||||
let mut a = Archetype::from_bundle_info(super::ArchetypeId(0), infos);
|
||||
|
@ -744,10 +818,10 @@ mod tests {
|
|||
a.add_entity(
|
||||
Entity {
|
||||
id: EntityId(0),
|
||||
generation: 0
|
||||
generation: 0,
|
||||
},
|
||||
dynamic_bundle,
|
||||
&Tick::default()
|
||||
&Tick::default(),
|
||||
);
|
||||
|
||||
let col = a.columns.iter().next().unwrap();
|
||||
|
@ -764,15 +838,11 @@ mod tests {
|
|||
|
||||
let ae = Entity {
|
||||
id: EntityId(0),
|
||||
generation: 0
|
||||
generation: 0,
|
||||
};
|
||||
|
||||
a.add_entity(
|
||||
ae,
|
||||
Vec2::new(10.0, 50.0),
|
||||
&Tick::default()
|
||||
);
|
||||
a.add_entity(ae, Vec2::new(10.0, 50.0), &Tick::default());
|
||||
|
||||
a.remove_entity(ae, &Tick::default());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{any::TypeId, collections::HashMap, ptr::NonNull};
|
||||
use std::{any::TypeId, collections::HashMap, ops::Deref, ptr::NonNull};
|
||||
|
||||
use atomic_refcell::{AtomicRef, AtomicRefMut};
|
||||
|
||||
|
@ -10,6 +10,14 @@ use crate::{archetype::{Archetype, ArchetypeId}, bundle::Bundle, query::{dynamic
|
|||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ArchetypeEntityId(pub u64);
|
||||
|
||||
impl Deref for ArchetypeEntityId {
|
||||
type Target = u64;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Record {
|
||||
pub id: ArchetypeId,
|
||||
|
|
|
@ -3,6 +3,8 @@ use std::{ptr::NonNull, ops::Deref};
|
|||
use lyra_ecs::{query::dynamic::DynamicViewStateIter, Entity};
|
||||
use lyra_reflect::TypeRegistry;
|
||||
|
||||
use crate::ScriptWorldPtr;
|
||||
|
||||
#[cfg(feature = "lua")]
|
||||
use super::ReflectLuaProxy;
|
||||
|
||||
|
@ -19,54 +21,66 @@ pub struct ReflectedRow {
|
|||
pub row: Vec<ReflectedItem>,
|
||||
}
|
||||
|
||||
pub struct ReflectedIteratorOwned {
|
||||
pub world_ptr: ScriptWorldPtr,
|
||||
pub dyn_view: DynamicViewStateIter,
|
||||
//pub reflected_components: Option<NonNull<TypeRegistry>>
|
||||
}
|
||||
|
||||
impl ReflectedIteratorOwned {
|
||||
pub fn next_lua(&mut self, lua: &mlua::Lua) -> Option<ReflectedRow> {
|
||||
let world = self.world_ptr.read();
|
||||
next_lua(lua, &world, &mut self.dyn_view)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReflectedIterator<'a> {
|
||||
pub world: &'a lyra_ecs::World,
|
||||
pub dyn_view: DynamicViewStateIter,
|
||||
pub reflected_components: Option<NonNull<TypeRegistry>>
|
||||
}
|
||||
|
||||
impl<'a> ReflectedIterator<'a> {
|
||||
#[cfg(feature = "lua")]
|
||||
pub fn next_lua(&mut self, lua: &mlua::Lua) -> Option<ReflectedRow> {
|
||||
use mlua::IntoLua;
|
||||
next_lua(lua, &self.world, &mut self.dyn_view)
|
||||
}
|
||||
}
|
||||
|
||||
//let world = self.world.read();
|
||||
let n = self.dyn_view.next(&self.world);
|
||||
|
||||
if let Some((en, row)) = n {
|
||||
if self.reflected_components.is_none() {
|
||||
self.reflected_components = self.world.get_resource::<TypeRegistry>()
|
||||
.map(|r| NonNull::from(r.deref()));
|
||||
}
|
||||
fn next_lua(lua: &mlua::Lua, world: &lyra_ecs::World, dyn_view: &mut DynamicViewStateIter) -> Option<ReflectedRow> {
|
||||
use mlua::IntoLua;
|
||||
|
||||
let mut dynamic_row = vec![];
|
||||
for d in row.iter() {
|
||||
let id = d.info.type_id().as_rust();
|
||||
let reflected_components =
|
||||
unsafe { self.reflected_components.as_ref().unwrap().as_ref() };
|
||||
|
||||
let reg_type = reflected_components.get_type(id)
|
||||
.expect("Requested type was not found in TypeRegistry");
|
||||
let proxy = reg_type.get_data::<ReflectLuaProxy>()
|
||||
// TODO: properly handle this error
|
||||
.expect("Type does not have ReflectLuaProxy as a TypeData");
|
||||
let value = (proxy.fn_as_lua)(lua, d.ptr.cast()).unwrap()
|
||||
.into_lua(lua).unwrap();
|
||||
//let world = world.read();
|
||||
let n = dyn_view.next(&world);
|
||||
|
||||
if let Some((en, row)) = n {
|
||||
let reflected_components = world.get_resource::<TypeRegistry>().unwrap();
|
||||
|
||||
dynamic_row.push(ReflectedItem {
|
||||
comp_ptr: d.ptr,
|
||||
comp_val: value
|
||||
});
|
||||
}
|
||||
let mut dynamic_row = vec![];
|
||||
for d in row.iter() {
|
||||
let id = d.info.type_id().as_rust();
|
||||
/* let reflected_components =
|
||||
unsafe { reflected_components.as_ref().unwrap().as_ref() }; */
|
||||
|
||||
let reg_type = reflected_components.get_type(id)
|
||||
.expect("Requested type was not found in TypeRegistry");
|
||||
let proxy = reg_type.get_data::<ReflectLuaProxy>()
|
||||
// TODO: properly handle this error
|
||||
.expect("Type does not have ReflectLuaProxy as a TypeData");
|
||||
let value = (proxy.fn_as_lua)(lua, d.ptr.cast()).unwrap()
|
||||
.into_lua(lua).unwrap();
|
||||
|
||||
let row = ReflectedRow {
|
||||
entity: en,
|
||||
row: dynamic_row
|
||||
};
|
||||
|
||||
Some(row)
|
||||
} else {
|
||||
None
|
||||
dynamic_row.push(ReflectedItem {
|
||||
comp_ptr: d.ptr,
|
||||
comp_val: value
|
||||
});
|
||||
}
|
||||
|
||||
let row = ReflectedRow {
|
||||
entity: en,
|
||||
row: dynamic_row
|
||||
};
|
||||
|
||||
Some(row)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
use std::{any::TypeId, sync::Arc};
|
||||
|
||||
use lyra_ecs::{Entity, World};
|
||||
use lyra_reflect::TypeRegistry;
|
||||
use mlua::IntoLua;
|
||||
|
||||
use crate::ScriptWorldPtr;
|
||||
|
||||
use super::{reflect_user_data, ReflectLuaProxy, TypeLookup};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum LuaComponent {
|
||||
UserData(mlua::AnyUserData),
|
||||
Table(mlua::Table),
|
||||
}
|
||||
|
||||
impl mlua::FromLua for LuaComponent {
|
||||
fn from_lua(value: mlua::Value, lua: &mlua::Lua) -> mlua::Result<Self> {
|
||||
let tyname = value.type_name();
|
||||
match value {
|
||||
mlua::Value::UserData(ud) => Ok(Self::UserData(ud)),
|
||||
mlua::Value::Table(t) => Ok(Self::Table(t)),
|
||||
_ => Err(mlua::Error::FromLuaConversionError {
|
||||
from: tyname,
|
||||
to: "LuaComponent".into(),
|
||||
message: Some(
|
||||
"expected Table or UserData that can be converted to a native struct".into(),
|
||||
),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::IntoLua for LuaComponent {
|
||||
fn into_lua(self, lua: &mlua::Lua) -> mlua::Result<mlua::Value> {
|
||||
match self {
|
||||
Self::Table(t) => t.into_lua(lua),
|
||||
Self::UserData(ud) => ud.into_lua(lua),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaComponent {
|
||||
pub fn get_typeid(&self, world: &World) -> Option<TypeId> {
|
||||
match self {
|
||||
Self::Table(t) => {
|
||||
let name: String = t.get(mlua::MetaMethod::Type.name()).ok()?;
|
||||
let lookup = world.get_resource::<TypeLookup>().unwrap();
|
||||
lookup.typeid_from_name.get(&name).cloned()
|
||||
}
|
||||
Self::UserData(ud) => {
|
||||
let lua_comp = reflect_user_data(ud);
|
||||
let refl_comp = lua_comp.reflect_branch.as_component_unchecked();
|
||||
Some(refl_comp.info.type_id().as_rust())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reference to an Entity for Lua
|
||||
///
|
||||
/// This can be used to make it easier to update things on the Entity.
|
||||
pub struct LuaEntityRef {
|
||||
en: Entity,
|
||||
world: ScriptWorldPtr,
|
||||
}
|
||||
|
||||
impl LuaEntityRef {
|
||||
pub fn new(world: ScriptWorldPtr, en: Entity) -> Self {
|
||||
Self {
|
||||
en,
|
||||
world,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::UserData for LuaEntityRef {
|
||||
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method(
|
||||
"update",
|
||||
|lua, this, comps: mlua::Variadic<LuaComponent>| {
|
||||
let mut world = this.world.write();
|
||||
let world_tick = world.current_tick();
|
||||
|
||||
for (i, comp) in comps.iter().enumerate() {
|
||||
let tid = comp.get_typeid(&world).ok_or(mlua::Error::BadArgument {
|
||||
to: Some("Entity:update".into()),
|
||||
pos: 2 + i,
|
||||
name: Some("comps...".into()),
|
||||
cause: Arc::new(mlua::Error::runtime(
|
||||
"failed to get native TypeId from component",
|
||||
)),
|
||||
})?;
|
||||
// convert component to mlua::Value
|
||||
let comp = comp.clone().into_lua(lua)?;
|
||||
|
||||
// get the pointer of the component in the archetype column.
|
||||
let arch = world.entity_archetype_mut(this.en).unwrap();
|
||||
let arch_idx = *arch.entity_indexes().get(&this.en).unwrap();
|
||||
let col = arch.get_column_mut(tid).unwrap();
|
||||
let col_ptr = col.get_entity_ptr(*arch_idx as usize, &world_tick).cast();
|
||||
|
||||
// get the type registry to apply the new value
|
||||
let reg = world.get_resource::<TypeRegistry>().unwrap();
|
||||
let reg_type = reg.get_type(tid).unwrap();
|
||||
|
||||
let proxy = reg_type
|
||||
.get_data::<ReflectLuaProxy>()
|
||||
// this should actually be safe since the ReflectedIterator
|
||||
// attempts to get the type data before it is tried here
|
||||
.expect("Type does not have ReflectLuaProxy as a TypeData");
|
||||
(proxy.fn_apply)(lua, col_ptr, &comp)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -18,6 +18,12 @@ pub mod wrappers;
|
|||
pub mod proxy;
|
||||
pub use proxy::*;
|
||||
|
||||
mod view;
|
||||
pub use view::*;
|
||||
|
||||
mod entity_ref;
|
||||
pub use entity_ref::*;
|
||||
|
||||
pub mod system;
|
||||
pub use system::*;
|
||||
use wrappers::{LuaHandleWrapper, LuaResHandleToComponent, LuaWrappedEventProxy};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use lyra_ecs::ResourceObject;
|
||||
use lyra_reflect::Reflect;
|
||||
|
||||
use crate::{lua::{wrappers::*, LuaContext, LuaWrapper, RegisterLuaType, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptApiProvider, ScriptBorrow, ScriptData, ScriptDynamicBundle, ScriptWorldPtr};
|
||||
use crate::{lua::{wrappers::*, LuaContext, LuaWrapper, RegisterLuaType, View, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptApiProvider, ScriptBorrow, ScriptData, ScriptDynamicBundle, ScriptWorldPtr};
|
||||
|
||||
//fn register_lua_proxy::<T:
|
||||
|
||||
|
@ -42,6 +42,7 @@ impl ScriptApiProvider for LyraEcsApiProvider {
|
|||
globals.set("SceneHandler", ctx.create_proxy::<LuaSceneHandle>()?)?;
|
||||
globals.set("ActionHandler", ctx.create_proxy::<LuaActionHandler>()?)?;
|
||||
globals.set("Window", ctx.create_proxy::<LuaWindow>()?)?;
|
||||
globals.set("View", ctx.create_proxy::<View>()?)?;
|
||||
|
||||
expose_comp_table_wrapper::<LuaCamera>(&ctx, &globals, "Camera")?;
|
||||
expose_comp_table_wrapper::<LuaFreeFlyCamera>(&ctx, &globals, "FreeFlyCamera")?;
|
||||
|
|
|
@ -0,0 +1,225 @@
|
|||
use std::{cell::RefCell, ptr::NonNull, rc::Rc, sync::Arc};
|
||||
|
||||
use lyra_ecs::{query::dynamic::{DynamicViewState, QueryDynamicType}, Entity};
|
||||
use mlua::{FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{lua::{WorldError, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptBorrow, ScriptWorldPtr};
|
||||
|
||||
use super::{LuaEntityRef, ReflectedIteratorOwned, TypeLookup};
|
||||
|
||||
#[derive(Clone)]
|
||||
enum ViewQueryItem {
|
||||
UserData(mlua::AnyUserData),
|
||||
Table(mlua::Table),
|
||||
Function(mlua::Function),
|
||||
}
|
||||
|
||||
impl mlua::FromLua for ViewQueryItem {
|
||||
fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
|
||||
let tyname = value.type_name();
|
||||
match value {
|
||||
mlua::Value::Table(table) => Ok(Self::Table(table)),
|
||||
mlua::Value::Function(function) => Ok(Self::Function(function)),
|
||||
mlua::Value::UserData(any_user_data) => Ok(Self::UserData(any_user_data)),
|
||||
_ => Err(mlua::Error::FromLuaConversionError {
|
||||
from: tyname,
|
||||
to: "ViewQueryItem".into(),
|
||||
message: Some("expected Table, Function, or UserData".into()),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ViewQueryItem {
|
||||
pub fn is_filter(&self) -> bool {
|
||||
// TODO: check if UserData is a rust implemented filter
|
||||
matches!(self, ViewQueryItem::Function(_))
|
||||
}
|
||||
|
||||
/* pub fn get_lookup(&self) -> Option<Res<TypeLookup>> {
|
||||
match self {
|
||||
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct View {
|
||||
items: Vec<ViewQueryItem>,
|
||||
}
|
||||
|
||||
impl mlua::FromLua for View {
|
||||
fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
|
||||
let tyname = value.type_name();
|
||||
value.as_userdata()
|
||||
.ok_or(mlua::Error::FromLuaConversionError {
|
||||
from: tyname,
|
||||
to: "View".into(),
|
||||
message: None,
|
||||
})
|
||||
.and_then(|ud| ud.borrow::<Self>())
|
||||
.map(|ud| ud.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::UserData for View {
|
||||
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_function("new", |_, args: mlua::Variadic<ViewQueryItem>| {
|
||||
Ok(Self {
|
||||
items: args.iter().cloned().collect(),
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ViewResult {
|
||||
world: ScriptWorldPtr,
|
||||
items: Vec<ViewQueryItem>,
|
||||
query_iter: Arc<atomic_refcell::AtomicRefCell<ReflectedIteratorOwned>>,
|
||||
reg_key: Arc<atomic_refcell::AtomicRefCell<Option<mlua::RegistryKey>>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for ViewResult {}
|
||||
|
||||
impl ViewResult {
|
||||
pub fn new(world: ScriptWorldPtr, view: &View) -> Result<Self, mlua::Error> {
|
||||
let items = view.items.clone();
|
||||
let w = world.read();
|
||||
let mut view = DynamicViewState::new();
|
||||
|
||||
for (idx, comp) in items.iter().enumerate() {
|
||||
match comp {
|
||||
ViewQueryItem::Table(t) => {
|
||||
let name: String = t.get(mlua::MetaMethod::Type.name())?;
|
||||
|
||||
let lookup =
|
||||
w
|
||||
.get_resource::<TypeLookup>()
|
||||
.ok_or(mlua::Error::runtime(
|
||||
"Unable to lookup table proxy, none were ever registered!",
|
||||
))?;
|
||||
let info = lookup.comp_info_from_name.get(&name).ok_or_else(|| {
|
||||
mlua::Error::BadArgument {
|
||||
to: Some("ViewResult.new".into()),
|
||||
pos: 2 + idx,
|
||||
name: Some("query...".into()),
|
||||
cause: Arc::new(mlua::Error::external(
|
||||
WorldError::LuaInvalidUsage(format!(
|
||||
"the 'Table' with name {} is unknown to the engine!",
|
||||
name
|
||||
)),
|
||||
)),
|
||||
}
|
||||
})?;
|
||||
|
||||
let dyn_type = QueryDynamicType::from_info(info.clone());
|
||||
view.push(dyn_type);
|
||||
}
|
||||
ViewQueryItem::UserData(ud) => {
|
||||
let reflect = ud
|
||||
.call_function::<ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ())
|
||||
.expect("Type does not implement 'reflect_type' properly");
|
||||
let refl_comp = reflect.reflect_branch.as_component_unchecked();
|
||||
|
||||
let dyn_type = QueryDynamicType::from_info(refl_comp.info);
|
||||
view.push(dyn_type);
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
drop(w);
|
||||
|
||||
let view_iter = view.into_iter();
|
||||
let reflected_iter = ReflectedIteratorOwned {
|
||||
world_ptr: world.clone(),
|
||||
dyn_view: view_iter,
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
world,
|
||||
items,
|
||||
query_iter: Arc::new(atomic_refcell::AtomicRefCell::new(reflected_iter)),
|
||||
reg_key: Arc::new(atomic_refcell::AtomicRefCell::new(None)),
|
||||
})
|
||||
}
|
||||
|
||||
fn next(&mut self, lua: &mlua::Lua) -> Result<Option<(Entity, mlua::MultiValue)>, mlua::Error> {
|
||||
let mut query_iter = self.query_iter.borrow_mut();
|
||||
if let Some(row) = query_iter.next_lua(lua) {
|
||||
let r = row
|
||||
.row
|
||||
.into_iter()
|
||||
.into_iter()
|
||||
.map(|r| (r.comp_val, r.comp_ptr.cast::<()>()))
|
||||
.collect::<Vec<_>>();
|
||||
let (values, ptrs) =
|
||||
itertools::multiunzip::<(Vec<mlua::Value>, Vec<NonNull<()>>), _>(r);
|
||||
let mult_val = mlua::MultiValue::from_iter(values.into_iter());
|
||||
Ok(Some((row.entity, mult_val)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::FromLua for ViewResult {
|
||||
fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
|
||||
let tyname = value.type_name();
|
||||
value.as_userdata()
|
||||
.ok_or(mlua::Error::FromLuaConversionError {
|
||||
from: tyname,
|
||||
to: "View".into(),
|
||||
message: None,
|
||||
})
|
||||
.and_then(|ud| ud.borrow::<Self>())
|
||||
.map(|ud| ud.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::UserData for ViewResult {
|
||||
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method_mut("next", |lua, this, ()| {
|
||||
match this.next(lua)? {
|
||||
Some((_, val)) => Ok(val),
|
||||
None => mlua::Value::Nil.into_lua_multi(lua)
|
||||
}
|
||||
});
|
||||
|
||||
methods.add_method("iter", |lua, this, ()| {
|
||||
let key_arc = Arc::new(atomic_refcell::AtomicRefCell::new(Some(lua.create_registry_value(this.clone())?)));
|
||||
|
||||
lua.create_function(move |lua, ()| {
|
||||
let mut key_mut = key_arc.borrow_mut();
|
||||
|
||||
if let Some(key) = key_mut.as_ref() {
|
||||
let mut this = lua.registry_value::<Self>(&key)?;
|
||||
let v = this.next(lua)?;
|
||||
|
||||
match v {
|
||||
Some((en, mut vals)) => {
|
||||
let lua_en = LuaEntityRef::new(this.world, en)
|
||||
.into_lua(lua)?;
|
||||
vals.push_front(lua_en);
|
||||
Ok(vals)
|
||||
},
|
||||
None => {
|
||||
// If this is the last row, remove the registry value
|
||||
// This doesn't protect against iterators that aren't fully consumed,
|
||||
// that would cause a leak in the lua registry.
|
||||
// TODO: fix leak
|
||||
let key = key_mut.take().unwrap();
|
||||
lua.remove_registry_value(key)?;
|
||||
|
||||
mlua::Value::Nil.into_lua_multi(lua)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mlua::Value::Nil.into_lua_multi(lua)
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
|
@ -10,10 +10,7 @@ use lyra_resource::ResourceManager;
|
|||
use mlua::{IntoLua, ObjectLike};
|
||||
|
||||
use super::{
|
||||
reflect_user_data,
|
||||
wrappers::{LuaResHandleToComponent, LuaWrappedEventProxy},
|
||||
Error, ReflectLuaProxy, ReflectedIterator, TypeLookup, FN_NAME_INTERNAL_AS_COMPONENT,
|
||||
FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE,
|
||||
reflect_user_data, wrappers::{LuaResHandleToComponent, LuaWrappedEventProxy}, Error, ReflectLuaProxy, ReflectedIterator, TypeLookup, View, ViewResult, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE
|
||||
};
|
||||
|
||||
impl mlua::FromLua for ScriptEntity {
|
||||
|
@ -167,7 +164,6 @@ impl mlua::UserData for ScriptWorldPtr {
|
|||
// from the world.
|
||||
world: unsafe { NonNull::from(&*world).as_ref() },
|
||||
dyn_view: DynamicViewStateIter::from(iter),
|
||||
reflected_components: None,
|
||||
};
|
||||
|
||||
let current = world.current_tick();
|
||||
|
@ -353,5 +349,8 @@ impl mlua::UserData for ScriptWorldPtr {
|
|||
data.reader(&mut world).into_lua(lua)
|
||||
},
|
||||
);
|
||||
methods.add_method("view_query", |_, this, view: mlua::UserDataRef<View>| {
|
||||
ViewResult::new(this.clone(), &view)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue