mirror of
https://github.com/BlackMATov/evolved.lua.git
synced 2026-02-04 06:37:06 +07:00
Compare commits
16 Commits
a945401e9b
...
e48bdf0511
| Author | SHA1 | Date | |
|---|---|---|---|
| e48bdf0511 | |||
|
|
e9824a4776 | ||
|
|
c3760c40bf | ||
|
|
a2a4fc56e3 | ||
|
|
21d5091d14 | ||
|
|
18ee0d9a12 | ||
|
|
12c86ca679 | ||
|
|
a0f252f47c | ||
|
|
b17118544d | ||
|
|
b9cdbe961b | ||
|
|
4f78c8245c | ||
|
|
a7e5652ad4 | ||
|
|
c52f708184 | ||
|
|
e75ddef396 | ||
|
|
c9bfb26748 | ||
|
|
3a3abbd2fd |
299
README.md
299
README.md
@@ -35,6 +35,7 @@
|
||||
- [Structural Changes](#structural-changes)
|
||||
- [Spawning Entities](#spawning-entities)
|
||||
- [Entity Builders](#entity-builders)
|
||||
- [Multi-Entity Spawning](#multi-entity-spawning)
|
||||
- [Access Operations](#access-operations)
|
||||
- [Iterating Over Fragments](#iterating-over-fragments)
|
||||
- [Modifying Operations](#modifying-operations)
|
||||
@@ -53,6 +54,7 @@
|
||||
- [Shared Components](#shared-components)
|
||||
- [Fragment Requirements](#fragment-requirements)
|
||||
- [Destruction Policies](#destruction-policies)
|
||||
- [Custom Component Storages](#custom-component-storages)
|
||||
- [Cheat Sheet](#cheat-sheet)
|
||||
- [Aliases](#aliases)
|
||||
- [Predefs](#predefs)
|
||||
@@ -61,6 +63,7 @@
|
||||
- [Chunk](#chunk)
|
||||
- [Builder](#builder)
|
||||
- [Changelog](#changelog)
|
||||
- [v1.8.0](#v180)
|
||||
- [v1.7.0](#v170)
|
||||
- [v1.6.0](#v160)
|
||||
- [v1.5.0](#v150)
|
||||
@@ -418,17 +421,19 @@ You should try to avoid structural changes, especially in performance-critical c
|
||||
#### Spawning Entities
|
||||
|
||||
```lua
|
||||
---@param components? table<evolved.fragment, evolved.component>
|
||||
---@param component_table? table<evolved.fragment, evolved.component>
|
||||
---@param component_mapper? fun(chunk: evolved.chunk, place: integer)
|
||||
---@return evolved.entity
|
||||
function evolved.spawn(components) end
|
||||
function evolved.spawn(component_table, component_mapper) end
|
||||
|
||||
---@param prefab evolved.entity
|
||||
---@param components? table<evolved.fragment, evolved.component>
|
||||
---@param component_table? table<evolved.fragment, evolved.component>
|
||||
---@param component_mapper? fun(chunk: evolved.chunk, place: integer)
|
||||
---@return evolved.entity
|
||||
function evolved.clone(prefab, components) end
|
||||
function evolved.clone(prefab, component_table, component_mapper) 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.
|
||||
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 `component_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.
|
||||
|
||||
@@ -472,6 +477,54 @@ local enemy = evolved.builder()
|
||||
|
||||
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.
|
||||
|
||||
#### Multi-Entity Spawning
|
||||
|
||||
When you need to spawn multiple entities with identical fragments, use `multi_spawn` and `multi_clone` to optimize performance and reduce overhead.
|
||||
|
||||
```lua
|
||||
---@param entity_count integer
|
||||
---@param component_table? evolved.component_table
|
||||
---@param component_mapper? evolved.component_mapper
|
||||
---@return evolved.entity[] entity_list
|
||||
---@return integer entity_count
|
||||
function evolved.multi_spawn(entity_count, component_table, component_mapper) end
|
||||
|
||||
---@param entity_count integer
|
||||
---@param prefab evolved.entity
|
||||
---@param component_table? evolved.component_table
|
||||
---@param component_mapper? evolved.component_mapper
|
||||
---@return evolved.entity[] entity_list
|
||||
---@return integer entity_count
|
||||
function evolved.multi_clone(entity_count, prefab, component_table, component_mapper) end
|
||||
```
|
||||
|
||||
These functions behave like their single-entity counterparts, but they allow you to spawn or clone multiple entities in one call. This approach minimizes the overhead of repeated function calls and structural changes, improving performance when handling large numbers of entities.
|
||||
|
||||
Typically, when spawning multiple entities, they share the same set of fragments, but their components can differ. You can achieve this by providing a `component_mapper` function, which receives the chunk and the range of places for the newly spawned entities. This avoids many `evolved.set` calls after spawning, which can be costly when creating many entities.
|
||||
|
||||
Here is a small example of using `evolved.multi_spawn` with a `component_mapper`:
|
||||
|
||||
```lua
|
||||
local evolved = require 'evolved'
|
||||
|
||||
local position_x, position_y = evolved.id(2)
|
||||
|
||||
evolved.multi_spawn(1000, {
|
||||
[position_x] = 0,
|
||||
[position_y] = 0,
|
||||
}, function(chunk, b_place, e_place)
|
||||
local x_components = chunk:components(position_x)
|
||||
local y_components = chunk:components(position_y)
|
||||
|
||||
for i = b_place, e_place do
|
||||
x_components[i] = math.random(-100, 100)
|
||||
y_components[i] = math.random(-100, 100)
|
||||
end
|
||||
end)
|
||||
```
|
||||
|
||||
Of course, you can use `evolved.multi_clone` in the same way. Builders can also be used for multi-entity spawning and cloning by calling the corresponding methods on the builder object.
|
||||
|
||||
### 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:
|
||||
@@ -1153,6 +1206,145 @@ evolved.destroy(world)
|
||||
assert(not evolved.alive(entity))
|
||||
```
|
||||
|
||||
#### Custom Component Storages
|
||||
|
||||
In some cases, you might want custom storages for fragment components. For example, you might want to store components in a specialized way for performance reasons. The library provides two fragment traits for this purpose: [`evolved.REALLOC`](#evolvedrealloc) and [`evolved.COMPMOVE`](#evolvedcompmove).
|
||||
|
||||
The [`evolved.REALLOC`](#evolvedrealloc) trait expects a function that is called when the fragment storage needs to be reallocated. The [`evolved.COMPMOVE`](#evolvedcompmove) trait expects a function that is called when components need to be moved from one storage to another.
|
||||
|
||||
A canonical example of using custom storages is implementing a fragment that stores components in an FFI-backed storage for better processing performance in LuaJIT. This is an advanced topic and requires a good understanding of LuaJIT FFI and memory management. So I won't cover it here in detail, but here is a simple example to give you an idea of how it works. More information can be found on the [LuaJIT](https://luajit.org/ext_ffi.html) website.
|
||||
|
||||
```lua
|
||||
local ffi = require 'ffi'
|
||||
local evolved = require 'evolved'
|
||||
|
||||
--
|
||||
--
|
||||
-- Define FFI double storage realloc and compmove functions
|
||||
--
|
||||
--
|
||||
|
||||
local FFI_DOUBLE_TYPEOF = ffi.typeof('double')
|
||||
local FFI_DOUBLE_SIZEOF = ffi.sizeof(FFI_DOUBLE_TYPEOF)
|
||||
local FFI_DOUBLE_STORAGE_TYPEOF = ffi.typeof('double[?]')
|
||||
|
||||
---@param src ffi.cdata*?
|
||||
---@param src_size integer
|
||||
---@param dst_size integer
|
||||
---@return ffi.cdata*?
|
||||
local function FFI_DOUBLE_STORAGE_REALLOC(src, src_size, dst_size)
|
||||
if dst_size == 0 then
|
||||
-- freeing the src storage, just let the GC handle it
|
||||
return
|
||||
end
|
||||
|
||||
-- to support 1-based indexing, allocate one extra element
|
||||
local dst = ffi.new(FFI_DOUBLE_STORAGE_TYPEOF, dst_size + 1)
|
||||
|
||||
if src and src_size > 0 then
|
||||
-- handle both expanding and shrinking
|
||||
local min_size = math.min(src_size, dst_size)
|
||||
ffi.copy(dst + 1, src + 1, min_size * FFI_DOUBLE_SIZEOF)
|
||||
end
|
||||
|
||||
return dst
|
||||
end
|
||||
|
||||
---@param src ffi.cdata*
|
||||
---@param f integer
|
||||
---@param e integer
|
||||
---@param t integer
|
||||
---@param dst ffi.cdata*
|
||||
local function FFI_DOUBLE_STORAGE_COMPMOVE(src, f, e, t, dst)
|
||||
ffi.copy(dst + t, src + f, (e - f + 1) * FFI_DOUBLE_SIZEOF)
|
||||
end
|
||||
|
||||
--
|
||||
--
|
||||
-- Define fragments with our custom FFI storages
|
||||
--
|
||||
--
|
||||
|
||||
local POSITION_X = evolved.builder()
|
||||
:default(0)
|
||||
:realloc(FFI_DOUBLE_STORAGE_REALLOC)
|
||||
:compmove(FFI_DOUBLE_STORAGE_COMPMOVE)
|
||||
:build()
|
||||
|
||||
local POSITION_Y = evolved.builder()
|
||||
:default(0)
|
||||
:realloc(FFI_DOUBLE_STORAGE_REALLOC)
|
||||
:compmove(FFI_DOUBLE_STORAGE_COMPMOVE)
|
||||
:build()
|
||||
|
||||
local VELOCITY_X = evolved.builder()
|
||||
:default(0)
|
||||
:realloc(FFI_DOUBLE_STORAGE_REALLOC)
|
||||
:compmove(FFI_DOUBLE_STORAGE_COMPMOVE)
|
||||
:build()
|
||||
|
||||
local VELOCITY_Y = evolved.builder()
|
||||
:default(0)
|
||||
:realloc(FFI_DOUBLE_STORAGE_REALLOC)
|
||||
:compmove(FFI_DOUBLE_STORAGE_COMPMOVE)
|
||||
:build()
|
||||
|
||||
--
|
||||
--
|
||||
-- Define a movement system that uses these components
|
||||
--
|
||||
--
|
||||
|
||||
local MOVEMENT_SYSTEM = evolved.builder()
|
||||
:include(POSITION_X, POSITION_Y)
|
||||
:include(VELOCITY_X, VELOCITY_Y)
|
||||
:execute(function(chunk, entity_list, entity_count, delta_time)
|
||||
local position_xs, position_ys = chunk:components(POSITION_X, POSITION_Y)
|
||||
local velocity_xs, velocity_ys = chunk:components(VELOCITY_X, VELOCITY_Y)
|
||||
|
||||
for i = 1, entity_count do
|
||||
local px, py = position_xs[i], position_ys[i]
|
||||
local vx, vy = velocity_xs[i], velocity_ys[i]
|
||||
|
||||
px = px + vx * delta_time
|
||||
py = py + vy * delta_time
|
||||
|
||||
position_xs[i], position_ys[i] = px, py
|
||||
end
|
||||
end):build()
|
||||
|
||||
--
|
||||
--
|
||||
-- Spawn some entities with these components
|
||||
--
|
||||
--
|
||||
|
||||
evolved.builder()
|
||||
:set(POSITION_X)
|
||||
:set(POSITION_Y)
|
||||
:set(VELOCITY_X)
|
||||
:set(VELOCITY_Y)
|
||||
:multi_spawn(10000, function(chunk, b_place, e_place)
|
||||
local position_xs, position_ys = chunk:components(POSITION_X, POSITION_Y)
|
||||
local velocity_xs, velocity_ys = chunk:components(VELOCITY_X, VELOCITY_Y)
|
||||
|
||||
for place = b_place, e_place do
|
||||
position_xs[place] = math.random(0, 640)
|
||||
position_ys[place] = math.random(0, 480)
|
||||
velocity_xs[place] = math.random(-100, 100)
|
||||
velocity_ys[place] = math.random(-100, 100)
|
||||
end
|
||||
end)
|
||||
|
||||
--
|
||||
--
|
||||
-- Process the movement system with a delta time payload
|
||||
--
|
||||
--
|
||||
|
||||
evolved.process_with(MOVEMENT_SYSTEM, 0.016)
|
||||
```
|
||||
|
||||
## Cheat Sheet
|
||||
|
||||
### Aliases
|
||||
@@ -1168,9 +1360,15 @@ system :: id
|
||||
component :: any
|
||||
storage :: component[]
|
||||
|
||||
component_table :: <fragment, component>
|
||||
component_mapper :: {chunk, integer, integer}
|
||||
|
||||
default :: component
|
||||
duplicate :: {component -> component}
|
||||
|
||||
realloc :: {storage?, integer, integer -> storage?}
|
||||
compmove :: {storage, integer, integer, integer, storage}
|
||||
|
||||
execute :: {chunk, entity[], integer, any...}
|
||||
prologue :: {any...}
|
||||
epilogue :: {any...}
|
||||
@@ -1200,6 +1398,9 @@ INTERNAL :: fragment
|
||||
DEFAULT :: fragment
|
||||
DUPLICATE :: fragment
|
||||
|
||||
REALLOC :: fragment
|
||||
COMPMOVE :: fragment
|
||||
|
||||
PREFAB :: fragment
|
||||
DISABLED :: fragment
|
||||
|
||||
@@ -1240,11 +1441,11 @@ depth :: integer
|
||||
commit :: boolean
|
||||
cancel :: boolean
|
||||
|
||||
spawn :: <fragment, component>? -> entity
|
||||
multi_spawn :: integer, <fragment, component>? -> entity[], integer
|
||||
spawn :: component_table?, component_mapper? -> entity
|
||||
multi_spawn :: integer, component_table?, component_mapper? -> entity[], integer
|
||||
|
||||
clone :: entity, <fragment, component>? -> entity
|
||||
multi_clone :: integer, entity, <fragment, component>? -> entity[], integer
|
||||
clone :: entity, component_table?, component_mapper? -> entity
|
||||
multi_clone :: integer, entity, component_table?, component_mapper? -> entity[], integer
|
||||
|
||||
alive :: entity -> boolean
|
||||
alive_all :: entity... -> boolean
|
||||
@@ -1258,7 +1459,7 @@ has :: entity, fragment -> boolean
|
||||
has_all :: entity, fragment... -> boolean
|
||||
has_any :: entity, fragment... -> boolean
|
||||
|
||||
get :: entity, fragment... -> component...
|
||||
get :: entity, fragment... -> component...
|
||||
|
||||
set :: entity, fragment, component -> ()
|
||||
remove :: entity, fragment... -> ()
|
||||
@@ -1306,14 +1507,14 @@ chunk_mt:components :: fragment... -> storage...
|
||||
```
|
||||
builder :: builder
|
||||
|
||||
builder_mt:build :: entity? -> entity
|
||||
builder_mt:multi_build :: integer, entity? -> entity[], integer
|
||||
builder_mt:build :: entity?, component_mapper? -> entity
|
||||
builder_mt:multi_build :: integer, entity?, component_mapper? -> entity[], integer
|
||||
|
||||
builder_mt:spawn :: entity
|
||||
builder_mt:multi_spawn :: integer -> entity[], integer
|
||||
builder_mt:spawn :: component_mapper? -> entity
|
||||
builder_mt:multi_spawn :: integer, component_mapper? -> entity[], integer
|
||||
|
||||
builder_mt:clone :: entity -> entity
|
||||
builder_mt:multi_clone :: integer, entity -> entity[], integer
|
||||
builder_mt:clone :: entity, component_mapper? -> entity
|
||||
builder_mt:multi_clone :: integer, entity, component_mapper? -> entity[], integer
|
||||
|
||||
builder_mt:has :: fragment -> boolean
|
||||
builder_mt:has_all :: fragment... -> boolean
|
||||
@@ -1335,6 +1536,9 @@ builder_mt:internal :: builder
|
||||
builder_mt:default :: component -> builder
|
||||
builder_mt:duplicate :: {component -> component} -> builder
|
||||
|
||||
builder_mt:realloc :: {storage?, integer, integer -> storage?} -> builder
|
||||
builder_mt:compmove :: {storage, integer, integer, integer, storage} -> builder
|
||||
|
||||
builder_mt:prefab :: builder
|
||||
builder_mt:disabled :: builder
|
||||
|
||||
@@ -1361,6 +1565,11 @@ builder_mt:destruction_policy :: id -> builder
|
||||
|
||||
## Changelog
|
||||
|
||||
### v1.8.0
|
||||
|
||||
- Added the new [`evolved.REALLOC`](#evolvedrealloc) and [`evolved.COMPMOVE`](#evolvedcompmove) fragment traits that allow customizing component storages
|
||||
- Added `component_mapper` argument to the spawning and cloning functions that allows filling components in chunks during the operation
|
||||
|
||||
### v1.7.0
|
||||
|
||||
- Added the new [`evolved.VARIANTS`](#evolvedvariants) query fragment that allows specifying any of multiple fragments in queries
|
||||
@@ -1430,6 +1639,10 @@ builder_mt:destruction_policy :: id -> builder
|
||||
|
||||
### `evolved.DUPLICATE`
|
||||
|
||||
### `evolved.REALLOC`
|
||||
|
||||
### `evolved.COMPMOVE`
|
||||
|
||||
### `evolved.PREFAB`
|
||||
|
||||
### `evolved.DISABLED`
|
||||
@@ -1538,28 +1751,31 @@ function evolved.cancel() end
|
||||
### `evolved.spawn`
|
||||
|
||||
```lua
|
||||
---@param components? table<evolved.fragment, evolved.component>
|
||||
---@param component_table? evolved.component_table
|
||||
---@param component_mapper? evolved.component_mapper
|
||||
---@return evolved.entity entity
|
||||
function evolved.spawn(components) end
|
||||
function evolved.spawn(component_table, component_mapper) end
|
||||
```
|
||||
|
||||
### `evolved.multi_spawn`
|
||||
|
||||
```lua
|
||||
---@param entity_count integer
|
||||
---@param components? table<evolved.fragment, evolved.component>
|
||||
---@param component_table? evolved.component_table
|
||||
---@param component_mapper? evolved.component_mapper
|
||||
---@return evolved.entity[] entity_list
|
||||
---@return integer entity_count
|
||||
function evolved.multi_spawn(entity_count, components) end
|
||||
function evolved.multi_spawn(entity_count, component_table, component_mapper) end
|
||||
```
|
||||
|
||||
### `evolved.clone`
|
||||
|
||||
```lua
|
||||
---@param prefab evolved.entity
|
||||
---@param components? table<evolved.fragment, evolved.component>
|
||||
---@param component_table? evolved.component_table
|
||||
---@param component_mapper? evolved.component_mapper
|
||||
---@return evolved.entity entity
|
||||
function evolved.clone(prefab, components) end
|
||||
function evolved.clone(prefab, component_table, component_mapper) end
|
||||
```
|
||||
|
||||
### `evolved.multi_clone`
|
||||
@@ -1567,10 +1783,11 @@ function evolved.clone(prefab, components) end
|
||||
```lua
|
||||
---@param entity_count integer
|
||||
---@param prefab evolved.entity
|
||||
---@param components? table<evolved.fragment, evolved.component>
|
||||
---@param component_table? evolved.component_table
|
||||
---@param component_mapper? evolved.component_mapper
|
||||
---@return evolved.entity[] entity_list
|
||||
---@return integer entity_count
|
||||
function evolved.multi_clone(entity_count, prefab, components) end
|
||||
function evolved.multi_clone(entity_count, prefab, component_table, component_mapper) end
|
||||
```
|
||||
|
||||
### `evolved.alive`
|
||||
@@ -1887,8 +2104,9 @@ function evolved.builder() end
|
||||
|
||||
```lua
|
||||
---@param prefab? evolved.entity
|
||||
---@param component_mapper? evolved.component_mapper
|
||||
---@return evolved.entity entity
|
||||
function evolved.builder_mt:build(prefab) end
|
||||
function evolved.builder_mt:build(prefab, component_mapper) end
|
||||
```
|
||||
|
||||
### `evolved.builder_mt:multi_build`
|
||||
@@ -1896,33 +2114,37 @@ function evolved.builder_mt:build(prefab) end
|
||||
```lua
|
||||
---@param entity_count integer
|
||||
---@param prefab? evolved.entity
|
||||
---@param component_mapper? evolved.component_mapper
|
||||
---@return evolved.entity[] entity_list
|
||||
---@return integer entity_count
|
||||
function evolved.builder_mt:multi_build(entity_count, prefab) end
|
||||
function evolved.builder_mt:multi_build(entity_count, prefab, component_mapper) end
|
||||
```
|
||||
|
||||
#### `evolved.builder_mt:spawn`
|
||||
|
||||
```lua
|
||||
---@param component_mapper? evolved.component_mapper
|
||||
---@return evolved.entity entity
|
||||
function evolved.builder_mt:spawn() end
|
||||
function evolved.builder_mt:spawn(component_mapper) end
|
||||
```
|
||||
|
||||
#### `evolved.builder_mt:multi_spawn`
|
||||
|
||||
```lua
|
||||
---@param entity_count integer
|
||||
---@param component_mapper? evolved.component_mapper
|
||||
---@return evolved.entity[] entity_list
|
||||
---@return integer entity_count
|
||||
function evolved.builder_mt:multi_spawn(entity_count) end
|
||||
function evolved.builder_mt:multi_spawn(entity_count, component_mapper) end
|
||||
```
|
||||
|
||||
#### `evolved.builder_mt:clone`
|
||||
|
||||
```lua
|
||||
---@param prefab evolved.entity
|
||||
---@param component_mapper? evolved.component_mapper
|
||||
---@return evolved.entity entity
|
||||
function evolved.builder_mt:clone(prefab) end
|
||||
function evolved.builder_mt:clone(prefab, component_mapper) end
|
||||
```
|
||||
|
||||
#### `evolved.builder_mt:multi_clone`
|
||||
@@ -1930,9 +2152,10 @@ function evolved.builder_mt:clone(prefab) end
|
||||
```lua
|
||||
---@param entity_count integer
|
||||
---@param prefab evolved.entity
|
||||
---@param component_mapper? evolved.component_mapper
|
||||
---@return evolved.entity[] entity_list
|
||||
---@return integer entity_count
|
||||
function evolved.builder_mt:multi_clone(entity_count, prefab) end
|
||||
function evolved.builder_mt:multi_clone(entity_count, prefab, component_mapper) end
|
||||
```
|
||||
|
||||
#### `evolved.builder_mt:has`
|
||||
@@ -2047,6 +2270,22 @@ function evolved.builder_mt:default(default) end
|
||||
function evolved.builder_mt:duplicate(duplicate) end
|
||||
```
|
||||
|
||||
#### `evolved.builder_mt:realloc`
|
||||
|
||||
```lua
|
||||
---@param realloc evolved.realloc
|
||||
---@return evolved.builder builder
|
||||
function evolved.builder_mt:realloc(realloc) end
|
||||
```
|
||||
|
||||
#### `evolved.builder_mt:compmove`
|
||||
|
||||
```lua
|
||||
---@param compmove evolved.compmove
|
||||
---@return evolved.builder builder
|
||||
function evolved.builder_mt:compmove(compmove) end
|
||||
```
|
||||
|
||||
#### `evolved.builder_mt:prefab`
|
||||
|
||||
```lua
|
||||
|
||||
@@ -10,7 +10,13 @@
|
||||
## Thoughts
|
||||
|
||||
- We should have a way to not copy components on deferred spawn/clone
|
||||
- Not all assoc_list_remove operations need to keep order, we can have an unordered variant also
|
||||
- We still have several places where we use __lua_next without deterministic order, we should fix that
|
||||
- Having a light version of the gargabe collector can be useful for some use-cases
|
||||
- We can shrink the table pool tables on garbage collection if they are too large
|
||||
- Should we sort chunk children by fragment id?
|
||||
- Basic default component value as true looks awful, should we use something else?
|
||||
|
||||
## Known Issues
|
||||
|
||||
- Errors in hooks are cannot be handled properly right now
|
||||
- Errors in hooks or rellocs/compmoves/mappers are cannot be handled properly right now
|
||||
|
||||
@@ -5,9 +5,11 @@ require 'develop.testing.depth_tests'
|
||||
require 'develop.testing.destroy_tests'
|
||||
require 'develop.testing.locate_tests'
|
||||
require 'develop.testing.main_tests'
|
||||
require 'develop.testing.mappers_tests'
|
||||
require 'develop.testing.multi_spawn_tests'
|
||||
require 'develop.testing.name_tests'
|
||||
require 'develop.testing.process_with_tests'
|
||||
require 'develop.testing.realloc_tests'
|
||||
require 'develop.testing.requires_fragment_tests'
|
||||
require 'develop.testing.spawn_tests'
|
||||
require 'develop.testing.system_as_query_tests'
|
||||
@@ -35,3 +37,5 @@ print '----------------------------------------'
|
||||
basics.describe_fuzz 'develop.fuzzing.requires_fuzz'
|
||||
print '----------------------------------------'
|
||||
basics.describe_fuzz 'develop.fuzzing.unique_fuzz'
|
||||
|
||||
print 'All tests passed.'
|
||||
|
||||
242
develop/testing/mappers_tests.lua
Normal file
242
develop/testing/mappers_tests.lua
Normal file
@@ -0,0 +1,242 @@
|
||||
local evo = require 'evolved'
|
||||
|
||||
do
|
||||
local f1, f2 = evo.id(2)
|
||||
|
||||
local e1 = evo.spawn({ [f1] = 1, [f2] = 2 }, function(c, p)
|
||||
local f1s, f2s = c:components(f1, f2)
|
||||
f1s[p] = f1s[p] * 2
|
||||
f2s[p] = f2s[p] * 3
|
||||
end)
|
||||
|
||||
local e2 = evo.spawn({ [f1] = 1, [f2] = 2 }, function(c, p)
|
||||
local f1s, f2s = c:components(f1, f2)
|
||||
f1s[p] = f1s[p] + 10
|
||||
f2s[p] = f2s[p] + 20
|
||||
end)
|
||||
|
||||
assert(evo.has(e1, f1) and evo.get(e1, f1) == 2)
|
||||
assert(evo.has(e1, f2) and evo.get(e1, f2) == 6)
|
||||
assert(evo.has(e2, f1) and evo.get(e2, f1) == 11)
|
||||
assert(evo.has(e2, f2) and evo.get(e2, f2) == 22)
|
||||
|
||||
local e11 = evo.clone(e1, nil, function(c, p)
|
||||
local f1s, f2s = c:components(f1, f2)
|
||||
f1s[p] = f1s[p] * 3
|
||||
f2s[p] = f2s[p] * 4
|
||||
end)
|
||||
|
||||
local e22 = evo.clone(e2, nil, function(c, p)
|
||||
local f1s, f2s = c:components(f1, f2)
|
||||
f1s[p] = f1s[p] + 30
|
||||
f2s[p] = f2s[p] + 40
|
||||
end)
|
||||
|
||||
assert(evo.has(e11, f1) and evo.get(e11, f1) == 6)
|
||||
assert(evo.has(e11, f2) and evo.get(e11, f2) == 24)
|
||||
assert(evo.has(e22, f1) and evo.get(e22, f1) == 41)
|
||||
assert(evo.has(e22, f2) and evo.get(e22, f2) == 62)
|
||||
end
|
||||
|
||||
do
|
||||
local f1, f2 = evo.id(2)
|
||||
|
||||
evo.defer()
|
||||
|
||||
local e1 = evo.spawn({ [f1] = 1, [f2] = 2 }, function(c, p)
|
||||
local f1s, f2s = c:components(f1, f2)
|
||||
f1s[p] = f1s[p] * 2
|
||||
f2s[p] = f2s[p] * 3
|
||||
end)
|
||||
|
||||
local e2 = evo.spawn({ [f1] = 1, [f2] = 2 }, function(c, p)
|
||||
local f1s, f2s = c:components(f1, f2)
|
||||
f1s[p] = f1s[p] + 10
|
||||
f2s[p] = f2s[p] + 20
|
||||
end)
|
||||
|
||||
assert(not evo.has(e1, f1) and evo.get(e1, f1) == nil)
|
||||
assert(not evo.has(e1, f2) and evo.get(e1, f2) == nil)
|
||||
assert(not evo.has(e2, f1) and evo.get(e2, f1) == nil)
|
||||
assert(not evo.has(e2, f2) and evo.get(e2, f2) == nil)
|
||||
|
||||
evo.commit()
|
||||
|
||||
assert(evo.has(e1, f1) and evo.get(e1, f1) == 2)
|
||||
assert(evo.has(e1, f2) and evo.get(e1, f2) == 6)
|
||||
assert(evo.has(e2, f1) and evo.get(e2, f1) == 11)
|
||||
assert(evo.has(e2, f2) and evo.get(e2, f2) == 22)
|
||||
|
||||
evo.defer()
|
||||
|
||||
local e11 = evo.clone(e1, nil, function(c, p)
|
||||
local f1s, f2s = c:components(f1, f2)
|
||||
f1s[p] = f1s[p] * 3
|
||||
f2s[p] = f2s[p] * 4
|
||||
end)
|
||||
|
||||
local e22 = evo.clone(e2, nil, function(c, p)
|
||||
local f1s, f2s = c:components(f1, f2)
|
||||
f1s[p] = f1s[p] + 30
|
||||
f2s[p] = f2s[p] + 40
|
||||
end)
|
||||
|
||||
assert(not evo.has(e11, f1) and evo.get(e11, f1) == nil)
|
||||
assert(not evo.has(e11, f2) and evo.get(e11, f2) == nil)
|
||||
assert(not evo.has(e22, f1) and evo.get(e22, f1) == nil)
|
||||
assert(not evo.has(e22, f2) and evo.get(e22, f2) == nil)
|
||||
|
||||
evo.commit()
|
||||
|
||||
assert(evo.has(e11, f1) and evo.get(e11, f1) == 6)
|
||||
assert(evo.has(e11, f2) and evo.get(e11, f2) == 24)
|
||||
assert(evo.has(e22, f1) and evo.get(e22, f1) == 41)
|
||||
assert(evo.has(e22, f2) and evo.get(e22, f2) == 62)
|
||||
end
|
||||
|
||||
do
|
||||
local f1, f2 = evo.id(2)
|
||||
|
||||
local es, ec = evo.multi_spawn(10, { [f1] = 1, [f2] = 2 }, function(c, b, e)
|
||||
local f1s, f2s = c:components(f1, f2)
|
||||
for p = b, e do
|
||||
f1s[p] = f1s[p] * 2
|
||||
f2s[p] = f2s[p] * 3
|
||||
end
|
||||
end)
|
||||
|
||||
for i = 1, ec do
|
||||
local e = es[i]
|
||||
assert(evo.has(e, f1) and evo.get(e, f1) == 2)
|
||||
assert(evo.has(e, f2) and evo.get(e, f2) == 6)
|
||||
end
|
||||
|
||||
local es2, ec2 = evo.multi_clone(10, es[1], nil, function(c, b, e)
|
||||
local f1s, f2s = c:components(f1, f2)
|
||||
for p = b, e do
|
||||
f1s[p] = f1s[p] + 10
|
||||
f2s[p] = f2s[p] + 20
|
||||
end
|
||||
end)
|
||||
|
||||
for i = 1, ec2 do
|
||||
local e = es2[i]
|
||||
assert(evo.has(e, f1) and evo.get(e, f1) == 12)
|
||||
assert(evo.has(e, f2) and evo.get(e, f2) == 26)
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local f1, f2 = evo.id(2)
|
||||
|
||||
evo.defer()
|
||||
|
||||
local es, ec = evo.multi_spawn(10, { [f1] = 1, [f2] = 2 }, function(c, b, e)
|
||||
local f1s, f2s = c:components(f1, f2)
|
||||
for p = b, e do
|
||||
f1s[p] = f1s[p] * 2
|
||||
f2s[p] = f2s[p] * 3
|
||||
end
|
||||
end)
|
||||
|
||||
for i = 1, ec do
|
||||
local e = es[i]
|
||||
assert(not evo.has(e, f1) and evo.get(e, f1) == nil)
|
||||
assert(not evo.has(e, f2) and evo.get(e, f2) == nil)
|
||||
end
|
||||
|
||||
evo.commit()
|
||||
|
||||
for i = 1, ec do
|
||||
local e = es[i]
|
||||
assert(evo.has(e, f1) and evo.get(e, f1) == 2)
|
||||
assert(evo.has(e, f2) and evo.get(e, f2) == 6)
|
||||
end
|
||||
|
||||
evo.defer()
|
||||
|
||||
local es2, ec2 = evo.multi_clone(10, es[1], nil, function(c, b, e)
|
||||
local f1s, f2s = c:components(f1, f2)
|
||||
for p = b, e do
|
||||
f1s[p] = f1s[p] + 10
|
||||
f2s[p] = f2s[p] + 20
|
||||
end
|
||||
end)
|
||||
|
||||
for i = 1, ec2 do
|
||||
local e = es2[i]
|
||||
assert(not evo.has(e, f1) and evo.get(e, f1) == nil)
|
||||
assert(not evo.has(e, f2) and evo.get(e, f2) == nil)
|
||||
end
|
||||
|
||||
evo.commit()
|
||||
|
||||
for i = 1, ec2 do
|
||||
local e = es2[i]
|
||||
assert(evo.has(e, f1) and evo.get(e, f1) == 12)
|
||||
assert(evo.has(e, f2) and evo.get(e, f2) == 26)
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local f1, f2 = evo.id(2)
|
||||
|
||||
local e1 = evo.builder():set(f1, 1):set(f2, 2):build(nil, function(c, p)
|
||||
local f1s, f2s = c:components(f1, f2)
|
||||
f1s[p] = f1s[p] * 2
|
||||
f2s[p] = f2s[p] * 3
|
||||
end)
|
||||
|
||||
assert(evo.has(e1, f1) and evo.get(e1, f1) == 2)
|
||||
assert(evo.has(e1, f2) and evo.get(e1, f2) == 6)
|
||||
|
||||
local e2 = evo.builder():build(e1, function(c, p)
|
||||
local f1s, f2s = c:components(f1, f2)
|
||||
f1s[p] = f1s[p] + 10
|
||||
f2s[p] = f2s[p] + 20
|
||||
end)
|
||||
|
||||
assert(evo.has(e2, f1) and evo.get(e2, f1) == 12)
|
||||
assert(evo.has(e2, f2) and evo.get(e2, f2) == 26)
|
||||
|
||||
local e3 = evo.builder():set(f2, 3):build(e1, function(c, p)
|
||||
local f1s, f2s = c:components(f1, f2)
|
||||
f1s[p] = f1s[p] + 10
|
||||
f2s[p] = f2s[p] + 20
|
||||
end)
|
||||
|
||||
assert(evo.has(e3, f1) and evo.get(e3, f1) == 12)
|
||||
assert(evo.has(e3, f2) and evo.get(e3, f2) == 23)
|
||||
end
|
||||
|
||||
do
|
||||
local f1, f2 = evo.id(2)
|
||||
|
||||
local es, ec = evo.builder():set(f1, 1):set(f2, 2):multi_build(10, nil, function(c, b, e)
|
||||
local f1s, f2s = c:components(f1, f2)
|
||||
for p = b, e do
|
||||
f1s[p] = f1s[p] * 2
|
||||
f2s[p] = f2s[p] * 3
|
||||
end
|
||||
end)
|
||||
|
||||
for i = 1, ec do
|
||||
local e = es[i]
|
||||
assert(evo.has(e, f1) and evo.get(e, f1) == 2)
|
||||
assert(evo.has(e, f2) and evo.get(e, f2) == 6)
|
||||
end
|
||||
|
||||
local es2, ec2 = evo.builder():multi_build(10, es[1], function(c, b, e)
|
||||
local f1s, f2s = c:components(f1, f2)
|
||||
for p = b, e do
|
||||
f1s[p] = f1s[p] + 10
|
||||
f2s[p] = f2s[p] + 20
|
||||
end
|
||||
end)
|
||||
|
||||
for i = 1, ec2 do
|
||||
local e = es2[i]
|
||||
assert(evo.has(e, f1) and evo.get(e, f1) == 12)
|
||||
assert(evo.has(e, f2) and evo.get(e, f2) == 26)
|
||||
end
|
||||
end
|
||||
588
develop/testing/realloc_tests.lua
Normal file
588
develop/testing/realloc_tests.lua
Normal file
@@ -0,0 +1,588 @@
|
||||
local evo = require 'evolved'
|
||||
|
||||
---@type ffilib?
|
||||
local ffi = (function()
|
||||
local ffi_loader = package and package.preload and package.preload['ffi']
|
||||
local ffi = ffi_loader and ffi_loader()
|
||||
return ffi
|
||||
end)()
|
||||
|
||||
if not ffi then
|
||||
return
|
||||
end
|
||||
|
||||
local FLOAT_TYPEOF = ffi.typeof('float')
|
||||
local FLOAT_SIZEOF = ffi.sizeof(FLOAT_TYPEOF)
|
||||
local FLOAT_STORAGE_TYPEOF = ffi.typeof('$[?]', FLOAT_TYPEOF)
|
||||
|
||||
local DOUBLE_TYPEOF = ffi.typeof('double')
|
||||
local DOUBLE_SIZEOF = ffi.sizeof(DOUBLE_TYPEOF)
|
||||
local DOUBLE_STORAGE_TYPEOF = ffi.typeof('$[?]', DOUBLE_TYPEOF)
|
||||
|
||||
local STORAGE_SIZES = {}
|
||||
|
||||
---@type evolved.realloc
|
||||
local function float_realloc(src, src_size, dst_size)
|
||||
if dst_size == 0 then
|
||||
assert(src and src_size > 0)
|
||||
local expected_src_size = STORAGE_SIZES[src]
|
||||
assert(expected_src_size == src_size)
|
||||
STORAGE_SIZES[src] = nil
|
||||
return
|
||||
else
|
||||
if src then
|
||||
assert(src_size > 0)
|
||||
local expected_src_size = STORAGE_SIZES[src]
|
||||
assert(expected_src_size == src_size)
|
||||
else
|
||||
assert(src_size == 0)
|
||||
end
|
||||
|
||||
local dst = ffi.new(FLOAT_STORAGE_TYPEOF, dst_size + 1)
|
||||
STORAGE_SIZES[dst] = dst_size
|
||||
|
||||
if src then
|
||||
ffi.copy(dst + 1, src + 1, math.min(src_size, dst_size) * FLOAT_SIZEOF)
|
||||
end
|
||||
|
||||
return dst
|
||||
end
|
||||
end
|
||||
|
||||
---@type evolved.realloc
|
||||
local function double_realloc(src, src_size, dst_size)
|
||||
if dst_size == 0 then
|
||||
assert(src and src_size > 0)
|
||||
local expected_src_size = STORAGE_SIZES[src]
|
||||
assert(expected_src_size == src_size)
|
||||
STORAGE_SIZES[src] = nil
|
||||
return
|
||||
else
|
||||
if src then
|
||||
assert(src_size > 0)
|
||||
local expected_src_size = STORAGE_SIZES[src]
|
||||
assert(expected_src_size == src_size)
|
||||
else
|
||||
assert(src_size == 0)
|
||||
end
|
||||
|
||||
local dst = ffi.new(DOUBLE_STORAGE_TYPEOF, dst_size + 1)
|
||||
STORAGE_SIZES[dst] = dst_size
|
||||
|
||||
if src then
|
||||
ffi.copy(dst + 1, src + 1, math.min(src_size, dst_size) * DOUBLE_SIZEOF)
|
||||
end
|
||||
|
||||
return dst
|
||||
end
|
||||
end
|
||||
|
||||
---@type evolved.compmove
|
||||
local function double_compmove(src, f, e, t, dst)
|
||||
ffi.copy(dst + t, src + f, (e - f + 1) * DOUBLE_SIZEOF)
|
||||
end
|
||||
|
||||
do
|
||||
local f1 = evo.builder():realloc(double_realloc):build()
|
||||
|
||||
local e1 = evo.builder():set(f1, 21):build()
|
||||
assert(evo.has(e1, f1) and evo.get(e1, f1) == 21)
|
||||
|
||||
local e2 = evo.builder():set(f1, 42):build()
|
||||
assert(evo.has(e1, f1) and evo.get(e1, f1) == 21)
|
||||
assert(evo.has(e2, f1) and evo.get(e2, f1) == 42)
|
||||
|
||||
local e3 = evo.builder():set(f1, 84):build()
|
||||
assert(evo.has(e1, f1) and evo.get(e1, f1) == 21)
|
||||
assert(evo.has(e2, f1) and evo.get(e2, f1) == 42)
|
||||
assert(evo.has(e3, f1) and evo.get(e3, f1) == 84)
|
||||
|
||||
evo.destroy(e1)
|
||||
assert(not evo.has(e1, f1))
|
||||
assert(evo.has(e2, f1) and evo.get(e2, f1) == 42)
|
||||
assert(evo.has(e3, f1) and evo.get(e3, f1) == 84)
|
||||
|
||||
evo.destroy(e3)
|
||||
assert(not evo.has(e1, f1))
|
||||
assert(evo.has(e2, f1) and evo.get(e2, f1) == 42)
|
||||
assert(not evo.has(e3, f1))
|
||||
|
||||
evo.destroy(e2)
|
||||
assert(not evo.has(e1, f1))
|
||||
assert(not evo.has(e2, f1))
|
||||
assert(not evo.has(e3, f1))
|
||||
end
|
||||
|
||||
do
|
||||
local f1 = evo.builder():realloc(double_realloc):build()
|
||||
local q1 = evo.builder():include(f1):build()
|
||||
|
||||
do
|
||||
local es, ec = {}, 10
|
||||
for i = 1, ec do es[i] = evo.spawn({ [f1] = i }) end
|
||||
for i = 1, ec do assert(evo.has(es[i], f1) and evo.get(es[i], f1) == i) end
|
||||
end
|
||||
|
||||
do
|
||||
local p = evo.builder():set(f1, 42):build()
|
||||
local es, ec = {}, 10
|
||||
for i = 1, ec do es[i] = evo.clone(p) end
|
||||
for i = 1, ec do assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 42) end
|
||||
end
|
||||
|
||||
do
|
||||
local es1, ec1 = evo.multi_spawn(10, { [f1] = 42 })
|
||||
for i = 1, ec1 do assert(evo.has(es1[i], f1) and evo.get(es1[i], f1) == 42) end
|
||||
|
||||
local es2, ec2 = evo.multi_spawn(20, { [f1] = 84 })
|
||||
for i = 1, ec1 do assert(evo.has(es1[i], f1) and evo.get(es1[i], f1) == 42) end
|
||||
for i = 1, ec2 do assert(evo.has(es2[i], f1) and evo.get(es2[i], f1) == 84) end
|
||||
end
|
||||
|
||||
do
|
||||
local p = evo.builder():set(f1, 21):build()
|
||||
|
||||
local es1, ec1 = evo.multi_clone(10, p)
|
||||
for i = 1, ec1 do assert(evo.has(es1[i], f1) and evo.get(es1[i], f1) == 21) end
|
||||
|
||||
local es2, ec2 = evo.multi_clone(20, p)
|
||||
for i = 1, ec1 do assert(evo.has(es1[i], f1) and evo.get(es1[i], f1) == 21) end
|
||||
for i = 1, ec2 do assert(evo.has(es2[i], f1) and evo.get(es2[i], f1) == 21) end
|
||||
end
|
||||
|
||||
evo.batch_destroy(q1)
|
||||
end
|
||||
|
||||
do
|
||||
local f1 = evo.builder():realloc(double_realloc):build()
|
||||
local f2 = evo.builder():realloc(double_realloc):build()
|
||||
|
||||
local q1 = evo.builder():include(f1):build()
|
||||
local q2 = evo.builder():include(f2):build()
|
||||
|
||||
do
|
||||
local e = evo.builder():set(f1, 21):set(f2, 42):build()
|
||||
assert(evo.has(e, f1) and evo.get(e, f1) == 21)
|
||||
assert(evo.has(e, f2) and evo.get(e, f2) == 42)
|
||||
|
||||
evo.remove(e, f1)
|
||||
|
||||
assert(not evo.has(e, f1))
|
||||
assert(evo.has(e, f2) and evo.get(e, f2) == 42)
|
||||
end
|
||||
|
||||
do
|
||||
local e = evo.builder():set(f1, 21):set(f2, 42):build()
|
||||
assert(evo.has(e, f1) and evo.get(e, f1) == 21)
|
||||
assert(evo.has(e, f2) and evo.get(e, f2) == 42)
|
||||
|
||||
evo.clear(e)
|
||||
|
||||
assert(not evo.has(e, f1))
|
||||
assert(not evo.has(e, f2))
|
||||
end
|
||||
|
||||
do
|
||||
local es, ec = evo.multi_spawn(10, { [f1] = 21, [f2] = 42 })
|
||||
|
||||
for i = 1, ec do
|
||||
assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 21)
|
||||
assert(evo.has(es[i], f2) and evo.get(es[i], f2) == 42)
|
||||
end
|
||||
|
||||
evo.batch_remove(q1, f1)
|
||||
|
||||
local e12 = evo.builder():set(f1, 1):set(f2, 2):build()
|
||||
assert(evo.has(e12, f1) and evo.get(e12, f1) == 1)
|
||||
assert(evo.has(e12, f2) and evo.get(e12, f2) == 2)
|
||||
|
||||
for i = 1, ec do
|
||||
assert(not evo.has(es[i], f1))
|
||||
assert(evo.has(es[i], f2) and evo.get(es[i], f2) == 42)
|
||||
end
|
||||
|
||||
evo.batch_set(q2, f1, 84)
|
||||
|
||||
assert(evo.has(e12, f1) and evo.get(e12, f1) == 84)
|
||||
assert(evo.has(e12, f2) and evo.get(e12, f2) == 2)
|
||||
|
||||
for i = 1, ec do
|
||||
assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 84)
|
||||
assert(evo.has(es[i], f2) and evo.get(es[i], f2) == 42)
|
||||
end
|
||||
|
||||
evo.batch_set(q2, f1, 21)
|
||||
|
||||
assert(evo.has(e12, f1) and evo.get(e12, f1) == 21)
|
||||
assert(evo.has(e12, f2) and evo.get(e12, f2) == 2)
|
||||
|
||||
for i = 1, ec do
|
||||
assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 21)
|
||||
assert(evo.has(es[i], f2) and evo.get(es[i], f2) == 42)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local f1 = evo.builder():realloc(double_realloc):compmove(double_compmove):build()
|
||||
local f2 = evo.builder():realloc(double_realloc):compmove(double_compmove):build()
|
||||
|
||||
local q1 = evo.builder():include(f1):build()
|
||||
local q2 = evo.builder():include(f2):build()
|
||||
|
||||
do
|
||||
local es1, ec1 = evo.multi_spawn(10, { [f1] = 1, [f2] = 2 })
|
||||
|
||||
for i = 1, ec1 do
|
||||
assert(evo.has(es1[i], f1) and evo.get(es1[i], f1) == 1)
|
||||
assert(evo.has(es1[i], f2) and evo.get(es1[i], f2) == 2)
|
||||
end
|
||||
|
||||
local es2, ec2 = evo.multi_spawn(20, { [f1] = 3, [f2] = 4 })
|
||||
|
||||
for i = 1, ec1 do
|
||||
assert(evo.has(es1[i], f1) and evo.get(es1[i], f1) == 1)
|
||||
assert(evo.has(es1[i], f2) and evo.get(es1[i], f2) == 2)
|
||||
end
|
||||
|
||||
for i = 1, ec2 do
|
||||
assert(evo.has(es2[i], f1) and evo.get(es2[i], f1) == 3)
|
||||
assert(evo.has(es2[i], f2) and evo.get(es2[i], f2) == 4)
|
||||
end
|
||||
|
||||
local e2 = evo.builder():set(f2, 42):build()
|
||||
assert(evo.has(e2, f2) and evo.get(e2, f2) == 42)
|
||||
|
||||
evo.batch_remove(q1, f1)
|
||||
|
||||
assert(evo.has(e2, f2) and evo.get(e2, f2) == 42)
|
||||
|
||||
for i = 1, ec1 do
|
||||
assert(not evo.has(es1[i], f1))
|
||||
assert(evo.has(es1[i], f2) and evo.get(es1[i], f2) == 2)
|
||||
end
|
||||
|
||||
for i = 1, ec2 do
|
||||
assert(not evo.has(es2[i], f1))
|
||||
assert(evo.has(es2[i], f2) and evo.get(es2[i], f2) == 4)
|
||||
end
|
||||
|
||||
local e12 = evo.builder():set(f1, 21):set(f2, 42):build()
|
||||
|
||||
assert(evo.has(e2, f2) and evo.get(e2, f2) == 42)
|
||||
assert(evo.has(e12, f1) and evo.get(e12, f1) == 21)
|
||||
assert(evo.has(e12, f2) and evo.get(e12, f2) == 42)
|
||||
|
||||
evo.batch_set(q2, f1, 84)
|
||||
|
||||
assert(evo.has(e2, f2) and evo.get(e2, f2) == 42)
|
||||
assert(evo.has(e12, f1) and evo.get(e12, f1) == 84)
|
||||
assert(evo.has(e12, f2) and evo.get(e12, f2) == 42)
|
||||
|
||||
for i = 1, ec1 do
|
||||
assert(evo.has(es1[i], f1) and evo.get(es1[i], f1) == 84)
|
||||
assert(evo.has(es1[i], f2) and evo.get(es1[i], f2) == 2)
|
||||
end
|
||||
|
||||
for i = 1, ec2 do
|
||||
assert(evo.has(es2[i], f1) and evo.get(es2[i], f1) == 84)
|
||||
assert(evo.has(es2[i], f2) and evo.get(es2[i], f2) == 4)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local f1 = evo.builder():default(42):build()
|
||||
|
||||
local es, ec = evo.multi_spawn(10, { [f1] = 21 })
|
||||
for i = 1, ec do assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 21) end
|
||||
|
||||
evo.set(f1, evo.TAG)
|
||||
for i = 1, ec do assert(evo.has(es[i], f1) and evo.get(es[i], f1) == nil) end
|
||||
|
||||
evo.remove(f1, evo.TAG)
|
||||
for i = 1, ec do assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 42) end
|
||||
end
|
||||
|
||||
do
|
||||
local f1 = evo.builder():realloc(float_realloc):build()
|
||||
|
||||
local e1 = evo.builder():set(f1, 3):build()
|
||||
assert(evo.has(e1, f1) and evo.get(e1, f1) == 3)
|
||||
|
||||
evo.set(f1, evo.REALLOC, double_realloc)
|
||||
assert(evo.has(e1, f1) and evo.get(e1, f1) == 3)
|
||||
|
||||
evo.remove(f1, evo.REALLOC)
|
||||
assert(evo.has(e1, f1) and evo.get(e1, f1) == 3)
|
||||
|
||||
evo.set(f1, evo.REALLOC, double_realloc)
|
||||
assert(evo.has(e1, f1) and evo.get(e1, f1) == 3)
|
||||
end
|
||||
|
||||
do
|
||||
local f1 = evo.builder():realloc(double_realloc):build()
|
||||
|
||||
local es, ec = evo.multi_spawn(20, { [f1] = 42 })
|
||||
|
||||
for i = 1, ec / 2 do
|
||||
evo.destroy(es[ec - i + 1])
|
||||
end
|
||||
|
||||
evo.collect_garbage()
|
||||
|
||||
for i = 1, ec / 2 do
|
||||
assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 42)
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
evo.collect_garbage()
|
||||
|
||||
local f1 = evo.builder():name('f1'):realloc(double_realloc):compmove(double_compmove):build()
|
||||
local f2 = evo.builder():name('f2'):realloc(double_realloc):compmove(double_compmove):build()
|
||||
|
||||
local q1 = evo.builder():include(f1):build()
|
||||
local q2 = evo.builder():include(f2):build()
|
||||
|
||||
do
|
||||
local es, ec = evo.multi_spawn(40, { [f2] = 2 })
|
||||
for i = 1, ec do
|
||||
assert(not evo.has(es[i], f1))
|
||||
assert(evo.has(es[i], f2) and evo.get(es[i], f2) == 2)
|
||||
end
|
||||
evo.batch_destroy(q2)
|
||||
end
|
||||
|
||||
do
|
||||
local es, ec = evo.multi_spawn(50, { [f1] = 1, [f2] = 2 })
|
||||
for i = 1, ec do
|
||||
assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 1)
|
||||
assert(evo.has(es[i], f2) and evo.get(es[i], f2) == 2)
|
||||
end
|
||||
|
||||
evo.batch_remove(q1, f1)
|
||||
for i = 1, ec do
|
||||
assert(not evo.has(es[i], f1))
|
||||
assert(evo.has(es[i], f2) and evo.get(es[i], f2) == 2)
|
||||
end
|
||||
|
||||
evo.batch_destroy(q1, q2)
|
||||
end
|
||||
|
||||
do
|
||||
evo.spawn({ [f1] = 1 })
|
||||
evo.spawn({ [f2] = 2 })
|
||||
evo.spawn({ [f1] = 1, [f2] = 2 })
|
||||
end
|
||||
|
||||
evo.collect_garbage()
|
||||
end
|
||||
|
||||
do
|
||||
evo.collect_garbage()
|
||||
|
||||
local f1 = evo.builder():name('f1'):realloc(double_realloc):compmove(double_compmove):build()
|
||||
local f2 = evo.builder():name('f2'):realloc(double_realloc):compmove(double_compmove):build()
|
||||
|
||||
local q1 = evo.builder():include(f1):build()
|
||||
local q2 = evo.builder():include(f2):build()
|
||||
|
||||
do
|
||||
local es, ec = evo.multi_spawn(40, { [f1] = 1, [f2] = 2 })
|
||||
for i = 1, ec do
|
||||
assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 1)
|
||||
assert(evo.has(es[i], f2) and evo.get(es[i], f2) == 2)
|
||||
end
|
||||
evo.batch_destroy(q2)
|
||||
end
|
||||
|
||||
do
|
||||
local es, ec = evo.multi_spawn(50, { [f1] = 1 })
|
||||
for i = 1, ec do
|
||||
assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 1)
|
||||
assert(not evo.has(es[i], f2))
|
||||
end
|
||||
|
||||
evo.batch_set(q1, f2, 2)
|
||||
for i = 1, ec do
|
||||
assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 1)
|
||||
assert(evo.has(es[i], f2) and evo.get(es[i], f2) == 2)
|
||||
end
|
||||
|
||||
evo.batch_destroy(q1, q2)
|
||||
end
|
||||
|
||||
do
|
||||
evo.spawn({ [f1] = 1 })
|
||||
evo.spawn({ [f2] = 2 })
|
||||
evo.spawn({ [f1] = 1, [f2] = 2 })
|
||||
end
|
||||
|
||||
evo.collect_garbage()
|
||||
end
|
||||
|
||||
do
|
||||
evo.collect_garbage()
|
||||
|
||||
local alloc_call_count = 0
|
||||
local free_call_count = 0
|
||||
local resize_call_count = 0
|
||||
|
||||
local function ctor_realloc()
|
||||
---@type evolved.realloc
|
||||
return function(src, src_size, dst_size)
|
||||
if dst_size == 0 then
|
||||
assert(src and src_size > 0)
|
||||
free_call_count = free_call_count + 1
|
||||
return
|
||||
else
|
||||
if src then
|
||||
assert(src_size > 0)
|
||||
resize_call_count = resize_call_count + 1
|
||||
else
|
||||
assert(src_size == 0)
|
||||
alloc_call_count = alloc_call_count + 1
|
||||
end
|
||||
|
||||
local dst = {}
|
||||
|
||||
if src then
|
||||
for i = 1, math.min(src_size, dst_size) do
|
||||
dst[i] = src[i]
|
||||
end
|
||||
end
|
||||
|
||||
return dst
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local realloc1 = ctor_realloc()
|
||||
local realloc2 = ctor_realloc()
|
||||
|
||||
local f1 = evo.builder():default(44):realloc(realloc1):build()
|
||||
|
||||
alloc_call_count, free_call_count, resize_call_count = 0, 0, 0
|
||||
|
||||
do
|
||||
local e1 = evo.builder():set(f1, 21):build()
|
||||
assert(evo.has(e1, f1) and evo.get(e1, f1) == 21)
|
||||
assert(alloc_call_count == 1 and free_call_count == 0)
|
||||
|
||||
local e2 = evo.builder():set(f1, 42):build()
|
||||
assert(evo.has(e1, f1) and evo.get(e1, f1) == 21)
|
||||
assert(evo.has(e2, f1) and evo.get(e2, f1) == 42)
|
||||
assert(alloc_call_count == 1 and free_call_count == 0)
|
||||
|
||||
evo.collect_garbage()
|
||||
assert(alloc_call_count == 1 and free_call_count == 0 and resize_call_count == 0)
|
||||
|
||||
evo.destroy(e1)
|
||||
assert(alloc_call_count == 1 and free_call_count == 0 and resize_call_count == 0)
|
||||
|
||||
evo.collect_garbage()
|
||||
assert(alloc_call_count == 1 and free_call_count == 0 and resize_call_count == 0)
|
||||
|
||||
evo.destroy(e2)
|
||||
assert(alloc_call_count == 1 and free_call_count == 0 and resize_call_count == 0)
|
||||
|
||||
evo.collect_garbage()
|
||||
assert(alloc_call_count == 1 and free_call_count == 1 and resize_call_count == 0)
|
||||
end
|
||||
|
||||
alloc_call_count, free_call_count, resize_call_count = 0, 0, 0
|
||||
|
||||
do
|
||||
local es, ec = evo.multi_spawn(10, { [f1] = 84 })
|
||||
assert(alloc_call_count == 1 and free_call_count == 0 and resize_call_count == 0)
|
||||
|
||||
for i = 1, ec / 2 do evo.destroy(es[i]) end
|
||||
assert(alloc_call_count == 1 and free_call_count == 0 and resize_call_count == 0)
|
||||
|
||||
evo.collect_garbage()
|
||||
assert(alloc_call_count == 1 and free_call_count == 0 and resize_call_count == 1)
|
||||
|
||||
evo.set(f1, evo.REALLOC, realloc2)
|
||||
assert(alloc_call_count == 2 and free_call_count == 1 and resize_call_count == 1)
|
||||
|
||||
for i = 1, ec do evo.destroy(es[i]) end
|
||||
evo.collect_garbage()
|
||||
assert(alloc_call_count == 2 and free_call_count == 2 and resize_call_count == 1)
|
||||
end
|
||||
|
||||
alloc_call_count, free_call_count, resize_call_count = 0, 0, 0
|
||||
|
||||
do
|
||||
local e1 = evo.builder():set(f1, 24):build()
|
||||
assert(evo.has(e1, f1) and evo.get(e1, f1) == 24)
|
||||
assert(alloc_call_count == 1 and free_call_count == 0 and resize_call_count == 0)
|
||||
|
||||
evo.set(f1, evo.TAG)
|
||||
assert(evo.has(e1, f1) and evo.get(e1, f1) == nil)
|
||||
assert(alloc_call_count == 1 and free_call_count == 1 and resize_call_count == 0)
|
||||
|
||||
local es, ec = evo.multi_spawn(20, { [f1] = 48 })
|
||||
for i = 1, ec do assert(evo.has(es[i], f1) and evo.get(es[i], f1) == nil) end
|
||||
assert(alloc_call_count == 1 and free_call_count == 1 and resize_call_count == 0)
|
||||
|
||||
evo.remove(f1, evo.TAG)
|
||||
assert(evo.has(e1, f1) and evo.get(e1, f1) == 44)
|
||||
for i = 1, ec do assert(evo.has(es[i], f1) and evo.get(es[i], f1) == 44) end
|
||||
assert(alloc_call_count == 2 and free_call_count == 1 and resize_call_count == 0)
|
||||
|
||||
evo.destroy(e1)
|
||||
for i = 1, ec do evo.destroy(es[i]) end
|
||||
assert(alloc_call_count == 2 and free_call_count == 1 and resize_call_count == 0)
|
||||
|
||||
evo.collect_garbage()
|
||||
assert(alloc_call_count == 2 and free_call_count == 2 and resize_call_count == 0)
|
||||
end
|
||||
|
||||
alloc_call_count, free_call_count, resize_call_count = 0, 0, 0
|
||||
|
||||
do
|
||||
local e1 = evo.builder():set(f1, 100):build()
|
||||
assert(evo.has(e1, f1) and evo.get(e1, f1) == 100)
|
||||
assert(alloc_call_count == 1 and free_call_count == 0 and resize_call_count == 0)
|
||||
|
||||
evo.set(f1, evo.TAG)
|
||||
assert(evo.has(e1, f1) and evo.get(e1, f1) == nil)
|
||||
assert(alloc_call_count == 1 and free_call_count == 1 and resize_call_count == 0)
|
||||
|
||||
local es, ec = evo.multi_spawn(20, { [f1] = 48 })
|
||||
for i = 1, ec do assert(evo.has(es[i], f1) and evo.get(es[i], f1) == nil) end
|
||||
assert(alloc_call_count == 1 and free_call_count == 1 and resize_call_count == 0)
|
||||
|
||||
evo.destroy(e1)
|
||||
for i = 1, ec do evo.destroy(es[i]) end
|
||||
assert(alloc_call_count == 1 and free_call_count == 1 and resize_call_count == 0)
|
||||
|
||||
evo.collect_garbage()
|
||||
assert(alloc_call_count == 1 and free_call_count == 1 and resize_call_count == 0)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
local realloc = ctor_realloc()
|
||||
|
||||
local f1 = evo.builder():realloc(realloc):build()
|
||||
|
||||
alloc_call_count, free_call_count, resize_call_count = 0, 0, 0
|
||||
|
||||
do
|
||||
local e1 = evo.builder():set(f1, 42):build()
|
||||
assert(evo.has(e1, f1) and evo.get(e1, f1) == 42)
|
||||
assert(alloc_call_count == 1 and free_call_count == 0 and resize_call_count == 0)
|
||||
|
||||
evo.destroy(e1)
|
||||
assert(not evo.has(e1, f1) and evo.get(e1, f1) == nil)
|
||||
assert(alloc_call_count == 1 and free_call_count == 0 and resize_call_count == 0)
|
||||
|
||||
evo.set(f1, evo.TAG)
|
||||
assert(alloc_call_count == 1 and free_call_count == 1 and resize_call_count == 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
56
evolved.d.tl
56
evolved.d.tl
@@ -31,14 +31,30 @@
|
||||
end
|
||||
|
||||
interface Builder
|
||||
build: function(self: Builder, prefab?: Entity): Entity
|
||||
multi_build: function(self: Builder, entity_count: integer, prefab?: Entity): { Entity }, integer
|
||||
build: function(self: Builder,
|
||||
prefab?: Entity,
|
||||
component_mapper?: function(Chunk, integer)): Entity
|
||||
|
||||
spawn: function(self: Builder): Entity
|
||||
multi_spawn: function(self: Builder, entity_count: integer): { Entity }, integer
|
||||
multi_build: function(self: Builder,
|
||||
entity_count: integer,
|
||||
prefab?: Entity,
|
||||
component_mapper?: function(Chunk, integer, integer)): { Entity }, integer
|
||||
|
||||
clone: function(self: Builder, prefab: Entity): Entity
|
||||
multi_clone: function(self: Builder, entity_count: integer, prefab: Entity): { Entity }, integer
|
||||
spawn: function(self: Builder,
|
||||
component_mapper?: function(Chunk, integer)): Entity
|
||||
|
||||
multi_spawn: function(self: Builder,
|
||||
entity_count: integer,
|
||||
component_mapper?: function(Chunk, integer, integer)): { Entity }, integer
|
||||
|
||||
clone: function(self: Builder,
|
||||
prefab: Entity,
|
||||
component_mapper?: function(Chunk, integer)): Entity
|
||||
|
||||
multi_clone: function(self: Builder,
|
||||
entity_count: integer,
|
||||
prefab: Entity,
|
||||
component_mapper?: function(Chunk, integer, integer)): { Entity }, integer
|
||||
|
||||
has: function(self: Builder, fragment: Fragment): boolean
|
||||
has_all: function(self: Builder, ...: Fragment): boolean
|
||||
@@ -64,6 +80,9 @@
|
||||
default: function<Component>(self: Builder, default: Component): Builder
|
||||
duplicate: function<Component>(self: Builder, duplicate: function(Component): Component): Builder
|
||||
|
||||
realloc: function<Component>(self: Builder, realloc: function({ Component } | nil, integer, integer): { Component } | nil): Builder
|
||||
compmove: function<Component>(self: Builder, compmove: function({ Component }, integer, integer, integer, { Component })): Builder
|
||||
|
||||
prefab: function(self: Builder): Builder
|
||||
disabled: function(self: Builder): Builder
|
||||
|
||||
@@ -98,6 +117,9 @@
|
||||
DEFAULT: Fragment
|
||||
DUPLICATE: Fragment
|
||||
|
||||
REALLOC: Fragment
|
||||
COMPMOVE: Fragment
|
||||
|
||||
PREFAB: Fragment
|
||||
DISABLED: Fragment
|
||||
|
||||
@@ -133,11 +155,25 @@
|
||||
commit: function(): boolean
|
||||
cancel: function(): boolean
|
||||
|
||||
spawn: function(components?: { Fragment: any }): Entity
|
||||
multi_spawn: function(entity_count: integer, components?: { Fragment: any }): { Entity }, integer
|
||||
spawn: function(
|
||||
component_table?: { Fragment: any },
|
||||
component_mapper?: function(Chunk, integer)): Entity
|
||||
|
||||
clone: function(prefab: Entity, components?: { Fragment: any }): Entity
|
||||
multi_clone: function(entity_count: integer, prefab: Entity, components?: { Fragment: any }): { Entity }, integer
|
||||
multi_spawn: function(
|
||||
entity_count: integer,
|
||||
component_table?: { Fragment: any },
|
||||
component_mapper?: function(Chunk, integer, integer)): { Entity }, integer
|
||||
|
||||
clone: function(
|
||||
prefab: Entity,
|
||||
component_table?: { Fragment: any },
|
||||
component_mapper?: function(Chunk, integer)): Entity
|
||||
|
||||
multi_clone: function(
|
||||
entity_count: integer,
|
||||
prefab: Entity,
|
||||
component_table?: { Fragment: any },
|
||||
component_mapper?: function(Chunk, integer, integer)): { Entity }, integer
|
||||
|
||||
alive: function(entity: Entity): boolean
|
||||
alive_all: function(...: Entity): boolean
|
||||
|
||||
1081
evolved.lua
1081
evolved.lua
File diff suppressed because it is too large
Load Diff
@@ -52,25 +52,28 @@ evolved.builder()
|
||||
:name('SYSTEMS.STARTUP')
|
||||
:group(STAGES.ON_SETUP)
|
||||
:prologue(function()
|
||||
local screen_width, screen_height = love.graphics.getDimensions()
|
||||
evolved.multi_clone(500, PREFABS.CIRCLE, nil, function(chunk, b_place, e_place)
|
||||
local screen_width, screen_height = love.graphics.getDimensions()
|
||||
|
||||
local circle_list, circle_count = evolved.multi_clone(100, PREFABS.CIRCLE)
|
||||
---@type number[], number[]
|
||||
local position_xs, position_ys = chunk:components(
|
||||
FRAGMENTS.POSITION_X, FRAGMENTS.POSITION_Y)
|
||||
|
||||
for i = 1, circle_count do
|
||||
local circle = circle_list[i]
|
||||
---@type number[], number[]
|
||||
local velocity_xs, velocity_ys = chunk:components(
|
||||
FRAGMENTS.VELOCITY_X, FRAGMENTS.VELOCITY_Y)
|
||||
|
||||
local px = math.random() * screen_width
|
||||
local py = math.random() * screen_height
|
||||
for i = b_place, e_place do
|
||||
local px = math.random() * screen_width
|
||||
local py = math.random() * screen_height
|
||||
|
||||
local vx = math.random(-100, 100)
|
||||
local vy = math.random(-100, 100)
|
||||
local vx = math.random(-100, 100)
|
||||
local vy = math.random(-100, 100)
|
||||
|
||||
evolved.set(circle, FRAGMENTS.POSITION_X, px)
|
||||
evolved.set(circle, FRAGMENTS.POSITION_Y, py)
|
||||
|
||||
evolved.set(circle, FRAGMENTS.VELOCITY_X, vx)
|
||||
evolved.set(circle, FRAGMENTS.VELOCITY_Y, vy)
|
||||
end
|
||||
position_xs[i], position_ys[i] = px, py
|
||||
velocity_xs[i], velocity_ys[i] = vx, vy
|
||||
end
|
||||
end)
|
||||
end):build()
|
||||
|
||||
evolved.builder()
|
||||
@@ -124,7 +127,7 @@ evolved.builder()
|
||||
|
||||
for i = 1, entity_count do
|
||||
local x, y = position_xs[i], position_ys[i]
|
||||
love.graphics.circle('fill', x, y, 10)
|
||||
love.graphics.circle('fill', x, y, 5)
|
||||
end
|
||||
end):build()
|
||||
|
||||
|
||||
34
rockspecs/evolved.lua-1.8.0-0.rockspec
Normal file
34
rockspecs/evolved.lua-1.8.0-0.rockspec
Normal file
@@ -0,0 +1,34 @@
|
||||
rockspec_format = "3.0"
|
||||
package = "evolved.lua"
|
||||
version = "1.8.0-0"
|
||||
source = {
|
||||
url = "git://github.com/BlackMATov/evolved.lua",
|
||||
tag = "v1.8.0",
|
||||
}
|
||||
description = {
|
||||
homepage = "https://github.com/BlackMATov/evolved.lua",
|
||||
summary = "Evolved ECS (Entity-Component-System) for Lua",
|
||||
detailed = [[
|
||||
`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.
|
||||
]],
|
||||
license = "MIT",
|
||||
labels = {
|
||||
"ecs",
|
||||
"entity",
|
||||
"entities",
|
||||
"component",
|
||||
"components",
|
||||
"entity-component",
|
||||
"entity-component-system",
|
||||
},
|
||||
}
|
||||
dependencies = {
|
||||
"lua >= 5.1",
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
evolved = "evolved.lua",
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user