mirror of
https://github.com/BlackMATov/evolved.lua.git
synced 2026-02-04 06:37:06 +07:00
Compare commits
6 Commits
8d88d55267
...
7ce5ee924c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ce5ee924c | ||
|
|
539a62c8a8 | ||
|
|
63a7ab5c79 | ||
|
|
9a9fb1ddb9 | ||
|
|
7b44740803 | ||
|
|
b0d035c0d4 |
@@ -63,7 +63,7 @@
|
||||
- [Chunk](#chunk)
|
||||
- [Builder](#builder)
|
||||
- [Changelog](#changelog)
|
||||
- [vX.Y.Z](#vxyz)
|
||||
- [v1.9.0](#v190)
|
||||
- [v1.8.0](#v180)
|
||||
- [v1.7.0](#v170)
|
||||
- [v1.6.0](#v160)
|
||||
@@ -1566,9 +1566,10 @@ builder_mt:destruction_policy :: id -> builder
|
||||
|
||||
## Changelog
|
||||
|
||||
### vX.Y.Z
|
||||
### v1.9.0
|
||||
|
||||
- Performance improvements of the [`evolved.destroy`](#evolveddestroy) and [`evolved.batch_destroy`](#evolvedbatch_destroy) functions
|
||||
- Ensured deterministic chunk ordering to improve processing consistency across runs
|
||||
|
||||
### v1.8.0
|
||||
|
||||
|
||||
@@ -9,11 +9,7 @@
|
||||
## 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
|
||||
|
||||
@@ -2302,27 +2302,66 @@ do
|
||||
evo.set(e2, f2, 44)
|
||||
|
||||
do
|
||||
local iter, state = evo.execute(q)
|
||||
local chunk = iter(state)
|
||||
assert(chunk and chunk ~= evo.chunk(f1))
|
||||
local e1_count = 0
|
||||
local e2_count = 0
|
||||
|
||||
for _, entity_list, entity_count in evo.execute(q) do
|
||||
for i = 1, entity_count do
|
||||
if entity_list[i] == e1 then
|
||||
e1_count = e1_count + 1
|
||||
elseif entity_list[i] == e2 then
|
||||
e2_count = e2_count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assert(e1_count == 1)
|
||||
assert(e2_count == 1)
|
||||
end
|
||||
|
||||
evo.set(q, evo.EXCLUDES, { f2 })
|
||||
|
||||
do
|
||||
local iter, state = evo.execute(q)
|
||||
local chunk = iter(state)
|
||||
assert(chunk and chunk ~= evo.chunk(f1))
|
||||
local e1_count = 0
|
||||
local e2_count = 0
|
||||
|
||||
for chunk, entity_list, entity_count in evo.execute(q) do
|
||||
assert(not chunk:has(f2))
|
||||
|
||||
for i = 1, entity_count do
|
||||
if entity_list[i] == e1 then
|
||||
e1_count = e1_count + 1
|
||||
elseif entity_list[i] == e2 then
|
||||
e2_count = e2_count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assert(e1_count == 1)
|
||||
assert(e2_count == 0)
|
||||
end
|
||||
|
||||
evo.set(q, evo.INCLUDES, { f1 })
|
||||
|
||||
do
|
||||
local iter, state = evo.execute(q)
|
||||
local chunk, entity_list, entity_count = iter(state)
|
||||
assert(chunk == evo.chunk(f1))
|
||||
assert(entity_list and entity_list[1] == e1)
|
||||
assert(entity_count == 1)
|
||||
local e1_count = 0
|
||||
local e2_count = 0
|
||||
|
||||
for chunk, entity_list, entity_count in evo.execute(q) do
|
||||
assert(chunk:has(f1))
|
||||
assert(not chunk:has(f2))
|
||||
|
||||
for i = 1, entity_count do
|
||||
if entity_list[i] == e1 then
|
||||
e1_count = e1_count + 1
|
||||
elseif entity_list[i] == e2 then
|
||||
e2_count = e2_count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assert(e1_count == 1)
|
||||
assert(e2_count == 0)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
269
evolved.lua
269
evolved.lua
@@ -1,7 +1,7 @@
|
||||
local evolved = {
|
||||
__HOMEPAGE = 'https://github.com/BlackMATov/evolved.lua',
|
||||
__DESCRIPTION = 'Evolved ECS (Entity-Component-System) for Lua',
|
||||
__VERSION = '1.8.0',
|
||||
__VERSION = '1.9.0',
|
||||
__LICENSE = [[
|
||||
MIT License
|
||||
|
||||
@@ -131,7 +131,10 @@ local __defer_points = {} ---@type integer[]
|
||||
local __defer_length = 0 ---@type integer
|
||||
local __defer_bytecode = {} ---@type any[]
|
||||
|
||||
local __root_chunks = {} ---@type table<evolved.fragment, evolved.chunk>
|
||||
local __root_set = {} ---@type table<evolved.fragment, integer>
|
||||
local __root_list = {} ---@type evolved.chunk[]
|
||||
local __root_count = 0 ---@type integer
|
||||
|
||||
local __major_chunks = {} ---@type table<evolved.fragment, evolved.assoc_list<evolved.chunk>>
|
||||
local __minor_chunks = {} ---@type table<evolved.fragment, evolved.assoc_list<evolved.chunk>>
|
||||
|
||||
@@ -699,6 +702,19 @@ local __table_pool_tag = {
|
||||
__count = 9,
|
||||
}
|
||||
|
||||
---@type table<evolved.table_pool_tag, integer>
|
||||
local __table_pool_reserve = {
|
||||
[__table_pool_tag.bytecode] = 16,
|
||||
[__table_pool_tag.chunk_list] = 16,
|
||||
[__table_pool_tag.system_list] = 16,
|
||||
[__table_pool_tag.each_state] = 16,
|
||||
[__table_pool_tag.execute_state] = 16,
|
||||
[__table_pool_tag.entity_list] = 16,
|
||||
[__table_pool_tag.fragment_set] = 16,
|
||||
[__table_pool_tag.fragment_list] = 16,
|
||||
[__table_pool_tag.component_table] = 16,
|
||||
}
|
||||
|
||||
---@class (exact) evolved.table_pool
|
||||
---@field package __size integer
|
||||
---@field package [integer] table
|
||||
@@ -706,14 +722,20 @@ local __table_pool_tag = {
|
||||
---@type table<evolved.table_pool_tag, evolved.table_pool>
|
||||
local __tagged_table_pools = (function()
|
||||
local table_pools = __lua_table_new(__table_pool_tag.__count)
|
||||
local table_pool_reserve = 16
|
||||
|
||||
for tag = 1, __table_pool_tag.__count do
|
||||
for table_pool_tag = 1, __table_pool_tag.__count do
|
||||
local table_pool_reserve = __table_pool_reserve[table_pool_tag]
|
||||
|
||||
---@type evolved.table_pool
|
||||
local table_pool = __lua_table_new(table_pool_reserve)
|
||||
for i = 1, table_pool_reserve do table_pool[i] = {} end
|
||||
|
||||
for table_pool_index = 1, table_pool_reserve do
|
||||
table_pool[table_pool_index] = {}
|
||||
end
|
||||
|
||||
table_pool.__size = table_pool_reserve
|
||||
table_pools[tag] = table_pool
|
||||
|
||||
table_pools[table_pool_tag] = table_pool
|
||||
end
|
||||
|
||||
return table_pools
|
||||
@@ -765,6 +787,7 @@ end
|
||||
|
||||
local __list_new
|
||||
local __list_dup
|
||||
local __list_lwr
|
||||
|
||||
---@param reserve? integer
|
||||
---@return any[]
|
||||
@@ -793,6 +816,42 @@ function __list_dup(list)
|
||||
return dup_list
|
||||
end
|
||||
|
||||
---@generic V
|
||||
---@param list V[]
|
||||
---@param item V
|
||||
---@param comp? fun(a: V, b: V): boolean
|
||||
---@return integer lower_bound_index
|
||||
---@nodiscard
|
||||
function __list_lwr(list, item, comp)
|
||||
local lower, upper = 1, #list
|
||||
|
||||
if comp then
|
||||
while lower <= upper do
|
||||
local middle = lower + (upper - lower) / 2
|
||||
middle = middle - middle % 1 -- fast math.floor
|
||||
|
||||
if comp(item, list[middle]) then
|
||||
upper = middle - 1
|
||||
else
|
||||
lower = middle + 1
|
||||
end
|
||||
end
|
||||
else
|
||||
while lower <= upper do
|
||||
local middle = lower + (upper - lower) / 2
|
||||
middle = middle - middle % 1 -- fast math.floor
|
||||
|
||||
if item < list[middle] then
|
||||
upper = middle - 1
|
||||
else
|
||||
lower = middle + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return lower
|
||||
end
|
||||
|
||||
---
|
||||
---
|
||||
---
|
||||
@@ -1108,6 +1167,11 @@ local __new_chunk
|
||||
local __default_realloc
|
||||
local __default_compmove
|
||||
|
||||
local __add_root_chunk
|
||||
local __remove_root_chunk
|
||||
local __add_child_chunk
|
||||
local __remove_child_chunk
|
||||
|
||||
local __update_chunk_caches
|
||||
local __update_chunk_queries
|
||||
local __update_chunk_storages
|
||||
@@ -1269,23 +1333,10 @@ function __new_chunk(chunk_parent, chunk_fragment)
|
||||
__has_required_fragments = false,
|
||||
}, __chunk_mt)
|
||||
|
||||
if chunk_parent then
|
||||
chunk.__parent = chunk_parent
|
||||
|
||||
chunk_parent.__child_count = __assoc_list_insert_ex(
|
||||
chunk_parent.__child_set, chunk_parent.__child_list, chunk_parent.__child_count,
|
||||
chunk)
|
||||
|
||||
chunk_parent.__with_fragment_edges[chunk_fragment] = chunk
|
||||
chunk.__without_fragment_edges[chunk_fragment] = chunk_parent
|
||||
end
|
||||
|
||||
if not chunk_parent then
|
||||
if __root_chunks[chunk_fragment] ~= nil then
|
||||
__error_fmt('unexpected root chunk %s',
|
||||
__lua_tostring(__root_chunks[chunk_fragment]))
|
||||
end
|
||||
__root_chunks[chunk_fragment] = chunk
|
||||
__add_root_chunk(chunk)
|
||||
else
|
||||
__add_child_chunk(chunk, chunk_parent)
|
||||
end
|
||||
|
||||
do
|
||||
@@ -1352,6 +1403,107 @@ function __default_compmove(src, f, e, t, dst)
|
||||
__lua_table_move(src, f, e, t, dst)
|
||||
end
|
||||
|
||||
---@param root evolved.chunk
|
||||
function __add_root_chunk(root)
|
||||
local root_index = __list_lwr(__root_list, root, function(a, b)
|
||||
return a.__fragment < b.__fragment
|
||||
end)
|
||||
|
||||
for sib_root_index = __root_count, root_index, -1 do
|
||||
local sib_root = __root_list[sib_root_index]
|
||||
__root_set[sib_root.__fragment] = sib_root_index + 1
|
||||
__root_list[sib_root_index + 1] = sib_root
|
||||
end
|
||||
|
||||
__root_set[root.__fragment] = root_index
|
||||
__root_list[root_index] = root
|
||||
__root_count = __root_count + 1
|
||||
end
|
||||
|
||||
---@param root evolved.chunk
|
||||
function __remove_root_chunk(root)
|
||||
if root.__parent then
|
||||
__error_fmt('unexpected root chunk: (%s)',
|
||||
__lua_tostring(root))
|
||||
return
|
||||
end
|
||||
|
||||
local root_index = __root_set[root.__fragment]
|
||||
|
||||
if not root_index or __root_list[root_index] ~= root then
|
||||
__error_fmt('unexpected root chunk: (%s)',
|
||||
__lua_tostring(root))
|
||||
return
|
||||
end
|
||||
|
||||
for sib_root_index = root_index, __root_count - 1 do
|
||||
local sib_root = __root_list[sib_root_index + 1]
|
||||
__root_set[sib_root.__fragment] = sib_root_index
|
||||
__root_list[sib_root_index] = sib_root
|
||||
end
|
||||
|
||||
__root_set[root.__fragment] = nil
|
||||
__root_list[__root_count] = nil
|
||||
__root_count = __root_count - 1
|
||||
end
|
||||
|
||||
---@param child evolved.chunk
|
||||
---@param parent evolved.chunk
|
||||
function __add_child_chunk(child, parent)
|
||||
local child_index = __list_lwr(parent.__child_list, child, function(a, b)
|
||||
return a.__fragment < b.__fragment
|
||||
end)
|
||||
|
||||
for sib_child_index = parent.__child_count, child_index, -1 do
|
||||
local sib_child = parent.__child_list[sib_child_index]
|
||||
parent.__child_set[sib_child] = sib_child_index + 1
|
||||
parent.__child_list[sib_child_index + 1] = sib_child
|
||||
end
|
||||
|
||||
parent.__child_set[child] = child_index
|
||||
parent.__child_list[child_index] = child
|
||||
parent.__child_count = parent.__child_count + 1
|
||||
|
||||
parent.__with_fragment_edges[child.__fragment] = child
|
||||
child.__without_fragment_edges[child.__fragment] = parent
|
||||
|
||||
child.__parent = parent
|
||||
end
|
||||
|
||||
---@param child evolved.chunk
|
||||
function __remove_child_chunk(child)
|
||||
local parent = child.__parent
|
||||
|
||||
if not parent then
|
||||
__error_fmt('unexpected child chunk: (%s)',
|
||||
__lua_tostring(child))
|
||||
return
|
||||
end
|
||||
|
||||
local child_index = parent.__child_set[child]
|
||||
|
||||
if not child_index or parent.__child_list[child_index] ~= child then
|
||||
__error_fmt('unexpected child chunk: (%s)',
|
||||
__lua_tostring(child))
|
||||
return
|
||||
end
|
||||
|
||||
for sib_child_index = child_index, parent.__child_count - 1 do
|
||||
local next_sib_child = parent.__child_list[sib_child_index + 1]
|
||||
parent.__child_set[next_sib_child] = sib_child_index
|
||||
parent.__child_list[sib_child_index] = next_sib_child
|
||||
end
|
||||
|
||||
parent.__child_set[child] = nil
|
||||
parent.__child_list[parent.__child_count] = nil
|
||||
parent.__child_count = parent.__child_count - 1
|
||||
|
||||
parent.__with_fragment_edges[child.__fragment] = nil
|
||||
child.__without_fragment_edges[child.__fragment] = nil
|
||||
|
||||
child.__parent = nil
|
||||
end
|
||||
|
||||
---@param chunk evolved.chunk
|
||||
function __update_chunk_caches(chunk)
|
||||
local chunk_parent = chunk.__parent
|
||||
@@ -1877,7 +2029,7 @@ end
|
||||
---@nodiscard
|
||||
function __chunk_with_fragment(chunk, fragment)
|
||||
if not chunk then
|
||||
local root_chunk = __root_chunks[fragment]
|
||||
local root_chunk = __root_list[__root_set[fragment]]
|
||||
return root_chunk or __new_chunk(nil, fragment)
|
||||
end
|
||||
|
||||
@@ -2063,7 +2215,7 @@ end
|
||||
---@return evolved.chunk
|
||||
---@nodiscard
|
||||
function __chunk_fragments(head_fragment, ...)
|
||||
local chunk = __root_chunks[head_fragment]
|
||||
local chunk = __root_list[__root_set[head_fragment]]
|
||||
or __new_chunk(nil, head_fragment)
|
||||
|
||||
for tail_fragment_index = 1, __lua_select('#', ...) do
|
||||
@@ -2086,7 +2238,7 @@ function __chunk_components(components)
|
||||
return
|
||||
end
|
||||
|
||||
local chunk = __root_chunks[head_fragment]
|
||||
local chunk = __root_list[__root_set[head_fragment]]
|
||||
or __new_chunk(nil, head_fragment)
|
||||
|
||||
for tail_fragment in __lua_next, components, head_fragment do
|
||||
@@ -2936,25 +3088,14 @@ function __purge_chunk(chunk)
|
||||
__shrink_chunk(chunk, 0)
|
||||
end
|
||||
|
||||
local chunk_parent = chunk.__parent
|
||||
local chunk_fragment = chunk.__fragment
|
||||
|
||||
local chunk_fragment_list = chunk.__fragment_list
|
||||
local chunk_fragment_count = chunk.__fragment_count
|
||||
|
||||
local with_fragment_edges = chunk.__with_fragment_edges
|
||||
local without_fragment_edges = chunk.__without_fragment_edges
|
||||
|
||||
if not chunk_parent then
|
||||
if __root_chunks[chunk_fragment] ~= chunk then
|
||||
__error_fmt('unexpected root chunk %s',
|
||||
__lua_tostring(__root_chunks[chunk_fragment]))
|
||||
end
|
||||
__root_chunks[chunk_fragment] = nil
|
||||
if not chunk.__parent then
|
||||
__remove_root_chunk(chunk)
|
||||
else
|
||||
__remove_child_chunk(chunk)
|
||||
end
|
||||
|
||||
do
|
||||
local major = chunk_fragment
|
||||
local major = chunk.__fragment
|
||||
local major_chunks = __major_chunks[major]
|
||||
|
||||
if major_chunks and __assoc_list_remove(major_chunks, chunk) == 0 then
|
||||
@@ -2962,8 +3103,8 @@ function __purge_chunk(chunk)
|
||||
end
|
||||
end
|
||||
|
||||
for chunk_fragment_index = 1, chunk_fragment_count do
|
||||
local minor = chunk_fragment_list[chunk_fragment_index]
|
||||
for chunk_fragment_index = 1, chunk.__fragment_count do
|
||||
local minor = chunk.__fragment_list[chunk_fragment_index]
|
||||
local minor_chunks = __minor_chunks[minor]
|
||||
|
||||
if minor_chunks and __assoc_list_remove(minor_chunks, chunk) == 0 then
|
||||
@@ -2971,19 +3112,13 @@ function __purge_chunk(chunk)
|
||||
end
|
||||
end
|
||||
|
||||
if chunk_parent then
|
||||
chunk.__parent, chunk_parent.__child_count = nil, __assoc_list_remove_ex(
|
||||
chunk_parent.__child_set, chunk_parent.__child_list, chunk_parent.__child_count,
|
||||
chunk)
|
||||
end
|
||||
|
||||
for with_fragment, with_fragment_edge in __lua_next, with_fragment_edges do
|
||||
with_fragment_edges[with_fragment] = nil
|
||||
for with_fragment, with_fragment_edge in __lua_next, chunk.__with_fragment_edges do
|
||||
chunk.__with_fragment_edges[with_fragment] = nil
|
||||
with_fragment_edge.__without_fragment_edges[with_fragment] = nil
|
||||
end
|
||||
|
||||
for without_fragment, without_fragment_edge in __lua_next, without_fragment_edges do
|
||||
without_fragment_edges[without_fragment] = nil
|
||||
for without_fragment, without_fragment_edge in __lua_next, chunk.__without_fragment_edges do
|
||||
chunk.__without_fragment_edges[without_fragment] = nil
|
||||
without_fragment_edge.__with_fragment_edges[without_fragment] = nil
|
||||
end
|
||||
|
||||
@@ -5885,7 +6020,9 @@ function __evolved_execute(query)
|
||||
chunk_stack_size = chunk_stack_size + query_chunk_count
|
||||
end
|
||||
elseif query_exclude_count > 0 then
|
||||
for _, root_chunk in __lua_next, __root_chunks do
|
||||
for root_index = 1, __root_count do
|
||||
local root_chunk = __root_list[root_index]
|
||||
|
||||
local is_root_chunk_matched =
|
||||
not root_chunk.__has_explicit_fragments and
|
||||
not query_exclude_set[root_chunk.__fragment]
|
||||
@@ -5896,7 +6033,9 @@ function __evolved_execute(query)
|
||||
end
|
||||
end
|
||||
else
|
||||
for _, root_chunk in __lua_next, __root_chunks do
|
||||
for root_index = 1, __root_count do
|
||||
local root_chunk = __root_list[root_index]
|
||||
|
||||
local is_root_chunk_matched =
|
||||
not root_chunk.__has_explicit_fragments
|
||||
|
||||
@@ -5997,7 +6136,9 @@ function __evolved_collect_garbage()
|
||||
local postorder_chunk_stack ---@type evolved.chunk[]?
|
||||
local postorder_chunk_stack_size = 0
|
||||
|
||||
for _, root_chunk in __lua_next, __root_chunks do
|
||||
for root_index = 1, __root_count do
|
||||
local root_chunk = __root_list[root_index]
|
||||
|
||||
if not working_chunk_stack then
|
||||
---@type evolved.chunk[]
|
||||
working_chunk_stack = __acquire_table(__table_pool_tag.chunk_list)
|
||||
@@ -6069,11 +6210,19 @@ function __evolved_collect_garbage()
|
||||
end
|
||||
end
|
||||
|
||||
for table_tag = 1, __table_pool_tag.__count do
|
||||
local table_pool = __tagged_table_pools[table_tag]
|
||||
for pool_index = 1, table_pool.__size do
|
||||
table_pool[pool_index] = {}
|
||||
for table_pool_tag = 1, __table_pool_tag.__count do
|
||||
local table_pool_reserve = __table_pool_reserve[table_pool_tag]
|
||||
|
||||
---@type evolved.table_pool
|
||||
local new_table_pool = __lua_table_new(table_pool_reserve)
|
||||
|
||||
for table_pool_index = 1, table_pool_reserve do
|
||||
new_table_pool[table_pool_index] = {}
|
||||
end
|
||||
|
||||
new_table_pool.__size = table_pool_reserve
|
||||
|
||||
__tagged_table_pools[table_pool_tag] = new_table_pool
|
||||
end
|
||||
|
||||
do
|
||||
|
||||
34
rockspecs/evolved.lua-1.9.0-0.rockspec
Normal file
34
rockspecs/evolved.lua-1.9.0-0.rockspec
Normal file
@@ -0,0 +1,34 @@
|
||||
rockspec_format = "3.0"
|
||||
package = "evolved.lua"
|
||||
version = "1.9.0-0"
|
||||
source = {
|
||||
url = "git://github.com/BlackMATov/evolved.lua",
|
||||
tag = "v1.9.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