# 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)

**Bubel ECS** 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.

Bubel ECS 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 blocks 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 doesn't exists. Accessing 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 uses, 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 throught system ```onUpdate()``` callback is fastest way of access data so it's should be main way of making calculations.
 * Using 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. 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
 * 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
 * Automatic multithreaded jobs generation
 * External dependencies - ability to provide dependencies between systems which aren't related to components 
 * Compatibility with all big compilers: DMD, LDC, GDC.

### Planned

 * Worlds - every world works as separate environment. Entities don't interact 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. 
 * 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 (e.g. multithreading). 
 * 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 - it's highly depends on amount of work required. Makes it possible to use library from different languages.
 * More smaller improvements...

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

## 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, unittest-runner, unittest-runner-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
```

## 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 Meson. \
Online demo support multithreading and page tries to check if client support WASM multithreading or not and loads proper JS and WASM code. It was tested on Chrome, Firefox, Opera, Brave on Linux, Windows and Android. On firefox there is problem with multithreaded version so if demo doesn't work please try to disable shared_memory in browser flags.

Demos uses these libraries: SDL2, SDL2_Image, bindbc-loader, bindbc-sdl, cimgui, glad, mmutils. \
C++ compiler is required for building dependencies.

Meson is preferred way of building demos. It will download and compile dependencies automatically. DUB version is used only for library development. Build instructions:

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

## Code example

```d

import bubel.ecs.core;
import bubel.ecs.manager;
import bubel.ecs.attributes;
import std.array : staticArray;

struct Position
{
    float x;
    float y;
}

struct Velocity
{
    //default values works
    float x = 0.1;
    float y = 1;
}

struct StaticFlag
{
}

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

    mixin 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, by default components are considered to has write access (used for multithreading dependencies)
        @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;
            data.positions[i].y += data.velocities[i].y;
        }
    }
}

void main()
{
    //initialize ECS
    EntityManager.initialize();

    //begin registering process
    gEntityManager.beginRegister();
    //register components
    gEntityManager.registerComponent!Position;
    gEntityManager.registerComponent!Velocity;
    gEntityManager.registerComponent!StaticFlag;
    //register system with priority 0
    gEntityManager.registerSystem!UpdateSystem(0);
    //end registering process
    gEntityManager.endRegister();

    //allocate template
    EntityTemplate* tmpl = gEntityManager.allocateTemplate([becsID!Velocity, becsID!Position].staticArray);
    scope (exit) gEntityManager.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;
        gEntityManager.addEntity(tmpl);
    }

    gEntityManager.begin(); //start frame, inside system onBegin callbacks are called
    gEntityManager.update(); //update all systems, there onUpdate callbacks are called
    gEntityManager.end(); //end frame, inside system onEnd callbacks are called*/

    //free ECS data
    EntityManager.destroy();
}

```

## Links

Documentation: https://mergul.gitlab.io/bubel-ecs/ecs.html \
Wiki: https://gitlab.com/Mergul/bubel-ecs/-/wikis/home \
Online demo: https://mergul.gitlab.io/bubel-ecs/ecs_demo.html
