copygirl 2 months ago
parent 9b821f26eb
commit 574a2ee54c
  1. 27
      .vscode/launch.json
  2. 160
      src/ecs/archegraph.rs
  3. 63
      src/ecs/archetable.rs
  4. 67
      src/ecs/archetype.rs
  5. 2
      src/ecs/component.rs
  6. 1
      src/ecs/mod.rs
  7. 64
      src/ecs/world.rs

@ -0,0 +1,27 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug unit tests in library 'gaemstone'",
"cargo": {
"args": [
"test",
"--no-run",
"--lib",
"--package=gaemstone"
],
"filter": {
"name": "gaemstone",
"kind": "lib"
}
},
"args": [],
"cwd": "${workspaceFolder}"
}
]
}

@ -0,0 +1,160 @@
use std::alloc::Layout;
use std::collections::HashMap;
use super::archetable::{Archetable, EntityRow};
use super::component::Component;
use super::entity::Entity;
use super::entity_index::EntityIndex;
use super::prelude::Archetype;
use super::world::World;
pub struct Archegraph {
node_count: usize,
node_storage: Vec<Option<Node>>,
// unused_indices: Vec<NodeIndex>,
type_lookup: HashMap<Archetype, NodeIndex>,
}
pub type NodeIndex = usize;
pub struct Node {
index: NodeIndex,
archetable: Archetable,
with: HashMap<Component, NodeIndex>,
without: HashMap<Component, NodeIndex>,
}
pub type GraphEntityIndex = EntityIndex<(NodeIndex, EntityRow)>;
impl Archegraph {
pub fn new() -> Self {
let empty_node = Node::new(0, Archetable::new_empty());
let mut type_lookup = HashMap::new();
type_lookup.insert(Archetype::EMPTY, 0);
Self {
node_count: 1,
node_storage: vec![Some(empty_node)],
// unused_indices: Vec::new(),
type_lookup,
}
}
#[inline]
#[must_use]
pub fn node_count(&self) -> usize {
self.node_count
}
#[must_use]
pub fn root(&self) -> &Node {
self.node_storage[0].as_ref().unwrap()
}
#[must_use]
pub fn root_mut(&mut self) -> &mut Node {
self.node_storage[0].as_mut().unwrap()
}
#[must_use]
pub fn get(&self, index: NodeIndex) -> Option<&Node> {
self.node_storage.get(index).and_then(Option::as_ref)
}
#[must_use]
pub fn get_mut(&mut self, index: NodeIndex) -> Option<&mut Node> {
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,
) -> &mut Node {
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 index = self.reserve_archetable_index();
let layouts = self.get_layouts(archetype, entity_index);
let archetable = Archetable::new(archetype.clone(), layouts);
let mut node = Node::new(index, archetable);
self.type_lookup.insert(archetype.clone(), index);
self.fill_edges(&mut node, entity_index);
let entry = &mut self.node_storage[index];
*entry = Some(node);
entry.as_mut().unwrap()
}
}
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.get_column(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 Node, 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.without.insert(*component, other_node.index);
other_node.with.insert(*component, node.index);
}
}
}
impl Node {
#[must_use]
fn new(index: NodeIndex, archetable: Archetable) -> Self {
Self {
index,
archetable,
with: HashMap::new(),
without: HashMap::new(),
}
}
#[inline]
#[must_use]
pub fn index(&self) -> NodeIndex {
self.index
}
#[inline]
#[must_use]
pub fn get(&self) -> &Archetable {
&self.archetable
}
#[inline]
#[must_use]
pub fn get_mut(&mut self) -> &mut Archetable {
&mut self.archetable
}
}

@ -1,27 +1,27 @@
use std::any::TypeId;
use std::alloc::Layout;
use super::archetype::Archetype;
use super::entity::Entity;
use super::world::World;
use super::utility::RawComponentVec;
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 `colums` vector of an `Archetable`.
pub type ValueComponentColumn = usize;
pub struct ComponentData {
column_index: Option<ValueComponentColumn>,
}
struct Column {
struct ColumnData {
data: RawComponentVec,
component_index: usize,
}
@ -38,17 +38,16 @@ impl Archetable {
}
#[must_use]
pub fn new(world: &World, archetype: Archetype) -> Self {
pub fn new(archetype: Archetype, layouts: Vec<Option<Layout>>) -> Self {
assert_eq!(archetype.components().len(), layouts.len());
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();
for layout in layouts {
// If the entity has `Layout`, add a column for it.
let column_index = if let Some(layout) = layout {
columns.push(Column {
columns.push(ColumnData {
data: RawComponentVec::new(layout),
component_index: components.len(),
});
@ -57,11 +56,7 @@ impl Archetable {
None
};
components.push(ComponentInfo {
entity,
column_index,
type_id,
});
components.push(ComponentData { column_index });
}
Self {
@ -72,7 +67,13 @@ impl Archetable {
}
}
pub fn push(&mut self, entity: Entity) -> EntityRow {
#[inline]
#[must_use]
pub fn archetype(&self) -> &Archetype {
&self.archetype
}
pub fn insert(&mut self, entity: Entity) -> EntityRow {
self.entities.push(entity);
let new_len = self.entities.len();
for column in self.columns.iter_mut() {
@ -92,23 +93,13 @@ 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 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 get_column(&self, index: ValueComponentColumn) -> Option<&RawComponentVec> {
Some(&self.columns.get(index)?.data)
}
#[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 get_column_mut(&mut self, index: ValueComponentColumn) -> Option<&mut RawComponentVec> {
Some(&mut self.columns.get_mut(index)?.data)
}
}

@ -1,6 +1,6 @@
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)]
@ -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,52 @@ impl Archetype {
self.remove(component);
self
}
// #[must_use]
// pub fn union(&self, other: &Self) -> Self {
// let mut components = Vec::new();
// let mut self_iter = self.components.iter();
// let mut other_iter = other.components.iter();
// {
// let mut maybe_a = self_iter.next();
// let mut maybe_b = other_iter.next();
// while let (Some(a), Some(b)) = (maybe_a, maybe_b) {
// use std::cmp::Ordering::*;
// components.push(match a.cmp(b) {
// Less => {
// maybe_a = self_iter.next();
// *a
// }
// Equal => {
// maybe_a = self_iter.next();
// maybe_b = other_iter.next();
// *a
// }
// Greater => {
// maybe_b = other_iter.next();
// *b
// }
// })
// }
// if let Some(a) = maybe_a {
// components.push(*a);
// }
// if let Some(b) = maybe_b {
// components.push(*b);
// }
// }
// for a in self_iter {
// components.push(*a);
// }
// for b in other_iter {
// components.push(*b);
// }
// Self { components }
// }
}
impl<T> FromIterator<T> for Archetype
@ -71,13 +122,3 @@ where
Self::new(iter)
}
}
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()
}
}

@ -96,7 +96,7 @@ impl std::hash::Hash for Component {
}
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`.

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

@ -2,26 +2,28 @@ use std::alloc::Layout;
use std::any::TypeId;
use std::collections::HashMap;
use super::archetable::Archetable;
use crate::ecs::prelude::Archetype;
use super::archegraph::{Archegraph, GraphEntityIndex};
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.register_component::<Layout>();
@ -37,34 +39,44 @@ 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 type_id = TypeId::of::<T>();
self.typeid_lookup.insert(type_id, entity);
entity
}
#[must_use]
pub fn create(&mut self) -> Entity {
pub fn create(&mut self, archetype: &Archetype) -> Entity {
let graph_node = self
.archegraph
.lookup_or_create(archetype, &self.entity_index);
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 {

Loading…
Cancel
Save