# Bubel Entity Component System
[![pipeline status](https://gitlab.com/Mergul/bubel-ecs/badges/master/pipeline.svg)](https://gitlab.com/Mergul/bubel-ecs/-/commits/master)
[![codecov](https://codecov.io/gl/Mergul/bubel-ecs/branch/master/graph/badge.svg?token=Unm0TJhFoW)](https://codecov.io/gl/Mergul/bubel-ecs)

BubelECS is Entity-Component-System architectural pattern implementation in D language.
Library aims to delivery fast and flexible architecture for developing games. Library is **@nogc** and **betterC** compatible. WASM is supported thorugh Emscripten.
Project haven't any external dependencies.

BubelECS was tested on Linux, Windows, Android and WASM.

**Currently library is in beta stage so some significant API changes can appear.**

## Design

For core information about Entity-Component-System architectural pattern please read definition described at [Wikipedia](https://en.wikipedia.org/wiki/Entity_component_system).

Main design principles are:

 * **Data oriented design** - components memory is packed into tight block so iterating over entity components is cache friendly
 * **Fast and safe EntityID** - every entity is referenced by its unique ID. Accessing by ID is safe even if entity don'y exist. Access by ID is constant time operation.
 * **Multithreading support** - whole library was developed with multithreading in mind. Adding components is thread-friendly so you get usable EntityID as early as possible. Operations like adding or removing components are deferred to end of frame. Dependencies between systems parallel execution are generated automatically.
 * **Good logic separation** - system needs information only about components which it's use, there is no relation between systems. Systems can be compiled as multiple separate libraries and used together.
 * **Flexible execution model** - system iterate over entities which meet specified conditions. Components can be marked as required, optional or excluded. Systems are exectued in specific order determined by system priority.
 * **Builtin events handling** - library has builtin support for event handlig to makes easier communication between different entities.
 * **Hot-reload** - hot-reloading for systems should be as easy as possible. In other words library should give functionality to support hot-reload of systems and components with minimal work. **(WIP!)**.

There are some assumptions that should be considered when developing application:
 
 * Iterating over components is fastest way of access data so it's should be main way of making calculations.
 * Using of direct access and events should be used very wisely and minimized.
 * Direct component access is faster than events, because events must buffer memory and call multiple system handler callbacks.
 * Components can be used to marking entities,  assingment to systems and changing logic of entity in runtime. Using too much component based marking can lead to memory fragmentation and performence drop.
 * Systems give great opporunity to separate game logic. Systems can be enabled and disabled easly in runtime. It's can be used to enabling some debug systems if needed. Additionaly this concept makes game logic changes easier to deal with. If you develop your application wisely it should be trivial to change some core logic by changing only several systems or adding some new systems. Every entity can easily takes some behaviour from different entity type by adding several components.

### Features

 * ECS architectural pattern
 * Data oriented design
 * Safe identifiers (EntityID)
 * EntityTemplates
 * Basic events handling
 * Easy systems ordering
 * Automatic multithreaded jobs generating
 * Runtime and fast components adding and removing
 * Listeners for adding and removing entity components inside systems
 * Update passes
 * Calling custom callbacks for system entity groups
 * betterC compatibility
 * Emscripten compatibility

### Planned

 * Worlds - every world works as separate environment. Entities don't with entities from different world. Systems and components should be registered for every world separately.
 * Hot-reload support - currently only reloading systems (their functions) works. There is possibility to serialize every entity and system, but it's should be provided by library itself with special callbacks and helpers. Additionaly planned is system which helps with taking new EntityID from serialized identifiers. 
 * External dependencies - ability to provide dependencies between system which isn't related to components.
 * Static components - this components will have different memory model and can be accessed only directly. It will be slower to access but won't trigger memory copy when component is added. It should fix problem with big components which isn't needed by systems iteration callbacks or is required rarely.
 * Better EventManager - there are several optimization and improvements that can be added in the future. 
 * More demos and examples - demo appliaction is very basic now, but in future more minigames and sanbox mode (opportunity to mix many components and systems) are planned.
 * C API - in highly depends on amount of work required. Makes possible to use library from different languages.
 * GPU compute - idea in draft stage. Special components and systems whose data wolud be on GPU memory.
 * More smaller improvements...

For more information about design and usage feel free to read [documentation](https://mergul.gitlab.io/bubel-ecs/ecs.html)**(WIP)** and [WIKI](https://gitlab.com/Mergul/bubel-ecs/-/wikis/home).

## Build Instructions

To build library you needs recent D compiler and optionally Emscripten (with python) to build web version. Currenlty tested are: LDC, DMD and GDC. \
Supported build systems are DUB and Meson.

##### DUB
```shell
#available configurations: library,  dynlib, library-betterC, dynlib-betterC
dub build -c library -b release
```

##### Meson
```shell
#use '-DbetterC=true ' to build betterC code
meson build . #add '--buildtype=release' to build release code 
cd build
ninja
```

##### Emscripten
```shell
python compile_wasm.py -opt
```

##### Documentation
```shell
adrdox -i source/bubel/ecs/ -o docs/ -s skeleton.html
```

For more detailed build options please check documentation for used build system.

## Demos

Repository contain demo application. You can check demo [online](https://mergul.gitlab.io/bubel-ecs/ecs_demo.html) or build it form source code using DUB. \
Online demo support multithreading and page tries to check if client support WASM multithreading or not and loads properly JS and WASM code. It was tested on Chrome, Firefox, Opera, Brave on Linux and Android. On firefox there is problem with multithreaded version so if demo don't works please try to disable shared_memory in browser flags.

## Code example

```d

struct Position
{
    mixin ECS.Components; //makes struct component
    float x;
    float y;
}

struct Velocity
{
    mixin ECS.Components;
    //default values works
    float x = 0.1;
    float y = 1;
}

struct StaticFlag
{
    mixin ECS.Components;
}

struct UpdateSystem
{
    mixin ECS.System; //makes struct system

    ECS.ExcludedComponents!(StaticFlag); //prevents static entities from update

    struct EntitiesData
    {
        int length; //entities count
        @readonly Entity[] entities; //entities arrays, entity contain ID only
        Position[] positions; //positions array
        @readonly Velocity[] velocities; //veocities array, readonly (Multithreading tag)
    }

    void onUpdate(EntitiesData data) //update callback, called multiple times per frame for every entities types
    {
        foreach(i; 0..data.length) //iterate over entities
        {
            data.positions[i].x += data.velocities[i].x * dt;
            data.positions[i].y += data.velocities[i].y * dt;
        }
    }
}

void main()
{
    manager.beginRegister();
    //register components
    manager.registerComponent!Position;
    manager.registerComponent!Velocity;
    manager.registerComponent!StaticFlag;
    //register system with priority 0
    manager.registerSystem!UpdateSystem(0);
    manager.endRegister();

    //allocate template
    EntityTemplate* tmpl = manager.allocateEmplate([Velocity.component_id, Position.component_id].staticArray);
    scope (exit) manager.freeTemplate(tmpl);

    //gets pointer to template component data
    Position* position = tmpl.getComponent!Position;
    foreach(i; 0 .. 100)
    {
        position.x = i % 10;
        position.y = i / 10;
        manager.addEntity(tmpl);
    }

    manager.begin(); //start frame
    manager.update(); //update all systems, there onUpdate callbacks are called
    manager.end(); //end frame
}

```

## Links

Documentation: https://mergul.gitlab.io/bubel-ecs/ecs.html \
Online demo: https://mergul.gitlab.io/bubel-ecs/ecs_demo.html
