Merge branch 'dev' into feature/schemes

This commit is contained in:
BlackMATov
2025-09-12 05:45:33 +07:00
8 changed files with 1712 additions and 550 deletions

View File

@@ -1103,7 +1103,10 @@ defer :: boolean
commit :: boolean
spawn :: <fragment, component>? -> entity
clone :: entity -> <fragment, component>? -> entity
multi_spawn :: integer, <fragment, component>? -> entity[]
clone :: entity, <fragment, component>? -> entity
multi_clone :: integer, entity, <fragment, component>? -> entity[]
alive :: entity -> boolean
alive_all :: entity... -> boolean
@@ -1163,7 +1166,10 @@ chunk_mt:components :: fragment... -> storage...
builder :: builder
builder_mt:spawn :: entity
builder_mt:multi_spawn :: integer -> entity[]
builder_mt:clone :: entity -> entity
builder_mt:multi_clone :: integer, entity -> entity[]
builder_mt:has :: fragment -> boolean
builder_mt:has_all :: fragment... -> boolean
@@ -1214,9 +1220,10 @@ builder_mt:destruction_policy :: id -> builder
# Changelog
## vX.X.X
## 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
@@ -1338,19 +1345,38 @@ function evolved.commit() end
```lua
---@param components? table<evolved.fragment, evolved.component>
---@return evolved.entity
---@return evolved.entity entity
function evolved.spawn(components) end
```
### `evolved.multi_spawn`
```lua
---@param entity_count integer
---@param components? table<evolved.fragment, evolved.component>
---@return evolved.entity[] entity_list
function evolved.multi_spawn(entity_count, components) end
```
### `evolved.clone`
```lua
---@param prefab evolved.entity
---@param components? table<evolved.fragment, evolved.component>
---@return evolved.entity
---@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<evolved.fragment, evolved.component>
---@return evolved.entity[] entity_list
function evolved.multi_clone(entity_count, prefab, components) end
```
### `evolved.alive`
```lua
@@ -1646,18 +1672,35 @@ function evolved.builder() end
#### `evolved.builder_mt:spawn`
```lua
---@return evolved.entity
---@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
function evolved.builder_mt:multi_spawn(entity_count) end
```
#### `evolved.builder_mt:clone`
```lua
---@param prefab evolved.entity
---@return 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
function evolved.builder_mt:multi_clone(entity_count, prefab) end
```
#### `evolved.builder_mt:has`
```lua

View File

@@ -5,7 +5,6 @@
- Improve the performance of required fragments by caching first-level required chunks.
- Improve the performance of builders that are used multiple times by caching hint chunks.
- Queries can cache major chunks to avoid finding them every time.
- Add multi-spawn to the builder to spawn multiple entities at once.
- Add a function to shrink storages to free unused memory.
- observers and events
- add INDEX fragment trait

View File

@@ -1,10 +1,14 @@
require 'develop.samples.systems'
require 'develop.testing.multi_spawn_tests'
require 'develop.testing.name_tests'
require 'develop.testing.requires_fragment_tests'
require 'develop.testing.scheme_tests'
require 'develop.testing.system_as_query_tests'
require 'develop.benchmarks.multi_clone_bmarks'
require 'develop.benchmarks.multi_spawn_bmarks'
require 'develop.untests'
require 'develop.unbench'

View File

@@ -0,0 +1,59 @@
local evo = require 'evolved'
local basics = require 'develop.basics'
evo.debug_mode(false)
local N = 1000
local F1, F2, F3, F4, F5 = evo.id(5)
local Q1 = evo.builder():include(F1):spawn()
print '----------------------------------------'
basics.describe_bench(string.format('Multi Clone Benchmarks: Simple Clone | %d entities with 1 component', N),
function()
local clone = evo.clone
local prefab = evo.spawn { [F1] = true }
for _ = 1, N do
clone(prefab)
end
evo.batch_destroy(Q1)
end)
basics.describe_bench(string.format('Multi Clone Benchmarks: Simple Clone | %d entities with 5 components', N),
function()
local clone = evo.clone
local prefab = evo.spawn { [F1] = true, [F2] = true, [F3] = true, [F4] = true, [F5] = true }
for _ = 1, N do
clone(prefab)
end
evo.batch_destroy(Q1)
end)
basics.describe_bench(string.format('Multi Clone Benchmarks: Multi Clone | %d entities with 1 component', N),
function()
local multi_clone = evo.multi_clone
local prefab = evo.spawn { [F1] = true }
multi_clone(N, prefab)
evo.batch_destroy(Q1)
end)
basics.describe_bench(string.format('Multi Clone Benchmarks: Multi Clone | %d entities with 5 components', N),
function()
local multi_clone = evo.multi_clone
local prefab = evo.spawn { [F1] = true, [F2] = true, [F3] = true, [F4] = true, [F5] = true }
multi_clone(N, prefab)
evo.batch_destroy(Q1)
end)

