I don't really blog anymore. Click here to go to my main website.

muhuk's blog

Nature, to Be Commanded, Must Be Obeyed

October 07, 2015

Is Clecs Dead?

My last commit to clecs repository was six months ago. It was nowhere near complete by then, I suppose it wouldn’t be unfair to think I have abandoned the project. I have not completely given up clecs, but I don’t have the motivation.

I have asked two questions about clecs a year ago:

  • Do you think having a game development library that is completely agnostic about rendering, sound & inputs, i.e. doesn’t provide any of these, make sense?
  • Do you think the API looks right? Is it idiomatic Clojure from a library user’s perspective?

I got very little feedback in a year’s time. Not only to these two questions, but feedback about clecs in general. I think clecs design is misunderstood. Or it sucks. But I am not yet convinced of it.

First Airplane to Land in Twin Bridges, Montana

Clecs Complements Game Engines, It Does Not Replace Them

Most frequent thing was the expectation of a fully featured game engine, graphics, sound… the works. Clecs only keeps state (world) and it formalizes state transformations (systems). Incidentally most game engines don’t handle these. So you still need some sort of game engine to make your game work. And if you are using a game engine you need to take care of things clecs do for you. Game engines and clecs, they complement to each other.

Second most frequent feedback was about the API. Why is it not functional? And, why is it not storing the state in a clojure data structure? I will address the second question later. But the first one is an interesting one. Worlds are mutable. As you add, remove components etc. the changes are reflected immediately. So far it’s nothing like functional. But systems are (supposed to be) run in parallel and within a single run a system gets to see a consistent snapshot of a world. In this sense it resembles Clojure’s persistent data structures. But in reality it is not necessarily persistent. In fact making it persistent would probably hurt performance. Consider the difference between a Java array and a persistent vector. I do not wist to hide the mutable nature of the world abstraction from the end user.

Different Kinds of Games

I think we can group games in three categories in the context of clecs. First of which I call learning projects. These are for beginners learning clecs, possibly learning game programming, perhaps they are even new to programming in general. Primary concern for this type of project is the ease of use. Defaults would do. Current reference implementation atom-world would do. I do not think clecs is quite ready for these yet, especially because documentation is next to non-existent.

Second category is high performance games. By high-performance I don’t necessarily mean high end AAA games. But games that need to go through a lot of data in a short time. At the very least, a rendering system must complete executing within 16 milliseconds in order to provide 60 frame per second frame rate. Clecs doesn’t have a storage backend optimized for this use. For small worlds atom-world runs fine, but it is effectively single threaded. To cater these types of games clecs needs a fast in-memory backend.

Before we move on to the third category. I would like to mention why atom-world is single threaded, in case it is not obvious. This should also explain why clecs doesn’t see the world as one big persistent map like other entity-component-system libraries do. Atom world keeps the world data in an atom as a map. It runs systems sequentially because retries would do nothing but hurt performance. Even if we run those systems that work on separate sets of components, there would still be retries. It would only keep the cores busy. So I see this world is an atom holding a map approach unsuitable for projects other than the learning projects. I should also mention that atom-world has always meant to be a reference with which I wanted to check more complex implementations’ correctness.

Third category is large scale games where world data wouldn’t fit to RAM, or perhaps even (a single) disk. This kind of game would require storage backend with different kind of optimizations. Perhaps it will require implementation of per entity processing of systems[1], so that the same can be run simultaneously on each shard. This category of games is no more likely than high performance games to appear in the near future. Nevertheless it is one of clecs’ goals to support their development.

Regardless of its size and performance requirements, a game project should be able to start with the atom-world and switch to a more suitable backend later. The purpose of World abstraction is to allow this.

Breaking Ground

From all this, I think clecs needs more and better backends. Specifically backends that can run systems in parallel. Groundwork is already there. Systems’ :reads and :writes sets designate components they are dealing with and using this data we can figure out which of them can be run in parallel. It is not entirely trivial since other features like intervals (between executions) and turning systems on an off will eventually be implemented and the scheduling should be able to take them into account. Currently component access checks are in atom-world code, but it shouldn’t be too hard to move them into clecs.world namespace.

Another thing sorely missing is the lack of syntactic sugar. If I was checking out clecs, I would look at the example projects. Seeing the low-level, imperative style code of systems I would decide the author doesn’t get Clojure and I would move on. I have already identified a couple of patterns that can be easily simplified via macros. Or we can take an entirely different approach and transition to a more data-driven way of writing systems[2]. Functions may be the go to means of abstraction, but they are opaque.

One thing that worries me a bit is getting data locality right, especially in in-memory storage backends. Since we know types of each attribute, I suppose the backend can use one primitive array[3] per attribute but hide it from the consumer code.

This is of course if and when I feel motivated to work on clecs again. Feel free to tell me your opinion if you are using clecs, or if you have checked it out and decided that it’s useless.

Airplane image is taken from here (CC BY-NC-SA).

[1]Similar to EntityProcessingSystems in Artemis.
[2]Like Aspects in Artemis. They are more advanced forms of :reads & :writes.
[3]With the exception of Strings.

If you have any questions, suggestions or corrections feel free to drop me a line.