Skip to content

Instantly share code, notes, and snippets.

@techgeek1
Last active September 16, 2018 15:38
Show Gist options
  • Save techgeek1/5bbb66e8c5fe67b7380925c401bd2b47 to your computer and use it in GitHub Desktop.
Save techgeek1/5bbb66e8c5fe67b7380925c401bd2b47 to your computer and use it in GitHub Desktop.
Attempt two at an ecs api. This time encoding as much data in the type system as possible via associated types
#![feature(const_fn, const_type_id)]
#[macro_use]
mod macros {
macro_rules! foreach_permutation {
($action:ident!(), $type:ident) => {
$action!($type);
};
($action:ident!(), $type_lead:ident, $($type:ident),*) => {
$action!($type_lead, $($type),*);
foreach_permutation!($action!(), $($type),*);
};
}
}
mod slice {
use std::slice;
pub fn empty<'a, T: 'a>() -> &'a [T] {
unsafe { slice::from_raw_parts(0x1 as *const T, 0) }
}
pub fn empty_mut<'a, T: 'a>() -> &'a mut [T] {
unsafe { slice::from_raw_parts_mut(0x1 as *mut T, 0) }
}
}
mod component {
use slice;
use id::ComponentId;
pub trait Component : Sized + 'static {
const ID: ComponentId = ComponentId::of::<Self>();
}
pub trait ComponentData<'a> : Sized {
type IterData;
type IterItem;
type IterDataMut;
type IterItemMut;
fn empty() -> Self::IterData;
fn empty_mut() -> Self::IterDataMut;
}
macro_rules! impl_component_data {
($($type:ident),*) => {
impl<'data, $($type),*> ComponentData<'data> for ($($type),*)
where $($type: 'data + Component),*
{
type IterData = ($(&'data [$type]),*);
type IterItem = ($(&'data $type),*);
type IterDataMut = ($(&'data mut [$type]),*);
type IterItemMut = ($(&'data mut $type),*);
fn empty() -> Self::IterData {
($(slice::empty::<$type>()),*)
}
fn empty_mut() -> Self::IterDataMut {
($(slice::empty_mut::<$type>()),*)
}
}
}
}
foreach_permutation!(
impl_component_data!(),
A, B, C, D, E, F, G, H, I, J, K, L, M//,
//N, O, P, Q, R, S, T, U, V, W, X, Y, Z
);
pub mod id {
use std::any::TypeId;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ComponentId{
id: TypeId
}
impl ComponentId {
pub const fn of<T: ?Sized + 'static>() -> ComponentId {
ComponentId {
id: TypeId::of::<T>()
}
}
}
}
pub mod iter {
use component::*;
pub struct ReadIter<'data, T: ComponentData<'data>> {
data: T::IterData,
index: usize
}
impl<'data, T> ReadIter<'data, T>
where T: ComponentData<'data>
{
pub fn new(data: T::IterData) -> Self {
ReadIter {
data: data,
index: 0
}
}
}
impl<'data, T> Iterator for ReadIter<'data, T>
where T: ComponentData<'data>
{
type Item = T::IterItem;
fn next(&mut self) -> Option<Self::Item> {
unimplemented!()
}
}
pub struct WriteIter<'data, T: ComponentData<'data>> {
data: T::IterDataMut,
index: usize
}
impl<'data, T> WriteIter<'data, T>
where T: ComponentData<'data>
{
pub fn new(data: T::IterDataMut) -> Self {
WriteIter {
data: data,
index: 0
}
}
}
impl<'data, T> Iterator for WriteIter<'data, T>
where T: ComponentData<'data>
{
type Item = T::IterItemMut;
fn next(&mut self) -> Option<Self::Item> {
unimplemented!()
}
}
}
}
mod storage {
use component::*;
use component::id::ComponentId;
use std::collections::HashMap;
use std::marker::PhantomData;
trait Array { }
struct ComponentArray<T> { marker: PhantomData<T> }
impl<T> Array for ComponentArray<T> {}
// Context for locating components in storage and getting their reference
pub struct ComponentStorage {
components: HashMap<ComponentId, Box<Array>>
}
impl ComponentStorage {
pub fn new() -> Self {
ComponentStorage {
components: HashMap::new()
}
}
pub fn add<T: Component>(&mut self) {
self.components.insert(T::ID, Box::new(ComponentArray::<T> { marker: PhantomData }));
}
pub fn find<'a, T: Component>(&self) -> Option<&'a [T]> {
if let Some(storage) = self.components.get(&T::ID) {
None
}
else {
None
}
}
pub fn find_mut<'a, T: Component>(&self) -> Option<&'a mut [T]> {
None
}
}
}
mod system {
use std::marker::PhantomData;
use storage::ComponentStorage;
use component::ComponentData;
use component::iter::{ReadIter, WriteIter};
pub struct SystemContext<'ctx, TReads, TWrites> {
storage: &'ctx ComponentStorage,
read_marker: PhantomData<TReads>,
write_marker: PhantomData<TWrites>
}
impl<'ctx, 'a, TReads, TWrites> SystemContext<'ctx, TReads, TWrites>
where TReads: ComponentData<'a>,
TWrites: ComponentData<'a>
{
pub fn new(storage: &'ctx ComponentStorage) -> Self {
SystemContext {
storage: storage,
read_marker: PhantomData,
write_marker: PhantomData
}
}
pub fn reads(&self) -> ReadIter<'a, TReads> {
// TODO: - Figure out a way to query all the necessary components, return a 0 length iterator on failure
// - Construct the iterator
// - Verify lifetimes
ReadIter::new(TReads::empty())
}
pub fn writes(&self) -> WriteIter<'a, TWrites> {
// TODO: - Figure out a way to query all the necessary components, return a 0 length iterator on failure
// - Construct the iterator
// - Verify lifetimes
// - Enforce mutability constraints, preferrably at compile time
WriteIter::new(TWrites::empty_mut())
}
}
pub trait System<'a> {
type Reads: ComponentData<'a>;
type Writes: ComponentData<'a>;
fn run<'ctx>(&self, context: &SystemContext<'ctx, Self::Reads, Self::Writes>);
fn create_context<'ctx>(&self, storage: &'ctx ComponentStorage) -> SystemContext<'ctx, Self::Reads, Self::Writes> {
SystemContext::new(storage)
}
}
}
mod test_components {
use component::Component;
#[derive(Debug)]
pub struct Position {
pub x: f32,
pub y: f32,
pub z: f32
}
impl Component for Position {}
#[derive(Debug)]
pub struct Rotation {
pub x: f32,
pub y: f32,
pub z: f32,
pub w: f32
}
impl Component for Rotation {}
#[derive(Debug)]
pub struct Velocity {
pub x: f32,
pub y: f32,
pub z: f32
}
impl Component for Velocity {}
#[derive(Debug)]
pub struct AngularVelocity {
pub x: f32,
pub y: f32,
pub z: f32
}
impl Component for AngularVelocity {}
}
use storage::*;
use component::*;
use system::*;
use test_components::*;
struct Sys;
impl<'a> System<'a> for Sys {
type Reads = (Velocity, AngularVelocity);
type Writes = (Position, Rotation);
fn run<'ctx>(&self, context: &SystemContext<'ctx, Self::Reads, Self::Writes>) {
// Standard read
for (vel, ang_vel) in context.reads() {
println!("{:?}", vel);
}
// Standard write
for (pos, rot) in context.writes() {
pos.x += 1.0;
}
// Read only over writes
for (pos, rot) in context.writes() {
println!("{:?}", pos);
}
// INVALID: Mutation of reads
for (vel, ang_vel) in context.reads() {
vel.x += 1.0;
}
}
}
fn main() {
let mut storage = ComponentStorage::new();
storage.add::<Position>();
storage.add::<Velocity>();
let sys = Sys {};
let context = sys.create_context(&storage);
sys.run(&context);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment