mirror of
https://github.com/BlackMATov/evolved.lua.git
synced 2025-12-14 20:11:27 +07:00
add evolved.each function
This commit is contained in:
@@ -52,6 +52,8 @@ batch_destroy :: query -> integer, boolean
|
|||||||
|
|
||||||
chunk :: fragment... -> chunk?, entity[]?
|
chunk :: fragment... -> chunk?, entity[]?
|
||||||
select :: chunk, fragment... -> component[]?...
|
select :: chunk, fragment... -> component[]?...
|
||||||
|
|
||||||
|
each :: entity -> {each_state? -> fragment?, component?}, each_state?
|
||||||
execute :: query -> {execute_state? -> chunk?, entity[]?}, execute_state?
|
execute :: query -> {execute_state? -> chunk?, entity[]?}, execute_state?
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -8,5 +8,4 @@
|
|||||||
- optimize batch operations for cases with moving entities to empty chunks
|
- optimize batch operations for cases with moving entities to empty chunks
|
||||||
- should we clear chunk's components by on_insert tag callback?
|
- should we clear chunk's components by on_insert tag callback?
|
||||||
- use table.new/clear for cached tables
|
- use table.new/clear for cached tables
|
||||||
- add `each :: entity -> {each_state? -> fragment?, component?}, each_state?` function
|
|
||||||
- replace chunk_list/fragment_list pools to one generic table pool
|
- replace chunk_list/fragment_list pools to one generic table pool
|
||||||
|
|||||||
@@ -2336,3 +2336,89 @@ do
|
|||||||
assert(entities and entities[1] == e1)
|
assert(entities and entities[1] == e1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
do
|
||||||
|
local f1, f2 = evo.id(3)
|
||||||
|
|
||||||
|
do
|
||||||
|
local e = evo.id()
|
||||||
|
|
||||||
|
local iter, state = evo.each(e)
|
||||||
|
local fragment, component = iter(state)
|
||||||
|
assert(not fragment and not component)
|
||||||
|
end
|
||||||
|
|
||||||
|
do
|
||||||
|
local e = evo.id()
|
||||||
|
assert(evo.insert(e, f1, 41))
|
||||||
|
|
||||||
|
local iter, state = evo.each(e)
|
||||||
|
local fragment, component = iter(state)
|
||||||
|
assert(fragment == f1 and component == 41)
|
||||||
|
|
||||||
|
fragment, component = iter(state)
|
||||||
|
assert(not fragment and not component)
|
||||||
|
end
|
||||||
|
|
||||||
|
do
|
||||||
|
local e = evo.id()
|
||||||
|
assert(evo.insert(e, f1, 41))
|
||||||
|
assert(evo.insert(e, f2, 42))
|
||||||
|
|
||||||
|
do
|
||||||
|
local iter, state = evo.each(e)
|
||||||
|
local fragment, component = iter(state)
|
||||||
|
assert(fragment == f1 or fragment == f2)
|
||||||
|
assert((fragment == f1 and component == 41) or (fragment == f2 and component == 42))
|
||||||
|
|
||||||
|
fragment, component = iter(state)
|
||||||
|
assert(fragment == f1 or fragment == f2)
|
||||||
|
assert((fragment == f1 and component == 41) or (fragment == f2 and component == 42))
|
||||||
|
|
||||||
|
fragment, component = iter(state)
|
||||||
|
assert(not fragment and not component)
|
||||||
|
end
|
||||||
|
|
||||||
|
do
|
||||||
|
local fragment_sum = 0
|
||||||
|
local component_sum = 0
|
||||||
|
for f, c in evo.each(e) do
|
||||||
|
fragment_sum = fragment_sum + f
|
||||||
|
component_sum = component_sum + c
|
||||||
|
end
|
||||||
|
assert(fragment_sum == f1 + f2)
|
||||||
|
assert(component_sum == 41 + 42)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
do
|
||||||
|
local s = evo.id()
|
||||||
|
evo.set(s, evo.TAG)
|
||||||
|
|
||||||
|
local e = evo.id()
|
||||||
|
assert(evo.insert(e, f1))
|
||||||
|
assert(evo.insert(e, s))
|
||||||
|
|
||||||
|
do
|
||||||
|
local iter, state = evo.each(e)
|
||||||
|
local fragment, component = iter(state)
|
||||||
|
assert(fragment == f1 or fragment == s)
|
||||||
|
if fragment == f1 then
|
||||||
|
assert(component == true)
|
||||||
|
elseif fragment == s then
|
||||||
|
assert(component == nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
fragment, component = iter(state)
|
||||||
|
assert(fragment == f1 or fragment == s)
|
||||||
|
if fragment == f1 then
|
||||||
|
assert(component == true)
|
||||||
|
elseif fragment == s then
|
||||||
|
assert(component == nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
fragment, component = iter(state)
|
||||||
|
assert(not fragment and not component)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|||||||
113
evolved.lua
113
evolved.lua
@@ -17,7 +17,18 @@ local evolved = {}
|
|||||||
---@field package __with_fragment_edges table<evolved.fragment, evolved.chunk>
|
---@field package __with_fragment_edges table<evolved.fragment, evolved.chunk>
|
||||||
---@field package __without_fragment_edges table<evolved.fragment, evolved.chunk>
|
---@field package __without_fragment_edges table<evolved.fragment, evolved.chunk>
|
||||||
|
|
||||||
---@alias evolved.execute_state [integer, evolved.chunk[], table<evolved.fragment, boolean>?]
|
---@class (exact) evolved.each_state
|
||||||
|
---@field [1] integer structural_changes
|
||||||
|
---@field [2] evolved.chunk entity_chunk
|
||||||
|
---@field [3] integer entity_place
|
||||||
|
---@field [4] evolved.fragment? fragments_index
|
||||||
|
|
||||||
|
---@class (exact) evolved.execute_state
|
||||||
|
---@field [1] integer structural_changes
|
||||||
|
---@field [2] evolved.chunk[] chunk_stack
|
||||||
|
---@field [3] table<evolved.fragment, boolean>? exclude_set
|
||||||
|
|
||||||
|
---@alias evolved.each_iterator fun(state: evolved.each_state?): evolved.fragment?, evolved.component?
|
||||||
---@alias evolved.execute_iterator fun(state: evolved.execute_state?): evolved.chunk?, evolved.entity[]?
|
---@alias evolved.execute_iterator fun(state: evolved.execute_state?): evolved.chunk?, evolved.entity[]?
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -42,6 +53,7 @@ local __entity_places = {} ---@type table<integer, integer>
|
|||||||
local __chunk_lists = {} ---@type evolved.chunk[][]
|
local __chunk_lists = {} ---@type evolved.chunk[][]
|
||||||
local __fragment_lists = {} ---@type evolved.fragment[][]
|
local __fragment_lists = {} ---@type evolved.fragment[][]
|
||||||
|
|
||||||
|
local __each_states = {} ---@type evolved.each_state[]
|
||||||
local __execute_states = {} ---@type evolved.execute_state[]
|
local __execute_states = {} ---@type evolved.execute_state[]
|
||||||
|
|
||||||
local __structural_changes = 0 ---@type integer
|
local __structural_changes = 0 ---@type integer
|
||||||
@@ -219,24 +231,79 @@ end
|
|||||||
---
|
---
|
||||||
---
|
---
|
||||||
|
|
||||||
|
---@param chunk evolved.chunk
|
||||||
|
---@param place integer
|
||||||
|
---@return evolved.each_state
|
||||||
|
---@nodiscard
|
||||||
|
local function __acquire_each_state(chunk, place)
|
||||||
|
local each_state_count = #__each_states
|
||||||
|
|
||||||
|
if each_state_count == 0 then
|
||||||
|
---@type evolved.each_state
|
||||||
|
return { __structural_changes, chunk, place }
|
||||||
|
end
|
||||||
|
|
||||||
|
local state = __each_states[each_state_count]
|
||||||
|
__each_states[each_state_count] = nil
|
||||||
|
|
||||||
|
state[1], state[2], state[3] =
|
||||||
|
__structural_changes, chunk, place
|
||||||
|
|
||||||
|
return state
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param state evolved.each_state
|
||||||
|
local function __release_each_state(state)
|
||||||
|
for i = #state, 1, -1 do state[i] = nil end
|
||||||
|
__each_states[#__each_states + 1] = state
|
||||||
|
end
|
||||||
|
|
||||||
|
---@type evolved.each_iterator
|
||||||
|
local function __each_iterator(state)
|
||||||
|
if not state then return end
|
||||||
|
|
||||||
|
local structural_changes, chunk, place, fragment =
|
||||||
|
state[1], state[2], state[3], state[4]
|
||||||
|
|
||||||
|
if structural_changes ~= __structural_changes then
|
||||||
|
error('structural changes are prohibited during iteration', 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
fragment = next(chunk.__fragments, fragment)
|
||||||
|
|
||||||
|
if fragment then
|
||||||
|
state[4] = fragment
|
||||||
|
local fragment_components = chunk.__components[fragment]
|
||||||
|
return fragment, fragment_components and fragment_components[place]
|
||||||
|
end
|
||||||
|
|
||||||
|
__release_each_state(state)
|
||||||
|
end
|
||||||
|
|
||||||
|
---
|
||||||
|
---
|
||||||
|
---
|
||||||
|
---
|
||||||
|
---
|
||||||
|
|
||||||
---@param exclude_set? table<evolved.fragment, boolean>
|
---@param exclude_set? table<evolved.fragment, boolean>
|
||||||
---@return evolved.execute_state
|
---@return evolved.execute_state
|
||||||
---@return evolved.chunk[]
|
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
local function __acquire_execute_state(exclude_set)
|
local function __acquire_execute_state(exclude_set)
|
||||||
local execute_state_count = #__execute_states
|
local execute_state_count = #__execute_states
|
||||||
|
|
||||||
if execute_state_count == 0 then
|
if execute_state_count == 0 then
|
||||||
local chunk_stack = __acquire_chunk_list()
|
---@type evolved.execute_state
|
||||||
return { __structural_changes, chunk_stack, exclude_set }, chunk_stack
|
return { __structural_changes, __acquire_chunk_list(), exclude_set }
|
||||||
end
|
end
|
||||||
|
|
||||||
local state = __execute_states[execute_state_count]
|
local state = __execute_states[execute_state_count]
|
||||||
__execute_states[execute_state_count] = nil
|
__execute_states[execute_state_count] = nil
|
||||||
|
|
||||||
local chunk_stack = __acquire_chunk_list()
|
state[1], state[2], state[3] =
|
||||||
state[1], state[2], state[3] = __structural_changes, chunk_stack, exclude_set
|
__structural_changes, __acquire_chunk_list(), exclude_set
|
||||||
return state, chunk_stack
|
|
||||||
|
return state
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param state evolved.execute_state
|
---@param state evolved.execute_state
|
||||||
@@ -2286,33 +2353,59 @@ function evolved.select(chunk, ...)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param entity evolved.entity
|
||||||
|
---@return evolved.each_iterator
|
||||||
|
---@return evolved.each_state?
|
||||||
|
---@nodiscard
|
||||||
|
function evolved.each(entity)
|
||||||
|
if not __is_id_alive(entity) then
|
||||||
|
return __each_iterator
|
||||||
|
end
|
||||||
|
|
||||||
|
local index = __unpack_id(entity)
|
||||||
|
|
||||||
|
local chunk = __entity_chunks[index]
|
||||||
|
local place = __entity_places[index]
|
||||||
|
|
||||||
|
if not chunk then
|
||||||
|
return __each_iterator
|
||||||
|
end
|
||||||
|
|
||||||
|
return __each_iterator, __acquire_each_state(chunk, place)
|
||||||
|
end
|
||||||
|
|
||||||
---@param query evolved.query
|
---@param query evolved.query
|
||||||
---@return evolved.execute_iterator
|
---@return evolved.execute_iterator
|
||||||
---@return evolved.execute_state?
|
---@return evolved.execute_state?
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
function evolved.execute(query)
|
function evolved.execute(query)
|
||||||
|
if not __is_id_alive(query) then
|
||||||
|
return __execute_iterator
|
||||||
|
end
|
||||||
|
|
||||||
---@type evolved.fragment[]?, evolved.fragment[]?
|
---@type evolved.fragment[]?, evolved.fragment[]?
|
||||||
local include_list, exclude_list = evolved.get(query,
|
local include_list, exclude_list = evolved.get(query,
|
||||||
__SORTED_INCLUDE_LIST, __SORTED_EXCLUDE_LIST)
|
__SORTED_INCLUDE_LIST, __SORTED_EXCLUDE_LIST)
|
||||||
|
|
||||||
if not include_list or #include_list == 0 then
|
if not include_list or #include_list == 0 then
|
||||||
return __execute_iterator, nil
|
return __execute_iterator
|
||||||
end
|
end
|
||||||
|
|
||||||
local major_fragment = include_list[#include_list]
|
local major_fragment = include_list[#include_list]
|
||||||
local major_fragment_chunks = __major_chunks[major_fragment]
|
local major_fragment_chunks = __major_chunks[major_fragment]
|
||||||
|
|
||||||
if not major_fragment_chunks then
|
if not major_fragment_chunks then
|
||||||
return __execute_iterator, nil
|
return __execute_iterator
|
||||||
end
|
end
|
||||||
|
|
||||||
---@type table<evolved.fragment, boolean>?
|
---@type table<evolved.fragment, boolean>?
|
||||||
local exclude_set = evolved.get(query, __EXCLUDE_SET)
|
local exclude_set = evolved.get(query, __EXCLUDE_SET)
|
||||||
local execute_state, chunk_stack = __acquire_execute_state(exclude_set)
|
local execute_state = __acquire_execute_state(exclude_set)
|
||||||
|
|
||||||
for _, major_fragment_chunk in ipairs(major_fragment_chunks) do
|
for _, major_fragment_chunk in ipairs(major_fragment_chunks) do
|
||||||
if __chunk_has_all_fragment_list(major_fragment_chunk, include_list) then
|
if __chunk_has_all_fragment_list(major_fragment_chunk, include_list) then
|
||||||
if not exclude_list or not __chunk_has_any_fragment_list(major_fragment_chunk, exclude_list) then
|
if not exclude_list or not __chunk_has_any_fragment_list(major_fragment_chunk, exclude_list) then
|
||||||
|
local chunk_stack = execute_state[2]
|
||||||
chunk_stack[#chunk_stack + 1] = major_fragment_chunk
|
chunk_stack[#chunk_stack + 1] = major_fragment_chunk
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user