de.tum.ei.lkn.eces:core

This project implements an Entity-Component Event-based System (ECES) framework. In such a framework, classes are organized as components that attach to entities, and listeners (or callbacks) can be easily configured to asynchronously run upon the creation/deletion/update of selected components.

License

License

GroupId

GroupId

de.tum.ei.lkn.eces
ArtifactId

ArtifactId

core
Last Version

Last Version

2.0.3
Release Date

Release Date

Type

Type

jar
Description

Description

This project implements an Entity-Component Event-based System (ECES) framework. In such a framework, classes are organized as components that attach to entities, and listeners (or callbacks) can be easily configured to asynchronously run upon the creation/deletion/update of selected components.
Project URL

Project URL

https://github.com/AmoVanB/eces-core
Source Code Management

Source Code Management

http://github.com/amovanb/eces-core/tree/master

Download core

How to add to project

<!-- https://jarcasting.com/artifacts/de.tum.ei.lkn.eces/core/ -->
<dependency>
    <groupId>de.tum.ei.lkn.eces</groupId>
    <artifactId>core</artifactId>
    <version>2.0.3</version>
</dependency>
// https://jarcasting.com/artifacts/de.tum.ei.lkn.eces/core/
implementation 'de.tum.ei.lkn.eces:core:2.0.3'
// https://jarcasting.com/artifacts/de.tum.ei.lkn.eces/core/
implementation ("de.tum.ei.lkn.eces:core:2.0.3")
'de.tum.ei.lkn.eces:core:jar:2.0.3'
<dependency org="de.tum.ei.lkn.eces" name="core" rev="2.0.3">
  <artifact name="core" type="jar" />
</dependency>
@Grapes(
@Grab(group='de.tum.ei.lkn.eces', module='core', version='2.0.3')
)
libraryDependencies += "de.tum.ei.lkn.eces" % "core" % "2.0.3"
[de.tum.ei.lkn.eces/core "2.0.3"]

Dependencies

compile (5)

Group / Artifact Type Version
com.google.guava : guava jar 18.0
org.json : json jar 20150729
de.tum.ei.lkn.eces : master-pom-commons jar 1.0.21
org.aeonbits.owner : owner jar 1.0.10
log4j : log4j jar 1.2.17

test (2)

Group / Artifact Type Version
de.tum.ei.lkn.eces : master-pom-commons test-jar 1.0.21
junit : junit jar 4.13.1

Project Modules

There are no modules declared in this project.

Core

This project implements an Entity-Component Event-based System (ECES) framework. In such a framework, classes are organized as components that attach to entities, and listeners (or callbacks) can be easily configured to asynchronously run upon the creation/deletion/update of selected components.

Usage

The project can be downloaded from maven central using:

<dependency>
  <groupId>de.tum.ei.lkn.eces</groupId>
  <artifactId>core</artifactId>
  <version>X.Y.Z</version>
</dependency>

Simple Example

The following piece of code creates a simple system that counts the number of times a C1 component has been attached to an entity.

public class S1 extends RootSystem {
	private int counter = 0;
	private C1Mapper c1Mapper = new C1Mapper(controller);

	public S1(Controller controller) {
		super(controller);
	}

	@ComponentStateIs(State = ComponentStatus.New)
	public synchronized void countC1(C1 c) {
		counter++;
	}
	
	public int getCounter() {
		return counter;
	}
}

The following piece of code would then print out 1, as one C1 component was attached to an entity.

Controller controller = new Controller();
S1 s = new S1(contoller);
Entity ent = controller.createEntity();
C1 comp = new C1();
C1Mapper c1Mapper = new C1Mapper(controller);
c1Mapper.attachComponent(ent, comp);
System.out.println(s.getCounter());

Advanced Examples

See other ECES repositories (e.g., graph, network, and routing) for more detailed/advanced examples.

The ECES Framework

The framework consists of entities to which components can be attached. An entity is an instance of the Entity.java class while components are instances of (subclasses of) the Component.java class. A component must hence derive from the basic Component.java class but can then define any number of new members and/or methods for its operational purpose.

A single entity can hold any number of components but only one instance of each component class.

Systems

A component belongs to a single system. This is defined using the @ComponentBelongsTo annotation in the definition of the class of the component. A system is an instance of (a subclass of) RootSystem.java. Using the @ComponentStateIs annotation, a system can implement methods that will be run on the creation (i.e., attachment to an entity), update or deletion (i.e., detachment from an entity) of a given component class. Since they listen to events on particular components, these methods are called listeners. These methods must have a single parameter the type of which defines the type of component to which it has to listen.

The Controller

Any system, when instantiated, is automatically registered to a controller (an automatically created instance of Controller.java). A same system class can only be registered once to a controller (i.e., can only be instantiated once). The controller is responsible for handling the events and running the corresponding listeners methods of the systems registered to it.

The controller provides a createEntity() method to generate entities.

The Mapper

The attachment, update and detachment of components from an entity are done using a mapper, i.e., an instance of the Mapper.java class. When implementing a new component, a corresponding mapper should be created. It simply has to extend Mapper<X> where X is replaced by the given component. The new Mapper(Controller controller) constructor can then be used. A mapper can simply be obtained by calling its constructor with the responsible controller as parameter. The attachComponent(), updateComponent() and detachComponent() of the mapper can then be used to respectively attach, update or detach a component to/from an entity. The mapper also provides a get() method allowing to retrieve the instance of the component class managed by the mapper which is attached to a given entity.

The Mapper Space

The framework is multi-threaded. For this purpose, a mapper space concept is introduced. A mapper space is a block of code in which the mapper methods can be used. All the attachment, detachment and update jobs triggered within a mapper space are effectively executed at the closure (i.e., at the end) of the mapper space, along with their associated listeners. If the mapper methods are not used within a mapper space, they are automatically enclosed within a mapper space of one statement.

A mapper space is defined as follows using the Java try-with-resources statement:

try (MapperSpace ms = controller.startMapperSpace()) {
    // commands to execute in the mapper space
}

In order to ensure consistency when reading data from a component, the mapper also provides an acquireReadLock() method. The read lock(s) acquired is (are) automatically released at the closure of the mapper space in which they have been acquired.

Note that it is possible to define a mapper space within a mapper space. However, this will result in the internal mapper space not being created. This allows methods using mapper spaces to be called within another mapper space. Note that the attachment, update, detachment and associated listeners will then be executed only at the end of the global mapper space. This means that, if one defines a mapper space, nothing ensures that, when executing the lines of code written after the mapper space, the attachment, update, detachment and associated listeners of the mapper space will have been executed. Indeed, these lines of code might also be part of a parent mapper space which is not yet closed.

The Local Component

In some situations, it might be handy to have different data stored for a single component of a system. This is done by deriving from the LocalComponent.java class. Each instance of data is then an instance of the Java Object.java class. Such a local component, which is a component, is to be managed by a local mapper, which can be obtained using the getLocalMapper() method of the controller. When asking for such a mapper, an owner must be specified. This owner is a Java object owning a given instance of the data stored in the local component. The local mapper method get() method will then take care to return the data instance corresponding to the owner for which this local mapper has been created. Different owners hence define different instances of data stored in the component.

Versions

Version
2.0.3
2.0.2
2.0.1