# Ecsilarant
Welcome to Ecsilarant, an ECS framework for the future!
## Goals
The following are the goals of ecsilarant, in order of priority.
### Safety
If something compiles, then it should not produce errors at runtime.
### Expressiveness
Users should be empowered to express the behaviors they care about.
### Introspection and decoupling induction
The framework should induce the user to think about the meaningful relationship between newly inserted behaviors and pre-existing behaviors, but incremental insertions of new behaviors should minimize the amount of pre-existing code that needs to be changed or understood.
### Speed & Parallelism
Applications written on to of ecsilarant should run fast, and they should exploit all parallelism available in the target platform.
### Intuitiveness & Predictability
Users should have intuitive ways of expressing the semantics they care about, and the results of what they write should be unsurprising.
## Core API
### Component
A component is just any rust type. A single world cannot have two components of the same type, but this constraint can be circumvented by using the newtype pattern (i.e.: by wrapping a type in different structs with a single field of the desired type).
### Entity
Entities are a specific assignment of values for a set of components.
### Archetype
An archetype represents a set of components. Each entity at a particular point in time conforms to a specific archetype.
### World
A world is a collection of Archetypes and entities with conform to exactly one Archetype.
### Realm
A realm represent a temporary claim over some part of the world. It is expressed a set of immutable or mutable references of components, where each component is only represented once.
```rust
trait Realm {}
impl<Component, RealmTail : Realm> Realm for Cons<& Component, RealmTail>
where RealmTail : DoesNotContain<& Component> // TODO: Can this be expressed?
{}
impl<Component, RealmTail : Realm> Realm for Cons<&mut Component, RealmTail>
where RealmTail : DoesNotContain<&mut Component> // TODO: Can this be expressed?
{}
```
### Subjects
The concept of `Subjects` is also important for a few more advanced use cases. Subjects represents the set of Archetypes that are matched by a particular Realm.
### EntityId
Represents an identifier for an entity which contains a particular set of components.
EntityId can only be used when a pertinent index exists (See IndexedQuery) and they can be converted into Ids for any larger ComponentSet.
```rust
struct EntityId<ComponentSet> {
persistent_id: usize,
}
impl<ComponentSet, OtherComponentSet> Into<EntityId<OtherComponentSet>> for EntityId<ComponentSet>
where ComponentSet: IsSubset<OtherComponentSet> { /*...*/ }
```
### Component Visitors
Component visitors allows you to read, modify, insert or remove a specific component on a set of entities.
```rust
trait ComponentVisitor {
type RealmAtom;
/*...*/
}
```
#### Immutable references
Immutable references are component visitors. They give you access to a read only view on a particular component.
```rust
impl<Component> ComponentVisitor for &Component {
type RealmAtom = &Component;
/*...*/
}
```
#### Mutable references
Mutable references are component visitors. They give you access to a mutable view on a particular component.
```rust
impl<Component> ComponentVisitor for &mut Component {
type RealmAtom = &mut Component;
/*...*/
}
```
#### Inserter
Inserter is a component visitor which allows inserting a component in an entity which doesn't have that component yet.
```rust
impl<Component> ComponentVisitor for Inserter<Component> {
type RealmAtom = &mut Component;
/*...*/
}
struct Inserter<Component>{}
impl Inserter<Component> {
fn insert(value: Component) {/*...*/}
}
```
#### Remover
Remover is a component visitor which allows removing a component from an entity which has that component.
```rust
impl<Component> ComponentVisitor<Component> for Remover<Component> {
type RealmAtom = &mut Component;
/*...*/
}
struct Remover<Component>{}
impl Remover<Component> {
fn remove() {/*...*/}
}
```
#### With
Component visitor used to enforce that the queried entity has a specific component.
Under the hood, this is a convenience method which requests an immutable reference without exposing it to the querying system. Useful to signal to the reader of the system that the component is merely used as a marker.
```rust
impl<Component> ComponentVisitor for With<Component> {
type RealmAtom = &Component;
/*...*/
}
```
#### Without
Component visitor used to enforce that the queried entity does not have a specific component.
This is a convenience method which creates a dummy component which is enforced to be exclusive with another component.
```rust
impl<Component> ComponentVisitor for Without<Component> {
type RealmAtom = &Without<Component>;
/*...*/
}
```
#### Immutable Option (is this a good idea?)
Component visitor used to match archetypes with or without a specific component, and provide an immutable reference is the component is present.
```rust
impl<Component> ComponentVisitor for Option<& Component> {
type RealmAtom = & Component;
/*...*/
}
```
#### Mutable Option (is this a good idea?)
Component visitor used to match archetypes with or without a specific component, and provide a mutable reference is the component is present.
```rust
impl<Component> ComponentVisitor for Option<&mut Component> {
type RealmAtom = &mut Component;
/*...*/
}
```
### ArchetypeSlice
An ArchetypeSlice represents a set of compatible component visitors.
The rule for compatibility between component visitors is simple:
- The same component can't be referenced more than once.
The Realm of an ArchetypeSlice is the set of the RealmAtoms of each component visitor.
```rust
type ArchetypeSliceCons<HeadComponentVisitor, ArchetypeSliceTail> =
Cons<HeadComponentVisitor, ArchetypeSliceTail> ;
trait ArchetypeSlice {
type Realm;
}
impl<HeadComponentVisitor: ComponentVisitor, ArchetypeSliceTail: ArchetypeSlice> ArchetypeSlice
for ArchetypeSliceCons<HeadComponentVisitor, ArchetypeSliceTail>
where HeadComponentVisitor: IsCompatibleWithArchetypeSlice<ArchetypeSliceTail> {/*...*/}
impl ArchetypeSlice for Nil {/*...*/}
```
### Entity visitors
Entity visitors allows you to read, modify, create or destroy a set of entities.
Each Entity Visitor has an associated Realm.
```rust
trait EntityVisitor {/*...*/}
```
#### Query
A Query is a view of the entities which conform to a particular ArchetypeSlice.
The Realm of a query, is the same as the Realm of the associated ArchetypeSlice.
A query allows you to iterate over the matched entities, potentially in parallel, or to access an ArchetypeSlice for a specific index.
```rust
trait QueryEntityVisitor: EntityVisitor {
type ArchetypeSlice;
type ComponentSet; // Components touched by this Query
type Iter : Iterator<Item=ArchetypeSlice>;
type ParIter: ParallelIterator<Item:ArchetypeSlice>;
// Get an entity from an entity_id if the entity still exists and if the entity still matches this Query.
fn get<ComponentSet>(&mut self, entity_id: EntityId<ComponentSet>) -> Option<Self::ArchetypeSlice>
where Self::ComponentSet : IsSubSet<ComponentSet>; //Can this be implemented?
// Apply some function to the ArchetypeSlice matching this query.
fn for_each<F : Fn(ArchetypeSlice)>(&mut self, f: F);
// Apply some function to the ArchetypeSlice matching this query in parallel.
fn par_for_each<F : Fn(ArchetypeSlice)>(&mut self, f: F);
// Iterate over each ArchetypeSlices matching this query.
fn iter(&mut self) -> Self::Iter;
// Iterate over each ArchetypeSlices matching this query in parallel.
fn par_iter(&mut self) -> Self::ParallelIterator;
// Count the amount of entities matching the query.
// Runtime complexity is O(A) where A is the number of Archetype matching the query.
fn count() -> usize;
}
struct Query<QueriedArchetypeSlice: ArchetypeSlice, IsIndexed> { /*...*/ }
impl<QueriedArchetypeSlice> EntityVisitor for Query<QueriedArchetypeSlice> { /*...*/ }
impl<QueriedArchetypeSlice> QueryEntityVisitor for Query<QueriedArchetypeSlice> {
type ArchetypeSlice = QueriedArchetypeSlice;
/*...*/
}
```
#### IndexedQuery
An indexed query is very similar to a query, but additionally offers access to a persistent EntityId for each entity matched.
```rust
trait IndexedQueryEntityVisitor: QueryEntityVisitor {
type IndexedIter : Iterator<(EntityId<Self::ComponentSet>, ArchetypeSlice)>;
type IndexedParIter: ParallelIterator<(EntityId<Self::ComponentSet>, ArchetypeSlice)>;
// Apply some function to the ArchetypeSlice matching this query.
fn enumerate_for_each<F : Fn((EntityId<Self::ComponentSet>, ArchetypeSlice))>(&mut self, f: F);
// Apply some function to the ArchetypeSlice matching this query in parallel.
fn par_enumerate_for_each<F : Fn((EntityId<Self::ComponentSet>, ArchetypeSlice))>(&mut self, f: F);
// Iterate over each ArchetypeSlices matching this query.
fn enumerate(&mut self) -> Self::IndexedIter;
// Iterate over each ArchetypeSlices matching this query in parallel.
fn par_enumerate(&mut self) -> Self::IndexedParIter;
}
struct IndexedQuery<QueriedArchetypeSlice: ArchetypeSlice> { /*...*/ }
impl<QueriedArchetypeSlice> EntityVisitor for IndexedQuery<QueriedArchetypeSlice> { /*...*/ }
impl<QueriedArchetypeSlice> QueryEntityVisitor for IndexedQuery<QueriedArchetypeSlice> {
type ArchetypeSlice = QueriedArchetypeSlice;
/*...*/
}
```
#### QueryAndDestroyer
#### QueryAndDeferredDestroyer
#### Creator
A creator allows the creation of entities fulfilling a particular Archetype. The entities are created immediately after the end of the associated system execution, and before any dependent system.
The realm of a Creator, is the set of mutable references for each component in the Archetype.
```rust
struct EntityCreator<Archetype> { /*...*/ }
impl EntityCreator {
fn create(component_values: Archetype) { /*...*/ }
}
//TODO: Decide if you actually need a trait for EntityVisitors.
```
#### IndexedCreator
An IndexedCreator is very similar to a Creator, but it allows you to specify one or more indexes which will be returned when an entity is created.
```rust
struct IndexedEntityCreator<Indexes, Archetype> { /*...*/ }
impl<Indexes, Archetype> EntityCreator<Indexes, Archetype> {
fn create(component_values: Archetype) -> Indexes { /*...*/ }
}
//TODO: Decide if you actually need a trait for EntityVisitors.
```
#### Destroyer
A destroyer allows the destruction of entities matching a set of components.
The destroyer has a really large realm. The Realm of the destroyer corresponds to the set of mutable references for all components
#### DeferredCreator
#### DeferredIndexedCreator
#### DeferredDestroyer
#### MultiEntityVisitor
## Rules for
## Implementation details
### Storage layout
Ecsilarant uses an archetype based storage, where all components pertinent to entities conforming to an individual archetypes are stored in one place.
#### World Layout
The `WorldStorage`consist of a recursive `Cons` structure where each Head represent a single `ArchetypeStorage`.
#### Archetype Layout
Each `ArchetypeStorage` is itself a `Cons` where each Head represents a single `ComponentStorage`.
#### Component Layout
Each `ComponentStorage` is an abstraction that allows iterations or indexed accesses over all component values of a particular type within a single Archetype. Within a single `ArchetypeStorage`, component values for a single entity will be consistently indexed across the component storages.
#### Persistent indexes?
How to maintain across add/remove of entities or component?
### World creation
### Parallel scheduler
#### Recursive async structure
#### World view splitting / merging
#### Precedence rule
#### DAG validation
#### Signal missing dependencies
#### Signal useless (harmful) dependencies
#### Removal of redundant edges
#### Same-component parallelism?
### Supported Query language
#### Query
#### Insert
#### Add component (how to forward to follow-up query?)
#### Remove component (how to forward to follow-up query?)
#### Filter by Without?
#### Filter by With? (Or just use immutable ref)
#### MaybeComponent query?
#### #Query by index? how to expose. Use type erased fn.
### Crust with macro transmongrification