Compare commits

...

7 Commits

  1. 323
      src/ecs/archegraph.rs
  2. 112
      src/ecs/archetable.rs
  3. 20
      src/ecs/archetype.rs
  4. 54
      src/ecs/component.rs
  5. 7
      src/ecs/entity.rs
  6. 4
      src/ecs/entity_index.rs
  7. 1
      src/ecs/mod.rs
  8. 9
      src/ecs/relation.rs
  9. 102
      src/ecs/world.rs

@ -0,0 +1,323 @@
use std::alloc::Layout;
use std::collections::{HashMap, HashSet};
use super::archetable::{Archetable, EntityRow};
use super::archetype::Archetype;
use super::component::Component;
use super::entity::Entity;
use super::world::World;
/// Graph of [`Archetable`]s contained within [`Node`]s.
///
/// Allows for traversal by of `Archetables` by their [`Archetype`], one
/// [`Component`] at a time. For example to get all [`entities`][`Entity`] with
/// both `Position` and `Velocity` you would start at `root()`, then traverse
/// to `Position`, then `Velocity`, or the other way around, since you end up
/// at the same `Node` either way. You can then `iter()` all `Archetables` that
/// have those same components.
pub struct Archegraph {
node_count: usize,
node_storage: Vec<Option<NodeData>>,
archetable_storage: Vec<Option<Archetable>>,
// unused_indices: Vec<NodeIndex>,
type_lookup: HashMap<Archetype, NodeIndex>,
}
pub type GraphEntityIndex = super::entity_index::EntityIndex<(NodeIndex, EntityRow)>;
type NodeIndex = usize;
pub struct Node<'a> {
archegraph: &'a Archegraph,
node_index: NodeIndex,
}
pub struct NodeMut<'a> {
archegraph: &'a mut Archegraph,
node_index: NodeIndex,
}
#[derive(Default)]
struct NodeData {
add: HashMap<Component, NodeIndex>,
remove: HashMap<Component, NodeIndex>,
}
impl Archegraph {
pub fn new() -> Self {
// Empty archetable is added in `World::bootstrap_archetable`.
Self {
node_count: 0,
node_storage: Vec::new(),
archetable_storage: Vec::new(),
// unused_indices: Vec::new(),
type_lookup: HashMap::new(),
}
}
pub fn bootstrap(&mut self, pairs: Vec<(Entity, Layout)>) {
// Inner function is just so call to `bootstrap` is prettier.
fn inner(graph: &mut Archegraph, pairs: Vec<(Entity, Layout)>, mut start_index: usize) {
// Iterate all the subsets of the pairs and initialize them, first.
// The `start_index` should ensure that no subset is bootstrapped twice.
for i in start_index..pairs.len() {
let mut pairs_subset = pairs.clone();
pairs_subset.remove(i);
inner(graph, pairs_subset, start_index);
start_index += 1;
}
let layouts = pairs.iter().map(|p| Some(p.1)).collect();
let archetype = Archetype::new(pairs.iter().map(|p| p.0));
let archetable = Archetable::new(archetype, layouts);
// No entity lookups necessary during bootstrapping.
graph.insert(archetable, &GraphEntityIndex::EMPTY);
}
inner(self, pairs, 0);
}
#[inline]
#[must_use]
pub fn node_count(&self) -> usize {
self.node_count
}
#[must_use]
pub fn root(&self) -> &NodeData {
self.node_storage[0].as_ref().unwrap()
}
#[must_use]
pub fn root_mut(&mut self) -> &mut NodeData {
self.node_storage[0].as_mut().unwrap()
}
#[must_use]
pub fn get(&self, index: NodeIndex) -> Option<&NodeData> {
self.node_storage.get(index).and_then(Option::as_ref)
}
#[must_use]
pub fn get_mut(&mut self, index: NodeIndex) -> Option<&mut NodeData> {
self.node_storage.get_mut(index).and_then(Option::as_mut)
}
#[must_use]
pub fn lookup_or_create(
&mut self,
archetype: &Archetype,
entity_index: &GraphEntityIndex,
) -> NodeMut {
if let Some(index) = self.type_lookup.get(&archetype) {
// SAFETY: `type_lookup` is guaranteed to point to a valid archetable.
self.node_storage[*index].as_mut().unwrap()
} else {
let layouts = self.get_layouts(archetype, entity_index);
let archetable = Archetable::new(archetype.clone(), layouts);
self.insert(archetable, entity_index)
}
}
pub fn insert(&mut self, archetable: Archetable, entity_index: &GraphEntityIndex) -> NodeMut {
let archetype = archetable.archetype().clone();
let index = self.reserve_archetable_index();
let mut node = Default::default();
self.fill_edges(&mut node, entity_index);
self.node_storage[index] = Some(node);
self.archetable_storage[index] = Some(archetable);
self.type_lookup.insert(archetype, index);
()
}
fn get_layout(&self, component: Component, entity_index: &GraphEntityIndex) -> Option<Layout> {
// FIXME: Relations can be components too.
let entity: Entity = component.try_into().ok()?;
let (node_index, row) = entity_index.get(entity.id())?.data;
let archetable = self.get(node_index).unwrap().get();
let component_index = archetable.archetype().position(World::LAYOUT)?;
let column = archetable.column_by_index(component_index).unwrap();
let column = unsafe { column.as_typed_slice::<Layout>() };
Some(column[row])
}
fn get_layouts(
&self,
archetype: &Archetype,
entity_index: &GraphEntityIndex,
) -> Vec<Option<Layout>> {
let iter = archetype.components().iter();
iter.map(|c| self.get_layout(*c, entity_index)).collect()
}
#[must_use]
fn reserve_archetable_index(&mut self) -> usize {
self.node_count += 1;
// self.unused_indices.pop().unwrap_or_else(|| {
self.node_storage.push(None);
self.node_storage.len() - 1
// })
}
fn fill_edges(&mut self, node: &mut NodeData, entity_index: &GraphEntityIndex) {
let archetype = node.archetable.archetype();
for component in archetype.components() {
let other_archetype = archetype.clone().without(*component);
let other_node = self.lookup_or_create(&other_archetype, entity_index);
node.remove.insert(*component, other_node.index);
other_node.add.insert(*component, node.index);
}
}
}
impl<'a> Node<'a> {
#[must_use]
pub fn iter(&'a self) -> impl Iterator<Item = &Archetable> {
let storage = &self.archegraph.archetable_storage;
GraphIter::new(&self.archegraph.node_storage, self.node_index)
.map(|index| storage[index].as_ref().unwrap())
}
#[must_use]
pub fn get(&self) -> &Archetable {
let storage = &self.archegraph.archetable_storage;
storage[self.node_index].as_ref().unwrap()
}
}
impl<'a> NodeMut<'a> {
#[must_use]
pub fn iter(&'a mut self) -> impl Iterator<Item = &'a mut Archetable> {
let node_storage = &self.archegraph.node_storage;
let archetable_storage = &mut self.archegraph.archetable_storage;
GraphIter::new(node_storage, self.node_index)
.map(|index| archetable_storage[index].as_mut().unwrap())
}
#[must_use]
pub fn get(&mut self) -> &mut Archetable {
let storage = &mut self.archegraph.archetable_storage;
storage[self.node_index].as_mut().unwrap()
}
}
struct GraphIter<'a> {
node_storage: &'a Vec<Option<NodeData>>,
stack: Vec<NodeIndex>,
visited: HashSet<NodeIndex>,
}
impl GraphIter<'_> {
fn new(node_storage: &Vec<Option<NodeData>>, start_index: NodeIndex) -> GraphIter {
GraphIter {
node_storage,
stack: vec![start_index],
visited: HashSet::new(),
}
}
}
impl<'a> Iterator for GraphIter<'a> {
type Item = NodeIndex;
fn next(&mut self) -> Option<Self::Item> {
while let Some(index) = self.stack.pop() {
if !self.visited.contains(&index) {
let node = self.archegraph.get(index).unwrap();
self.stack.extend(node.add.values());
self.visited.insert(index);
return Some(index);
}
}
None
}
}
impl NodeData {
#[must_use]
fn new(archetable: Archetable) -> Self {
Self {
archetable,
add: HashMap::new(),
remove: HashMap::new(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bootstrap() {
let mut archegraph = Archegraph::new();
let a = Entity::new_checked(1, 0).unwrap();
let b = Entity::new_checked(2, 0).unwrap();
let c = Entity::new_checked(10, 0).unwrap();
let d = Entity::new_checked(2000, 0).unwrap();
archegraph.bootstrap(vec![
(a, Layout::new::<u8>()),
(b, Layout::new::<u16>()),
(c, Layout::new::<u32>()),
(d, Layout::new::<u64>()),
]);
// {a, b, c, d} has 16 subsets including {} (the empty root archetype)
// and {a, b, c, d} (archetype containing all components at the same time).
assert_eq!(16, archegraph.node_count());
let mut archetypes = archegraph
.root()
.iter(&archegraph)
.map(Archetable::archetype)
.map(Archetype::components)
.collect::<Vec<_>>();
// Not guaranteed to iter in any specific order.
archetypes.sort();
assert_eq!(
vec![
vec![],
vec![a],
vec![a, b],
vec![a, b, c],
vec![a, b, c, d],
vec![a, b, d],
vec![a, c],
vec![a, c, d],
vec![a, d],
vec![b],
vec![b, c],
vec![b, c, d],
vec![b, d],
vec![c],
vec![c, d],
vec![d],
],
archetypes
);
// Ensure the columns' layouts are correct for a given Archetype.
let archetype = Archetype::new([a, c, d]);
let index = &GraphEntityIndex::EMPTY;
let table = &archegraph.lookup_or_create(&archetype, index).get();
assert_eq!(
Layout::new::<u8>(),
table.column_by_index(0).unwrap().layout()
);
assert_eq!(
Layout::new::<u32>(),
table.column_by_index(1).unwrap().layout()
);
assert_eq!(
Layout::new::<u64>(),
table.column_by_index(2).unwrap().layout()
);
}
}

@ -1,67 +1,59 @@
use std::any::TypeId;
use std::alloc::Layout;
use super::archetype::Archetype;
use super::component::Component;
use super::entity::Entity;
use super::world::World;
use super::utility::RawComponentVec;
/// Table data structure that holds [`Entities`](`Entity`) with a
/// specific [`Archetype`] and their [`Component`] values, if any.
///
/// Each row in this table refers to an alive `Entity`, which may only be in
/// one `Archetable` at a time, per [`World`]. There is also a column for each
/// "value component" – a component that holds a value. Components that don't
/// hold a value, known as tags, don't have an associated column.
///
/// [`World`]: super::world::World
pub struct Archetable {
archetype: Archetype,
components: Vec<ComponentInfo>,
columns: Vec<Column>,
components: Vec<ComponentData>,
columns: Vec<ColumnData>,
entities: Vec<Entity>,
}
/// Represents an index into the `entities` vector of an `Archetable`.
pub type EntityRow = usize;
struct ComponentInfo {
entity: Entity,
type_id: Option<TypeId>,
column_index: Option<usize>,
/// Represents an index into the `columns` vector of an `Archetable`.
pub type ValueComponentColumn = usize;
pub struct ComponentData {
column_index: Option<ValueComponentColumn>,
}
struct Column {
struct ColumnData {
data: RawComponentVec,
component_index: usize,
// component_index: usize,
}
impl Archetable {
#[must_use]
pub fn new_empty() -> Self {
Self {
archetype: Archetype::EMPTY,
components: Vec::new(),
columns: Vec::new(),
entities: Vec::new(),
}
}
pub fn new(archetype: Archetype, layouts: Vec<Option<Layout>>) -> Self {
assert_eq!(archetype.components().len(), layouts.len());
#[must_use]
pub fn new(world: &World, archetype: Archetype) -> Self {
let mut components = Vec::new();
let mut columns = Vec::new();
for component in archetype.components() {
let entity = (*component).try_into().unwrap();
let (layout, type_id) = world.lookup_component_info(entity).unzip();
// If the entity has `Layout`, add a column for it.
let column_index = if let Some(layout) = layout {
columns.push(Column {
for layout in layouts {
// If component has `Layout`, add a column for it.
let column_index = layout.map(|layout| {
columns.push(ColumnData {
data: RawComponentVec::new(layout),
component_index: components.len(),
// component_index: components.len(),
});
Some(columns.len() - 1)
} else {
None
};
components.push(ComponentInfo {
entity,
column_index,
type_id,
columns.len() - 1
});
components.push(ComponentData { column_index });
}
Self {
@ -72,7 +64,13 @@ impl Archetable {
}
}
pub fn push(&mut self, entity: Entity) -> EntityRow {
#[inline]
#[must_use]
pub fn archetype(&self) -> &Archetype {
&self.archetype
}
pub(crate) fn insert(&mut self, entity: Entity) -> EntityRow {
self.entities.push(entity);
let new_len = self.entities.len();
for column in self.columns.iter_mut() {
@ -84,7 +82,7 @@ impl Archetable {
/// Removes an entity from the specified `row`, including its component values.
/// Potentially swaps the last alive entity into the vacant spot, then shrinks the `entities` vector.
/// If a swap occurred, the entity swapped into `row` is returned, so references to it can be updated.
pub fn swap_remove(&mut self, row: EntityRow) -> Option<Entity> {
pub(crate) fn swap_remove(&mut self, row: EntityRow) -> Option<Entity> {
self.entities.swap_remove(row);
for column in self.columns.iter_mut() {
column.data.swap_remove(row);
@ -92,23 +90,33 @@ impl Archetable {
self.entities.get(row).copied()
}
fn find_component_info<T: 'static>(&self) -> Option<&ComponentInfo> {
let type_id = Some(TypeId::of::<T>());
self.components.iter().find(|e| e.type_id == type_id)
#[must_use]
pub fn column_by_index(&self, index: ValueComponentColumn) -> Option<&RawComponentVec> {
Some(&self.columns.get(index)?.data)
}
#[must_use]
pub fn column_by_index_mut(
&mut self,
index: ValueComponentColumn,
) -> Option<&mut RawComponentVec> {
Some(&mut self.columns.get_mut(index)?.data)
}
#[must_use]
pub fn get_component_column<T: 'static>(&self) -> Option<&[T]> {
let info = self.find_component_info::<T>()?;
let column = &self.columns[info.column_index?];
Some(unsafe { column.data.as_typed_slice::<T>() })
pub fn column_by_component(&self, component: impl Into<Component>) -> Option<&RawComponentVec> {
let component_index = self.archetype.position(component.into())?;
let column_index = self.components[component_index].column_index?;
self.column_by_index(column_index)
}
#[must_use]
pub fn get_component_column_mut<T: 'static>(&mut self) -> Option<&mut [T]> {
let info = self.find_component_info::<T>()?;
let column_index = info.column_index?; // Avoid borrow.
let column = &mut self.columns[column_index];
Some(unsafe { column.data.as_typed_slice_mut::<T>() })
pub fn column_by_component_mut(
&mut self,
component: impl Into<Component>,
) -> Option<&mut RawComponentVec> {
let component_index = self.archetype.position(component.into())?;
let column_index = self.components[component_index].column_index?;
self.column_by_index_mut(column_index)
}
}

@ -1,9 +1,9 @@
use super::component::Component;
/// Represents a set of components an [`Entity`] may have.
/// Represents a sorted set of components an [`Entity`] may have.
///
/// [`Entity`]: super::entity::Entity
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct Archetype {
components: Vec<Component>,
}
@ -15,8 +15,8 @@ impl Archetype {
#[must_use]
pub fn new(iter: impl IntoIterator<Item = impl Into<Component>>) -> Self {
let iter = iter.into_iter().map(Into::into).collect();
let mut components: Vec<_> = iter;
let iter = iter.into_iter().map(Into::into);
let mut components: Vec<_> = iter.collect();
components.sort();
components.dedup();
Self { components }
@ -33,6 +33,11 @@ impl Archetype {
self.components.binary_search(&component.into()).is_ok()
}
#[must_use]
pub fn position(&self, component: impl Into<Component>) -> Option<usize> {
self.components.binary_search(&component.into()).ok()
}
pub fn add(&mut self, component: impl Into<Component>) {
let component = component.into();
if let Err(index) = self.components.binary_search(&component) {
@ -60,6 +65,12 @@ impl Archetype {
self.remove(component);
self
}
#[inline]
#[must_use]
pub fn iter(&self) -> std::slice::Iter<'_, Component> {
self.components.iter()
}
}
impl<T> FromIterator<T> for Archetype
@ -76,7 +87,6 @@ impl IntoIterator for Archetype {
type Item = Component;
type IntoIter = std::vec::IntoIter<Self::Item>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.components.into_iter()
}

@ -1,3 +1,5 @@
use std::num::NonZeroU32;
/// Components are plain identifiers which represent anything that can be
/// added to an [`Entity`], which includes other entities and [`Relation`]s.
///
@ -13,7 +15,7 @@
#[repr(C)]
#[derive(Copy, Clone, Eq)]
pub struct Component {
low: u32,
low: NonZeroU32,
high: u32,
}
@ -95,8 +97,25 @@ impl std::hash::Hash for Component {
}
}
impl std::fmt::Debug for Component {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.is_entity() {
use super::entity::Entity;
unsafe { Entity::from_bits_unchecked(self.to_bits()).fmt(f) }
} else if self.is_relation() {
use super::relation::Relation;
unsafe { Relation::from_bits_unchecked(self.to_bits()).fmt(f) }
} else {
f.debug_struct("Component")
.field("low", &self.low)
.field("high", &self.high)
.finish()
}
}
}
impl Flags {
// NOTE: When changing this be sure to update documentation below.
// NOTE: When changing this be sure to update documentation.
pub(crate) const MASK: u32 = 0xF0000000;
/// Extracts only the flags from the specified `u32`.
@ -148,7 +167,9 @@ mod tests {
let entity = Entity::new_checked(1337, 42).unwrap();
let component: Component = entity.into();
assert!(component.is_entity());
assert_eq!(entity, component.try_into().unwrap());
let back_to_entity: Entity = component.try_into().unwrap();
assert_eq!(entity, back_to_entity);
}
#[test]
@ -156,6 +177,31 @@ mod tests {
let relation = Relation::new_checked(20, 21).unwrap();
let component: Component = relation.into();
assert!(component.is_relation());
assert_eq!(relation, component.try_into().unwrap());
let back_to_relation: Relation = component.try_into().unwrap();
assert_eq!(relation, back_to_relation);
}
#[test]
fn debug() {
// Component, as long as it's a valid Entity or Relation, will format as such.
// In cases where it's neither (not a valid Component), it has a default formatting.
let entity: Component = Entity::new_checked(69, 1337).unwrap().into();
let relation: Component = Relation::new_checked(420, 9001).unwrap().into();
let invalid = unsafe { Component::from_bits_unchecked(0x20000000_00000001) };
assert_eq!(
"Entity { id: 69, gen: Generation(1337) }",
format!("{entity:?}")
);
assert_eq!(
"Relation { target: 9001, kind: 420 }",
format!("{relation:?}")
);
assert_eq!(
"Component { low: 1, high: 536870912 }",
format!("{invalid:?}")
);
}
}

@ -140,6 +140,13 @@ impl PartialEq for Entity {
}
}
impl PartialEq<Component> for Entity {
#[inline]
fn eq(&self, other: &Component) -> bool {
self.to_bits() == other.to_bits()
}
}
impl Ord for Entity {
#[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {

@ -58,8 +58,10 @@ pub struct EntityIndex<T: Copy + Default> {
}
impl<T: Copy + Default> EntityIndex<T> {
pub const EMPTY: EntityIndex<T> = Self::new();
#[must_use]
pub fn new() -> Self {
pub const fn new() -> Self {
Self {
dense: Vec::new(),
alive_count: 0,

@ -1,3 +1,4 @@
pub mod archegraph;
pub mod archetable;
pub mod archetype;
pub mod component;

@ -81,7 +81,7 @@ impl Relation {
let target = bits as u32;
let high = (bits >> 32) as u32;
if let (kind, Flags::RELATION) = Flags::unpack(high)? {
Self::new_checked(kind, target)
Self::new_checked(kind, target)
} else {
None
}
@ -122,6 +122,13 @@ impl PartialEq for Relation {
}
}
impl PartialEq<Component> for Relation {
#[inline]
fn eq(&self, other: &Component) -> bool {
self.to_bits() == other.to_bits()
}
}
impl Ord for Relation {
#[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {

@ -2,28 +2,34 @@ use std::alloc::Layout;
use std::any::TypeId;
use std::collections::HashMap;
use super::archetable::Archetable;
use super::archegraph::{Archegraph, GraphEntityIndex};
use super::archetype::Archetype;
use super::entity::{Entity, EntityId};
use super::entity_index::EntityIndex;
pub struct World {
entity_index: EntityIndex<usize>,
empty_archetable: Archetable,
type_id_lookup: HashMap<TypeId, Entity>,
// Temporary. To be removed once we can build and use Archetables that hold this information.
component_lookup: HashMap<Entity, (Layout, TypeId)>,
entity_index: GraphEntityIndex,
archegraph: Archegraph,
typeid_lookup: HashMap<TypeId, Entity>,
}
impl World {
pub const LAYOUT: Entity = unsafe { Entity::new_unchecked(1, 0) };
pub const TYPEID: Entity = unsafe { Entity::new_unchecked(2, 0) };
#[must_use]
pub fn new() -> Self {
let mut world = Self {
entity_index: EntityIndex::new(),
empty_archetable: Archetable::new_empty(),
type_id_lookup: HashMap::new(),
component_lookup: HashMap::new(),
archegraph: Archegraph::new(),
typeid_lookup: HashMap::new(),
};
world.archegraph.bootstrap(vec![
(World::LAYOUT, Layout::new::<Layout>()),
(World::TYPEID, Layout::new::<TypeId>()),
]);
world.register_component::<Layout>();
world.register_component::<TypeId>();
@ -37,34 +43,58 @@ impl World {
#[must_use]
pub fn lookup_by_type<T: 'static>(&self) -> Option<Entity> {
self.type_id_lookup.get(&TypeId::of::<T>()).copied()
}
#[must_use]
pub fn lookup_component_info(&self, entity: Entity) -> Option<(Layout, TypeId)> {
self.component_lookup.get(&entity).copied()
self.typeid_lookup.get(&TypeId::of::<T>()).copied()
}
pub fn register_component<T: 'static>(&mut self) -> Entity {
assert_ne!(0, size_of::<T>());
let entity = self.create();
let component_info = (Layout::new::<T>(), TypeId::of::<T>());
self.component_lookup.insert(entity, component_info);
let entity = if size_of::<T>() > 0 {
let archetype = Archetype::new([Self::LAYOUT, Self::TYPEID]);
let entity = self.create(&archetype);
entity
} else {
let archetype = Archetype::new([Self::TYPEID]);
let entity = self.create(&archetype);
entity
};
let (node_index, row) = self.entity_index.get(entity.id()).unwrap().data;
let archetable = self.archegraph.get_mut(node_index).unwrap().get_mut();
if size_of::<T>() > 0 {
let layout_column = archetable.column_by_component_mut(World::LAYOUT).unwrap();
let layout_column = unsafe { layout_column.as_typed_slice_mut::<Layout>() };
layout_column[row] = Layout::new::<T>();
}
let typeid = TypeId::of::<T>();
self.typeid_lookup.insert(typeid, entity);
let typeid_column = archetable.column_by_component_mut(World::TYPEID).unwrap();
let typeid_column = unsafe { typeid_column.as_typed_slice_mut::<TypeId>() };
typeid_column[row] = typeid;
entity
}
#[must_use]
pub fn create(&mut self) -> Entity {
pub fn create(&mut self, archetype: &Archetype) -> Entity {
let entities = &self.entity_index;
let graph_node = self.archegraph.lookup_or_create(archetype, entities);
let archetable = graph_node.get_mut();
let entry = self.entity_index.create();
let row = self.empty_archetable.push(entry.entity);
entry.update(row)
let row = archetable.insert(entry.entity);
entry.update((graph_node.index(), row))
}
pub fn destroy(&mut self, entity: Entity) -> bool {
if let Ok(row) = self.entity_index.destroy(entity) {
if let Some(swapped) = self.empty_archetable.swap_remove(row) {
if let Ok((node_index, row)) = self.entity_index.destroy(entity) {
let graph_node = self.archegraph.get_mut(node_index).unwrap();
let archetable = graph_node.get_mut();
if let Some(swapped) = archetable.swap_remove(row) {
// Assume that `swapped` we got from the archetype table is alive.
self.entity_index.get(swapped.id()).unwrap().data = row;
let mut entry = self.entity_index.get(swapped.id()).unwrap();
entry.data = (graph_node.index(), row);
}
true
} else {
@ -72,3 +102,23 @@ impl World {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn world_new_check() {
let world = World::new();
let (node_index, row) = world.entity_index.get(World::LAYOUT.id()).unwrap().data;
let archetable = world.archegraph.get(node_index).unwrap().get();
let layout_column = archetable.column_by_component(World::LAYOUT).unwrap();
let layout_column = unsafe { layout_column.as_typed_slice::<Layout>() };
assert_eq!(Layout::new::<Layout>(), layout_column[row]);
let typeid_column = archetable.column_by_component(World::TYPEID).unwrap();
let typeid_column = unsafe { typeid_column.as_typed_slice::<TypeId>() };
assert_eq!(TypeId::of::<Layout>(), typeid_column[row]);
}
}

Loading…
Cancel
Save