# evolved.lua > Evolved ECS (Entity-Component-System) for Lua [![lua5.1][badge.lua5.1]][lua5.1] [![lua5.4][badge.lua5.4]][lua5.4] [![luajit][badge.luajit]][luajit] [![license][badge.license]][license] [badge.lua5.1]: https://img.shields.io/github/actions/workflow/status/BlackMATov/evolved.lua/.github/workflows/lua5.1.yml?label=Lua%205.1 [badge.lua5.4]: https://img.shields.io/github/actions/workflow/status/BlackMATov/evolved.lua/.github/workflows/lua5.4.yml?label=Lua%205.4 [badge.luajit]: https://img.shields.io/github/actions/workflow/status/BlackMATov/evolved.lua/.github/workflows/luajit.yml?label=LuaJIT [badge.license]: https://img.shields.io/badge/license-MIT-blue [lua5.1]: https://github.com/BlackMATov/evolved.lua/actions?query=workflow%3Alua5.1 [lua5.4]: https://github.com/BlackMATov/evolved.lua/actions?query=workflow%3Alua5.4 [luajit]: https://github.com/BlackMATov/evolved.lua/actions?query=workflow%3Aluajit [license]: https://en.wikipedia.org/wiki/MIT_License [evolved]: https://github.com/BlackMATov/evolved.lua - [Introduction](#introduction) - [Performance](#performance) - [Simplicity](#simplicity) - [Flexibility](#flexibility) - [Installation](#installation) - [Quick Start](#quick-start) - [Overview](#overview) - [Identifiers](#identifiers) - [Entities, Fragments, and Components](#entities-fragments-and-components) - [Traits](#traits) - [Singletons](#singletons) - [Chunks](#chunks) - [Entity Location](#entity-location) - [Structural Changes](#structural-changes) - [Spawning Entities](#spawning-entities) - [Entity Builders](#entity-builders) - [Access Operations](#access-operations) - [Iterating Over Fragments](#iterating-over-fragments) - [Modifying Operations](#modifying-operations) - [Debug Mode](#debug-mode) - [Queries](#queries) - [Deferred Operations](#deferred-operations) - [Batch Operations](#batch-operations) - [Systems](#systems) - [Predefined Traits](#predefined-traits) - [Fragment Tags](#fragment-tags) - [Fragment Hooks](#fragment-hooks) - [Unique Fragments](#unique-fragments) - [Explicit Fragments](#explicit-fragments) - [Internal Fragments](#internal-fragments) - [Shared Components](#shared-components) - [Fragment Requirements](#fragment-requirements) - [Destruction Policies](#destruction-policies) - [Cheat Sheet](#cheat-sheet) - [Aliases](#aliases) - [Predefs](#predefs) - [Functions](#functions) - [Classes](#classes) - [Chunk](#chunk) - [Builder](#builder) - [Changelog](#changelog) - [v1.5.0](#v150) - [v1.4.0](#v140) - [v1.3.0](#v130) - [v1.2.0](#v120) - [v1.1.0](#v110) - [v1.0.0](#v100) - [License](#license) ## Introduction `evolved.lua` is a fast and flexible ECS (Entity-Component-System) library for Lua. It is designed to be simple and easy to use, while providing all the features needed to create complex systems with blazing performance. Before we start exploring the library, let's take a look at the main advantages of using `evolved.lua`: ### Performance This library is designed to be fast. Many techniques are employed to achieve this. It uses an archetype-based approach to store entities and their components. Components are stored in contiguous arrays in a SoA (Structure of Arrays) manner, which allows for fast iteration and processing. Chunks are used to group entities with the same set of components together, enabling efficient filtering through queries. Additionally, all operations are designed to minimize GC (Garbage Collector) pressure and avoid unnecessary allocations. I have tried to take into account all the performance pitfalls of vanilla Lua and LuaJIT. Not all the optimizations I want to implement are done yet, but I will be working on them. However, I can already say that the library is fast enough for most use cases. ### Simplicity I have tried to keep the [API](#cheat-sheet) as simple and intuitive as possible. I also keep the number of functions under control. All the functions are self-explanatory and easy to use. After reading the [Overview](#overview) section, you should be able to use the library without any problems. And yes, the library has some unusual concepts at its core, but once you get the hang of it, you will find it's very easy to use. ### Flexibility `evolved.lua` is not just about keeping components in entities. It's a full-fledged ECS library that allows you to create complex [systems](#systems) and processes. You can create [queries](#queries) with filters, use [deferred operations](#deferred-operations), and [batch operations](#batch-operations). You can also create systems that process entities in a specific order. The library is designed to be flexible and extensible, so you can easily add your own features and functionality. Features like [fragment hooks](#fragment-hooks) allow you to manage your components in a more flexible way, synchronizing them with external systems or libraries. The library also provides syntactic sugar like the [entity builder](#entity-builders) for creating entities, fragments, and systems to make your life easier. On the other hand, `evolved.lua` tries to be minimalistic and does not provide features that can be implemented outside the library. I'm trying to find a balance between minimalism and the number of possibilities, which forces me to make flexible decisions in the library's design. I hope you will find this balance acceptable. ## Installation `evolved.lua` is a single-file pure Lua library and does not require any external dependencies. It is designed to work with [Lua 5.1](https://www.lua.org/) and later, [LuaJIT](https://luajit.org/), and [Luau](https://luau.org/) (Roblox). All you need to start using the library is the [evolved.lua](./evolved.lua) source file. You can download it from the [releases](https://github.com/BlackMATov/evolved.lua/releases) page or clone the [repository](https://github.com/BlackMATov/evolved.lua) and copy the file to your project. If you are using [LuaRocks](https://luarocks.org/), you can install the library using the following command: ```bash luarocks install evolved.lua ``` ## Quick Start To get started with `evolved.lua`, read the [Overview](#overview) section to understand the basic concepts and how to use the library. After that, check the [Example](example), which demonstrate complex usage of the library. Finally, refer to the [Cheat Sheet](#cheat-sheet) for a quick reference of all the functions and classes provided by the library. ## Overview The library is designed to be simple and highly performant. It uses an archetype-based approach to store entities and their components. This allows you to filter and process your entities very efficiently, especially when you have many of them. If you are familiar with the ECS (Entity-Component-System) pattern, you will feel right at home. If not, I highly recommend reading about it first. Here is a good starting point: [Entity Component System FAQ](https://github.com/SanderMertens/ecs-faq). Let's get started! :godmode: ### Identifiers An identifier is a packed 40-bit integer. The first 20 bits represent the index, and the last 20 bits represent the version. To create a new identifier, use the [`evolved.id`](#evolvedid) function. ```lua ---@param count? integer ---@return evolved.id ... ids function evolved.id(count) end ``` The `count` parameter is optional and defaults to `1`. The function returns one or more identifiers depending on the `count` parameter. The maximum number of alive identifiers is `2^20-1` (1048575). After that, the function will throw an error: `| evolved.lua | id index overflow`. Identifiers can be recycled. When an identifier is no longer needed, use the [`evolved.destroy`](#evolveddestroy) function to destroy it. This will free the identifier for reuse. ```lua ---@param ... evolved.id ids function evolved.destroy(...) end ``` The [`evolved.destroy`](#evolveddestroy) function takes one or more identifiers as arguments. Destroyed identifiers will be added to the recycler free list. It is safe to call [`evolved.destroy`](#evolveddestroy) on identifiers that are not alive; the function will simply ignore them. After destroying an identifier, it can be reused by calling the [`evolved.id`](#evolvedid) function again. The new identifier will have the same index as the destroyed one, but a different version. The version is incremented each time an identifier is destroyed. This mechanism allows us to reuse indices and to know whether an identifier is alive or not. The set of [`evolved.alive`](#evolvedalive) functions can be used to check whether identifiers are alive. ```lua ---@param id evolved.id ---@return boolean function evolved.alive(id) end ---@param ... evolved.id ids ---@return boolean function evolved.alive_all(...) end ---@param ... evolved.id ids ---@return boolean function evolved.alive_any(...) end ``` Sometimes (for debugging purposes, for example), it is necessary to extract the index and version from an identifier or to pack them back into an identifier. The [`evolved.pack`](#evolvedpack) and [`evolved.unpack`](#evolvedunpack) functions can be used for this purpose. ```lua ---@param primary integer ---@param secondary integer ---@return evolved.id id ---@nodiscard function evolved.pack(primary, secondary) end ---@param id evolved.id ---@return integer primary ---@return integer secondary ---@nodiscard function evolved.unpack(id) end ``` Here is a short example of how to use identifiers: ```lua local evolved = require 'evolved' local id = evolved.id() -- create a new identifier assert(evolved.alive(id)) -- check that the identifier is alive local index, version = evolved.unpack(id) -- unpack the identifier assert(evolved.pack(index, version) == id) -- pack it back evolved.destroy(id) -- destroy the identifier assert(not evolved.alive(id)) -- check that the identifier is not alive now ``` ### Entities, Fragments, and Components First, you need to understand that entities and fragments are just identifiers. The difference between them is purely semantic. Entities are used to represent objects in the world, while fragments are used to represent types of components that can be attached to entities. Components, on the other hand, are any data that is attached to entities through fragments. ```lua ---@alias evolved.entity evolved.id ---@alias evolved.fragment evolved.id ---@alias evolved.component any ``` Here is a simple example of how to attach a component to an entity: ```lua local evolved = require 'evolved' local entity, fragment = evolved.id(2) evolved.set(entity, fragment, 100) assert(evolved.get(entity, fragment) == 100) ``` I know it's not very clear yet, but don't worry, we'll get there. In the next example, I'm going to name the entity and fragment, so it will be easier to understand what's going on here. ```lua local evolved = require 'evolved' local player = evolved.id() local health = evolved.id() local stamina = evolved.id() evolved.set(player, health, 100) evolved.set(player, stamina, 50) assert(evolved.get(player, health) == 100) assert(evolved.get(player, stamina) == 50) ``` We created an entity called `player` and two fragments called `health` and `stamina`. We attached the components `100` and `50` to the entity through these fragments. After that, we can retrieve the components using the [`evolved.get`](#evolvedget) function. We'll cover the [`evolved.set`](#evolvedset) and [`evolved.get`](#evolvedget) functions in more detail later in the section about [modifying operations](#modifying-operations). For now, let's just say that they are used to set and get components from entities through fragments. The main thing to understand here is that you can attach any data to any identifier by using other identifiers. #### Traits Since fragments are just identifiers, you can use them as entities too! Fragments of fragments are usually called `traits`. This is very useful, for example, for marking fragments with some metadata. ```lua local evolved = require 'evolved' local serializable = evolved.id() local position = evolved.id() evolved.set(position, serializable, true) local velocity = evolved.id() evolved.set(velocity, serializable, true) local player = evolved.id() evolved.set(player, position, {x = 0, y = 0}) evolved.set(player, velocity, {x = 0, y = 0}) ``` In this example, we create a trait called `serializable` and mark the fragments `position` and `velocity` as serializable. After that, you can write a function that will serialize entities, and this function will serialize only fragments that are marked as serializable. This is a very powerful feature of the library, and it allows you to create very flexible systems. #### Singletons Fragments can even be attached to themselves; this is called a singleton. Use this when you want to store some data without having a separate entity. For example, you can use it to store global data, like the game state or the current level. ```lua local evolved = require 'evolved' local gravity = evolved.id() evolved.set(gravity, gravity, 10) assert(evolved.get(gravity, gravity) == 10) ``` ### Chunks The next thing we need to understand is that all non-empty entities are stored in chunks. Chunks are just tables that store entities and their components together. Each unique combination of fragments is stored in a separate chunk. This means that if you have two entities with `health` and `stamina` fragments, they will be stored in the `` chunk. If you have another entity with `health`, `stamina`, and `mana` fragments, it will be stored in the `` chunk. This is very useful for performance reasons, as it allows us to store entities with the same fragments together, making it easier to iterate, filter, and process them. ```lua local evolved = require 'evolved' local health, stamina, mana = evolved.id(3) local entity1 = evolved.id() evolved.set(entity1, health, 100) evolved.set(entity1, stamina, 50) local entity2 = evolved.id() evolved.set(entity2, health, 75) evolved.set(entity2, stamina, 40) local entity3 = evolved.id() evolved.set(entity3, health, 50) evolved.set(entity3, stamina, 30) evolved.set(entity3, mana, 20) ``` Here is what the chunks will look like after the code above has executed: | chunk | health | stamina | | ------- | :----: | :-----: | | entity1 | 100 | 50 | | entity2 | 75 | 40 | | chunk | health | stamina | mana | | ------- | :----: | :-----: | :---: | | entity3 | 50 | 30 | 20 | Usually, you don't need to operate on chunks directly, but you can use the [`evolved.chunk`](#evolvedchunk) function to get the specific chunk. ```lua ---@param fragment evolved.fragment ---@param ... evolved.fragment fragments ---@return evolved.chunk chunk function evolved.chunk(fragment, ...) end ``` The [`evolved.chunk`](#evolvedchunk) function takes one or more fragments as arguments and returns the chunk for this combination. After that, you can use the chunk's methods to retrieve their entities, fragments, and components. ```lua ---@return evolved.entity[] entity_list ---@return integer entity_count function chunk_mt:entities() end ---@return evolved.fragment[] fragment_list ---@return integer fragment_count function chunk_mt:fragments() end ---@param ... evolved.fragment fragments ---@return evolved.storage ... storages function chunk_mt:components(...) ``` Full example: ```lua local evolved = require 'evolved' local health, stamina, mana = evolved.id(3) local entity1 = evolved.id() evolved.set(entity1, health, 100) evolved.set(entity1, stamina, 50) local entity2 = evolved.id() evolved.set(entity2, health, 75) evolved.set(entity2, stamina, 40) local entity3 = evolved.id() evolved.set(entity3, health, 50) evolved.set(entity3, stamina, 30) evolved.set(entity3, mana, 20) -- get (or create if it doesn't exist) the chunk local chunk = evolved.chunk(health, stamina) -- get the list of entities in the chunk and the number of them local entity_list, entity_count = chunk:entities() -- get the columns of components in the chunk local health_components = chunk:components(health) local stamina_components = chunk:components(stamina) for i = 1, entity_count do local entity = entity_list[i] local entity_health = health_components[i] local entity_stamina = stamina_components[i] -- do something with the entity and its components print(string.format( 'Entity: %d, Health: %d, Stamina: %d', entity, entity_health, entity_stamina)) end -- Expected output: -- Entity: 1048602, Health: 100, Stamina: 50 -- Entity: 1048603, Health: 75, Stamina: 40 ``` #### Entity Location Sometimes it is useful to know which chunk a specific entity is in and its position within that chunk. The [`evolved.locate`](#evolvedlocate) function provides this information. ```lua ---@param entity evolved.entity ---@return evolved.chunk? chunk ---@return integer place function evolved.locate(entity) end ``` This function takes an entity and returns the chunk that contains it and the entity’s position (index) within that chunk. If the entity is not alive or is empty, the function returns `nil` for the chunk and `0` for the place. This is low-level functionality that you will rarely need. However, it can be useful in specific cases. For example, when you need to modify an entity’s component directly to avoid unnecessary [Deferred Operations](#deferred-operations) or [Fragment Hooks](#fragment-hooks), instead of using a higher-level function like [`evolved.set`](#evolvedset). ```lua local evolved = require 'evolved' local health, stamina = evolved.id(2) local player = evolved.id() evolved.set(player, health, 100) evolved.set(player, stamina, 50) local player_chunk, player_place = evolved.locate(player) local health_components = player_chunk:components(health) local stamina_components = player_chunk:components(stamina) health_components[player_place] = 75 stamina_components[player_place] = 42 ``` > [!WARNING] > Do not use [`evolved.locate`](#evolvedlocate) to manipulate components directly unless you fully understand what you are doing. Because it is low-level functionality, incorrect use can lead to inconsistencies, as it bypasses the library’s safety checks and hook mechanisms. Use it only when you are certain it is safe and necessary. ### Structural Changes Every time we insert or remove a fragment from an entity, the entity will be migrated to a new chunk. This is done automatically by the library, of course. However, you should be aware of this because it can affect performance, especially if you have many fragments on the entity. This is called a `structural change`. You should try to avoid structural changes, especially in performance-critical code. For example, you can spawn entities with all the fragments they will ever need and avoid changing them during the entity's lifetime. Overriding existing components is not a structural change, so you can do it freely. #### Spawning Entities ```lua ---@param components? table ---@return evolved.entity function evolved.spawn(components) end ---@param prefab evolved.entity ---@param components? table ---@return evolved.entity function evolved.clone(prefab, components) end ``` The [`evolved.spawn`](#evolvedspawn) function allows you to spawn an entity with all the necessary fragments. It takes a table of components as an argument, where the keys are fragments and the values are components. By the way, you don't need to create this `components` table every time; consider using a predefined table for maximum performance. You can also use the [`evolved.clone`](#evolvedclone) function to clone an existing entity. This is useful for creating entities with the same fragments as an existing entity but with different components. ```lua local evolved = require 'evolved' local health, stamina = evolved.id(2) -- spawn an entity with all the necessary fragments local enemy1 = evolved.spawn { [health] = 100, [stamina] = 50, } -- spawn another entity with the same fragments, -- but with a different component for some of them local enemy2 = evolved.clone(enemy1, { [health] = 50, }) -- there are no structural changes here, -- we just override existing components evolved.set(enemy1, health, 75) evolved.set(enemy1, stamina, 42) ``` #### Entity Builders Another way to avoid structural changes when spawning entities is to use the [`evolved.builder`](#evolvedbuilder) fluent interface. The [`evolved.builder`](#evolvedbuilder) function returns a builder object that allows you to spawn entities with a specific set of fragments and components without the necessity of setting them one by one with structural changes for each change. ```lua local evolved = require 'evolved' local health, stamina = evolved.id(2) local enemy = evolved.builder() :set(health, 100) :set(stamina, 50) :build() ``` Builders can be reused, so you can create a builder with a specific set of fragments and components and then use it to spawn multiple entities with the same fragments and components. ### Access Operations The library provides all the necessary functions to access entities and their components. I'm not going to cover all the accessor functions here, because they are pretty straightforward and self-explanatory. You can check the [API Reference](#api-reference) for all of them. Here are some of the most important ones: ```lua ---@param entity evolved.entity ---@return boolean function evolved.alive(entity) end ---@param entity evolved.entity ---@return boolean function evolved.empty(entity) end ---@param entity evolved.entity ---@param fragment evolved.fragment function evolved.has(entity, fragment) end ---@param entity evolved.entity ---@param ... evolved.fragment fragments ---@return evolved.component ... components function evolved.get(entity, ...) end ``` The [`evolved.alive`](#evolvedalive) function checks whether an entity is alive. The [`evolved.empty`](#evolvedempty) function checks whether an entity is empty (has no fragments). The [`evolved.has`](#evolvedhas) function checks whether an entity has a specific fragment. The [`evolved.get`](#evolvedget) function retrieves the components of an entity for the specified fragments. If the entity doesn't have some of the fragments or if the fragments are marked with the [`evolved.TAG`](#evolvedtag), the function will return `nil` for them. All of these functions can be safely called on non-alive entities and non-alive fragments. Also, they do not cause any structural changes, because they do not modify anything. ### Iterating Over Fragments Sometimes, you may need to iterate over all fragments attached to an entity. You can use the [`evolved.each`](#evolvedeach) function for this purpose. ```lua local evolved = require 'evolved' local health = evolved.builder() :name('health') :build() local stamina = evolved.builder() :name('stamina') :build() local player = evolved.builder() :set(health, 100) :set(stamina, 50) :build() for fragment, component in evolved.each(player) do print(string.format('Fragment (%s) has value %d', evolved.name(fragment), component)) end ``` > [!NOTE] > [Structural changes](#structural-changes) are not allowed during iteration. If you want to spawn new entities or insert/remove fragments while iterating, defer these operations until the iteration is complete. See the [Deferred Operations](#deferred-operations) section for more details. ### Modifying Operations The library provides a classic set of functions for modifying entities. These functions are used to insert, override, and remove fragments from entities. ```lua ---@param entity evolved.entity ---@param fragment evolved.fragment ---@param component evolved.component function evolved.set(entity, fragment, component) end ---@param entity evolved.entity ---@param ... evolved.fragment fragments function evolved.remove(entity, ...) ---@param ... evolved.entity entities function evolved.clear(...) ---@param ... evolved.entity entities function evolved.destroy(...) ``` The [`evolved.set`](#evolvedset) function is used to set a component on an entity. If the entity doesn't have this fragment, it will be inserted, with causing a structural change, of course. If the entity already has the fragment, the component will be overridden. The function should not be called on non-alive entities, because it is not possible to set any component on a destroyed entity, ignoring this can lead to errors. The [Debug Mode](#debug-mode) can be used to check this kind of error. Use the [`evolved.remove`](#evolvedremove) function to remove fragments from an entity. If the entity doesn't have some of the fragments, they will be ignored. When one or more fragments are removed from an entity, the entity will be migrated to a new chunk, which is a structural change. When you want to remove more than one fragment, pass all of them as arguments. Do not remove fragments one by one, as this will cause a structural change for each fragment. The [`evolved.remove`](#evolvedremove) function will ignore non-alive entities, because post-conditions are satisfied (destroyed entities do not have any fragments, including those that we want to remove). To remove all fragments from an entity, use the [`evolved.clear`](#evolvedclear) function. This function will remove all fragments at once, causing only one structural change. The [`evolved.clear`](#evolvedclear) function does not destroy the entity, it just removes all fragments from it. The entity after this operation will be empty, but it will still be alive. You can use this function to clear more than one entity at once, passing them as arguments. The function will ignore empty and non-alive entities. To destroy an entity, use the [`evolved.destroy`](#evolveddestroy) function. This function will remove all fragments from the entity and free the identifier of the entity for reuse. The [`evolved.destroy`](#evolveddestroy) function will ignore non-alive entities. To destroy more than one entity, pass them as arguments. ### Debug Mode The library has a debug mode that can be enabled by the [`evolved.debug_mode`](#evolveddebug_mode) function. When the debug mode is enabled, the library will check for incorrect usages of the API and throw errors when they are detected. This is very useful for debugging and development, but it can slow down performance a bit. ```lua ---@param yesno boolean function evolved.debug_mode(yesno) end ``` The debug mode is disabled by default, so you need to enable it manually. I strongly recommend doing this in the development environment. You can even leave it enabled in production, but only if you are sure the performance is acceptable for your case. ```lua local evolved = require 'evolved' evolved.debug_mode(true) local entity = evolved.id() local fragment = evolved.id() evolved.destroy(fragment) -- try to use the destroyed fragment evolved.set(entity, fragment, 42) -- [error] | evolved.lua | the fragment ($1048599#23:1) is not alive and cannot be used ``` ### Queries One of the most important features of any ECS library is the ability to process entities by filters or queries. `evolved.lua` provides a simple and efficient way to do this. First, you need to create a query that describes which entities you want to process. You can specify fragments you want to include, and fragments you want to exclude. Queries are just identifiers with a special predefined fragments: [`evolved.INCLUDES`](#evolvedincludes) and [`evolved.EXCLUDES`](#evolvedexcludes). These fragments expect a list of fragments as their components. ```lua local evolved = require 'evolved' local health, poisoned, resistant = evolved.id(3) local query = evolved.id() evolved.set(query, evolved.INCLUDES, { health, poisoned }) evolved.set(query, evolved.EXCLUDES, { resistant }) ``` The builder interface can be used to create queries too. It is more convenient to use, because the builder has special methods for including and excluding fragments. Here is a simple example of this: ```lua local query = evolved.builder() :include(health, poisoned) :exclude(resistant) :build() ``` We don't have to set both [`evolved.INCLUDES`](#evolvedincludes) and [`evolved.EXCLUDES`](#evolvedexcludes) fragments, we can even do it without filters at all, then the query will match all chunks in the world. After the query is created, we are ready to process our filtered by this query entities. You can do this by using the [`evolved.execute`](#evolvedexecute) function. This function takes a query as an argument and returns an iterator that can be used to iterate over all matching with the query chunks. ```lua ---@param query evolved.query ---@return evolved.execute_iterator iterator ---@return evolved.execute_state? iterator_state function evolved.execute(query) end ``` ```lua for chunk, entity_list, entity_count in evolved.execute(query) do ---@type number[] local health_components = chunk:components(health) for i = 1, entity_count do health_components[i] = math.max( health_components[i] - 1, 0) end end ``` As you can see, `evolved.execute_iterator` returns a chunk, a list of entities in the chunk, and the number of entities in this chunk. We [already know](#chunks) how to use chunks, so we can use the chunk's methods to retrieve the components of the entities in the chunk, change them, and so on. > [!NOTE] > But I haven't mentioned one important thing yet: [structural changes](#structural-changes) are not allowed during any iteration over chunks. This means that you cannot insert or remove fragments from entities while iterating. Also, you cannot destroy or spawn entities because this will cause structural changes too. This is done to avoid inconsistencies in the iteration process. If we allowed structural changes here, we might skip some entities during iteration, or process the same entity multiple times. The [debug mode](#debug-mode) can catch this kind of error. #### Deferred Operations Now we know that structural changes are not allowed during iteration, but what if we want to make some structural changes after the iteration is finished? For example, we might want to remove some fragments from entities after we have processed them, or we might want to spawn new entities while processing existing ones. To do all of this, we can use deferred operations. ```lua ---@return boolean started function evolved.defer() end ---@return boolean committed function evolved.commit() end ---@return boolean cancelled function evolved.cancel() end ``` The [`evolved.defer`](#evolveddefer) function starts a deferred scope. This means that all changes made inside the scope will be queued and applied after leaving the scope. The [`evolved.commit`](#evolvedcommit) function closes the last deferred scope and applies all queued changes. These functions can be nested, so you can start a new deferred scope inside an existing one. The [`evolved.commit`](#evolvedcommit) function will apply all queued changes only when the last deferred scope is closed. ```lua local evolved = require 'evolved' local health, poisoned = evolved.id(2) local player = evolved.builder() :set(health, 100) :set(poisoned, true) :build() -- start a deferred scope evolved.defer() -- this removal will be queued, not applied immediately evolved.remove(player, poisoned) -- the player still has the poisoned fragment inside the deferred scope assert(evolved.has(player, poisoned)) -- commit the deferred operations evolved.commit() -- now the poisoned fragment is removed assert(not evolved.has(player, poisoned)) ``` The [`evolved.cancel`](#evolvedcancel) function can be used to cancel all queued changes in the current deferred scope. This is useful if you want to discard all changes made inside the scope and revert to the previous state on an error or some other condition. ```lua local evolved = require 'evolved' local health, poisoned = evolved.id(2) local player = evolved.builder() :set(health, 100) :set(poisoned, true) :build() -- start a deferred scope evolved.defer() -- this removal will be queued, not applied immediately evolved.remove(player, poisoned) -- the player still has the poisoned fragment inside the deferred scope assert(evolved.has(player, poisoned)) -- cancel the deferred operations evolved.cancel() -- the poisoned fragment is still there assert(evolved.has(player, poisoned)) ``` #### Batch Operations The library provides a set of functions for batch operations. These functions are used to perform modifying operations on multiple chunks at once. This is very useful for performance reasons. ```lua ---@param query evolved.query ---@param fragment evolved.fragment ---@param component evolved.component function evolved.batch_set(query, fragment, component) end ---@param query evolved.query ---@param ... evolved.fragment fragments function evolved.batch_remove(query, ...) end ---@param ... evolved.query queries function evolved.batch_clear(...) end ---@param ... evolved.query queries function evolved.batch_destroy(...) end ``` These functions are similar to the common [modifying operations](#modifying-operations), but they take a query as an argument instead of an entity. Here is a classic example that provides a huge performance boost when applied. ```lua local evolved = require 'evolved' local destroying_mark = evolved.id() local destroying_mark_query = evolved.builder() :include(destroying_mark) :build() -- destroy all entities with the destroying_mark fragment evolved.batch_destroy(destroying_mark_query) ``` > [!TIP] > You should always prefer batch operations over common modifying operations when you need to perform simple operations like destroying or removing fragments from multiple entities at once. Instead of applying the operation to each entity one by one, batch operations will apply the operation chunk by chunk. In all other respects, batch operations behave the same way as the common modifying operations that we have already covered. Of course, they can also be used with [deferred operations](#deferred-operations). ### Systems Usually, we want to organize our processing of entities into systems that will be executed in a specific order. The library has a way to do this using special [`evolved.QUERY`](#evolvedquery) and [`evolved.EXECUTE`](#evolvedexecute) fragments that are used to specify the system's queries and execution callbacks. And yes, systems are just entities with special fragments. ```lua local evolved = require 'evolved' local health, max_health = evolved.id(2) local query = evolved.builder() :include(health, max_health) :build() local system = evolved.builder() :query(query) :execute(function(chunk, entity_list, entity_count) local health_components = chunk:components(health) local max_health_components = chunk:components(max_health) for i = 1, entity_count do health_components[i] = math.min( health_components[i] + 1, max_health_components[i]) end end):build() ``` The [`evolved.process`](#evolvedprocess) function is used to process systems. It takes systems as arguments and executes them in the order they were passed. ```lua ---@param ... evolved.system systems function evolved.process(...) end ``` If you don't specify a query for the system, the system itself will be treated as a query. This means the system can contain `evolved.INCLUDES` and `evolved.EXCLUDES` fragments, and it will be processed according to them. This is useful for creating systems with unique queries that don't need to be reused in other systems. ```lua local evolved = require 'evolved' local health = evolved.id() local system = evolved.builder() :include(health) :execute(function(chunk, entity_list, entity_count) local health_components = chunk:components(health) for i = 1, entity_count do health_components[i] = math.max( health_components[i] - 1, 0) end end):build() evolved.process(system) ``` To group systems together, you can use the [`evolved.GROUP`](#evolvedgroup) fragment. Systems with a specified group will be processed when you call the [`evolved.process`](#evolvedprocess) function with this group. For example, you can group all physics systems together and process them in one [`evolved.process`](#evolvedprocess) call. ```lua local evolved = require 'evolved' local gravity_x = 0 local gravity_y = -9.81 local position_x, position_y = evolved.id(2) local velocity_x, velocity_y = evolved.id(2) local physical_body_query = evolved.builder() :include(position_x, position_y) :include(velocity_x, velocity_y) :build() local physics_group = evolved.id() evolved.builder() :group(physics_group) :query(physical_body_query) :execute(function(chunk, entity_list, entity_count) local vx = chunk:components(velocity_x) local vy = chunk:components(velocity_y) for i = 1, entity_count do vx[i] = vx[i] + gravity_x vy[i] = vy[i] + gravity_y end end):build() evolved.builder() :group(physics_group) :query(physical_body_query) :execute(function(chunk, entity_list, entity_count) local px = chunk:components(position_x) local py = chunk:components(position_y) local vx = chunk:components(velocity_x) local vy = chunk:components(velocity_y) for i = 1, entity_count do px[i] = px[i] + vx[i] py[i] = py[i] + vy[i] end end):build() evolved.process(physics_group) ``` Systems and groups also can have the [`evolved.PROLOGUE`](#evolvedprologue) and [`evolved.EPILOGUE`](#evolvedepilogue) fragments. These fragments are used to specify callbacks that will be executed before and after the system or group is processed. This is useful for setting up and tearing down systems or groups, or for performing some additional processing before or after the main processing. ```lua local evolved = require 'evolved' local system = evolved.builder() :prologue(function() print('Prologue') end) :epilogue(function() print('Epilogue') end) :build() evolved.process(system) ``` The prologue and epilogue fragments do not require an explicit query. They will be executed before and after the system is processed, regardless of the query. > [!NOTE] > And one more thing about systems. Execution callbacks are called in the [deferred scope](#deferred-operations), which means that all modifying operations inside the callback will be queued and applied after the system has processed all chunks. But prologue and epilogue callbacks are not called in the deferred scope, so all modifying operations inside them will be applied immediately. This is done to avoid confusion and to make it clear that prologue and epilogue callbacks are not part of the chunk processing. ### Predefined Traits #### Fragment Tags Sometimes you want to have a fragment without a component. For example, you might want to have some marks that will be used to mark entities for processing. Fragments without components are called `tags`. Such fragments take up less memory, because they do not require any components to be stored. Migration of entities with tags is faster, because the library does not need to migrate components, only the tags themselves. To create a tag, mark the fragment with the [`evolved.TAG`](#evolvedtag) fragment. ```lua local evolved = require 'evolved' local player_tag = evolved.id() evolved.set(player_tag, evolved.TAG) local player = evolved.id() evolved.set(player, player_tag) -- player has the player_tag fragment assert(evolved.has(player, player_tag)) -- player_tag is a tag, so it doesn't have a component assert(evolved.get(player, player_tag) == nil) ``` #### Fragment Hooks The library provides a way to execute callbacks when fragments are set, assigned, inserted, or removed from entities. This is done using special fragments: [`evolved.ON_SET`](#evolvedon_set), [`evolved.ON_ASSIGN`](#evolvedon_assign), [`evolved.ON_INSERT`](#evolvedon_insert), and [`evolved.ON_REMOVE`](#evolvedon_remove). These fragments are used to specify the callbacks that will be executed when the corresponding operation is performed on the fragment. ```lua local evolved = require 'evolved' local health = evolved.builder() :on_set(function(entity, fragment, component) print('health set to ' .. component) end):build() local player = evolved.id() evolved.set(player, health, 100) -- prints "health set to 100" evolved.set(player, health, 200) -- prints "health set to 200" ``` Use [`evolved.ON_SET`](#evolvedon_set) for callbacks on fragment insert or override, [`evolved.ON_ASSIGN`](#evolvedon_assign) for overrides, and [`evolved.ON_INSERT`](#evolvedon_insert)/[`evolved.ON_REMOVE`](#evolvedon_remove) for insertions or removals. > [!NOTE] > Because fragments marked with [`evolved.TAG`](#evolvedtag) (also called [Fragment Tags](#fragment-tags)) have no components, their [`evolved.ON_SET`](#evolvedon_set) hooks are invoked only when the tag is inserted, not when it is overridden, as there is nothing to override. Their [`evolved.ON_ASSIGN`](#evolvedon_assign) hooks are never invoked for such tags for the same reason. #### Unique Fragments Some fragments should not be cloned when cloning entities. For example, `evolved.lua` has a special fragment called `evolved.PREFAB`, which marks entities used as sources for cloning. This fragment should not be present on the cloned entities. To prevent a fragment from being cloned, mark it as unique using the [`evolved.UNIQUE`](#evolvedunique) fragment trait. This ensures the fragment will not be copied when cloning entities. ```lua local evolved = require 'evolved' local health, stamina = evolved.id(2) local enemy_prefab = evolved.builder() :prefab() :set(health, 100) :set(stamina, 50) :build() local enemy_clone = evolved.clone(enemy_prefab) -- the enemy_prefab has the evolved.PREFAB fragment assert(evolved.has(enemy_prefab, evolved.PREFAB)) -- but the enemy_clone doesn't have it, because it is marked as unique assert(not evolved.has(enemy_clone, evolved.PREFAB)) ``` #### Explicit Fragments In some cases, you might want to hide chunks with certain fragments from queries by default. For example, the library has a special fragment called [`evolved.DISABLED`](#evolveddisabled) that behaves this way. This fragment is marked with the [`evolved.EXPLICIT`](#evolvedexplicit) fragment trait, which means it will be hidden from queries unless you explicitly include it. This is useful for fragments that are used for internal or editor purposes and should not be exposed to queries by default. Additionally, the [`evolved.PREFAB`](#evolvedprefab) fragment is also marked with the [`evolved.EXPLICIT`](#evolvedexplicit) fragment trait. This prevents prefabs from being processed in queries at runtime. Prefabs are used only for cloning entities, so they should not be processed by default. ```lua local evolved = require 'evolved' local enemy_tag = evolved.builder() :tag() :build() local only_enabled_enemies = evolved.builder() :include(enemy_tag) :build() local all_enemies_including_disabled = evolved.builder() :include(enemy_tag) :include(evolved.DISABLED) :build() ``` #### Internal Fragments All predefined fragments provided by the library are marked as internal with the [`evolved.INTERNAL`](#evolvedinternal) trait. Because [`evolved.INTERNAL`](#evolvedinternal) is itself marked as [`evolved.EXPLICIT`](#evolvedexplicit), these fragments are hidden from queries by default. This prevents accidental use of internal fragments in user code and helps distinguish them from user-defined fragments. #### Shared Components Often, we want to store components as tables, and by default, these tables will be shared between entities. This means that if you modify the table in one entity, it will be modified in all entities that share this table. Sometimes this is what we want. For example, when we want to share a configuration or some resource between entities. But in other cases, we want each entity to have its own copy of the table. For example, if we want to store the position of an entity as a table, we don't want to share this table with other entities. Yes, we can copy the table manually, but the library provides a little bit of syntactic sugar for this. ```lua local evolved = require 'evolved' local initial_position = { x = 0, y = 0 } local position = evolved.id() local enemy1 = evolved.builder() :set(position, initial_position) :build() local enemy2 = evolved.builder() :set(position, initial_position) :build() -- the enemy1 and enemy2 share the same table, -- and that's definitely not what we want in this case assert(evolved.get(enemy1, position) == evolved.get(enemy2, position)) ``` To avoid this, `evolved.lua` provides a fragment trait called [`evolved.DUPLICATE`](#evolvedduplicate). This trait expects a function that will be called when the component of this fragment is set. The function should return a new table to be used as the component for the entity. This way, each entity will have its own copy of the table, and modifying one entity will not affect the others. To make this example clearer, we will also use the [`evolved.DEFAULT`](#evolveddefault) fragment trait. This trait is used to specify a default value for the component. The default value will be used when the component is not set explicitly. ```lua local evolved = require 'evolved' local function vector2(x, y) return { x = x, y = y } end local function vector2_duplicate(v) return { x = v.x, y = v.y } end local position = evolved.builder() :default(vector2(0, 0)) :duplicate(vector2_duplicate) :build() local enemy1 = evolved.builder() :set(position) :build() local enemy2 = evolved.builder() :set(position) :build() -- the enemy1 and enemy2 have different tables now assert(evolved.get(enemy1, position) ~= evolved.get(enemy2, position)) ``` #### Fragment Requirements Sometimes you want to add additional fragments to an entity when it receives a specific fragment. For example, you might want to add `position` and `velocity` fragments when an entity is given a `physical` fragment. This can be done using the [`evolved.REQUIRES`](#evolvedrequires) fragment trait. This trait expects a list of fragments that will be added to the entity when the fragment is inserted. ```lua local evolved = require 'evolved' local position = evolved.builder() :default(vector2(0, 0)) :duplicate(vector2_duplicate) :build() local velocity = evolved.builder() :default(vector2(0, 0)) :duplicate(vector2_duplicate) :build() local physical = evolved.builder() :tag() :require(position, velocity) :build() local enemy = evolved.builder() :set(physical) :build() assert(evolved.has_all(enemy, position, velocity)) ``` #### Destruction Policies Typically, fragments remain alive for the entire lifetime of the program. However, in some cases, you might want to destroy fragments when they are no longer needed. For example, you can use some runtime entities as fragments for other entities. In this case, you might want to destroy such fragments even while they are still attached to other entities. Since entities cannot have destroyed fragments, a destruction policy must be applied to resolve this. By default, the library will remove the destroyed fragment from all entities that have it. ```lua local evolved = require 'evolved' local world = evolved.builder() :tag() :build() local entity = evolved.builder() :set(world) :build() -- destroy the world fragment that is attached to the entity evolved.destroy(world) -- the entity is still alive, but it no longer has the world fragment assert(evolved.alive(entity) and not evolved.has(entity, world)) ``` The default behavior works well in most cases, but you can change it by using the [`evolved.DESTRUCTION_POLICY`](#evolveddestruction_policy) fragment. This fragment expects one of the following predefined identifiers: - [`evolved.DESTRUCTION_POLICY_DESTROY_ENTITY`](#evolveddestruction_policy_destroy_entity) will destroy any entity that has the destroyed fragment. This is useful for cases like the one above, where you want to destroy all entities when their world is destroyed. - [`evolved.DESTRUCTION_POLICY_REMOVE_FRAGMENT`](#evolveddestruction_policy_remove_fragment) will remove the destroyed fragment from all entities that have it. This is the default behavior, so you don't have to set it explicitly, but you can if you want. ```lua local evolved = require 'evolved' local world = evolved.builder() :tag() :destruction_policy(evolved.DESTRUCTION_POLICY_DESTROY_ENTITY) :build() local entity = evolved.builder() :set(world) :build() -- destroy the world fragment that is attached to the entity evolved.destroy(world) -- the entity is destroyed together with the world fragment now assert(not evolved.alive(entity)) ``` ## Cheat Sheet ### Aliases ``` id :: implementation-specific entity :: id fragment :: id query :: id system :: id component :: any storage :: component[] default :: component duplicate :: {component -> component} execute :: {chunk, entity[], integer} prologue :: {} epilogue :: {} set_hook :: {entity, fragment, component, component} assign_hook :: {entity, fragment, component, component} insert_hook :: {entity, fragment, component} remove_hook :: {entity, fragment, component} each_state :: implementation-specific execute_state :: implementation-specific each_iterator :: {each_state? -> fragment?, component?} execute_iterator :: {execute_state? -> chunk?, entity[]?, integer?} ``` ### Predefs ``` TAG :: fragment NAME :: fragment UNIQUE :: fragment EXPLICIT :: fragment INTERNAL :: fragment DEFAULT :: fragment DUPLICATE :: fragment PREFAB :: fragment DISABLED :: fragment INCLUDES :: fragment EXCLUDES :: fragment REQUIRES :: fragment ON_SET :: fragment ON_ASSIGN :: fragment ON_INSERT :: fragment ON_REMOVE :: fragment GROUP :: fragment QUERY :: fragment EXECUTE :: fragment PROLOGUE :: fragment EPILOGUE :: fragment DESTRUCTION_POLICY :: fragment DESTRUCTION_POLICY_DESTROY_ENTITY :: id DESTRUCTION_POLICY_REMOVE_FRAGMENT :: id ``` ### Functions ``` id :: integer? -> id... name :: id... -> string... pack :: integer, integer -> id unpack :: id -> integer, integer defer :: boolean commit :: boolean cancel :: boolean spawn :: ? -> entity multi_spawn :: integer, ? -> entity[], integer clone :: entity, ? -> entity multi_clone :: integer, entity, ? -> entity[], integer alive :: entity -> boolean alive_all :: entity... -> boolean alive_any :: entity... -> boolean empty :: entity -> boolean empty_all :: entity... -> boolean empty_any :: entity... -> boolean has :: entity, fragment -> boolean has_all :: entity, fragment... -> boolean has_any :: entity, fragment... -> boolean get :: entity, fragment... -> component... set :: entity, fragment, component -> () remove :: entity, fragment... -> () clear :: entity... -> () destroy :: entity... -> () batch_set :: query, fragment, component -> () batch_remove :: query, fragment... -> () batch_clear :: query... -> () batch_destroy :: query... -> () each :: entity -> {each_state? -> fragment?, component?}, each_state? execute :: query -> {execute_state? -> chunk?, entity[]?, integer?}, execute_state? locate :: entity -> chunk?, integer process :: system... -> () debug_mode :: boolean -> () collect_garbage :: () ``` ### Classes #### Chunk ``` chunk :: fragment, fragment... -> chunk, entity[], integer chunk_mt:alive :: boolean chunk_mt:empty :: boolean chunk_mt:has :: fragment -> boolean chunk_mt:has_all :: fragment... -> boolean chunk_mt:has_any :: fragment... -> boolean chunk_mt:entities :: entity[], integer chunk_mt:fragments :: fragment[], integer chunk_mt:components :: fragment... -> storage... ``` #### Builder ``` builder :: builder builder_mt:build :: entity? -> entity builder_mt:multi_build :: integer, entity? -> entity[], integer builder_mt:spawn :: entity builder_mt:multi_spawn :: integer -> entity[], integer builder_mt:clone :: entity -> entity builder_mt:multi_clone :: integer, entity -> entity[], integer builder_mt:has :: fragment -> boolean builder_mt:has_all :: fragment... -> boolean builder_mt:has_any :: fragment... -> boolean builder_mt:get :: fragment... -> component... builder_mt:set :: fragment, component -> builder builder_mt:remove :: fragment... -> builder builder_mt:clear :: builder builder_mt:tag :: builder builder_mt:name :: string -> builder builder_mt:unique :: builder builder_mt:explicit :: builder builder_mt:internal :: builder builder_mt:default :: component -> builder builder_mt:duplicate :: {component -> component} -> builder builder_mt:prefab :: builder builder_mt:disabled :: builder builder_mt:include :: fragment... -> builder builder_mt:exclude :: fragment... -> builder builder_mt:require :: fragment... -> builder builder_mt:on_set :: {entity, fragment, component, component} -> builder builder_mt:on_assign :: {entity, fragment, component, component} -> builder builder_mt:on_insert :: {entity, fragment, component} -> builder builder_mt:on_remove :: {entity, fragment} -> builder builder_mt:group :: system -> builder builder_mt:query :: query -> builder builder_mt:execute :: {chunk, entity[], integer} -> builder builder_mt:prologue :: {} -> builder builder_mt:epilogue :: {} -> builder builder_mt:destruction_policy :: id -> builder ``` ## Changelog ### v1.5.0 - Added a little [LÖVE](https://love2d.org) example; - The spawn and clone operations with defaults have been significantly optimized; - Added basic [Teal](https://github.com/teal-language) type definitions, thanks to [@p0sel0k](https://github.com/p0sel0k). ### v1.4.0 - Improved query execution performance by caching some internal calculations - Added the universal [`builder.build`](#evolvedbuilder_mtbuild) and [`builder.multi_build`](#evolvedbuilder_mtmulti_build) methods that can be used to spawn or clone entities depending on the method arguments - [`evolved.ON_REMOVE`](#evolvedon_remove) hooks are now invoked before the fragment is actually removed from the entity ### v1.3.0 - Added the new [`evolved.cancel`](#evolvedcancel) function - Added the new [`evolved.locate`](#evolvedlocate) function - The internal garbage collector now collects more garbage - Improved system processing debugging experience with stack traces on errors - [`SET/ASSIGN hooks`](#fragment-hooks) are not invoked for tags on override operations anymore - Improved performance of cloning prefabs with many [`Unique Fragments`](#unique-fragments) - Improved performance of builders that are used for spawning multiple times ### v1.2.0 - Added the new [`evolved.name`](#evolvedname-1) function - Added the new [`evolved.multi_spawn`](#evolvedmulti_spawn) and [`evolved.multi_clone`](#evolvedmulti_clone) functions - Added the new [`evolved.INTERNAL`](#evolvedinternal) fragment trait ### v1.1.0 - [`Systems`](#systems) can be queries themselves now - Added the new [`evolved.REQUIRES`](#evolvedrequires) fragment trait ### v1.0.0 - Initial release ## License `evolved.lua` is licensed under the [MIT License][license]. For more details, see the [LICENSE.md](./LICENSE.md) file in the repository. # API Reference ## Predefs ### `evolved.TAG` ### `evolved.NAME` ### `evolved.UNIQUE` ### `evolved.EXPLICIT` ### `evolved.INTERNAL` ### `evolved.DEFAULT` ### `evolved.DUPLICATE` ### `evolved.PREFAB` ### `evolved.DISABLED` ### `evolved.INCLUDES` ### `evolved.EXCLUDES` ### `evolved.REQUIRES` ### `evolved.ON_SET` ### `evolved.ON_ASSIGN` ### `evolved.ON_INSERT` ### `evolved.ON_REMOVE` ### `evolved.GROUP` ### `evolved.QUERY` ### `evolved.EXECUTE` ### `evolved.PROLOGUE` ### `evolved.EPILOGUE` ### `evolved.DESTRUCTION_POLICY` ### `evolved.DESTRUCTION_POLICY_DESTROY_ENTITY` ### `evolved.DESTRUCTION_POLICY_REMOVE_FRAGMENT` ## Functions ### `evolved.id` ```lua ---@param count? integer ---@return evolved.id ... ids ---@nodiscard function evolved.id(count) end ``` ### `evolved.name` ```lua ---@param ... evolved.id ids ---@return string... names ---@nodiscard function evolved.name(...) end ``` ### `evolved.pack` ```lua ---@param primary integer ---@param secondary integer ---@return evolved.id id ---@nodiscard function evolved.pack(primary, secondary) end ``` ### `evolved.unpack` ```lua ---@param id evolved.id ---@return integer primary ---@return integer secondary ---@nodiscard function evolved.unpack(id) end ``` ### `evolved.defer` ```lua ---@return boolean started function evolved.defer() end ``` ### `evolved.commit` ```lua ---@return boolean committed function evolved.commit() end ``` ### `evolved.cancel` ```lua ---@return boolean cancelled function evolved.cancel() end ``` ### `evolved.spawn` ```lua ---@param components? table ---@return evolved.entity entity function evolved.spawn(components) end ``` ### `evolved.multi_spawn` ```lua ---@param entity_count integer ---@param components? table ---@return evolved.entity[] entity_list ---@return integer entity_count function evolved.multi_spawn(entity_count, components) end ``` ### `evolved.clone` ```lua ---@param prefab evolved.entity ---@param components? table ---@return evolved.entity entity function evolved.clone(prefab, components) end ``` ### `evolved.multi_clone` ```lua ---@param entity_count integer ---@param prefab evolved.entity ---@param components? table ---@return evolved.entity[] entity_list ---@return integer entity_count function evolved.multi_clone(entity_count, prefab, components) end ``` ### `evolved.alive` ```lua ---@param entity evolved.entity ---@return boolean ---@nodiscard function evolved.alive(entity) end ``` ### `evolved.alive_all` ```lua ---@param ... evolved.entity entities ---@return boolean ---@nodiscard function evolved.alive_all(...) end ``` ### `evolved.alive_any` ```lua ---@param ... evolved.entity entities ---@return boolean ---@nodiscard function evolved.alive_any(...) end ``` ### `evolved.empty` ```lua ---@param entity evolved.entity ---@return boolean ---@nodiscard function evolved.empty(entity) end ``` ### `evolved.empty_all` ```lua ---@param ... evolved.entity entities ---@return boolean ---@nodiscard function evolved.empty_all(...) end ``` ### `evolved.empty_any` ```lua ---@param ... evolved.entity entities ---@return boolean ---@nodiscard function evolved.empty_any(...) end ``` ### `evolved.has` ```lua ---@param entity evolved.entity ---@param fragment evolved.fragment ---@return boolean ---@nodiscard function evolved.has(entity, fragment) end ``` ### `evolved.has_all` ```lua ---@param entity evolved.entity ---@param ... evolved.fragment fragments ---@return boolean ---@nodiscard function evolved.has_all(entity, ...) end ``` ### `evolved.has_any` ```lua ---@param entity evolved.entity ---@param ... evolved.fragment fragments ---@return boolean ---@nodiscard function evolved.has_any(entity, ...) end ``` ### `evolved.get` ```lua ---@param entity evolved.entity ---@param ... evolved.fragment fragments ---@return evolved.component ... components ---@nodiscard function evolved.get(entity, ...) end ``` ### `evolved.set` ```lua ---@param entity evolved.entity ---@param fragment evolved.fragment ---@param component evolved.component function evolved.set(entity, fragment, component) end ``` ### `evolved.remove` ```lua ---@param entity evolved.entity ---@param ... evolved.fragment fragments function evolved.remove(entity, ...) end ``` ### `evolved.clear` ```lua ---@param ... evolved.entity entities function evolved.clear(...) end ``` ### `evolved.destroy` ```lua ---@param ... evolved.entity entities function evolved.destroy(...) end ``` ### `evolved.batch_set` ```lua ---@param query evolved.query ---@param fragment evolved.fragment ---@param component evolved.component function evolved.batch_set(query, fragment, component) end ``` ### `evolved.batch_remove` ```lua ---@param query evolved.query ---@param ... evolved.fragment fragments function evolved.batch_remove(query, ...) end ``` ### `evolved.batch_clear` ```lua ---@param ... evolved.query queries function evolved.batch_clear(...) end ``` ### `evolved.batch_destroy` ```lua ---@param ... evolved.query queries function evolved.batch_destroy(...) end ``` ### `evolved.each` ```lua ---@param entity evolved.entity ---@return evolved.each_iterator iterator ---@return evolved.each_state? iterator_state ---@nodiscard function evolved.each(entity) end ``` ### `evolved.execute` ```lua ---@param query evolved.query ---@return evolved.execute_iterator iterator ---@return evolved.execute_state? iterator_state ---@nodiscard function evolved.execute(query) end ``` ### `evolved.locate` ```lua ---@param entity evolved.entity ---@return evolved.chunk? chunk ---@return integer place ---@nodiscard function evolved.locate(entity) end ``` ### `evolved.process` ```lua ---@param ... evolved.system systems function evolved.process(...) end ``` ### `evolved.debug_mode` ```lua ---@param yesno boolean function evolved.debug_mode(yesno) end ``` ### `evolved.collect_garbage` ```lua function evolved.collect_garbage() end ``` ## Classes ### Chunk #### `evolved.chunk` ```lua ---@param fragment evolved.fragment ---@param ... evolved.fragment fragments ---@return evolved.chunk chunk ---@return evolved.entity[] entity_list ---@return integer entity_count ---@nodiscard function evolved.chunk(fragment, ...) end ``` #### `evolved.chunk_mt:alive` ```lua ---@return boolean ---@nodiscard function evolved.chunk_mt:alive() end ``` #### `evolved.chunk_mt:empty` ```lua ---@return boolean ---@nodiscard function evolved.chunk_mt:empty() end ``` #### `evolved.chunk_mt:has` ```lua ---@param fragment evolved.fragment ---@return boolean ---@nodiscard function evolved.chunk_mt:has(fragment) end ``` #### `evolved.chunk_mt:has_all` ```lua ---@param ... evolved.fragment fragments ---@return boolean ---@nodiscard function evolved.chunk_mt:has_all(...) end ``` #### `evolved.chunk_mt:has_any` ```lua ---@param ... evolved.fragment fragments ---@return boolean ---@nodiscard function evolved.chunk_mt:has_any(...) end ``` #### `evolved.chunk_mt:entities` ```lua ---@return evolved.entity[] entity_list ---@return integer entity_count ---@nodiscard function evolved.chunk_mt:entities() end ``` #### `evolved.chunk_mt:fragments` ```lua ---@return evolved.fragment[] fragment_list ---@return integer fragment_count ---@nodiscard function evolved.chunk_mt:fragments() end ``` #### `evolved.chunk_mt:components` ```lua ---@param ... evolved.fragment fragments ---@return evolved.storage ... storages ---@nodiscard function evolved.chunk_mt:components(...) end ``` ### Builder #### `evolved.builder` ```lua ---@return evolved.builder builder ---@nodiscard function evolved.builder() end ``` #### `evolved.builder_mt:build` ```lua ---@param prefab? evolved.entity ---@return evolved.entity entity function evolved.builder_mt:build(prefab) end ``` ### `evolved.builder_mt:multi_build` ```lua ---@param entity_count integer ---@param prefab? evolved.entity ---@return evolved.entity[] entity_list ---@return integer entity_count function evolved.builder_mt:multi_build(entity_count, prefab) end ``` #### `evolved.builder_mt:spawn` ```lua ---@return evolved.entity entity function evolved.builder_mt:spawn() end ``` #### `evolved.builder_mt:multi_spawn` ```lua ---@param entity_count integer ---@return evolved.entity[] entity_list ---@return integer entity_count function evolved.builder_mt:multi_spawn(entity_count) end ``` #### `evolved.builder_mt:clone` ```lua ---@param prefab evolved.entity ---@return evolved.entity entity function evolved.builder_mt:clone(prefab) end ``` #### `evolved.builder_mt:multi_clone` ```lua ---@param entity_count integer ---@param prefab evolved.entity ---@return evolved.entity[] entity_list ---@return integer entity_count function evolved.builder_mt:multi_clone(entity_count, prefab) end ``` #### `evolved.builder_mt:has` ```lua ---@param fragment evolved.fragment ---@return boolean ---@nodiscard function evolved.builder_mt:has(fragment) end ``` #### `evolved.builder_mt:has_all` ```lua ---@param ... evolved.fragment fragments ---@return boolean ---@nodiscard function evolved.builder_mt:has_all(...) end ``` #### `evolved.builder_mt:has_any` ```lua ---@param ... evolved.fragment fragments ---@return boolean ---@nodiscard function evolved.builder_mt:has_any(...) end ``` #### `evolved.builder_mt:get` ```lua ---@param ... evolved.fragment fragments ---@return evolved.component ... components ---@nodiscard function evolved.builder_mt:get(...) end ``` #### `evolved.builder_mt:set` ```lua ---@param fragment evolved.fragment ---@param component evolved.component ---@return evolved.builder builder function evolved.builder_mt:set(fragment, component) end ``` #### `evolved.builder_mt:remove` ```lua ---@param ... evolved.fragment fragments ---@return evolved.builder builder function evolved.builder_mt:remove(...) end ``` #### `evolved.builder_mt:clear` ```lua ---@return evolved.builder builder function evolved.builder_mt:clear() end ``` #### `evolved.builder_mt:tag` ```lua ---@return evolved.builder builder function evolved.builder_mt:tag() end ``` #### `evolved.builder_mt:name` ```lua ---@param name string ---@return evolved.builder builder function evolved.builder_mt:name(name) end ``` #### `evolved.builder_mt:unique` ```lua ---@return evolved.builder builder function evolved.builder_mt:unique() end ``` #### `evolved.builder_mt:explicit` ```lua ---@return evolved.builder builder function evolved.builder_mt:explicit() end ``` #### `evolved.builder_mt:internal` ```lua ---@return evolved.builder builder function evolved.builder_mt:internal() end ``` #### `evolved.builder_mt:default` ```lua ---@param default evolved.default ---@return evolved.builder builder function evolved.builder_mt:default(default) end ``` #### `evolved.builder_mt:duplicate` ```lua ---@param duplicate evolved.duplicate ---@return evolved.builder builder function evolved.builder_mt:duplicate(duplicate) end ``` #### `evolved.builder_mt:prefab` ```lua ---@return evolved.builder builder function evolved.builder_mt:prefab() end ``` #### `evolved.builder_mt:disabled` ```lua ---@return evolved.builder builder function evolved.builder_mt:disabled() end ``` #### `evolved.builder_mt:include` ```lua ---@param ... evolved.fragment fragments ---@return evolved.builder builder function evolved.builder_mt:include(...) end ``` #### `evolved.builder_mt:exclude` ```lua ---@param ... evolved.fragment fragments ---@return evolved.builder builder function evolved.builder_mt:exclude(...) end ``` ### `evolved.builder_mt:require` ```lua ---@param ... evolved.fragment fragments ---@return evolved.builder builder function evolved.builder_mt:require(...) end ``` #### `evolved.builder_mt:on_set` ```lua ---@param on_set evolved.set_hook ---@return evolved.builder builder function evolved.builder_mt:on_set(on_set) end ``` #### `evolved.builder_mt:on_assign` ```lua ---@param on_assign evolved.assign_hook ---@return evolved.builder builder function evolved.builder_mt:on_assign(on_assign) end ``` #### `evolved.builder_mt:on_insert` ```lua ---@param on_insert evolved.insert_hook ---@return evolved.builder builder function evolved.builder_mt:on_insert(on_insert) end ``` #### `evolved.builder_mt:on_remove` ```lua ---@param on_remove evolved.remove_hook ---@return evolved.builder builder function evolved.builder_mt:on_remove(on_remove) end ``` #### `evolved.builder_mt:group` ```lua ---@param group evolved.system ---@return evolved.builder builder function evolved.builder_mt:group(group) end ``` #### `evolved.builder_mt:query` ```lua ---@param query evolved.query ---@return evolved.builder builder function evolved.builder_mt:query(query) end ``` #### `evolved.builder_mt:execute` ```lua ---@param execute evolved.execute ---@return evolved.builder builder function evolved.builder_mt:execute(execute) end ``` #### `evolved.builder_mt:prologue` ```lua ---@param prologue evolved.prologue ---@return evolved.builder builder function evolved.builder_mt:prologue(prologue) end ``` #### `evolved.builder_mt:epilogue` ```lua ---@param epilogue evolved.epilogue ---@return evolved.builder builder function evolved.builder_mt:epilogue(epilogue) end ``` #### `evolved.builder_mt:destruction_policy` ```lua ---@param destruction_policy evolved.id ---@return evolved.builder builder function evolved.builder_mt:destruction_policy(destruction_policy) end ```