View File

@@ -0,0 +1,59 @@
local evo = require 'evolved'
local basics = require 'develop.basics'
evo.debug_mode(false)
local N = 1000
local F1, F2, F3, F4, F5 = evo.id(5)
local Q1 = evo.builder():include(F1):spawn()
print '----------------------------------------'
basics.describe_bench(string.format('Multi Spawn Benchmarks: Simple Spawn | %d entities with 1 component', N),
function()
local spawn = evo.spawn
local components = { [F1] = true }
for _ = 1, N do
spawn(components)
end
evo.batch_destroy(Q1)
end)
basics.describe_bench(string.format('Multi Spawn Benchmarks: Simple Spawn | %d entities with 5 components', N),
function()
local spawn = evo.spawn
local components = { [F1] = true, [F2] = true, [F3] = true, [F4] = true, [F5] = true }
for _ = 1, N do
spawn(components)
end
evo.batch_destroy(Q1)
end)
basics.describe_bench(string.format('Multi Spawn Benchmarks: Multi Spawn | %d entities with 1 component', N),
function()
local multi_spawn = evo.multi_spawn
local components = { [F1] = true }
multi_spawn(N, components)
evo.batch_destroy(Q1)
end)
basics.describe_bench(string.format('Multi Spawn Benchmarks: Multi Spawn | %d entities with 5 components', N),
function()
local multi_spawn = evo.multi_spawn
local components = { [F1] = true, [F2] = true, [F3] = true, [F4] = true, [F5] = true }
multi_spawn(N, components)
evo.batch_destroy(Q1)
end)

View File

