Debugging - Quillraven/Fleks GitHub Wiki

Sometimes, it will be necessary to debug your world. In this section you are going to learn more details about the internal structure of Fleks. Because of the efficient and performant way Fleks is storing data, it is a little bit tricky to understand where to find things. Luckily with version 1.6 it got way easier with the new snapshot and snapshotOf functions of the world. Let`s have a look at those first.

When you just want to see all your entities and their components you can call the snapshot function of your world like so:

val snapshot = world.snapshot()

This will return a map. The keys are the entities and the values are a list of components of an entity. Here is an example that shows a snapshot with an Entity 3 that has two components, a Move and a Position component. It also shows that there are in total 102 active entities:

image

Similar to the snapshot of the entire world, you can use the snapshotOf function to get the information of a single entity:

val eSnapshot = world.snapshotOf(Entity(3))

This will return the components of a specific entity. Here is an example of Entity 3 from above, which shows the Move and Position component:

image

Those two functions should hopefully be sufficient for most of your debugging work. It is also possible to load a specific snapshot via the loadSnapshot function of the world:

world.loadSnapshot(snapshot)

This will clear any entities of your world and restore the provided snapshot.

It is also possible to load a snapshot of a single entity which will replace all its current components with the given snapshot. If the entity does not exist yet, then it will be created with the given id:

val eSnapshot = world.snapshotOf(Entity(3))

world.loadSnapshotOf(Entity(3), eSnapshot)

Further details

If the functionality described above is not sufficient then here is some more details. Let`s have a look at a world with 128 entities (active and recycled):

image

You can see that there are three services in a world:

  • SystemService: responsible for creating and updating systems. (Update: this service does no longer exist in version 2.x+. Its functionality got merged directly into the World).
  • ComponentService: responsible for maintaining ComponentMapper and components. (Update: ComponentMapper are called ComponentsHolder since version 2.x+.).
  • EntityService: responsible for maintaining entities.

In addition the world keeps a reference to all families to avoid creating the same family multiple times.

SystemService

The system service is very simple. It creates all the systems provided in the world configuration and stores them to update them accordingly when world.update is called.

image

ComponentService

The component service creates all the necessary ComponentMapper instances which are responsible to maintain the component data of one specific component for all entities. Each mapper has a unique ID which is used for certain lookups. (Update: since 2.x+ the ID is part of the ComponentType of a Component).

We will have a look into this ID in more detail when we come to the EntityService in the next section.

Also, each mapper has an array of components of one specific type. The index of the array is the ID of an entity. Here is an example of a ComponentMapper for a Move component. We can see that entity 3, 6, 9 and 12 have such a component:

image

EntityService

The entity service is responsible to maintain entity data which also includes the component configuration of a specific entity. A component configuration is simply the type of components that an entity has. It is called a component mask in Fleks (=cmpMasks). Also, the service keeps track of which ID will be used for the next entity and the already removed entities which are recycled and reused for future create calls.

image


You might wonder why there are removedEntities and recycledEntities. They actually contain the same information. However, recycledEntities is an ArrayDeque which is a very fast way to add/remove values at the end or beginning but it is not that fast to lookup a value like finding out if an entity is already removed/recycled. The removedEntities is a BitArray which is very efficient to lookup a value but slower for other operations like finding out if there is any entity removed/recycled.

The combination of those two provides a very efficient and fast way to be able to add and remove a recycled entity and also to lookup if an entity is already removed.


The cmpMasks contain the information what kind of components an entity has. This information is stored in a BitArray. The index of the bit relates to the ComponentMapper ID mentioned above. Let's have a look again on our entity 3 from the top. It has a Position and Move component. The Position ComponentMapper has ID 0 and the Move ComponentMapper has ID 1. This means that the component mask for entity 3 has the bits set at index 0 and 1 and all other bits are set to 0. Therefore, the component mask is showing 11 because trailing zeroes are cut off by the toString implementation of the BitArray.

In the example we can also see that entity 0 has a component mask of 0 which means it has no components.

image