D O M I N I O N

An insanely fast ECS library for Java

Dominion

Dominion is an Entity Component System library for Java.

Entity Component System (ECS) architecture promotes data-oriented programming. It’s all about data (components) and first-class functions (systems) that operate on data.

This means that, unlike OOP, data and operations are not encapsulated together in objects, which are called entities in ECS.

Entities model the business objects of the user application, and the entity promotes “composition over inheritance” by grouping a dynamic list of components to define its specific features.

Systems usually operate on components sequentially and can be very fast if data are stored in cache-friendly ways. Systems are decoupled from each other and each system knows only about the data it operates on. This strongly promotes high concurrency, running systems in parallel whenever they can independently operate on the data.

ECS architecture is particularly suitable (but not limited to) if you have to manage many objects in your application. In addition, application code tends to be more reusable and easier to extend with new functionality thanks to the components’ composition and subsequent addition of new systems.

Dominion Features

Archetype Concept

Dominion implements the so-called Archetype concept through the DataComposition class, an aggregation of component types of which an entity can be made of.

In an ECS, the Archetype is a way to organize and manage entities based on their components. It provides a structured approach for efficiently storing and querying entities with similar component compositions.

In an ECS, entities are composed of various components that define their behavior and characteristics. The Archetype groups entities into archetypes based on their shared component types. An archetype represents a specific combination of component types that entities possess.

The purpose of using archetypes is to optimize entity storage and system processing. Entities within the same archetype have the same layout of components, which allows for data-oriented design and improves memory locality. This arrangement enhances cache coherency, enables more efficient processing of system operations, and is primarily handled in Dominion through the ChunkedPool.Tenant class.

Archetypes also facilitate efficient querying and iteration over entities that match specific component combinations. By organizing entities based on their archetypes, systems can quickly identify and process only the relevant entities that contain the required component types, reducing unnecessary overhead.

When a new entity is created or modified, the ECS manager checks the archetype it belongs to. If an existing archetype matches the component composition, the entity is added to that archetype. Otherwise, a new archetype is created for the entity’s unique component combination.

By leveraging the Archetype, Dominion implementations can achieve high performance and scalability by optimizing memory usage, facilitating cache-friendly access patterns, and enabling efficient processing of entities with similar component compositions.

Overall, the Archetype in an Entity Component System provides an effective means of organizing, storing, and processing entities with shared component types, leading to improved performance and flexibility in game development and other related domains.

Struct of Arrays

Dominion implements a specific Struct of Arrays (SoA) layout through the ChunkedPool.LinkedChunk class.

In an ECS, the SoA layout refers to a data organization approach where the components of entities are stored in separate arrays based on their data types. This is in contrast to the traditional approach of storing entities as Arrays of Structs (AoS) where all components of an entity are grouped together in a single struct.

The SoA layout is designed to improve data locality and cache efficiency, which can lead to significant performance benefits in certain scenarios. By storing components of the same data type in contiguous arrays, the SoA layout allows for better memory access patterns, reducing cache misses and improving CPU cache utilization.

For example, let’s consider an ECS with three component types: Position, Velocity, and Renderable. In an SoA layout, all Position components would be stored in a separate array, all Velocity components in another array, and so on. Each array would contain the respective component data for the entities.

By leveraging the SoA layout, which stores components of the same data type in contiguous arrays, data can be easily aligned for SIMD operations. This alignment allows SIMD instructions to operate on multiple data elements in parallel, providing significant performance benefits.

Overall, the SoA layout in Dominion provides a way to optimize memory access patterns and improve performance by storing components in separate arrays based on their data types, allowing for efficient batch processing of components by systems.

Quick Start

In your local environment you must have already installed a Java 17 (or newer) and Maven.

Add the following dependency declaration in your project pom.xml:

<dependency>
    <groupId>dev.dominion.ecs</groupId>
    <artifactId>dominion-ecs-engine</artifactId>
    <version>0.9.0</version>
</dependency>

Check out the Dominion API documentation as a reference to get started implementing your first app.

About Performance

Designing a high-performance and high-concurrency Java library is a piece of cake quite a challenge as the execution speed of Java code could be affected in many ways. Dominion mitigates Java’s performance pitfalls by taking a few actions:

Ok let’s start

Here is a first example:

public class HelloDominion {

    public static void main(String[] args) {
        // creates your world
        Dominion hello = Dominion.create();

        // creates an entity with components
        hello.createEntity(
                "my-entity",
                new Position(0, 0),
                new Velocity(1, 1)
        );

        // creates a system
        Runnable system = () -> {
            //finds entities
            hello.findEntitiesWith(Position.class, Velocity.class)
                    // stream the results
                    .stream().forEach(result -> {
                        Position position = result.comp1();
                        Velocity velocity = result.comp2();
                        position.x += velocity.x;
                        position.y += velocity.y;
                        System.out.printf("Entity %s moved with %s to %s\n",
                                result.entity().getName(), velocity, position);
                    });
        };

        // creates a scheduler
        Scheduler scheduler = hello.createScheduler();
        // schedules the system
        scheduler.schedule(system);
        // starts 3 ticks per second
        scheduler.tickAtFixedRate(3);
    }

    // component types can be both classes and records

    static class Position {
        double x, y;

        public Position(double x, double y) {/*...*/}

        @Override
        public String toString() {/*...*/}
    }

    record Velocity(double x, double y) {
    }
}

Dominion Examples Page

Dominion comes with some documented sample apps to help adopt the solution, like the HelloDominion sample above.

The DarkEntities sample app has also been added, it could inspire a turn-based rogue-like game running on a terminal window:

dark-entities

Here the Dominion Examples page with all the documented code.

Join the Discord for further updates!

Credits

Java Profiler

IntelliJ Ultimate

Support Dominion

If you want to support Dominion project, consider giving it a Star ⭐️