ecs: improve docs
This commit is contained in:
parent
21537481c9
commit
7d90b1d38f
|
@ -19,8 +19,8 @@ impl Drop for ComponentColumn {
|
|||
|
||||
unsafe {
|
||||
// layout of current alloc
|
||||
let layout = Layout::from_size_align_unchecked(self.info.layout.size * self.capacity,
|
||||
self.info.layout.alignment);
|
||||
let layout = Layout::from_size_align_unchecked(self.info.layout.size() * self.capacity,
|
||||
self.info.layout.align());
|
||||
dealloc(data, layout);
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ impl ComponentColumn {
|
|||
}
|
||||
|
||||
pub unsafe fn new(info: ComponentInfo, capacity: usize) -> Self {
|
||||
let data = ComponentColumn::alloc(info.layout.into_layout().unwrap(), capacity);
|
||||
let data = ComponentColumn::alloc(info.layout, capacity);
|
||||
|
||||
Self {
|
||||
data: RefCell::new(data),
|
||||
|
@ -64,8 +64,9 @@ impl ComponentColumn {
|
|||
let mut data = self.data.borrow_mut();
|
||||
let data = data.deref_mut();
|
||||
|
||||
let dest = NonNull::new_unchecked(data.as_ptr().add(entity_index * self.info.layout.size));
|
||||
ptr::copy_nonoverlapping(comp_src.as_ptr(), dest.as_ptr(), self.info.layout.size);
|
||||
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);
|
||||
|
||||
// check if a component spot is being set twice and that the entity's tick is
|
||||
// already stored
|
||||
|
@ -86,7 +87,7 @@ impl ComponentColumn {
|
|||
|
||||
Ref::map(data, |data| {
|
||||
let ptr = NonNull::new_unchecked(data.as_ptr()
|
||||
.add(entity_index * self.info.layout.size))
|
||||
.add(entity_index * self.info.layout.size()))
|
||||
.cast();
|
||||
&*ptr.as_ptr()
|
||||
})
|
||||
|
@ -105,7 +106,7 @@ impl ComponentColumn {
|
|||
|
||||
let p = data.as_ptr()
|
||||
.cast::<T>()
|
||||
.add(entity_index * self.info.layout.size);
|
||||
.add(entity_index * self.info.layout.size());
|
||||
&mut *p
|
||||
}
|
||||
|
||||
|
@ -124,17 +125,17 @@ impl ComponentColumn {
|
|||
|
||||
let mut data = self.data.borrow_mut();
|
||||
|
||||
let mut new_ptr = Self::alloc(self.info.layout.into_layout().unwrap(), new_capacity);
|
||||
let mut new_ptr = Self::alloc(self.info.layout, new_capacity);
|
||||
|
||||
if self.len > 0 {
|
||||
ptr::copy_nonoverlapping(data.as_ptr(), new_ptr.as_ptr(), self.len * self.info.layout.size);
|
||||
ptr::copy_nonoverlapping(data.as_ptr(), new_ptr.as_ptr(), self.len * self.info.layout.size());
|
||||
}
|
||||
|
||||
// dont attempt to free if we weren't able to store anything anyway
|
||||
if self.capacity != 0 {
|
||||
let old_layout = Layout::from_size_align_unchecked(
|
||||
self.info.layout.size.checked_mul(self.capacity).unwrap(),
|
||||
self.info.layout.alignment
|
||||
self.info.layout.size().checked_mul(self.capacity).unwrap(),
|
||||
self.info.layout.align()
|
||||
);
|
||||
|
||||
mem::swap(data.deref_mut(), &mut new_ptr);
|
||||
|
@ -153,15 +154,16 @@ impl ComponentColumn {
|
|||
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 * self.info.layout.size));
|
||||
.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 * self.info.layout.size));
|
||||
.add(moved_index * size));
|
||||
|
||||
ptr::copy_nonoverlapping(new_comp_ptr.as_ptr(), old_comp_ptr.as_ptr(), self.info.layout.size);
|
||||
ptr::copy_nonoverlapping(new_comp_ptr.as_ptr(), old_comp_ptr.as_ptr(), size);
|
||||
|
||||
mem::swap(&mut old_comp_ptr, &mut new_comp_ptr); // new_comp_ptr is now the old ptr
|
||||
|
||||
|
@ -194,6 +196,7 @@ impl ComponentColumn {
|
|||
}
|
||||
}
|
||||
|
||||
/// An id of an Archetype
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
|
||||
pub struct ArchetypeId(pub u64);
|
||||
|
||||
|
@ -207,20 +210,41 @@ impl ArchetypeId {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Archetype {
|
||||
pub id: ArchetypeId,
|
||||
pub(crate) entities: HashMap<Entity, ArchetypeEntityId>,
|
||||
id: ArchetypeId,
|
||||
/// The indexes of the entities in the archetype.
|
||||
pub(crate) entity_ids: HashMap<Entity, ArchetypeEntityId>,
|
||||
/// map an Archetype entity id to an entity
|
||||
pub(crate) ids_to_entity: HashMap<ArchetypeEntityId, Entity>,
|
||||
//pub(crate) ids_to_entity: HashMap<ArchetypeEntityId, Entity>,
|
||||
/// The entities in the Archetype.
|
||||
///
|
||||
/// Can be used to map `ArchetypeEntityId` to an Entity since `ArchetypeEntityId` has
|
||||
/// the index that the entity is stored at.
|
||||
pub(crate) entities: Vec<Entity>,
|
||||
pub(crate) columns: Vec<ComponentColumn>,
|
||||
pub capacity: usize,
|
||||
capacity: usize,
|
||||
}
|
||||
|
||||
/// The default capacity of the columns
|
||||
const DEFAULT_CAPACITY: usize = 32;
|
||||
|
||||
impl Archetype {
|
||||
/// Returns the id of the Archetype
|
||||
pub fn id(&self) -> ArchetypeId {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// Returns the max amount of Entities that the Archetype can store without reallocating.
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.capacity
|
||||
}
|
||||
|
||||
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) }
|
||||
|
@ -228,8 +252,8 @@ impl Archetype {
|
|||
|
||||
Archetype {
|
||||
id: new_id,
|
||||
entities: HashMap::new(),
|
||||
ids_to_entity: HashMap::new(),
|
||||
entity_ids: HashMap::new(),
|
||||
entities: Vec::new(),
|
||||
columns,
|
||||
capacity: DEFAULT_CAPACITY,
|
||||
}
|
||||
|
@ -244,15 +268,16 @@ impl Archetype {
|
|||
where
|
||||
B: Bundle
|
||||
{
|
||||
if self.capacity == self.entities.len() {
|
||||
if self.capacity == self.entity_ids.len() {
|
||||
let new_cap = self.capacity * 2;
|
||||
self.grow_columns(new_cap);
|
||||
self.capacity = new_cap;
|
||||
}
|
||||
|
||||
let entity_index = ArchetypeEntityId(self.entities.len() as u64);
|
||||
self.entities.insert(entity, entity_index);
|
||||
self.ids_to_entity.insert(entity_index, entity);
|
||||
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);
|
||||
|
||||
bundle.take(|data, type_id, _size| {
|
||||
let col = self.get_column_mut(type_id).unwrap();
|
||||
|
@ -263,39 +288,41 @@ impl Archetype {
|
|||
entity_index
|
||||
}
|
||||
|
||||
/// Removes an entity from the Archetype and frees its components. Returns the entity record that took its place in the component column.
|
||||
/// Removes an entity from the Archetype and frees its components. Returns the entity record
|
||||
/// that took its place in the component column.
|
||||
pub(crate) fn remove_entity(&mut self, entity: Entity, tick: &Tick) -> Option<(Entity, ArchetypeEntityId)> {
|
||||
let entity_index = *self.entities.get(&entity)
|
||||
let entity_index = *self.entity_ids.get(&entity)
|
||||
.expect("The entity is not in this Archetype!");
|
||||
let mut removed_entity: Option<(Entity, ArchetypeEntityId)> = None;
|
||||
|
||||
for c in self.columns.iter_mut() {
|
||||
let moved_entity = unsafe { c.remove_component(entity_index.0 as usize, tick) };
|
||||
|
||||
// Make sure that the moved entity is the same as what was moved in other columns.
|
||||
// If this is the first move, find the EntityId that points to the column index.
|
||||
// If there wasn't a moved entity, make sure no other columns moved something.
|
||||
if let Some(res) = moved_entity {
|
||||
|
||||
if let Some((_, aid)) = removed_entity {
|
||||
assert!(res as u64 == aid.0); // make sure all columns removed the same entity
|
||||
// Make sure that the moved entity is the same as what was moved in other columns.
|
||||
assert!(res as u64 == aid.0);
|
||||
} else {
|
||||
let replaced_entity = self.entities.iter().find(|(_, a)| a.0 == res as u64)
|
||||
.map(|(e, _a)| *e).expect("Failure to find entity for moved component!");
|
||||
removed_entity = Some((replaced_entity, ArchetypeEntityId(res as u64)));
|
||||
// This is the first move, so find the EntityId that points to the column index.
|
||||
let just_removed = self.entities[res];
|
||||
removed_entity = Some((just_removed, ArchetypeEntityId(res as u64)));
|
||||
}
|
||||
} else {
|
||||
// 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.entities.remove(&entity).unwrap();
|
||||
self.ids_to_entity.remove(&entity_index).unwrap();
|
||||
self.entity_ids.remove(&entity).unwrap();
|
||||
if self.entities.len() > 1 {
|
||||
let len = self.entities.len();
|
||||
self.entities.swap(entity_index.0 as _, len - 1);
|
||||
}
|
||||
self.entities.pop().unwrap();
|
||||
|
||||
// now change the ArchetypeEntityId to be the index that the moved entity was moved into.
|
||||
removed_entity.map(|(e, _a)| (e, entity_index))
|
||||
|
||||
}
|
||||
|
||||
/// Returns a boolean indicating whether this archetype can store the TypeIds given
|
||||
|
@ -312,12 +339,12 @@ impl Archetype {
|
|||
|
||||
/// Returns a boolean indicating whether this archetype is empty or not.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.entities.is_empty()
|
||||
self.entity_ids.is_empty()
|
||||
}
|
||||
|
||||
/// Returns the amount of entities that are stored in the archetype.
|
||||
pub fn len(&self) -> usize {
|
||||
self.entities.len()
|
||||
self.entity_ids.len()
|
||||
}
|
||||
|
||||
/// Grows columns in the archetype
|
||||
|
@ -338,28 +365,32 @@ impl Archetype {
|
|||
self.capacity = new_capacity;
|
||||
}
|
||||
|
||||
pub fn get_column(&self, type_id: DynTypeId) -> Option<&ComponentColumn> {
|
||||
/// Attempts to find the column storing components of `type_id`
|
||||
pub fn get_column<I: Into<DynTypeId>>(&self, type_id: I) -> Option<&ComponentColumn> {
|
||||
let type_id = type_id.into();
|
||||
self.columns.iter().find(|c| c.info.type_id == type_id)
|
||||
}
|
||||
|
||||
/// 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(&mut self, type_id: DynTypeId) -> 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)
|
||||
}
|
||||
|
||||
/// Reserves a slot in the columns for an entity and returns the index of that reserved spot
|
||||
pub fn reserve_one(&mut self, entity: Entity) -> ArchetypeEntityId {
|
||||
if self.capacity == self.entities.len() {
|
||||
if self.capacity == self.entity_ids.len() {
|
||||
let new_cap = self.capacity * 2;
|
||||
self.grow_columns(new_cap);
|
||||
self.capacity = new_cap;
|
||||
}
|
||||
|
||||
let entity_index = ArchetypeEntityId(self.entities.len() as u64);
|
||||
self.entities.insert(entity, entity_index);
|
||||
self.ids_to_entity.insert(entity_index, entity);
|
||||
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);
|
||||
|
||||
for col in self.columns.iter_mut() {
|
||||
col.len += 1;
|
||||
|
@ -371,8 +402,8 @@ impl Archetype {
|
|||
/// 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`].
|
||||
/// It was done this way because I had some borrow check issues when writing [`World::insert`]
|
||||
/// 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)
|
||||
/// related to borrowing mutably from self more than once.
|
||||
/* pub fn move_into<B>(&self, into_arch: &mut Archetype, entity: Entity, new_components: B)
|
||||
where
|
||||
|
@ -405,16 +436,19 @@ impl Archetype {
|
|||
//self.remove_entity(entity);
|
||||
} */
|
||||
|
||||
pub fn entities(&self) -> &HashMap<Entity, ArchetypeEntityId> {
|
||||
&self.entities
|
||||
/// Returns a borrow to the map used to find the column indices of the entity.
|
||||
pub fn entity_indexes(&self) -> &HashMap<Entity, ArchetypeEntityId> {
|
||||
&self.entity_ids
|
||||
}
|
||||
|
||||
pub fn entity_of_index(&self, id: ArchetypeEntityId) -> Option<Entity> {
|
||||
self.ids_to_entity.get(&id).cloned()
|
||||
/// Returns the Entity that is stored at the column matching `id`.
|
||||
pub fn entity_at_index(&self, id: ArchetypeEntityId) -> Option<Entity> {
|
||||
self.entities.get(id.0 as usize).cloned()
|
||||
}
|
||||
|
||||
/// Returns a boolean indicating if the Archetype is storing the entity.
|
||||
pub fn has_entity(&self, e: Entity) -> bool {
|
||||
self.entities.contains_key(&e)
|
||||
self.entity_ids.contains_key(&e)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -424,7 +458,7 @@ mod tests {
|
|||
|
||||
use rand::Rng;
|
||||
|
||||
use crate::{bundle::Bundle, tests::{Vec2, Vec3}, ComponentInfo, DynTypeId, DynamicBundle, Entity, EntityId, MemoryLayout, Tick};
|
||||
use crate::{bundle::Bundle, tests::{Vec2, Vec3}, ComponentInfo, DynTypeId, DynamicBundle, Entity, EntityId, Tick};
|
||||
|
||||
use super::Archetype;
|
||||
|
||||
|
@ -604,7 +638,7 @@ mod tests {
|
|||
/// This test simulates an archetype that stores types that rust does not know about.
|
||||
#[test]
|
||||
fn dynamic_archetype() {
|
||||
let layout = MemoryLayout::from(Layout::new::<u32>());
|
||||
let layout = Layout::new::<u32>();
|
||||
let info = ComponentInfo::new_unknown(DynTypeId::Unknown(100), "u32", layout);
|
||||
let infos = vec![info.clone()];
|
||||
|
||||
|
|
|
@ -152,7 +152,7 @@ impl Bundle for DynamicBundle {
|
|||
|
||||
fn take(self, mut f: impl FnMut(NonNull<u8>, DynTypeId, usize)) {
|
||||
for (data, info) in self.bundle.iter() {
|
||||
f(*data, info.type_id, info.layout.size);
|
||||
f(*data, info.type_id, info.layout.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,30 +2,55 @@ use std::{any::Any, cell::RefMut, collections::VecDeque, ptr::{self, NonNull}};
|
|||
|
||||
use crate::{system::FnArgFetcher, Access, Bundle, Entities, Entity, World};
|
||||
|
||||
/// A Command be used to delay mutation of the world until after this system is ran.
|
||||
pub trait Command: Any {
|
||||
fn as_any_boxed(self: Box<Self>) -> Box<dyn Any>;
|
||||
|
||||
fn run(self, world: &mut World) -> anyhow::Result<()>;
|
||||
/// Executes the command
|
||||
fn run(self, world: &mut World);
|
||||
}
|
||||
|
||||
impl<F> Command for F
|
||||
where
|
||||
F: FnOnce(&mut World) -> anyhow::Result<()> + 'static
|
||||
F: FnOnce(&mut World) + 'static
|
||||
{
|
||||
fn as_any_boxed(self: Box<Self>) -> Box<dyn Any> {
|
||||
self
|
||||
}
|
||||
|
||||
fn run(self, world: &mut World) -> anyhow::Result<()> {
|
||||
fn run(self, world: &mut World) {
|
||||
self(world)
|
||||
}
|
||||
}
|
||||
|
||||
type RunCommand = unsafe fn(cmd: Box<dyn Command>, world: &mut World) -> anyhow::Result<()>;
|
||||
type RunCommand = unsafe fn(cmd: Box<dyn Command>, world: &mut World);
|
||||
|
||||
/// Stores a queue of commands that will get executed after the system is ran.
|
||||
///
|
||||
/// This struct can be inserted as a resource into the world, and the commands will be
|
||||
/// executed by the [`GraphExecutor`](crate::system::GraphExecutor) after the system is executed.
|
||||
#[derive(Default)]
|
||||
pub struct CommandQueue(VecDeque<(RunCommand, Box<dyn Command>)>);
|
||||
|
||||
/// Used in a system to queue up commands that will run right after this system.
|
||||
///
|
||||
/// This can be used to delay the mutation of the world until after the system is ran. These
|
||||
/// must be used if you're mutating the world inside a [`ViewState`](crate::query::ViewState).
|
||||
///
|
||||
/// ```nobuild
|
||||
/// fn particle_spawner_system(
|
||||
/// commands: Commands,
|
||||
/// view: ViewState<(&Campfire, &Transform)>
|
||||
/// ) -> anyhow::Result<()> {
|
||||
/// for (campfire, pos) in view.iter() {
|
||||
/// // If you do not use commands to spawn this, the next iteration
|
||||
/// // of the view will cause a segfault.
|
||||
/// commands.spawn((pos, Particle::new(/* ... */)));
|
||||
/// }
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Commands<'a, 'b> {
|
||||
queue: &'b mut CommandQueue,
|
||||
entities: &'a mut Entities,
|
||||
|
@ -48,20 +73,18 @@ impl<'a, 'b> Commands<'a, 'b> {
|
|||
.downcast::<C>()
|
||||
.unwrap();
|
||||
|
||||
cmd.run(world)?;
|
||||
|
||||
Ok(())
|
||||
cmd.run(world);
|
||||
};
|
||||
|
||||
self.queue.0.push_back((run_fn, cmd));
|
||||
}
|
||||
|
||||
/// Spawn an entity into the World. See [`World::spawn`]
|
||||
pub fn spawn<B: Bundle + 'static>(&mut self, bundle: B) -> Entity {
|
||||
let e = self.entities.reserve();
|
||||
|
||||
self.add(move |world: &mut World| {
|
||||
world.spawn_into(e, bundle);
|
||||
Ok(())
|
||||
});
|
||||
|
||||
e
|
||||
|
@ -71,7 +94,7 @@ impl<'a, 'b> Commands<'a, 'b> {
|
|||
pub fn execute(&mut self, world: &mut World) -> anyhow::Result<()> {
|
||||
while let Some((cmd_fn, cmd_ptr)) = self.queue.0.pop_front() {
|
||||
unsafe {
|
||||
cmd_fn(cmd_ptr, world)?;
|
||||
cmd_fn(cmd_ptr, world);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,41 +1,4 @@
|
|||
use std::{any::TypeId, alloc::{Layout, LayoutError}};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct MemoryLayout {
|
||||
pub size: usize,
|
||||
pub alignment: usize
|
||||
}
|
||||
|
||||
impl TryInto<Layout> for MemoryLayout {
|
||||
type Error = LayoutError;
|
||||
|
||||
fn try_into(self) -> Result<Layout, Self::Error> {
|
||||
Layout::from_size_align(self.size, self.alignment)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Layout> for MemoryLayout {
|
||||
fn from(value: Layout) -> Self {
|
||||
Self {
|
||||
size: value.size(),
|
||||
alignment: value.align(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MemoryLayout {
|
||||
pub fn new(size: usize, alignment: usize) -> Self {
|
||||
MemoryLayout {
|
||||
size,
|
||||
alignment
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts self into a Layout.
|
||||
pub fn into_layout(self) -> Result<Layout, LayoutError> {
|
||||
Layout::from_size_align(self.size, self.alignment)
|
||||
}
|
||||
}
|
||||
use std::{any::TypeId, alloc::Layout};
|
||||
|
||||
/// A dynamic type id. Supports types that are not known to Rust.
|
||||
#[derive(Clone, Copy, Hash, Debug, PartialEq, Eq)]
|
||||
|
@ -88,31 +51,29 @@ impl DynTypeId {
|
|||
}
|
||||
}
|
||||
|
||||
/// Some information about a component.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct ComponentInfo {
|
||||
pub type_id: DynTypeId,
|
||||
//pub name: String,
|
||||
pub layout: MemoryLayout,
|
||||
pub(crate) layout: Layout,
|
||||
}
|
||||
|
||||
impl ComponentInfo {
|
||||
pub fn new<T: 'static>() -> Self {
|
||||
Self {
|
||||
type_id: TypeId::of::<T>().into(),
|
||||
//name: type_name::<T>().to_string(),
|
||||
layout: MemoryLayout::from(Layout::new::<T>()),
|
||||
layout: Layout::new::<T>(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create ComponentInfo from a type that is not known to rust
|
||||
pub fn new_unknown<D>(type_id: D, name: &str, layout: MemoryLayout) -> Self
|
||||
pub fn new_unknown<D>(type_id: D, name: &str, layout: Layout) -> Self
|
||||
where
|
||||
D: Into<DynTypeId>,
|
||||
{
|
||||
let _ = name; // would be used at some point
|
||||
Self {
|
||||
type_id: type_id.into(),
|
||||
//name: name.to_string(),
|
||||
layout,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ where
|
|||
|
||||
FetchBorrow {
|
||||
col,
|
||||
size: col.info.layout.size,
|
||||
size: col.info.layout.size(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ where
|
|||
unsafe fn fetch<'a>(&self, _world: &'a World, archetype: &'a crate::archetype::Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
|
||||
let col = archetype.columns.iter().find(|c| c.info.type_id == self.type_id)
|
||||
.expect("You ignored 'can_visit_archetype'!");
|
||||
let layout_size = col.info.layout.size;
|
||||
let layout_size = col.info.layout.size();
|
||||
let col = NonNull::from(col);
|
||||
|
||||
// TODO: find a way to get the component column mutable with a borrowed archetype so its tick can be updated.
|
||||
|
|
|
@ -38,7 +38,7 @@ impl<'a> Fetch<'a> for FetchDynamicType<'a> {
|
|||
unsafe fn get_item(&mut self, entity: crate::ArchetypeEntityId) -> Self::Item {
|
||||
let ptr = self.col.borrow_ptr();
|
||||
let ptr = NonNull::new_unchecked(ptr.as_ptr()
|
||||
.add(entity.0 as usize * self.info.layout.size));
|
||||
.add(entity.0 as usize * self.info.layout.size()));
|
||||
|
||||
DynamicType {
|
||||
info: self.info,
|
||||
|
|
|
@ -83,7 +83,7 @@ impl<'a> Iterator for DynamicViewIter<'a> {
|
|||
self.next_archetype += 1;
|
||||
let arch = unsafe { self.archetypes.get_unchecked(arch_id) };
|
||||
|
||||
if arch.entities.is_empty() {
|
||||
if arch.entity_ids.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -94,7 +94,7 @@ impl<'a> Iterator for DynamicViewIter<'a> {
|
|||
self.fetchers = self.queries.iter()
|
||||
.map(|q| unsafe { q.fetch(self.world, ArchetypeId(arch_id as u64), arch) } )
|
||||
.collect();
|
||||
self.component_indices = 0..arch.entities.len() as u64;
|
||||
self.component_indices = 0..arch.entity_ids.len() as u64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -104,13 +104,13 @@ impl<'a> Iterator for DynamicViewIter<'a> {
|
|||
mod tests {
|
||||
use std::{alloc::Layout, ptr::NonNull};
|
||||
|
||||
use crate::{world::World, MemoryLayout, ComponentInfo, DynTypeId, DynamicBundle, query::dynamic::QueryDynamicType};
|
||||
use crate::{world::World, ComponentInfo, DynTypeId, DynamicBundle, query::dynamic::QueryDynamicType};
|
||||
|
||||
use super::DynamicView;
|
||||
|
||||
#[test]
|
||||
fn single_dynamic_view() {
|
||||
let comp_layout = MemoryLayout::from(Layout::new::<u32>());
|
||||
let comp_layout = Layout::new::<u32>();
|
||||
let comp_info = ComponentInfo::new_unknown(DynTypeId::Unknown(100), "u32", comp_layout);
|
||||
|
||||
let mut dynamic_bundle = DynamicBundle::default();
|
||||
|
|
|
@ -41,7 +41,7 @@ impl Query for Entities {
|
|||
unsafe fn fetch<'a>(&self, _world: &'a World, archetype: &'a Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
|
||||
let _ = tick; // ignore unused warnings
|
||||
EntitiesFetch {
|
||||
entities: archetype.entities.keys().cloned().collect::<Vec<Entity>>(),
|
||||
entities: archetype.entity_ids.keys().cloned().collect::<Vec<Entity>>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ impl<'a> Fetch<'a> for () {
|
|||
unreachable!()
|
||||
}
|
||||
|
||||
unsafe fn get_item(&mut self, entity: ArchetypeEntityId) -> Self::Item {
|
||||
unsafe fn get_item(&mut self, _: ArchetypeEntityId) -> Self::Item {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
@ -104,15 +104,17 @@ impl Query for () {
|
|||
|
||||
type Fetch<'a> = ();
|
||||
|
||||
const ALWAYS_FETCHES: bool = true;
|
||||
|
||||
fn new() -> Self {
|
||||
()
|
||||
}
|
||||
|
||||
fn can_visit_archetype(&self, archetype: &Archetype) -> bool {
|
||||
fn can_visit_archetype(&self, _: &Archetype) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
unsafe fn fetch<'a>(&self, world: &'a World, archetype: &'a Archetype, tick: Tick) -> Self::Fetch<'a> {
|
||||
unsafe fn fetch<'a>(&self, _: &'a World, _: &'a Archetype, _: Tick) -> Self::Fetch<'a> {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -192,9 +192,7 @@ impl<'a, T: ResourceObject> AsQuery for ResMut<'a, T> {
|
|||
mod tests {
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use crate::{world::World, tests::{Vec2, Vec3}, query::QueryResourceMut};
|
||||
|
||||
use super::QueryResource;
|
||||
use crate::{query::{Res, ResMut}, tests::{Vec2, Vec3}, world::World};
|
||||
|
||||
struct SomeCounter(u32);
|
||||
|
||||
|
@ -207,7 +205,7 @@ mod tests {
|
|||
println!("Added resource");
|
||||
}
|
||||
|
||||
let mut res_iter = world.view_iter::<QueryResource<SomeCounter>>();
|
||||
let mut res_iter = world.view_iter::<Res<SomeCounter>>();
|
||||
let res = res_iter.next().unwrap();
|
||||
let res = res.deref();
|
||||
assert_eq!(res.0, 0);
|
||||
|
@ -228,16 +226,16 @@ mod tests {
|
|||
println!("Added resource");
|
||||
}
|
||||
|
||||
let i = world.view_iter::<(QueryResource<SomeCounter>, &Vec2)>();
|
||||
let i = world.view_iter::<(Res<SomeCounter>, &Vec2)>();
|
||||
assert_eq!(i.count(), 3);
|
||||
let i = world.view_iter::<(&Vec2, QueryResource<SomeCounter>)>();
|
||||
let i = world.view_iter::<(&Vec2, Res<SomeCounter>)>();
|
||||
assert_eq!(i.count(), 3);
|
||||
|
||||
for (res, e) in world.view_iter::<(QueryResource<SomeCounter>, &Vec2)>() {
|
||||
for (res, e) in world.view_iter::<(Res<SomeCounter>, &Vec2)>() {
|
||||
println!("Got res {}! and entity at {:?}", res.deref().0, e);
|
||||
}
|
||||
|
||||
let i = world.view_iter::<QueryResource<SomeCounter>>();
|
||||
let i = world.view_iter::<Res<SomeCounter>>();
|
||||
assert_eq!(i.count(), 1);
|
||||
}
|
||||
|
||||
|
@ -251,14 +249,14 @@ mod tests {
|
|||
}
|
||||
|
||||
{
|
||||
let mut resmut_iter = world.view_iter::<QueryResourceMut<SomeCounter>>();
|
||||
let mut resmut_iter = world.view_iter::<ResMut<SomeCounter>>();
|
||||
let mut resmut = resmut_iter.next().unwrap();
|
||||
let resmut = resmut.deref_mut();
|
||||
assert_eq!(resmut.0, 0);
|
||||
resmut.0 += 20;
|
||||
}
|
||||
|
||||
let mut res_iter = world.view_iter::<QueryResource<SomeCounter>>();
|
||||
let mut res_iter = world.view_iter::<Res<SomeCounter>>();
|
||||
let res = res_iter.next().unwrap();
|
||||
let res = res.deref();
|
||||
assert_eq!(res.0, 20);
|
||||
|
|
|
@ -122,7 +122,7 @@ where
|
|||
self.next_archetype += 1;
|
||||
let arch = unsafe { self.archetypes.get_unchecked(arch_id) };
|
||||
|
||||
if arch.entities.is_empty() {
|
||||
if arch.entity_ids.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,7 @@ where
|
|||
self.fetcher = Some(self.query.fetch(self.world, arch, self.tick));
|
||||
self.filter_fetcher = Some(self.filter.fetch(self.world, arch, self.tick));
|
||||
}
|
||||
self.component_indices = 0..arch.entities.len() as u64;
|
||||
self.component_indices = 0..arch.entity_ids.len() as u64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ mod relate_pair;
|
|||
#[doc(hidden)]
|
||||
pub use relate_pair::*;
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub trait Relation: 'static {
|
||||
/// called when a relation of this type is set on a target
|
||||
fn relation_add(&self, origin: Entity, target: Entity) { }
|
||||
|
@ -40,6 +41,7 @@ pub struct RelationOriginComponent<R: Relation> {
|
|||
/// origin of the relation -- the entity that targets this entity.
|
||||
#[derive(Component)]
|
||||
pub struct RelationTargetComponent<R: Relation> {
|
||||
#[allow(dead_code)]
|
||||
origin: Entity,
|
||||
_marker: PhantomData<R>,
|
||||
}
|
||||
|
@ -47,7 +49,7 @@ pub struct RelationTargetComponent<R: Relation> {
|
|||
impl World {
|
||||
/// Creates a relation between two entities.
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// ```nobuild
|
||||
/// struct ChildOf;
|
||||
///
|
||||
/// impl Relation for ChildOf { /* snip */ };
|
||||
|
|
|
@ -23,7 +23,7 @@ where
|
|||
unsafe fn get_item(&mut self, entity: crate::world::ArchetypeEntityId) -> Self::Item {
|
||||
let comp: Ref<RelationOriginComponent<R>> = self.col.get(entity.0 as usize);
|
||||
let rel_target = comp.target;
|
||||
let rel_origin = self.arch.entity_of_index(entity).unwrap();
|
||||
let rel_origin = self.arch.entity_at_index(entity).unwrap();
|
||||
|
||||
let comp = Ref::map(comp, |r| &r.relation);
|
||||
(rel_origin, comp, rel_target)
|
||||
|
|
|
@ -93,7 +93,7 @@ where
|
|||
/// This can be combined with Entities, to query all entities that have a relation targeting
|
||||
/// the target entity.
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// ```nobuild
|
||||
/// let v = world.view::<Entities>()
|
||||
/// .relates_to::<ChildOf>(b);
|
||||
///
|
||||
|
|
|
@ -18,7 +18,7 @@ pub enum CriteriaSchedule {
|
|||
NoAndLoop,
|
||||
}
|
||||
|
||||
/// A Criteria can be used to conditionally execute [`BatchedSystems`](super::BatchedSystems).
|
||||
/// A Criteria can be used to conditionally execute [`BatchedSystem`](super::BatchedSystem).
|
||||
pub trait Criteria {
|
||||
/// Checks if this Criteria can run, and if it should check it again.
|
||||
///
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
/// TickTracker is used for tracking changes of [`Component`]s and entities.
|
||||
/// TickTracker is used for tracking changes of [`Component`](crate::Component)s and entities.
|
||||
///
|
||||
/// TickTracker stores an [`AtomicU64`], making all operations on `TickTracker`, atomic as well.
|
||||
/// Note that [`Tick::Clone`] only clones the inner value of atomic, and not the atomic itself.
|
||||
/// Note that [`Tick::clone`] only clones the inner value of atomic, and not the atomic itself.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TickTracker {
|
||||
tick: AtomicU64,
|
||||
|
|
|
@ -54,7 +54,7 @@ impl World {
|
|||
}
|
||||
|
||||
/// Spawn the components into a reserved entity. Only do this with entities that
|
||||
/// were 'reserved' with [`World::reserve`]
|
||||
/// were reserved with [`World::reserve_entity`].
|
||||
///
|
||||
/// # Safety
|
||||
/// Do not use this method with an entity that is currently alive, it WILL cause undefined behavior.
|
||||
|
@ -73,14 +73,14 @@ impl World {
|
|||
|
||||
if let Some(archetype) = archetype {
|
||||
// make at just one check to ensure you're not spawning twice
|
||||
debug_assert!(!archetype.entities.contains_key(&entity),
|
||||
debug_assert!(!archetype.entity_ids.contains_key(&entity),
|
||||
"You attempted to spawn components into an entity that already exists!");
|
||||
|
||||
let arche_idx = archetype.add_entity(entity, bundle, &tick);
|
||||
|
||||
// Create entity record and store it
|
||||
let record = Record {
|
||||
id: archetype.id,
|
||||
id: archetype.id(),
|
||||
index: arche_idx,
|
||||
};
|
||||
|
||||
|
@ -158,7 +158,7 @@ impl World {
|
|||
for (col_type, (col_ptr, col_info)) in orig_col.into_iter().zip(col_ptrs.into_iter()) {
|
||||
unsafe {
|
||||
let ptr = NonNull::new_unchecked(col_ptr.as_ptr()
|
||||
.add(res_index.0 as usize * col_info.layout.size));
|
||||
.add(res_index.0 as usize * col_info.layout.size()));
|
||||
let col = arch.get_column_mut(col_type).unwrap();
|
||||
col.set_at(res_index.0 as _, ptr, tick);
|
||||
}
|
||||
|
@ -170,10 +170,10 @@ impl World {
|
|||
col.len += 1;
|
||||
});
|
||||
|
||||
arch.entities.insert(entity, res_index);
|
||||
arch.entity_ids.insert(entity, res_index);
|
||||
|
||||
let new_record = Record {
|
||||
id: arch.id,
|
||||
id: arch.id(),
|
||||
index: res_index,
|
||||
};
|
||||
self.entities.insert_entity_record(entity, new_record);
|
||||
|
@ -264,7 +264,7 @@ impl World {
|
|||
|
||||
/// Gets a resource from the World.
|
||||
///
|
||||
/// Will panic if the resource is not in the world. See [`try_get_resource`] for
|
||||
/// Will panic if the resource is not in the world. See [`World::try_get_resource`] for
|
||||
/// a function that returns an option.
|
||||
pub fn get_resource<T: 'static>(&self) -> Ref<T> {
|
||||
self.resources.get(&TypeId::of::<T>())
|
||||
|
@ -287,7 +287,7 @@ impl World {
|
|||
|
||||
/// Gets a mutable borrow of a resource from the World.
|
||||
///
|
||||
/// Will panic if the resource is not in the world. See [`try_get_resource_mut`] for
|
||||
/// Will panic if the resource is not in the world. See [`World::try_get_resource_mut`] for
|
||||
/// a function that returns an option.
|
||||
pub fn get_resource_mut<T: 'static>(&self) -> RefMut<T> {
|
||||
self.resources.get(&TypeId::of::<T>())
|
||||
|
|
Loading…
Reference in New Issue