@@ -0,0 +1,308 @@
local evo = require 'evolved'
do
local entity_list
do
entity_list = evo.multi_spawn(0)
assert(entity_list and #entity_list == 0)
entity_list = evo.multi_spawn(0, {})
assert(entity_list and #entity_list == 0)
end
do
entity_list = evo.multi_spawn(-1)
assert(entity_list and #entity_list == 0)
entity_list = evo.multi_spawn(-1, {})
assert(entity_list and #entity_list == 0)
end
do
entity_list = evo.builder():multi_spawn(0)
assert(entity_list and #entity_list == 0)
end
do
entity_list = evo.builder():multi_spawn(-1)
assert(entity_list and #entity_list == 0)
end
end
do
local entity_list
do
entity_list = evo.multi_spawn(1)
assert(entity_list and #entity_list == 1)
assert(entity_list[1] and evo.empty(entity_list[1]))
assert(not entity_list[2])
entity_list = evo.multi_spawn(1, {})
assert(entity_list and #entity_list == 1)
assert(entity_list[1] and evo.empty(entity_list[1]))
assert(not entity_list[2])
end
do
entity_list = evo.multi_spawn(2)
assert(entity_list and #entity_list == 2)
assert(entity_list[1] and evo.empty(entity_list[1]))
assert(entity_list[2] and evo.empty(entity_list[2]))
assert(not entity_list[3])
entity_list = evo.multi_spawn(2, {})
assert(entity_list and #entity_list == 2)
assert(entity_list[1] and evo.empty(entity_list[1]))
assert(entity_list[2] and evo.empty(entity_list[2]))
assert(not entity_list[3])
end
do
entity_list = evo.builder():multi_spawn(1)
assert(entity_list and #entity_list == 1)
assert(entity_list[1] and evo.empty(entity_list[1]))
assert(not entity_list[2])
end
do
entity_list = evo.builder():multi_spawn(2)
assert(entity_list and #entity_list == 2)
assert(entity_list[1] and evo.empty(entity_list[1]))
assert(entity_list[2] and evo.empty(entity_list[2]))
assert(not entity_list[3])
end
end
do
local entity_list
local prefab = evo.id()
do
entity_list = evo.multi_clone(0, prefab)
assert(entity_list and #entity_list == 0)
entity_list = evo.multi_clone(0, prefab, {})
assert(entity_list and #entity_list == 0)
end
do
entity_list = evo.multi_clone(-1, prefab)
assert(entity_list and #entity_list == 0)
entity_list = evo.multi_clone(-1, prefab, {})
assert(entity_list and #entity_list == 0)
end
do
entity_list = evo.builder():multi_clone(0, prefab)
assert(entity_list and #entity_list == 0)
end
do
entity_list = evo.builder():multi_clone(-1, prefab)
assert(entity_list and #entity_list == 0)
end
end
do
local entity_list
local prefab = evo.id()
do
entity_list = evo.multi_clone(1, prefab)
assert(entity_list and #entity_list == 1)
assert(entity_list[1] and evo.empty(entity_list[1]))
assert(not entity_list[2])
entity_list = evo.multi_clone(1, prefab, {})
assert(entity_list and #entity_list == 1)
assert(entity_list[1] and evo.empty(entity_list[1]))
assert(not entity_list[2])
end
do
entity_list = evo.multi_clone(2, prefab)
assert(entity_list and #entity_list == 2)
assert(entity_list[1] and evo.empty(entity_list[1]))
assert(entity_list[2] and evo.empty(entity_list[2]))
assert(not entity_list[3])
entity_list = evo.multi_clone(2, prefab, {})
assert(entity_list and #entity_list == 2)
assert(entity_list[1] and evo.empty(entity_list[1]))
assert(entity_list[2] and evo.empty(entity_list[2]))
assert(not entity_list[3])
end
do
entity_list = evo.builder():multi_clone(1, prefab)
assert(entity_list and #entity_list == 1)
assert(entity_list[1] and evo.empty(entity_list[1]))
assert(not entity_list[2])
end
do
entity_list = evo.builder():multi_clone(2, prefab)
assert(entity_list and #entity_list == 2)
assert(entity_list[1] and evo.empty(entity_list[1]))
assert(entity_list[2] and evo.empty(entity_list[2]))
assert(not entity_list[3])
end
end
do
local f1, f2 = evo.id(2)
do
local entity_list
entity_list = evo.multi_spawn(2, { [f1] = true, [f2] = 123 })
assert(entity_list and #entity_list == 2)
assert(entity_list[1] and evo.get(entity_list[1], f1) == true and evo.get(entity_list[1], f2) == 123)
assert(entity_list[2] and evo.get(entity_list[2], f1) == true and evo.get(entity_list[2], f2) == 123)
entity_list = evo.multi_spawn(2, { [f1] = false, [f2] = 456 })
assert(entity_list and #entity_list == 2)
assert(entity_list[1] and evo.get(entity_list[1], f1) == false and evo.get(entity_list[1], f2) == 456)
assert(entity_list[2] and evo.get(entity_list[2], f1) == false and evo.get(entity_list[2], f2) == 456)
end
do
local prefab = evo.builder():set(f1, true):set(f2, 123):spawn()
local entity_list
entity_list = evo.multi_clone(2, prefab)
assert(entity_list and #entity_list == 2)
assert(entity_list[1] and evo.get(entity_list[1], f1) == true and evo.get(entity_list[1], f2) == 123)
assert(entity_list[2] and evo.get(entity_list[2], f1) == true and evo.get(entity_list[2], f2) == 123)
entity_list = evo.multi_clone(2, prefab, {})
assert(entity_list and #entity_list == 2)
assert(entity_list[1] and evo.get(entity_list[1], f1) == true and evo.get(entity_list[1], f2) == 123)
assert(entity_list[2] and evo.get(entity_list[2], f1) == true and evo.get(entity_list[2], f2) == 123)
entity_list = evo.multi_clone(2, prefab, { [f1] = false, [f2] = 456 })
assert(entity_list and #entity_list == 2)
assert(entity_list[1] and evo.get(entity_list[1], f1) == false and evo.get(entity_list[1], f2) == 456)
assert(entity_list[2] and evo.get(entity_list[2], f1) == false and evo.get(entity_list[2], f2) == 456)
end
end
do
local f1, f2, f3 = evo.id(3)
do
local entity_list1, entity_list2
evo.defer()
do
entity_list1 = evo.multi_spawn(2, { [f1] = 42, [f2] = "hello", [f3] = false })
assert(entity_list1 and #entity_list1 == 2)
assert(entity_list1[1] and evo.empty(entity_list1[1]))
assert(entity_list1[2] and evo.empty(entity_list1[2]))
assert(not entity_list1[3])
entity_list2 = evo.multi_spawn(3, { [f2] = "world", [f3] = true })
assert(entity_list2 and #entity_list2 == 3)
assert(entity_list2[1] and evo.empty(entity_list2[1]))
assert(entity_list2[2] and evo.empty(entity_list2[2]))
assert(entity_list2[3] and evo.empty(entity_list2[3]))
end
evo.commit()
do
assert(entity_list1 and #entity_list1 == 2)
assert(entity_list1[1] and not evo.empty(entity_list1[1]))
assert(entity_list1[2] and not evo.empty(entity_list1[2]))
assert(not entity_list1[3])
assert(
evo.get(entity_list1[1], f1) == 42 and
evo.get(entity_list1[1], f2) == "hello" and
evo.get(entity_list1[1], f3) == false)
assert(
evo.get(entity_list1[2], f1) == 42 and
evo.get(entity_list1[2], f2) == "hello" and
evo.get(entity_list1[2], f3) == false)
assert(entity_list2 and #entity_list2 == 3)
assert(entity_list2[1] and not evo.empty(entity_list2[1]))
assert(entity_list2[2] and not evo.empty(entity_list2[2]))
assert(entity_list2[3] and not evo.empty(entity_list2[3]))
assert(not entity_list2[4])
assert(
evo.get(entity_list2[1], f1) == nil and
evo.get(entity_list2[1], f2) == "world" and
evo.get(entity_list2[1], f3) == true)
assert(
evo.get(entity_list2[2], f1) == nil and
evo.get(entity_list2[2], f2) == "world" and
evo.get(entity_list2[2], f3) == true)
assert(
evo.get(entity_list2[3], f1) == nil and
evo.get(entity_list2[3], f2) == "world" and
evo.get(entity_list2[3], f3) == true)
end
end
end
do
local f1, f2, f3 = evo.id(3)
do
local prefab = evo.builder():set(f1, false):set(f2, 123):spawn()
local entity_list1, entity_list2
evo.defer()
do
entity_list1 = evo.multi_clone(2, prefab)
assert(entity_list1 and #entity_list1 == 2)
assert(entity_list1[1] and evo.empty(entity_list1[1]))
assert(entity_list1[2] and evo.empty(entity_list1[2]))
assert(not entity_list1[3])
entity_list2 = evo.multi_clone(3, prefab, { [f2] = 456, [f3] = "world" })
assert(entity_list2 and #entity_list2 == 3)
assert(entity_list2[1] and evo.empty(entity_list2[1]))
assert(entity_list2[2] and evo.empty(entity_list2[2]))
assert(entity_list2[3] and evo.empty(entity_list2[3]))
end
evo.commit()
do
assert(entity_list1 and #entity_list1 == 2)
assert(entity_list1[1] and not evo.empty(entity_list1[1]))
assert(entity_list1[2] and not evo.empty(entity_list1[2]))
assert(not entity_list1[3])
assert(
evo.get(entity_list1[1], f1) == false and
evo.get(entity_list1[1], f2) == 123 and
evo.get(entity_list1[1], f3) == nil)
assert(
evo.get(entity_list1[2], f1) == false and
evo.get(entity_list1[2], f2) == 123 and
evo.get(entity_list1[2], f3) == nil)
assert(entity_list2 and #entity_list2 == 3)
assert(entity_list2[1] and not evo.empty(entity_list2[1]))
assert(entity_list2[2] and not evo.empty(entity_list2[2]))
assert(entity_list2[3] and not evo.empty(entity_list2[3]))
assert(not entity_list2[4])
assert(
evo.get(entity_list2[1], f1) == false and
evo.get(entity_list2[1], f2) == 456 and
evo.get(entity_list2[1], f3) == "world")
assert(
evo.get(entity_list2[2], f1) == false and
evo.get(entity_list2[2], f2) == 456 and
evo.get(entity_list2[2], f3) == "world")
assert(
evo.get(entity_list2[3], f1) == false and
evo.get(entity_list2[3], f2) == 456 and
evo.get(entity_list2[3], f3) == "world")
end
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,34 @@
rockspec_format = "3.0"
package = "evolved.lua"
version = "1.2.0-0"
source = {
url = "git://github.com/BlackMATov/evolved.lua",
tag = "v1.2.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",
}
}